Merge branch 'pci/controller/rcar'

- Add generic T_PVPERL macro for the required interval between power being
  stable and PERST# being inactive (Yoshihiro Shimoda)

- Factor out dw_pcie_link_set_max_link_width() (Yoshihiro Shimoda)

- Update PCI_EXP_LNKCAP_MLW so Link Capabilities shows the correct max link
  width (Yoshihiro Shimoda)

- Drop tegra194 PCI_EXP_LNKCAP_MLW setting since dw_pcie_setup() already
  does it (Yoshihiro Shimoda)

- Add dwc support for different dbi and dbi2 register offsets, to be used
  for R-Car Gen4 controllers (Yoshihiro Shimoda)

- Add EDMA_UNROLL capability flag for R-Car Gen4 controllers that don't
  correctly advertise unrolled mapping via their eDMA CTRL register
  (Yoshihiro Shimoda)

- Export dw_pcie_ep_exit() for use by the modular R-Car Gen4 driver
  (Yoshihiro Shimoda)

- Add .pre_init() and .deinit() hooks for use by R-Car Gen4 controllers
  (Yoshihiro Shimoda)

- Increase snps,dw-pcie DT reg and reg-names maxItems for R-Car Gen4
  controllers (Yoshihiro Shimoda)

- Add rcar-gen4-pci host and endpoint DT bindings and drivers (Yoshihiro
  Shimoda)

- Add Renesas R8A779F0 Device ID to pci_endpoint_test to allow testing on
  R-Car S4-8 (Yoshihiro Shimoda)

* pci/controller/rcar:
  misc: pci_endpoint_test: Add Device ID for R-Car S4-8 PCIe controller
  MAINTAINERS: Update PCI DRIVER FOR RENESAS R-CAR for R-Car Gen4
  PCI: rcar-gen4: Add endpoint mode support
  PCI: rcar-gen4: Add R-Car Gen4 PCIe controller support for host mode
  dt-bindings: PCI: renesas: Add R-Car Gen4 PCIe Endpoint
  dt-bindings: PCI: renesas: Add R-Car Gen4 PCIe Host
  dt-bindings: PCI: dwc: Update maxItems of reg and reg-names
  PCI: dwc: endpoint: Introduce .pre_init() and .deinit()
  PCI: dwc: Expose dw_pcie_write_dbi2() to module
  PCI: dwc: Expose dw_pcie_ep_exit() to module
  PCI: dwc: Add EDMA_UNROLL capability flag
  PCI: dwc: endpoint: Add multiple PFs support for dbi2
  PCI: tegra194: Drop PCI_EXP_LNKSTA_NLW setting
  PCI: dwc: Add missing PCI_EXP_LNKCAP_MLW handling
  PCI: dwc: Add dw_pcie_link_set_max_link_width()
  PCI: Add T_PVPERL macro
This commit is contained in:
Bjorn Helgaas 2023-10-28 13:31:01 -05:00
commit db20113d70
15 changed files with 907 additions and 69 deletions

View File

@ -0,0 +1,115 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# Copyright (C) 2022-2023 Renesas Electronics Corp.
%YAML 1.2
---
$id: http://devicetree.org/schemas/pci/rcar-gen4-pci-ep.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas R-Car Gen4 PCIe Endpoint
maintainers:
- Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
allOf:
- $ref: snps,dw-pcie-ep.yaml#
properties:
compatible:
items:
- const: renesas,r8a779f0-pcie-ep # R-Car S4-8
- const: renesas,rcar-gen4-pcie-ep # R-Car Gen4
reg:
maxItems: 7
reg-names:
items:
- const: dbi
- const: dbi2
- const: atu
- const: dma
- const: app
- const: phy
- const: addr_space
interrupts:
maxItems: 3
interrupt-names:
items:
- const: dma
- const: sft_ce
- const: app
clocks:
maxItems: 2
clock-names:
items:
- const: core
- const: ref
power-domains:
maxItems: 1
resets:
maxItems: 1
reset-names:
items:
- const: pwr
max-link-speed:
maximum: 4
num-lanes:
maximum: 4
max-functions:
maximum: 2
required:
- compatible
- reg
- reg-names
- interrupts
- interrupt-names
- clocks
- clock-names
- power-domains
- resets
- reset-names
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/r8a779f0-cpg-mssr.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/power/r8a779f0-sysc.h>
soc {
#address-cells = <2>;
#size-cells = <2>;
pcie0_ep: pcie-ep@e65d0000 {
compatible = "renesas,r8a779f0-pcie-ep", "renesas,rcar-gen4-pcie-ep";
reg = <0 0xe65d0000 0 0x2000>, <0 0xe65d2000 0 0x1000>,
<0 0xe65d3000 0 0x2000>, <0 0xe65d5000 0 0x1200>,
<0 0xe65d6200 0 0x0e00>, <0 0xe65d7000 0 0x0400>,
<0 0xfe000000 0 0x400000>;
reg-names = "dbi", "dbi2", "atu", "dma", "app", "phy", "addr_space";
interrupts = <GIC_SPI 417 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 418 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 422 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "dma", "sft_ce", "app";
clocks = <&cpg CPG_MOD 624>, <&pcie0_clkref>;
clock-names = "core", "ref";
power-domains = <&sysc R8A779F0_PD_ALWAYS_ON>;
resets = <&cpg 624>;
reset-names = "pwr";
max-link-speed = <4>;
num-lanes = <2>;
max-functions = /bits/ 8 <2>;
};
};

View File

@ -0,0 +1,127 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# Copyright (C) 2022-2023 Renesas Electronics Corp.
%YAML 1.2
---
$id: http://devicetree.org/schemas/pci/rcar-gen4-pci-host.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas R-Car Gen4 PCIe Host
maintainers:
- Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
allOf:
- $ref: snps,dw-pcie.yaml#
properties:
compatible:
items:
- const: renesas,r8a779f0-pcie # R-Car S4-8
- const: renesas,rcar-gen4-pcie # R-Car Gen4
reg:
maxItems: 7
reg-names:
items:
- const: dbi
- const: dbi2
- const: atu
- const: dma
- const: app
- const: phy
- const: config
interrupts:
maxItems: 4
interrupt-names:
items:
- const: msi
- const: dma
- const: sft_ce
- const: app
clocks:
maxItems: 2
clock-names:
items:
- const: core
- const: ref
power-domains:
maxItems: 1
resets:
maxItems: 1
reset-names:
items:
- const: pwr
max-link-speed:
maximum: 4
num-lanes:
maximum: 4
required:
- compatible
- reg
- reg-names
- interrupts
- interrupt-names
- clocks
- clock-names
- power-domains
- resets
- reset-names
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/r8a779f0-cpg-mssr.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/power/r8a779f0-sysc.h>
soc {
#address-cells = <2>;
#size-cells = <2>;
pcie: pcie@e65d0000 {
compatible = "renesas,r8a779f0-pcie", "renesas,rcar-gen4-pcie";
reg = <0 0xe65d0000 0 0x1000>, <0 0xe65d2000 0 0x0800>,
<0 0xe65d3000 0 0x2000>, <0 0xe65d5000 0 0x1200>,
<0 0xe65d6200 0 0x0e00>, <0 0xe65d7000 0 0x0400>,
<0 0xfe000000 0 0x400000>;
reg-names = "dbi", "dbi2", "atu", "dma", "app", "phy", "config";
interrupts = <GIC_SPI 416 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 417 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 418 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 422 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "msi", "dma", "sft_ce", "app";
clocks = <&cpg CPG_MOD 624>, <&pcie0_clkref>;
clock-names = "core", "ref";
power-domains = <&sysc R8A779F0_PD_ALWAYS_ON>;
resets = <&cpg 624>;
reset-names = "pwr";
max-link-speed = <4>;
num-lanes = <2>;
#address-cells = <3>;
#size-cells = <2>;
bus-range = <0x00 0xff>;
device_type = "pci";
ranges = <0x01000000 0 0x00000000 0 0xfe000000 0 0x00400000>,
<0x02000000 0 0x30000000 0 0x30000000 0 0x10000000>;
dma-ranges = <0x42000000 0 0x00000000 0 0x00000000 1 0x00000000>;
#interrupt-cells = <1>;
interrupt-map-mask = <0 0 0 7>;
interrupt-map = <0 0 0 1 &gic GIC_SPI 416 IRQ_TYPE_LEVEL_HIGH>,
<0 0 0 2 &gic GIC_SPI 416 IRQ_TYPE_LEVEL_HIGH>,
<0 0 0 3 &gic GIC_SPI 416 IRQ_TYPE_LEVEL_HIGH>,
<0 0 0 4 &gic GIC_SPI 416 IRQ_TYPE_LEVEL_HIGH>;
snps,enable-cdm-check;
};
};

View File

@ -33,11 +33,11 @@ properties:
specific for each activated function, while the rest of the sub-spaces
are common for all of them (if there are more than one).
minItems: 2
maxItems: 6
maxItems: 7
reg-names:
minItems: 2
maxItems: 6
maxItems: 7
interrupts:
description:

View File

@ -33,11 +33,11 @@ properties:
normal controller functioning. iATU memory IO region is also required
if the space is unrolled (IP-core version >= 4.80a).
minItems: 2
maxItems: 5
maxItems: 7
reg-names:
minItems: 2
maxItems: 5
maxItems: 7
items:
oneOf:
- description:

View File

@ -42,11 +42,11 @@ properties:
are required for the normal controller work. iATU memory IO region is
also required if the space is unrolled (IP-core version >= 4.80a).
minItems: 2
maxItems: 5
maxItems: 7
reg-names:
minItems: 2
maxItems: 5
maxItems: 7
items:
oneOf:
- description:

View File

@ -16424,6 +16424,7 @@ L: linux-renesas-soc@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/pci/*rcar*
F: drivers/pci/controller/*rcar*
F: drivers/pci/controller/dwc/*rcar*
PCI DRIVER FOR SAMSUNG EXYNOS
M: Jingoo Han <jingoohan1@gmail.com>

View File

@ -81,6 +81,7 @@
#define PCI_DEVICE_ID_RENESAS_R8A774B1 0x002b
#define PCI_DEVICE_ID_RENESAS_R8A774C0 0x002d
#define PCI_DEVICE_ID_RENESAS_R8A774E1 0x0025
#define PCI_DEVICE_ID_RENESAS_R8A779F0 0x0031
static DEFINE_IDA(pci_endpoint_test_ida);
@ -990,6 +991,9 @@ static const struct pci_device_id pci_endpoint_test_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774B1),},
{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774C0),},
{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774E1),},
{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A779F0),
.driver_data = (kernel_ulong_t)&default_data,
},
{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_J721E),
.driver_data = (kernel_ulong_t)&j721e_data,
},

View File

@ -286,6 +286,31 @@ config PCIE_QCOM_EP
to work in endpoint mode. The PCIe controller uses the DesignWare core
plus Qualcomm-specific hardware wrappers.
config PCIE_RCAR_GEN4
tristate
config PCIE_RCAR_GEN4_HOST
tristate "Renesas R-Car Gen4 PCIe controller (host mode)"
depends on ARCH_RENESAS || COMPILE_TEST
depends on PCI_MSI
select PCIE_DW_HOST
select PCIE_RCAR_GEN4
help
Say Y here if you want PCIe controller (host mode) on R-Car Gen4 SoCs.
To compile this driver as a module, choose M here: the module will be
called pcie-rcar-gen4.ko. This uses the DesignWare core.
config PCIE_RCAR_GEN4_EP
tristate "Renesas R-Car Gen4 PCIe controller (endpoint mode)"
depends on ARCH_RENESAS || COMPILE_TEST
depends on PCI_ENDPOINT
select PCIE_DW_EP
select PCIE_RCAR_GEN4
help
Say Y here if you want PCIe controller (endpoint mode) on R-Car Gen4
SoCs. To compile this driver as a module, choose M here: the module
will be called pcie-rcar-gen4.ko. This uses the DesignWare core.
config PCIE_ROCKCHIP_DW_HOST
bool "Rockchip DesignWare PCIe controller"
select PCIE_DW

View File

@ -26,6 +26,7 @@ obj-$(CONFIG_PCIE_TEGRA194) += pcie-tegra194.o
obj-$(CONFIG_PCIE_UNIPHIER) += pcie-uniphier.o
obj-$(CONFIG_PCIE_UNIPHIER_EP) += pcie-uniphier-ep.o
obj-$(CONFIG_PCIE_VISCONTI_HOST) += pcie-visconti.o
obj-$(CONFIG_PCIE_RCAR_GEN4) += pcie-rcar-gen4.o
# The following drivers are for devices that use the generic ACPI
# pci_root.c driver but don't support standard ECAM config access.

View File

@ -52,21 +52,35 @@ static unsigned int dw_pcie_ep_func_select(struct dw_pcie_ep *ep, u8 func_no)
return func_offset;
}
static unsigned int dw_pcie_ep_get_dbi2_offset(struct dw_pcie_ep *ep, u8 func_no)
{
unsigned int dbi2_offset = 0;
if (ep->ops->get_dbi2_offset)
dbi2_offset = ep->ops->get_dbi2_offset(ep, func_no);
else if (ep->ops->func_conf_select) /* for backward compatibility */
dbi2_offset = ep->ops->func_conf_select(ep, func_no);
return dbi2_offset;
}
static void __dw_pcie_ep_reset_bar(struct dw_pcie *pci, u8 func_no,
enum pci_barno bar, int flags)
{
u32 reg;
unsigned int func_offset = 0;
unsigned int func_offset, dbi2_offset;
struct dw_pcie_ep *ep = &pci->ep;
u32 reg, reg_dbi2;
func_offset = dw_pcie_ep_func_select(ep, func_no);
dbi2_offset = dw_pcie_ep_get_dbi2_offset(ep, func_no);
reg = func_offset + PCI_BASE_ADDRESS_0 + (4 * bar);
reg_dbi2 = dbi2_offset + PCI_BASE_ADDRESS_0 + (4 * bar);
dw_pcie_dbi_ro_wr_en(pci);
dw_pcie_writel_dbi2(pci, reg, 0x0);
dw_pcie_writel_dbi2(pci, reg_dbi2, 0x0);
dw_pcie_writel_dbi(pci, reg, 0x0);
if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) {
dw_pcie_writel_dbi2(pci, reg + 4, 0x0);
dw_pcie_writel_dbi2(pci, reg_dbi2 + 4, 0x0);
dw_pcie_writel_dbi(pci, reg + 4, 0x0);
}
dw_pcie_dbi_ro_wr_dis(pci);
@ -228,16 +242,18 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
{
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
unsigned int func_offset, dbi2_offset;
enum pci_barno bar = epf_bar->barno;
size_t size = epf_bar->size;
int flags = epf_bar->flags;
unsigned int func_offset = 0;
u32 reg, reg_dbi2;
int ret, type;
u32 reg;
func_offset = dw_pcie_ep_func_select(ep, func_no);
dbi2_offset = dw_pcie_ep_get_dbi2_offset(ep, func_no);
reg = PCI_BASE_ADDRESS_0 + (4 * bar) + func_offset;
reg_dbi2 = PCI_BASE_ADDRESS_0 + (4 * bar) + dbi2_offset;
if (!(flags & PCI_BASE_ADDRESS_SPACE))
type = PCIE_ATU_TYPE_MEM;
@ -253,11 +269,11 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
dw_pcie_dbi_ro_wr_en(pci);
dw_pcie_writel_dbi2(pci, reg, lower_32_bits(size - 1));
dw_pcie_writel_dbi2(pci, reg_dbi2, lower_32_bits(size - 1));
dw_pcie_writel_dbi(pci, reg, flags);
if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) {
dw_pcie_writel_dbi2(pci, reg + 4, upper_32_bits(size - 1));
dw_pcie_writel_dbi2(pci, reg_dbi2 + 4, upper_32_bits(size - 1));
dw_pcie_writel_dbi(pci, reg + 4, 0);
}
@ -621,7 +637,11 @@ void dw_pcie_ep_exit(struct dw_pcie_ep *ep)
epc->mem->window.page_size);
pci_epc_mem_exit(epc);
if (ep->ops->deinit)
ep->ops->deinit(ep);
}
EXPORT_SYMBOL_GPL(dw_pcie_ep_exit);
static unsigned int dw_pcie_ep_find_ext_capability(struct dw_pcie *pci, int cap)
{
@ -723,6 +743,9 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
ep->phys_base = res->start;
ep->addr_size = resource_size(res);
if (ep->ops->pre_init)
ep->ops->pre_init(ep);
dw_pcie_version_detect(pci);
dw_pcie_iatu_detect(pci);
@ -777,7 +800,7 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
ep->page_size);
if (ret < 0) {
dev_err(dev, "Failed to initialize address space\n");
return ret;
goto err_ep_deinit;
}
ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys,
@ -814,6 +837,10 @@ err_free_epc_mem:
err_exit_epc_mem:
pci_epc_mem_exit(epc);
err_ep_deinit:
if (ep->ops->deinit)
ep->ops->deinit(ep);
return ret;
}
EXPORT_SYMBOL_GPL(dw_pcie_ep_init);

View File

@ -365,6 +365,7 @@ void dw_pcie_write_dbi2(struct dw_pcie *pci, u32 reg, size_t size, u32 val)
if (ret)
dev_err(pci->dev, "write DBI address failed\n");
}
EXPORT_SYMBOL_GPL(dw_pcie_write_dbi2);
static inline void __iomem *dw_pcie_select_atu(struct dw_pcie *pci, u32 dir,
u32 index)
@ -732,6 +733,53 @@ static void dw_pcie_link_set_max_speed(struct dw_pcie *pci, u32 link_gen)
}
static void dw_pcie_link_set_max_link_width(struct dw_pcie *pci, u32 num_lanes)
{
u32 lnkcap, lwsc, plc;
u8 cap;
if (!num_lanes)
return;
/* Set the number of lanes */
plc = dw_pcie_readl_dbi(pci, PCIE_PORT_LINK_CONTROL);
plc &= ~PORT_LINK_FAST_LINK_MODE;
plc &= ~PORT_LINK_MODE_MASK;
/* Set link width speed control register */
lwsc = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
lwsc &= ~PORT_LOGIC_LINK_WIDTH_MASK;
switch (num_lanes) {
case 1:
plc |= PORT_LINK_MODE_1_LANES;
lwsc |= PORT_LOGIC_LINK_WIDTH_1_LANES;
break;
case 2:
plc |= PORT_LINK_MODE_2_LANES;
lwsc |= PORT_LOGIC_LINK_WIDTH_2_LANES;
break;
case 4:
plc |= PORT_LINK_MODE_4_LANES;
lwsc |= PORT_LOGIC_LINK_WIDTH_4_LANES;
break;
case 8:
plc |= PORT_LINK_MODE_8_LANES;
lwsc |= PORT_LOGIC_LINK_WIDTH_8_LANES;
break;
default:
dev_err(pci->dev, "num-lanes %u: invalid value\n", num_lanes);
return;
}
dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, plc);
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, lwsc);
cap = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
lnkcap = dw_pcie_readl_dbi(pci, cap + PCI_EXP_LNKCAP);
lnkcap &= ~PCI_EXP_LNKCAP_MLW;
lnkcap |= FIELD_PREP(PCI_EXP_LNKCAP_MLW, num_lanes);
dw_pcie_writel_dbi(pci, cap + PCI_EXP_LNKCAP, lnkcap);
}
void dw_pcie_iatu_detect(struct dw_pcie *pci)
{
int max_region, ob, ib;
@ -840,8 +888,14 @@ static int dw_pcie_edma_find_chip(struct dw_pcie *pci)
* Indirect eDMA CSRs access has been completely removed since v5.40a
* thus no space is now reserved for the eDMA channels viewport and
* former DMA CTRL register is no longer fixed to FFs.
*
* Note that Renesas R-Car S4-8's PCIe controllers for unknown reason
* have zeros in the eDMA CTRL register even though the HW-manual
* explicitly states there must FFs if the unrolled mapping is enabled.
* For such cases the low-level drivers are supposed to manually
* activate the unrolled mapping to bypass the auto-detection procedure.
*/
if (dw_pcie_ver_is_ge(pci, 540A))
if (dw_pcie_ver_is_ge(pci, 540A) || dw_pcie_cap_is(pci, EDMA_UNROLL))
val = 0xFFFFFFFF;
else
val = dw_pcie_readl_dbi(pci, PCIE_DMA_VIEWPORT_BASE + PCIE_DMA_CTRL);
@ -1013,49 +1067,5 @@ void dw_pcie_setup(struct dw_pcie *pci)
val |= PORT_LINK_DLL_LINK_EN;
dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val);
if (!pci->num_lanes) {
dev_dbg(pci->dev, "Using h/w default number of lanes\n");
return;
}
/* Set the number of lanes */
val &= ~PORT_LINK_FAST_LINK_MODE;
val &= ~PORT_LINK_MODE_MASK;
switch (pci->num_lanes) {
case 1:
val |= PORT_LINK_MODE_1_LANES;
break;
case 2:
val |= PORT_LINK_MODE_2_LANES;
break;
case 4:
val |= PORT_LINK_MODE_4_LANES;
break;
case 8:
val |= PORT_LINK_MODE_8_LANES;
break;
default:
dev_err(pci->dev, "num-lanes %u: invalid value\n", pci->num_lanes);
return;
}
dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val);
/* Set link width speed control register */
val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
val &= ~PORT_LOGIC_LINK_WIDTH_MASK;
switch (pci->num_lanes) {
case 1:
val |= PORT_LOGIC_LINK_WIDTH_1_LANES;
break;
case 2:
val |= PORT_LOGIC_LINK_WIDTH_2_LANES;
break;
case 4:
val |= PORT_LOGIC_LINK_WIDTH_4_LANES;
break;
case 8:
val |= PORT_LOGIC_LINK_WIDTH_8_LANES;
break;
}
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
dw_pcie_link_set_max_link_width(pci, pci->num_lanes);
}

View File

@ -51,8 +51,9 @@
/* DWC PCIe controller capabilities */
#define DW_PCIE_CAP_REQ_RES 0
#define DW_PCIE_CAP_IATU_UNROLL 1
#define DW_PCIE_CAP_CDM_CHECK 2
#define DW_PCIE_CAP_EDMA_UNROLL 1
#define DW_PCIE_CAP_IATU_UNROLL 2
#define DW_PCIE_CAP_CDM_CHECK 3
#define dw_pcie_cap_is(_pci, _cap) \
test_bit(DW_PCIE_CAP_ ## _cap, &(_pci)->caps)
@ -330,7 +331,9 @@ struct dw_pcie_rp {
};
struct dw_pcie_ep_ops {
void (*pre_init)(struct dw_pcie_ep *ep);
void (*ep_init)(struct dw_pcie_ep *ep);
void (*deinit)(struct dw_pcie_ep *ep);
int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no,
enum pci_epc_irq_type type, u16 interrupt_num);
const struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep);
@ -342,6 +345,7 @@ struct dw_pcie_ep_ops {
* driver.
*/
unsigned int (*func_conf_select)(struct dw_pcie_ep *ep, u8 func_no);
unsigned int (*get_dbi2_offset)(struct dw_pcie_ep *ep, u8 func_no);
};
struct dw_pcie_ep_func {

View File

@ -0,0 +1,527 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* PCIe controller driver for Renesas R-Car Gen4 Series SoCs
* Copyright (C) 2022-2023 Renesas Electronics Corporation
*/
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include "../../pci.h"
#include "pcie-designware.h"
/* Renesas-specific */
/* PCIe Mode Setting Register 0 */
#define PCIEMSR0 0x0000
#define BIFUR_MOD_SET_ON BIT(0)
#define DEVICE_TYPE_EP 0
#define DEVICE_TYPE_RC BIT(4)
/* PCIe Interrupt Status 0 */
#define PCIEINTSTS0 0x0084
/* PCIe Interrupt Status 0 Enable */
#define PCIEINTSTS0EN 0x0310
#define MSI_CTRL_INT BIT(26)
#define SMLH_LINK_UP BIT(7)
#define RDLH_LINK_UP BIT(6)
/* PCIe DMA Interrupt Status Enable */
#define PCIEDMAINTSTSEN 0x0314
#define PCIEDMAINTSTSEN_INIT GENMASK(15, 0)
/* PCIe Reset Control Register 1 */
#define PCIERSTCTRL1 0x0014
#define APP_HOLD_PHY_RST BIT(16)
#define APP_LTSSM_ENABLE BIT(0)
#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
struct rcar_gen4_pcie {
struct dw_pcie dw;
void __iomem *base;
struct platform_device *pdev;
enum dw_pcie_device_mode mode;
};
#define to_rcar_gen4_pcie(_dw) container_of(_dw, struct rcar_gen4_pcie, dw)
/* Common */
static void rcar_gen4_pcie_ltssm_enable(struct rcar_gen4_pcie *rcar,
bool enable)
{
u32 val;
val = readl(rcar->base + PCIERSTCTRL1);
if (enable) {
val |= APP_LTSSM_ENABLE;
val &= ~APP_HOLD_PHY_RST;
} else {
/*
* Since the datasheet of R-Car doesn't mention how to assert
* the APP_HOLD_PHY_RST, don't assert it again. Otherwise,
* hang-up issue happened in the dw_edma_core_off() when
* the controller didn't detect a PCI device.
*/
val &= ~APP_LTSSM_ENABLE;
}
writel(val, rcar->base + PCIERSTCTRL1);
}
static int rcar_gen4_pcie_link_up(struct dw_pcie *dw)
{
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
u32 val, mask;
val = readl(rcar->base + PCIEINTSTS0);
mask = RDLH_LINK_UP | SMLH_LINK_UP;
return (val & mask) == mask;
}
/*
* Manually initiate the speed change. Return 0 if change succeeded; otherwise
* -ETIMEDOUT.
*/
static int rcar_gen4_pcie_speed_change(struct dw_pcie *dw)
{
u32 val;
int i;
val = dw_pcie_readl_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL);
val &= ~PORT_LOGIC_SPEED_CHANGE;
dw_pcie_writel_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
val = dw_pcie_readl_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL);
val |= PORT_LOGIC_SPEED_CHANGE;
dw_pcie_writel_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
for (i = 0; i < RCAR_NUM_SPEED_CHANGE_RETRIES; i++) {
val = dw_pcie_readl_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL);
if (!(val & PORT_LOGIC_SPEED_CHANGE))
return 0;
usleep_range(10000, 11000);
}
return -ETIMEDOUT;
}
/*
* Enable LTSSM of this controller and manually initiate the speed change.
* Always return 0.
*/
static int rcar_gen4_pcie_start_link(struct dw_pcie *dw)
{
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
int i, changes;
rcar_gen4_pcie_ltssm_enable(rcar, true);
/*
* Require direct speed change with retrying here if the link_gen is
* PCIe Gen2 or higher.
*/
changes = min_not_zero(dw->link_gen, RCAR_MAX_LINK_SPEED) - 1;
/*
* Since dw_pcie_setup_rc() sets it once, PCIe Gen2 will be trained.
* So, this needs remaining times for up to PCIe Gen4 if RC mode.
*/
if (changes && rcar->mode == DW_PCIE_RC_TYPE)
changes--;
for (i = 0; i < changes; i++) {
/* It may not be connected in EP mode yet. So, break the loop */
if (rcar_gen4_pcie_speed_change(dw))
break;
}
return 0;
}
static void rcar_gen4_pcie_stop_link(struct dw_pcie *dw)
{
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
rcar_gen4_pcie_ltssm_enable(rcar, false);
}
static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)
{
struct dw_pcie *dw = &rcar->dw;
u32 val;
int ret;
ret = clk_bulk_prepare_enable(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
if (ret) {
dev_err(dw->dev, "Enabling core clocks failed\n");
return ret;
}
if (!reset_control_status(dw->core_rsts[DW_PCIE_PWR_RST].rstc))
reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
val = readl(rcar->base + PCIEMSR0);
if (rcar->mode == DW_PCIE_RC_TYPE) {
val |= DEVICE_TYPE_RC;
} else if (rcar->mode == DW_PCIE_EP_TYPE) {
val |= DEVICE_TYPE_EP;
} else {
ret = -EINVAL;
goto err_unprepare;
}
if (dw->num_lanes < 4)
val |= BIFUR_MOD_SET_ON;
writel(val, rcar->base + PCIEMSR0);
ret = reset_control_deassert(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
if (ret)
goto err_unprepare;
return 0;
err_unprepare:
clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
return ret;
}
static void rcar_gen4_pcie_common_deinit(struct rcar_gen4_pcie *rcar)
{
struct dw_pcie *dw = &rcar->dw;
reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
}
static int rcar_gen4_pcie_prepare(struct rcar_gen4_pcie *rcar)
{
struct device *dev = rcar->dw.dev;
int err;
pm_runtime_enable(dev);
err = pm_runtime_resume_and_get(dev);
if (err < 0) {
dev_err(dev, "Runtime resume failed\n");
pm_runtime_disable(dev);
}
return err;
}
static void rcar_gen4_pcie_unprepare(struct rcar_gen4_pcie *rcar)
{
struct device *dev = rcar->dw.dev;
pm_runtime_put(dev);
pm_runtime_disable(dev);
}
static int rcar_gen4_pcie_get_resources(struct rcar_gen4_pcie *rcar)
{
/* Renesas-specific registers */
rcar->base = devm_platform_ioremap_resource_byname(rcar->pdev, "app");
return PTR_ERR_OR_ZERO(rcar->base);
}
static const struct dw_pcie_ops dw_pcie_ops = {
.start_link = rcar_gen4_pcie_start_link,
.stop_link = rcar_gen4_pcie_stop_link,
.link_up = rcar_gen4_pcie_link_up,
};
static struct rcar_gen4_pcie *rcar_gen4_pcie_alloc(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rcar_gen4_pcie *rcar;
rcar = devm_kzalloc(dev, sizeof(*rcar), GFP_KERNEL);
if (!rcar)
return ERR_PTR(-ENOMEM);
rcar->dw.ops = &dw_pcie_ops;
rcar->dw.dev = dev;
rcar->pdev = pdev;
dw_pcie_cap_set(&rcar->dw, EDMA_UNROLL);
dw_pcie_cap_set(&rcar->dw, REQ_RES);
platform_set_drvdata(pdev, rcar);
return rcar;
}
/* Host mode */
static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
{
struct dw_pcie *dw = to_dw_pcie_from_pp(pp);
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
int ret;
u32 val;
gpiod_set_value_cansleep(dw->pe_rst, 1);
ret = rcar_gen4_pcie_common_init(rcar);
if (ret)
return ret;
/*
* According to the section 3.5.7.2 "RC Mode" in DWC PCIe Dual Mode
* Rev.5.20a and 3.5.6.1 "RC mode" in DWC PCIe RC databook v5.20a, we
* should disable two BARs to avoid unnecessary memory assignment
* during device enumeration.
*/
dw_pcie_writel_dbi2(dw, PCI_BASE_ADDRESS_0, 0x0);
dw_pcie_writel_dbi2(dw, PCI_BASE_ADDRESS_1, 0x0);
/* Enable MSI interrupt signal */
val = readl(rcar->base + PCIEINTSTS0EN);
val |= MSI_CTRL_INT;
writel(val, rcar->base + PCIEINTSTS0EN);
msleep(PCIE_T_PVPERL_MS); /* pe_rst requires 100msec delay */
gpiod_set_value_cansleep(dw->pe_rst, 0);
return 0;
}
static void rcar_gen4_pcie_host_deinit(struct dw_pcie_rp *pp)
{
struct dw_pcie *dw = to_dw_pcie_from_pp(pp);
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
gpiod_set_value_cansleep(dw->pe_rst, 1);
rcar_gen4_pcie_common_deinit(rcar);
}
static const struct dw_pcie_host_ops rcar_gen4_pcie_host_ops = {
.host_init = rcar_gen4_pcie_host_init,
.host_deinit = rcar_gen4_pcie_host_deinit,
};
static int rcar_gen4_add_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
{
struct dw_pcie_rp *pp = &rcar->dw.pp;
if (!IS_ENABLED(CONFIG_PCIE_RCAR_GEN4_HOST))
return -ENODEV;
pp->num_vectors = MAX_MSI_IRQS;
pp->ops = &rcar_gen4_pcie_host_ops;
return dw_pcie_host_init(pp);
}
static void rcar_gen4_remove_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
{
dw_pcie_host_deinit(&rcar->dw.pp);
}
/* Endpoint mode */
static void rcar_gen4_pcie_ep_pre_init(struct dw_pcie_ep *ep)
{
struct dw_pcie *dw = to_dw_pcie_from_ep(ep);
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
int ret;
ret = rcar_gen4_pcie_common_init(rcar);
if (ret)
return;
writel(PCIEDMAINTSTSEN_INIT, rcar->base + PCIEDMAINTSTSEN);
}
static void rcar_gen4_pcie_ep_init(struct dw_pcie_ep *ep)
{
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
enum pci_barno bar;
for (bar = 0; bar < PCI_STD_NUM_BARS; bar++)
dw_pcie_ep_reset_bar(pci, bar);
}
static void rcar_gen4_pcie_ep_deinit(struct dw_pcie_ep *ep)
{
struct dw_pcie *dw = to_dw_pcie_from_ep(ep);
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
writel(0, rcar->base + PCIEDMAINTSTSEN);
rcar_gen4_pcie_common_deinit(rcar);
}
static int rcar_gen4_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
enum pci_epc_irq_type type,
u16 interrupt_num)
{
struct dw_pcie *dw = to_dw_pcie_from_ep(ep);
switch (type) {
case PCI_EPC_IRQ_LEGACY:
return dw_pcie_ep_raise_legacy_irq(ep, func_no);
case PCI_EPC_IRQ_MSI:
return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
default:
dev_err(dw->dev, "Unknown IRQ type\n");
return -EINVAL;
}
return 0;
}
static const struct pci_epc_features rcar_gen4_pcie_epc_features = {
.linkup_notifier = false,
.msi_capable = true,
.msix_capable = false,
.reserved_bar = 1 << BAR_1 | 1 << BAR_3 | 1 << BAR_5,
.align = SZ_1M,
};
static const struct pci_epc_features*
rcar_gen4_pcie_ep_get_features(struct dw_pcie_ep *ep)
{
return &rcar_gen4_pcie_epc_features;
}
static unsigned int rcar_gen4_pcie_ep_func_conf_select(struct dw_pcie_ep *ep,
u8 func_no)
{
return func_no * RCAR_GEN4_PCIE_EP_FUNC_DBI_OFFSET;
}
static unsigned int rcar_gen4_pcie_ep_get_dbi2_offset(struct dw_pcie_ep *ep,
u8 func_no)
{
return func_no * RCAR_GEN4_PCIE_EP_FUNC_DBI2_OFFSET;
}
static const struct dw_pcie_ep_ops pcie_ep_ops = {
.pre_init = rcar_gen4_pcie_ep_pre_init,
.ep_init = rcar_gen4_pcie_ep_init,
.deinit = rcar_gen4_pcie_ep_deinit,
.raise_irq = rcar_gen4_pcie_ep_raise_irq,
.get_features = rcar_gen4_pcie_ep_get_features,
.func_conf_select = rcar_gen4_pcie_ep_func_conf_select,
.get_dbi2_offset = rcar_gen4_pcie_ep_get_dbi2_offset,
};
static int rcar_gen4_add_dw_pcie_ep(struct rcar_gen4_pcie *rcar)
{
struct dw_pcie_ep *ep = &rcar->dw.ep;
if (!IS_ENABLED(CONFIG_PCIE_RCAR_GEN4_EP))
return -ENODEV;
ep->ops = &pcie_ep_ops;
return dw_pcie_ep_init(ep);
}
static void rcar_gen4_remove_dw_pcie_ep(struct rcar_gen4_pcie *rcar)
{
dw_pcie_ep_exit(&rcar->dw.ep);
}
/* Common */
static int rcar_gen4_add_dw_pcie(struct rcar_gen4_pcie *rcar)
{
rcar->mode = (enum dw_pcie_device_mode)of_device_get_match_data(&rcar->pdev->dev);
switch (rcar->mode) {
case DW_PCIE_RC_TYPE:
return rcar_gen4_add_dw_pcie_rp(rcar);
case DW_PCIE_EP_TYPE:
return rcar_gen4_add_dw_pcie_ep(rcar);
default:
return -EINVAL;
}
}
static int rcar_gen4_pcie_probe(struct platform_device *pdev)
{
struct rcar_gen4_pcie *rcar;
int err;
rcar = rcar_gen4_pcie_alloc(pdev);
if (IS_ERR(rcar))
return PTR_ERR(rcar);
err = rcar_gen4_pcie_get_resources(rcar);
if (err)
return err;
err = rcar_gen4_pcie_prepare(rcar);
if (err)
return err;
err = rcar_gen4_add_dw_pcie(rcar);
if (err)
goto err_unprepare;
return 0;
err_unprepare:
rcar_gen4_pcie_unprepare(rcar);
return err;
}
static void rcar_gen4_remove_dw_pcie(struct rcar_gen4_pcie *rcar)
{
switch (rcar->mode) {
case DW_PCIE_RC_TYPE:
rcar_gen4_remove_dw_pcie_rp(rcar);
break;
case DW_PCIE_EP_TYPE:
rcar_gen4_remove_dw_pcie_ep(rcar);
break;
default:
break;
}
}
static void rcar_gen4_pcie_remove(struct platform_device *pdev)
{
struct rcar_gen4_pcie *rcar = platform_get_drvdata(pdev);
rcar_gen4_remove_dw_pcie(rcar);
rcar_gen4_pcie_unprepare(rcar);
}
static const struct of_device_id rcar_gen4_pcie_of_match[] = {
{
.compatible = "renesas,rcar-gen4-pcie",
.data = (void *)DW_PCIE_RC_TYPE,
},
{
.compatible = "renesas,rcar-gen4-pcie-ep",
.data = (void *)DW_PCIE_EP_TYPE,
},
{},
};
MODULE_DEVICE_TABLE(of, rcar_gen4_pcie_of_match);
static struct platform_driver rcar_gen4_pcie_driver = {
.driver = {
.name = "pcie-rcar-gen4",
.of_match_table = rcar_gen4_pcie_of_match,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.probe = rcar_gen4_pcie_probe,
.remove_new = rcar_gen4_pcie_remove,
};
module_platform_driver(rcar_gen4_pcie_driver);
MODULE_DESCRIPTION("Renesas R-Car Gen4 PCIe controller driver");
MODULE_LICENSE("GPL");

View File

@ -917,12 +917,6 @@ static int tegra_pcie_dw_host_init(struct dw_pcie_rp *pp)
AMBA_ERROR_RESPONSE_CRS_SHIFT);
dw_pcie_writel_dbi(pci, PORT_LOGIC_AMBA_ERROR_RESPONSE_DEFAULT, val);
/* Configure Max lane width from DT */
val = dw_pcie_readl_dbi(pci, pcie->pcie_cap_base + PCI_EXP_LNKCAP);
val &= ~PCI_EXP_LNKCAP_MLW;
val |= (pcie->num_lanes << PCI_EXP_LNKSTA_NLW_SHIFT);
dw_pcie_writel_dbi(pci, pcie->pcie_cap_base + PCI_EXP_LNKCAP, val);
/* Clear Slot Clock Configuration bit if SRNS configuration */
if (pcie->enable_srns) {
val_16 = dw_pcie_readw_dbi(pci, pcie->pcie_cap_base +

View File

@ -13,6 +13,9 @@
#define PCIE_LINK_RETRAIN_TIMEOUT_MS 1000
/* Power stable to PERST# inactive from PCIe card Electromechanical Spec */
#define PCIE_T_PVPERL_MS 100
/*
* PCIe r6.0, sec 5.3.3.2.1 <PME Synchronization>
* Recommends 1ms to 10ms timeout to check L2 ready.