diff options
Diffstat (limited to 'drivers/pci/controller/dwc/pcie-rcar-gen4.c')
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-rcar-gen4.c | 308 | 
1 files changed, 272 insertions, 36 deletions
| diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c index cfeccc2f9ee1..f0f3ebd1a033 100644 --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c @@ -2,11 +2,17 @@  /*   * PCIe controller driver for Renesas R-Car Gen4 Series SoCs   * 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 <linux/delay.h> +#include <linux/firmware.h>  #include <linux/interrupt.h>  #include <linux/io.h> +#include <linux/iopoll.h>  #include <linux/module.h>  #include <linux/of.h>  #include <linux/pci.h> @@ -20,9 +26,10 @@  /* Renesas-specific */  /* PCIe Mode Setting Register 0 */  #define PCIEMSR0		0x0000 -#define BIFUR_MOD_SET_ON	BIT(0) +#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 @@ -37,47 +44,49 @@  #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 +MODULE_FIRMWARE(RCAR_GEN4_PCIE_FIRMWARE_NAME); + +struct rcar_gen4_pcie; +struct rcar_gen4_pcie_drvdata { +	void (*additional_common_init)(struct rcar_gen4_pcie *rcar); +	int (*ltssm_control)(struct rcar_gen4_pcie *rcar, bool enable); +	enum dw_pcie_device_mode mode; +}; +  struct rcar_gen4_pcie {  	struct dw_pcie dw;  	void __iomem *base; +	void __iomem *phy_base;  	struct platform_device *pdev; -	enum dw_pcie_device_mode mode; +	const struct rcar_gen4_pcie_drvdata *drvdata;  };  #define to_rcar_gen4_pcie(_dw)	container_of(_dw, struct rcar_gen4_pcie, dw)  /* Common */ -static void rcar_gen4_pcie_ltssm_enable(struct rcar_gen4_pcie *rcar, -					bool enable) -{ -	u32 val; - -	val = readl(rcar->base + PCIERSTCTRL1); -	if (enable) { -		val |= APP_LTSSM_ENABLE; -		val &= ~APP_HOLD_PHY_RST; -	} else { -		/* -		 * Since the datasheet of R-Car doesn't mention how to assert -		 * the APP_HOLD_PHY_RST, don't assert it again. Otherwise, -		 * hang-up issue happened in the dw_edma_core_off() when -		 * the controller didn't detect a PCI device. -		 */ -		val &= ~APP_LTSSM_ENABLE; -	} -	writel(val, rcar->base + PCIERSTCTRL1); -} -  static int rcar_gen4_pcie_link_up(struct dw_pcie *dw)  {  	struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw); @@ -123,9 +132,13 @@ static int rcar_gen4_pcie_speed_change(struct dw_pcie *dw)  static int rcar_gen4_pcie_start_link(struct dw_pcie *dw)  {  	struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw); -	int i, changes; +	int i, changes, ret; -	rcar_gen4_pcie_ltssm_enable(rcar, true); +	if (rcar->drvdata->ltssm_control) { +		ret = rcar->drvdata->ltssm_control(rcar, true); +		if (ret) +			return ret; +	}  	/*  	 * Require direct speed change with retrying here if the link_gen is @@ -137,7 +150,7 @@ static int rcar_gen4_pcie_start_link(struct dw_pcie *dw)  	 * Since dw_pcie_setup_rc() sets it once, PCIe Gen2 will be trained.  	 * So, this needs remaining times for up to PCIe Gen4 if RC mode.  	 */ -	if (changes && rcar->mode == DW_PCIE_RC_TYPE) +	if (changes && rcar->drvdata->mode == DW_PCIE_RC_TYPE)  		changes--;  	for (i = 0; i < changes; i++) { @@ -153,7 +166,8 @@ static void rcar_gen4_pcie_stop_link(struct dw_pcie *dw)  {  	struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw); -	rcar_gen4_pcie_ltssm_enable(rcar, false); +	if (rcar->drvdata->ltssm_control) +		rcar->drvdata->ltssm_control(rcar, false);  }  static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar) @@ -172,9 +186,9 @@ static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)  		reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc);  	val = readl(rcar->base + PCIEMSR0); -	if (rcar->mode == DW_PCIE_RC_TYPE) { +	if (rcar->drvdata->mode == DW_PCIE_RC_TYPE) {  		val |= DEVICE_TYPE_RC; -	} else if (rcar->mode == DW_PCIE_EP_TYPE) { +	} else if (rcar->drvdata->mode == DW_PCIE_EP_TYPE) {  		val |= DEVICE_TYPE_EP;  	} else {  		ret = -EINVAL; @@ -190,6 +204,9 @@ static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)  	if (ret)  		goto err_unprepare; +	if (rcar->drvdata->additional_common_init) +		rcar->drvdata->additional_common_init(rcar); +  	return 0;  err_unprepare: @@ -231,6 +248,10 @@ static void rcar_gen4_pcie_unprepare(struct rcar_gen4_pcie *rcar)  static int rcar_gen4_pcie_get_resources(struct rcar_gen4_pcie *rcar)  { +	rcar->phy_base = devm_platform_ioremap_resource_byname(rcar->pdev, "phy"); +	if (IS_ERR(rcar->phy_base)) +		return PTR_ERR(rcar->phy_base); +  	/* Renesas-specific registers */  	rcar->base = devm_platform_ioremap_resource_byname(rcar->pdev, "app"); @@ -255,7 +276,7 @@ static struct rcar_gen4_pcie *rcar_gen4_pcie_alloc(struct platform_device *pdev)  	rcar->dw.ops = &dw_pcie_ops;  	rcar->dw.dev = dev;  	rcar->pdev = pdev; -	dw_pcie_cap_set(&rcar->dw, EDMA_UNROLL); +	rcar->dw.edma.mf = EDMA_MF_EDMA_UNROLL;  	dw_pcie_cap_set(&rcar->dw, REQ_RES);  	platform_set_drvdata(pdev, rcar); @@ -437,7 +458,7 @@ static int rcar_gen4_add_dw_pcie_ep(struct rcar_gen4_pcie *rcar)  		rcar_gen4_pcie_ep_deinit(rcar);  	} -	dw_pcie_ep_init_notify(ep); +	pci_epc_init_notify(ep->epc);  	return ret;  } @@ -451,9 +472,11 @@ static void rcar_gen4_remove_dw_pcie_ep(struct rcar_gen4_pcie *rcar)  /* Common */  static int rcar_gen4_add_dw_pcie(struct rcar_gen4_pcie *rcar)  { -	rcar->mode = (uintptr_t)of_device_get_match_data(&rcar->pdev->dev); +	rcar->drvdata = of_device_get_match_data(&rcar->pdev->dev); +	if (!rcar->drvdata) +		return -EINVAL; -	switch (rcar->mode) { +	switch (rcar->drvdata->mode) {  	case DW_PCIE_RC_TYPE:  		return rcar_gen4_add_dw_pcie_rp(rcar);  	case DW_PCIE_EP_TYPE: @@ -494,7 +517,7 @@ err_unprepare:  static void rcar_gen4_remove_dw_pcie(struct rcar_gen4_pcie *rcar)  { -	switch (rcar->mode) { +	switch (rcar->drvdata->mode) {  	case DW_PCIE_RC_TYPE:  		rcar_gen4_remove_dw_pcie_rp(rcar);  		break; @@ -514,14 +537,227 @@ static void rcar_gen4_pcie_remove(struct platform_device *pdev)  	rcar_gen4_pcie_unprepare(rcar);  } +static int r8a779f0_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable) +{ +	u32 val; + +	val = readl(rcar->base + PCIERSTCTRL1); +	if (enable) { +		val |= APP_LTSSM_ENABLE; +		val &= ~APP_HOLD_PHY_RST; +	} else { +		/* +		 * Since the datasheet of R-Car doesn't mention how to assert +		 * the APP_HOLD_PHY_RST, don't assert it again. Otherwise, +		 * hang-up issue happened in the dw_edma_core_off() when +		 * the controller didn't detect a PCI device. +		 */ +		val &= ~APP_LTSSM_ENABLE; +	} +	writel(val, rcar->base + PCIERSTCTRL1); + +	return 0; +} + +static void rcar_gen4_pcie_additional_common_init(struct rcar_gen4_pcie *rcar) +{ +	struct dw_pcie *dw = &rcar->dw; +	u32 val; + +	val = dw_pcie_readl_dbi(dw, PCIE_PORT_LANE_SKEW); +	val &= ~PORT_LANE_SKEW_INSERT_MASK; +	if (dw->num_lanes < 4) +		val |= BIT(6); +	dw_pcie_writel_dbi(dw, PCIE_PORT_LANE_SKEW, val); + +	val = readl(rcar->base + PCIEPWRMNGCTRL); +	val |= APP_CLK_REQ_N | APP_CLK_PM_EN; +	writel(val, rcar->base + PCIEPWRMNGCTRL); +} + +static void rcar_gen4_pcie_phy_reg_update_bits(struct rcar_gen4_pcie *rcar, +					       u32 offset, u32 mask, u32 val) +{ +	u32 tmp; + +	tmp = readl(rcar->phy_base + offset); +	tmp &= ~mask; +	tmp |= val; +	writel(tmp, rcar->phy_base + offset); +} + +/* + * 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) +{ +	struct dw_pcie *dw = &rcar->dw; + +	if (dw_pcie_readl_dbi(dw, 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 */ +	const u32 check_addr[] = { 0x00101018, 0x00101118, 0x00101021, 0x00101121}; +	struct dw_pcie *dw = &rcar->dw; +	const struct firmware *fw; +	unsigned int i, timeout; +	u32 data; +	int ret; + +	ret = request_firmware(&fw, RCAR_GEN4_PCIE_FIRMWARE_NAME, dw->dev); +	if (ret) { +		dev_err(dw->dev, "Failed to load firmware (%s): %d\n", +			RCAR_GEN4_PCIE_FIRMWARE_NAME, ret); +		return ret; +	} + +	for (i = 0; i < (fw->size / 2); i++) { +		data = fw->data[(i * 2) + 1] << 8 | fw->data[i * 2]; +		timeout = 100; +		do { +			dw_pcie_writel_dbi(dw, PRTLGC89, RCAR_GEN4_PCIE_FIRMWARE_BASE_ADDR + i); +			dw_pcie_writel_dbi(dw, PRTLGC90, data); +			if (!rcar_gen4_pcie_reg_test_bit(rcar, PRTLGC89, BIT(30))) +				break; +			if (!(--timeout)) { +				ret = -ETIMEDOUT; +				goto exit; +			} +			usleep_range(100, 200); +		} while (1); +	} + +	rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x0f8, BIT(17), BIT(17)); + +	for (i = 0; i < ARRAY_SIZE(check_addr); i++) { +		timeout = 100; +		do { +			dw_pcie_writel_dbi(dw, PRTLGC89, check_addr[i]); +			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)) { +				ret = -ETIMEDOUT; +				goto exit; +			} +			usleep_range(100, 200); +		} while (1); +	} + +exit: +	release_firmware(fw); + +	return ret; +} + +static int rcar_gen4_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable) +{ +	struct dw_pcie *dw = &rcar->dw; +	u32 val; +	int ret; + +	if (!enable) { +		val = readl(rcar->base + PCIERSTCTRL1); +		val &= ~APP_LTSSM_ENABLE; +		writel(val, rcar->base + PCIERSTCTRL1); + +		return 0; +	} + +	val = dw_pcie_readl_dbi(dw, PCIE_PORT_FORCE); +	val |= PORT_FORCE_DO_DESKEW_FOR_SRIS; +	dw_pcie_writel_dbi(dw, PCIE_PORT_FORCE, val); + +	val = readl(rcar->base + PCIEMSR0); +	val |= APP_SRIS_MODE; +	writel(val, rcar->base + PCIEMSR0); + +	/* +	 * 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. +	 */ +	rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x700, BIT(28), 0); +	rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x700, BIT(20), 0); +	rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x700, BIT(12), 0); +	rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x700, BIT(4), 0); + +	rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x148, GENMASK(23, 22), BIT(22)); +	rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x148, GENMASK(18, 16), GENMASK(17, 16)); +	rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x148, GENMASK(7, 6), BIT(6)); +	rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x148, GENMASK(2, 0), GENMASK(11, 0)); +	rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x1d4, GENMASK(16, 15), GENMASK(16, 15)); +	rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x514, BIT(26), BIT(26)); +	rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x0f8, BIT(16), 0); +	rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x0f8, BIT(19), BIT(19)); + +	val = readl(rcar->base + PCIERSTCTRL1); +	val &= ~APP_HOLD_PHY_RST; +	writel(val, rcar->base + PCIERSTCTRL1); + +	ret = readl_poll_timeout(rcar->phy_base + 0x0f8, val, !(val & BIT(18)), 100, 10000); +	if (ret < 0) +		return ret; + +	ret = rcar_gen4_pcie_download_phy_firmware(rcar); +	if (ret) +		return ret; + +	val = readl(rcar->base + PCIERSTCTRL1); +	val |= APP_LTSSM_ENABLE; +	writel(val, rcar->base + PCIERSTCTRL1); + +	return 0; +} + +static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie = { +	.ltssm_control = r8a779f0_pcie_ltssm_control, +	.mode = DW_PCIE_RC_TYPE, +}; + +static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie_ep = { +	.ltssm_control = r8a779f0_pcie_ltssm_control, +	.mode = DW_PCIE_EP_TYPE, +}; + +static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie = { +	.additional_common_init = rcar_gen4_pcie_additional_common_init, +	.ltssm_control = rcar_gen4_pcie_ltssm_control, +	.mode = DW_PCIE_RC_TYPE, +}; + +static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie_ep = { +	.additional_common_init = rcar_gen4_pcie_additional_common_init, +	.ltssm_control = rcar_gen4_pcie_ltssm_control, +	.mode = DW_PCIE_EP_TYPE, +}; +  static const struct of_device_id rcar_gen4_pcie_of_match[] = {  	{ +		.compatible = "renesas,r8a779f0-pcie", +		.data = &drvdata_r8a779f0_pcie, +	}, +	{ +		.compatible = "renesas,r8a779f0-pcie-ep", +		.data = &drvdata_r8a779f0_pcie_ep, +	}, +	{  		.compatible = "renesas,rcar-gen4-pcie", -		.data = (void *)DW_PCIE_RC_TYPE, +		.data = &drvdata_rcar_gen4_pcie,  	},  	{  		.compatible = "renesas,rcar-gen4-pcie-ep", -		.data = (void *)DW_PCIE_EP_TYPE, +		.data = &drvdata_rcar_gen4_pcie_ep,  	},  	{},  }; |