Message ID | 20240415081135.3814373-7-yoshihiro.shimoda.uh@renesas.com (mailing list archive) |
---|---|
State | Superseded |
Delegated to: | Manivannan Sadhasivam |
Headers | show |
Series | PCI: rcar-gen4: Add R-Car V4H support | expand |
On Mon, Apr 15, 2024 at 05:11:34PM +0900, Yoshihiro Shimoda wrote: > This driver previously supported r8a779f0 (R-Car S4-8). Add support > for r8a779g0 (R-Car V4H). PCIe features of both r8a779f0 and r8a779g0 > are almost all the same. For example: > - PCI Express Base Specification Revision 4.0 > - Root complex mode and endpoint mode are supported > > However, r8a779g0 requires specific firmware downloading, to > initialize the PHY. Otherwise, the PCIe controller cannot work. > The firmware is attached in the manual of the r8a779g0 as text. > So, convert it to a binary file by using a script. > > Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> > --- > drivers/pci/controller/dwc/pcie-rcar-gen4.c | 201 +++++++++++++++++++- > 1 file changed, 200 insertions(+), 1 deletion(-) > > diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c > index 980a916933d6..4e934e9156f2 100644 > --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c > +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c > @@ -5,8 +5,10 @@ > */ > > #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 +22,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,19 +40,47 @@ > #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) > + > +/* > + * The R-Car Gen4 documents don't describe the PHY registers' name. > + * But, the initialization procedure describes these offsets. So, > + * this driver makes up own #defines for the offsets. > + */ > +#define RCAR_GEN4_PCIE_PHY_0f8 0x0f8 > +#define RCAR_GEN4_PCIE_PHY_148 0x148 > +#define RCAR_GEN4_PCIE_PHY_1d4 0x1d4 > +#define RCAR_GEN4_PCIE_PHY_514 0x514 > +#define RCAR_GEN4_PCIE_PHY_700 0x700 > + > #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_enable)(struct rcar_gen4_pcie *rcar); > enum dw_pcie_device_mode mode; > }; > @@ -57,12 +88,144 @@ struct rcar_gen4_pcie_drvdata { > struct rcar_gen4_pcie { > struct dw_pcie dw; > void __iomem *base; > + void __iomem *phy_base; > struct platform_device *pdev; > 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_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); > +} > + > +static int rcar_gen4_pcie_reg_check(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) > +{ > + 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, "%s: Requesting firmware failed\n", __func__); > + 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_check(rcar, PRTLGC89, BIT(30)) >= 0) > + break; > + if (!(--timeout)) { > + ret = -ETIMEDOUT; > + goto exit; > + } > + usleep_range(100, 200); > + } while (1); > + } > + > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_0f8, 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_check(rcar, PRTLGC89, BIT(30)); > + ret |= rcar_gen4_pcie_reg_check(rcar, PRTLGC90, BIT(0)); > + if (ret >= 0) > + break; > + if (!(--timeout)) { > + ret = -ETIMEDOUT; > + goto exit; > + } > + usleep_range(100, 200); > + } while (1); > + } > + > + ret = 0; > +exit: > + release_firmware(fw); > + > + return ret; > +} > + > +static int rcar_gen4_pcie_enable_phy(struct rcar_gen4_pcie *rcar) > +{ > + struct dw_pcie *dw = &rcar->dw; > + u32 val; > + int ret; > + > + 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); > + > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(28), 0); > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(20), 0); > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(12), 0); > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(4), 0); > + > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, > + GENMASK(23, 22), BIT(22)); > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, > + GENMASK(18, 16), GENMASK(17, 16)); > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, > + GENMASK(7, 6), BIT(6)); > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, > + GENMASK(2, 0), GENMASK(11, 0)); > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_1d4, > + GENMASK(16, 15), GENMASK(16, 15)); > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_514, BIT(26), BIT(26)); > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_0f8, BIT(16), 0); > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_0f8, 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 + RCAR_GEN4_PCIE_PHY_0f8, 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; > +} > + Is there a reason why you didn't chose to implement this as a PHY driver in drivers/phy ? Kind regards, Niklas > static void rcar_gen4_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, > bool enable) > { > @@ -200,6 +363,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: > @@ -241,6 +407,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"); > > @@ -517,6 +687,31 @@ static int r8a779f0_pcie_ltssm_enable(struct rcar_gen4_pcie *rcar) > return 0; > } > > +static void rcar_gen4_pcie_additional_common_init(struct rcar_gen4_pcie *rcar) > +{ > + struct dw_pcie *dw = &rcar->dw; > + u32 val; > + > + /* > + * The SoC manual said the register setting is required. Otherwise, > + * linkup failed. > + */ > + 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 int rcar_gen4_pcie_ltssm_enable(struct rcar_gen4_pcie *rcar) > +{ > + return rcar_gen4_pcie_enable_phy(rcar); > +} > + > static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie = { > .ltssm_enable = r8a779f0_pcie_ltssm_enable, > .mode = DW_PCIE_RC_TYPE, > @@ -528,10 +723,14 @@ static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie_ep = { > }; > > static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie = { > + .additional_common_init = rcar_gen4_pcie_additional_common_init, > + .ltssm_enable = rcar_gen4_pcie_ltssm_enable, > .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_enable = rcar_gen4_pcie_ltssm_enable, > .mode = DW_PCIE_EP_TYPE, > }; > > -- > 2.25.1 >
Hi Niklas, > From: Niklas Cassel, Sent: Monday, April 15, 2024 5:27 PM > > On Mon, Apr 15, 2024 at 05:11:34PM +0900, Yoshihiro Shimoda wrote: > > This driver previously supported r8a779f0 (R-Car S4-8). Add support > > for r8a779g0 (R-Car V4H). PCIe features of both r8a779f0 and r8a779g0 > > are almost all the same. For example: > > - PCI Express Base Specification Revision 4.0 > > - Root complex mode and endpoint mode are supported > > > > However, r8a779g0 requires specific firmware downloading, to > > initialize the PHY. Otherwise, the PCIe controller cannot work. > > The firmware is attached in the manual of the r8a779g0 as text. > > So, convert it to a binary file by using a script. > > > > Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> > > --- > > drivers/pci/controller/dwc/pcie-rcar-gen4.c | 201 +++++++++++++++++++- > > 1 file changed, 200 insertions(+), 1 deletion(-) > > > > diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c > > index 980a916933d6..4e934e9156f2 100644 > > --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c > > +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c > > @@ -5,8 +5,10 @@ > > */ > > > > #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 +22,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,19 +40,47 @@ > > #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) > > + > > +/* > > + * The R-Car Gen4 documents don't describe the PHY registers' name. > > + * But, the initialization procedure describes these offsets. So, > > + * this driver makes up own #defines for the offsets. > > + */ > > +#define RCAR_GEN4_PCIE_PHY_0f8 0x0f8 > > +#define RCAR_GEN4_PCIE_PHY_148 0x148 > > +#define RCAR_GEN4_PCIE_PHY_1d4 0x1d4 > > +#define RCAR_GEN4_PCIE_PHY_514 0x514 > > +#define RCAR_GEN4_PCIE_PHY_700 0x700 > > + > > #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_enable)(struct rcar_gen4_pcie *rcar); > > enum dw_pcie_device_mode mode; > > }; > > @@ -57,12 +88,144 @@ struct rcar_gen4_pcie_drvdata { > > struct rcar_gen4_pcie { > > struct dw_pcie dw; > > void __iomem *base; > > + void __iomem *phy_base; > > struct platform_device *pdev; > > 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_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); > > +} > > + > > +static int rcar_gen4_pcie_reg_check(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) > > +{ > > + 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, "%s: Requesting firmware failed\n", __func__); > > + 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_check(rcar, PRTLGC89, BIT(30)) >= 0) > > + break; > > + if (!(--timeout)) { > > + ret = -ETIMEDOUT; > > + goto exit; > > + } > > + usleep_range(100, 200); > > + } while (1); > > + } > > + > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_0f8, 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_check(rcar, PRTLGC89, BIT(30)); > > + ret |= rcar_gen4_pcie_reg_check(rcar, PRTLGC90, BIT(0)); > > + if (ret >= 0) > > + break; > > + if (!(--timeout)) { > > + ret = -ETIMEDOUT; > > + goto exit; > > + } > > + usleep_range(100, 200); > > + } while (1); > > + } > > + > > + ret = 0; > > +exit: > > + release_firmware(fw); > > + > > + return ret; > > +} > > + > > +static int rcar_gen4_pcie_enable_phy(struct rcar_gen4_pcie *rcar) > > +{ > > + struct dw_pcie *dw = &rcar->dw; > > + u32 val; > > + int ret; > > + > > + 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); > > + > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(28), 0); > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(20), 0); > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(12), 0); > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(4), 0); > > + > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, > > + GENMASK(23, 22), BIT(22)); > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, > > + GENMASK(18, 16), GENMASK(17, 16)); > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, > > + GENMASK(7, 6), BIT(6)); > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, > > + GENMASK(2, 0), GENMASK(11, 0)); > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_1d4, > > + GENMASK(16, 15), GENMASK(16, 15)); > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_514, BIT(26), BIT(26)); > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_0f8, BIT(16), 0); > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_0f8, 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 + RCAR_GEN4_PCIE_PHY_0f8, 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; > > +} > > + > > Is there a reason why you didn't chose to implement this as a PHY driver > in drivers/phy ? This is because the initialization needs both PCIe and PHY registers' settings alternately like below: Write PHY regs Write PCI regs Read a PHY reg Write PCI regs Write a PHY reg ... Best regards, Yoshihiro Shimoda > > Kind regards, > Niklas > > > > static void rcar_gen4_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, > > bool enable) > > { > > @@ -200,6 +363,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: > > @@ -241,6 +407,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"); > > > > @@ -517,6 +687,31 @@ static int r8a779f0_pcie_ltssm_enable(struct rcar_gen4_pcie *rcar) > > return 0; > > } > > > > +static void rcar_gen4_pcie_additional_common_init(struct rcar_gen4_pcie *rcar) > > +{ > > + struct dw_pcie *dw = &rcar->dw; > > + u32 val; > > + > > + /* > > + * The SoC manual said the register setting is required. Otherwise, > > + * linkup failed. > > + */ > > + 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 int rcar_gen4_pcie_ltssm_enable(struct rcar_gen4_pcie *rcar) > > +{ > > + return rcar_gen4_pcie_enable_phy(rcar); > > +} > > + > > static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie = { > > .ltssm_enable = r8a779f0_pcie_ltssm_enable, > > .mode = DW_PCIE_RC_TYPE, > > @@ -528,10 +723,14 @@ static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie_ep = { > > }; > > > > static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie = { > > + .additional_common_init = rcar_gen4_pcie_additional_common_init, > > + .ltssm_enable = rcar_gen4_pcie_ltssm_enable, > > .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_enable = rcar_gen4_pcie_ltssm_enable, > > .mode = DW_PCIE_EP_TYPE, > > }; > > > > -- > > 2.25.1 > >
On Mon, Apr 15, 2024 at 09:19:43AM +0000, Yoshihiro Shimoda wrote: > Hi Niklas, > > > From: Niklas Cassel, Sent: Monday, April 15, 2024 5:27 PM > > > > On Mon, Apr 15, 2024 at 05:11:34PM +0900, Yoshihiro Shimoda wrote: > > > This driver previously supported r8a779f0 (R-Car S4-8). Add support > > > for r8a779g0 (R-Car V4H). PCIe features of both r8a779f0 and r8a779g0 > > > are almost all the same. For example: > > > - PCI Express Base Specification Revision 4.0 > > > - Root complex mode and endpoint mode are supported > > > > > > However, r8a779g0 requires specific firmware downloading, to > > > initialize the PHY. Otherwise, the PCIe controller cannot work. > > > The firmware is attached in the manual of the r8a779g0 as text. > > > So, convert it to a binary file by using a script. > > > > > > Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> > > > --- > > > drivers/pci/controller/dwc/pcie-rcar-gen4.c | 201 +++++++++++++++++++- > > > 1 file changed, 200 insertions(+), 1 deletion(-) > > > > > > diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c > > > index 980a916933d6..4e934e9156f2 100644 > > > --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c > > > +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c > > > @@ -5,8 +5,10 @@ > > > */ > > > > > > #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 +22,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,19 +40,47 @@ > > > #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) > > > + > > > +/* > > > + * The R-Car Gen4 documents don't describe the PHY registers' name. > > > + * But, the initialization procedure describes these offsets. So, > > > + * this driver makes up own #defines for the offsets. > > > + */ > > > +#define RCAR_GEN4_PCIE_PHY_0f8 0x0f8 > > > +#define RCAR_GEN4_PCIE_PHY_148 0x148 > > > +#define RCAR_GEN4_PCIE_PHY_1d4 0x1d4 > > > +#define RCAR_GEN4_PCIE_PHY_514 0x514 > > > +#define RCAR_GEN4_PCIE_PHY_700 0x700 > > > + > > > #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_enable)(struct rcar_gen4_pcie *rcar); > > > enum dw_pcie_device_mode mode; > > > }; > > > @@ -57,12 +88,144 @@ struct rcar_gen4_pcie_drvdata { > > > struct rcar_gen4_pcie { > > > struct dw_pcie dw; > > > void __iomem *base; > > > + void __iomem *phy_base; > > > struct platform_device *pdev; > > > 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_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); > > > +} > > > + > > > +static int rcar_gen4_pcie_reg_check(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) > > > +{ > > > + 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, "%s: Requesting firmware failed\n", __func__); > > > + 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_check(rcar, PRTLGC89, BIT(30)) >= 0) > > > + break; > > > + if (!(--timeout)) { > > > + ret = -ETIMEDOUT; > > > + goto exit; > > > + } > > > + usleep_range(100, 200); > > > + } while (1); > > > + } > > > + > > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_0f8, 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_check(rcar, PRTLGC89, BIT(30)); > > > + ret |= rcar_gen4_pcie_reg_check(rcar, PRTLGC90, BIT(0)); > > > + if (ret >= 0) > > > + break; > > > + if (!(--timeout)) { > > > + ret = -ETIMEDOUT; > > > + goto exit; > > > + } > > > + usleep_range(100, 200); > > > + } while (1); > > > + } > > > + > > > + ret = 0; > > > +exit: > > > + release_firmware(fw); > > > + > > > + return ret; > > > +} > > > + > > > +static int rcar_gen4_pcie_enable_phy(struct rcar_gen4_pcie *rcar) > > > +{ > > > + struct dw_pcie *dw = &rcar->dw; > > > + u32 val; > > > + int ret; > > > + > > > + 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); > > > + > > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(28), 0); > > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(20), 0); > > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(12), 0); > > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(4), 0); > > > + > > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, > > > + GENMASK(23, 22), BIT(22)); > > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, > > > + GENMASK(18, 16), GENMASK(17, 16)); > > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, > > > + GENMASK(7, 6), BIT(6)); > > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, > > > + GENMASK(2, 0), GENMASK(11, 0)); > > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_1d4, > > > + GENMASK(16, 15), GENMASK(16, 15)); > > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_514, BIT(26), BIT(26)); > > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_0f8, BIT(16), 0); > > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_0f8, 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 + RCAR_GEN4_PCIE_PHY_0f8, 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; > > > +} > > > + > > > > Is there a reason why you didn't chose to implement this as a PHY driver > > in drivers/phy ? > > This is because the initialization needs both PCIe and PHY registers' settings > alternately like below: > Write PHY regs > Write PCI regs > Read a PHY reg > Write PCI regs > Write a PHY reg > ... > > Best regards, > Yoshihiro Shimoda I see, that makes sense. Thanks. Kind regards, Niklas
On Mon, Apr 15, 2024 at 05:11:34PM +0900, Yoshihiro Shimoda wrote: > This driver previously supported r8a779f0 (R-Car S4-8). Add support > for r8a779g0 (R-Car V4H). PCIe features of both r8a779f0 and r8a779g0 > are almost all the same. For example: > - PCI Express Base Specification Revision 4.0 > - Root complex mode and endpoint mode are supported > > However, r8a779g0 requires specific firmware downloading, to > initialize the PHY. Otherwise, the PCIe controller cannot work. > The firmware is attached in the manual of the r8a779g0 as text. > So, convert it to a binary file by using a script. The firmware is expected to be present in userspace. So where is it btw? Is it upstreamed to linux-firmware? You cannot ask users to manually copy the text and convert it to a binary file. But if the firmware or sequence is not going to change, why can't you hardcode it in the driver itself? > > Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> > --- > drivers/pci/controller/dwc/pcie-rcar-gen4.c | 201 +++++++++++++++++++- > 1 file changed, 200 insertions(+), 1 deletion(-) > > diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c > index 980a916933d6..4e934e9156f2 100644 > --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c > +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c > @@ -5,8 +5,10 @@ > */ > > #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 +22,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,19 +40,47 @@ > #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) > + > +/* > + * The R-Car Gen4 documents don't describe the PHY registers' name. > + * But, the initialization procedure describes these offsets. So, > + * this driver makes up own #defines for the offsets. > + */ > +#define RCAR_GEN4_PCIE_PHY_0f8 0x0f8 > +#define RCAR_GEN4_PCIE_PHY_148 0x148 > +#define RCAR_GEN4_PCIE_PHY_1d4 0x1d4 > +#define RCAR_GEN4_PCIE_PHY_514 0x514 > +#define RCAR_GEN4_PCIE_PHY_700 0x700 > + As I said before, these defines provide no information about the registers at all. So please use the offset directly and add a comment. > #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); What is this init for? Controller? PHY? > int (*ltssm_enable)(struct rcar_gen4_pcie *rcar); > enum dw_pcie_device_mode mode; > }; > @@ -57,12 +88,144 @@ struct rcar_gen4_pcie_drvdata { > struct rcar_gen4_pcie { > struct dw_pcie dw; > void __iomem *base; > + void __iomem *phy_base; > struct platform_device *pdev; > 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_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); > +} > + > +static int rcar_gen4_pcie_reg_check(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; What is this function checking actually? It is just a DBI read. Do you expect these register accesses to fail? > + > + return 0; > +} > + > +static int rcar_gen4_pcie_download_phy_firmware(struct rcar_gen4_pcie *rcar) > +{ > + const u32 check_addr[] = { 0x00101018, 0x00101118, 0x00101021, 0x00101121}; What does this check_addr corresponds to? > + 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, "%s: Requesting firmware failed\n", __func__); Print the firmware name in the case of error. But as I said above, please try to hardcode the fw if it is not going to change. We do this in other drivers as well. - Mani > + 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_check(rcar, PRTLGC89, BIT(30)) >= 0) > + break; > + if (!(--timeout)) { > + ret = -ETIMEDOUT; > + goto exit; > + } > + usleep_range(100, 200); > + } while (1); > + } > + > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_0f8, 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_check(rcar, PRTLGC89, BIT(30)); > + ret |= rcar_gen4_pcie_reg_check(rcar, PRTLGC90, BIT(0)); > + if (ret >= 0) > + break; > + if (!(--timeout)) { > + ret = -ETIMEDOUT; > + goto exit; > + } > + usleep_range(100, 200); > + } while (1); > + } > + > + ret = 0; > +exit: > + release_firmware(fw); > + > + return ret; > +} > + > +static int rcar_gen4_pcie_enable_phy(struct rcar_gen4_pcie *rcar) > +{ > + struct dw_pcie *dw = &rcar->dw; > + u32 val; > + int ret; > + > + 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); > + > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(28), 0); > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(20), 0); > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(12), 0); > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(4), 0); > + > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, > + GENMASK(23, 22), BIT(22)); > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, > + GENMASK(18, 16), GENMASK(17, 16)); > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, > + GENMASK(7, 6), BIT(6)); > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, > + GENMASK(2, 0), GENMASK(11, 0)); > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_1d4, > + GENMASK(16, 15), GENMASK(16, 15)); > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_514, BIT(26), BIT(26)); > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_0f8, BIT(16), 0); > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_0f8, 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 + RCAR_GEN4_PCIE_PHY_0f8, 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 void rcar_gen4_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, > bool enable) > { > @@ -200,6 +363,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: > @@ -241,6 +407,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"); > > @@ -517,6 +687,31 @@ static int r8a779f0_pcie_ltssm_enable(struct rcar_gen4_pcie *rcar) > return 0; > } > > +static void rcar_gen4_pcie_additional_common_init(struct rcar_gen4_pcie *rcar) > +{ > + struct dw_pcie *dw = &rcar->dw; > + u32 val; > + > + /* > + * The SoC manual said the register setting is required. Otherwise, > + * linkup failed. > + */ > + 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 int rcar_gen4_pcie_ltssm_enable(struct rcar_gen4_pcie *rcar) > +{ > + return rcar_gen4_pcie_enable_phy(rcar); > +} > + > static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie = { > .ltssm_enable = r8a779f0_pcie_ltssm_enable, > .mode = DW_PCIE_RC_TYPE, > @@ -528,10 +723,14 @@ static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie_ep = { > }; > > static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie = { > + .additional_common_init = rcar_gen4_pcie_additional_common_init, > + .ltssm_enable = rcar_gen4_pcie_ltssm_enable, > .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_enable = rcar_gen4_pcie_ltssm_enable, > .mode = DW_PCIE_EP_TYPE, > }; > > -- > 2.25.1 > >
Hi Manivannan, > From: Manivannan Sadhasivam, Sent: Saturday, May 11, 2024 5:03 PM > > On Mon, Apr 15, 2024 at 05:11:34PM +0900, Yoshihiro Shimoda wrote: > > This driver previously supported r8a779f0 (R-Car S4-8). Add support > > for r8a779g0 (R-Car V4H). PCIe features of both r8a779f0 and r8a779g0 > > are almost all the same. For example: > > - PCI Express Base Specification Revision 4.0 > > - Root complex mode and endpoint mode are supported > > > > However, r8a779g0 requires specific firmware downloading, to > > initialize the PHY. Otherwise, the PCIe controller cannot work. > > The firmware is attached in the manual of the r8a779g0 as text. > > So, convert it to a binary file by using a script. > > The firmware is expected to be present in userspace. So where is it btw? Is it > upstreamed to linux-firmware? I may misunderstand your question, but the firmware is in the SoC's datasheet as a text file. So, we need to convert it to a binary file and store it into the rootfs. (Also, for debug purpose, we can use built-in firmware from "CONFIG_EXTRA_FIRMWARE".) > You cannot ask users to manually copy the text and convert it to a binary file. > But if the firmware or sequence is not going to change, why can't you hardcode > it in the driver itself? This is because that Renesas is not able to distribute the firmware freely. > > > > Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> > > --- > > drivers/pci/controller/dwc/pcie-rcar-gen4.c | 201 +++++++++++++++++++- > > 1 file changed, 200 insertions(+), 1 deletion(-) > > > > diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c > > index 980a916933d6..4e934e9156f2 100644 > > --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c > > +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c > > @@ -5,8 +5,10 @@ > > */ > > > > #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 +22,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,19 +40,47 @@ > > #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) > > + > > +/* > > + * The R-Car Gen4 documents don't describe the PHY registers' name. > > + * But, the initialization procedure describes these offsets. So, > > + * this driver makes up own #defines for the offsets. > > + */ > > +#define RCAR_GEN4_PCIE_PHY_0f8 0x0f8 > > +#define RCAR_GEN4_PCIE_PHY_148 0x148 > > +#define RCAR_GEN4_PCIE_PHY_1d4 0x1d4 > > +#define RCAR_GEN4_PCIE_PHY_514 0x514 > > +#define RCAR_GEN4_PCIE_PHY_700 0x700 > > + > > As I said before, these defines provide no information about the registers at > all. So please use the offset directly and add a comment. I got it. > > #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); > > What is this init for? Controller? PHY? This init is for controller. > > int (*ltssm_enable)(struct rcar_gen4_pcie *rcar); > > enum dw_pcie_device_mode mode; > > }; > > @@ -57,12 +88,144 @@ struct rcar_gen4_pcie_drvdata { > > struct rcar_gen4_pcie { > > struct dw_pcie dw; > > void __iomem *base; > > + void __iomem *phy_base; > > struct platform_device *pdev; > > 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_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); > > +} > > + > > +static int rcar_gen4_pcie_reg_check(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; > > What is this function checking actually? It is just a DBI read. Do you expect > these register accesses to fail? This function is checking whether the register's value with mask is zero or not. - If non-zero, return -EAGAIN. - If zero, return 0. (this is expected value.) Perhaps, should I change the function name? For example, rcar_gen4_pcie_reg_test_bit()? According to the datasheet, software needs to write registers again if the register value(s) is(are) not expected value(s). > > + > > + return 0; > > +} > > + > > +static int rcar_gen4_pcie_download_phy_firmware(struct rcar_gen4_pcie *rcar) > > +{ > > + const u32 check_addr[] = { 0x00101018, 0x00101118, 0x00101021, 0x00101121}; > > What does this check_addr corresponds to? I don't know. The datasheet just said that like other magic numbers. I'll add such a comment here. > > + 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, "%s: Requesting firmware failed\n", __func__); > > Print the firmware name in the case of error. I got it. > But as I said above, please try to hardcode the fw if it is not going to change. > We do this in other drivers as well. As I mentioned above, I would like to keep calling this function. Best regards, Yoshihiro Shimoda > - Mani > > > + 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_check(rcar, PRTLGC89, BIT(30)) >= 0) > > + break; > > + if (!(--timeout)) { > > + ret = -ETIMEDOUT; > > + goto exit; > > + } > > + usleep_range(100, 200); > > + } while (1); > > + } > > + > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_0f8, 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_check(rcar, PRTLGC89, BIT(30)); > > + ret |= rcar_gen4_pcie_reg_check(rcar, PRTLGC90, BIT(0)); > > + if (ret >= 0) > > + break; > > + if (!(--timeout)) { > > + ret = -ETIMEDOUT; > > + goto exit; > > + } > > + usleep_range(100, 200); > > + } while (1); > > + } > > + > > + ret = 0; > > +exit: > > + release_firmware(fw); > > + > > + return ret; > > +} > > + > > +static int rcar_gen4_pcie_enable_phy(struct rcar_gen4_pcie *rcar) > > +{ > > + struct dw_pcie *dw = &rcar->dw; > > + u32 val; > > + int ret; > > + > > + 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); > > + > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(28), 0); > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(20), 0); > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(12), 0); > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(4), 0); > > + > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, > > + GENMASK(23, 22), BIT(22)); > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, > > + GENMASK(18, 16), GENMASK(17, 16)); > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, > > + GENMASK(7, 6), BIT(6)); > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, > > + GENMASK(2, 0), GENMASK(11, 0)); > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_1d4, > > + GENMASK(16, 15), GENMASK(16, 15)); > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_514, BIT(26), BIT(26)); > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_0f8, BIT(16), 0); > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_0f8, 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 + RCAR_GEN4_PCIE_PHY_0f8, 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 void rcar_gen4_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, > > bool enable) > > { > > @@ -200,6 +363,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: > > @@ -241,6 +407,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"); > > > > @@ -517,6 +687,31 @@ static int r8a779f0_pcie_ltssm_enable(struct rcar_gen4_pcie *rcar) > > return 0; > > } > > > > +static void rcar_gen4_pcie_additional_common_init(struct rcar_gen4_pcie *rcar) > > +{ > > + struct dw_pcie *dw = &rcar->dw; > > + u32 val; > > + > > + /* > > + * The SoC manual said the register setting is required. Otherwise, > > + * linkup failed. > > + */ > > + 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 int rcar_gen4_pcie_ltssm_enable(struct rcar_gen4_pcie *rcar) > > +{ > > + return rcar_gen4_pcie_enable_phy(rcar); > > +} > > + > > static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie = { > > .ltssm_enable = r8a779f0_pcie_ltssm_enable, > > .mode = DW_PCIE_RC_TYPE, > > @@ -528,10 +723,14 @@ static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie_ep = { > > }; > > > > static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie = { > > + .additional_common_init = rcar_gen4_pcie_additional_common_init, > > + .ltssm_enable = rcar_gen4_pcie_ltssm_enable, > > .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_enable = rcar_gen4_pcie_ltssm_enable, > > .mode = DW_PCIE_EP_TYPE, > > }; > > > > -- > > 2.25.1 > > > > > > -- > மணிவண்ணன் சதாசிவம்
On Tue, May 14, 2024 at 11:27:30AM +0000, Yoshihiro Shimoda wrote: > Hi Manivannan, > > > From: Manivannan Sadhasivam, Sent: Saturday, May 11, 2024 5:03 PM > > > > On Mon, Apr 15, 2024 at 05:11:34PM +0900, Yoshihiro Shimoda wrote: > > > This driver previously supported r8a779f0 (R-Car S4-8). Add support > > > for r8a779g0 (R-Car V4H). PCIe features of both r8a779f0 and r8a779g0 > > > are almost all the same. For example: > > > - PCI Express Base Specification Revision 4.0 > > > - Root complex mode and endpoint mode are supported > > > > > > However, r8a779g0 requires specific firmware downloading, to > > > initialize the PHY. Otherwise, the PCIe controller cannot work. > > > The firmware is attached in the manual of the r8a779g0 as text. > > > So, convert it to a binary file by using a script. > > > > The firmware is expected to be present in userspace. So where is it btw? Is it > > upstreamed to linux-firmware? > > I may misunderstand your question, but the firmware is in the SoC's datasheet as > a text file. So, we need to convert it to a binary file and store it into the rootfs. > (Also, for debug purpose, we can use built-in firmware from "CONFIG_EXTRA_FIRMWARE".) > > > You cannot ask users to manually copy the text and convert it to a binary file. > > But if the firmware or sequence is not going to change, why can't you hardcode > > it in the driver itself? > > This is because that Renesas is not able to distribute the firmware freely. > Seriously? Are you saying that the user has to sign NDA to get this firmware? - Mani > > > > > > Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> > > > --- > > > drivers/pci/controller/dwc/pcie-rcar-gen4.c | 201 +++++++++++++++++++- > > > 1 file changed, 200 insertions(+), 1 deletion(-) > > > > > > diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c > > > index 980a916933d6..4e934e9156f2 100644 > > > --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c > > > +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c > > > @@ -5,8 +5,10 @@ > > > */ > > > > > > #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 +22,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,19 +40,47 @@ > > > #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) > > > + > > > +/* > > > + * The R-Car Gen4 documents don't describe the PHY registers' name. > > > + * But, the initialization procedure describes these offsets. So, > > > + * this driver makes up own #defines for the offsets. > > > + */ > > > +#define RCAR_GEN4_PCIE_PHY_0f8 0x0f8 > > > +#define RCAR_GEN4_PCIE_PHY_148 0x148 > > > +#define RCAR_GEN4_PCIE_PHY_1d4 0x1d4 > > > +#define RCAR_GEN4_PCIE_PHY_514 0x514 > > > +#define RCAR_GEN4_PCIE_PHY_700 0x700 > > > + > > > > As I said before, these defines provide no information about the registers at > > all. So please use the offset directly and add a comment. > > I got it. > > > > #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); > > > > What is this init for? Controller? PHY? > > This init is for controller. > > > > int (*ltssm_enable)(struct rcar_gen4_pcie *rcar); > > > enum dw_pcie_device_mode mode; > > > }; > > > @@ -57,12 +88,144 @@ struct rcar_gen4_pcie_drvdata { > > > struct rcar_gen4_pcie { > > > struct dw_pcie dw; > > > void __iomem *base; > > > + void __iomem *phy_base; > > > struct platform_device *pdev; > > > 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_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); > > > +} > > > + > > > +static int rcar_gen4_pcie_reg_check(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; > > > > What is this function checking actually? It is just a DBI read. Do you expect > > these register accesses to fail? > > This function is checking whether the register's value with mask is zero or not. > - If non-zero, return -EAGAIN. > - If zero, return 0. (this is expected value.) > > Perhaps, should I change the function name? For example, rcar_gen4_pcie_reg_test_bit()? > According to the datasheet, software needs to write registers again if the register > value(s) is(are) not expected value(s). > > > > + > > > + return 0; > > > +} > > > + > > > +static int rcar_gen4_pcie_download_phy_firmware(struct rcar_gen4_pcie *rcar) > > > +{ > > > + const u32 check_addr[] = { 0x00101018, 0x00101118, 0x00101021, 0x00101121}; > > > > What does this check_addr corresponds to? > > I don't know. The datasheet just said that like other magic numbers. > I'll add such a comment here. > > > > + 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, "%s: Requesting firmware failed\n", __func__); > > > > Print the firmware name in the case of error. > > I got it. > > > But as I said above, please try to hardcode the fw if it is not going to change. > > We do this in other drivers as well. > > As I mentioned above, I would like to keep calling this function. > > Best regards, > Yoshihiro Shimoda > > > - Mani > > > > > + 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_check(rcar, PRTLGC89, BIT(30)) >= 0) > > > + break; > > > + if (!(--timeout)) { > > > + ret = -ETIMEDOUT; > > > + goto exit; > > > + } > > > + usleep_range(100, 200); > > > + } while (1); > > > + } > > > + > > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_0f8, 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_check(rcar, PRTLGC89, BIT(30)); > > > + ret |= rcar_gen4_pcie_reg_check(rcar, PRTLGC90, BIT(0)); > > > + if (ret >= 0) > > > + break; > > > + if (!(--timeout)) { > > > + ret = -ETIMEDOUT; > > > + goto exit; > > > + } > > > + usleep_range(100, 200); > > > + } while (1); > > > + } > > > + > > > + ret = 0; > > > +exit: > > > + release_firmware(fw); > > > + > > > + return ret; > > > +} > > > + > > > +static int rcar_gen4_pcie_enable_phy(struct rcar_gen4_pcie *rcar) > > > +{ > > > + struct dw_pcie *dw = &rcar->dw; > > > + u32 val; > > > + int ret; > > > + > > > + 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); > > > + > > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(28), 0); > > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(20), 0); > > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(12), 0); > > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(4), 0); > > > + > > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, > > > + GENMASK(23, 22), BIT(22)); > > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, > > > + GENMASK(18, 16), GENMASK(17, 16)); > > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, > > > + GENMASK(7, 6), BIT(6)); > > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, > > > + GENMASK(2, 0), GENMASK(11, 0)); > > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_1d4, > > > + GENMASK(16, 15), GENMASK(16, 15)); > > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_514, BIT(26), BIT(26)); > > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_0f8, BIT(16), 0); > > > + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_0f8, 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 + RCAR_GEN4_PCIE_PHY_0f8, 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 void rcar_gen4_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, > > > bool enable) > > > { > > > @@ -200,6 +363,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: > > > @@ -241,6 +407,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"); > > > > > > @@ -517,6 +687,31 @@ static int r8a779f0_pcie_ltssm_enable(struct rcar_gen4_pcie *rcar) > > > return 0; > > > } > > > > > > +static void rcar_gen4_pcie_additional_common_init(struct rcar_gen4_pcie *rcar) > > > +{ > > > + struct dw_pcie *dw = &rcar->dw; > > > + u32 val; > > > + > > > + /* > > > + * The SoC manual said the register setting is required. Otherwise, > > > + * linkup failed. > > > + */ > > > + 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 int rcar_gen4_pcie_ltssm_enable(struct rcar_gen4_pcie *rcar) > > > +{ > > > + return rcar_gen4_pcie_enable_phy(rcar); > > > +} > > > + > > > static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie = { > > > .ltssm_enable = r8a779f0_pcie_ltssm_enable, > > > .mode = DW_PCIE_RC_TYPE, > > > @@ -528,10 +723,14 @@ static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie_ep = { > > > }; > > > > > > static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie = { > > > + .additional_common_init = rcar_gen4_pcie_additional_common_init, > > > + .ltssm_enable = rcar_gen4_pcie_ltssm_enable, > > > .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_enable = rcar_gen4_pcie_ltssm_enable, > > > .mode = DW_PCIE_EP_TYPE, > > > }; > > > > > > -- > > > 2.25.1 > > > > > > > > > > -- > > மணிவண்ணன் சதாசிவம்
> > This is because that Renesas is not able to distribute the firmware freely. > > > > Seriously? Are you saying that the user has to sign NDA to get this firmware? No, the user has to buy the SoC and will get the firmware with the documentation. Renesas is not allowed to distribute the firmware to non-users of the SoC. So, linux-firmware cannot be used, sadly. We all wish it would be different.
> No, the user has to buy the SoC and will get the firmware with the > documentation. Renesas is not allowed to distribute the firmware to > non-users of the SoC. So, linux-firmware cannot be used, sadly. We all > wish it would be different. I am not a lawyer and all this. But this is how I, as a contractor working for Renesas, understand the situation.
On Wed, May 15, 2024 at 10:59:39AM +0200, Wolfram Sang wrote: > > > > This is because that Renesas is not able to distribute the firmware freely. > > > > > > > Seriously? Are you saying that the user has to sign NDA to get this firmware? > > No, the user has to buy the SoC and will get the firmware with the > documentation. Renesas is not allowed to distribute the firmware to > non-users of the SoC. So, linux-firmware cannot be used, sadly. We all > wish it would be different. If Renesas could bother to spend the effort to be legally allowed to include the firmware in linux-firmware, do we want to spend the effort to maintain the support for this PCIe controller in mainline? Is there even an example of a device driver that *requires* firmware that is not in linux-firmware? (I know of some device drivers that does not have firmware in linux-firmware, but in that case the firmware is *optional*, so the device driver still works even without loading an updated firmware. For this patch series, the driver seems to error out if request_firmware fails.) Kind regards, Niklas
Hi, > If Renesas could bother to spend the effort to be legally allowed to > include the firmware in linux-firmware, do we want to spend the effort > to maintain the support for this PCIe controller in mainline? We (including me) have no indication if Renesas bothered or not. Maybe they tried but could not convince a third party. I don't know. > Is there even an example of a device driver that *requires* firmware > that is not in linux-firmware? Sure thing! === snip here $ cat Documentation/admin-guide/media/opera-firmware.rst .. SPDX-License-Identifier: GPL-2.0 Opera firmware ============== Author: Marco Gittler <g.marco@freenet.de> To extract the firmware for the Opera DVB-S1 USB-Box you need to copy the files: 2830SCap2.sys 2830SLoad2.sys from the windriver disk into this directory. Then run: .. code-block:: none scripts/get_dvb_firmware opera1 and after that you have 2 files: dvb-usb-opera-01.fw dvb-usb-opera1-fpga-01.fw in here. Copy them into /lib/firmware/ . After that the driver can load the firmware (if you have enabled firmware loading in kernel config and have hotplug running). === snap And this is probably not the only driver where you need to get firmware out of a Windows driver (media drivers and webcam drivers are good candidates). And the redistribution of such drivers is likely to be limited as well. Happy hacking, Wolfram
On Wed, May 15, 2024 at 10:59:39AM +0200, Wolfram Sang wrote: > > > > This is because that Renesas is not able to distribute the firmware freely. > > > > > > > Seriously? Are you saying that the user has to sign NDA to get this firmware? > > No, the user has to buy the SoC and will get the firmware with the > documentation. Renesas is not allowed to distribute the firmware to > non-users of the SoC. So, linux-firmware cannot be used, sadly. We all > wish it would be different. > Okay. I think this could be _one_ of the exceptions we have in the kernel, but I'll leave it up to Lorenzo/Krzysztof (PCIe controller maintainers) to make a call. - Mani
On Tue, May 14, 2024 at 11:27:30AM +0000, Yoshihiro Shimoda wrote: > Hi Manivannan, > > > From: Manivannan Sadhasivam, Sent: Saturday, May 11, 2024 5:03 PM > > > > On Mon, Apr 15, 2024 at 05:11:34PM +0900, Yoshihiro Shimoda wrote: > > > This driver previously supported r8a779f0 (R-Car S4-8). Add support > > > for r8a779g0 (R-Car V4H). PCIe features of both r8a779f0 and r8a779g0 > > > are almost all the same. For example: > > > - PCI Express Base Specification Revision 4.0 > > > - Root complex mode and endpoint mode are supported > > > > > > However, r8a779g0 requires specific firmware downloading, to > > > initialize the PHY. Otherwise, the PCIe controller cannot work. > > > The firmware is attached in the manual of the r8a779g0 as text. > > > So, convert it to a binary file by using a script. > > > > The firmware is expected to be present in userspace. So where is it btw? Is it > > upstreamed to linux-firmware? > > I may misunderstand your question, but the firmware is in the SoC's datasheet as > a text file. So, we need to convert it to a binary file and store it into the rootfs. > (Also, for debug purpose, we can use built-in firmware from "CONFIG_EXTRA_FIRMWARE".) > So how does the conversion need to happen? Does it require any tool or just copy the content to a file and pass it as a firmware? Whatever the method is, it should be documented in the commit message (also in the driver). > > You cannot ask users to manually copy the text and convert it to a binary file. > > But if the firmware or sequence is not going to change, why can't you hardcode > > it in the driver itself? > > This is because that Renesas is not able to distribute the firmware freely. > > > > > > > Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> > > > --- > > > drivers/pci/controller/dwc/pcie-rcar-gen4.c | 201 +++++++++++++++++++- > > > 1 file changed, 200 insertions(+), 1 deletion(-) > > > > > > diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c > > > index 980a916933d6..4e934e9156f2 100644 > > > --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c > > > +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c > > > @@ -5,8 +5,10 @@ > > > */ > > > > > > #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 +22,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,19 +40,47 @@ > > > #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) > > > + > > > +/* > > > + * The R-Car Gen4 documents don't describe the PHY registers' name. > > > + * But, the initialization procedure describes these offsets. So, > > > + * this driver makes up own #defines for the offsets. > > > + */ > > > +#define RCAR_GEN4_PCIE_PHY_0f8 0x0f8 > > > +#define RCAR_GEN4_PCIE_PHY_148 0x148 > > > +#define RCAR_GEN4_PCIE_PHY_1d4 0x1d4 > > > +#define RCAR_GEN4_PCIE_PHY_514 0x514 > > > +#define RCAR_GEN4_PCIE_PHY_700 0x700 > > > + > > > > As I said before, these defines provide no information about the registers at > > all. So please use the offset directly and add a comment. > > I got it. > > > > #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); > > > > What is this init for? Controller? PHY? > > This init is for controller. > Why do you need a callback for this? There is just a single function that is used as of now, so please move the contents to rcar_gen4_pcie_common_init(). > > > int (*ltssm_enable)(struct rcar_gen4_pcie *rcar); > > > enum dw_pcie_device_mode mode; > > > }; > > > @@ -57,12 +88,144 @@ struct rcar_gen4_pcie_drvdata { > > > struct rcar_gen4_pcie { > > > struct dw_pcie dw; > > > void __iomem *base; > > > + void __iomem *phy_base; > > > struct platform_device *pdev; > > > 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_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); > > > +} > > > + > > > +static int rcar_gen4_pcie_reg_check(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; > > > > What is this function checking actually? It is just a DBI read. Do you expect > > these register accesses to fail? > > This function is checking whether the register's value with mask is zero or not. > - If non-zero, return -EAGAIN. > - If zero, return 0. (this is expected value.) > > Perhaps, should I change the function name? For example, rcar_gen4_pcie_reg_test_bit()? > According to the datasheet, software needs to write registers again if the register > value(s) is(are) not expected value(s). > Well, I was asking under what circumstances the write may result in non-zero value? - Mani
Hi Manivannan, > From: Manivannan Sadhasivam, Sent: Thursday, May 16, 2024 9:52 PM > > On Tue, May 14, 2024 at 11:27:30AM +0000, Yoshihiro Shimoda wrote: > > Hi Manivannan, > > > > > From: Manivannan Sadhasivam, Sent: Saturday, May 11, 2024 5:03 PM > > > > > > On Mon, Apr 15, 2024 at 05:11:34PM +0900, Yoshihiro Shimoda wrote: > > > > This driver previously supported r8a779f0 (R-Car S4-8). Add support > > > > for r8a779g0 (R-Car V4H). PCIe features of both r8a779f0 and r8a779g0 > > > > are almost all the same. For example: > > > > - PCI Express Base Specification Revision 4.0 > > > > - Root complex mode and endpoint mode are supported > > > > > > > > However, r8a779g0 requires specific firmware downloading, to > > > > initialize the PHY. Otherwise, the PCIe controller cannot work. > > > > The firmware is attached in the manual of the r8a779g0 as text. > > > > So, convert it to a binary file by using a script. > > > > > > The firmware is expected to be present in userspace. So where is it btw? Is it > > > upstreamed to linux-firmware? > > > > I may misunderstand your question, but the firmware is in the SoC's datasheet as > > a text file. So, we need to convert it to a binary file and store it into the rootfs. > > (Also, for debug purpose, we can use built-in firmware from "CONFIG_EXTRA_FIRMWARE".) > > > > So how does the conversion need to happen? Does it require any tool or just copy > the content to a file and pass it as a firmware? I'm sorry for lack explanation. The way is: 1) copy a text file to a PC. 2) do a script with the text file, and then generate a binary file. 3) copy the binary file to the rootfs of the R-Car. > Whatever the method is, it should be documented in the commit message (also in > the driver). I got it. I'll add such description in the commit and the driver. > > > You cannot ask users to manually copy the text and convert it to a binary file. > > > But if the firmware or sequence is not going to change, why can't you hardcode > > > it in the driver itself? > > > > This is because that Renesas is not able to distribute the firmware freely. > > > > > > > > > > Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> > > > > --- > > > > drivers/pci/controller/dwc/pcie-rcar-gen4.c | 201 +++++++++++++++++++- > > > > 1 file changed, 200 insertions(+), 1 deletion(-) > > > > > > > > diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c > > > > index 980a916933d6..4e934e9156f2 100644 > > > > --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c > > > > +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c > > > > @@ -5,8 +5,10 @@ > > > > */ > > > > > > > > #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 +22,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,19 +40,47 @@ > > > > #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) > > > > + > > > > +/* > > > > + * The R-Car Gen4 documents don't describe the PHY registers' name. > > > > + * But, the initialization procedure describes these offsets. So, > > > > + * this driver makes up own #defines for the offsets. > > > > + */ > > > > +#define RCAR_GEN4_PCIE_PHY_0f8 0x0f8 > > > > +#define RCAR_GEN4_PCIE_PHY_148 0x148 > > > > +#define RCAR_GEN4_PCIE_PHY_1d4 0x1d4 > > > > +#define RCAR_GEN4_PCIE_PHY_514 0x514 > > > > +#define RCAR_GEN4_PCIE_PHY_700 0x700 > > > > + > > > > > > As I said before, these defines provide no information about the registers at > > > all. So please use the offset directly and add a comment. > > > > I got it. > > > > > > #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); > > > > > > What is this init for? Controller? PHY? > > > > This init is for controller. > > > > Why do you need a callback for this? There is just a single function that is > used as of now, so please move the contents to rcar_gen4_pcie_common_init(). r8a779g0 requires the settings. But, r8a779f0 doesn't requires the setting. So, we can not move the contents to rcar_gen4_pcie_common_init(). > > > > int (*ltssm_enable)(struct rcar_gen4_pcie *rcar); > > > > enum dw_pcie_device_mode mode; > > > > }; > > > > @@ -57,12 +88,144 @@ struct rcar_gen4_pcie_drvdata { > > > > struct rcar_gen4_pcie { > > > > struct dw_pcie dw; > > > > void __iomem *base; > > > > + void __iomem *phy_base; > > > > struct platform_device *pdev; > > > > 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_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); > > > > +} > > > > + > > > > +static int rcar_gen4_pcie_reg_check(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; > > > > > > What is this function checking actually? It is just a DBI read. Do you expect > > > these register accesses to fail? > > > > This function is checking whether the register's value with mask is zero or not. > > - If non-zero, return -EAGAIN. > > - If zero, return 0. (this is expected value.) > > > > Perhaps, should I change the function name? For example, rcar_gen4_pcie_reg_test_bit()? > > According to the datasheet, software needs to write registers again if the register > > value(s) is(are) not expected value(s). > > > > Well, I was asking under what circumstances the write may result in non-zero > value? The current implementation is: ----- 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_check(rcar, PRTLGC89, BIT(30)) >= 0) break; ... dw_pcie_writel_dbi(dw, PRTLGC89, check_addr[i]); ret = rcar_gen4_pcie_reg_check(rcar, PRTLGC89, BIT(30)); ret |= rcar_gen4_pcie_reg_check(rcar, PRTLGC90, BIT(0)); if (ret >= 0) break; ----- This is from flowchart of the datasheet. Unfortunately, the datasheet doesn't describe detail. But, I think that I should implement such a code. And, I assume that these registers have a flag in the bits, so the write may result in non-zero value. Best regards, Yoshihiro Shimoda > - Mani > > -- > மணிவண்ணன் சதாசிவம்
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c index 980a916933d6..4e934e9156f2 100644 --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c @@ -5,8 +5,10 @@ */ #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 +22,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,19 +40,47 @@ #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) + +/* + * The R-Car Gen4 documents don't describe the PHY registers' name. + * But, the initialization procedure describes these offsets. So, + * this driver makes up own #defines for the offsets. + */ +#define RCAR_GEN4_PCIE_PHY_0f8 0x0f8 +#define RCAR_GEN4_PCIE_PHY_148 0x148 +#define RCAR_GEN4_PCIE_PHY_1d4 0x1d4 +#define RCAR_GEN4_PCIE_PHY_514 0x514 +#define RCAR_GEN4_PCIE_PHY_700 0x700 + #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_enable)(struct rcar_gen4_pcie *rcar); enum dw_pcie_device_mode mode; }; @@ -57,12 +88,144 @@ struct rcar_gen4_pcie_drvdata { struct rcar_gen4_pcie { struct dw_pcie dw; void __iomem *base; + void __iomem *phy_base; struct platform_device *pdev; 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_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); +} + +static int rcar_gen4_pcie_reg_check(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) +{ + 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, "%s: Requesting firmware failed\n", __func__); + 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_check(rcar, PRTLGC89, BIT(30)) >= 0) + break; + if (!(--timeout)) { + ret = -ETIMEDOUT; + goto exit; + } + usleep_range(100, 200); + } while (1); + } + + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_0f8, 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_check(rcar, PRTLGC89, BIT(30)); + ret |= rcar_gen4_pcie_reg_check(rcar, PRTLGC90, BIT(0)); + if (ret >= 0) + break; + if (!(--timeout)) { + ret = -ETIMEDOUT; + goto exit; + } + usleep_range(100, 200); + } while (1); + } + + ret = 0; +exit: + release_firmware(fw); + + return ret; +} + +static int rcar_gen4_pcie_enable_phy(struct rcar_gen4_pcie *rcar) +{ + struct dw_pcie *dw = &rcar->dw; + u32 val; + int ret; + + 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); + + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(28), 0); + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(20), 0); + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(12), 0); + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_700, BIT(4), 0); + + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, + GENMASK(23, 22), BIT(22)); + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, + GENMASK(18, 16), GENMASK(17, 16)); + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, + GENMASK(7, 6), BIT(6)); + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_148, + GENMASK(2, 0), GENMASK(11, 0)); + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_1d4, + GENMASK(16, 15), GENMASK(16, 15)); + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_514, BIT(26), BIT(26)); + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_0f8, BIT(16), 0); + rcar_gen4_pcie_phy_reg_update_bits(rcar, RCAR_GEN4_PCIE_PHY_0f8, 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 + RCAR_GEN4_PCIE_PHY_0f8, 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 void rcar_gen4_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable) { @@ -200,6 +363,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: @@ -241,6 +407,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"); @@ -517,6 +687,31 @@ static int r8a779f0_pcie_ltssm_enable(struct rcar_gen4_pcie *rcar) return 0; } +static void rcar_gen4_pcie_additional_common_init(struct rcar_gen4_pcie *rcar) +{ + struct dw_pcie *dw = &rcar->dw; + u32 val; + + /* + * The SoC manual said the register setting is required. Otherwise, + * linkup failed. + */ + 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 int rcar_gen4_pcie_ltssm_enable(struct rcar_gen4_pcie *rcar) +{ + return rcar_gen4_pcie_enable_phy(rcar); +} + static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie = { .ltssm_enable = r8a779f0_pcie_ltssm_enable, .mode = DW_PCIE_RC_TYPE, @@ -528,10 +723,14 @@ static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie_ep = { }; static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie = { + .additional_common_init = rcar_gen4_pcie_additional_common_init, + .ltssm_enable = rcar_gen4_pcie_ltssm_enable, .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_enable = rcar_gen4_pcie_ltssm_enable, .mode = DW_PCIE_EP_TYPE, };
This driver previously supported r8a779f0 (R-Car S4-8). Add support for r8a779g0 (R-Car V4H). PCIe features of both r8a779f0 and r8a779g0 are almost all the same. For example: - PCI Express Base Specification Revision 4.0 - Root complex mode and endpoint mode are supported However, r8a779g0 requires specific firmware downloading, to initialize the PHY. Otherwise, the PCIe controller cannot work. The firmware is attached in the manual of the r8a779g0 as text. So, convert it to a binary file by using a script. Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> --- drivers/pci/controller/dwc/pcie-rcar-gen4.c | 201 +++++++++++++++++++- 1 file changed, 200 insertions(+), 1 deletion(-)