mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-13 06:42:56 +00:00
490753ace3
Instead of allocating space in the driver for the serdes specification table, just allow the board file to set a pointer to it. Also, allow the board to only specify the lanes that are used instead of including unused lanes. Signed-off-by: Kevin Smith <kevin.smith@elecsyscorp.com> Acked-by: Stefan Roese <sr@denx.de> Cc: Dirk Eibach <eibach@gdsys.de> Cc: Luka Perkov <luka.perkov@sartura.hr>
346 lines
9.4 KiB
C
346 lines
9.4 KiB
C
/*
|
|
* 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(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");
|
|
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 cpabilities\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;
|
|
}
|