diff options
| author | Bjorn Helgaas <bhelgaas@google.com> | 2026-08-21 16:40:46 -0500 |
|---|---|---|
| committer | Bjorn Helgaas <bhelgaas@google.com> | 2026-08-21 16:40:46 -0500 |
| commit | 003bb33da95b0465ea524b32c94c3534bc8e01cc (patch) | |
| tree | 0d07e3b0f5500de144c6688de74dcbbd99bc4018 | |
| parent | 707ba6063c47125dedb712fb075e6a7e5239218d (diff) | |
| parent | 007c7304d80f1462b98a36284f45adcc1eb8adec (diff) | |
Merge branch 'pci/controller/rzg3s-host'
- Add DT binding and driver support for RZ/V2H(P) SoC, which contains two
PCIe controllers, configured either as a single x4 link or two
independent x2 link controllers (Lad Prabhakar)
* pci/controller/rzg3s-host:
PCI: rzg3s-host: Add support for RZ/V2H(P) SoC
PCI: rzg3s-host: Prepare System Controller handling for multiple controllers
PCI: rzg3s-host: Use shared reset controls for power domain resets
dt-bindings: PCI: renesas,r9a08g045-pcie: Add RZ/V2H(P) support
| -rw-r--r-- | Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml | 34 | ||||
| -rw-r--r-- | drivers/pci/controller/pcie-rzg3s-host.c | 233 |
2 files changed, 247 insertions, 20 deletions
diff --git a/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml b/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml index 90086909e921..09d78bd1a577 100644 --- a/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml @@ -14,7 +14,7 @@ description: | with PCIe Base Specification 4.0 and supports different link speeds depending on the SoC variant: - Gen2 (5 GT/s): RZ/G3S - - Gen3 (8 GT/s): RZ/G3E, RZ/V2N + - Gen3 (8 GT/s): RZ/G3E, RZ/V2H(P), RZ/V2N properties: compatible: @@ -22,6 +22,7 @@ properties: - enum: - renesas,r9a08g045-pcie # RZ/G3S - renesas,r9a09g047-pcie # RZ/G3E + - renesas,r9a09g057-pcie # RZ/V2H(P) - items: - const: renesas,r9a09g056-pcie # RZ/V2N - const: renesas,r9a09g047-pcie @@ -139,7 +140,13 @@ properties: - clkl1pm clock request state - power off information in L2 state - errors (fatal, non-fatal, correctable) - $ref: /schemas/types.yaml#/definitions/phandle + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: Phandle to system controller + - description: PCIe controller index + enum: [0, 1] + minItems: 1 patternProperties: "^pcie@0,[0-0]$": @@ -220,7 +227,9 @@ allOf: properties: compatible: contains: - const: renesas,r9a09g047-pcie + enum: + - renesas,r9a09g047-pcie + - renesas,r9a09g057-pcie then: properties: interrupts: @@ -235,6 +244,25 @@ allOf: maxItems: 1 reset-names: maxItems: 1 + - if: + properties: + compatible: + contains: + const: renesas,r9a09g057-pcie + then: + properties: + num-lanes: + enum: [2, 4] + renesas,sysc: + items: + - minItems: 2 + required: + - num-lanes + else: + properties: + renesas,sysc: + items: + - maxItems: 1 unevaluatedProperties: false diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c index 66f687304c1c..a1a78bdb7af1 100644 --- a/drivers/pci/controller/pcie-rzg3s-host.c +++ b/drivers/pci/controller/pcie-rzg3s-host.c @@ -181,6 +181,16 @@ #define RZG3S_REQ_ISSUE_TIMEOUT_US 2500 /** + * enum rzg3s_sysc_link_mode - PCIe link configuration modes + * @RZG3S_SYSC_LINK_MODE_SINGLE_X4: Single port with x4 lanes + * @RZG3S_SYSC_LINK_MODE_DUAL_X2: Dual ports with x2 lanes each + */ +enum rzg3s_sysc_link_mode { + RZG3S_SYSC_LINK_MODE_SINGLE_X4 = 1, + RZG3S_SYSC_LINK_MODE_DUAL_X2 = 3, +}; + +/** * struct rzg3s_sysc_function - System Controller function descriptor * @offset: Register offset from the System Controller base address * @mask: Bit mask for the function within the register @@ -195,12 +205,14 @@ struct rzg3s_sysc_function { * @RZG3S_SYSC_FUNC_ID_RST_RSM_B: RST_RSM_B SYSC function ID * @RZG3S_SYSC_FUNC_ID_L1_ALLOW: L1 allow SYSC function ID * @RZG3S_SYSC_FUNC_ID_MODE: Mode SYSC function ID + * @RZG3S_SYSC_FUNC_ID_LINK_MASTER: Link master SYSC function ID * @RZG3S_SYSC_FUNC_ID_MAX: Max SYSC function ID */ enum rzg3s_sysc_func_id { RZG3S_SYSC_FUNC_ID_RST_RSM_B, RZG3S_SYSC_FUNC_ID_L1_ALLOW, RZG3S_SYSC_FUNC_ID_MODE, + RZG3S_SYSC_FUNC_ID_LINK_MASTER, RZG3S_SYSC_FUNC_ID_MAX, }; @@ -242,6 +254,18 @@ struct rzg3s_pcie_msi { int irq; }; +/** + * enum rzg3s_pcie_controller_id - RZ/G3S PCIe controller IDs + * @RZG3S_PCIE_CONTROLLER_ID_0: PCIe controller 0 + * @RZG3S_PCIE_CONTROLLER_ID_1: PCIe controller 1 + * @RZG3S_PCIE_CONTROLLER_ID_MAX: Max PCIe controllers + */ +enum rzg3s_pcie_controller_id { + RZG3S_PCIE_CONTROLLER_ID_0, + RZG3S_PCIE_CONTROLLER_ID_1, + RZG3S_PCIE_CONTROLLER_ID_MAX, +}; + struct rzg3s_pcie_host; /** @@ -250,24 +274,28 @@ struct rzg3s_pcie_host; * @config_pre_init: Optional callback for SoC-specific pre-configuration * @config_post_init: Callback for SoC-specific post-configuration * @config_deinit: Callback for SoC-specific de-initialization + * @setup_lanes: Callback for setting up the number of lanes * @power_resets: array with the resets that need to be de-asserted after * power-on * @cfg_resets: array with the resets that need to be de-asserted after * configuration - * @sysc_info: SYSC info + * @sysc_info: System Controller info for each controller * @num_power_resets: number of power resets * @num_cfg_resets: number of configuration resets + * @num_pcie_controllers: number of PCIe controllers */ struct rzg3s_pcie_soc_data { int (*init_phy)(struct rzg3s_pcie_host *host); void (*config_pre_init)(struct rzg3s_pcie_host *host); int (*config_post_init)(struct rzg3s_pcie_host *host); int (*config_deinit)(struct rzg3s_pcie_host *host); + int (*setup_lanes)(struct rzg3s_pcie_host *host); const char * const *power_resets; const char * const *cfg_resets; - struct rzg3s_sysc_info sysc_info; + struct rzg3s_sysc_info sysc_info[RZG3S_PCIE_CONTROLLER_ID_MAX]; u8 num_power_resets; u8 num_cfg_resets; + u8 num_pcie_controllers; }; /** @@ -297,6 +325,8 @@ struct rzg3s_pcie_port { * @hw_lock: lock for access to the HW resources * @intx_irqs: INTx interrupts * @max_link_speed: maximum supported link speed + * @controller_id: PCIe controller identifier, used for System Controller access + * @num_lanes: The number of lanes */ struct rzg3s_pcie_host { void __iomem *axi; @@ -312,10 +342,24 @@ struct rzg3s_pcie_host { raw_spinlock_t hw_lock; int intx_irqs[PCI_NUM_INTX]; int max_link_speed; + enum rzg3s_pcie_controller_id controller_id; + u8 num_lanes; }; #define rzg3s_msi_to_host(_msi) container_of(_msi, struct rzg3s_pcie_host, msi) +/* + * RZ/V2H(P) supports a total of 4 lanes shared across two controllers. + * rzv2h_lane_lock serialises both the counter update and the SYSC + * register write so that concurrent async probes cannot race on the + * shared LINK_MASTER register (offset 0x1060). + * rzv2h_num_total_lanes tracks global lane usage to prevent + * over-allocation or invalid bifurcation modes. + */ +#define RZV2H_PCIE_MAX_LANES 4 +static DEFINE_SPINLOCK(rzv2h_lane_lock); +static u8 rzv2h_num_total_lanes; + static int rzg3s_sysc_config_func(struct rzg3s_sysc *sysc, enum rzg3s_sysc_func_id fid, u32 val) { @@ -1142,6 +1186,13 @@ static int rzg3s_pcie_config_init(struct rzg3s_pcie_host *host) rzg3s_pcie_update_bits(host->pcie, PCI_CLASS_REVISION, mask, field_prep(mask, PCI_CLASS_BRIDGE_PCI_NORMAL)); + if (host->num_lanes) { + rzg3s_pcie_update_bits(host->pcie + RZG3S_PCI_CFG_PCIEC, + PCI_EXP_LNKCAP, PCI_EXP_LNKCAP_MLW, + FIELD_PREP(PCI_EXP_LNKCAP_MLW, + host->num_lanes)); + } + /* Disable access control to the CFGU */ writel_relaxed(0, host->axi + RZG3S_PCI_PERM); @@ -1277,9 +1328,9 @@ static int rzg3s_pcie_resets_prepare_and_get(struct rzg3s_pcie_host *host) for (i = 0; i < data->num_cfg_resets; i++) host->cfg_resets[i].id = data->cfg_resets[i]; - ret = devm_reset_control_bulk_get_exclusive(host->dev, - data->num_power_resets, - host->power_resets); + ret = devm_reset_control_bulk_get_shared(host->dev, + data->num_power_resets, + host->power_resets); if (ret) return ret; @@ -1674,6 +1725,76 @@ teardown_irqdomain: return ret; } +static int rzg3s_pcie_get_controller_id(struct rzg3s_pcie_host *host) +{ + struct device_node *np = host->dev->of_node; + struct of_phandle_args sysc_args; + int ret; + + if (host->data->num_pcie_controllers == 1) + return 0; + + ret = of_parse_phandle_with_fixed_args(np, "renesas,sysc", 1, 0, &sysc_args); + if (ret) + return ret; + + of_node_put(sysc_args.np); + + if (sysc_args.args[0] >= host->data->num_pcie_controllers || + sysc_args.args[0] >= RZG3S_PCIE_CONTROLLER_ID_MAX) + return -EINVAL; + + host->controller_id = sysc_args.args[0]; + + return 0; +} + +static int rzv2h_pcie_setup_lanes(struct rzg3s_pcie_host *host) +{ + struct device_node *np = host->dev->of_node; + u32 num_lanes; + int ret; + + ret = of_property_read_u32(np, "num-lanes", &num_lanes); + if (ret) + return ret; + + /* + * RZ/V2H(P) supports up to 4 lanes, but only in single x4 mode + * for the first controller. Dual x2 mode is supported with 2 + * lanes for both controllers. + */ + if (num_lanes != 4 && num_lanes != 2) + return -EINVAL; + + if (host->controller_id == RZG3S_PCIE_CONTROLLER_ID_1 && num_lanes > 2) + return -EINVAL; + + guard(spinlock)(&rzv2h_lane_lock); + if (rzv2h_num_total_lanes + num_lanes > RZV2H_PCIE_MAX_LANES) + return -EINVAL; + + ret = rzg3s_sysc_config_func(host->sysc, RZG3S_SYSC_FUNC_ID_LINK_MASTER, + num_lanes == 2 ? + RZG3S_SYSC_LINK_MODE_DUAL_X2 : + RZG3S_SYSC_LINK_MODE_SINGLE_X4); + if (ret) + return ret; + + rzv2h_num_total_lanes += num_lanes; + host->num_lanes = num_lanes; + + return 0; +} + +static void rzv2h_pcie_release_lanes(void *data) +{ + struct rzg3s_pcie_host *host = data; + + guard(spinlock)(&rzv2h_lane_lock); + rzv2h_num_total_lanes -= host->num_lanes; +} + static int rzg3s_pcie_probe(struct platform_device *pdev) { struct pci_host_bridge *bridge; @@ -1698,8 +1819,12 @@ static int rzg3s_pcie_probe(struct platform_device *pdev) if (!host->sysc) return -ENOMEM; + ret = rzg3s_pcie_get_controller_id(host); + if (ret) + return ret; + sysc = host->sysc; - sysc->info = &host->data->sysc_info; + sysc->info = &host->data->sysc_info[host->controller_id]; host->axi = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(host->axi)) @@ -1727,6 +1852,16 @@ static int rzg3s_pcie_probe(struct platform_device *pdev) if (ret) goto port_refclk_put; + if (host->data->setup_lanes) { + ret = host->data->setup_lanes(host); + if (ret) + goto sysc_signal_restore; + + ret = devm_add_action_or_reset(dev, rzv2h_pcie_release_lanes, host); + if (ret) + goto sysc_signal_restore; + } + ret = rzg3s_pcie_resets_prepare_and_get(host); if (ret) goto sysc_signal_restore; @@ -1841,6 +1976,16 @@ static int rzg3s_pcie_resume_noirq(struct device *dev) if (ret) return ret; + if (host->num_lanes) { + ret = rzg3s_sysc_config_func(host->sysc, + RZG3S_SYSC_FUNC_ID_LINK_MASTER, + host->num_lanes == 2 ? + RZG3S_SYSC_LINK_MODE_DUAL_X2 : + RZG3S_SYSC_LINK_MODE_SINGLE_X4); + if (ret) + goto assert_rst_rsm_b; + } + ret = rzg3s_pcie_power_resets_deassert(host); if (ret) goto assert_rst_rsm_b; @@ -1888,14 +2033,17 @@ static const struct rzg3s_pcie_soc_data rzg3s_soc_data = { .num_power_resets = ARRAY_SIZE(rzg3s_soc_power_resets), .cfg_resets = rzg3s_soc_cfg_resets, .num_cfg_resets = ARRAY_SIZE(rzg3s_soc_cfg_resets), + .num_pcie_controllers = 1, .config_post_init = rzg3s_pcie_config_post_init, .config_deinit = rzg3s_pcie_config_deinit, .init_phy = rzg3s_soc_pcie_init_phy, .sysc_info = { - .functions = { - [RZG3S_SYSC_FUNC_ID_RST_RSM_B] = { - .offset = 0xd74, - .mask = BIT(0), + [RZG3S_PCIE_CONTROLLER_ID_0] = { + .functions = { + [RZG3S_SYSC_FUNC_ID_RST_RSM_B] = { + .offset = 0xd74, + .mask = BIT(0), + }, }, }, }, @@ -1906,18 +2054,65 @@ static const char * const rzg3e_soc_power_resets[] = { "aresetn" }; static const struct rzg3s_pcie_soc_data rzg3e_soc_data = { .power_resets = rzg3e_soc_power_resets, .num_power_resets = ARRAY_SIZE(rzg3e_soc_power_resets), + .num_pcie_controllers = 1, .config_pre_init = rzg3e_pcie_config_pre_init, .config_post_init = rzg3e_pcie_config_post_init, .config_deinit = rzg3e_pcie_config_deinit, .sysc_info = { - .functions = { - [RZG3S_SYSC_FUNC_ID_L1_ALLOW] = { - .offset = 0x1020, - .mask = BIT(0), + [RZG3S_PCIE_CONTROLLER_ID_0] = { + .functions = { + [RZG3S_SYSC_FUNC_ID_L1_ALLOW] = { + .offset = 0x1020, + .mask = BIT(0), + }, + [RZG3S_SYSC_FUNC_ID_MODE] = { + .offset = 0x1024, + .mask = BIT(0), + }, }, - [RZG3S_SYSC_FUNC_ID_MODE] = { - .offset = 0x1024, - .mask = BIT(0), + }, + }, +}; + +static const struct rzg3s_pcie_soc_data rzv2h_soc_data = { + .power_resets = rzg3e_soc_power_resets, + .num_power_resets = ARRAY_SIZE(rzg3e_soc_power_resets), + .num_pcie_controllers = 2, + .config_pre_init = rzg3e_pcie_config_pre_init, + .config_post_init = rzg3e_pcie_config_post_init, + .config_deinit = rzg3e_pcie_config_deinit, + .setup_lanes = rzv2h_pcie_setup_lanes, + .sysc_info = { + [RZG3S_PCIE_CONTROLLER_ID_0] = { + .functions = { + [RZG3S_SYSC_FUNC_ID_L1_ALLOW] = { + .offset = 0x1020, + .mask = BIT(0), + }, + [RZG3S_SYSC_FUNC_ID_MODE] = { + .offset = 0x1024, + .mask = BIT(0), + }, + [RZG3S_SYSC_FUNC_ID_LINK_MASTER] = { + .offset = 0x1060, + .mask = GENMASK(9, 8), + }, + }, + }, + [RZG3S_PCIE_CONTROLLER_ID_1] = { + .functions = { + [RZG3S_SYSC_FUNC_ID_L1_ALLOW] = { + .offset = 0x1050, + .mask = BIT(0), + }, + [RZG3S_SYSC_FUNC_ID_MODE] = { + .offset = 0x1054, + .mask = BIT(0), + }, + [RZG3S_SYSC_FUNC_ID_LINK_MASTER] = { + .offset = 0x1060, + .mask = GENMASK(9, 8), + }, }, }, }, @@ -1932,6 +2127,10 @@ static const struct of_device_id rzg3s_pcie_of_match[] = { .compatible = "renesas,r9a09g047-pcie", .data = &rzg3e_soc_data, }, + { + .compatible = "renesas,r9a09g057-pcie", + .data = &rzv2h_soc_data, + }, {} }; |
