blob: 87cd69f989d8bf53bc2daa219bfc3f9bc1af17a5 [file] [log] [blame]
// 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),
};