mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2025-12-21 17:32:09 +01:00
Add R-Car Gen4 PCIe controller support for host mode. This controller is based on Synopsys DesignWare PCIe. However, this particular controller has a number of vendor-specific registers, and as such, requires initialization code, including PHY firmware loading. The PHY firmware loading is implemented in an entirely generic manner, by calling a firmware loading script, which the user can configure in a way they require. This provides the user with flexibility of loading the PCIe firmware from whichever storage device they need to load it from. Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
566 lines
15 KiB
C
566 lines
15 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* PCIe controller driver for Renesas R-Car Gen4 Series SoCs
|
|
* Copyright (C) 2025 Marek Vasut <marek.vasut+renesas@mailbox.org>
|
|
* Based on Linux kernel driver
|
|
* Copyright (C) 2022-2023 Renesas Electronics Corporation
|
|
*
|
|
* The r8a779g0 (R-Car V4H) controller requires a specific firmware to be
|
|
* provided, to initialize the PHY. Otherwise, the PCIe controller will not
|
|
* work.
|
|
*/
|
|
|
|
#include <asm-generic/gpio.h>
|
|
#include <asm/arch/gpio.h>
|
|
#include <asm/global_data.h>
|
|
#include <asm/io.h>
|
|
#include <clk.h>
|
|
#include <command.h>
|
|
#include <dm.h>
|
|
#include <dm/device_compat.h>
|
|
#include <env.h>
|
|
#include <log.h>
|
|
#include <reset.h>
|
|
|
|
#include <linux/bitfield.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/err.h>
|
|
#include <linux/iopoll.h>
|
|
|
|
#include "pcie_dw_common.h"
|
|
|
|
/* Renesas-specific */
|
|
/* PCIe Mode Setting Register 0 */
|
|
#define PCIEMSR0 0x0000
|
|
#define APP_SRIS_MODE BIT(6)
|
|
#define DEVICE_TYPE_EP 0
|
|
#define DEVICE_TYPE_RC BIT(4)
|
|
#define BIFUR_MOD_SET_ON BIT(0)
|
|
|
|
/* PCIe Interrupt Status 0 */
|
|
#define PCIEINTSTS0 0x0084
|
|
|
|
/* PCIe Interrupt Status 0 Enable */
|
|
#define PCIEINTSTS0EN 0x0310
|
|
#define MSI_CTRL_INT BIT(26)
|
|
#define SMLH_LINK_UP BIT(7)
|
|
#define RDLH_LINK_UP BIT(6)
|
|
|
|
/* PCIe DMA Interrupt Status Enable */
|
|
#define PCIEDMAINTSTSEN 0x0314
|
|
#define PCIEDMAINTSTSEN_INIT GENMASK(15, 0)
|
|
|
|
/* Port Logic Registers 89 */
|
|
#define PRTLGC89 0x0b70
|
|
|
|
/* Port Logic Registers 90 */
|
|
#define PRTLGC90 0x0b74
|
|
|
|
/* PCIe Reset Control Register 1 */
|
|
#define PCIERSTCTRL1 0x0014
|
|
#define APP_HOLD_PHY_RST BIT(16)
|
|
#define APP_LTSSM_ENABLE BIT(0)
|
|
|
|
/* PCIe Power Management Control */
|
|
#define PCIEPWRMNGCTRL 0x0070
|
|
#define APP_CLK_REQ_N BIT(11)
|
|
#define APP_CLK_PM_EN BIT(10)
|
|
|
|
#define RCAR_NUM_SPEED_CHANGE_RETRIES 10
|
|
#define RCAR_MAX_LINK_SPEED 4
|
|
|
|
#define RCAR_GEN4_PCIE_EP_FUNC_DBI_OFFSET 0x1000
|
|
#define RCAR_GEN4_PCIE_EP_FUNC_DBI2_OFFSET 0x800
|
|
|
|
#define RCAR_GEN4_PCIE_FIRMWARE_NAME "rcar_gen4_pcie.bin"
|
|
#define RCAR_GEN4_PCIE_FIRMWARE_BASE_ADDR 0xc000
|
|
|
|
#define PCIE_T_PVPERL_MS 100
|
|
|
|
/**
|
|
* struct rcar_gen4_pcie - Renesas R-Car Gen4 DW PCIe controller state
|
|
*
|
|
* @rcar: The common PCIe DW structure
|
|
* @pwr_rst: The PWR reset of the PCIe core
|
|
* @core_clk: The core clock of the PCIe core
|
|
* @ref_clk: The reference clock of the PCIe core and possibly bus
|
|
* @pe_rst: PERST GPIO
|
|
* @app_base: The base address of application register space
|
|
* @dbi2_base: The base address of DBI2 register space
|
|
* @phy_base: The base address of PHY register space
|
|
* @max_link_speed: Maximum PCIe link speed supported by the setup
|
|
* @num_lanes: Number of PCIe lanes used by the setup
|
|
* @firmware: PHY firmware
|
|
* @firmware_size: PHY firmware size in Bytes
|
|
*/
|
|
struct rcar_gen4_pcie {
|
|
/* Must be first member of the struct */
|
|
struct pcie_dw dw;
|
|
struct reset_ctl pwr_rst;
|
|
struct clk *core_clk;
|
|
struct clk *ref_clk;
|
|
struct gpio_desc pe_rst;
|
|
void *app_base;
|
|
void *dbi2_base;
|
|
void *phy_base;
|
|
u32 max_link_speed;
|
|
u32 num_lanes;
|
|
u16 *firmware;
|
|
u32 firmware_size;
|
|
};
|
|
|
|
/* Common */
|
|
static bool rcar_gen4_pcie_link_up(struct rcar_gen4_pcie *rcar)
|
|
{
|
|
u32 val, mask;
|
|
|
|
val = readl(rcar->app_base + PCIEINTSTS0);
|
|
mask = RDLH_LINK_UP | SMLH_LINK_UP;
|
|
|
|
return (val & mask) == mask;
|
|
}
|
|
|
|
/*
|
|
* Manually initiate the speed change. Return 0 if change succeeded; otherwise
|
|
* -ETIMEDOUT.
|
|
*/
|
|
static int rcar_gen4_pcie_speed_change(struct rcar_gen4_pcie *rcar)
|
|
{
|
|
u32 val;
|
|
int i;
|
|
|
|
clrbits_le32(rcar->dw.dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL,
|
|
PORT_LOGIC_SPEED_CHANGE);
|
|
|
|
setbits_le32(rcar->dw.dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL,
|
|
PORT_LOGIC_SPEED_CHANGE);
|
|
|
|
for (i = 0; i < RCAR_NUM_SPEED_CHANGE_RETRIES; i++) {
|
|
val = readl(rcar->dw.dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
|
|
if (!(val & PORT_LOGIC_SPEED_CHANGE))
|
|
return 0;
|
|
mdelay(10);
|
|
}
|
|
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
/*
|
|
* SoC datasheet suggests checking port logic register bits during firmware
|
|
* write. If read returns non-zero value, then this function returns -EAGAIN
|
|
* indicating that the write needs to be done again. If read returns zero,
|
|
* then return 0 to indicate success.
|
|
*/
|
|
static int rcar_gen4_pcie_reg_test_bit(struct rcar_gen4_pcie *rcar,
|
|
u32 offset, u32 mask)
|
|
{
|
|
if (readl(rcar->dw.dbi_base + offset) & mask)
|
|
return -EAGAIN;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rcar_gen4_pcie_download_phy_firmware(struct rcar_gen4_pcie *rcar)
|
|
{
|
|
/* The check_addr values are magical numbers in the datasheet */
|
|
static const u32 check_addr[] = {
|
|
0x00101018,
|
|
0x00101118,
|
|
0x00101021,
|
|
0x00101121,
|
|
};
|
|
unsigned int i, timeout;
|
|
u32 data;
|
|
int ret;
|
|
|
|
for (i = 0; i < rcar->firmware_size / 2; i++) {
|
|
data = rcar->firmware[i];
|
|
timeout = 100;
|
|
do {
|
|
writel(RCAR_GEN4_PCIE_FIRMWARE_BASE_ADDR + i, rcar->dw.dbi_base + PRTLGC89);
|
|
writel(data, rcar->dw.dbi_base + PRTLGC90);
|
|
if (!rcar_gen4_pcie_reg_test_bit(rcar, PRTLGC89, BIT(30)))
|
|
break;
|
|
if (!(--timeout))
|
|
return -ETIMEDOUT;
|
|
udelay(100);
|
|
} while (1);
|
|
}
|
|
|
|
setbits_le32(rcar->phy_base + 0x0f8, BIT(17));
|
|
|
|
for (i = 0; i < ARRAY_SIZE(check_addr); i++) {
|
|
timeout = 100;
|
|
do {
|
|
writel(check_addr[i], rcar->dw.dbi_base + PRTLGC89);
|
|
ret = rcar_gen4_pcie_reg_test_bit(rcar, PRTLGC89, BIT(30));
|
|
ret |= rcar_gen4_pcie_reg_test_bit(rcar, PRTLGC90, BIT(0));
|
|
if (!ret)
|
|
break;
|
|
if (!(--timeout))
|
|
return -ETIMEDOUT;
|
|
udelay(100);
|
|
} while (1);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rcar_gen4_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable)
|
|
{
|
|
u32 val;
|
|
int ret;
|
|
|
|
if (!enable) {
|
|
clrbits_le32(rcar->app_base + PCIERSTCTRL1, APP_LTSSM_ENABLE);
|
|
return 0;
|
|
}
|
|
|
|
setbits_le32(rcar->dw.dbi_base + PCIE_PORT_FORCE,
|
|
PORT_FORCE_DO_DESKEW_FOR_SRIS);
|
|
|
|
setbits_le32(rcar->app_base + PCIEMSR0, APP_SRIS_MODE);
|
|
|
|
/*
|
|
* The R-Car Gen4 datasheet doesn't describe the PHY registers' name.
|
|
* But, the initialization procedure describes these offsets. So,
|
|
* this driver has magical offset numbers.
|
|
*/
|
|
clrsetbits_le32(rcar->phy_base + 0x700, BIT(28), 0);
|
|
clrsetbits_le32(rcar->phy_base + 0x700, BIT(20), 0);
|
|
clrsetbits_le32(rcar->phy_base + 0x700, BIT(12), 0);
|
|
clrsetbits_le32(rcar->phy_base + 0x700, BIT(4), 0);
|
|
|
|
clrsetbits_le32(rcar->phy_base + 0x148, GENMASK(23, 22), BIT(22));
|
|
clrsetbits_le32(rcar->phy_base + 0x148, GENMASK(18, 16), GENMASK(17, 16));
|
|
clrsetbits_le32(rcar->phy_base + 0x148, GENMASK(7, 6), BIT(6));
|
|
clrsetbits_le32(rcar->phy_base + 0x148, GENMASK(2, 0), GENMASK(11, 0));
|
|
clrsetbits_le32(rcar->phy_base + 0x1d4, GENMASK(16, 15), GENMASK(16, 15));
|
|
clrsetbits_le32(rcar->phy_base + 0x514, BIT(26), BIT(26));
|
|
clrsetbits_le32(rcar->phy_base + 0x0f8, BIT(16), 0);
|
|
clrsetbits_le32(rcar->phy_base + 0x0f8, BIT(19), BIT(19));
|
|
|
|
clrbits_le32(rcar->app_base + PCIERSTCTRL1, APP_HOLD_PHY_RST);
|
|
|
|
ret = readl_poll_timeout(rcar->phy_base + 0x0f8, val, !(val & BIT(18)), 10000);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = rcar_gen4_pcie_download_phy_firmware(rcar);
|
|
if (ret)
|
|
return ret;
|
|
|
|
setbits_le32(rcar->app_base + PCIERSTCTRL1, APP_LTSSM_ENABLE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Enable LTSSM of this controller and manually initiate the speed change.
|
|
* Always return 0.
|
|
*/
|
|
static int rcar_gen4_pcie_start_link(struct rcar_gen4_pcie *rcar)
|
|
{
|
|
int i, ret;
|
|
|
|
ret = rcar_gen4_pcie_ltssm_control(rcar, true);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/*
|
|
* Require direct speed change with retrying here if the max_link_speed
|
|
* is PCIe Gen2 or higher.
|
|
*/
|
|
if (rcar->max_link_speed == LINK_SPEED_GEN_1)
|
|
return 0;
|
|
|
|
for (i = 0; i < RCAR_MAX_LINK_SPEED; i++) {
|
|
/* It may not be connected in EP mode yet. So, break the loop */
|
|
if (rcar_gen4_pcie_speed_change(rcar))
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rcar_gen4_pcie_additional_common_init(struct rcar_gen4_pcie *rcar)
|
|
{
|
|
clrsetbits_le32(rcar->dw.dbi_base + PCIE_PORT_LANE_SKEW,
|
|
PORT_LANE_SKEW_INSERT_MASK,
|
|
(rcar->num_lanes < 4) ? BIT(6) : 0);
|
|
|
|
setbits_le32(rcar->app_base + PCIEPWRMNGCTRL,
|
|
APP_CLK_REQ_N | APP_CLK_PM_EN);
|
|
}
|
|
|
|
static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)
|
|
{
|
|
int ret;
|
|
|
|
ret = clk_prepare_enable(rcar->core_clk);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = reset_assert(&rcar->pwr_rst);
|
|
if (ret)
|
|
goto err_unprepare;
|
|
|
|
setbits_le32(rcar->app_base + PCIEMSR0,
|
|
DEVICE_TYPE_RC |
|
|
((rcar->num_lanes < 4) ? BIFUR_MOD_SET_ON : 0));
|
|
|
|
ret = reset_deassert(&rcar->pwr_rst);
|
|
if (ret)
|
|
goto err_unprepare;
|
|
|
|
rcar_gen4_pcie_additional_common_init(rcar);
|
|
|
|
return 0;
|
|
|
|
err_unprepare:
|
|
clk_disable_unprepare(rcar->core_clk);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Host mode */
|
|
static int rcar_gen4_pcie_host_init(struct udevice *dev)
|
|
{
|
|
struct rcar_gen4_pcie *rcar = dev_get_priv(dev);
|
|
int ret;
|
|
|
|
dm_gpio_set_value(&rcar->pe_rst, 1);
|
|
|
|
ret = rcar_gen4_pcie_common_init(rcar);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/*
|
|
* According to the section 3.5.7.2 "RC Mode" in DWC PCIe Dual Mode
|
|
* Rev.5.20a and 3.5.6.1 "RC mode" in DWC PCIe RC databook v5.20a, we
|
|
* should disable two BARs to avoid unnecessary memory assignment
|
|
* during device enumeration.
|
|
*/
|
|
writel(0x0, rcar->dbi2_base + PCI_BASE_ADDRESS_0);
|
|
writel(0x0, rcar->dbi2_base + PCI_BASE_ADDRESS_1);
|
|
|
|
/* Disable MSI interrupt signal */
|
|
clrbits_le32(rcar->app_base + PCIEINTSTS0EN, MSI_CTRL_INT);
|
|
|
|
mdelay(PCIE_T_PVPERL_MS); /* pe_rst requires 100msec delay */
|
|
|
|
dm_gpio_set_value(&rcar->pe_rst, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rcar_gen4_pcie_load_firmware(struct rcar_gen4_pcie *rcar)
|
|
{
|
|
ulong addr, size;
|
|
int ret;
|
|
|
|
/*
|
|
* Run user specified firmware loading script, which loads the
|
|
* firmware from whichever location the user decides it should
|
|
* load the firmware from, by whatever means the user decides.
|
|
*/
|
|
ret = run_command_list("run renesas_rcar_gen4_load_firmware", -1, 0);
|
|
if (ret) {
|
|
printf("Firmware loading script 'renesas_rcar_gen4_load_firmware' not defined or failed.\n");
|
|
goto fail;
|
|
}
|
|
|
|
/* Find out where the firmware got loaded and how long it is. */
|
|
addr = env_get_hex("renesas_rcar_gen4_load_firmware_addr", 0);
|
|
size = env_get_hex("renesas_rcar_gen4_load_firmware_size", 0);
|
|
|
|
/*
|
|
* Clear the variables set by the firmware loading script, as
|
|
* their content would become stale once this function exits.
|
|
*/
|
|
env_set("renesas_rcar_gen4_load_firmware_addr", NULL);
|
|
env_set("renesas_rcar_gen4_load_firmware_size", NULL);
|
|
|
|
if (!addr || !size) {
|
|
printf("Firmware address (%lx) or size (%lx) are invalid.\n", addr, size);
|
|
goto fail;
|
|
}
|
|
|
|
/* Create local copy of the loaded firmware. */
|
|
rcar->firmware = (u16 *)memdup((void *)addr, size);
|
|
if (!rcar->firmware)
|
|
return -ENOMEM;
|
|
|
|
rcar->firmware_size = size;
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
printf("Define 'renesas_rcar_gen4_load_firmware' script which loads the R-Car\n"
|
|
"Gen4 PCIe controller firmware from storage into memory and sets these\n"
|
|
"two environment variables:\n"
|
|
" renesas_rcar_gen4_load_firmware_addr ... address of firmware in memory\n"
|
|
" renesas_rcar_gen4_load_firmware_size ... length of firmware in bytes\n"
|
|
"\n"
|
|
"Example:\n"
|
|
" => env set renesas_rcar_gen4_load_firmware 'env set renesas_rcar_gen4_load_firmware_addr 0x54000000 && load mmc 0:1 ${renesas_rcar_gen4_load_firmware_addr} lib/firmware/rcar_gen4_pcie.bin && env set renesas_rcar_gen4_load_firmware_size ${filesize}'\n"
|
|
);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/**
|
|
* rcar_gen4_pcie_probe() - Probe the PCIe bus for active link
|
|
*
|
|
* @dev: A pointer to the device being operated on
|
|
*
|
|
* Probe for an active link on the PCIe bus and configure the controller
|
|
* to enable this port.
|
|
*
|
|
* Return: 0 on success, else -ENODEV
|
|
*/
|
|
static int rcar_gen4_pcie_probe(struct udevice *dev)
|
|
{
|
|
struct rcar_gen4_pcie *rcar = dev_get_priv(dev);
|
|
struct udevice *ctlr = pci_get_controller(dev);
|
|
struct pci_controller *hose = dev_get_uclass_priv(ctlr);
|
|
int ret;
|
|
|
|
ret = rcar_gen4_pcie_load_firmware(rcar);
|
|
if (ret)
|
|
return ret;
|
|
|
|
rcar->dw.first_busno = dev_seq(dev);
|
|
rcar->dw.dev = dev;
|
|
|
|
ret = reset_get_by_name(dev, "pwr", &rcar->pwr_rst);
|
|
if (ret)
|
|
return ret;
|
|
|
|
rcar->core_clk = devm_clk_get(dev, "core");
|
|
if (IS_ERR(rcar->core_clk))
|
|
return PTR_ERR(rcar->core_clk);
|
|
|
|
rcar->ref_clk = devm_clk_get(dev, "ref");
|
|
if (IS_ERR(rcar->ref_clk))
|
|
return PTR_ERR(rcar->ref_clk);
|
|
|
|
ret = clk_prepare_enable(rcar->ref_clk);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = gpio_request_by_name(dev, "reset-gpios", 0, &rcar->pe_rst,
|
|
GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = rcar_gen4_pcie_host_init(dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
pcie_dw_setup_host(&rcar->dw);
|
|
|
|
dw_pcie_dbi_write_enable(&rcar->dw, true);
|
|
|
|
dw_pcie_link_set_max_link_width(&rcar->dw, rcar->num_lanes);
|
|
|
|
ret = rcar_gen4_pcie_start_link(rcar);
|
|
if (ret)
|
|
return ret;
|
|
|
|
dw_pcie_dbi_write_enable(&rcar->dw, false);
|
|
|
|
if (!rcar_gen4_pcie_link_up(rcar)) {
|
|
printf("PCIE-%d: Link down\n", dev_seq(dev));
|
|
return -ENODEV;
|
|
}
|
|
|
|
printf("PCIE-%d: Link up (Gen%d-x%d, Bus%d)\n", dev_seq(dev),
|
|
pcie_dw_get_link_speed(&rcar->dw),
|
|
pcie_dw_get_link_width(&rcar->dw),
|
|
hose->first_busno);
|
|
|
|
pcie_dw_prog_outbound_atu_unroll(&rcar->dw, PCIE_ATU_REGION_INDEX0,
|
|
PCIE_ATU_TYPE_MEM,
|
|
rcar->dw.mem.phys_start,
|
|
rcar->dw.mem.bus_start, rcar->dw.mem.size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* rcar_gen4_pcie_of_to_plat() - Translate from DT to device state
|
|
*
|
|
* @dev: A pointer to the device being operated on
|
|
*
|
|
* Translate relevant data from the device tree pertaining to device @dev into
|
|
* state that the driver will later make use of. This state is stored in the
|
|
* device's private data structure.
|
|
*
|
|
* Return: 0 on success, else -EINVAL
|
|
*/
|
|
static int rcar_gen4_pcie_of_to_plat(struct udevice *dev)
|
|
{
|
|
struct rcar_gen4_pcie *rcar = dev_get_priv(dev);
|
|
|
|
/* Get the controller base address */
|
|
rcar->dw.dbi_base = (void *)dev_read_addr_name(dev, "dbi");
|
|
if ((fdt_addr_t)rcar->dw.dbi_base == FDT_ADDR_T_NONE)
|
|
return -EINVAL;
|
|
|
|
/* Get the config space base address and size */
|
|
rcar->dw.cfg_base = (void *)dev_read_addr_size_name(dev, "config",
|
|
&rcar->dw.cfg_size);
|
|
if ((fdt_addr_t)rcar->dw.cfg_base == FDT_ADDR_T_NONE)
|
|
return -EINVAL;
|
|
|
|
/* Get the iATU base address and size */
|
|
rcar->dw.atu_base = (void *)dev_read_addr_name(dev, "atu");
|
|
if ((fdt_addr_t)rcar->dw.atu_base == FDT_ADDR_T_NONE)
|
|
return -EINVAL;
|
|
|
|
/* Get the PHY base address and size */
|
|
rcar->phy_base = (void *)dev_read_addr_name(dev, "phy");
|
|
if ((fdt_addr_t)rcar->phy_base == FDT_ADDR_T_NONE)
|
|
return -EINVAL;
|
|
|
|
/* Get the app base address and size */
|
|
rcar->app_base = (void *)dev_read_addr_name(dev, "app");
|
|
if ((fdt_addr_t)rcar->app_base == FDT_ADDR_T_NONE)
|
|
return -EINVAL;
|
|
|
|
/* Get the dbi2 base address and size */
|
|
rcar->dbi2_base = (void *)dev_read_addr_name(dev, "dbi2");
|
|
if ((fdt_addr_t)rcar->dbi2_base == FDT_ADDR_T_NONE)
|
|
return -EINVAL;
|
|
|
|
rcar->max_link_speed =
|
|
clamp(dev_read_u32_default(dev, "max-link-speed",
|
|
LINK_SPEED_GEN_4),
|
|
LINK_SPEED_GEN_1, RCAR_MAX_LINK_SPEED);
|
|
|
|
rcar->num_lanes = dev_read_u32_default(dev, "num-lanes", 4);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct dm_pci_ops rcar_gen4_pcie_ops = {
|
|
.read_config = pcie_dw_read_config,
|
|
.write_config = pcie_dw_write_config,
|
|
};
|
|
|
|
static const struct udevice_id rcar_gen4_pcie_ids[] = {
|
|
{ .compatible = "renesas,rcar-gen4-pcie" },
|
|
{ }
|
|
};
|
|
|
|
U_BOOT_DRIVER(rcar_gen4_pcie) = {
|
|
.name = "rcar_gen4_pcie",
|
|
.id = UCLASS_PCI,
|
|
.of_match = rcar_gen4_pcie_ids,
|
|
.ops = &rcar_gen4_pcie_ops,
|
|
.of_to_plat = rcar_gen4_pcie_of_to_plat,
|
|
.probe = rcar_gen4_pcie_probe,
|
|
.priv_auto = sizeof(struct rcar_gen4_pcie),
|
|
};
|