123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright (C) Marvell International Ltd. and its affiliates
- */
- #include <common.h>
- #include <spl.h>
- #include <asm/io.h>
- #include <asm/arch/cpu.h>
- #include <asm/arch/soc.h>
- #include <linux/delay.h>
- #include "ctrl_pex.h"
- #include "sys_env_lib.h"
- __weak void board_pex_config(void)
- {
- /* nothing in this weak default implementation */
- }
- int hws_pex_config(const struct serdes_map *serdes_map, u8 count)
- {
- 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;
- DEBUG_INIT_FULL_S("\n### hws_pex_config ###\n");
- for (idx = 0; idx < count; 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 < count; 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");
- board_pex_config();
- next_busno = 0;
- mdelay(150);
- for (idx = 0; idx < count; 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 capabilities\n");
- }
- /* Update pex DEVICE ID */
- ctrl_mode = sys_env_model_get();
- for (idx = 0; idx < count; 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;
- }
|