diff options
author | Stefan Roese <sr@denx.de> | 2015-04-20 09:31:27 +0200 |
---|---|---|
committer | Luka Perkov <luka.perkov@sartura.hr> | 2015-07-23 10:38:14 +0200 |
commit | edb470253346f4a882ba9e891c8b102ce388b9cc (patch) | |
tree | 339a9b773b85f6f6780bcc75853838ca488f702d /arch/arm | |
parent | 29b103c733f6c17ecf8ee8d66140254788e2bdda (diff) |
arm: mvebu: Add Armada 38x SERDES / PHY init code from Marvell bin_hdr
This code is ported from the Marvell bin_hdr code into mainline
SPL U-Boot. It needs to be executed very early so that the devices
connected to the serdes PHY are configured correctly.
Signed-off-by: Stefan Roese <sr@denx.de>
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/mach-mvebu/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/serdes/a38x/Makefile | 10 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c | 347 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h | 82 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec-38x.c | 158 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec.c | 2228 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec.h | 251 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/serdes/a38x/high_speed_topology_spec-38x.c | 1009 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/serdes/a38x/high_speed_topology_spec.h | 124 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/serdes/a38x/seq_exec.c | 170 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/serdes/a38x/seq_exec.h | 65 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/serdes/a38x/sys_env_lib.c | 388 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/serdes/a38x/sys_env_lib.h | 371 |
13 files changed, 5204 insertions, 0 deletions
diff --git a/arch/arm/mach-mvebu/Makefile b/arch/arm/mach-mvebu/Makefile index 9cdbefd24b1..446ce041097 100644 --- a/arch/arm/mach-mvebu/Makefile +++ b/arch/arm/mach-mvebu/Makefile @@ -20,6 +20,7 @@ obj-y += timer.o obj-$(CONFIG_SPL_BUILD) += spl.o obj-$(CONFIG_SPL_BUILD) += lowlevel_spl.o +obj-$(CONFIG_SYS_MVEBU_DDR_A38X) += serdes/a38x/ obj-$(CONFIG_SYS_MVEBU_DDR_AXP) += serdes/axp/ endif diff --git a/arch/arm/mach-mvebu/serdes/a38x/Makefile b/arch/arm/mach-mvebu/serdes/a38x/Makefile new file mode 100644 index 00000000000..1503da84041 --- /dev/null +++ b/arch/arm/mach-mvebu/serdes/a38x/Makefile @@ -0,0 +1,10 @@ +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_SPL_BUILD) = ctrl_pex.o +obj-$(CONFIG_SPL_BUILD) += high_speed_env_spec.o +obj-$(CONFIG_SPL_BUILD) += high_speed_env_spec-38x.o +obj-$(CONFIG_SPL_BUILD) += high_speed_topology_spec-38x.o +obj-$(CONFIG_SPL_BUILD) += seq_exec.o +obj-$(CONFIG_SPL_BUILD) += sys_env_lib.o diff --git a/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c b/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c new file mode 100644 index 00000000000..5f223f9b560 --- /dev/null +++ b/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c @@ -0,0 +1,347 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "ctrl_pex.h" +#include "sys_env_lib.h" + +int hws_pex_config(struct serdes_map *serdes_map) +{ + u32 pex_idx, tmp, next_busno, first_busno, temp_pex_reg, + temp_reg, addr, dev_id, ctrl_mode; + enum serdes_type serdes_type; + u32 idx, max_lane_num; + + DEBUG_INIT_FULL_S("\n### hws_pex_config ###\n"); + + max_lane_num = hws_serdes_get_max_lane(); + for (idx = 0; idx < max_lane_num; idx++) { + serdes_type = serdes_map[idx].serdes_type; + /* configuration for PEX only */ + if ((serdes_type != PEX0) && (serdes_type != PEX1) && + (serdes_type != PEX2) && (serdes_type != PEX3)) + continue; + + if ((serdes_type != PEX0) && + ((serdes_map[idx].serdes_mode == PEX_ROOT_COMPLEX_X4) || + (serdes_map[idx].serdes_mode == PEX_END_POINT_X4))) { + /* for PEX by4 - relevant for the first port only */ + continue; + } + + pex_idx = serdes_type - PEX0; + tmp = reg_read(PEX_CAPABILITIES_REG(pex_idx)); + tmp &= ~(0xf << 20); + tmp |= (0x4 << 20); + reg_write(PEX_CAPABILITIES_REG(pex_idx), tmp); + } + + tmp = reg_read(SOC_CTRL_REG); + tmp &= ~0x03; + + for (idx = 0; idx < max_lane_num; idx++) { + serdes_type = serdes_map[idx].serdes_type; + if ((serdes_type != PEX0) && + ((serdes_map[idx].serdes_mode == PEX_ROOT_COMPLEX_X4) || + (serdes_map[idx].serdes_mode == PEX_END_POINT_X4))) { + /* for PEX by4 - relevant for the first port only */ + continue; + } + + switch (serdes_type) { + case PEX0: + tmp |= 0x1 << PCIE0_ENABLE_OFFS; + break; + case PEX1: + tmp |= 0x1 << PCIE1_ENABLE_OFFS; + break; + case PEX2: + tmp |= 0x1 << PCIE2_ENABLE_OFFS; + break; + case PEX3: + tmp |= 0x1 << PCIE3_ENABLE_OFFS; + break; + default: + break; + } + } + + reg_write(SOC_CTRL_REG, tmp); + + /* Support gen1/gen2 */ + DEBUG_INIT_FULL_S("Support gen1/gen2\n"); + next_busno = 0; + mdelay(150); + + for (idx = 0; idx < max_lane_num; idx++) { + serdes_type = serdes_map[idx].serdes_type; + DEBUG_INIT_FULL_S(" serdes_type=0x"); + DEBUG_INIT_FULL_D(serdes_type, 8); + DEBUG_INIT_FULL_S("\n"); + DEBUG_INIT_FULL_S(" idx=0x"); + DEBUG_INIT_FULL_D(idx, 8); + DEBUG_INIT_FULL_S("\n"); + + /* Configuration for PEX only */ + if ((serdes_type != PEX0) && (serdes_type != PEX1) && + (serdes_type != PEX2) && (serdes_type != PEX3)) + continue; + + if ((serdes_type != PEX0) && + ((serdes_map[idx].serdes_mode == PEX_ROOT_COMPLEX_X4) || + (serdes_map[idx].serdes_mode == PEX_END_POINT_X4))) { + /* for PEX by4 - relevant for the first port only */ + continue; + } + + pex_idx = serdes_type - PEX0; + tmp = reg_read(PEX_DBG_STATUS_REG(pex_idx)); + + first_busno = next_busno; + if ((tmp & 0x7f) != 0x7e) { + DEBUG_INIT_S("PCIe, Idx "); + DEBUG_INIT_D(pex_idx, 1); + DEBUG_INIT_S(": detected no link\n"); + continue; + } + + next_busno++; + temp_pex_reg = reg_read((PEX_CFG_DIRECT_ACCESS + (pex_idx, PEX_LINK_CAPABILITY_REG))); + temp_pex_reg &= 0xf; + if (temp_pex_reg != 0x2) + continue; + + temp_reg = (reg_read(PEX_CFG_DIRECT_ACCESS( + pex_idx, + PEX_LINK_CTRL_STAT_REG)) & + 0xf0000) >> 16; + + /* Check if the link established is GEN1 */ + DEBUG_INIT_FULL_S + ("Checking if the link established is gen1\n"); + if (temp_reg != 0x1) + continue; + + pex_local_bus_num_set(pex_idx, first_busno); + pex_local_dev_num_set(pex_idx, 1); + DEBUG_INIT_FULL_S("PCIe, Idx "); + DEBUG_INIT_FULL_D(pex_idx, 1); + + DEBUG_INIT_S(":** Link is Gen1, check the EP capability\n"); + /* link is Gen1, check the EP capability */ + addr = pex_config_read(pex_idx, first_busno, 0, 0, 0x34) & 0xff; + DEBUG_INIT_FULL_C("pex_config_read: return addr=0x%x", addr, 4); + if (addr == 0xff) { + DEBUG_INIT_FULL_C + ("pex_config_read: return 0xff -->PCIe (%d): Detected No Link.", + pex_idx, 1); + continue; + } + + while ((pex_config_read(pex_idx, first_busno, 0, 0, addr) + & 0xff) != 0x10) { + addr = (pex_config_read(pex_idx, first_busno, 0, + 0, addr) & 0xff00) >> 8; + } + + /* Check for Gen2 and above */ + if ((pex_config_read(pex_idx, first_busno, 0, 0, + addr + 0xc) & 0xf) < 0x2) { + DEBUG_INIT_S("PCIe, Idx "); + DEBUG_INIT_D(pex_idx, 1); + DEBUG_INIT_S(": remains Gen1\n"); + continue; + } + + tmp = reg_read(PEX_LINK_CTRL_STATUS2_REG(pex_idx)); + DEBUG_RD_REG(PEX_LINK_CTRL_STATUS2_REG(pex_idx), tmp); + tmp &= ~(BIT(0) | BIT(1)); + tmp |= BIT(1); + tmp |= BIT(6); /* Select Deemphasize (-3.5d_b) */ + reg_write(PEX_LINK_CTRL_STATUS2_REG(pex_idx), tmp); + DEBUG_WR_REG(PEX_LINK_CTRL_STATUS2_REG(pex_idx), tmp); + + tmp = reg_read(PEX_CTRL_REG(pex_idx)); + DEBUG_RD_REG(PEX_CTRL_REG(pex_idx), tmp); + tmp |= BIT(10); + reg_write(PEX_CTRL_REG(pex_idx), tmp); + DEBUG_WR_REG(PEX_CTRL_REG(pex_idx), tmp); + + /* + * We need to wait 10ms before reading the PEX_DBG_STATUS_REG + * in order not to read the status of the former state + */ + mdelay(10); + + DEBUG_INIT_S("PCIe, Idx "); + DEBUG_INIT_D(pex_idx, 1); + DEBUG_INIT_S + (": Link upgraded to Gen2 based on client cpabilities\n"); + } + + /* Update pex DEVICE ID */ + ctrl_mode = sys_env_model_get(); + + for (idx = 0; idx < max_lane_num; idx++) { + serdes_type = serdes_map[idx].serdes_type; + /* configuration for PEX only */ + if ((serdes_type != PEX0) && (serdes_type != PEX1) && + (serdes_type != PEX2) && (serdes_type != PEX3)) + continue; + + if ((serdes_type != PEX0) && + ((serdes_map[idx].serdes_mode == PEX_ROOT_COMPLEX_X4) || + (serdes_map[idx].serdes_mode == PEX_END_POINT_X4))) { + /* for PEX by4 - relevant for the first port only */ + continue; + } + + pex_idx = serdes_type - PEX0; + dev_id = reg_read(PEX_CFG_DIRECT_ACCESS + (pex_idx, PEX_DEVICE_AND_VENDOR_ID)); + dev_id &= 0xffff; + dev_id |= ((ctrl_mode << 16) & 0xffff0000); + reg_write(PEX_CFG_DIRECT_ACCESS + (pex_idx, PEX_DEVICE_AND_VENDOR_ID), dev_id); + } + DEBUG_INIT_FULL_C("Update PEX Device ID ", ctrl_mode, 4); + + return MV_OK; +} + +int pex_local_bus_num_set(u32 pex_if, u32 bus_num) +{ + u32 pex_status; + + DEBUG_INIT_FULL_S("\n### pex_local_bus_num_set ###\n"); + + if (bus_num >= MAX_PEX_BUSSES) { + DEBUG_INIT_C("pex_local_bus_num_set: Illegal bus number %d\n", + bus_num, 4); + return MV_BAD_PARAM; + } + + pex_status = reg_read(PEX_STATUS_REG(pex_if)); + pex_status &= ~PXSR_PEX_BUS_NUM_MASK; + pex_status |= + (bus_num << PXSR_PEX_BUS_NUM_OFFS) & PXSR_PEX_BUS_NUM_MASK; + reg_write(PEX_STATUS_REG(pex_if), pex_status); + + return MV_OK; +} + +int pex_local_dev_num_set(u32 pex_if, u32 dev_num) +{ + u32 pex_status; + + DEBUG_INIT_FULL_S("\n### pex_local_dev_num_set ###\n"); + + pex_status = reg_read(PEX_STATUS_REG(pex_if)); + pex_status &= ~PXSR_PEX_DEV_NUM_MASK; + pex_status |= + (dev_num << PXSR_PEX_DEV_NUM_OFFS) & PXSR_PEX_DEV_NUM_MASK; + reg_write(PEX_STATUS_REG(pex_if), pex_status); + + return MV_OK; +} + +/* + * pex_config_read - Read from configuration space + * + * DESCRIPTION: + * This function performs a 32 bit read from PEX configuration space. + * It supports both type 0 and type 1 of Configuration Transactions + * (local and over bridge). In order to read from local bus segment, use + * bus number retrieved from pex_local_bus_num_get(). Other bus numbers + * will result configuration transaction of type 1 (over bridge). + * + * INPUT: + * pex_if - PEX interface number. + * bus - PEX segment bus number. + * dev - PEX device number. + * func - Function number. + * reg_offs - Register offset. + * + * OUTPUT: + * None. + * + * RETURN: + * 32bit register data, 0xffffffff on error + */ +u32 pex_config_read(u32 pex_if, u32 bus, u32 dev, u32 func, u32 reg_off) +{ + u32 pex_data = 0; + u32 local_dev, local_bus; + u32 pex_status; + + pex_status = reg_read(PEX_STATUS_REG(pex_if)); + local_dev = + ((pex_status & PXSR_PEX_DEV_NUM_MASK) >> PXSR_PEX_DEV_NUM_OFFS); + local_bus = + ((pex_status & PXSR_PEX_BUS_NUM_MASK) >> PXSR_PEX_BUS_NUM_OFFS); + + /* + * In PCI Express we have only one device number + * and this number is the first number we encounter + * else that the local_dev + * spec pex define return on config read/write on any device + */ + if (bus == local_bus) { + if (local_dev == 0) { + /* + * if local dev is 0 then the first number we encounter + * after 0 is 1 + */ + if ((dev != 1) && (dev != local_dev)) + return MV_ERROR; + } else { + /* + * if local dev is not 0 then the first number we + * encounter is 0 + */ + if ((dev != 0) && (dev != local_dev)) + return MV_ERROR; + } + } + + /* Creating PEX address to be passed */ + pex_data = (bus << PXCAR_BUS_NUM_OFFS); + pex_data |= (dev << PXCAR_DEVICE_NUM_OFFS); + pex_data |= (func << PXCAR_FUNC_NUM_OFFS); + /* Legacy register space */ + pex_data |= (reg_off & PXCAR_REG_NUM_MASK); + /* Extended register space */ + pex_data |= (((reg_off & PXCAR_REAL_EXT_REG_NUM_MASK) >> + PXCAR_REAL_EXT_REG_NUM_OFFS) << PXCAR_EXT_REG_NUM_OFFS); + pex_data |= PXCAR_CONFIG_EN; + + /* Write the address to the PEX configuration address register */ + reg_write(PEX_CFG_ADDR_REG(pex_if), pex_data); + + /* + * In order to let the PEX controller absorbed the address + * of the read transaction we perform a validity check that + * the address was written + */ + if (pex_data != reg_read(PEX_CFG_ADDR_REG(pex_if))) + return MV_ERROR; + + /* Cleaning Master Abort */ + reg_bit_set(PEX_CFG_DIRECT_ACCESS(pex_if, PEX_STATUS_AND_COMMAND), + PXSAC_MABORT); + /* Read the Data returned in the PEX Data register */ + pex_data = reg_read(PEX_CFG_DATA_REG(pex_if)); + + DEBUG_INIT_FULL_C(" --> ", pex_data, 4); + + return pex_data; +} diff --git a/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h b/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h new file mode 100644 index 00000000000..50327595fcc --- /dev/null +++ b/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _CTRL_PEX_H +#define _CTRL_PEX_H + +#include "high_speed_env_spec.h" + +/* Sample at Reset */ +#define MPP_SAMPLE_AT_RESET(id) (0xe4200 + (id * 4)) + +/* PCI Express Control and Status Registers */ +#define MAX_PEX_BUSSES 256 + +#define MISC_REGS_OFFSET 0x18200 +#define MV_MISC_REGS_BASE MISC_REGS_OFFSET +#define SOC_CTRL_REG (MV_MISC_REGS_BASE + 0x4) + +#define PEX_CAPABILITIES_REG(if) ((PEX_IF_REGS_BASE(if)) + 0x60) +#define PEX_LINK_CTRL_STATUS2_REG(if) ((PEX_IF_REGS_BASE(if)) + 0x90) +#define PEX_CTRL_REG(if) ((PEX_IF_REGS_BASE(if)) + 0x1a00) +#define PEX_STATUS_REG(if) ((PEX_IF_REGS_BASE(if)) + 0x1a04) +#define PEX_DBG_STATUS_REG(if) ((PEX_IF_REGS_BASE(if)) + 0x1a64) +#define PEX_LINK_CAPABILITY_REG 0x6c +#define PEX_LINK_CTRL_STAT_REG 0x70 +#define PXSR_PEX_DEV_NUM_OFFS 16 /* Device Number Indication */ +#define PXSR_PEX_DEV_NUM_MASK (0x1f << PXSR_PEX_DEV_NUM_OFFS) +#define PXSR_PEX_BUS_NUM_OFFS 8 /* Bus Number Indication */ +#define PXSR_PEX_BUS_NUM_MASK (0xff << PXSR_PEX_BUS_NUM_OFFS) + +/* PEX_CAPABILITIES_REG fields */ +#define PCIE0_ENABLE_OFFS 0 +#define PCIE0_ENABLE_MASK (0x1 << PCIE0_ENABLE_OFFS) +#define PCIE1_ENABLE_OFFS 1 +#define PCIE1_ENABLE_MASK (0x1 << PCIE1_ENABLE_OFFS) +#define PCIE2_ENABLE_OFFS 2 +#define PCIE2_ENABLE_MASK (0x1 << PCIE2_ENABLE_OFFS) +#define PCIE3_ENABLE_OFFS 3 +#define PCIE4_ENABLE_MASK (0x1 << PCIE3_ENABLE_OFFS) + +/* Controller revision info */ +#define PEX_DEVICE_AND_VENDOR_ID 0x000 + +/* PCI Express Configuration Address Register */ +#define PXCAR_REG_NUM_OFFS 2 +#define PXCAR_REG_NUM_MAX 0x3f +#define PXCAR_REG_NUM_MASK (PXCAR_REG_NUM_MAX << \ + PXCAR_REG_NUM_OFFS) +#define PXCAR_FUNC_NUM_OFFS 8 +#define PXCAR_FUNC_NUM_MAX 0x7 +#define PXCAR_FUNC_NUM_MASK (PXCAR_FUNC_NUM_MAX << \ + PXCAR_FUNC_NUM_OFFS) +#define PXCAR_DEVICE_NUM_OFFS 11 +#define PXCAR_DEVICE_NUM_MAX 0x1f +#define PXCAR_DEVICE_NUM_MASK (PXCAR_DEVICE_NUM_MAX << \ + PXCAR_DEVICE_NUM_OFFS) +#define PXCAR_BUS_NUM_OFFS 16 +#define PXCAR_BUS_NUM_MAX 0xff +#define PXCAR_BUS_NUM_MASK (PXCAR_BUS_NUM_MAX << \ + PXCAR_BUS_NUM_OFFS) +#define PXCAR_EXT_REG_NUM_OFFS 24 +#define PXCAR_EXT_REG_NUM_MAX 0xf + +#define PEX_CFG_ADDR_REG(if) ((PEX_IF_REGS_BASE(if)) + 0x18f8) +#define PEX_CFG_DATA_REG(if) ((PEX_IF_REGS_BASE(if)) + 0x18fc) + +#define PXCAR_REAL_EXT_REG_NUM_OFFS 8 +#define PXCAR_REAL_EXT_REG_NUM_MASK (0xf << PXCAR_REAL_EXT_REG_NUM_OFFS) + +#define PXCAR_CONFIG_EN BIT(31) +#define PEX_STATUS_AND_COMMAND 0x004 +#define PXSAC_MABORT BIT(29) /* Recieved Master Abort */ + +int hws_pex_config(struct serdes_map *serdes_map); +int pex_local_bus_num_set(u32 pex_if, u32 bus_num); +int pex_local_dev_num_set(u32 pex_if, u32 dev_num); +u32 pex_config_read(u32 pex_if, u32 bus, u32 dev, u32 func, u32 reg_off); + +#endif diff --git a/arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec-38x.c b/arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec-38x.c new file mode 100644 index 00000000000..5ff85672013 --- /dev/null +++ b/arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec-38x.c @@ -0,0 +1,158 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "high_speed_env_spec.h" +#include "sys_env_lib.h" + +#define SERDES_VERION "2.0" + +u8 selectors_serdes_rev1_map[LAST_SERDES_TYPE][MAX_SERDES_LANES] = { + /* 0 1 2 3 4 5 */ + {0x1, 0x1, NA, NA, NA, NA}, /* PEX0 */ + {NA, 0x2, 0x1, NA, 0x1, NA}, /* PEX1 */ + {NA, NA, 0x2, NA, NA, 0x1}, /* PEX2 */ + {NA, NA, NA, 0x1, NA, NA}, /* PEX3 */ + {0x2, 0x3, NA, NA, NA, NA}, /* SATA0 */ + {NA, NA, 0x3, NA, 0x2, NA}, /* SATA1 */ + {NA, NA, NA, NA, 0x6, 0x2}, /* SATA2 */ + {NA, NA, NA, 0x3, NA, NA}, /* SATA3 */ + {0x3, 0x4, NA, NA, NA, NA}, /* SGMII0 */ + {NA, 0x5, 0x4, NA, 0x3, NA}, /* SGMII1 */ + {NA, NA, NA, 0x4, NA, 0x3}, /* SGMII2 */ + {NA, 0x7, NA, NA, NA, NA}, /* QSGMII */ + {NA, 0x6, NA, NA, 0x4, NA}, /* USB3_HOST0 */ + {NA, NA, NA, 0x5, NA, 0x4}, /* USB3_HOST1 */ + {NA, NA, NA, 0x6, 0x5, 0x5}, /* USB3_DEVICE */ + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0} /* DEFAULT_SERDES */ +}; + +int hws_serdes_seq_init(void) +{ + DEBUG_INIT_FULL_S("\n### serdes_seq_init ###\n"); + + if (hws_serdes_seq_db_init() != MV_OK) { + printf("hws_serdes_seq_init: Error: Serdes initialization fail\n"); + return MV_FAIL; + } + + return MV_OK; +} + +int serdes_power_up_ctrl_ext(u32 serdes_num, int serdes_power_up, + enum serdes_type serdes_type, + enum serdes_speed baud_rate, + enum serdes_mode serdes_mode, + enum ref_clock ref_clock) +{ + return MV_NOT_SUPPORTED; +} + +u32 hws_serdes_silicon_ref_clock_get(void) +{ + DEBUG_INIT_FULL_S("\n### hws_serdes_silicon_ref_clock_get ###\n"); + + return REF_CLOCK_25MHZ; +} + +u32 hws_serdes_get_max_lane(void) +{ + switch (sys_env_device_id_get()) { + case MV_6811: /* A381/A3282: 6811/6821: single/dual cpu */ + return 4; + case MV_6810: + return 5; + case MV_6820: + case MV_6828: + return 6; + default: /* not the right module */ + printf("%s: Device ID Error, using 4 SerDes lanes\n", + __func__); + return 4; + } + return 6; +} + +int hws_is_serdes_active(u8 lane_num) +{ + int ret = 1; + + /* Maximum lane count for A388 (6828) is 6 */ + if (lane_num > 6) + ret = 0; + + /* 4th Lane (#4 on Device 6810 is not Active */ + if (sys_env_device_id_get() == MV_6810 && lane_num == 4) { + printf("%s: Error: Lane#4 on Device 6810 is not Active.\n", + __func__); + return 0; + } + + /* + * 6th Lane (#5) on Device 6810 is Active, even though 6810 + * has only 5 lanes + */ + if (sys_env_device_id_get() == MV_6810 && lane_num == 5) + return 1; + + if (lane_num >= hws_serdes_get_max_lane()) + ret = 0; + + return ret; +} + +int hws_get_ext_base_addr(u32 serdes_num, u32 base_addr, u32 unit_base_offset, + u32 *unit_base_reg, u32 *unit_offset) +{ + *unit_base_reg = base_addr; + *unit_offset = unit_base_offset; + + return MV_OK; +} + +/* + * hws_serdes_get_phy_selector_val + * + * DESCRIPTION: Get the mapping of Serdes Selector values according to the + * Serdes revision number + * INPUT: serdes_num - Serdes number + * serdes_type - Serdes type + * OUTPUT: None + * RETURN: + * Mapping of Serdes Selector values + */ +u32 hws_serdes_get_phy_selector_val(int serdes_num, + enum serdes_type serdes_type) +{ + if (serdes_type >= LAST_SERDES_TYPE) + return 0xff; + + if (hws_ctrl_serdes_rev_get() == MV_SERDES_REV_1_2) { + return selectors_serdes_rev1_map + [serdes_type][serdes_num]; + } else + return selectors_serdes_rev2_map + [serdes_type][serdes_num]; +} + +u32 hws_get_physical_serdes_num(u32 serdes_num) +{ + if ((serdes_num == 4) && (sys_env_device_id_get() == MV_6810)) { + /* + * For 6810, there are 5 Serdes and Serdes Num 4 doesn't + * exist. Instead Serdes Num 5 is connected. + */ + return 5; + } else { + return serdes_num; + } +} diff --git a/arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec.c b/arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec.c new file mode 100644 index 00000000000..23af7698fd1 --- /dev/null +++ b/arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec.c @@ -0,0 +1,2228 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "high_speed_env_spec.h" +#include "high_speed_topology_spec.h" +#include "sys_env_lib.h" +#include "ctrl_pex.h" + +#if defined(CONFIG_ARMADA_38X) +#elif defined(CONFIG_ARMADA_39X) +#else +#error "No device is defined" +#endif + +/* + * The board topology map, initialized in the beginning of + * ctrl_high_speed_serdes_phy_config + */ +struct serdes_map serdes_configuration_map[MAX_SERDES_LANES]; + +/* + * serdes_seq_db - holds all serdes sequences, their size and the + * relevant index in the data array initialized in serdes_seq_init + */ +struct cfg_seq serdes_seq_db[SERDES_LAST_SEQ]; + +#define SERDES_VERION "2.0" +#define ENDED_OK "High speed PHY - Ended Successfully\n" + +#define LINK_WAIT_CNTR 100 +#define LINK_WAIT_SLEEP 100 + +#define MAX_UNIT_NUMB 4 +#define TOPOLOGY_TEST_OK 0 +#define WRONG_NUMBER_OF_UNITS 1 +#define SERDES_ALREADY_IN_USE 2 +#define UNIT_NUMBER_VIOLATION 3 + +/* + * serdes_lane_in_use_count contains the exact amount of serdes lanes + * needed per type + */ +u8 serdes_lane_in_use_count[MAX_UNITS_ID][MAX_UNIT_NUMB] = { + /* 0 1 2 3 */ + { 1, 1, 1, 1 }, /* PEX */ + { 1, 1, 1, 1 }, /* ETH_GIG */ + { 1, 1, 0, 0 }, /* USB3H */ + { 1, 1, 1, 0 }, /* USB3D */ + { 1, 1, 1, 1 }, /* SATA */ + { 1, 0, 0, 0 }, /* QSGMII */ + { 4, 0, 0, 0 }, /* XAUI */ + { 2, 0, 0, 0 } /* RXAUI */ +}; + +/* + * serdes_unit_count count unit number. + * (i.e a single XAUI is counted as 1 unit) + */ +u8 serdes_unit_count[MAX_UNITS_ID] = { 0 }; + +/* Selector mapping for A380-A0 and A390-Z1 */ +u8 selectors_serdes_rev2_map[LAST_SERDES_TYPE][MAX_SERDES_LANES] = { + /* 0 1 2 3 4 5 6 */ + { 0x1, 0x1, NA, NA, NA, NA, NA }, /* PEX0 */ + { NA, NA, 0x1, NA, 0x1, NA, 0x1 }, /* PEX1 */ + { NA, NA, NA, NA, 0x7, 0x1, NA }, /* PEX2 */ + { NA, NA, NA, 0x1, NA, NA, NA }, /* PEX3 */ + { 0x2, 0x3, NA, NA, NA, NA, NA }, /* SATA0 */ + { NA, NA, 0x3, NA, NA, NA, NA }, /* SATA1 */ + { NA, NA, NA, NA, 0x6, 0x2, NA }, /* SATA2 */ + { NA, NA, NA, 0x3, NA, NA, NA }, /* SATA3 */ + { 0x3, 0x4, NA, NA, NA, NA, NA }, /* SGMII0 */ + { NA, 0x5, 0x4, NA, 0x3, NA, NA }, /* SGMII1 */ + { NA, NA, NA, 0x4, NA, 0x3, NA }, /* SGMII2 */ + { NA, 0x7, NA, NA, NA, NA, NA }, /* QSGMII */ + { NA, 0x6, NA, NA, 0x4, NA, NA }, /* USB3_HOST0 */ + { NA, NA, NA, 0x5, NA, 0x4, NA }, /* USB3_HOST1 */ + { NA, NA, NA, 0x6, 0x5, 0x5, NA }, /* USB3_DEVICE */ +#ifdef CONFIG_ARMADA_39X + { NA, NA, 0x5, NA, 0x8, NA, 0x2 }, /* SGMII3 */ + { NA, NA, NA, 0x8, 0x9, 0x8, 0x4 }, /* XAUI */ + { NA, NA, NA, NA, NA, 0x8, 0x4 }, /* RXAUI */ +#endif + { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, NA } /* DEFAULT_SERDES */ +}; + +/* Selector mapping for PEX by 4 confiuration */ +u8 common_phys_selectors_pex_by4_lanes[] = { 0x1, 0x2, 0x2, 0x2 }; + +static const char *const serdes_type_to_string[] = { + "PCIe0", + "PCIe1", + "PCIe2", + "PCIe3", + "SATA0", + "SATA1", + "SATA2", + "SATA3", + "SGMII0", + "SGMII1", + "SGMII2", + "QSGMII", + "USB3 HOST0", + "USB3 HOST1", + "USB3 DEVICE", + "SGMII3", + "XAUI", + "RXAUI", + "DEFAULT SERDES", + "LAST_SERDES_TYPE" +}; + +struct serdes_unit_data { + u8 serdes_unit_id; + u8 serdes_unit_num; +}; + +static struct serdes_unit_data serdes_type_to_unit_info[] = { + {PEX_UNIT_ID, 0,}, + {PEX_UNIT_ID, 1,}, + {PEX_UNIT_ID, 2,}, + {PEX_UNIT_ID, 3,}, + {SATA_UNIT_ID, 0,}, + {SATA_UNIT_ID, 1,}, + {SATA_UNIT_ID, 2,}, + {SATA_UNIT_ID, 3,}, + {ETH_GIG_UNIT_ID, 0,}, + {ETH_GIG_UNIT_ID, 1,}, + {ETH_GIG_UNIT_ID, 2,}, + {QSGMII_UNIT_ID, 0,}, + {USB3H_UNIT_ID, 0,}, + {USB3H_UNIT_ID, 1,}, + {USB3D_UNIT_ID, 0,}, + {ETH_GIG_UNIT_ID, 3,}, + {XAUI_UNIT_ID, 0,}, + {RXAUI_UNIT_ID, 0,}, +}; + +/* Sequences DB */ + +/* + * SATA and SGMII + */ + +struct op_params sata_port0_power_up_params[] = { + /* + * unit_base_reg, unit_offset, mask, SATA data, wait_time, + * num_of_loops + */ + /* Access to reg 0x48(OOB param 1) */ + {SATA_VENDOR_PORT_0_REG_ADDR, 0x38000, 0xffffffff, {0x48,}, 0, 0}, + /* OOB Com_wake and Com_reset spacing upper limit data */ + {SATA_VENDOR_PORT_0_REG_DATA, 0x38000, 0xf03f, {0x6018,}, 0, 0}, + /* Access to reg 0xa(PHY Control) */ + {SATA_VENDOR_PORT_0_REG_ADDR, 0x38000, 0xffffffff, {0xa,}, 0, 0}, + /* Rx clk and Tx clk select non-inverted mode */ + {SATA_VENDOR_PORT_0_REG_DATA, 0x38000, 0x3000, {0x0,}, 0, 0}, + /* Power Down Sata addr */ + {SATA_CTRL_REG_IND_ADDR, 0x38000, 0xffffffff, {0x0,}, 0, 0}, + /* Power Down Sata Port 0 */ + {SATA_CTRL_REG_IND_DATA, 0x38000, 0xffff00ff, {0xc40040,}, 0, 0}, +}; + +struct op_params sata_port1_power_up_params[] = { + /* + * unit_base_reg, unit_offset, mask, SATA data, wait_time, + * num_of_loops + */ + /* Access to reg 0x48(OOB param 1) */ + {SATA_VENDOR_PORT_1_REG_ADDR, 0x38000, 0xffffffff, {0x48,}, 0, 0}, + /* OOB Com_wake and Com_reset spacing upper limit data */ + {SATA_VENDOR_PORT_1_REG_DATA, 0x38000, 0xf03f, {0x6018,}, 0, 0}, + /* Access to reg 0xa(PHY Control) */ + {SATA_VENDOR_PORT_1_REG_ADDR, 0x38000, 0xffffffff, {0xa,}, 0, 0}, + /* Rx clk and Tx clk select non-inverted mode */ + {SATA_VENDOR_PORT_1_REG_DATA, 0x38000, 0x3000, {0x0,}, 0, 0}, + /* Power Down Sata addr */ + {SATA_CTRL_REG_IND_ADDR, 0x38000, 0xffffffff, {0x0,}, 0, 0}, + /* Power Down Sata Port 1 */ + {SATA_CTRL_REG_IND_DATA, 0x38000, 0xffffff00, {0xc44000,}, 0, 0}, +}; + +/* SATA and SGMII - power up seq */ +struct op_params sata_and_sgmii_power_up_params[] = { + /* + * unit_base_reg, unit_offset, mask, SATA data, SGMII data, + * wait_time, num_of_loops + */ + /* Power Up */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x90006, {0x80002, 0x80002}, + 0, 0}, + /* Unreset */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x7800, {0x6000, 0x6000}, 0, 0}, + /* Phy Selector */ + {POWER_AND_PLL_CTRL_REG, 0x800, 0x0e0, {0x0, 0x80}, 0, 0}, + /* Ref clock source select */ + {MISC_REG, 0x800, 0x440, {0x440, 0x400}, 0, 0} +}; + +/* SATA and SGMII - speed config seq */ +struct op_params sata_and_sgmii_speed_config_params[] = { + /* + * unit_base_reg, unit_offset, mask, SATA data, + * SGMII (1.25G), SGMII (3.125G), wait_time, num_of_loops + */ + /* Baud Rate */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x3fc00000, + {0x8800000, 0x19800000, 0x22000000}, 0, 0}, + /* Select Baud Rate for SATA only */ + {INTERFACE_REG, 0x800, 0xc00, {0x800, NO_DATA, NO_DATA}, 0, 0}, + /* Phy Gen RX and TX */ + {ISOLATE_REG, 0x800, 0xff, {NO_DATA, 0x66, 0x66}, 0, 0}, + /* Bus Width */ + {LOOPBACK_REG, 0x800, 0xe, {0x4, 0x2, 0x2}, 0, 0} +}; + +/* SATA and SGMII - TX config seq */ +struct op_params sata_and_sgmii_tx_config_params1[] = { + /* + * unitunit_base_reg, unit_offset, mask, SATA data, SGMII data, + * wait_time, num_of_loops + */ + {GLUE_REG, 0x800, 0x1800, {NO_DATA, 0x800}, 0, 0}, + /* Sft Reset pulse */ + {RESET_DFE_REG, 0x800, 0x401, {0x401, 0x401}, 0, 0}, + /* Sft Reset pulse */ + {RESET_DFE_REG, 0x800, 0x401, {0x0, 0x0}, 0, 0}, + /* Power up PLL, RX and TX */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0xf0000, {0x70000, 0x70000}, + 0, 0} +}; + +struct op_params sata_port0_tx_config_params[] = { + /* + * unit_base_reg, unit_offset, mask, SATA data, wait_time, + * num_of_loops + */ + /* Power Down Sata addr */ + {SATA_CTRL_REG_IND_ADDR, 0x38000, 0xffffffff, {0x0}, 0, 0}, + /* Power Down Sata Port 0 */ + {SATA_CTRL_REG_IND_DATA, 0x38000, 0xffff00ff, {0xc40000}, 0, 0}, + /* Regret bit addr */ + {SATA_CTRL_REG_IND_ADDR, 0x38000, 0xffffffff, {0x4}, 0, 0}, + /* Regret bit data */ + {SATA_CTRL_REG_IND_DATA, 0x38000, 0xffffffff, {0x80}, 0, 0} +}; + +struct op_params sata_port1_tx_config_params[] = { + /* + * unit_base_reg, unit_offset, mask, SATA data, wait_time, + * num_of_loops + */ + /* Power Down Sata addr */ + {SATA_CTRL_REG_IND_ADDR, 0x38000, 0xffffffff, {0x0}, 0, 0}, + /* Power Down Sata Port 1 */ + {SATA_CTRL_REG_IND_DATA, 0x38000, 0xffffff00, {0xc40000}, 0, 0}, + /* Regret bit addr */ + {SATA_CTRL_REG_IND_ADDR, 0x38000, 0xffffffff, {0x4}, 0, 0}, + /* Regret bit data */ + {SATA_CTRL_REG_IND_DATA, 0x38000, 0xffffffff, {0x80}, 0, 0} +}; + +struct op_params sata_and_sgmii_tx_config_serdes_rev1_params2[] = { + /* + * unit_base_reg, unit_offset, mask, SATA data, SGMII data, + * wait_time, num_of_loops + */ + /* Wait for PHY power up sequence to finish */ + {COMMON_PHY_STATUS1_REG, 0x28, 0xc, {0xc, 0xc}, 10, 1000}, + /* Wait for PHY power up sequence to finish */ + {COMMON_PHY_STATUS1_REG, 0x28, 0x1, {0x1, 0x1}, 1, 1000} +}; + +struct op_params sata_and_sgmii_tx_config_serdes_rev2_params2[] = { + /* + * unit_base_reg, unit_offset, mask, SATA data, SGMII data, + * wait_time, num_of_loops + */ + /* Wait for PHY power up sequence to finish */ + {COMMON_PHY_STATUS1_REG, 0x28, 0xc, {0xc, 0xc}, 10, 1000}, + /* Assert Rx Init for SGMII */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x40000000, {NA, 0x40000000}, + 0, 0}, + /* Assert Rx Init for SATA */ + {ISOLATE_REG, 0x800, 0x400, {0x400, NA}, 0, 0}, + /* Wait for PHY power up sequence to finish */ + {COMMON_PHY_STATUS1_REG, 0x28, 0x1, {0x1, 0x1}, 1, 1000}, + /* De-assert Rx Init for SGMII */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x40000000, {NA, 0x0}, 0, 0}, + /* De-assert Rx Init for SATA */ + {ISOLATE_REG, 0x800, 0x400, {0x0, NA}, 0, 0}, + /* os_ph_offset_force (align 90) */ + {RX_REG3, 0x800, 0xff, {0xde, NO_DATA}, 0, 0}, + /* Set os_ph_valid */ + {RX_REG3, 0x800, 0x100, {0x100, NO_DATA}, 0, 0}, + /* Unset os_ph_valid */ + {RX_REG3, 0x800, 0x100, {0x0, NO_DATA}, 0, 0}, +}; + +struct op_params sata_electrical_config_serdes_rev1_params[] = { + /* + * unit_base_reg, unit_offset, mask, SATA data, wait_time, + * num_of_loops + */ + /* enable SSC and DFE update enable */ + {COMMON_PHY_CONFIGURATION4_REG, 0x28, 0x400008, {0x400000,}, 0, 0}, + /* tximpcal_th and rximpcal_th */ + {VTHIMPCAL_CTRL_REG, 0x800, 0xff00, {0x4000,}, 0, 0}, + /* SQ_THRESH and FFE Setting */ + {SQUELCH_FFE_SETTING_REG, 0x800, 0xfff, {0x6cf,}, 0, 0}, + /* G1_TX SLEW, EMPH1 and AMP */ + {G1_SETTINGS_0_REG, 0x800, 0xffff, {0x8a32,}, 0, 0}, + /* G1_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI */ + {G1_SETTINGS_1_REG, 0x800, 0x3ff, {0x3c9,}, 0, 0}, + /* G2_TX SLEW, EMPH1 and AMP */ + {G2_SETTINGS_0_REG, 0x800, 0xffff, {0x8b5c,}, 0, 0}, + /* G2_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI */ + {G2_SETTINGS_1_REG, 0x800, 0x3ff, {0x3d2,}, 0, 0}, + /* G3_TX SLEW, EMPH1 and AMP */ + {G3_SETTINGS_0_REG, 0x800, 0xffff, {0xe6e,}, 0, 0}, + /* G3_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI */ + {G3_SETTINGS_1_REG, 0x800, 0x3ff, {0x3d2,}, 0, 0}, + /* Cal rxclkalign90 ext enable and Cal os ph ext */ + {CAL_REG6, 0x800, 0xff00, {0xdd00,}, 0, 0}, + /* Dtl Clamping disable and Dtl clamping Sel(6000ppm) */ + {RX_REG2, 0x800, 0xf0, {0x70,}, 0, 0}, +}; + +struct op_params sata_electrical_config_serdes_rev2_params[] = { + /* + * unit_base_reg, unit_offset, mask, SATA data, wait_time, + * num_of_loops + */ + /* SQ_THRESH and FFE Setting */ + {SQUELCH_FFE_SETTING_REG, 0x800, 0xf00, {0x600}, 0, 0}, + /* enable SSC and DFE update enable */ + {COMMON_PHY_CONFIGURATION4_REG, 0x28, 0x400008, {0x400000}, 0, 0}, + /* G1_TX SLEW, EMPH1 and AMP */ + {G1_SETTINGS_0_REG, 0x800, 0xffff, {0x8a32}, 0, 0}, + /* G1_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI */ + {G1_SETTINGS_1_REG, 0x800, 0x3ff, {0x3c9}, 0, 0}, + /* G2_TX SLEW, EMPH1 and AMP */ + {G2_SETTINGS_0_REG, 0x800, 0xffff, {0x8b5c}, 0, 0}, + /* G2_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI */ + {G2_SETTINGS_1_REG, 0x800, 0x3ff, {0x3d2}, 0, 0}, + /* G3_TX SLEW, EMPH1 and AMP */ + {G3_SETTINGS_0_REG, 0x800, 0xffff, {0xe6e}, 0, 0}, + /* + * G3_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI & DFE_En Gen3, + * DC wander calibration dis + */ + {G3_SETTINGS_1_REG, 0x800, 0x47ff, {0x7d2}, 0, 0}, + /* Bit[12]=0x0 idle_sync_en */ + {PCIE_REG0, 0x800, 0x1000, {0x0}, 0, 0}, + /* Dtl Clamping disable and Dtl clamping Sel(6000ppm) */ + {RX_REG2, 0x800, 0xf0, {0x70,}, 0, 0}, + /* tximpcal_th and rximpcal_th */ + {VTHIMPCAL_CTRL_REG, 0x800, 0xff00, {0x3000}, 0, 0}, + /* DFE_STEP_FINE_FX[3:0] =0xa */ + {DFE_REG0, 0x800, 0xa00f, {0x800a}, 0, 0}, + /* DFE_EN and Dis Update control from pin disable */ + {DFE_REG3, 0x800, 0xc000, {0x0}, 0, 0}, + /* FFE Force FFE_REs and cap settings for Gen1 */ + {G1_SETTINGS_3_REG, 0x800, 0xff, {0xcf}, 0, 0}, + /* FFE Force FFE_REs and cap settings for Gen2 */ + {G2_SETTINGS_3_REG, 0x800, 0xff, {0xbf}, 0, 0}, + /* FE Force FFE_REs=4 and cap settings for Gen3n */ + {G3_SETTINGS_3_REG, 0x800, 0xff, {0xcf}, 0, 0}, + /* Set DFE Gen 3 Resolution to 3 */ + {G3_SETTINGS_4_REG, 0x800, 0x300, {0x300}, 0, 0}, +}; + +struct op_params sgmii_electrical_config_serdes_rev1_params[] = { + /* + * unit_base_reg, unit_offset, mask, SGMII (1.25G), SGMII (3.125G), + * wait_time, num_of_loops + */ + /* G1_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI */ + {G1_SETTINGS_1_REG, 0x800, 0x3ff, {0x3c9, 0x3c9}, 0, 0}, + /* SQ_THRESH and FFE Setting */ + {SQUELCH_FFE_SETTING_REG, 0x800, 0xfff, {0x8f, 0xbf}, 0, 0}, + /* tximpcal_th and rximpcal_th */ + {VTHIMPCAL_CTRL_REG, 0x800, 0xff00, {0x4000, 0x4000}, 0, 0}, +}; + +struct op_params sgmii_electrical_config_serdes_rev2_params[] = { + /* + * unit_base_reg, unit_offset, mask, SGMII (1.25G), SGMII (3.125G), + * wait_time, num_of_loops + */ + /* Set Slew_rate, Emph and Amp */ + {G1_SETTINGS_0_REG, 0x800, 0xffff, {0x8fa, 0x8fa}, 0, 0}, + /* G1_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI */ + {G1_SETTINGS_1_REG, 0x800, 0x3ff, {0x3c9, 0x3c9}, 0, 0}, + /* DTL_FLOOP_EN */ + {RX_REG2, 0x800, 0x4, {0x0, 0x0}, 0, 0}, + /* G1 FFE Setting Force, RES and CAP */ + {G1_SETTINGS_3_REG, 0x800, 0xff, {0x8f, 0xbf}, 0, 0}, + /* tximpcal_th and rximpcal_th */ + {VTHIMPCAL_CTRL_REG, 0x800, 0xff00, {0x3000, 0x3000}, 0, 0}, +}; + +/* + * PEX and USB3 + */ + +/* PEX and USB3 - power up seq for Serdes Rev 1.2 */ +struct op_params pex_and_usb3_power_up_serdes_rev1_params[] = { + /* + * unit_base_reg, unit_offset, mask, PEX data, USB3 data, + * wait_time, num_of_loops + */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x3fc7f806, + {0x4471804, 0x4479804}, 0, 0}, + {COMMON_PHY_CONFIGURATION2_REG, 0x28, 0x5c, {0x58, 0x58}, 0, 0}, + {COMMON_PHY_CONFIGURATION4_REG, 0x28, 0x3, {0x1, 0x1}, 0, 0}, + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x7800, {0x6000, 0xe000}, 0, 0}, + {GLOBAL_CLK_CTRL, 0x800, 0xd, {0x5, 0x1}, 0, 0}, + /* Ref clock source select */ + {MISC_REG, 0x800, 0x4c0, {0x80, 0x4c0}, 0, 0} +}; + +/* PEX and USB3 - power up seq for Serdes Rev 2.1 */ +struct op_params pex_and_usb3_power_up_serdes_rev2_params[] = { + /* + * unit_base_reg, unit_offset, mask, PEX data, USB3 data, + * wait_time, num_of_loops + */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x3fc7f806, + {0x4471804, 0x4479804}, 0, 0}, + {COMMON_PHY_CONFIGURATION2_REG, 0x28, 0x5c, {0x58, 0x58}, 0, 0}, + {COMMON_PHY_CONFIGURATION4_REG, 0x28, 0x3, {0x1, 0x1}, 0, 0}, + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x7800, {0x6000, 0xe000}, 0, 0}, + {GLOBAL_CLK_CTRL, 0x800, 0xd, {0x5, 0x1}, 0, 0}, + {GLOBAL_MISC_CTRL, 0x800, 0xc0, {0x0, NO_DATA}, 0, 0}, + /* Ref clock source select */ + {MISC_REG, 0x800, 0x4c0, {0x80, 0x4c0}, 0, 0} +}; + +/* PEX and USB3 - speed config seq */ +struct op_params pex_and_usb3_speed_config_params[] = { + /* + * unit_base_reg, unit_offset, mask, PEX data, USB3 data, + * wait_time, num_of_loops + */ + /* Maximal PHY Generation Setting */ + {INTERFACE_REG, 0x800, 0xc00, {0x400, 0x400, 0x400, 0x400, 0x400}, + 0, 0}, +}; + +struct op_params usb3_electrical_config_serdes_rev1_params[] = { + /* Spread Spectrum Clock Enable */ + {LANE_CFG4_REG, 0x800, 0x80, {0x80}, 0, 0}, + /* G2_TX_SSC_AMP[6:0]=4.5k_p_pM and TX emphasis mode=m_v */ + {G2_SETTINGS_2_REG, 0x800, 0xfe40, {0x4440}, 0, 0}, + /* tximpcal_th and rximpcal_th */ + {VTHIMPCAL_CTRL_REG, 0x800, 0xff00, {0x4000}, 0, 0}, + /* G2_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI */ + {G2_SETTINGS_1_REG, 0x800, 0x3ff, {0x3d2}, 0, 0}, + /* FFE Setting Force, RES and CAP */ + {SQUELCH_FFE_SETTING_REG, 0x800, 0xff, {0xef}, 0, 0}, + /* Dtl Clamping disable and Dtl-clamping-Sel(6000ppm) */ + {RX_REG2, 0x800, 0xf0, {0x70}, 0, 0}, + /* cal_rxclkalign90_ext_en and cal_os_ph_ext */ + {CAL_REG6, 0x800, 0xff00, {0xd500}, 0, 0}, + /* vco_cal_vth_sel */ + {REF_REG0, 0x800, 0x38, {0x20}, 0, 0}, +}; + +struct op_params usb3_electrical_config_serdes_rev2_params[] = { + /* Spread Spectrum Clock Enable */ + {LANE_CFG4_REG, 0x800, 0x80, {0x80}, 0, 0}, + /* G2_TX_SSC_AMP[6:0]=4.5k_p_pM and TX emphasis mode=m_v */ + {G2_SETTINGS_2_REG, 0x800, 0xfe40, {0x4440}, 0, 0}, + /* G2_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI */ + {G2_SETTINGS_1_REG, 0x800, 0x3ff, {0x3d2}, 0, 0}, + /* Dtl Clamping disable and Dtl-clamping-Sel(6000ppm) */ + {RX_REG2, 0x800, 0xf0, {0x70}, 0, 0}, + /* vco_cal_vth_sel */ + {REF_REG0, 0x800, 0x38, {0x20}, 0, 0}, + /* Spread Spectrum Clock Enable */ + {LANE_CFG5_REG, 0x800, 0x4, {0x4}, 0, 0}, +}; + +/* PEX and USB3 - TX config seq */ + +/* + * For PEXx1: the pex_and_usb3_tx_config_params1/2/3 configurations should run + * one by one on the lane. + * For PEXx4: the pex_and_usb3_tx_config_params1/2/3 configurations should run + * by setting each sequence for all 4 lanes. + */ +struct op_params pex_and_usb3_tx_config_params1[] = { + /* + * unit_base_reg, unit_offset, mask, PEX data, USB3 data, + * wait_time, num_of_loops + */ + {GLOBAL_CLK_CTRL, 0x800, 0x1, {0x0, 0x0}, 0, 0}, + /* 10ms delay */ + {0x0, 0x0, 0x0, {0x0, 0x0}, 10, 0}, + /* os_ph_offset_force (align 90) */ + {RX_REG3, 0x800, 0xff, {0xdc, NO_DATA}, 0, 0}, + /* Set os_ph_valid */ + {RX_REG3, 0x800, 0x100, {0x100, NO_DATA}, 0, 0}, + /* Unset os_ph_valid */ + {RX_REG3, 0x800, 0x100, {0x0, NO_DATA}, 0, 0}, +}; + +struct op_params pex_and_usb3_tx_config_params2[] = { + /* + * unit_base_reg, unit_offset, mask, PEX data, USB3 data, + * wait_time, num_of_loops + */ + /* Sft Reset pulse */ + {RESET_DFE_REG, 0x800, 0x401, {0x401, 0x401}, 0, 0}, +}; + +struct op_params pex_and_usb3_tx_config_params3[] = { + /* + * unit_base_reg, unit_offset, mask, PEX data, USB3 data, + * wait_time, num_of_loops + */ + /* Sft Reset pulse */ + {RESET_DFE_REG, 0x800, 0x401, {0x0, 0x0}, 0, 0}, + /* 10ms delay */ + {0x0, 0x0, 0x0, {0x0, 0x0}, 10, 0} +}; + +/* PEX by 4 config seq */ +struct op_params pex_by4_config_params[] = { + /* unit_base_reg, unit_offset, mask, data, wait_time, num_of_loops */ + {GLOBAL_CLK_SRC_HI, 0x800, 0x7, {0x5, 0x0, 0x0, 0x2}, 0, 0}, + /* Lane Alignement enable */ + {LANE_ALIGN_REG0, 0x800, 0x1000, {0x0, 0x0, 0x0, 0x0}, 0, 0}, + /* Max PLL phy config */ + {CALIBRATION_CTRL_REG, 0x800, 0x1000, {0x1000, 0x1000, 0x1000, 0x1000}, + 0, 0}, + /* Max PLL pipe config */ + {LANE_CFG1_REG, 0x800, 0x600, {0x600, 0x600, 0x600, 0x600}, 0, 0}, +}; + +/* USB3 device donfig seq */ +struct op_params usb3_device_config_params[] = { + /* unit_base_reg, unit_offset, mask, data, wait_time, num_of_loops */ + {LANE_CFG4_REG, 0x800, 0x200, {0x200}, 0, 0} +}; + +/* PEX - electrical configuration seq Rev 1.2 */ +struct op_params pex_electrical_config_serdes_rev1_params[] = { + /* + * unit_base_reg, unit_offset, mask, PEX data, wait_time, + * num_of_loops + */ + /* G1_TX_SLEW_CTRL_EN and G1_TX_SLEW_RATE */ + {G1_SETTINGS_0_REG, 0x800, 0xf000, {0xb000}, 0, 0}, + /* G1_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI */ + {G1_SETTINGS_1_REG, 0x800, 0x3ff, {0x3c9}, 0, 0}, + /* G2_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI */ + {G2_SETTINGS_1_REG, 0x800, 0x3ff, {0x3c9}, 0, 0}, + /* CFG_DFE_EN_SEL */ + {LANE_CFG4_REG, 0x800, 0x8, {0x8}, 0, 0}, + /* FFE Setting Force, RES and CAP */ + {SQUELCH_FFE_SETTING_REG, 0x800, 0xff, {0xaf}, 0, 0}, + /* tximpcal_th and rximpcal_th */ + {VTHIMPCAL_CTRL_REG, 0x800, 0xff00, {0x3000}, 0, 0}, + /* cal_rxclkalign90_ext_en and cal_os_ph_ext */ + {CAL_REG6, 0x800, 0xff00, {0xdc00}, 0, 0}, +}; + +/* PEX - electrical configuration seq Rev 2.1 */ +struct op_params pex_electrical_config_serdes_rev2_params[] = { + /* + * unit_base_reg, unit_offset, mask, PEX data, wait_time, + * num_of_loops + */ + /* G1_TX_SLEW_CTRL_EN and G1_TX_SLEW_RATE */ + {G1_SETTINGS_0_REG, 0x800, 0xf000, {0xb000}, 0, 0}, + /* G1_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI */ + {G1_SETTINGS_1_REG, 0x800, 0x3ff, {0x3c9}, 0, 0}, + /* G1 FFE Setting Force, RES and CAP */ + {G1_SETTINGS_3_REG, 0x800, 0xff, {0xcf}, 0, 0}, + /* G2_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI */ + {G2_SETTINGS_1_REG, 0x800, 0x3ff, {0x3c9}, 0, 0}, + /* G2 FFE Setting Force, RES and CAP */ + {G2_SETTINGS_3_REG, 0x800, 0xff, {0xaf}, 0, 0}, + /* G2 DFE resolution value */ + {G2_SETTINGS_4_REG, 0x800, 0x300, {0x300}, 0, 0}, + /* DFE resolution force */ + {DFE_REG0, 0x800, 0x8000, {0x8000}, 0, 0}, + /* Tx amplitude for Tx Margin 0 */ + {PCIE_REG1, 0x800, 0xf80, {0xd00}, 0, 0}, + /* Tx_Emph value for -3.5d_b and -6d_b */ + {PCIE_REG3, 0x800, 0xff00, {0xaf00}, 0, 0}, + /* CFG_DFE_EN_SEL */ + {LANE_CFG4_REG, 0x800, 0x8, {0x8}, 0, 0}, + /* tximpcal_th and rximpcal_th */ + {VTHIMPCAL_CTRL_REG, 0x800, 0xff00, {0x3000}, 0, 0}, +}; + +/* PEX - configuration seq for REF_CLOCK_25MHz */ +struct op_params pex_config_ref_clock25_m_hz[] = { + /* + * unit_base_reg, unit_offset, mask, PEX data, wait_time, + * num_of_loops + */ + /* Bits[4:0]=0x2 - REF_FREF_SEL */ + {POWER_AND_PLL_CTRL_REG, 0x800, 0x1f, {0x2}, 0, 0}, + /* Bit[10]=0x1 - REFCLK_SEL */ + {MISC_REG, 0x800, 0x400, {0x400}, 0, 0}, + /* Bits[7:0]=0x7 - CFG_PM_RXDLOZ_WAIT */ + {GLOBAL_PM_CTRL, 0x800, 0xff, {0x7}, 0, 0}, +}; + +/* PEX - configuration seq for REF_CLOCK_40MHz */ +struct op_params pex_config_ref_clock40_m_hz[] = { + /* + * unit_base_reg, unit_offset, mask, PEX data, wait_time, + * num_of_loops + */ + /* Bits[4:0]=0x3 - REF_FREF_SEL */ + {POWER_AND_PLL_CTRL_REG, 0x800, 0x1f, {0x3}, 0, 0}, + /* Bits[10]=0x1 - REFCLK_SEL */ + {MISC_REG, 0x800, 0x400, {0x400}, 0, 0}, + /* Bits[7:0]=0xc - CFG_PM_RXDLOZ_WAIT */ + {GLOBAL_PM_CTRL, 0x800, 0xff, {0xc}, 0, 0}, +}; + +/* PEX - configuration seq for REF_CLOCK_100MHz */ +struct op_params pex_config_ref_clock100_m_hz[] = { + /* + * unit_base_reg, unit_offset, mask, PEX data, wait_time, + * num_of_loops + */ + /* Bits[4:0]=0x0 - REF_FREF_SEL */ + {POWER_AND_PLL_CTRL_REG, 0x800, 0x1f, {0x0}, 0, 0}, + /* Bit[10]=0x0 - REFCLK_SEL */ + {MISC_REG, 0x800, 0x400, {0x0}, 0, 0}, + /* Bits[7:0]=0x1e - CFG_PM_RXDLOZ_WAIT */ + {GLOBAL_PM_CTRL, 0x800, 0xff, {0x1e}, 0, 0}, +}; + +/* + * USB2 + */ + +struct op_params usb2_power_up_params[] = { + /* + * unit_base_reg, unit_offset, mask, USB2 data, wait_time, + * num_of_loops + */ + /* Init phy 0 */ + {0x18440, 0x0 /*NA*/, 0xffffffff, {0x62}, 0, 0}, + /* Init phy 1 */ + {0x18444, 0x0 /*NA*/, 0xffffffff, {0x62}, 0, 0}, + /* Init phy 2 */ + {0x18448, 0x0 /*NA*/, 0xffffffff, {0x62}, 0, 0}, + /* Phy offset 0x0 - PLL_CONTROL0 */ + {0xc0000, 0x0 /*NA*/, 0xffffffff, {0x40605205}, 0, 0}, + {0xc001c, 0x0 /*NA*/, 0xffffffff, {0x39f16ce}, 0, 0}, + {0xc201c, 0x0 /*NA*/, 0xffffffff, {0x39f16ce}, 0, 0}, + {0xc401c, 0x0 /*NA*/, 0xffffffff, {0x39f16ce}, 0, 0}, + /* Phy offset 0x1 - PLL_CONTROL1 */ + {0xc0004, 0x0 /*NA*/, 0x1, {0x1}, 0, 0}, + /* Phy0 register 3 - TX Channel control 0 */ + {0xc000c, 0x0 /*NA*/, 0x1000000, {0x1000000}, 0, 0}, + /* Phy0 register 3 - TX Channel control 0 */ + {0xc200c, 0x0 /*NA*/, 0x1000000, {0x1000000}, 0, 0}, + /* Phy0 register 3 - TX Channel control 0 */ + {0xc400c, 0x0 /*NA*/, 0x1000000, {0x1000000}, 0, 0}, + /* check PLLCAL_DONE is set and IMPCAL_DONE is set */ + {0xc0008, 0x0 /*NA*/, 0x80800000, {0x80800000}, 1, 1000}, + /* check REG_SQCAL_DONE is set */ + {0xc0018, 0x0 /*NA*/, 0x80000000, {0x80000000}, 1, 1000}, + /* check PLL_READY is set */ + {0xc0000, 0x0 /*NA*/, 0x80000000, {0x80000000}, 1, 1000} +}; + +/* + * QSGMII + */ + +/* QSGMII - power up seq */ +struct op_params qsgmii_port_power_up_params[] = { + /* + * unit_base_reg, unit_offset, mask, QSGMII data, wait_time, + * num_of_loops + */ + /* Connect the QSGMII to Gigabit Ethernet units */ + {QSGMII_CONTROL_REG1, 0x0, 0x40000000, {0x40000000}, 0, 0}, + /* Power Up */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0xf0006, {0x80002}, 0, 0}, + /* Unreset */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x7800, {0x6000}, 0, 0}, + /* Phy Selector */ + {POWER_AND_PLL_CTRL_REG, 0x800, 0xff, {0xfc81}, 0, 0}, + /* Ref clock source select */ + {MISC_REG, 0x800, 0x4c0, {0x480}, 0, 0} +}; + +/* QSGMII - speed config seq */ +struct op_params qsgmii_port_speed_config_params[] = { + /* + * unit_base_reg, unit_offset, mask, QSGMII data, wait_time, + * num_of_loops + */ + /* Baud Rate */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x3fc00000, {0xcc00000}, 0, 0}, + /* Phy Gen RX and TX */ + {ISOLATE_REG, 0x800, 0xff, {0x33}, 0, 0}, + /* Bus Width */ + {LOOPBACK_REG, 0x800, 0xe, {0x2}, 0, 0} +}; + +/* QSGMII - Select electrical param seq */ +struct op_params qsgmii_port_electrical_config_params[] = { + /* + * unit_base_reg, unit_offset, mask, QSGMII data, wait_time, + * num_of_loops + */ + /* Slew rate and emphasis */ + {G1_SETTINGS_0_REG, 0x800, 0x8000, {0x0}, 0, 0} +}; + +/* QSGMII - TX config seq */ +struct op_params qsgmii_port_tx_config_params1[] = { + /* + * unit_base_reg, unit_offset, mask, QSGMII data, wait_time, + * num_of_loops + */ + {GLUE_REG, 0x800, 0x1800, {0x800}, 0, 0}, + /* Sft Reset pulse */ + {RESET_DFE_REG, 0x800, 0x401, {0x401}, 0, 0}, + /* Sft Reset pulse */ + {RESET_DFE_REG, 0x800, 0x401, {0x0}, 0, 0}, + /* Lane align */ + {LANE_ALIGN_REG0, 0x800, 0x1000, {0x1000}, 0, 0}, + /* Power up PLL, RX and TX */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x70000, {0x70000}, 0, 0}, + /* Tx driver output idle */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x80000, {0x80000}, 0, 0} +}; + +struct op_params qsgmii_port_tx_config_params2[] = { + /* + * unit_base_reg, unit_offset, mask, QSGMII data, wait_time, + * num_of_loops + */ + /* Wait for PHY power up sequence to finish */ + {COMMON_PHY_STATUS1_REG, 0x28, 0xc, {0xc}, 10, 1000}, + /* Assert Rx Init and Tx driver output valid */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x40080000, {0x40000000}, 0, 0}, + /* Wait for PHY power up sequence to finish */ + {COMMON_PHY_STATUS1_REG, 0x28, 0x1, {0x1}, 1, 1000}, + /* De-assert Rx Init */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x40000000, {0x0}, 0, 0} +}; + +/* SERDES_POWER_DOWN */ +struct op_params serdes_power_down_params[] = { + {COMMON_PHY_CONFIGURATION1_REG, 0x28, (0xf << 11), {(0x3 << 11)}, + 0, 0}, + {COMMON_PHY_CONFIGURATION1_REG, 0x28, (0x7 << 16), {0}, 0, 0} +}; + +/* + * hws_ctrl_serdes_rev_get + * + * DESCRIPTION: Get the Serdes revision number + * + * INPUT: config_field - Field description enum + * + * OUTPUT: None + * + * RETURN: + * 8bit Serdes revision number + */ +u8 hws_ctrl_serdes_rev_get(void) +{ +#ifdef CONFIG_ARMADA_38X + /* for A38x-Z1 */ + if (sys_env_device_rev_get() == MV_88F68XX_Z1_ID) + return MV_SERDES_REV_1_2; +#endif + + /* for A39x-Z1, A38x-A0 */ + return MV_SERDES_REV_2_1; +} + +u32 hws_serdes_topology_verify(enum serdes_type serdes_type, u32 serdes_id, + enum serdes_mode serdes_mode) +{ + u32 test_result = 0; + u8 serd_max_num, unit_numb; + enum unit_id unit_id; + + if (serdes_type > RXAUI) { + printf("%s: Warning: Wrong serdes type %s serdes#%d\n", + __func__, serdes_type_to_string[serdes_type], serdes_id); + return MV_FAIL; + } + + unit_id = serdes_type_to_unit_info[serdes_type].serdes_unit_id; + unit_numb = serdes_type_to_unit_info[serdes_type].serdes_unit_num; + serd_max_num = sys_env_unit_max_num_get(unit_id); + + /* if didn't exceed amount of required Serdes lanes for current type */ + if (serdes_lane_in_use_count[unit_id][unit_numb] != 0) { + /* update amount of required Serdes lanes for current type */ + serdes_lane_in_use_count[unit_id][unit_numb]--; + + /* + * If reached the exact amount of required Serdes lanes for + * current type + */ + if (serdes_lane_in_use_count[unit_id][unit_numb] == 0) { + if (((serdes_type <= PEX3)) && + ((serdes_mode == PEX_END_POINT_X4) || + (serdes_mode == PEX_ROOT_COMPLEX_X4))) { + /* PCiex4 uses 2 SerDes */ + serdes_unit_count[PEX_UNIT_ID] += 2; + } else { + serdes_unit_count[unit_id]++; + } + + /* test SoC unit count limitation */ + if (serdes_unit_count[unit_id] > serd_max_num) { + test_result = WRONG_NUMBER_OF_UNITS; + } else if (unit_numb >= serd_max_num) { + /* test SoC unit number limitation */ + test_result = UNIT_NUMBER_VIOLATION; + } + } + } else { + test_result = SERDES_ALREADY_IN_USE; + if (test_result == SERDES_ALREADY_IN_USE) { + printf("%s: Error: serdes lane %d is configured to type %s: type already in use\n", + __func__, serdes_id, + serdes_type_to_string[serdes_type]); + return MV_FAIL; + } else if (test_result == WRONG_NUMBER_OF_UNITS) { + printf("%s: Warning: serdes lane %d is set to type %s.\n", + __func__, serdes_id, + serdes_type_to_string[serdes_type]); + printf("%s: Maximum supported lanes are already set to this type (limit = %d)\n", + __func__, serd_max_num); + return MV_FAIL; + } else if (test_result == UNIT_NUMBER_VIOLATION) { + printf("%s: Warning: serdes lane %d type is %s: current device support only %d units of this type.\n", + __func__, serdes_id, + serdes_type_to_string[serdes_type], + serd_max_num); + return MV_FAIL; + } + } + + return MV_OK; +} + +void hws_serdes_xaui_topology_verify(void) +{ + /* + * If XAUI is in use - serdes_lane_in_use_count has to be = 0; + * if it is not in use hast be = 4 + */ + if ((serdes_lane_in_use_count[XAUI_UNIT_ID][0] != 0) && + (serdes_lane_in_use_count[XAUI_UNIT_ID][0] != 4)) { + printf("%s: Warning: wrong number of lanes is set to XAUI - %d\n", + __func__, serdes_lane_in_use_count[XAUI_UNIT_ID][0]); + printf("%s: XAUI has to be defined on 4 lanes\n", __func__); + } + + /* + * If RXAUI is in use - serdes_lane_in_use_count has to be = 0; + * if it is not in use hast be = 2 + */ + if ((serdes_lane_in_use_count[RXAUI_UNIT_ID][0] != 0) && + (serdes_lane_in_use_count[RXAUI_UNIT_ID][0] != 2)) { + printf("%s: Warning: wrong number of lanes is set to RXAUI - %d\n", + __func__, serdes_lane_in_use_count[RXAUI_UNIT_ID][0]); + printf("%s: RXAUI has to be defined on 2 lanes\n", __func__); + } +} + +int hws_serdes_seq_db_init(void) +{ + u8 serdes_rev = hws_ctrl_serdes_rev_get(); + + DEBUG_INIT_FULL_S("\n### serdes_seq38x_init ###\n"); + + if (serdes_rev == MV_SERDES_REV_NA) { + printf("hws_serdes_seq_db_init: serdes revision number is not supported\n"); + return MV_NOT_SUPPORTED; + } + + /* SATA_PORT_0_ONLY_POWER_UP_SEQ sequence init */ + serdes_seq_db[SATA_PORT_0_ONLY_POWER_UP_SEQ].op_params_ptr = + sata_port0_power_up_params; + serdes_seq_db[SATA_PORT_0_ONLY_POWER_UP_SEQ].cfg_seq_size = + sizeof(sata_port0_power_up_params) / sizeof(struct op_params); + serdes_seq_db[SATA_PORT_0_ONLY_POWER_UP_SEQ].data_arr_idx = SATA; + + /* SATA_PORT_1_ONLY_POWER_UP_SEQ sequence init */ + serdes_seq_db[SATA_PORT_1_ONLY_POWER_UP_SEQ].op_params_ptr = + sata_port1_power_up_params; + serdes_seq_db[SATA_PORT_1_ONLY_POWER_UP_SEQ].cfg_seq_size = + sizeof(sata_port1_power_up_params) / sizeof(struct op_params); + serdes_seq_db[SATA_PORT_1_ONLY_POWER_UP_SEQ].data_arr_idx = SATA; + + /* SATA_POWER_UP_SEQ sequence init */ + serdes_seq_db[SATA_POWER_UP_SEQ].op_params_ptr = + sata_and_sgmii_power_up_params; + serdes_seq_db[SATA_POWER_UP_SEQ].cfg_seq_size = + sizeof(sata_and_sgmii_power_up_params) / sizeof(struct op_params); + serdes_seq_db[SATA_POWER_UP_SEQ].data_arr_idx = SATA; + + /* SATA_1_5_SPEED_CONFIG_SEQ sequence init */ + serdes_seq_db[SATA_1_5_SPEED_CONFIG_SEQ].op_params_ptr = + sata_and_sgmii_speed_config_params; + serdes_seq_db[SATA_1_5_SPEED_CONFIG_SEQ].cfg_seq_size = + sizeof(sata_and_sgmii_speed_config_params) / + sizeof(struct op_params); + serdes_seq_db[SATA_1_5_SPEED_CONFIG_SEQ].data_arr_idx = SATA; + + /* SATA_3_SPEED_CONFIG_SEQ sequence init */ + serdes_seq_db[SATA_3_SPEED_CONFIG_SEQ].op_params_ptr = + sata_and_sgmii_speed_config_params; + serdes_seq_db[SATA_3_SPEED_CONFIG_SEQ].cfg_seq_size = + sizeof(sata_and_sgmii_speed_config_params) / + sizeof(struct op_params); + serdes_seq_db[SATA_3_SPEED_CONFIG_SEQ].data_arr_idx = SATA; + + /* SATA_6_SPEED_CONFIG_SEQ sequence init */ + serdes_seq_db[SATA_6_SPEED_CONFIG_SEQ].op_params_ptr = + sata_and_sgmii_speed_config_params; + serdes_seq_db[SATA_6_SPEED_CONFIG_SEQ].cfg_seq_size = + sizeof(sata_and_sgmii_speed_config_params) / + sizeof(struct op_params); + serdes_seq_db[SATA_6_SPEED_CONFIG_SEQ].data_arr_idx = SATA; + + /* SATA_ELECTRICAL_CONFIG_SEQ seq sequence init */ + if (serdes_rev == MV_SERDES_REV_1_2) { + serdes_seq_db[SATA_ELECTRICAL_CONFIG_SEQ].op_params_ptr = + sata_electrical_config_serdes_rev1_params; + serdes_seq_db[SATA_ELECTRICAL_CONFIG_SEQ].cfg_seq_size = + sizeof(sata_electrical_config_serdes_rev1_params) / + sizeof(struct op_params); + } else { + serdes_seq_db[SATA_ELECTRICAL_CONFIG_SEQ].op_params_ptr = + sata_electrical_config_serdes_rev2_params; + serdes_seq_db[SATA_ELECTRICAL_CONFIG_SEQ].cfg_seq_size = + sizeof(sata_electrical_config_serdes_rev2_params) / + sizeof(struct op_params); + } + serdes_seq_db[SATA_ELECTRICAL_CONFIG_SEQ].data_arr_idx = SATA; + + /* SATA_TX_CONFIG_SEQ sequence init */ + serdes_seq_db[SATA_TX_CONFIG_SEQ1].op_params_ptr = + sata_and_sgmii_tx_config_params1; + serdes_seq_db[SATA_TX_CONFIG_SEQ1].cfg_seq_size = + sizeof(sata_and_sgmii_tx_config_params1) / sizeof(struct op_params); + serdes_seq_db[SATA_TX_CONFIG_SEQ1].data_arr_idx = SATA; + + /* SATA_PORT_0_ONLY_TX_CONFIG_SEQ sequence init */ + serdes_seq_db[SATA_PORT_0_ONLY_TX_CONFIG_SEQ].op_params_ptr = + sata_port0_tx_config_params; + serdes_seq_db[SATA_PORT_0_ONLY_TX_CONFIG_SEQ].cfg_seq_size = + sizeof(sata_port0_tx_config_params) / sizeof(struct op_params); + serdes_seq_db[SATA_PORT_0_ONLY_TX_CONFIG_SEQ].data_arr_idx = SATA; + + /* SATA_PORT_1_ONLY_TX_CONFIG_SEQ sequence init */ + serdes_seq_db[SATA_PORT_1_ONLY_TX_CONFIG_SEQ].op_params_ptr = + sata_port1_tx_config_params; + serdes_seq_db[SATA_PORT_1_ONLY_TX_CONFIG_SEQ].cfg_seq_size = + sizeof(sata_port1_tx_config_params) / sizeof(struct op_params); + serdes_seq_db[SATA_PORT_1_ONLY_TX_CONFIG_SEQ].data_arr_idx = SATA; + + /* SATA_TX_CONFIG_SEQ2 sequence init */ + if (serdes_rev == MV_SERDES_REV_1_2) { + serdes_seq_db[SATA_TX_CONFIG_SEQ2].op_params_ptr = + sata_and_sgmii_tx_config_serdes_rev1_params2; + serdes_seq_db[SATA_TX_CONFIG_SEQ2].cfg_seq_size = + sizeof(sata_and_sgmii_tx_config_serdes_rev1_params2) / + sizeof(struct op_params); + } else { + serdes_seq_db[SATA_TX_CONFIG_SEQ2].op_params_ptr = + sata_and_sgmii_tx_config_serdes_rev2_params2; + serdes_seq_db[SATA_TX_CONFIG_SEQ2].cfg_seq_size = + sizeof(sata_and_sgmii_tx_config_serdes_rev2_params2) / + sizeof(struct op_params); + } + serdes_seq_db[SATA_TX_CONFIG_SEQ2].data_arr_idx = SATA; + + /* SGMII_POWER_UP_SEQ sequence init */ + serdes_seq_db[SGMII_POWER_UP_SEQ].op_params_ptr = + sata_and_sgmii_power_up_params; + serdes_seq_db[SGMII_POWER_UP_SEQ].cfg_seq_size = + sizeof(sata_and_sgmii_power_up_params) / sizeof(struct op_params); + serdes_seq_db[SGMII_POWER_UP_SEQ].data_arr_idx = SGMII; + + /* SGMII_1_25_SPEED_CONFIG_SEQ sequence init */ + serdes_seq_db[SGMII_1_25_SPEED_CONFIG_SEQ].op_params_ptr = + sata_and_sgmii_speed_config_params; + serdes_seq_db[SGMII_1_25_SPEED_CONFIG_SEQ].cfg_seq_size = + sizeof(sata_and_sgmii_speed_config_params) / + sizeof(struct op_params); + serdes_seq_db[SGMII_1_25_SPEED_CONFIG_SEQ].data_arr_idx = SGMII; + + /* SGMII_3_125_SPEED_CONFIG_SEQ sequence init */ + serdes_seq_db[SGMII_3_125_SPEED_CONFIG_SEQ].op_params_ptr = + sata_and_sgmii_speed_config_params; + serdes_seq_db[SGMII_3_125_SPEED_CONFIG_SEQ].cfg_seq_size = + sizeof(sata_and_sgmii_speed_config_params) / + sizeof(struct op_params); + serdes_seq_db[SGMII_3_125_SPEED_CONFIG_SEQ].data_arr_idx = SGMII_3_125; + + /* SGMII_ELECTRICAL_CONFIG_SEQ seq sequence init */ + if (serdes_rev == MV_SERDES_REV_1_2) { + serdes_seq_db[SGMII_ELECTRICAL_CONFIG_SEQ].op_params_ptr = + sgmii_electrical_config_serdes_rev1_params; + serdes_seq_db[SGMII_ELECTRICAL_CONFIG_SEQ].cfg_seq_size = + sizeof(sgmii_electrical_config_serdes_rev1_params) / + sizeof(struct op_params); + } else { + serdes_seq_db[SGMII_ELECTRICAL_CONFIG_SEQ].op_params_ptr = + sgmii_electrical_config_serdes_rev2_params; + serdes_seq_db[SGMII_ELECTRICAL_CONFIG_SEQ].cfg_seq_size = + sizeof(sgmii_electrical_config_serdes_rev2_params) / + sizeof(struct op_params); + } + serdes_seq_db[SGMII_ELECTRICAL_CONFIG_SEQ].data_arr_idx = SGMII; + + /* SGMII_TX_CONFIG_SEQ sequence init */ + serdes_seq_db[SGMII_TX_CONFIG_SEQ1].op_params_ptr = + sata_and_sgmii_tx_config_params1; + serdes_seq_db[SGMII_TX_CONFIG_SEQ1].cfg_seq_size = + sizeof(sata_and_sgmii_tx_config_params1) / sizeof(struct op_params); + serdes_seq_db[SGMII_TX_CONFIG_SEQ1].data_arr_idx = SGMII; + + /* SGMII_TX_CONFIG_SEQ sequence init */ + if (serdes_rev == MV_SERDES_REV_1_2) { + serdes_seq_db[SGMII_TX_CONFIG_SEQ2].op_params_ptr = + sata_and_sgmii_tx_config_serdes_rev1_params2; + serdes_seq_db[SGMII_TX_CONFIG_SEQ2].cfg_seq_size = + sizeof(sata_and_sgmii_tx_config_serdes_rev1_params2) / + sizeof(struct op_params); + } else { + serdes_seq_db[SGMII_TX_CONFIG_SEQ2].op_params_ptr = + sata_and_sgmii_tx_config_serdes_rev2_params2; + serdes_seq_db[SGMII_TX_CONFIG_SEQ2].cfg_seq_size = + sizeof(sata_and_sgmii_tx_config_serdes_rev2_params2) / + sizeof(struct op_params); + } + serdes_seq_db[SGMII_TX_CONFIG_SEQ2].data_arr_idx = SGMII; + + /* PEX_POWER_UP_SEQ sequence init */ + if (serdes_rev == MV_SERDES_REV_1_2) { + serdes_seq_db[PEX_POWER_UP_SEQ].op_params_ptr = + pex_and_usb3_power_up_serdes_rev1_params; + serdes_seq_db[PEX_POWER_UP_SEQ].cfg_seq_size = + sizeof(pex_and_usb3_power_up_serdes_rev1_params) / + sizeof(struct op_params); + } else { + serdes_seq_db[PEX_POWER_UP_SEQ].op_params_ptr = + pex_and_usb3_power_up_serdes_rev2_params; + serdes_seq_db[PEX_POWER_UP_SEQ].cfg_seq_size = + sizeof(pex_and_usb3_power_up_serdes_rev2_params) / + sizeof(struct op_params); + } + serdes_seq_db[PEX_POWER_UP_SEQ].data_arr_idx = PEX; + + /* PEX_2_5_SPEED_CONFIG_SEQ sequence init */ + serdes_seq_db[PEX_2_5_SPEED_CONFIG_SEQ].op_params_ptr = + pex_and_usb3_speed_config_params; + serdes_seq_db[PEX_2_5_SPEED_CONFIG_SEQ].cfg_seq_size = + sizeof(pex_and_usb3_speed_config_params) / sizeof(struct op_params); + serdes_seq_db[PEX_2_5_SPEED_CONFIG_SEQ].data_arr_idx = + PEXSERDES_SPEED_2_5_GBPS; + + /* PEX_5_SPEED_CONFIG_SEQ sequence init */ + serdes_seq_db[PEX_5_SPEED_CONFIG_SEQ].op_params_ptr = + pex_and_usb3_speed_config_params; + serdes_seq_db[PEX_5_SPEED_CONFIG_SEQ].cfg_seq_size = + sizeof(pex_and_usb3_speed_config_params) / sizeof(struct op_params); + serdes_seq_db[PEX_5_SPEED_CONFIG_SEQ].data_arr_idx = + PEXSERDES_SPEED_5_GBPS; + + /* PEX_ELECTRICAL_CONFIG_SEQ seq sequence init */ + if (serdes_rev == MV_SERDES_REV_1_2) { + serdes_seq_db[PEX_ELECTRICAL_CONFIG_SEQ].op_params_ptr = + pex_electrical_config_serdes_rev1_params; + serdes_seq_db[PEX_ELECTRICAL_CONFIG_SEQ].cfg_seq_size = + sizeof(pex_electrical_config_serdes_rev1_params) / + sizeof(struct op_params); + } else { + serdes_seq_db[PEX_ELECTRICAL_CONFIG_SEQ].op_params_ptr = + pex_electrical_config_serdes_rev2_params; + serdes_seq_db[PEX_ELECTRICAL_CONFIG_SEQ].cfg_seq_size = + sizeof(pex_electrical_config_serdes_rev2_params) / + sizeof(struct op_params); + } + serdes_seq_db[PEX_ELECTRICAL_CONFIG_SEQ].data_arr_idx = PEX; + + /* PEX_TX_CONFIG_SEQ1 sequence init */ + serdes_seq_db[PEX_TX_CONFIG_SEQ1].op_params_ptr = + pex_and_usb3_tx_config_params1; + serdes_seq_db[PEX_TX_CONFIG_SEQ1].cfg_seq_size = + sizeof(pex_and_usb3_tx_config_params1) / sizeof(struct op_params); + serdes_seq_db[PEX_TX_CONFIG_SEQ1].data_arr_idx = PEX; + + /* PEX_TX_CONFIG_SEQ2 sequence init */ + serdes_seq_db[PEX_TX_CONFIG_SEQ2].op_params_ptr = + pex_and_usb3_tx_config_params2; + serdes_seq_db[PEX_TX_CONFIG_SEQ2].cfg_seq_size = + sizeof(pex_and_usb3_tx_config_params2) / sizeof(struct op_params); + serdes_seq_db[PEX_TX_CONFIG_SEQ2].data_arr_idx = PEX; + + /* PEX_TX_CONFIG_SEQ3 sequence init */ + serdes_seq_db[PEX_TX_CONFIG_SEQ3].op_params_ptr = + pex_and_usb3_tx_config_params3; + serdes_seq_db[PEX_TX_CONFIG_SEQ3].cfg_seq_size = + sizeof(pex_and_usb3_tx_config_params3) / sizeof(struct op_params); + serdes_seq_db[PEX_TX_CONFIG_SEQ3].data_arr_idx = PEX; + + /* PEX_BY_4_CONFIG_SEQ sequence init */ + serdes_seq_db[PEX_BY_4_CONFIG_SEQ].op_params_ptr = + pex_by4_config_params; + serdes_seq_db[PEX_BY_4_CONFIG_SEQ].cfg_seq_size = + sizeof(pex_by4_config_params) / sizeof(struct op_params); + serdes_seq_db[PEX_BY_4_CONFIG_SEQ].data_arr_idx = PEX; + + /* PEX_CONFIG_REF_CLOCK_25MHZ_SEQ sequence init */ + serdes_seq_db[PEX_CONFIG_REF_CLOCK_25MHZ_SEQ].op_params_ptr = + pex_config_ref_clock25_m_hz; + serdes_seq_db[PEX_CONFIG_REF_CLOCK_25MHZ_SEQ].cfg_seq_size = + sizeof(pex_config_ref_clock25_m_hz) / sizeof(struct op_params); + serdes_seq_db[PEX_CONFIG_REF_CLOCK_25MHZ_SEQ].data_arr_idx = PEX; + + /* PEX_ELECTRICAL_CONFIG_REF_CLOCK_40MHZ_SEQ sequence init */ + serdes_seq_db[PEX_CONFIG_REF_CLOCK_40MHZ_SEQ].op_params_ptr = + pex_config_ref_clock40_m_hz; + serdes_seq_db[PEX_CONFIG_REF_CLOCK_40MHZ_SEQ].cfg_seq_size = + sizeof(pex_config_ref_clock40_m_hz) / sizeof(struct op_params); + serdes_seq_db[PEX_CONFIG_REF_CLOCK_40MHZ_SEQ].data_arr_idx = PEX; + + /* PEX_CONFIG_REF_CLOCK_100MHZ_SEQ sequence init */ + serdes_seq_db[PEX_CONFIG_REF_CLOCK_100MHZ_SEQ].op_params_ptr = + pex_config_ref_clock100_m_hz; + serdes_seq_db[PEX_CONFIG_REF_CLOCK_100MHZ_SEQ].cfg_seq_size = + sizeof(pex_config_ref_clock100_m_hz) / sizeof(struct op_params); + serdes_seq_db[PEX_CONFIG_REF_CLOCK_100MHZ_SEQ].data_arr_idx = PEX; + + /* USB3_POWER_UP_SEQ sequence init */ + if (serdes_rev == MV_SERDES_REV_1_2) { + serdes_seq_db[USB3_POWER_UP_SEQ].op_params_ptr = + pex_and_usb3_power_up_serdes_rev1_params; + serdes_seq_db[USB3_POWER_UP_SEQ].cfg_seq_size = + sizeof(pex_and_usb3_power_up_serdes_rev1_params) / + sizeof(struct op_params); + } else { + serdes_seq_db[USB3_POWER_UP_SEQ].op_params_ptr = + pex_and_usb3_power_up_serdes_rev2_params; + serdes_seq_db[USB3_POWER_UP_SEQ].cfg_seq_size = + sizeof(pex_and_usb3_power_up_serdes_rev2_params) / + sizeof(struct op_params); + } + serdes_seq_db[USB3_POWER_UP_SEQ].data_arr_idx = USB3; + + /* USB3_HOST_SPEED_CONFIG_SEQ sequence init */ + serdes_seq_db[USB3_HOST_SPEED_CONFIG_SEQ].op_params_ptr = + pex_and_usb3_speed_config_params; + serdes_seq_db[USB3_HOST_SPEED_CONFIG_SEQ].cfg_seq_size = + sizeof(pex_and_usb3_speed_config_params) / sizeof(struct op_params); + serdes_seq_db[USB3_HOST_SPEED_CONFIG_SEQ].data_arr_idx = + USB3SERDES_SPEED_5_GBPS_HOST; + + /* USB3_DEVICE_SPEED_CONFIG_SEQ sequence init */ + serdes_seq_db[USB3_DEVICE_SPEED_CONFIG_SEQ].op_params_ptr = + pex_and_usb3_speed_config_params; + serdes_seq_db[USB3_DEVICE_SPEED_CONFIG_SEQ].cfg_seq_size = + sizeof(pex_and_usb3_speed_config_params) / sizeof(struct op_params); + serdes_seq_db[USB3_DEVICE_SPEED_CONFIG_SEQ].data_arr_idx = + USB3SERDES_SPEED_5_GBPS_DEVICE; + + /* USB3_ELECTRICAL_CONFIG_SEQ seq sequence init */ + if (serdes_rev == MV_SERDES_REV_1_2) { + serdes_seq_db[USB3_ELECTRICAL_CONFIG_SEQ].op_params_ptr = + usb3_electrical_config_serdes_rev1_params; + serdes_seq_db[USB3_ELECTRICAL_CONFIG_SEQ].cfg_seq_size = + sizeof(usb3_electrical_config_serdes_rev1_params) / + sizeof(struct op_params); + } else { + serdes_seq_db[USB3_ELECTRICAL_CONFIG_SEQ].op_params_ptr = + usb3_electrical_config_serdes_rev2_params; + serdes_seq_db[USB3_ELECTRICAL_CONFIG_SEQ].cfg_seq_size = + sizeof(usb3_electrical_config_serdes_rev2_params) / + sizeof(struct op_params); + } + serdes_seq_db[USB3_ELECTRICAL_CONFIG_SEQ].data_arr_idx = USB3; + + /* USB3_TX_CONFIG_SEQ sequence init */ + serdes_seq_db[USB3_TX_CONFIG_SEQ1].op_params_ptr = + pex_and_usb3_tx_config_params1; + serdes_seq_db[USB3_TX_CONFIG_SEQ1].cfg_seq_size = + sizeof(pex_and_usb3_tx_config_params1) / sizeof(struct op_params); + serdes_seq_db[USB3_TX_CONFIG_SEQ1].data_arr_idx = USB3; + + /* USB3_TX_CONFIG_SEQ sequence init */ + serdes_seq_db[USB3_TX_CONFIG_SEQ2].op_params_ptr = + pex_and_usb3_tx_config_params2; + serdes_seq_db[USB3_TX_CONFIG_SEQ2].cfg_seq_size = + sizeof(pex_and_usb3_tx_config_params2) / sizeof(struct op_params); + serdes_seq_db[USB3_TX_CONFIG_SEQ2].data_arr_idx = USB3; + + /* USB3_TX_CONFIG_SEQ sequence init */ + serdes_seq_db[USB3_TX_CONFIG_SEQ3].op_params_ptr = + pex_and_usb3_tx_config_params3; + serdes_seq_db[USB3_TX_CONFIG_SEQ3].cfg_seq_size = + sizeof(pex_and_usb3_tx_config_params3) / sizeof(struct op_params); + serdes_seq_db[USB3_TX_CONFIG_SEQ3].data_arr_idx = USB3; + + /* USB2_POWER_UP_SEQ sequence init */ + serdes_seq_db[USB2_POWER_UP_SEQ].op_params_ptr = usb2_power_up_params; + serdes_seq_db[USB2_POWER_UP_SEQ].cfg_seq_size = + sizeof(usb2_power_up_params) / sizeof(struct op_params); + serdes_seq_db[USB2_POWER_UP_SEQ].data_arr_idx = 0; + + /* USB3_DEVICE_CONFIG_SEQ sequence init */ + serdes_seq_db[USB3_DEVICE_CONFIG_SEQ].op_params_ptr = + usb3_device_config_params; + serdes_seq_db[USB3_DEVICE_CONFIG_SEQ].cfg_seq_size = + sizeof(usb3_device_config_params) / sizeof(struct op_params); + serdes_seq_db[USB3_DEVICE_CONFIG_SEQ].data_arr_idx = 0; /* Not relevant */ + + /* SERDES_POWER_DOWN_SEQ sequence init */ + serdes_seq_db[SERDES_POWER_DOWN_SEQ].op_params_ptr = + serdes_power_down_params; + serdes_seq_db[SERDES_POWER_DOWN_SEQ].cfg_seq_size = + sizeof(serdes_power_down_params) / + sizeof(struct op_params); + serdes_seq_db[SERDES_POWER_DOWN_SEQ].data_arr_idx = FIRST_CELL; + + if (serdes_rev == MV_SERDES_REV_2_1) { + /* QSGMII_POWER_UP_SEQ sequence init */ + serdes_seq_db[QSGMII_POWER_UP_SEQ].op_params_ptr = + qsgmii_port_power_up_params; + serdes_seq_db[QSGMII_POWER_UP_SEQ].cfg_seq_size = + sizeof(qsgmii_port_power_up_params) / + sizeof(struct op_params); + serdes_seq_db[QSGMII_POWER_UP_SEQ].data_arr_idx = + QSGMII_SEQ_IDX; + + /* QSGMII_5_SPEED_CONFIG_SEQ sequence init */ + serdes_seq_db[QSGMII_5_SPEED_CONFIG_SEQ].op_params_ptr = + qsgmii_port_speed_config_params; + serdes_seq_db[QSGMII_5_SPEED_CONFIG_SEQ].cfg_seq_size = + sizeof(qsgmii_port_speed_config_params) / + sizeof(struct op_params); + serdes_seq_db[QSGMII_5_SPEED_CONFIG_SEQ].data_arr_idx = + QSGMII_SEQ_IDX; + + /* QSGMII_ELECTRICAL_CONFIG_SEQ seq sequence init */ + serdes_seq_db[QSGMII_ELECTRICAL_CONFIG_SEQ].op_params_ptr = + qsgmii_port_electrical_config_params; + serdes_seq_db[QSGMII_ELECTRICAL_CONFIG_SEQ].cfg_seq_size = + sizeof(qsgmii_port_electrical_config_params) / + sizeof(struct op_params); + serdes_seq_db[QSGMII_ELECTRICAL_CONFIG_SEQ].data_arr_idx = + QSGMII_SEQ_IDX; + + /* QSGMII_TX_CONFIG_SEQ sequence init */ + serdes_seq_db[QSGMII_TX_CONFIG_SEQ1].op_params_ptr = + qsgmii_port_tx_config_params1; + serdes_seq_db[QSGMII_TX_CONFIG_SEQ1].cfg_seq_size = + sizeof(qsgmii_port_tx_config_params1) / + sizeof(struct op_params); + serdes_seq_db[QSGMII_TX_CONFIG_SEQ1].data_arr_idx = + QSGMII_SEQ_IDX; + + /* QSGMII_TX_CONFIG_SEQ sequence init */ + serdes_seq_db[QSGMII_TX_CONFIG_SEQ2].op_params_ptr = + qsgmii_port_tx_config_params2; + serdes_seq_db[QSGMII_TX_CONFIG_SEQ2].cfg_seq_size = + sizeof(qsgmii_port_tx_config_params2) / + sizeof(struct op_params); + serdes_seq_db[QSGMII_TX_CONFIG_SEQ2].data_arr_idx = + QSGMII_SEQ_IDX; + } + + return MV_OK; +} + +enum serdes_seq serdes_type_and_speed_to_speed_seq(enum serdes_type serdes_type, + enum serdes_speed baud_rate) +{ + enum serdes_seq seq_id = SERDES_LAST_SEQ; + + DEBUG_INIT_FULL_S("\n### serdes_type_and_speed_to_speed_seq ###\n"); + switch (serdes_type) { + case PEX0: + case PEX1: + case PEX2: + case PEX3: + if (baud_rate == SERDES_SPEED_2_5_GBPS) + seq_id = PEX_2_5_SPEED_CONFIG_SEQ; + else if (baud_rate == SERDES_SPEED_5_GBPS) + seq_id = PEX_5_SPEED_CONFIG_SEQ; + break; + case USB3_HOST0: + case USB3_HOST1: + if (baud_rate == SERDES_SPEED_5_GBPS) + seq_id = USB3_HOST_SPEED_CONFIG_SEQ; + break; + case USB3_DEVICE: + if (baud_rate == SERDES_SPEED_5_GBPS) + seq_id = USB3_DEVICE_SPEED_CONFIG_SEQ; + break; + case SATA0: + case SATA1: + case SATA2: + case SATA3: + if (baud_rate == SERDES_SPEED_1_5_GBPS) + seq_id = SATA_1_5_SPEED_CONFIG_SEQ; + else if (baud_rate == SERDES_SPEED_3_GBPS) + seq_id = SATA_3_SPEED_CONFIG_SEQ; + else if (baud_rate == SERDES_SPEED_6_GBPS) + seq_id = SATA_6_SPEED_CONFIG_SEQ; + break; + case SGMII0: + case SGMII1: + case SGMII2: +#ifdef CONFIG_ARMADA_39X + case SGMII3: +#endif + if (baud_rate == SERDES_SPEED_1_25_GBPS) + seq_id = SGMII_1_25_SPEED_CONFIG_SEQ; + else if (baud_rate == SERDES_SPEED_3_125_GBPS) + seq_id = SGMII_3_125_SPEED_CONFIG_SEQ; + break; + case QSGMII: + seq_id = QSGMII_5_SPEED_CONFIG_SEQ; + break; +#ifdef CONFIG_ARMADA_39X + case XAUI: + seq_id = XAUI_3_125_SPEED_CONFIG_SEQ; + break; + case RXAUI: + seq_id = RXAUI_6_25_SPEED_CONFIG_SEQ; + break; +#endif + default: + return SERDES_LAST_SEQ; + } + + return seq_id; +} + +/* + * This is the weak default function for the Marvell evaluation or + * development boarrds. Like the DB-88F6820-GP and others. + * Custom boards should define this function in their board + * code (board directory). And overwrite this default function + * with this custom specific code. + */ +__weak int hws_board_topology_load(struct serdes_map *serdes_map_array) +{ + u32 board_id = mv_board_id_get(); + u32 board_id_index = mv_board_id_index_get(board_id); + + DEBUG_INIT_FULL_S("\n### hws_board_topology_load ###\n"); + /* getting board topology according to the board id */ + DEBUG_INIT_FULL_S("Getting board topology according to the board id\n"); + + CHECK_STATUS(load_topology_func_arr[board_id_index] (serdes_map_array)); + + return MV_OK; +} + +void print_topology_details(struct serdes_map *serdes_map_array) +{ + u32 lane_num; + + DEBUG_INIT_S("board SerDes lanes topology details:\n"); + + DEBUG_INIT_S(" | Lane # | Speed | Type |\n"); + DEBUG_INIT_S(" --------------------------------\n"); + for (lane_num = 0; lane_num < hws_serdes_get_max_lane(); lane_num++) { + if (serdes_map_array[lane_num].serdes_type == DEFAULT_SERDES) + continue; + DEBUG_INIT_S(" | "); + DEBUG_INIT_D(hws_get_physical_serdes_num(lane_num), 1); + DEBUG_INIT_S(" | "); + DEBUG_INIT_D(serdes_map_array[lane_num].serdes_speed, 2); + DEBUG_INIT_S(" | "); + DEBUG_INIT_S((char *) + serdes_type_to_string[serdes_map_array[lane_num]. + serdes_type]); + DEBUG_INIT_S("\t|\n"); + } + DEBUG_INIT_S(" --------------------------------\n"); +} + +int hws_pre_serdes_init_config(void) +{ + u32 data; + + /* + * Configure Core PLL + */ + /* + * set PLL parameters + * bits[2:0] =0x3 (Core-PLL Kdiv) + * bits[20:12]=0x9f (Core-PLL Ndiv) + * bits[24:21]=0x7(Core-PLL VCO Band) + * bits[28:25]=0x1(Core-PLL Rlf) + * bits[31:29]=0x2(Core-PLL charge-pump adjust) + */ + reg_write(CORE_PLL_PARAMETERS_REG, 0x42e9f003); + + /* Enable PLL Configuration */ + data = reg_read(CORE_PLL_CONFIG_REG); + data = SET_BIT(data, 9); + reg_write(CORE_PLL_CONFIG_REG, data); + + return MV_OK; +} + +int serdes_phy_config(void) +{ + DEBUG_INIT_FULL_S("\n### ctrl_high_speed_serdes_phy_config ###\n"); + + DEBUG_INIT_S("High speed PHY - Version: "); + DEBUG_INIT_S(SERDES_VERION); + DEBUG_INIT_S("\n"); + + /* Init serdes sequences DB */ + if (hws_serdes_seq_init() != MV_OK) { + printf("hws_ctrl_high_speed_serdes_phy_config: Error: Serdes initialization fail\n"); + return MV_FAIL; + } + + /* I2C init */ + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + + /* Board topology load */ + DEBUG_INIT_FULL_S + ("ctrl_high_speed_serdes_phy_config: Loading board topology..\n"); + CHECK_STATUS(hws_board_topology_load(serdes_configuration_map)); + + /* print topology */ + print_topology_details(serdes_configuration_map); + CHECK_STATUS(hws_pre_serdes_init_config()); + + /* Power-Up sequence */ + DEBUG_INIT_FULL_S + ("ctrl_high_speed_serdes_phy_config: Starting serdes power up sequence\n"); + + CHECK_STATUS(hws_power_up_serdes_lanes(serdes_configuration_map)); + + DEBUG_INIT_FULL_S + ("\n### ctrl_high_speed_serdes_phy_config ended successfully ###\n"); + + DEBUG_INIT_S(ENDED_OK); + + return MV_OK; +} + +int serdes_polarity_config(u32 serdes_num, int is_rx) +{ + u32 data; + u32 reg_addr; + u8 bit_off = (is_rx) ? 11 : 10; + + reg_addr = SERDES_REGS_LANE_BASE_OFFSET(serdes_num) + SYNC_PATTERN_REG; + data = reg_read(reg_addr); + data = SET_BIT(data, bit_off); + reg_write(reg_addr, data); + + return MV_OK; +} + +int hws_power_up_serdes_lanes(struct serdes_map *serdes_config_map) +{ + u32 serdes_id, serdes_lane_num; + enum ref_clock ref_clock; + enum serdes_type serdes_type; + enum serdes_speed serdes_speed; + enum serdes_mode serdes_mode; + int serdes_rx_polarity_swap; + int serdes_tx_polarity_swap; + int is_pex_enabled = 0; + + /* + * is_pex_enabled: + * Flag which indicates that one of the Serdes is of PEX. + * In this case, PEX unit will be initialized after Serdes power-up + */ + + DEBUG_INIT_FULL_S("\n### hws_power_up_serdes_lanes ###\n"); + + /* COMMON PHYS SELECTORS register configuration */ + DEBUG_INIT_FULL_S + ("hws_power_up_serdes_lanes: Updating COMMON PHYS SELECTORS reg\n"); + CHECK_STATUS(hws_update_serdes_phy_selectors(serdes_configuration_map)); + + /* per Serdes Power Up */ + for (serdes_id = 0; serdes_id < hws_serdes_get_max_lane(); + serdes_id++) { + DEBUG_INIT_FULL_S + ("calling serdes_power_up_ctrl: serdes lane number "); + DEBUG_INIT_FULL_D_10(serdes_lane_num, 1); + DEBUG_INIT_FULL_S("\n"); + + serdes_lane_num = hws_get_physical_serdes_num(serdes_id); + serdes_type = serdes_config_map[serdes_id].serdes_type; + serdes_speed = serdes_config_map[serdes_id].serdes_speed; + serdes_mode = serdes_config_map[serdes_id].serdes_mode; + serdes_rx_polarity_swap = serdes_config_map[serdes_id].swap_rx; + serdes_tx_polarity_swap = serdes_config_map[serdes_id].swap_tx; + + /* serdes lane is not in use */ + if (serdes_type == DEFAULT_SERDES) + continue; + else if (serdes_type <= PEX3) /* PEX type */ + is_pex_enabled = 1; + + ref_clock = hws_serdes_get_ref_clock_val(serdes_type); + if (ref_clock == REF_CLOCK_UNSUPPORTED) { + DEBUG_INIT_S + ("hws_power_up_serdes_lanes: unsupported ref clock\n"); + return MV_NOT_SUPPORTED; + } + CHECK_STATUS(serdes_power_up_ctrl(serdes_lane_num, + 1, + serdes_type, + serdes_speed, + serdes_mode, ref_clock)); + + /* RX Polarity config */ + if (serdes_rx_polarity_swap) + CHECK_STATUS(serdes_polarity_config + (serdes_lane_num, 1)); + + /* TX Polarity config */ + if (serdes_tx_polarity_swap) + CHECK_STATUS(serdes_polarity_config + (serdes_lane_num, 0)); + } + + if (is_pex_enabled) { + /* Set PEX_TX_CONFIG_SEQ sequence for PEXx4 mode. + After finish the Power_up sequence for all lanes, + the lanes should be released from reset state. */ + CHECK_STATUS(hws_pex_tx_config_seq(serdes_config_map)); + + /* PEX configuration */ + CHECK_STATUS(hws_pex_config(serdes_config_map)); + } + + /* USB2 configuration */ + DEBUG_INIT_FULL_S("hws_power_up_serdes_lanes: init USB2 Phys\n"); + CHECK_STATUS(mv_seq_exec(0 /* not relevant */ , USB2_POWER_UP_SEQ)); + + DEBUG_INIT_FULL_S + ("### hws_power_up_serdes_lanes ended successfully ###\n"); + + return MV_OK; +} + +int ctrl_high_speed_serdes_phy_config(void) +{ + return hws_ctrl_high_speed_serdes_phy_config(); +} + +static int serdes_pex_usb3_pipe_delay_w_a(u32 serdes_num, u8 serdes_type) +{ + u32 reg_data; + + /* WA for A380 Z1 relevant for lanes 3,4,5 only */ + if (serdes_num >= 3) { + reg_data = reg_read(GENERAL_PURPOSE_RESERVED0_REG); + /* set delay on pipe - + * When lane 3 is connected to a MAC of Pex -> set bit 7 to 1. + * When lane 3 is connected to a MAC of USB3 -> set bit 7 to 0. + * When lane 4 is connected to a MAC of Pex -> set bit 8 to 1. + * When lane 4 is connected to a MAC of USB3 -> set bit 8 to 0. + * When lane 5 is connected to a MAC of Pex -> set bit 8 to 1. + * When lane 5 is connected to a MAC of USB3 -> set bit 8 to 0. + */ + if (serdes_type == PEX) + reg_data |= 1 << (7 + (serdes_num - 3)); + if (serdes_type == USB3) { + /* USB3 */ + reg_data &= ~(1 << (7 + (serdes_num - 3))); + } + reg_write(GENERAL_PURPOSE_RESERVED0_REG, reg_data); + } + + return MV_OK; +} + +/* + * hws_serdes_pex_ref_clock_satr_get - + * + * DESCRIPTION: Get the reference clock value from DEVICE_SAMPLE_AT_RESET1_REG + * and check: + * bit[2] for PEX#0, bit[3] for PEX#1, bit[30] for PEX#2, bit[31] + * for PEX#3. + * If bit=0 --> REF_CLOCK_100MHz + * If bit=1 && DEVICE_SAMPLE_AT_RESET2_REG bit[0]=0 + * --> REF_CLOCK_25MHz + * If bit=1 && DEVICE_SAMPLE_AT_RESET2_REG bit[0]=1 + * --> REF_CLOCK_40MHz + * + * INPUT: serdes_type - Type of Serdes + * + * OUTPUT: pex_satr - Return the REF_CLOCK value: + * REF_CLOCK_25MHz, REF_CLOCK_40MHz or REF_CLOCK_100MHz + * + * RETURNS: MV_OK - for success + * MV_BAD_PARAM - for fail + */ +int hws_serdes_pex_ref_clock_satr_get(enum serdes_type serdes_type, u32 *pex_satr) +{ + u32 data, reg_satr1; + + reg_satr1 = reg_read(DEVICE_SAMPLE_AT_RESET1_REG); + + switch (serdes_type) { + case PEX0: + data = REF_CLK_SELECTOR_VAL_PEX0(reg_satr1); + break; + case PEX1: + data = REF_CLK_SELECTOR_VAL_PEX1(reg_satr1); + break; + case PEX2: + data = REF_CLK_SELECTOR_VAL_PEX2(reg_satr1); + break; + case PEX3: + data = REF_CLK_SELECTOR_VAL_PEX3(reg_satr1); + break; + default: + printf("%s: Error: SerDes type %d is not supported\n", + __func__, serdes_type); + return MV_BAD_PARAM; + } + + *pex_satr = data; + + return MV_OK; +} + +u32 hws_serdes_get_ref_clock_val(enum serdes_type serdes_type) +{ + u32 pex_satr; + enum ref_clock ref_clock; + + DEBUG_INIT_FULL_S("\n### hws_serdes_get_ref_clock_val ###\n"); + + if (serdes_type >= LAST_SERDES_TYPE) + return REF_CLOCK_UNSUPPORTED; + + /* read ref clock from S@R */ + ref_clock = hws_serdes_silicon_ref_clock_get(); + + if (serdes_type > PEX3) { + /* for all Serdes types but PCIe */ + return ref_clock; + } + + /* for PCIe, need also to check PCIe S@R */ + CHECK_STATUS(hws_serdes_pex_ref_clock_satr_get + (serdes_type, &pex_satr)); + + if (pex_satr == 0) { + return REF_CLOCK_100MHZ; + } else if (pex_satr == 1) { + /* value of 1 means we can use ref clock from SoC (as other Serdes types) */ + return ref_clock; + } else { + printf + ("%s: Error: REF_CLK_SELECTOR_VAL for SerDes type %d is wrong\n", + __func__, serdes_type); + return REF_CLOCK_UNSUPPORTED; + } +} + +int serdes_power_up_ctrl(u32 serdes_num, int serdes_power_up, + enum serdes_type serdes_type, + enum serdes_speed baud_rate, + enum serdes_mode serdes_mode, enum ref_clock ref_clock) +{ + u32 sata_idx, pex_idx, sata_port; + enum serdes_seq speed_seq_id; + u32 reg_data; + int is_pex_by1; + + DEBUG_INIT_FULL_S("\n### serdes_power_up_ctrl ###\n"); + + if (serdes_power_up == 1) { /* Serdes power up */ + DEBUG_INIT_FULL_S + ("serdes_power_up_ctrl: executing power up.. "); + DEBUG_INIT_FULL_C("serdes num = ", serdes_num, 2); + DEBUG_INIT_FULL_C("serdes type = ", serdes_type, 2); + + DEBUG_INIT_FULL_S("Going access 1"); + + /* Getting the Speed Select sequence id */ + speed_seq_id = + serdes_type_and_speed_to_speed_seq(serdes_type, + baud_rate); + if (speed_seq_id == SERDES_LAST_SEQ) { + printf + ("serdes_power_up_ctrl: serdes type %d and speed %d are not supported together\n", + serdes_type, baud_rate); + + return MV_BAD_PARAM; + } + + /* Executing power up, ref clock set, speed config and TX config */ + switch (serdes_type) { + case PEX0: + case PEX1: + case PEX2: + case PEX3: + if (hws_ctrl_serdes_rev_get() == MV_SERDES_REV_1_2) { + CHECK_STATUS(serdes_pex_usb3_pipe_delay_w_a + (serdes_num, PEX)); + } + + is_pex_by1 = (serdes_mode == PEX_ROOT_COMPLEX_X1) || + (serdes_mode == PEX_END_POINT_X1); + pex_idx = serdes_type - PEX0; + + if ((is_pex_by1 == 1) || (serdes_type == PEX0)) { + /* For PEX by 4, init only the PEX 0 */ + reg_data = reg_read(SOC_CONTROL_REG1); + if (is_pex_by1 == 1) + reg_data |= 0x4000; + else + reg_data &= ~0x4000; + reg_write(SOC_CONTROL_REG1, reg_data); + + reg_data = + reg_read(((PEX_IF_REGS_BASE(pex_idx)) + + 0x6c)); + reg_data &= ~0x3f0; + if (is_pex_by1 == 1) + reg_data |= 0x10; + else + reg_data |= 0x40; + reg_write(((PEX_IF_REGS_BASE(pex_idx)) + 0x6c), + reg_data); + + reg_data = + reg_read(((PEX_IF_REGS_BASE(pex_idx)) + + 0x6c)); + reg_data &= ~0xf; + reg_data |= 0x2; + reg_write(((PEX_IF_REGS_BASE(pex_idx)) + 0x6c), + reg_data); + + reg_data = + reg_read(((PEX_IF_REGS_BASE(pex_idx)) + + 0x70)); + reg_data &= ~0x40; + reg_data |= 0x40; + reg_write(((PEX_IF_REGS_BASE(pex_idx)) + 0x70), + reg_data); + } + + CHECK_STATUS(mv_seq_exec(serdes_num, PEX_POWER_UP_SEQ)); + if (is_pex_by1 == 0) { + /* + * for PEX by 4 - use the PEX index as the + * seq array index + */ + serdes_seq_db[PEX_BY_4_CONFIG_SEQ]. + data_arr_idx = pex_idx; + CHECK_STATUS(mv_seq_exec + (serdes_num, PEX_BY_4_CONFIG_SEQ)); + } + + CHECK_STATUS(hws_ref_clock_set + (serdes_num, serdes_type, ref_clock)); + CHECK_STATUS(mv_seq_exec(serdes_num, speed_seq_id)); + CHECK_STATUS(mv_seq_exec + (serdes_num, PEX_ELECTRICAL_CONFIG_SEQ)); + + if (is_pex_by1 == 1) { + CHECK_STATUS(mv_seq_exec + (serdes_num, PEX_TX_CONFIG_SEQ2)); + CHECK_STATUS(mv_seq_exec + (serdes_num, PEX_TX_CONFIG_SEQ3)); + CHECK_STATUS(mv_seq_exec + (serdes_num, PEX_TX_CONFIG_SEQ1)); + } + udelay(20); + + break; + case USB3_HOST0: + case USB3_HOST1: + case USB3_DEVICE: + if (hws_ctrl_serdes_rev_get() == MV_SERDES_REV_1_2) { + CHECK_STATUS(serdes_pex_usb3_pipe_delay_w_a + (serdes_num, USB3)); + } + CHECK_STATUS(mv_seq_exec + (serdes_num, USB3_POWER_UP_SEQ)); + CHECK_STATUS(hws_ref_clock_set + (serdes_num, serdes_type, ref_clock)); + CHECK_STATUS(mv_seq_exec(serdes_num, speed_seq_id)); + if (serdes_type == USB3_DEVICE) { + CHECK_STATUS(mv_seq_exec + (serdes_num, + USB3_DEVICE_CONFIG_SEQ)); + } + CHECK_STATUS(mv_seq_exec + (serdes_num, USB3_ELECTRICAL_CONFIG_SEQ)); + CHECK_STATUS(mv_seq_exec + (serdes_num, USB3_TX_CONFIG_SEQ1)); + CHECK_STATUS(mv_seq_exec + (serdes_num, USB3_TX_CONFIG_SEQ2)); + CHECK_STATUS(mv_seq_exec + (serdes_num, USB3_TX_CONFIG_SEQ3)); + + udelay(10000); + break; + case SATA0: + case SATA1: + case SATA2: + case SATA3: + sata_idx = ((serdes_type == SATA0) || + (serdes_type == SATA1)) ? 0 : 1; + sata_port = ((serdes_type == SATA0) || + (serdes_type == SATA2)) ? 0 : 1; + + CHECK_STATUS(mv_seq_exec + (sata_idx, (sata_port == 0) ? + SATA_PORT_0_ONLY_POWER_UP_SEQ : + SATA_PORT_1_ONLY_POWER_UP_SEQ)); + CHECK_STATUS(mv_seq_exec + (serdes_num, SATA_POWER_UP_SEQ)); + CHECK_STATUS(hws_ref_clock_set + (serdes_num, serdes_type, ref_clock)); + CHECK_STATUS(mv_seq_exec(serdes_num, speed_seq_id)); + CHECK_STATUS(mv_seq_exec + (serdes_num, SATA_ELECTRICAL_CONFIG_SEQ)); + CHECK_STATUS(mv_seq_exec + (serdes_num, SATA_TX_CONFIG_SEQ1)); + CHECK_STATUS(mv_seq_exec + (sata_idx, (sata_port == 0) ? + SATA_PORT_0_ONLY_TX_CONFIG_SEQ : + SATA_PORT_1_ONLY_TX_CONFIG_SEQ)); + CHECK_STATUS(mv_seq_exec + (serdes_num, SATA_TX_CONFIG_SEQ2)); + + udelay(10000); + break; + case SGMII0: + case SGMII1: + case SGMII2: + CHECK_STATUS(mv_seq_exec + (serdes_num, SGMII_POWER_UP_SEQ)); + CHECK_STATUS(hws_ref_clock_set + (serdes_num, serdes_type, ref_clock)); + CHECK_STATUS(mv_seq_exec(serdes_num, speed_seq_id)); + CHECK_STATUS(mv_seq_exec + (serdes_num, SGMII_ELECTRICAL_CONFIG_SEQ)); + CHECK_STATUS(mv_seq_exec + (serdes_num, SGMII_TX_CONFIG_SEQ1)); + CHECK_STATUS(mv_seq_exec + (serdes_num, SGMII_TX_CONFIG_SEQ2)); + + /* GBE configuration */ + reg_data = reg_read(GBE_CONFIGURATION_REG); + /* write the SGMII index */ + reg_data |= 0x1 << (serdes_type - SGMII0); + reg_write(GBE_CONFIGURATION_REG, reg_data); + + break; + case QSGMII: + if (hws_ctrl_serdes_rev_get() < MV_SERDES_REV_2_1) + return MV_NOT_SUPPORTED; + + CHECK_STATUS(mv_seq_exec + (serdes_num, QSGMII_POWER_UP_SEQ)); + CHECK_STATUS(hws_ref_clock_set + (serdes_num, serdes_type, ref_clock)); + CHECK_STATUS(mv_seq_exec(serdes_num, speed_seq_id)); + CHECK_STATUS(mv_seq_exec + (serdes_num, + QSGMII_ELECTRICAL_CONFIG_SEQ)); + CHECK_STATUS(mv_seq_exec + (serdes_num, QSGMII_TX_CONFIG_SEQ1)); + CHECK_STATUS(mv_seq_exec + (serdes_num, QSGMII_TX_CONFIG_SEQ2)); + break; + case SGMII3: + case XAUI: + case RXAUI: + CHECK_STATUS(serdes_power_up_ctrl_ext + (serdes_num, serdes_power_up, serdes_type, + baud_rate, serdes_mode, ref_clock)); + break; + default: + DEBUG_INIT_S + ("serdes_power_up_ctrl: bad serdes_type parameter\n"); + return MV_BAD_PARAM; + } + } else { /* Serdes power down */ + DEBUG_INIT_FULL_S("serdes_power_up: executing power down.. "); + DEBUG_INIT_FULL_C("serdes num = ", serdes_num, 1); + + CHECK_STATUS(mv_seq_exec(serdes_num, SERDES_POWER_DOWN_SEQ)); + } + + DEBUG_INIT_FULL_C( + "serdes_power_up_ctrl ended successfully for serdes ", + serdes_num, 2); + + return MV_OK; +} + +int hws_update_serdes_phy_selectors(struct serdes_map *serdes_config_map) +{ + u32 lane_data, idx, serdes_lane_hw_num, reg_data = 0; + enum serdes_type serdes_type; + enum serdes_mode serdes_mode; + u8 select_bit_off; + int is_pex_x4 = 0; + int updated_topology_print = 0; + + DEBUG_INIT_FULL_S("\n### hws_update_serdes_phy_selectors ###\n"); + DEBUG_INIT_FULL_S + ("Updating the COMMON PHYS SELECTORS register with the serdes types\n"); + + if (hws_ctrl_serdes_rev_get() == MV_SERDES_REV_1_2) + select_bit_off = 3; + else + select_bit_off = 4; + + /* + * Updating bits 0-17 in the COMMON PHYS SELECTORS register + * according to the serdes types + */ + for (idx = 0; idx < hws_serdes_get_max_lane(); + idx++) { + serdes_type = serdes_config_map[idx].serdes_type; + serdes_mode = serdes_config_map[idx].serdes_mode; + serdes_lane_hw_num = hws_get_physical_serdes_num(idx); + + lane_data = + hws_serdes_get_phy_selector_val(serdes_lane_hw_num, + serdes_type); + + if (serdes_type == DEFAULT_SERDES) + continue; + + if (hws_serdes_topology_verify + (serdes_type, idx, serdes_mode) != MV_OK) { + serdes_config_map[idx].serdes_type = + DEFAULT_SERDES; + printf("%s: SerDes lane #%d is disabled\n", __func__, + serdes_lane_hw_num); + updated_topology_print = 1; + continue; + } + + /* + * Checking if the board topology configuration includes + * PEXx4 - for the next step + */ + if ((serdes_mode == PEX_END_POINT_X4) || + (serdes_mode == PEX_ROOT_COMPLEX_X4)) { + /* update lane data to the 3 next SERDES lanes */ + lane_data = + common_phys_selectors_pex_by4_lanes + [serdes_lane_hw_num]; + if (serdes_type == PEX0) + is_pex_x4 = 1; + } + + if (lane_data == NA) { + printf + ("%s: Warning: SerDes lane #%d and type %d are not supported together\n", + __func__, serdes_lane_hw_num, serdes_mode); + serdes_config_map[idx].serdes_type = + DEFAULT_SERDES; + printf("%s: SerDes lane #%d is disabled\n", __func__, + serdes_lane_hw_num); + continue; + } + + /* + * Updating the data that will be written to + * COMMON_PHYS_SELECTORS_REG + */ + reg_data |= (lane_data << + (select_bit_off * serdes_lane_hw_num)); + } + + /* + * Check that number of used lanes for XAUI and RXAUI + * (if used) is right + */ + hws_serdes_xaui_topology_verify(); + + /* Print topology */ + if (updated_topology_print) + print_topology_details(serdes_config_map); + + /* + * Updating the PEXx4 Enable bit in the COMMON PHYS SELECTORS + * register for PEXx4 mode + */ + reg_data |= (is_pex_x4 == 1) ? (0x1 << PEX_X4_ENABLE_OFFS) : 0; + + /* Updating the COMMON PHYS SELECTORS register */ + reg_write(COMMON_PHYS_SELECTORS_REG, reg_data); + + return MV_OK; +} + +int hws_ref_clock_set(u32 serdes_num, enum serdes_type serdes_type, + enum ref_clock ref_clock) +{ + u32 data1 = 0, data2 = 0, data3 = 0, reg_data; + + DEBUG_INIT_FULL_S("\n### hws_ref_clock_set ###\n"); + + if (hws_is_serdes_active(serdes_num) != 1) { + printf("%s: SerDes lane #%d is not Active\n", __func__, + serdes_num); + return MV_BAD_PARAM; + } + + switch (serdes_type) { + case PEX0: + case PEX1: + case PEX2: + case PEX3: + switch (ref_clock) { + case REF_CLOCK_25MHZ: + CHECK_STATUS(mv_seq_exec + (serdes_num, + PEX_CONFIG_REF_CLOCK_25MHZ_SEQ)); + return MV_OK; + case REF_CLOCK_100MHZ: + CHECK_STATUS(mv_seq_exec + (serdes_num, + PEX_CONFIG_REF_CLOCK_100MHZ_SEQ)); + return MV_OK; +#ifdef CONFIG_ARMADA_39X + case REF_CLOCK_40MHZ: + CHECK_STATUS(mv_seq_exec + (serdes_num, + PEX_CONFIG_REF_CLOCK_40MHZ_SEQ)); + return MV_OK; +#endif + default: + printf + ("%s: Error: ref_clock %d for SerDes lane #%d, type %d is not supported\n", + __func__, ref_clock, serdes_num, serdes_type); + return MV_BAD_PARAM; + } + case USB3_HOST0: + case USB3_HOST1: + case USB3_DEVICE: + if (ref_clock == REF_CLOCK_25MHZ) { + data1 = POWER_AND_PLL_CTRL_REG_25MHZ_VAL_2; + data2 = GLOBAL_PM_CTRL_REG_25MHZ_VAL; + data3 = LANE_CFG4_REG_25MHZ_VAL; + } else if (ref_clock == REF_CLOCK_40MHZ) { + data1 = POWER_AND_PLL_CTRL_REG_40MHZ_VAL; + data2 = GLOBAL_PM_CTRL_REG_40MHZ_VAL; + data3 = LANE_CFG4_REG_40MHZ_VAL; + } else { + printf + ("hws_ref_clock_set: ref clock is not valid for serdes type %d\n", + serdes_type); + return MV_BAD_PARAM; + } + break; + case SATA0: + case SATA1: + case SATA2: + case SATA3: + case SGMII0: + case SGMII1: + case SGMII2: + case QSGMII: + if (ref_clock == REF_CLOCK_25MHZ) { + data1 = POWER_AND_PLL_CTRL_REG_25MHZ_VAL_1; + } else if (ref_clock == REF_CLOCK_40MHZ) { + data1 = POWER_AND_PLL_CTRL_REG_40MHZ_VAL; + } else { + printf + ("hws_ref_clock_set: ref clock is not valid for serdes type %d\n", + serdes_type); + return MV_BAD_PARAM; + } + break; +#ifdef CONFIG_ARMADA_39X + case SGMII3: + case XAUI: + case RXAUI: + if (ref_clock == REF_CLOCK_25MHZ) { + data1 = POWER_AND_PLL_CTRL_REG_25MHZ_VAL_1; + } else if (ref_clock == REF_CLOCK_40MHZ) { + data1 = POWER_AND_PLL_CTRL_REG_40MHZ_VAL; + } else { + printf + ("hws_ref_clock_set: ref clock is not valid for serdes type %d\n", + serdes_type); + return MV_BAD_PARAM; + } + break; +#endif + default: + DEBUG_INIT_S("hws_ref_clock_set: not supported serdes type\n"); + return MV_BAD_PARAM; + } + + /* + * Write the ref_clock to relevant SELECT_REF_CLOCK_REG bits and + * offset + */ + reg_data = reg_read(POWER_AND_PLL_CTRL_REG + + SERDES_REGS_LANE_BASE_OFFSET(serdes_num)); + reg_data &= POWER_AND_PLL_CTRL_REG_MASK; + reg_data |= data1; + reg_write(POWER_AND_PLL_CTRL_REG + + SERDES_REGS_LANE_BASE_OFFSET(serdes_num), reg_data); + + if ((serdes_type == USB3_HOST0) || (serdes_type == USB3_HOST1) || + (serdes_type == USB3_DEVICE)) { + reg_data = reg_read(GLOBAL_PM_CTRL + + SERDES_REGS_LANE_BASE_OFFSET(serdes_num)); + reg_data &= GLOBAL_PM_CTRL_REG_MASK; + reg_data |= data2; + reg_write(GLOBAL_PM_CTRL + + SERDES_REGS_LANE_BASE_OFFSET(serdes_num), reg_data); + + reg_data = reg_read(LANE_CFG4_REG + + SERDES_REGS_LANE_BASE_OFFSET(serdes_num)); + reg_data &= LANE_CFG4_REG_MASK; + reg_data |= data3; + reg_write(LANE_CFG4_REG + + SERDES_REGS_LANE_BASE_OFFSET(serdes_num), reg_data); + } + + return MV_OK; +} + +/* + * hws_pex_tx_config_seq - + * + * DESCRIPTION: Set PEX_TX_CONFIG_SEQ sequence init for PEXx4 mode + * INPUT: serdes_map - The board topology map + * OUTPUT: None + * RETURNS: MV_OK - for success + * MV_BAD_PARAM - for fail + */ +int hws_pex_tx_config_seq(struct serdes_map *serdes_map) +{ + enum serdes_mode serdes_mode; + u32 serdes_lane_id, serdes_lane_hw_num; + + DEBUG_INIT_FULL_S("\n### hws_pex_tx_config_seq ###\n"); + + /* + * For PEXx4: the pex_and_usb3_tx_config_params1/2/3 + * configurations should run by setting each sequence for + * all 4 lanes. + */ + + /* relese pipe soft reset for all lanes */ + for (serdes_lane_id = 0; serdes_lane_id < hws_serdes_get_max_lane(); + serdes_lane_id++) { + serdes_mode = serdes_map[serdes_lane_id].serdes_mode; + serdes_lane_hw_num = + hws_get_physical_serdes_num(serdes_lane_id); + + if ((serdes_mode == PEX_ROOT_COMPLEX_X4) || + (serdes_mode == PEX_END_POINT_X4)) { + CHECK_STATUS(mv_seq_exec + (serdes_lane_hw_num, PEX_TX_CONFIG_SEQ1)); + } + } + + /* set phy soft reset for all lanes */ + for (serdes_lane_id = 0; serdes_lane_id < hws_serdes_get_max_lane(); + serdes_lane_id++) { + serdes_mode = serdes_map[serdes_lane_id].serdes_mode; + serdes_lane_hw_num = + hws_get_physical_serdes_num(serdes_lane_id); + if ((serdes_mode == PEX_ROOT_COMPLEX_X4) || + (serdes_mode == PEX_END_POINT_X4)) { + CHECK_STATUS(mv_seq_exec + (serdes_lane_hw_num, PEX_TX_CONFIG_SEQ2)); + } + } + + /* set phy soft reset for all lanes */ + for (serdes_lane_id = 0; serdes_lane_id < hws_serdes_get_max_lane(); + serdes_lane_id++) { + serdes_mode = serdes_map[serdes_lane_id].serdes_mode; + serdes_lane_hw_num = + hws_get_physical_serdes_num(serdes_lane_id); + if ((serdes_mode == PEX_ROOT_COMPLEX_X4) || + (serdes_mode == PEX_END_POINT_X4)) { + CHECK_STATUS(mv_seq_exec + (serdes_lane_hw_num, PEX_TX_CONFIG_SEQ3)); + } + } + + return MV_OK; +} diff --git a/arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec.h b/arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec.h new file mode 100644 index 00000000000..25087210ec2 --- /dev/null +++ b/arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec.h @@ -0,0 +1,251 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _HIGH_SPEED_ENV_SPEC_H +#define _HIGH_SPEED_ENV_SPEC_H + +#include "seq_exec.h" + +/* + * For setting or clearing a certain bit (bit is a number between 0 and 31) + * in the data + */ +#define SET_BIT(data, bit) ((data) | (0x1 << (bit))) +#define CLEAR_BIT(data, bit) ((data) & (~(0x1 << (bit)))) + +#define MAX_SERDES_LANES 7 /* as in a39x */ + +/* Serdes revision */ +/* Serdes revision 1.2 (for A38x-Z1) */ +#define MV_SERDES_REV_1_2 0x0 +/* Serdes revision 2.1 (for A39x-Z1, A38x-A0) */ +#define MV_SERDES_REV_2_1 0x1 +#define MV_SERDES_REV_NA 0xff + +#define SERDES_REGS_LANE_BASE_OFFSET(lane) (0x800 * (lane)) + +#define PEX_X4_ENABLE_OFFS \ + (hws_ctrl_serdes_rev_get() == MV_SERDES_REV_1_2 ? 18 : 31) + +/* Serdes lane types */ +enum serdes_type { + PEX0, + PEX1, + PEX2, + PEX3, + SATA0, + SATA1, + SATA2, + SATA3, + SGMII0, + SGMII1, + SGMII2, + QSGMII, + USB3_HOST0, + USB3_HOST1, + USB3_DEVICE, + SGMII3, + XAUI, + RXAUI, + DEFAULT_SERDES, + LAST_SERDES_TYPE +}; + +/* Serdes baud rates */ +enum serdes_speed { + SERDES_SPEED_1_25_GBPS, + SERDES_SPEED_1_5_GBPS, + SERDES_SPEED_2_5_GBPS, + SERDES_SPEED_3_GBPS, + SERDES_SPEED_3_125_GBPS, + SERDES_SPEED_5_GBPS, + SERDES_SPEED_6_GBPS, + SERDES_SPEED_6_25_GBPS, + LAST_SERDES_SPEED +}; + +/* Serdes modes */ +enum serdes_mode { + PEX_ROOT_COMPLEX_X1, + PEX_ROOT_COMPLEX_X4, + PEX_END_POINT_X1, + PEX_END_POINT_X4, + + SERDES_DEFAULT_MODE, /* not pex */ + + SERDES_LAST_MODE +}; + +struct serdes_map { + enum serdes_type serdes_type; + enum serdes_speed serdes_speed; + enum serdes_mode serdes_mode; + int swap_rx; + int swap_tx; +}; + +/* Serdes ref clock options */ +enum ref_clock { + REF_CLOCK_25MHZ, + REF_CLOCK_100MHZ, + REF_CLOCK_40MHZ, + REF_CLOCK_UNSUPPORTED +}; + +/* Serdes sequences */ +enum serdes_seq { + SATA_PORT_0_ONLY_POWER_UP_SEQ, + SATA_PORT_1_ONLY_POWER_UP_SEQ, + SATA_POWER_UP_SEQ, + SATA_1_5_SPEED_CONFIG_SEQ, + SATA_3_SPEED_CONFIG_SEQ, + SATA_6_SPEED_CONFIG_SEQ, + SATA_ELECTRICAL_CONFIG_SEQ, + SATA_TX_CONFIG_SEQ1, + SATA_PORT_0_ONLY_TX_CONFIG_SEQ, + SATA_PORT_1_ONLY_TX_CONFIG_SEQ, + SATA_TX_CONFIG_SEQ2, + + SGMII_POWER_UP_SEQ, + SGMII_1_25_SPEED_CONFIG_SEQ, + SGMII_3_125_SPEED_CONFIG_SEQ, + SGMII_ELECTRICAL_CONFIG_SEQ, + SGMII_TX_CONFIG_SEQ1, + SGMII_TX_CONFIG_SEQ2, + + PEX_POWER_UP_SEQ, + PEX_2_5_SPEED_CONFIG_SEQ, + PEX_5_SPEED_CONFIG_SEQ, + PEX_ELECTRICAL_CONFIG_SEQ, + PEX_TX_CONFIG_SEQ1, + PEX_TX_CONFIG_SEQ2, + PEX_TX_CONFIG_SEQ3, + PEX_BY_4_CONFIG_SEQ, + PEX_CONFIG_REF_CLOCK_25MHZ_SEQ, + PEX_CONFIG_REF_CLOCK_100MHZ_SEQ, + PEX_CONFIG_REF_CLOCK_40MHZ_SEQ, + + USB3_POWER_UP_SEQ, + USB3_HOST_SPEED_CONFIG_SEQ, + USB3_DEVICE_SPEED_CONFIG_SEQ, + USB3_ELECTRICAL_CONFIG_SEQ, + USB3_TX_CONFIG_SEQ1, + USB3_TX_CONFIG_SEQ2, + USB3_TX_CONFIG_SEQ3, + USB3_DEVICE_CONFIG_SEQ, + + USB2_POWER_UP_SEQ, + + SERDES_POWER_DOWN_SEQ, + + SGMII3_POWER_UP_SEQ, + SGMII3_1_25_SPEED_CONFIG_SEQ, + SGMII3_TX_CONFIG_SEQ1, + SGMII3_TX_CONFIG_SEQ2, + + QSGMII_POWER_UP_SEQ, + QSGMII_5_SPEED_CONFIG_SEQ, + QSGMII_ELECTRICAL_CONFIG_SEQ, + QSGMII_TX_CONFIG_SEQ1, + QSGMII_TX_CONFIG_SEQ2, + + XAUI_POWER_UP_SEQ, + XAUI_3_125_SPEED_CONFIG_SEQ, + XAUI_ELECTRICAL_CONFIG_SEQ, + XAUI_TX_CONFIG_SEQ1, + XAUI_TX_CONFIG_SEQ2, + + RXAUI_POWER_UP_SEQ, + RXAUI_6_25_SPEED_CONFIG_SEQ, + RXAUI_ELECTRICAL_CONFIG_SEQ, + RXAUI_TX_CONFIG_SEQ1, + RXAUI_TX_CONFIG_SEQ2, + + SERDES_LAST_SEQ +}; + +/* The different sequence types for PEX and USB3 */ +enum { + PEX, + USB3, + LAST_PEX_USB_SEQ_TYPE +}; + +enum { + PEXSERDES_SPEED_2_5_GBPS, + PEXSERDES_SPEED_5_GBPS, + USB3SERDES_SPEED_5_GBPS_HOST, + USB3SERDES_SPEED_5_GBPS_DEVICE, + LAST_PEX_USB_SPEED_SEQ_TYPE +}; + +/* The different sequence types for SATA and SGMII */ +enum { + SATA, + SGMII, + SGMII_3_125, + LAST_SATA_SGMII_SEQ_TYPE +}; + +enum { + QSGMII_SEQ_IDX, + LAST_QSGMII_SEQ_TYPE +}; + +enum { + XAUI_SEQ_IDX, + RXAUI_SEQ_IDX, + LAST_XAUI_RXAUI_SEQ_TYPE +}; + +enum { + SATASERDES_SPEED_1_5_GBPS, + SATASERDES_SPEED_3_GBPS, + SATASERDES_SPEED_6_GBPS, + SGMIISERDES_SPEED_1_25_GBPS, + SGMIISERDES_SPEED_3_125_GBPS, + LAST_SATA_SGMII_SPEED_SEQ_TYPE +}; + +extern u8 selectors_serdes_rev1_map[LAST_SERDES_TYPE][MAX_SERDES_LANES]; +extern u8 selectors_serdes_rev2_map[LAST_SERDES_TYPE][MAX_SERDES_LANES]; + +u8 hws_ctrl_serdes_rev_get(void); +int mv_update_serdes_select_phy_mode_seq(void); +int hws_board_topology_load(struct serdes_map *serdes_map_array); +enum serdes_seq serdes_type_and_speed_to_speed_seq(enum serdes_type serdes_type, + enum serdes_speed baud_rate); +int hws_serdes_seq_init(void); +int hws_serdes_seq_db_init(void); +int hws_power_up_serdes_lanes(struct serdes_map *serdes_config_map); +int hws_ctrl_high_speed_serdes_phy_config(void); +int serdes_power_up_ctrl(u32 serdes_num, int serdes_power_up, + enum serdes_type serdes_type, + enum serdes_speed baud_rate, + enum serdes_mode serdes_mode, + enum ref_clock ref_clock); +int serdes_power_up_ctrl_ext(u32 serdes_num, int serdes_power_up, + enum serdes_type serdes_type, + enum serdes_speed baud_rate, + enum serdes_mode serdes_mode, + enum ref_clock ref_clock); +u32 hws_serdes_silicon_ref_clock_get(void); +int hws_serdes_pex_ref_clock_get(enum serdes_type serdes_type, + enum ref_clock *ref_clock); +int hws_ref_clock_set(u32 serdes_num, enum serdes_type serdes_type, + enum ref_clock ref_clock); +int hws_update_serdes_phy_selectors(struct serdes_map *serdes_config_map); +u32 hws_serdes_get_phy_selector_val(int serdes_num, + enum serdes_type serdes_type); +u32 hws_serdes_get_ref_clock_val(enum serdes_type serdes_type); +u32 hws_serdes_get_max_lane(void); +int hws_get_ext_base_addr(u32 serdes_num, u32 base_addr, u32 unit_base_offset, + u32 *unit_base_reg, u32 *unit_offset); +int hws_pex_tx_config_seq(struct serdes_map *serdes_map); +u32 hws_get_physical_serdes_num(u32 serdes_num); +int hws_is_serdes_active(u8 lane_num); + +#endif /* _HIGH_SPEED_ENV_SPEC_H */ diff --git a/arch/arm/mach-mvebu/serdes/a38x/high_speed_topology_spec-38x.c b/arch/arm/mach-mvebu/serdes/a38x/high_speed_topology_spec-38x.c new file mode 100644 index 00000000000..5f2c3eb308f --- /dev/null +++ b/arch/arm/mach-mvebu/serdes/a38x/high_speed_topology_spec-38x.c @@ -0,0 +1,1009 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "high_speed_topology_spec.h" +#include "sys_env_lib.h" + +#ifdef CONFIG_CUSTOMER_BOARD_SUPPORT +/* + * This is an example implementation for this custom board + * specific function + */ +static struct serdes_map custom_board_topology_config[] = { + /* Customer Board Topology - reference from Marvell DB-GP board */ + {PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + {SATA0, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {SATA1, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {SATA3, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {SATA2, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {USB3_HOST1, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0} +}; + +int hws_board_topology_load(struct serdes_map *serdes_map_array) +{ + serdes_map_array = custom_board_topology_config; +} +#endif + +load_topology_func_ptr load_topology_func_arr[] = { + load_topology_rd, /* RD NAS */ + load_topology_db, /* 6820 DB-BP (A38x) */ + load_topology_rd, /* RD AP */ + load_topology_db_ap, /* DB AP */ + load_topology_db_gp, /* DB GP */ + load_topology_db_381, /* 6821 DB-BP (A381) */ + load_topology_db_amc, /* DB-AMC */ +}; + +/*****************************************/ +/** Load topology - Marvell 380 DB - BP **/ +/*****************************************/ +/* Configuration options */ +struct serdes_map db_config_default[MAX_SERDES_LANES] = { + {SATA0, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + {PEX1, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + {SATA3, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {USB3_HOST0, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {USB3_HOST1, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0} +}; + +struct serdes_map db_config_slm1363_c[MAX_SERDES_LANES] = { + {PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + {DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE, 0, 0}, + {PEX1, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + {PEX3, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + {SATA2, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE, 0, 0}, +}; + +struct serdes_map db_config_slm1363_d[MAX_SERDES_LANES] = { + {PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X4, 0, 0}, + {PEX1, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X4, 0, 0}, + {PEX2, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X4, 0, 0}, + {PEX3, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X4, 0, 0}, + {USB3_HOST0, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {USB3_HOST1, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0} +}; + +struct serdes_map db_config_slm1363_e[MAX_SERDES_LANES] = { + {PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + {USB3_HOST0, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {SATA1, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {USB3_HOST1, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE, 0, 0}, + {SATA2, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0} +}; + +struct serdes_map db_config_slm1363_f[MAX_SERDES_LANES] = { + {PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + {DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE, 0, 0}, + {PEX1, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + {PEX3, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + {SATA2, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {USB3_HOST1, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0} +}; + +struct serdes_map db_config_slm1364_d[MAX_SERDES_LANES] = { + {DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE, 0, 0}, + {SGMII0, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE, 0, 0}, + {DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE, 0, 0}, + {SGMII1, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {SGMII2, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, 0} +}; + +struct serdes_map db_config_slm1364_e[MAX_SERDES_LANES] = { + {SGMII0, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {SGMII1, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE, 0, 0}, + {SGMII2, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE, 0, 0}, + {PEX2, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0} +}; + +struct serdes_map db_config_slm1364_f[MAX_SERDES_LANES] = { + {SGMII0, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE, 0, 0}, + {SGMII1, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {SGMII2, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE, 0, 0}, + {PEX2, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0} +}; + +/*************************************************************************/ +/** The following structs are mapping for DB board 'SatR' configuration **/ +/*************************************************************************/ +struct serdes_map db_satr_config_lane1[SATR_DB_LANE1_MAX_OPTIONS] = { + /* 0 */ {DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE, 0, + 0}, + /* 1 */ {PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + /* 2 */ {SATA0, SERDES_SPEED_3_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + /* 3 */ {SGMII0, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, + 0}, + /* 4 */ {SGMII1, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, + 0}, + /* 5 */ {USB3_HOST0, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, + 0}, + /* 6 */ {QSGMII, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0} +}; + +struct serdes_map db_satr_config_lane2[SATR_DB_LANE2_MAX_OPTIONS] = { + /* 0 */ {DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE, 0, + 0}, + /* 1 */ {PEX1, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + /* 2 */ {SATA1, SERDES_SPEED_3_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + /* 3 */ {SGMII1, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, + 0} +}; + +/*******************************************************/ +/* Configuration options DB ****************************/ +/* mapping from TWSI address data to configuration map */ +/*******************************************************/ +struct serdes_map *topology_config_db[] = { + db_config_slm1363_c, + db_config_slm1363_d, + db_config_slm1363_e, + db_config_slm1363_f, + db_config_slm1364_d, + db_config_slm1364_e, + db_config_slm1364_f, + db_config_default +}; + +/*************************************/ +/** Load topology - Marvell DB - AP **/ +/*************************************/ +struct serdes_map db_ap_config_default[MAX_SERDES_LANES] = { + /* 0 */ {PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + /* 1 */ {SGMII1, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, + 0}, + /* 2 */ {PEX1, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + /* 3 */ {SGMII2, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, + 0}, + /* 4 */ {USB3_HOST0, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, + 0}, + /* 5 */ {PEX2, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0} +}; + +/*************************************/ +/** Load topology - Marvell DB - GP **/ +/*************************************/ +struct serdes_map db_gp_config_default[MAX_SERDES_LANES] = { + /* 0 */ {PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + /* 1 */ {SATA0, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + /* 2 */ {SATA1, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + /* 3 */ {SATA3, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + /* 4 */ {SATA2, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + /* 5 */ {USB3_HOST1, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, + 0} +}; + +struct serdes_map db_amc_config_default[MAX_SERDES_LANES] = { + /* 0 */ {PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X4, 0, 0}, + /* 1 */ {PEX1, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X4, 0, 0}, + /* 2 */ {PEX2, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X4, 0, 0}, + /* 3 */ {PEX3, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X4, 0, 0}, + /* 4 */ {SGMII1, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, + 0}, + /* 5 */ {SGMII2, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, + 0}, +}; + +/*****************************************/ +/** Load topology - Marvell 381 DB - BP **/ +/*****************************************/ +/* Configuration options */ +struct serdes_map db381_config_default[MAX_SERDES_LANES] = { + {SATA0, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 1, 1}, + {PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + {PEX1, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + {USB3_HOST1, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0} +}; + +struct serdes_map db_config_slm1427[MAX_SERDES_LANES] = { + {SATA0, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 1, 1}, + {PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 1, 1}, + {SATA1, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 1, 1}, + {USB3_HOST1, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 1, 1} +}; + +struct serdes_map db_config_slm1426[MAX_SERDES_LANES] = { + {SATA0, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 1, 1}, + {USB3_HOST0, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 1, 1}, + {SATA1, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 1, 1}, + {SGMII2, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 1, 1} +}; + +/* + * this array must be aligned with enum topology_config_db381 enum, + * every update to this array requires update to enum topology_config_db381 + * enum + */ +struct serdes_map *topology_config_db_381[] = { + db_config_slm1427, + db_config_slm1426, + db381_config_default, +}; + +u8 topology_config_db_mode_get(void) +{ + u8 mode; + + DEBUG_INIT_FULL_S("\n### topology_config_db_mode_get ###\n"); + + /* Default - return DB_CONFIG_DEFAULT */ + + if (!i2c_read(DB_GET_MODE_SLM1363_ADDR, 0, 1, &mode, 1)) { + switch (mode & 0xf) { + case 0xc: + DEBUG_INIT_S("\nInit DB board SLM 1363 C topology\n"); + return DB_CONFIG_SLM1363_C; + case 0xd: + DEBUG_INIT_S("\nInit DB board SLM 1363 D topology\n"); + return DB_CONFIG_SLM1363_D; + case 0xe: + DEBUG_INIT_S("\nInit DB board SLM 1363 E topology\n"); + return DB_CONFIG_SLM1363_E; + case 0xf: + DEBUG_INIT_S("\nInit DB board SLM 1363 F topology\n"); + return DB_CONFIG_SLM1363_F; + default: /* not the right module */ + break; + } + } + + /* SLM1364 Module */ + if (i2c_read(DB_GET_MODE_SLM1364_ADDR, 0, 1, &mode, 1)) { + DEBUG_INIT_S("\nInit DB board default topology\n"); + return DB_CONFIG_DEFAULT; + } + + switch (mode & 0xf) { + case 0xd: + DEBUG_INIT_S("\nInit DB board SLM 1364 D topology\n"); + return DB_CONFIG_SLM1364_D; + case 0xe: + DEBUG_INIT_S("\nInit DB board SLM 1364 E topology\n"); + return DB_CONFIG_SLM1364_E; + case 0xf: + DEBUG_INIT_S("\nInit DB board SLM 1364 F topology\n"); + return DB_CONFIG_SLM1364_F; + default: /* Default configuration */ + DEBUG_INIT_S("\nInit DB board default topology\n"); + return DB_CONFIG_DEFAULT; + } +} + +u8 topology_config_db_381_mode_get(void) +{ + u8 mode; + + DEBUG_INIT_FULL_S("\n### topology_config_db_381_mode_get ###\n"); + + if (!i2c_read(DB381_GET_MODE_SLM1426_1427_ADDR, 0, 2, &mode, 1)) { + switch (mode & 0xf) { + case 0x1: + DEBUG_INIT_S("\nInit DB-381 board SLM 1427 topology\n"); + return DB_CONFIG_SLM1427; + case 0x2: + DEBUG_INIT_S("\nInit DB-381 board SLM 1426 topology\n"); + return DB_CONFIG_SLM1426; + default: /* not the right module */ + break; + } + } + + /* in case not detected any supported module, use default topology */ + DEBUG_INIT_S("\nInit DB-381 board default topology\n"); + return DB_381_CONFIG_DEFAULT; +} + +/* + * Read SatR field 'sgmiispeed' and update lane topology SGMII entries + * speed setup + */ +int update_topology_sgmii_speed(struct serdes_map *serdes_map_array) +{ + u32 serdes_type, lane_num; + u8 config_val; + + /* Update SGMII speed settings by 'sgmiispeed' SatR value */ + for (lane_num = 0; lane_num < hws_serdes_get_max_lane(); lane_num++) { + serdes_type = serdes_map_array[lane_num].serdes_type; + /*Read SatR configuration for SGMII speed */ + if ((serdes_type == SGMII0) || (serdes_type == SGMII1) || + (serdes_type == SGMII2)) { + /* Read SatR 'sgmiispeed' value */ + if (i2c_read(EEPROM_I2C_ADDR, 0, 2, &config_val, 1)) { + printf("%s: TWSI Read of 'sgmiispeed' failed\n", + __func__); + return MV_FAIL; + } + + if (0 == (config_val & 0x40)) { + serdes_map_array[lane_num].serdes_speed = + SERDES_SPEED_1_25_GBPS; + } else { + serdes_map_array[lane_num].serdes_speed = + SERDES_SPEED_3_125_GBPS; + } + } + } + return MV_OK; +} + +struct serdes_map default_lane = { + DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE +}; +int is_custom_topology = 0; /* indicate user of non-default topology */ + +/* + * Read SatR fields (dbserdes1/2 , gpserdes1/2/5) and update lane + * topology accordingly + */ +int update_topology_satr(struct serdes_map *serdes_map_array) +{ + u8 config_val, lane_select, i; + u32 board_id = mv_board_id_get(); + + switch (board_id) { + case DB_68XX_ID: /* read 'dbserdes1' & 'dbserdes2' */ + case DB_BP_6821_ID: + if (i2c_read(EEPROM_I2C_ADDR, 1, 2, &config_val, 1)) { + printf("%s: TWSI Read of 'dbserdes1/2' failed\n", + __func__); + return MV_FAIL; + } + + /* Lane #1 */ + lane_select = (config_val & SATR_DB_LANE1_CFG_MASK) >> + SATR_DB_LANE1_CFG_OFFSET; + if (lane_select >= SATR_DB_LANE1_MAX_OPTIONS) { + printf("\n\%s: Error: invalid value for SatR field 'dbserdes1' (%x)\n", + __func__, lane_select); + printf("\t_skipping Topology update (run 'SatR write default')\n"); + return MV_FAIL; + } + + /* + * If modified default serdes_type for lane#1, update + * topology and mark it as custom + */ + if (serdes_map_array[1].serdes_type != + db_satr_config_lane1[lane_select].serdes_type) { + serdes_map_array[1] = db_satr_config_lane1[lane_select]; + is_custom_topology = 1; + /* DB 381/2 board has inverted SerDes polarity */ + if (board_id == DB_BP_6821_ID) + serdes_map_array[1].swap_rx = + serdes_map_array[1].swap_tx = 1; + } + + /* Lane #2 */ + lane_select = (config_val & SATR_DB_LANE2_CFG_MASK) >> + SATR_DB_LANE2_CFG_OFFSET; + if (lane_select >= SATR_DB_LANE2_MAX_OPTIONS) { + printf("\n\%s: Error: invalid value for SatR field 'dbserdes2' (%x)\n", + __func__, lane_select); + printf("\t_skipping Topology update (run 'SatR write default')\n"); + return MV_FAIL; + } + + /* + * If modified default serdes_type for lane@2, update + * topology and mark it as custom + */ + if (serdes_map_array[2].serdes_type != + db_satr_config_lane2[lane_select].serdes_type) { + serdes_map_array[2] = db_satr_config_lane2[lane_select]; + is_custom_topology = 1; + /* DB 381/2 board has inverted SerDes polarity */ + if (board_id == DB_BP_6821_ID) + serdes_map_array[2].swap_rx = + serdes_map_array[2].swap_tx = 1; + } + + if (is_custom_topology == 1) { + /* + * Check for conflicts with detected lane #1 and + * lane #2 (Disable conflicted lanes) + */ + for (i = 0; i < hws_serdes_get_max_lane(); i++) { + if (i != 1 && serdes_map_array[1].serdes_type == + serdes_map_array[i].serdes_type) { + printf("\t_lane #%d Type conflicts with Lane #1 (Lane #%d disabled)\n", + i, i); + serdes_map_array[i] = + db_satr_config_lane1[0]; + } + + if (i != 2 && + serdes_map_array[2].serdes_type == + serdes_map_array[i].serdes_type) { + printf("\t_lane #%d Type conflicts with Lane #2 (Lane #%d disabled)\n", + i, i); + serdes_map_array[i] = + db_satr_config_lane1[0]; + } + } + } + + break; /* case DB_68XX_ID */ + case DB_GP_68XX_ID: /* read 'gpserdes1' & 'gpserdes2' */ + if (i2c_read(EEPROM_I2C_ADDR, 2, 2, &config_val, 1)) { + printf("%s: TWSI Read of 'gpserdes1/2' failed\n", + __func__); + return MV_FAIL; + } + + /* + * Lane #1: + * lane_select = 0 --> SATA0, + * lane_select = 1 --> PCIe0 (mini PCIe) + */ + lane_select = (config_val & SATR_GP_LANE1_CFG_MASK) >> + SATR_GP_LANE1_CFG_OFFSET; + if (lane_select == 1) { + serdes_map_array[1].serdes_mode = PEX0; + serdes_map_array[1].serdes_speed = SERDES_SPEED_5_GBPS; + serdes_map_array[1].serdes_type = PEX_ROOT_COMPLEX_X1; + /* + * If lane 1 is set to PCIe0 --> disable PCIe0 + * on lane 0 + */ + serdes_map_array[0] = default_lane; + /* indicate user of non-default topology */ + is_custom_topology = 1; + } + printf("Lane 1 detection: %s\n", + lane_select ? "PCIe0 (mini PCIe)" : "SATA0"); + + /* + * Lane #2: + * lane_select = 0 --> SATA1, + * lane_select = 1 --> PCIe1 (mini PCIe) + */ + lane_select = (config_val & SATR_GP_LANE2_CFG_MASK) >> + SATR_GP_LANE2_CFG_OFFSET; + if (lane_select == 1) { + serdes_map_array[2].serdes_type = PEX1; + serdes_map_array[2].serdes_speed = SERDES_SPEED_5_GBPS; + serdes_map_array[2].serdes_mode = PEX_ROOT_COMPLEX_X1; + /* indicate user of non-default topology */ + is_custom_topology = 1; + } + printf("Lane 2 detection: %s\n", + lane_select ? "PCIe1 (mini PCIe)" : "SATA1"); + break; /* case DB_GP_68XX_ID */ + } + + if (is_custom_topology) + printf("\nDetected custom SerDes topology (to restore default run 'SatR write default')\n\n"); + + return MV_OK; +} + +/* + * hws_update_device_toplogy + * DESCRIPTION: Update the default board topology for specific device Id + * INPUT: + * topology_config_ptr - pointer to the Serdes mapping + * topology_mode - topology mode (index) + * OUTPUT: None + * RRETURNS: + * MV_OK - if updating the board topology success + * MV_BAD_PARAM - if the input parameter is wrong + */ +int hws_update_device_toplogy(struct serdes_map *topology_config_ptr, + enum topology_config_db topology_mode) +{ + u32 dev_id = sys_env_device_id_get(); + u32 board_id = mv_board_id_get(); + + switch (topology_mode) { + case DB_CONFIG_DEFAULT: + switch (dev_id) { + case MV_6810: + /* + * DB-AP : default for Lane3=SGMII2 --> + * 6810 supports only 2 SGMII interfaces: + * lane 3 disabled + */ + if (board_id == DB_AP_68XX_ID) { + printf("Device 6810 supports only 2 SGMII interfaces: SGMII-2 @ lane3 disabled\n"); + topology_config_ptr[3] = default_lane; + } + + /* + * 6810 has only 4 SerDes and the forth one is + * Serdes number 5 (i.e. Serdes 4 is not connected), + * therefore we need to copy SerDes 5 configuration + * to SerDes 4 + */ + printf("Device 6810 does not supports SerDes Lane #4: replaced topology entry with lane #5\n"); + topology_config_ptr[4] = topology_config_ptr[5]; + + /* + * No break between cases since the 1st + * 6820 limitation apply on 6810 + */ + case MV_6820: + /* + * DB-GP & DB-BP: default for Lane3=SATA3 --> + * 6810/20 supports only 2 SATA interfaces: + * lane 3 disabled + */ + if ((board_id == DB_68XX_ID) || + (board_id == DB_GP_68XX_ID)) { + printf("Device 6810/20 supports only 2 SATA interfaces: SATA Port 3 @ lane3 disabled\n"); + topology_config_ptr[3] = default_lane; + } + /* + * DB-GP on 6820 only: default for Lane4=SATA2 + * --> 6820 supports only 2 SATA interfaces: + * lane 3 disabled + */ + if (board_id == DB_GP_68XX_ID && dev_id == MV_6820) { + printf("Device 6820 supports only 2 SATA interfaces: SATA Port 2 @ lane4 disabled\n"); + topology_config_ptr[4] = default_lane; + } + break; + default: + break; + } + break; + + default: + printf("sys_env_update_device_toplogy: selected topology is not supported by this routine\n"); + break; + } + + return MV_OK; +} + +int load_topology_db_381(struct serdes_map *serdes_map_array) +{ + u32 lane_num; + u8 topology_mode; + struct serdes_map *topology_config_ptr; + u8 twsi_data; + u8 usb3_host0_or_device = 0, usb3_host1_or_device = 0; + + printf("\nInitialize DB-88F6821-BP board topology\n"); + + /* Getting the relevant topology mode (index) */ + topology_mode = topology_config_db_381_mode_get(); + topology_config_ptr = topology_config_db_381[topology_mode]; + + /* Read USB3.0 mode: HOST/DEVICE */ + if (load_topology_usb_mode_get(&twsi_data) == MV_OK) { + usb3_host0_or_device = (twsi_data & 0x1); + /* Only one USB3 device is enabled */ + if (usb3_host0_or_device == 0) + usb3_host1_or_device = ((twsi_data >> 1) & 0x1); + } + + /* Updating the topology map */ + for (lane_num = 0; lane_num < hws_serdes_get_max_lane(); lane_num++) { + serdes_map_array[lane_num].serdes_mode = + topology_config_ptr[lane_num].serdes_mode; + serdes_map_array[lane_num].serdes_speed = + topology_config_ptr[lane_num].serdes_speed; + serdes_map_array[lane_num].serdes_type = + topology_config_ptr[lane_num].serdes_type; + serdes_map_array[lane_num].swap_rx = + topology_config_ptr[lane_num].swap_rx; + serdes_map_array[lane_num].swap_tx = + topology_config_ptr[lane_num].swap_tx; + + /* Update USB3 device if needed */ + if (usb3_host0_or_device == 1 && + serdes_map_array[lane_num].serdes_type == USB3_HOST0) + serdes_map_array[lane_num].serdes_type = USB3_DEVICE; + + if (usb3_host1_or_device == 1 && + serdes_map_array[lane_num].serdes_type == USB3_HOST1) + serdes_map_array[lane_num].serdes_type = USB3_DEVICE; + } + + /* If not detected any SerDes Site module, read 'SatR' lane setup */ + if (topology_mode == DB_381_CONFIG_DEFAULT) + update_topology_satr(serdes_map_array); + + /* update 'sgmiispeed' settings */ + update_topology_sgmii_speed(serdes_map_array); + + return MV_OK; +} + +int load_topology_db(struct serdes_map *serdes_map_array) +{ + u32 lane_num; + u8 topology_mode; + struct serdes_map *topology_config_ptr; + u8 twsi_data; + u8 usb3_host0_or_device = 0, usb3_host1_or_device = 0; + + printf("\nInitialize DB-88F6820-BP board topology\n"); + + /* Getting the relevant topology mode (index) */ + topology_mode = topology_config_db_mode_get(); + + if (topology_mode == DB_NO_TOPOLOGY) + topology_mode = DB_CONFIG_DEFAULT; + + topology_config_ptr = topology_config_db[topology_mode]; + + /* Update the default board topology device flavours */ + CHECK_STATUS(hws_update_device_toplogy + (topology_config_ptr, topology_mode)); + + /* Read USB3.0 mode: HOST/DEVICE */ + if (load_topology_usb_mode_get(&twsi_data) == MV_OK) { + usb3_host0_or_device = (twsi_data & 0x1); + /* Only one USB3 device is enabled */ + if (usb3_host0_or_device == 0) + usb3_host1_or_device = ((twsi_data >> 1) & 0x1); + } + + /* Updating the topology map */ + for (lane_num = 0; lane_num < hws_serdes_get_max_lane(); lane_num++) { + serdes_map_array[lane_num].serdes_mode = + topology_config_ptr[lane_num].serdes_mode; + serdes_map_array[lane_num].serdes_speed = + topology_config_ptr[lane_num].serdes_speed; + serdes_map_array[lane_num].serdes_type = + topology_config_ptr[lane_num].serdes_type; + serdes_map_array[lane_num].swap_rx = + topology_config_ptr[lane_num].swap_rx; + serdes_map_array[lane_num].swap_tx = + topology_config_ptr[lane_num].swap_tx; + + /* + * Update USB3 device if needed - relevant for + * lane 3,4,5 only + */ + if (lane_num >= 3) { + if ((serdes_map_array[lane_num].serdes_type == + USB3_HOST0) && (usb3_host0_or_device == 1)) + serdes_map_array[lane_num].serdes_type = + USB3_DEVICE; + + if ((serdes_map_array[lane_num].serdes_type == + USB3_HOST1) && (usb3_host1_or_device == 1)) + serdes_map_array[lane_num].serdes_type = + USB3_DEVICE; + } + } + + /* If not detected any SerDes Site module, read 'SatR' lane setup */ + if (topology_mode == DB_CONFIG_DEFAULT) + update_topology_satr(serdes_map_array); + + /* update 'sgmiispeed' settings */ + update_topology_sgmii_speed(serdes_map_array); + + return MV_OK; +} + +int load_topology_db_ap(struct serdes_map *serdes_map_array) +{ + u32 lane_num; + struct serdes_map *topology_config_ptr; + + DEBUG_INIT_FULL_S("\n### load_topology_db_ap ###\n"); + + printf("\nInitialize DB-AP board topology\n"); + topology_config_ptr = db_ap_config_default; + + /* Update the default board topology device flavours */ + CHECK_STATUS(hws_update_device_toplogy + (topology_config_ptr, DB_CONFIG_DEFAULT)); + + /* Updating the topology map */ + for (lane_num = 0; lane_num < hws_serdes_get_max_lane(); lane_num++) { + serdes_map_array[lane_num].serdes_mode = + topology_config_ptr[lane_num].serdes_mode; + serdes_map_array[lane_num].serdes_speed = + topology_config_ptr[lane_num].serdes_speed; + serdes_map_array[lane_num].serdes_type = + topology_config_ptr[lane_num].serdes_type; + serdes_map_array[lane_num].swap_rx = + topology_config_ptr[lane_num].swap_rx; + serdes_map_array[lane_num].swap_tx = + topology_config_ptr[lane_num].swap_tx; + } + + update_topology_sgmii_speed(serdes_map_array); + + return MV_OK; +} + +int load_topology_db_gp(struct serdes_map *serdes_map_array) +{ + u32 lane_num; + struct serdes_map *topology_config_ptr; + int is_sgmii = 0; + + DEBUG_INIT_FULL_S("\n### load_topology_db_gp ###\n"); + + topology_config_ptr = db_gp_config_default; + + printf("\nInitialize DB-GP board topology\n"); + + /* check S@R: if lane 5 is USB3 or SGMII */ + if (load_topology_rd_sgmii_usb(&is_sgmii) != MV_OK) + printf("%s: TWSI Read failed - Loading Default Topology\n", + __func__); + else { + topology_config_ptr[5].serdes_type = + is_sgmii ? SGMII2 : USB3_HOST1; + topology_config_ptr[5].serdes_speed = is_sgmii ? + SERDES_SPEED_3_125_GBPS : SERDES_SPEED_5_GBPS; + topology_config_ptr[5].serdes_mode = SERDES_DEFAULT_MODE; + } + + /* Update the default board topology device flavours */ + CHECK_STATUS(hws_update_device_toplogy + (topology_config_ptr, DB_CONFIG_DEFAULT)); + + /* Updating the topology map */ + for (lane_num = 0; lane_num < hws_serdes_get_max_lane(); lane_num++) { + serdes_map_array[lane_num].serdes_mode = + topology_config_ptr[lane_num].serdes_mode; + serdes_map_array[lane_num].serdes_speed = + topology_config_ptr[lane_num].serdes_speed; + serdes_map_array[lane_num].serdes_type = + topology_config_ptr[lane_num].serdes_type; + serdes_map_array[lane_num].swap_rx = + topology_config_ptr[lane_num].swap_rx; + serdes_map_array[lane_num].swap_tx = + topology_config_ptr[lane_num].swap_tx; + } + + /* + * Update 'gpserdes1/2/3' lane configuration , and 'sgmiispeed' + * for SGMII lanes + */ + update_topology_satr(serdes_map_array); + update_topology_sgmii_speed(serdes_map_array); + + return MV_OK; +} + +int load_topology_db_amc(struct serdes_map *serdes_map_array) +{ + u32 lane_num; + struct serdes_map *topology_config_ptr; + + DEBUG_INIT_FULL_S("\n### load_topology_db_amc ###\n"); + + printf("\nInitialize DB-AMC board topology\n"); + topology_config_ptr = db_amc_config_default; + + /* Update the default board topology device flavours */ + CHECK_STATUS(hws_update_device_toplogy + (topology_config_ptr, DB_CONFIG_DEFAULT)); + + /* Updating the topology map */ + for (lane_num = 0; lane_num < hws_serdes_get_max_lane(); lane_num++) { + serdes_map_array[lane_num].serdes_mode = + topology_config_ptr[lane_num].serdes_mode; + serdes_map_array[lane_num].serdes_speed = + topology_config_ptr[lane_num].serdes_speed; + serdes_map_array[lane_num].serdes_type = + topology_config_ptr[lane_num].serdes_type; + serdes_map_array[lane_num].swap_rx = + topology_config_ptr[lane_num].swap_rx; + serdes_map_array[lane_num].swap_tx = + topology_config_ptr[lane_num].swap_tx; + } + + update_topology_sgmii_speed(serdes_map_array); + + return MV_OK; +} + +int load_topology_rd(struct serdes_map *serdes_map_array) +{ + u8 mode; + + DEBUG_INIT_FULL_S("\n### load_topology_rd ###\n"); + + DEBUG_INIT_S("\nInit RD board "); + + /* Reading mode */ + DEBUG_INIT_FULL_S("load_topology_rd: getting mode\n"); + if (i2c_read(EEPROM_I2C_ADDR, 0, 2, &mode, 1)) { + DEBUG_INIT_S("load_topology_rd: TWSI Read failed\n"); + return MV_FAIL; + } + + /* Updating the topology map */ + DEBUG_INIT_FULL_S("load_topology_rd: Loading board topology details\n"); + + /* RD mode: 0 = NAS, 1 = AP */ + if (((mode >> 1) & 0x1) == 0) { + CHECK_STATUS(load_topology_rd_nas(serdes_map_array)); + } else { + CHECK_STATUS(load_topology_rd_ap(serdes_map_array)); + } + + update_topology_sgmii_speed(serdes_map_array); + + return MV_OK; +} + +int load_topology_rd_nas(struct serdes_map *serdes_map_array) +{ + int is_sgmii = 0; + u32 i; + + DEBUG_INIT_S("\nInit RD NAS topology "); + + /* check if lane 4 is USB3 or SGMII */ + if (load_topology_rd_sgmii_usb(&is_sgmii) != MV_OK) { + DEBUG_INIT_S("load_topology_rd NAS: TWSI Read failed\n"); + return MV_FAIL; + } + + /* Lane 0 */ + serdes_map_array[0].serdes_type = PEX0; + serdes_map_array[0].serdes_speed = SERDES_SPEED_5_GBPS; + serdes_map_array[0].serdes_mode = PEX_ROOT_COMPLEX_X1; + + /* Lane 1 */ + serdes_map_array[1].serdes_type = SATA0; + serdes_map_array[1].serdes_speed = SERDES_SPEED_3_GBPS; + serdes_map_array[1].serdes_mode = SERDES_DEFAULT_MODE; + + /* Lane 2 */ + serdes_map_array[2].serdes_type = SATA1; + serdes_map_array[2].serdes_speed = SERDES_SPEED_3_GBPS; + serdes_map_array[2].serdes_mode = SERDES_DEFAULT_MODE; + + /* Lane 3 */ + serdes_map_array[3].serdes_type = SATA3; + serdes_map_array[3].serdes_speed = SERDES_SPEED_3_GBPS; + serdes_map_array[3].serdes_mode = SERDES_DEFAULT_MODE; + + /* Lane 4 */ + if (is_sgmii == 1) { + DEBUG_INIT_S("Serdes Lane 4 is SGMII\n"); + serdes_map_array[4].serdes_type = SGMII1; + serdes_map_array[4].serdes_speed = SERDES_SPEED_3_125_GBPS; + serdes_map_array[4].serdes_mode = SERDES_DEFAULT_MODE; + } else { + DEBUG_INIT_S("Serdes Lane 4 is USB3\n"); + serdes_map_array[4].serdes_type = USB3_HOST0; + serdes_map_array[4].serdes_speed = SERDES_SPEED_5_GBPS; + serdes_map_array[4].serdes_mode = SERDES_DEFAULT_MODE; + } + + /* Lane 5 */ + serdes_map_array[5].serdes_type = SATA2; + serdes_map_array[5].serdes_speed = SERDES_SPEED_3_GBPS; + serdes_map_array[5].serdes_mode = SERDES_DEFAULT_MODE; + + /* init swap configuration */ + for (i = 0; i <= 5; i++) { + serdes_map_array[i].swap_rx = 0; + serdes_map_array[i].swap_tx = 0; + } + + return MV_OK; +} + +int load_topology_rd_ap(struct serdes_map *serdes_map_array) +{ + int is_sgmii = 0; + u32 i; + + DEBUG_INIT_S("\nInit RD AP topology "); + + /* check if lane 4 is USB3 or SGMII */ + if (load_topology_rd_sgmii_usb(&is_sgmii) != MV_OK) { + DEBUG_INIT_S("load_topology_rd AP: TWSI Read failed\n"); + return MV_FAIL; + } + + /* Lane 0 */ + serdes_map_array[0].serdes_type = DEFAULT_SERDES; + serdes_map_array[0].serdes_speed = LAST_SERDES_SPEED; + serdes_map_array[0].serdes_mode = SERDES_DEFAULT_MODE; + + /* Lane 1 */ + serdes_map_array[1].serdes_type = PEX0; + serdes_map_array[1].serdes_speed = SERDES_SPEED_5_GBPS; + serdes_map_array[1].serdes_mode = PEX_ROOT_COMPLEX_X1; + + /* Lane 2 */ + serdes_map_array[2].serdes_type = PEX1; + serdes_map_array[2].serdes_speed = SERDES_SPEED_5_GBPS; + serdes_map_array[2].serdes_mode = PEX_ROOT_COMPLEX_X1; + + /* Lane 3 */ + serdes_map_array[3].serdes_type = SATA3; + serdes_map_array[3].serdes_speed = SERDES_SPEED_3_GBPS; + serdes_map_array[3].serdes_mode = SERDES_DEFAULT_MODE; + + /* Lane 4 */ + if (is_sgmii == 1) { + DEBUG_INIT_S("Serdes Lane 4 is SGMII\n"); + serdes_map_array[4].serdes_type = SGMII1; + serdes_map_array[4].serdes_speed = SERDES_SPEED_3_125_GBPS; + serdes_map_array[4].serdes_mode = SERDES_DEFAULT_MODE; + } else { + DEBUG_INIT_S("Serdes Lane 4 is USB3\n"); + serdes_map_array[4].serdes_type = USB3_HOST0; + serdes_map_array[4].serdes_speed = SERDES_SPEED_5_GBPS; + serdes_map_array[4].serdes_mode = SERDES_DEFAULT_MODE; + } + + /* Lane 5 */ + serdes_map_array[5].serdes_type = SATA2; + serdes_map_array[5].serdes_speed = SERDES_SPEED_3_GBPS; + serdes_map_array[5].serdes_mode = SERDES_DEFAULT_MODE; + + /* init swap configuration */ + for (i = 0; i <= 5; i++) { + serdes_map_array[i].swap_rx = 0; + serdes_map_array[i].swap_tx = 0; + } + + return MV_OK; +} + +int load_topology_rd_sgmii_usb(int *is_sgmii) +{ + u8 mode; + + /* + * DB-GP board: Device 6810 supports only 2 GbE ports: + * SGMII2 not supported (USE USB3 Host instead) + */ + if (sys_env_device_id_get() == MV_6810) { + printf("Device 6810 supports only 2 GbE ports: SGMII-2 @ lane5 disabled (setting USB3.0 H1 instead)\n"); + *is_sgmii = 0; + return MV_OK; + } + + if (!i2c_read(RD_GET_MODE_ADDR, 1, 2, &mode, 1)) { + *is_sgmii = ((mode >> 2) & 0x1); + } else { + /* else use the default - USB3 */ + *is_sgmii = 0; + } + + if (*is_sgmii) + is_custom_topology = 1; + + printf("Lane 5 detection: %s\n", + *is_sgmii ? "SGMII2" : "USB3.0 Host Port 1"); + + return MV_OK; +} + +/* + * 'usb3port0'/'usb3port1' fields are located in EEPROM, + * at 3rd byte(offset=2), bit 0:1 (respectively) + */ +int load_topology_usb_mode_get(u8 *twsi_data) +{ + if (!i2c_read(EEPROM_I2C_ADDR, 2, 2, twsi_data, 1)) + return MV_OK; + + return MV_ERROR; +} diff --git a/arch/arm/mach-mvebu/serdes/a38x/high_speed_topology_spec.h b/arch/arm/mach-mvebu/serdes/a38x/high_speed_topology_spec.h new file mode 100644 index 00000000000..3cfb1c71e70 --- /dev/null +++ b/arch/arm/mach-mvebu/serdes/a38x/high_speed_topology_spec.h @@ -0,0 +1,124 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _HIGHSPEED_TOPOLOGY_SPEC_H +#define _HIGHSPEED_TOPOLOGY_SPEC_H + +#include "high_speed_env_spec.h" + +/* Topology map options for the DB_A38X_BP board */ +enum topology_config_db { + DB_CONFIG_SLM1363_C, + DB_CONFIG_SLM1363_D, + DB_CONFIG_SLM1363_E, + DB_CONFIG_SLM1363_F, + DB_CONFIG_SLM1364_D, + DB_CONFIG_SLM1364_E, + DB_CONFIG_SLM1364_F, + DB_CONFIG_DEFAULT, + DB_NO_TOPOLOGY +}; + +/* + * this enum must be aligned with topology_config_db_381 array, + * every update to this enum requires update to topology_config_db_381 + * array + */ +enum topology_config_db381 { + DB_CONFIG_SLM1427, /* enum for db_config_slm1427 */ + DB_CONFIG_SLM1426, /* enum for db_config_slm1426 */ + DB_381_CONFIG_DEFAULT, + DB_381_NO_TOPOLOGY +}; + +/* A generic function pointer for loading the board topology map */ +typedef int (*load_topology_func_ptr)(struct serdes_map *serdes_map_array); + +extern load_topology_func_ptr load_topology_func_arr[]; + +/* + * topology_config_db_mode_get - + * + * DESCRIPTION: Gets the relevant topology mode (index). + * for load_topology_db use only. + * INPUT: None. + * OUTPUT: None. + * RETURNS: the topology mode + */ +u8 topology_config_db_mode_get(void); + +/* + * load_topology_xxx - + * + * DESCRIPTION: Loads the board topology for the XXX board + * INPUT: serdes_map_array - The struct that will contain + * the board topology map + * OUTPUT: The board topology map. + * RETURNS: MV_OK for success + * MV_FAIL for failure (a wrong topology mode was read + * from the board) + */ + +/* load_topology_db - Loads the board topology for DB Board */ +int load_topology_db(struct serdes_map *serdes_map_array); + +/* load_topology_rd - Loads the board topology for RD Board */ +int load_topology_rd(struct serdes_map *serdes_map_array); + +/* load_topology_rd_nas - Loads the board topology for RD NAS Board */ +int load_topology_rd_nas(struct serdes_map *serdes_map_array); + +/* load_topology_rd_ap - Loads the board topology for RD Ap Board */ +int load_topology_rd_ap(struct serdes_map *serdes_map_array); + +/* load_topology_db_ap - Loads the board topology for DB-AP Board */ +int load_topology_db_ap(struct serdes_map *serdes_map_array); + +/* load_topology_db_gp - Loads the board topology for DB GP Board */ +int load_topology_db_gp(struct serdes_map *serdes_map_array); + +/* load_topology_db_381 - Loads the board topology for 381 DB-BP Board */ +int load_topology_db_381(struct serdes_map *serdes_map_array); + +/* load_topology_db_amc - Loads the board topology for DB-AMC Board */ +int load_topology_db_amc(struct serdes_map *serdes_map_array); + +/* + * hws_update_device_toplogy + * DESCRIPTION: Update the default board topology for specific device Id + * INPUT: + * topology_config_ptr - pointer to the Serdes mapping + * topology_mode - topology mode (index) + * OUTPUT: None + * RRETURNS: + * MV_OK - if updating the board topology success + * MV_BAD_PARAM - if the input parameter is wrong + */ +int hws_update_device_toplogy(struct serdes_map *topology_config_ptr, + enum topology_config_db topology_mode); + +/* + * load_topology_rd_sgmii_usb - + * + * DESCRIPTION: For RD board check if lane 4 is USB3 or SGMII + * INPUT: None + * OUTPUT: is_sgmii - return 1 if lane 4 is SGMII + * return 0 if lane 4 is USB. + * RETURNS: MV_OK for success + */ +int load_topology_rd_sgmii_usb(int *is_sgmii); + +/* + * load_topology_usb_mode_get - + * + * DESCRIPTION: For DB board check if USB3.0 mode + * INPUT: None + * OUTPUT: twsi_data - return data read from S@R via I2C + * RETURNS: MV_OK for success + */ +int load_topology_usb_mode_get(u8 *twsi_data); + +#endif /* _HIGHSPEED_TOPOLOGY_SPEC_H */ diff --git a/arch/arm/mach-mvebu/serdes/a38x/seq_exec.c b/arch/arm/mach-mvebu/serdes/a38x/seq_exec.c new file mode 100644 index 00000000000..ee2305b9836 --- /dev/null +++ b/arch/arm/mach-mvebu/serdes/a38x/seq_exec.c @@ -0,0 +1,170 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "seq_exec.h" +#include "high_speed_env_spec.h" + +#include "../../../drivers/ddr/marvell/a38x/ddr3_init.h" + +#if defined(MV_DEBUG_INIT_FULL) || defined(MV_DEBUG) +#define DB(x) x +#else +#define DB(x) +#endif + +/* Array for mapping the operation (write, poll or delay) functions */ +op_execute_func_ptr op_execute_func_arr[] = { + write_op_execute, + delay_op_execute, + poll_op_execute +}; + +int write_op_execute(u32 serdes_num, struct op_params *params, u32 data_arr_idx) +{ + u32 unit_base_reg, unit_offset, data, mask, reg_data, reg_addr; + + /* Getting write op params from the input parameter */ + data = params->data[data_arr_idx]; + mask = params->mask; + + /* an empty operation */ + if (data == NO_DATA) + return MV_OK; + + /* get updated base address since it can be different between Serdes */ + CHECK_STATUS(hws_get_ext_base_addr(serdes_num, params->unit_base_reg, + params->unit_offset, + &unit_base_reg, &unit_offset)); + + /* Address calculation */ + reg_addr = unit_base_reg + unit_offset * serdes_num; + +#ifdef SEQ_DEBUG + printf("Write: 0x%x: 0x%x (mask 0x%x) - ", reg_addr, data, mask); +#endif + /* Reading old value */ + reg_data = reg_read(reg_addr); + reg_data &= (~mask); + + /* Writing new data */ + data &= mask; + reg_data |= data; + reg_write(reg_addr, reg_data); + +#ifdef SEQ_DEBUG + printf(" - 0x%x\n", reg_data); +#endif + + return MV_OK; +} + +int delay_op_execute(u32 serdes_num, struct op_params *params, u32 data_arr_idx) +{ + u32 delay; + + /* Getting delay op params from the input parameter */ + delay = params->wait_time; +#ifdef SEQ_DEBUG + printf("Delay: %d\n", delay); +#endif + mdelay(delay); + + return MV_OK; +} + +int poll_op_execute(u32 serdes_num, struct op_params *params, u32 data_arr_idx) +{ + u32 unit_base_reg, unit_offset, data, mask, num_of_loops, wait_time; + u32 poll_counter = 0; + u32 reg_addr, reg_data; + + /* Getting poll op params from the input parameter */ + data = params->data[data_arr_idx]; + mask = params->mask; + num_of_loops = params->num_of_loops; + wait_time = params->wait_time; + + /* an empty operation */ + if (data == NO_DATA) + return MV_OK; + + /* get updated base address since it can be different between Serdes */ + CHECK_STATUS(hws_get_ext_base_addr(serdes_num, params->unit_base_reg, + params->unit_offset, + &unit_base_reg, &unit_offset)); + + /* Address calculation */ + reg_addr = unit_base_reg + unit_offset * serdes_num; + + /* Polling */ +#ifdef SEQ_DEBUG + printf("Poll: 0x%x: 0x%x (mask 0x%x)\n", reg_addr, data, mask); +#endif + + do { + reg_data = reg_read(reg_addr) & mask; + poll_counter++; + udelay(wait_time); + } while ((reg_data != data) && (poll_counter < num_of_loops)); + + if ((poll_counter >= num_of_loops) && (reg_data != data)) { + DEBUG_INIT_S("poll_op_execute: TIMEOUT\n"); + return MV_TIMEOUT; + } + + return MV_OK; +} + +enum mv_op get_cfg_seq_op(struct op_params *params) +{ + if (params->wait_time == 0) + return WRITE_OP; + else if (params->num_of_loops == 0) + return DELAY_OP; + + return POLL_OP; +} + +int mv_seq_exec(u32 serdes_num, u32 seq_id) +{ + u32 seq_idx; + struct op_params *seq_arr; + u32 seq_size; + u32 data_arr_idx; + enum mv_op curr_op; + + DB(printf("\n### mv_seq_exec ###\n")); + DB(printf("seq id: %d\n", seq_id)); + + if (hws_is_serdes_active(serdes_num) != 1) { + printf("mv_seq_exec_ext:Error: SerDes lane %d is not valid\n", + serdes_num); + return MV_BAD_PARAM; + } + + seq_arr = serdes_seq_db[seq_id].op_params_ptr; + seq_size = serdes_seq_db[seq_id].cfg_seq_size; + data_arr_idx = serdes_seq_db[seq_id].data_arr_idx; + + DB(printf("seq_size: %d\n", seq_size)); + DB(printf("data_arr_idx: %d\n", data_arr_idx)); + + /* Executing the sequence operations */ + for (seq_idx = 0; seq_idx < seq_size; seq_idx++) { + curr_op = get_cfg_seq_op(&seq_arr[seq_idx]); + op_execute_func_arr[curr_op](serdes_num, &seq_arr[seq_idx], + data_arr_idx); + } + + return MV_OK; +} diff --git a/arch/arm/mach-mvebu/serdes/a38x/seq_exec.h b/arch/arm/mach-mvebu/serdes/a38x/seq_exec.h new file mode 100644 index 00000000000..14f406a75f6 --- /dev/null +++ b/arch/arm/mach-mvebu/serdes/a38x/seq_exec.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _SEQ_EXEC_H +#define _SEQ_EXEC_H + +#define NA 0xff +#define DEFAULT_PARAM 0 +#define MV_BOARD_TCLK_ERROR 0xffffffff + +#define NO_DATA 0xffffffff +#define MAX_DATA_ARRAY 5 +#define FIRST_CELL 0 + +/* Operation types */ +enum mv_op { + WRITE_OP, + DELAY_OP, + POLL_OP, +}; + +/* Operation parameters */ +struct op_params { + u32 unit_base_reg; + u32 unit_offset; + u32 mask; + u32 data[MAX_DATA_ARRAY]; /* data array */ + u8 wait_time; /* msec */ + u16 num_of_loops; /* for polling only */ +}; + +/* + * Sequence parameters. Each sequence contains: + * 1. Sequence id. + * 2. Sequence size (total amount of operations during the sequence) + * 3. a series of operations. operations can be write, poll or delay + * 4. index in the data array (the entry where the relevant data sits) + */ +struct cfg_seq { + struct op_params *op_params_ptr; + u8 cfg_seq_size; + u8 data_arr_idx; +}; + +extern struct cfg_seq serdes_seq_db[]; + +/* + * A generic function type for executing an operation (write, poll or delay) + */ +typedef int (*op_execute_func_ptr)(u32 serdes_num, struct op_params *params, + u32 data_arr_idx); + +/* Specific functions for executing each operation */ +int write_op_execute(u32 serdes_num, struct op_params *params, + u32 data_arr_idx); +int delay_op_execute(u32 serdes_num, struct op_params *params, + u32 data_arr_idx); +int poll_op_execute(u32 serdes_num, struct op_params *params, u32 data_arr_idx); +enum mv_op get_cfg_seq_op(struct op_params *params); +int mv_seq_exec(u32 serdes_num, u32 seq_id); + +#endif /*_SEQ_EXEC_H*/ diff --git a/arch/arm/mach-mvebu/serdes/a38x/sys_env_lib.c b/arch/arm/mach-mvebu/serdes/a38x/sys_env_lib.c new file mode 100644 index 00000000000..efd38732037 --- /dev/null +++ b/arch/arm/mach-mvebu/serdes/a38x/sys_env_lib.c @@ -0,0 +1,388 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "seq_exec.h" +#include "sys_env_lib.h" + +#include "../../../drivers/ddr/marvell/a38x/ddr3_a38x.h" + +#ifdef CONFIG_ARMADA_38X +enum unit_id sys_env_soc_unit_nums[MAX_UNITS_ID][MAX_DEV_ID_NUM] = { +/* 6820 6810 6811 6828 */ +/* PEX_UNIT_ID */ { 4, 3, 3, 4}, +/* ETH_GIG_UNIT_ID */ { 3, 2, 3, 3}, +/* USB3H_UNIT_ID */ { 2, 2, 2, 2}, +/* USB3D_UNIT_ID */ { 1, 1, 1, 1}, +/* SATA_UNIT_ID */ { 2, 2, 2, 4}, +/* QSGMII_UNIT_ID */ { 1, 0, 0, 1}, +/* XAUI_UNIT_ID */ { 0, 0, 0, 0}, +/* RXAUI_UNIT_ID */ { 0, 0, 0, 0} +}; +#else /* if (CONFIG_ARMADA_39X) */ +enum unit_id sys_env_soc_unit_nums[MAX_UNITS_ID][MAX_DEV_ID_NUM] = { +/* 6920 6928 */ +/* PEX_UNIT_ID */ { 4, 4}, +/* ETH_GIG_UNIT_ID */ { 3, 4}, +/* USB3H_UNIT_ID */ { 1, 2}, +/* USB3D_UNIT_ID */ { 0, 1}, +/* SATA_UNIT_ID */ { 0, 4}, +/* QSGMII_UNIT_ID */ { 0, 1}, +/* XAUI_UNIT_ID */ { 1, 1}, +/* RXAUI_UNIT_ID */ { 1, 1} +}; +#endif + +u32 g_dev_id = -1; + +u32 mv_board_id_get(void) +{ +#if defined(CONFIG_DB_88F6820_GP) + return DB_GP_68XX_ID; +#else + /* + * Return 0 here for custom board as this should not be used + * for custom boards. + */ + return 0; +#endif +} + +u32 mv_board_tclk_get(void) +{ + u32 value; + + value = (reg_read(DEVICE_SAMPLE_AT_RESET1_REG) >> 15) & 0x1; + + switch (value) { + case (0x0): + return 250000000; + case (0x1): + return 200000000; + default: + return 0xffffffff; + } +} + +u32 mv_board_id_index_get(u32 board_id) +{ + /* + * Marvell Boards use 0x10 as base for Board ID: + * mask MSB to receive index for board ID + */ + return board_id & (MARVELL_BOARD_ID_MASK - 1); +} + +/* + * sys_env_suspend_wakeup_check + * DESCRIPTION: Reads GPIO input for suspend-wakeup indication. + * INPUT: None. + * OUTPUT: + * RETURNS: u32 indicating suspend wakeup status: + * 0 - Not supported, + * 1 - supported: read magic word detect wakeup, + * 2 - detected wakeup from GPIO. + */ +enum suspend_wakeup_status sys_env_suspend_wakeup_check(void) +{ + u32 reg, board_id_index, gpio; + struct board_wakeup_gpio board_gpio[] = MV_BOARD_WAKEUP_GPIO_INFO; + + board_id_index = mv_board_id_index_get(mv_board_id_get()); + if (!(sizeof(board_gpio) / sizeof(struct board_wakeup_gpio) > + board_id_index)) { + printf("\n_failed loading Suspend-Wakeup information (invalid board ID)\n"); + return SUSPEND_WAKEUP_DISABLED; + } + + /* + * - Detect if Suspend-Wakeup is supported on current board + * - Fetch the GPIO number for wakeup status input indication + */ + if (board_gpio[board_id_index].gpio_num == -1) { + /* Suspend to RAM is not supported */ + return SUSPEND_WAKEUP_DISABLED; + } else if (board_gpio[board_id_index].gpio_num == -2) { + /* + * Suspend to RAM is supported but GPIO indication is + * not implemented - Skip + */ + return SUSPEND_WAKEUP_ENABLED; + } else { + gpio = board_gpio[board_id_index].gpio_num; + } + + /* Initialize MPP for GPIO (set MPP = 0x0) */ + reg = reg_read(MPP_CONTROL_REG(MPP_REG_NUM(gpio))); + /* reset MPP21 to 0x0, keep rest of MPP settings*/ + reg &= ~MPP_MASK(gpio); + reg_write(MPP_CONTROL_REG(MPP_REG_NUM(gpio)), reg); + + /* Initialize GPIO as input */ + reg = reg_read(GPP_DATA_OUT_EN_REG(GPP_REG_NUM(gpio))); + reg |= GPP_MASK(gpio); + reg_write(GPP_DATA_OUT_EN_REG(GPP_REG_NUM(gpio)), reg); + + /* + * Check GPP for input status from PIC: 0 - regular init, + * 1 - suspend wakeup + */ + reg = reg_read(GPP_DATA_IN_REG(GPP_REG_NUM(gpio))); + + /* if GPIO is ON: wakeup from S2RAM indication detected */ + return (reg & GPP_MASK(gpio)) ? SUSPEND_WAKEUP_ENABLED_GPIO_DETECTED : + SUSPEND_WAKEUP_DISABLED; +} + +/* + * mv_ctrl_dev_id_index_get + * + * DESCRIPTION: return SOC device index + * INPUT: None + * OUTPUT: None + * RETURN: + * return SOC device index + */ +u32 sys_env_id_index_get(u32 ctrl_model) +{ + switch (ctrl_model) { + case MV_6820_DEV_ID: + return MV_6820_INDEX; + case MV_6810_DEV_ID: + return MV_6810_INDEX; + case MV_6811_DEV_ID: + return MV_6811_INDEX; + case MV_6828_DEV_ID: + return MV_6828_INDEX; + case MV_6920_DEV_ID: + return MV_6920_INDEX; + case MV_6928_DEV_ID: + return MV_6928_INDEX; + default: + return MV_6820_INDEX; + } +} + +u32 sys_env_unit_max_num_get(enum unit_id unit) +{ + u32 dev_id_index; + + if (unit >= MAX_UNITS_ID) { + printf("%s: Error: Wrong unit type (%u)\n", __func__, unit); + return 0; + } + + dev_id_index = sys_env_id_index_get(sys_env_model_get()); + return sys_env_soc_unit_nums[unit][dev_id_index]; +} + +/* + * sys_env_model_get + * DESCRIPTION: Returns 16bit describing the device model (ID) as defined + * in Vendor ID configuration register + */ +u16 sys_env_model_get(void) +{ + u32 default_ctrl_id, ctrl_id = reg_read(DEV_ID_REG); + ctrl_id = (ctrl_id & (DEV_ID_REG_DEVICE_ID_MASK)) >> + DEV_ID_REG_DEVICE_ID_OFFS; + + switch (ctrl_id) { + case MV_6820_DEV_ID: + case MV_6810_DEV_ID: + case MV_6811_DEV_ID: + case MV_6828_DEV_ID: + case MV_6920_DEV_ID: + case MV_6928_DEV_ID: + return ctrl_id; + default: + /* Device ID Default for A38x: 6820 , for A39x: 6920 */ + #ifdef CONFIG_ARMADA_38X + default_ctrl_id = MV_6820_DEV_ID; + #else + default_ctrl_id = MV_6920_DEV_ID; + #endif + printf("%s: Error retrieving device ID (%x), using default ID = %x\n", + __func__, ctrl_id, default_ctrl_id); + return default_ctrl_id; + } +} + +/* + * sys_env_device_id_get + * DESCRIPTION: Returns enum (0..7) index of the device model (ID) + */ +u32 sys_env_device_id_get(void) +{ + char *device_id_str[7] = { + "6810", "6820", "6811", "6828", "NONE", "6920", "6928" + }; + + if (g_dev_id != -1) + return g_dev_id; + + g_dev_id = reg_read(DEVICE_SAMPLE_AT_RESET1_REG); + g_dev_id = g_dev_id >> SAR_DEV_ID_OFFS & SAR_DEV_ID_MASK; + printf("Detected Device ID %s\n", device_id_str[g_dev_id]); + + return g_dev_id; +} + +#ifdef MV_DDR_TOPOLOGY_UPDATE_FROM_TWSI +/* +* sys_env_get_topology_update_info +* DESCRIPTION: Read TWSI fields to update DDR topology structure +* INPUT: None +* OUTPUT: None, 0 means no topology update +* RETURN: +* Bit mask of changes topology features +*/ +#ifdef CONFIG_ARMADA_39X +u32 sys_env_get_topology_update_info( + struct topology_update_info *tui) +{ + /* Set 16/32 bit configuration*/ + tui->update_width = 1; + tui->width = TOPOLOGY_UPDATE_WIDTH_32BIT; + +#ifdef CONFIG_DDR3 + if (1 == sys_env_config_get(MV_CONFIG_DDR_BUSWIDTH)) { + /* 16bit */ + tui->width = TOPOLOGY_UPDATE_WIDTH_16BIT; + } else { + /* 32bit */ + tui->width = TOPOLOGY_UPDATE_WIDTH_32BIT; + } +#endif + + /* Set ECC/no ECC bit configuration */ + tui->update_ecc = 1; + if (0 == sys_env_config_get(MV_CONFIG_DDR_ECC_EN)) { + /* NO ECC */ + tui->ecc = TOPOLOGY_UPDATE_ECC_OFF; + } else { + /* ECC */ + tui->ecc = TOPOLOGY_UPDATE_ECC_ON; + } + + tui->update_ecc_pup3_mode = 1; + tui->ecc_pup_mode_offset = TOPOLOGY_UPDATE_ECC_OFFSET_PUP4; + + return MV_OK; +} +#else /*CONFIG_ARMADA_38X*/ +u32 sys_env_get_topology_update_info( + struct topology_update_info *tui) +{ + u8 config_val; + u8 ecc_mode[A38X_MV_MAX_MARVELL_BOARD_ID - + A38X_MARVELL_BOARD_ID_BASE][5] = TOPOLOGY_UPDATE; + u8 board_id = mv_board_id_get(); + int ret; + + board_id = mv_board_id_index_get(board_id); + ret = i2c_read(EEPROM_I2C_ADDR, 0, 2, &config_val, 1); + if (ret) { + DEBUG_INIT_S("sys_env_get_topology_update_info: TWSI Read failed\n"); + return 0; + } + + /* Set 16/32 bit configuration */ + if ((0 == (config_val & DDR_SATR_CONFIG_MASK_WIDTH)) || + (ecc_mode[board_id][TOPOLOGY_UPDATE_32BIT] == 0)) { + /* 16bit by SatR of 32bit mode not supported for the board */ + if ((ecc_mode[board_id][TOPOLOGY_UPDATE_16BIT] != 0)) { + tui->update_width = 1; + tui->width = TOPOLOGY_UPDATE_WIDTH_16BIT; + } + } else { + /* 32bit */ + if ((ecc_mode[board_id][TOPOLOGY_UPDATE_32BIT] != 0)) { + tui->update_width = 1; + tui->width = TOPOLOGY_UPDATE_WIDTH_32BIT; + } + } + + /* Set ECC/no ECC bit configuration */ + if (0 == (config_val & DDR_SATR_CONFIG_MASK_ECC)) { + /* NO ECC */ + tui->update_ecc = 1; + tui->ecc = TOPOLOGY_UPDATE_ECC_OFF; + } else { + /* ECC */ + if ((ecc_mode[board_id][TOPOLOGY_UPDATE_32BIT_ECC] != 0) || + (ecc_mode[board_id][TOPOLOGY_UPDATE_16BIT_ECC] != 0) || + (ecc_mode[board_id][TOPOLOGY_UPDATE_16BIT_ECC_PUP3] != 0)) { + tui->update_ecc = 1; + tui->ecc = TOPOLOGY_UPDATE_ECC_ON; + } + } + + /* Set ECC pup bit configuration */ + if (0 == (config_val & DDR_SATR_CONFIG_MASK_ECC_PUP)) { + /* PUP3 */ + /* + * Check if PUP3 configuration allowed, if not - + * force Pup4 with warning message + */ + if ((ecc_mode[board_id][TOPOLOGY_UPDATE_16BIT_ECC_PUP3] != 0)) { + if (tui->width == TOPOLOGY_UPDATE_WIDTH_16BIT) { + tui->update_ecc_pup3_mode = 1; + tui->ecc_pup_mode_offset = + TOPOLOGY_UPDATE_ECC_OFFSET_PUP3; + } else { + if ((ecc_mode[board_id][TOPOLOGY_UPDATE_32BIT_ECC] != 0)) { + printf("DDR Topology Update: ECC PUP3 not valid for 32bit mode, force ECC in PUP4\n"); + tui->update_ecc_pup3_mode = 1; + tui->ecc_pup_mode_offset = + TOPOLOGY_UPDATE_ECC_OFFSET_PUP4; + } + } + } else { + if (ecc_mode[board_id][TOPOLOGY_UPDATE_16BIT_ECC] != + 0) { + printf("DDR Topology Update: ECC on PUP3 not supported, force ECC on PUP4\n"); + tui->update_ecc_pup3_mode = 1; + tui->ecc_pup_mode_offset = + TOPOLOGY_UPDATE_ECC_OFFSET_PUP4; + } + } + } else { + /* PUP4 */ + if ((ecc_mode[board_id][TOPOLOGY_UPDATE_32BIT_ECC] != 0) || + (ecc_mode[board_id][TOPOLOGY_UPDATE_16BIT_ECC] != 0)) { + tui->update_ecc_pup3_mode = 1; + tui->ecc_pup_mode_offset = + TOPOLOGY_UPDATE_ECC_OFFSET_PUP4; + } + } + + /* + * Check for forbidden ECC mode, + * if by default width and pup selection set 32bit ECC mode and this + * mode not supported for the board - config 16bit with ECC on PUP3 + */ + if ((tui->ecc == TOPOLOGY_UPDATE_ECC_ON) && + (tui->width == TOPOLOGY_UPDATE_WIDTH_32BIT)) { + if (ecc_mode[board_id][TOPOLOGY_UPDATE_32BIT_ECC] == 0) { + printf("DDR Topology Update: 32bit mode with ECC not allowed on this board, forced 16bit with ECC on PUP3\n"); + tui->width = TOPOLOGY_UPDATE_WIDTH_16BIT; + tui->update_ecc_pup3_mode = 1; + tui->ecc_pup_mode_offset = + TOPOLOGY_UPDATE_ECC_OFFSET_PUP3; + } + } + + return MV_OK; +} +#endif /* CONFIG_ARMADA_38X */ +#endif /* MV_DDR_TOPOLOGY_UPDATE_FROM_TWSI */ diff --git a/arch/arm/mach-mvebu/serdes/a38x/sys_env_lib.h b/arch/arm/mach-mvebu/serdes/a38x/sys_env_lib.h new file mode 100644 index 00000000000..3e5373c085b --- /dev/null +++ b/arch/arm/mach-mvebu/serdes/a38x/sys_env_lib.h @@ -0,0 +1,371 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _SYS_ENV_LIB_H +#define _SYS_ENV_LIB_H + +#include "../../../drivers/ddr/marvell/a38x/ddr3_init.h" +#include "../../../drivers/ddr/marvell/a38x/ddr3_hws_hw_training.h" + +/* Serdes definitions */ +#define COMMON_PHY_BASE_ADDR 0x18300 + +#define DEVICE_CONFIGURATION_REG0 0x18284 +#define DEVICE_CONFIGURATION_REG1 0x18288 +#define COMMON_PHY_CONFIGURATION1_REG 0x18300 +#define COMMON_PHY_CONFIGURATION2_REG 0x18304 +#define COMMON_PHY_CONFIGURATION4_REG 0x1830c +#define COMMON_PHY_STATUS1_REG 0x18318 +#define COMMON_PHYS_SELECTORS_REG 0x183fc +#define SOC_CONTROL_REG1 0x18204 +#define GENERAL_PURPOSE_RESERVED0_REG 0x182e0 +#define GBE_CONFIGURATION_REG 0x18460 +#define DEVICE_SAMPLE_AT_RESET1_REG 0x18600 +#define DEVICE_SAMPLE_AT_RESET2_REG 0x18604 +#define DEV_ID_REG 0x18238 + +#define CORE_PLL_PARAMETERS_REG 0xe42e0 +#define CORE_PLL_CONFIG_REG 0xe42e4 + +#define QSGMII_CONTROL_REG1 0x18494 + +#define DEV_ID_REG_DEVICE_ID_OFFS 16 +#define DEV_ID_REG_DEVICE_ID_MASK 0xffff0000 + +#define SAR_DEV_ID_OFFS 27 +#define SAR_DEV_ID_MASK 0x7 + +#define POWER_AND_PLL_CTRL_REG 0xa0004 +#define CALIBRATION_CTRL_REG 0xa0008 +#define DFE_REG0 0xa001c +#define DFE_REG3 0xa0028 +#define RESET_DFE_REG 0xa0148 +#define LOOPBACK_REG 0xa008c +#define SYNC_PATTERN_REG 0xa0090 +#define INTERFACE_REG 0xa0094 +#define ISOLATE_REG 0xa0098 +#define MISC_REG 0xa013c +#define GLUE_REG 0xa0140 +#define GENERATION_DIVIDER_FORCE_REG 0xa0144 +#define PCIE_REG0 0xa0120 +#define LANE_ALIGN_REG0 0xa0124 +#define SQUELCH_FFE_SETTING_REG 0xa0018 +#define G1_SETTINGS_0_REG 0xa0034 +#define G1_SETTINGS_1_REG 0xa0038 +#define G1_SETTINGS_3_REG 0xa0440 +#define G1_SETTINGS_4_REG 0xa0444 +#define G2_SETTINGS_0_REG 0xa003c +#define G2_SETTINGS_1_REG 0xa0040 +#define G2_SETTINGS_2_REG 0xa00f8 +#define G2_SETTINGS_3_REG 0xa0448 +#define G2_SETTINGS_4_REG 0xa044c +#define G3_SETTINGS_0_REG 0xa0044 +#define G3_SETTINGS_1_REG 0xa0048 +#define G3_SETTINGS_3_REG 0xa0450 +#define G3_SETTINGS_4_REG 0xa0454 +#define VTHIMPCAL_CTRL_REG 0xa0104 +#define REF_REG0 0xa0134 +#define CAL_REG6 0xa0168 +#define RX_REG2 0xa0184 +#define RX_REG3 0xa0188 +#define PCIE_REG1 0xa0288 +#define PCIE_REG3 0xa0290 +#define LANE_CFG1_REG 0xa0604 +#define LANE_CFG4_REG 0xa0620 +#define LANE_CFG5_REG 0xa0624 +#define GLOBAL_CLK_CTRL 0xa0704 +#define GLOBAL_MISC_CTRL 0xa0718 +#define GLOBAL_CLK_SRC_HI 0xa0710 + +#define GLOBAL_CLK_CTRL 0xa0704 +#define GLOBAL_MISC_CTRL 0xa0718 +#define GLOBAL_PM_CTRL 0xa0740 + +/* SATA registers */ +#define SATA_CTRL_REG_IND_ADDR 0xa80a0 +#define SATA_CTRL_REG_IND_DATA 0xa80a4 + +#define SATA_VENDOR_PORT_0_REG_ADDR 0xa8178 +#define SATA_VENDOR_PORT_1_REG_ADDR 0xa81f8 +#define SATA_VENDOR_PORT_0_REG_DATA 0xa817c +#define SATA_VENDOR_PORT_1_REG_DATA 0xa81fc + +/* Reference clock values and mask */ +#define POWER_AND_PLL_CTRL_REG_100MHZ_VAL 0x0 +#define POWER_AND_PLL_CTRL_REG_25MHZ_VAL_1 0x1 +#define POWER_AND_PLL_CTRL_REG_25MHZ_VAL_2 0x2 +#define POWER_AND_PLL_CTRL_REG_40MHZ_VAL 0x3 +#define GLOBAL_PM_CTRL_REG_25MHZ_VAL 0x7 +#define GLOBAL_PM_CTRL_REG_40MHZ_VAL 0xc +#define LANE_CFG4_REG_25MHZ_VAL 0x200 +#define LANE_CFG4_REG_40MHZ_VAL 0x300 + +#define POWER_AND_PLL_CTRL_REG_MASK (~(0x1f)) +#define GLOBAL_PM_CTRL_REG_MASK (~(0xff)) +#define LANE_CFG4_REG_MASK (~(0x1f00)) + +#define REF_CLK_SELECTOR_VAL_PEX0(reg_val) (reg_val >> 2) & 0x1 +#define REF_CLK_SELECTOR_VAL_PEX1(reg_val) (reg_val >> 3) & 0x1 +#define REF_CLK_SELECTOR_VAL_PEX2(reg_val) (reg_val >> 30) & 0x1 +#define REF_CLK_SELECTOR_VAL_PEX3(reg_val) (reg_val >> 31) & 0x1 +#define REF_CLK_SELECTOR_VAL(reg_val) (reg_val & 0x1) + +#define MAX_SELECTOR_VAL 10 + +/* TWSI addresses */ +/* starting from A38x A0, i2c address of EEPROM is 0x57 */ +#ifdef CONFIG_ARMADA_39X +#define EEPROM_I2C_ADDR 0x50 +#else +#define EEPROM_I2C_ADDR (sys_env_device_rev_get() == \ + MV_88F68XX_Z1_ID ? 0x50 : 0x57) +#endif +#define RD_GET_MODE_ADDR 0x4c +#define DB_GET_MODE_SLM1363_ADDR 0x25 +#define DB_GET_MODE_SLM1364_ADDR 0x24 +#define DB381_GET_MODE_SLM1426_1427_ADDR 0x56 + +/* DB-BP Board 'SatR' mapping */ +#define SATR_DB_LANE1_MAX_OPTIONS 7 +#define SATR_DB_LANE1_CFG_MASK 0x7 +#define SATR_DB_LANE1_CFG_OFFSET 0 +#define SATR_DB_LANE2_MAX_OPTIONS 4 +#define SATR_DB_LANE2_CFG_MASK 0x38 +#define SATR_DB_LANE2_CFG_OFFSET 3 + +/* GP Board 'SatR' mapping */ +#define SATR_GP_LANE1_CFG_MASK 0x4 +#define SATR_GP_LANE1_CFG_OFFSET 2 +#define SATR_GP_LANE2_CFG_MASK 0x8 +#define SATR_GP_LANE2_CFG_OFFSET 3 + +/* For setting MPP2 and MPP3 to be TWSI mode and MPP 0,1 to UART mode */ +#define MPP_CTRL_REG 0x18000 +#define MPP_SET_MASK (~(0xffff)) +#define MPP_SET_DATA (0x1111) +#define MPP_UART1_SET_MASK (~(0xff000)) +#define MPP_UART1_SET_DATA (0x66000) + +#define AVS_DEBUG_CNTR_REG 0xe4124 +#define AVS_DEBUG_CNTR_DEFAULT_VALUE 0x08008073 + +#define AVS_ENABLED_CONTROL 0xe4130 +#define AVS_LOW_VDD_LIMIT_OFFS 4 +#define AVS_LOW_VDD_LIMIT_MASK (0xff << AVS_LOW_VDD_LIMIT_OFFS) +#define AVS_LOW_VDD_LIMIT_VAL (0x27 << AVS_LOW_VDD_LIMIT_OFFS) + +#define AVS_HIGH_VDD_LIMIT_OFFS 12 +#define AVS_HIGH_VDD_LIMIT_MASK (0xff << AVS_HIGH_VDD_LIMIT_OFFS) +#define AVS_HIGH_VDD_LIMIT_VAL (0x27 << AVS_HIGH_VDD_LIMIT_OFFS) + +/* Board ID numbers */ +#define MARVELL_BOARD_ID_MASK 0x10 +/* Customer boards for A38x */ +#define A38X_CUSTOMER_BOARD_ID_BASE 0x0 +#define A38X_CUSTOMER_BOARD_ID0 (A38X_CUSTOMER_BOARD_ID_BASE + 0) +#define A38X_CUSTOMER_BOARD_ID1 (A38X_CUSTOMER_BOARD_ID_BASE + 1) +#define A38X_MV_MAX_CUSTOMER_BOARD_ID (A38X_CUSTOMER_BOARD_ID_BASE + 2) +#define A38X_MV_CUSTOMER_BOARD_NUM (A38X_MV_MAX_CUSTOMER_BOARD_ID - \ + A38X_CUSTOMER_BOARD_ID_BASE) + +/* Marvell boards for A38x */ +#define A38X_MARVELL_BOARD_ID_BASE 0x10 +#define RD_NAS_68XX_ID (A38X_MARVELL_BOARD_ID_BASE + 0) +#define DB_68XX_ID (A38X_MARVELL_BOARD_ID_BASE + 1) +#define RD_AP_68XX_ID (A38X_MARVELL_BOARD_ID_BASE + 2) +#define DB_AP_68XX_ID (A38X_MARVELL_BOARD_ID_BASE + 3) +#define DB_GP_68XX_ID (A38X_MARVELL_BOARD_ID_BASE + 4) +#define DB_BP_6821_ID (A38X_MARVELL_BOARD_ID_BASE + 5) +#define DB_AMC_6820_ID (A38X_MARVELL_BOARD_ID_BASE + 6) +#define A38X_MV_MAX_MARVELL_BOARD_ID (A38X_MARVELL_BOARD_ID_BASE + 7) +#define A38X_MV_MARVELL_BOARD_NUM (A38X_MV_MAX_MARVELL_BOARD_ID - \ + A38X_MARVELL_BOARD_ID_BASE) + +/* Customer boards for A39x */ +#define A39X_CUSTOMER_BOARD_ID_BASE 0x20 +#define A39X_CUSTOMER_BOARD_ID0 (A39X_CUSTOMER_BOARD_ID_BASE + 0) +#define A39X_CUSTOMER_BOARD_ID1 (A39X_CUSTOMER_BOARD_ID_BASE + 1) +#define A39X_MV_MAX_CUSTOMER_BOARD_ID (A39X_CUSTOMER_BOARD_ID_BASE + 2) +#define A39X_MV_CUSTOMER_BOARD_NUM (A39X_MV_MAX_CUSTOMER_BOARD_ID - \ + A39X_CUSTOMER_BOARD_ID_BASE) + +/* Marvell boards for A39x */ +#define A39X_MARVELL_BOARD_ID_BASE 0x30 +#define A39X_DB_69XX_ID (A39X_MARVELL_BOARD_ID_BASE + 0) +#define A39X_RD_69XX_ID (A39X_MARVELL_BOARD_ID_BASE + 1) +#define A39X_MV_MAX_MARVELL_BOARD_ID (A39X_MARVELL_BOARD_ID_BASE + 2) +#define A39X_MV_MARVELL_BOARD_NUM (A39X_MV_MAX_MARVELL_BOARD_ID - \ + A39X_MARVELL_BOARD_ID_BASE) + +#ifdef CONFIG_ARMADA_38X +#define CUTOMER_BOARD_ID_BASE A38X_CUSTOMER_BOARD_ID_BASE +#define CUSTOMER_BOARD_ID0 A38X_CUSTOMER_BOARD_ID0 +#define CUSTOMER_BOARD_ID1 A38X_CUSTOMER_BOARD_ID1 +#define MV_MAX_CUSTOMER_BOARD_ID A38X_MV_MAX_CUSTOMER_BOARD_ID +#define MV_CUSTOMER_BOARD_NUM A38X_MV_CUSTOMER_BOARD_NUM +#define MARVELL_BOARD_ID_BASE A38X_MARVELL_BOARD_ID_BASE +#define MV_MAX_MARVELL_BOARD_ID A38X_MV_MAX_MARVELL_BOARD_ID +#define MV_MARVELL_BOARD_NUM A38X_MV_MARVELL_BOARD_NUM +#define MV_DEFAULT_BOARD_ID DB_68XX_ID +#define MV_DEFAULT_DEVICE_ID MV_6811 +#elif defined(CONFIG_ARMADA_39X) +#define CUTOMER_BOARD_ID_BASE A39X_CUSTOMER_BOARD_ID_BASE +#define CUSTOMER_BOARD_ID0 A39X_CUSTOMER_BOARD_ID0 +#define CUSTOMER_BOARD_ID1 A39X_CUSTOMER_BOARD_ID1 +#define MV_MAX_CUSTOMER_BOARD_ID A39X_MV_MAX_CUSTOMER_BOARD_ID +#define MV_CUSTOMER_BOARD_NUM A39X_MV_CUSTOMER_BOARD_NUM +#define MARVELL_BOARD_ID_BASE A39X_MARVELL_BOARD_ID_BASE +#define MV_MAX_MARVELL_BOARD_ID A39X_MV_MAX_MARVELL_BOARD_ID +#define MV_MARVELL_BOARD_NUM A39X_MV_MARVELL_BOARD_NUM +#define MV_DEFAULT_BOARD_ID A39X_DB_69XX_ID +#define MV_DEFAULT_DEVICE_ID MV_6920 +#endif + +#define MV_INVALID_BOARD_ID 0xffffffff + +/* device revesion */ +#define DEV_VERSION_ID_REG 0x1823c +#define REVISON_ID_OFFS 8 +#define REVISON_ID_MASK 0xf00 + +/* A38x revisions */ +#define MV_88F68XX_Z1_ID 0x0 +#define MV_88F68XX_A0_ID 0x4 +/* A39x revisions */ +#define MV_88F69XX_Z1_ID 0x2 + +#define MPP_CONTROL_REG(id) (0x18000 + (id * 4)) +#define GPP_DATA_OUT_REG(grp) (MV_GPP_REGS_BASE(grp) + 0x00) +#define GPP_DATA_OUT_EN_REG(grp) (MV_GPP_REGS_BASE(grp) + 0x04) +#define GPP_DATA_IN_REG(grp) (MV_GPP_REGS_BASE(grp) + 0x10) +#define MV_GPP_REGS_BASE(unit) (0x18100 + ((unit) * 0x40)) + +#define MPP_REG_NUM(GPIO_NUM) (GPIO_NUM / 8) +#define MPP_MASK(GPIO_NUM) (0xf << 4 * (GPIO_NUM - \ + (MPP_REG_NUM(GPIO_NUM) * 8))); +#define GPP_REG_NUM(GPIO_NUM) (GPIO_NUM / 32) +#define GPP_MASK(GPIO_NUM) (1 << GPIO_NUM % 32) + +/* device ID */ +/* Armada 38x Family */ +#define MV_6810_DEV_ID 0x6810 +#define MV_6811_DEV_ID 0x6811 +#define MV_6820_DEV_ID 0x6820 +#define MV_6828_DEV_ID 0x6828 +/* Armada 39x Family */ +#define MV_6920_DEV_ID 0x6920 +#define MV_6928_DEV_ID 0x6928 + +enum { + MV_6810, + MV_6820, + MV_6811, + MV_6828, + MV_NONE, + MV_6920, + MV_6928, + MV_MAX_DEV_ID, +}; + +#define MV_6820_INDEX 0 +#define MV_6810_INDEX 1 +#define MV_6811_INDEX 2 +#define MV_6828_INDEX 3 + +#define MV_6920_INDEX 0 +#define MV_6928_INDEX 1 + +#ifdef CONFIG_ARMADA_38X +#define MAX_DEV_ID_NUM 4 +#else +#define MAX_DEV_ID_NUM 2 +#endif + +#define MV_6820_INDEX 0 +#define MV_6810_INDEX 1 +#define MV_6811_INDEX 2 +#define MV_6828_INDEX 3 +#define MV_6920_INDEX 0 +#define MV_6928_INDEX 1 + +enum unit_id { + PEX_UNIT_ID, + ETH_GIG_UNIT_ID, + USB3H_UNIT_ID, + USB3D_UNIT_ID, + SATA_UNIT_ID, + QSGMII_UNIT_ID, + XAUI_UNIT_ID, + RXAUI_UNIT_ID, + MAX_UNITS_ID +}; + +struct board_wakeup_gpio { + u32 board_id; + int gpio_num; +}; + +enum suspend_wakeup_status { + SUSPEND_WAKEUP_DISABLED, + SUSPEND_WAKEUP_ENABLED, + SUSPEND_WAKEUP_ENABLED_GPIO_DETECTED, +}; + +/* + * GPIO status indication for Suspend Wakeup: + * If suspend to RAM is supported and GPIO inidcation is implemented, + * set the gpio number + * If suspend to RAM is supported but GPIO indication is not implemented + * set '-2' + * If suspend to RAM is not supported set '-1' + */ +#ifdef CONFIG_CUSTOMER_BOARD_SUPPORT +#ifdef CONFIG_ARMADA_38X +#define MV_BOARD_WAKEUP_GPIO_INFO { \ + {A38X_CUSTOMER_BOARD_ID0, -1 }, \ + {A38X_CUSTOMER_BOARD_ID0, -1 }, \ +}; +#else +#define MV_BOARD_WAKEUP_GPIO_INFO { \ + {A39X_CUSTOMER_BOARD_ID0, -1 }, \ + {A39X_CUSTOMER_BOARD_ID0, -1 }, \ +}; +#endif /* CONFIG_ARMADA_38X */ + +#else + +#ifdef CONFIG_ARMADA_38X +#define MV_BOARD_WAKEUP_GPIO_INFO { \ + {RD_NAS_68XX_ID, -2 }, \ + {DB_68XX_ID, -1 }, \ + {RD_AP_68XX_ID, -2 }, \ + {DB_AP_68XX_ID, -2 }, \ + {DB_GP_68XX_ID, -2 }, \ + {DB_BP_6821_ID, -2 }, \ + {DB_AMC_6820_ID, -2 }, \ +}; +#else +#define MV_BOARD_WAKEUP_GPIO_INFO { \ + {A39X_RD_69XX_ID, -1 }, \ + {A39X_DB_69XX_ID, -1 }, \ +}; +#endif /* CONFIG_ARMADA_38X */ +#endif /* CONFIG_CUSTOMER_BOARD_SUPPORT */ + +u32 mv_board_tclk_get(void); +u32 mv_board_id_get(void); +u32 mv_board_id_index_get(u32 board_id); +u32 sys_env_unit_max_num_get(enum unit_id unit); +enum suspend_wakeup_status sys_env_suspend_wakeup_check(void); +u8 sys_env_device_rev_get(void); +u32 sys_env_device_id_get(void); +u16 sys_env_model_get(void); +struct dlb_config *sys_env_dlb_config_ptr_get(void); +u32 sys_env_get_topology_update_info( + struct topology_update_info *topology_update_info); +u32 sys_env_get_cs_ena_from_reg(void); + +#endif /* _SYS_ENV_LIB_H */ |