linux-pci.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 00/17] PCI: dwc: Add dma-ranges/YAML-schema/Baikal-T1 support
@ 2022-05-03 21:46 Serge Semin
  2022-05-03 21:46 ` [PATCH v2 01/17] dt-bindings: PCI: dwc: Define common and native DT bindings Serge Semin
                   ` (16 more replies)
  0 siblings, 17 replies; 49+ messages in thread
From: Serge Semin @ 2022-05-03 21:46 UTC (permalink / raw)
  To: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Pavel Parkhomenko,
	Rob Herring, Krzysztof Wilczyński, Frank Li,
	Manivannan Sadhasivam, Rob Herring, linux-pci, devicetree,
	linux-kernel

This patchset is a third one in the series created in the framework of
my Baikal-T1 PCIe/eDMA-related work:

[1: In-progress v3] clk: Baikal-T1 DDR/PCIe resets and some xGMAC fixes
Link: https://lore.kernel.org/linux-pci/20220503205722.24755-1-Sergey.Semin@baikalelectronics.ru/
[2: In-progress v2] PCI: dwc: Various fixes and cleanups
Link: https://lore.kernel.org/linux-pci/20220503212300.30105-1-Sergey.Semin@baikalelectronics.ru/
[3: In-progress v2] PCI: dwc: Add dma-ranges/YAML-schema/Baikal-T1 support
Link: --you are looking at it--
[4: In-progress v1] dmaengine: dw-edma: Add RP/EP local DMA controllers support
Link: https://lore.kernel.org/linux-pci/20220324014836.19149-1-Sergey.Semin@baikalelectronics.ru/

Note it is very recommended to merge the patchsets in the same order as
they are listed in the set above in order to have them applied smoothly.
Nothing prevents them from being reviewed synchronously though.

This series is about adding new features to the DW PCIe Host/End-point
driver. First of all we start from splitting up the DT-bindings into
common properties schema and generic DW PCIe bindings definition. It's
done to support the generic DW PCIe Host/End-point available platforms
with adding a common YAML-schema to be reused by the platform-specific DW
PCIe bindings. @Rob could you please take a look at that patch? I've got a
problem with dt_bindings_check-ing the schema which is likely caused by
the dt-schema parser misbehaviour. After that we suggest to add a more
verbose link-up log message. Really printing link generation and width
would be much more informative than just "link up". Then a series of
IP-core version-related patches go, like using a native FourCC version
representation, adding the IP-core auto-detection, adding better
structured IP-core version/type interface. After that the
platform-specific host de-initialization method is introduced. A series of
iATU optimizations, cleanups and new features goes afterwards. In
particular we suggest to drop some redundant enumerations, add iATU
regions size detection procedure and then use the regions parameters to
verify the requested by the platform iATU ranges/dma-ranges settings.
After that the dma-ranges property support is added for the DW PCIe Host
controllers. Then a structured set of the DW PCIe RP/EP specific clocks
and resets names/IDs is introduced so to be re-used by the generic and new
platforms. Note it is fully coherent with the DW PCIe controller manuals
(see the patch log for details). Also note the patch doesn't affect the
already available DW PCIe platform-specific code since it would be too
risky for my to do the corresponding conversion, but the maintainers are
welcome to do that. Finally at the series closure we introduce the
Baikal-T1 PCIe interface support, which uses all the recently added
features including the set of the generic clocks and resets names.

Link: https://lore.kernel.org/linux-pci/20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru/
Changelog v2:
- Test the error condition first and return straight away if it comes true
  in the link up waiting and link state logging method (@Joe).
- Move the dw_pcie_region_type enumeration removal patch to being applied
  before the IB/OB iATU windows setup simplification patch (@Rob).
- Rename 'syscon' property to 'baikal,bt1-syscon'. (@Rob)
- Move the iATU region selection procedure into a helper function (@Rob).
- Rebase from kernel v5.17 onto v5.18-rc3 since the later kernel has
  already DT bindings converted. (@Rob)
- Simplify the iATU region selection procedure by recalculating the base
  address only if the space is unrolled. The iATU viewport base address
  will be saved in the pci->atu_base field.
- Move the IP-core version detection procedure call from
  dw_pcie_ep_init_complete() to dw_pcie_ep_init().
- Add a new patch: "PCI: dwc: Detect iATU settings after getting
  "addr_space" resource."
- Use 'definitions' property instead of the '$defs' one. It fixes the
  dt-validate error: 'X is not of type array.'
- Drop 'interrupts' and 'interrupt-names' property from being required
  for the native DW PCIe host.
- Evaluate the 'snps,dw-pcie-common.yaml' schema in the
  'socionext,uniphier-pcie-ep.yaml' DT-bindings since the later has
  platform-specific names defined.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Pavel Parkhomenko <Pavel.Parkhomenko@baikalelectronics.ru>
Cc: Rob Herring <robh@kernel.org>
Cc: "Krzysztof Wilczyński" <kw@linux.com>
Cc: Frank Li <Frank.Li@nxp.com>
Cc: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: linux-pci@vger.kernel.org
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org

Serge Semin (17):
  dt-bindings: PCI: dwc: Define common and native DT bindings
  dt-bindings: PCI: dwc: Add Baikal-T1 PCIe Root Port bindings
  PCI: dwc: Add more verbose link-up message
  PCI: dwc: Detect iATU settings after getting "addr_space" resource
  PCI: dwc: Convert to using native IP-core versions representation
  PCI: dwc: Add IP-core version detection procedure
  PCI: dwc: Introduce Synopsys IP-core versions/types interface
  PCI: dwc: Add host de-initialization callback
  PCI: dwc: Drop inbound iATU types enumeration - dw_pcie_as_type
  PCI: dwc: Drop iATU regions enumeration - dw_pcie_region_type
  PCI: dwc: Simplify in/outbound iATU setup methods
  PCI: dwc: Add iATU regions size detection procedure
  PCI: dwc: Verify in/out regions against iATU constraints
  PCI: dwc: Check iATU in/outbound ranges setup methods status
  PCI: dwc: Introduce dma-ranges property support for RC-host
  PCI: dwc: Introduce generic platform clocks and resets sets
  PCI: dwc: Add Baikal-T1 PCIe controller support

 .../bindings/pci/baikal,bt1-pcie.yaml         | 158 +++++
 .../bindings/pci/fsl,imx6q-pcie.yaml          |   5 +-
 .../bindings/pci/hisilicon,kirin-pcie.yaml    |   4 +-
 .../bindings/pci/sifive,fu740-pcie.yaml       |   4 +-
 .../bindings/pci/snps,dw-pcie-common.yaml     | 306 +++++++++
 .../bindings/pci/snps,dw-pcie-ep.yaml         | 143 ++--
 .../devicetree/bindings/pci/snps,dw-pcie.yaml | 189 ++++--
 .../pci/socionext,uniphier-pcie-ep.yaml       |   2 +-
 .../bindings/pci/toshiba,visconti-pcie.yaml   |   2 +-
 drivers/pci/controller/dwc/Kconfig            |   9 +
 drivers/pci/controller/dwc/Makefile           |   1 +
 drivers/pci/controller/dwc/pci-keystone.c     |  12 +-
 drivers/pci/controller/dwc/pcie-bt1.c         | 639 ++++++++++++++++++
 .../pci/controller/dwc/pcie-designware-ep.c   |  40 +-
 .../pci/controller/dwc/pcie-designware-host.c | 198 ++++--
 drivers/pci/controller/dwc/pcie-designware.c  | 463 ++++++-------
 drivers/pci/controller/dwc/pcie-designware.h  | 202 ++++--
 drivers/pci/controller/dwc/pcie-intel-gw.c    |  10 +-
 drivers/pci/controller/dwc/pcie-tegra194.c    |   2 +-
 19 files changed, 1877 insertions(+), 512 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/pci/baikal,bt1-pcie.yaml
 create mode 100644 Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml
 create mode 100644 drivers/pci/controller/dwc/pcie-bt1.c

-- 
2.35.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v2 01/17] dt-bindings: PCI: dwc: Define common and native DT bindings
  2022-05-03 21:46 [PATCH v2 00/17] PCI: dwc: Add dma-ranges/YAML-schema/Baikal-T1 support Serge Semin
@ 2022-05-03 21:46 ` Serge Semin
  2022-05-16 20:11   ` Rob Herring
  2022-05-03 21:46 ` [PATCH v2 02/17] dt-bindings: PCI: dwc: Add Baikal-T1 PCIe Root Port bindings Serge Semin
                   ` (15 subsequent siblings)
  16 siblings, 1 reply; 49+ messages in thread
From: Serge Semin @ 2022-05-03 21:46 UTC (permalink / raw)
  To: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Richard Zhu, Lucas Stach, Rob Herring, Krzysztof Kozlowski,
	Shawn Guo, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	NXP Linux Team, Xiaowei Song, Binghui Wang, Paul Walmsley,
	Greentime Hu, Palmer Dabbelt, Kunihiko Hayashi, Masami Hiramatsu,
	Nobuhiro Iwamatsu
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Pavel Parkhomenko,
	Rob Herring, Krzysztof Wilczyński, Frank Li,
	Manivannan Sadhasivam, linux-pci, devicetree, linux-kernel,
	linux-arm-kernel, linux-riscv

Currently both DW PCIe Root Port and End-point DT bindings are too generic
to be used as a descriptive set the device properties. Yes, it's very handy
to have them that way so the DT-schemas could be used to evaluate as many
DW PCIe-related DT-nodes as possible. But at the same time they don't
provide well defined DW PCIe RP/EP DT interface description thus leaving
too much flexibility for the new platforms, no encouraging the developers
to preserve a compatible interface. It causes having many
platform-specific DT bindings of the same generic properties.

Instead of currently implemented approach we suggest to be more
restrictive and yet preserve some level of flexibility in the DW PCIe
DT-bindings description. The device tree DT-schema is split up into
three parts: a common YAML-schema applicable for both DWC Root Port and
End-point controller configs, DWC PCIe Root Port-specific YAML-schema
and DWC PCIe End-point-specific YAML-schema, where
1) pci/snps,dw-pcie-common.yaml - the common DT-schema describes the most
generic constraints of the "reg", "interrupts", "clocks", "resets" and
"phys" properties together with a set of common for both device types
PCIe/AXI bus properties like a maximum number of lanes or a maximum link
speed, number of inbound and outbound iATU windows. In addition to that a
set of schema definitions declared under the "definitions" property with
"reg", "interrupt", "clock" and "reset" names common for DWC PCIe Root
Port and End-point devices. They can be used by the successive DT-schemas
in case they are supposed to be compatible with the generic DWC PCIe
controller DT-bindings.
2) pci/snps,dw-pcie.yaml, pci/snps,dw-pcie-ep.yaml - generic DW PCIe Root
Port and End-point DT-bindings which aside with the device-specific
properties set also contain more restrictive constraints. All new DW PCIe
platforms are supposed to be compatible with one of these bindings by
using "allOf: " property and additionally defining their own constraints
to close up the DT-bindings set.

So to speak in case if a DW PCIe-based device for some reason has too many
specific properties or it's bindings have already been defined in a
non-generic way, it's DT-schema is supposed to include 1) YAML-file and
provide its own constraints. Otherwise the ready-to-use bindings from 2)
should be utilized. There are only two DT-schemas compatible with 2) at the
moment are samsung,axynos-pcie.yaml and intel-gw-pcie.yaml. The
rest of the DW PCIe-related DT-schemas are supposed to use more generic
DW PCIe DT-bindings - pci/snps,dw-pcie-common.yaml.

Note the provided here generic properties and their possible values are
derived from the DW PCIe RC/EP hardware manuals and from the interface
implemented in the driver. The DT-bindings schemas are created to be as
full as possible with detailed properties/names description for the
easier interface comprehension and new platforms bindings development.

Also note since there are no generic DT-nodes can be found in the kernel
dts-es which would have a pure "snps,dw-pcie" compatible string, these
DT-bindings have been created to not be selected by default for
evaluation. They are supposed to be used by the new vendor-specific
DT-schemas to at least stop adding new bindings for the same set of DWC
PCIe signals or properties.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>

---

If the '$defs' property is used instead of the 'definitions' one, the next
error will be spotted:

>  DTC     Documentation/devicetree/bindings/pci/snps,dw-pcie.example.dt.yaml
>  CHECK   Documentation/devicetree/bindings/pci/snps,dw-pcie.example.dt.yaml
> /.../snps,dw-pcie.example.dt.yaml: pcie@1f052000: reg-names:0: 'oneOf' conditional failed, one must be fixed:
>        'dbi' is not of type 'array'
>        From schema: /.../snps,dw-pcie.yaml
> /.../snps,dw-pcie.example.dt.yaml: pcie@1f052000: reg-names:1: 'oneOf' conditional failed, one must be fixed:
>         'config' is not of type 'array'
>         From schema: /.../snps,dw-pcie.yaml
> /.../snps,dw-pcie.example.dt.yaml: pcie@1f052000: interrupt-names:0: 'anyOf' conditional failed, one must be fixed:
>         /.../snps,dw-pcie.example.dt.yaml: pcie@1f052000: interrupt-names:0: 'oneOf' conditional failed, one must be fixed:
>                 'msi' is not of type 'array'
>         /../snps,dw-pcie.example.dt.yaml: pcie@1f052000: interrupt-names:0: 'oneOf' conditional failed, one must be fixed:
>                 'msi' is not of type 'array'
>         From schema: /.../snps,dw-pcie.yaml

The problem is caused by the 'def fixup_sub_schema()' method defined in
the dtschema parser. AFAIU It skips the sub-'$defs'-schemas evaluation thus
not having them fixed up properly. The next patch solves the denoted
problem:
--- a/lib.py     2022-04-23 19:51:38.829759258 +0300
+++ b/lib.py     2022-04-23 20:17:16.218137170 +0300
@@ -470,7 +470,7 @@
             for subschema in v:
                 fixup_sub_schema(subschema, True)

-        if k not in ['dependentRequired', 'dependentSchemas', 'dependencies', 'properties', 'patternProperties', '$defs']:
+        if k not in ['dependentRequired', 'dependentSchemas', 'dependencies', 'properties', 'patternProperties']:
             continue

         for prop in v:

The same effect can be reached just by using the 'definitions' property.
Since I don't know the original reasons of having the fixup skipped for
the '$defs' property, I decided to just take the later path and use the
'definitions' property in the DT schemas. Rob, it's up to you to decide
whether the dtschema parser needs to be fixed.

Changelog v2:
- Use 'definitions' property instead of the '$defs' one. It fixes the
  dt-validate error: 'X is not of type array.'
- Drop 'interrupts' and 'interrupt-names' property from being required for
  the native DW PCIe host.
- Evaluate the 'snps,dw-pcie-common.yaml' schema in the
  'socionext,uniphier-pcie-ep.yaml' DT-bindings since the later has
  platform-specific names defined.
---
 .../bindings/pci/fsl,imx6q-pcie.yaml          |   5 +-
 .../bindings/pci/hisilicon,kirin-pcie.yaml    |   4 +-
 .../bindings/pci/sifive,fu740-pcie.yaml       |   4 +-
 .../bindings/pci/snps,dw-pcie-common.yaml     | 306 ++++++++++++++++++
 .../bindings/pci/snps,dw-pcie-ep.yaml         | 143 +++++---
 .../devicetree/bindings/pci/snps,dw-pcie.yaml | 189 +++++++----
 .../pci/socionext,uniphier-pcie-ep.yaml       |   2 +-
 .../bindings/pci/toshiba,visconti-pcie.yaml   |   2 +-
 8 files changed, 532 insertions(+), 123 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml

diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml
index 252e5b72aee0..541fb0b724ef 100644
--- a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml
+++ b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml
@@ -12,10 +12,11 @@ maintainers:
 
 description: |+
   This PCIe host controller is based on the Synopsys DesignWare PCIe IP
-  and thus inherits all the common properties defined in snps,dw-pcie.yaml.
+  and thus inherits all the common properties defined in
+  snps,dw-pcie-common.yaml.
 
 allOf:
-  - $ref: /schemas/pci/snps,dw-pcie.yaml#
+  - $ref: /schemas/pci/snps,dw-pcie-common.yaml#
 
 properties:
   compatible:
diff --git a/Documentation/devicetree/bindings/pci/hisilicon,kirin-pcie.yaml b/Documentation/devicetree/bindings/pci/hisilicon,kirin-pcie.yaml
index c9f04999c9cf..879d9ec41cb7 100644
--- a/Documentation/devicetree/bindings/pci/hisilicon,kirin-pcie.yaml
+++ b/Documentation/devicetree/bindings/pci/hisilicon,kirin-pcie.yaml
@@ -14,10 +14,10 @@ description: |
   Kirin PCIe host controller is based on the Synopsys DesignWare PCI core.
   It shares common functions with the PCIe DesignWare core driver and
   inherits common properties defined in
-  Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml.
+  Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml.
 
 allOf:
-  - $ref: /schemas/pci/snps,dw-pcie.yaml#
+  - $ref: /schemas/pci/snps,dw-pcie-common.yaml#
 
 properties:
   compatible:
diff --git a/Documentation/devicetree/bindings/pci/sifive,fu740-pcie.yaml b/Documentation/devicetree/bindings/pci/sifive,fu740-pcie.yaml
index 195e6afeb169..61b03f363196 100644
--- a/Documentation/devicetree/bindings/pci/sifive,fu740-pcie.yaml
+++ b/Documentation/devicetree/bindings/pci/sifive,fu740-pcie.yaml
@@ -10,14 +10,14 @@ description: |+
   SiFive FU740 PCIe host controller is based on the Synopsys DesignWare
   PCI core. It shares common features with the PCIe DesignWare core and
   inherits common properties defined in
-  Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml.
+  Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml.
 
 maintainers:
   - Paul Walmsley <paul.walmsley@sifive.com>
   - Greentime Hu <greentime.hu@sifive.com>
 
 allOf:
-  - $ref: /schemas/pci/snps,dw-pcie.yaml#
+  - $ref: /schemas/pci/snps,dw-pcie-common.yaml#
 
 properties:
   compatible:
diff --git a/Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml b/Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml
new file mode 100644
index 000000000000..6131ac737294
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml
@@ -0,0 +1,306 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/snps,dw-pcie-common.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Synopsys DWC PCIe RP/EP controller
+
+maintainers:
+  - Jingoo Han <jingoohan1@gmail.com>
+  - Gustavo Pimentel <gustavo.pimentel@synopsys.com>
+
+properties:
+  reg:
+    description:
+      DWC PCIe CSR space is normally accessed over the dedicated Data Bus
+      Interface - DBI. In accordance with the reference manual the register
+      configuration space belongs to the Configuration-Dependent Module (CDM)
+      and is split up into several sub-parts Standard PCIe configuration
+      space, Port Logic Registers (PL), Shadow Config-space Registers,
+      iATU/eDMA registers. The particular sub-space is selected by the
+      CDM/ELBI (dbi_cs) and CS2 (dbi_cs2) signals (selector bits). Such
+      configuration provides a flexible interface for the system engineers to
+      either map the particular space at a desired MMIO address or just leave
+      them in a contiguous memory space if pure Native or AXI Bridge DBI access
+      is selected. Note the PCIe CFG-space, PL and Shadow registers are
+      specific to 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
+
+  interrupts:
+    description:
+      There are two main sub-blocks which are normally capable of
+      generating interrupts. It's System Information Interface and MSI
+      interface. While the former one has some common for the Host and
+      End-point controllers IRQ-signals, the later interface is obviously
+      Root Complex specific since it's responsible for the incoming MSI
+      messages signalling. The System Information IRQ signals are mainly
+      responsible for reporting the generic PCIe hierarchy and Root
+      Complex events like VPD IO request, general AER, PME, Hot-plug, link
+      bandwidth change, link equalization request, INTx asserted/deasserted
+      Message detection, embedded DMA Tx/Rx/Error.
+    minItems: 1
+    maxItems: 26
+
+  clocks:
+    description:
+      DWC PCIe reference manual explicitly defines a set of the clocks required
+      to get the controller working correctly. In general all of them can
+      be divided into two groups':' application and core clocks. Note the
+      platforms may have some of the clock sources unspecified in case if the
+      corresponding domains are fed up from a common clock source.
+    minItems: 1
+    maxItems: 7
+
+  resets:
+    description:
+      DWC PCIe reference manual explicitly defines a set of the reset
+      signals required to be de-asserted to properly activate the controller
+      sub-parts. All of these signals can be divided into two sub-groups':'
+      application and core resets with respect to the main sub-domains they
+      are supposed to reset. Note the platforms may have some of these signals
+      unspecified in case if they are automatically handled or aggregated into
+      a comprehensive control module.
+    minItems: 1
+    maxItems: 10
+
+  reset-gpio:
+    deprecated: true
+    description:
+      Reference to the GPIO-controlled PERST# signal. It is used to reset all
+      the peripheral devices available on the PCIe bus.
+    maxItems: 1
+
+  reset-gpios:
+    description:
+      Reference to the GPIO-controlled PERST# signal. It is used to reset all
+      the peripheral devices available on the PCIe bus.
+    maxItems: 1
+
+  phys:
+    description:
+      There can be up to the number of possible lanes PHYs specified.
+      Obviously each specified PHY is supposed to be able to work in the
+      PCIe mode with a speed implied by the DWC PCIe controller it is
+      attached to.
+    minItems: 1
+    maxItems: 16
+
+  phy-names:
+    minItems: 1
+    maxItems: 16
+    items:
+      pattern: '^pcie([0-9]+|-?phy)?$'
+
+  num-lanes:
+    maximum: 16
+
+  max-link-speed:
+    maximum: 4
+
+  num-ob-windows:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    deprecated: true
+    description:
+      Number of outbound address translation windows. This parameter can be
+      auto-detected based on the iATU memory writability. So there is no
+      point in having a dedicated DT-property for it.
+    maximum: 256
+
+  num-ib-windows:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    deprecated: true
+    description:
+      Number of inbound address translation windows. In the same way as
+      for the outbound AT windows, this parameter can be auto-detected based
+      on the iATU memory writability. There is no point having a dedicated
+      DT-property for it either.
+    maximum: 256
+
+  num-viewport:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    deprecated: true
+    description:
+      Number of outbound view ports configured in hardware. It's the same as
+      the number of outbound AT windows.
+    maximum: 256
+
+  snps,enable-cdm-check:
+    $ref: /schemas/types.yaml#/definitions/flag
+    description:
+      Enable automatic checking of CDM (Configuration Dependent Module)
+      registers for data corruption. CDM registers include standard PCIe
+      configuration space registers, Port Logic registers, DMA and iATU
+      registers. This feature has been available since DWC PCIe v4.80a.
+
+additionalProperties: true
+
+definitions:
+  reg-names:
+    description:
+      CSR space names common for the DWC PCIe Root Port and End-point
+      controllers.
+    oneOf:
+      - description:
+          Basic DWC PCIe controller configuration-space accessible over
+          the DBI interface. This memory space is either activated with
+          CDM/ELBI = 0 and CS2 = 0 or is a contiguous memory region
+          with all spaces. Note iATU/eDMA CSRs are indirectly accessible
+          via the PL viewports on the DWC PCIe controllers older than
+          v4.80a.
+        const: dbi
+      - description:
+          Shadow DWC PCIe config-space registers. This space is selected
+          by setting CDM/ELBI = 0 and CS2 = 1. This is an intermix of
+          the PCI-SIG PCIe CFG-space with the shadow registers for some
+          PCI Header space, PCI Standard and Extended Structures. It's
+          mainly relevant for the end-point controller configuration,
+          but still there are some shadow registers available for the
+          Root Port mode too.
+        const: dbi2
+      - description:
+          External Local Bus registers. It's an application-dependent
+          registers normally defined by the platform engineers. The space
+          can be selected by setting CDM/ELBI = 1 and CS2 = 0 wires.
+        enum: [ elbi, appl, app, link ]
+      - description:
+          iATU/eDMA registers common for all device functions. It's an
+          unrolled memory space with the internal Address Translation
+          Unit and Enhanced DMA, which is selected by setting CDM/ELBI = 1
+          and CS2 = 1. For IP-core releases prior v4.80a, these registers
+          have been programmed via an indirect addressing scheme using a
+          set of viewport CSRs mapped into the PL space. Note iATU is
+          normally mapped to the 0x0 address of this region, while eDMA
+          is available at 0x80000 base address.
+        enum: [ atu, atu_dma ]
+      - description:
+          Platform-specific eDMA registers. Some platforms may have eDMA
+          CSRs mapped in non-standard base address. The registers offset
+          can be changed or the MS/LS-bits of the address can be added in
+          an additional RTL block before the MEM-IO transactions reach
+          the DW PCIe slave interface.
+        const: dma
+      - description:
+          Outbound iATU-available memory-region which usage scenario
+          depends on the DWC PCIe controller being either Root Port or
+          End-point. If it's Root Port then the register space shall
+          have the "config" name and it will be used to access the
+          peripheral PCIe devices configuration space. If it's PCIe
+          end-point controller, then the region shall be named as
+          "addr_space" and it will be used to generate various traffic
+          on the PCIe bus hierarchy. It's usage scenario depends on the
+          end-point functionality, for instance it can be used to create
+          MSI(X) messages.
+        enum: [ config, addr_space ]
+      - description:
+          PHY/PCS configuration registers. Some platforms can have the
+          PCS and PHY CSRs accessible over a dedicated memory mapped
+          region, but mainly these registers are indirectly accessible
+          either by means of the embedded PHY viewport schema or by some
+          platform-specific method.
+        enum: [ phy ]
+
+  interrupt-names:
+    description:
+      IRQ signal names common for the DWC PCIe Root Port and End-point
+      controllers.
+    oneOf:
+      - description:
+          Controller request to read or write virtual product data
+          from/to the VPD capability registers.
+        const: vpd
+      - description:
+          Link Equalization Request flag is set in the Link Status 2
+          register (applicable if the corresponding IRQ is enabled in
+          the Link Control 3 register).
+        const: l_eq
+      - description:
+          Indicates that the eDMA Tx/Rx transfer is complete or that an
+          error has occurred on the corresponding channel. eDMA can have
+          eight Tx (Write) and Rx (Read) eDMA channels thus supporting up
+          to 16 IRQ signals all together. Write eDMA channels shall go
+          first in the ordered row as per default edma_int[*] bus setup.
+        pattern: '^dma([0-9]|1[0-5])?$'
+      - description:
+          PCIe protocol correctable error or a Data Path protection
+          correctable error is detected by the automotive/safety
+          feature.
+        const: sft_ce
+      - description:
+          Indicates that the internal safety mechanism detected and
+          uncorrectable error.
+        const: sft_ue
+
+  clock-names:
+    description:
+      Reference clock names common for the DWC PCIe Root Port and End-point
+      controllers.
+    anyOf:
+      - description:
+          Data Bus Interface (DBI) clock. Clock signal for the AXI-bus
+          interface of the Configuration-Dependent Module, which is
+          basically the set of the controller CSRs.
+        enum: [ dbi, pcie ]
+      - description:
+          Application AXI-bus Master interface clock. Basically this is
+          a clock for the controller DMA interface (PCI-to-CPU).
+        enum: [ mstr, pcie_bus ]
+      - description:
+          Application AXI-bus Slave interface clock. This is a clock for
+          the CPU-to-PCI memory IO interface.
+        enum: [ slv, pcie_bus ]
+      - description:
+          Controller Core-PCS PIPE interface clock. It's normally
+          supplied by an external PCS-PHY.
+        const: pipe
+      - description:
+          Controller Primary clock. It's assumed that all controller input
+          signals (except resets) are synchronous to this clock.
+        const: core
+      - description:
+          Auxiliary clock for the controller PMC domain. The controller
+          partitioning implies having some parts to operate with this
+          clock in some power management states.
+        const: aux
+      - description:
+          Generic reference clock. In case if there are several
+          interfaces fed up with a common clock source it's advisable to
+          define it with this name (for instance pipe, core and aux can
+          be connected to a single source of the periodic signal).
+        const: ref
+      - description:
+          Clock for the PHY registers interface. Originally this is
+          a PHY-viewport-based interface, but some platform may have
+          specifically designed one.
+        const: phy_reg
+
+  reset-names:
+    description:
+      Reset signal names common for the DWC PCIe Root Port and End-point
+      controllers.
+    anyOf:
+      - description: Data Bus Interface (DBI) domain reset
+        const: dbi
+      - description: AXI-bus Master interface reset
+        const: mstr
+      - description: AXI-bus Slave interface reset
+        const: slv
+      - description: Controller Non-sticky CSR flags reset
+        const: non-sticky
+      - description: Controller sticky CSR flags reset
+        const: sticky
+      - description: PIPE-interface (Core-PCS) logic reset
+        const: pipe
+      - description:
+          Controller primary reset (resets everything except PMC module)
+        const: core
+      - description: PCS/PHY block reset
+        const: phy
+      - description: PMC hot reset signal
+        const: hot
+      - description: Cold reset signal
+        const: pwr
+
+...
diff --git a/Documentation/devicetree/bindings/pci/snps,dw-pcie-ep.yaml b/Documentation/devicetree/bindings/pci/snps,dw-pcie-ep.yaml
index e59059ab5be0..9e7b27bcf404 100644
--- a/Documentation/devicetree/bindings/pci/snps,dw-pcie-ep.yaml
+++ b/Documentation/devicetree/bindings/pci/snps,dw-pcie-ep.yaml
@@ -13,74 +13,115 @@ maintainers:
 description: |
   Synopsys DesignWare PCIe host controller endpoint
 
+# Please create a separate DT-schema for the particular DWC PCIe End-point
+# controller and make sure it's assigned with the vendor-specific
+# compatible string together with the generic Synopsys DWC PCIe strings so
+# the bindings would be successfully evaluated against this schema.
+select: false
+
 allOf:
   - $ref: /schemas/pci/pci-ep.yaml#
+  - $ref: /schemas/pci/snps,dw-pcie-common.yaml#
 
 properties:
   compatible:
-    anyOf:
-      - {}
-      - const: snps,dw-pcie-ep
+    contains:
+      oneOf:
+        - description:
+            DWC PCIe End-point controller (IP-core version is explicitly
+            specified in the additional compatible string)
+          items:
+            - pattern: '^snps,dw-pcie-ep-[0-9]+\.[0-9]+a?$'
+            - const: snps,dw-pcie-ep
+        - description:
+            DWC PCIe End-point controller (IP-core version is either unknown
+            or can be read from the PCIe version register of the PL reg-space)
+          const: snps,dw-pcie-ep
 
   reg:
-    description: |
-      It should contain Data Bus Interface (dbi) and config registers for all
-      versions.
-      For designware core version >= 4.80, it may contain ATU address space.
-    minItems: 2
-    maxItems: 4
+    description:
+      DBI, DBI2 reg-spaces and outbound memory window are required for the
+      normal controller functioning. iATU memory IO region is also required
+      if the space is unrolled (IP-core version >= 4.80a).
+    minItems: 3
+    maxItems: 5
 
   reg-names:
-    minItems: 2
-    maxItems: 4
+    minItems: 3
+    maxItems: 5
     items:
-      enum: [dbi, dbi2, config, atu, addr_space, link, atu_dma, appl]
-
-  reset-gpio:
-    description: GPIO pin number of PERST# signal
-    maxItems: 1
-    deprecated: true
-
-  reset-gpios:
-    description: GPIO controlled connection to PERST# signal
-    maxItems: 1
-
-  snps,enable-cdm-check:
-    type: boolean
-    description: |
-      This is a boolean property and if present enables
-      automatic checking of CDM (Configuration Dependent Module) registers
-      for data corruption. CDM registers include standard PCIe configuration
-      space registers, Port Logic registers, DMA and iATU (internal Address
-      Translation Unit) registers.
-
-  num-ib-windows:
-    description: number of inbound address translation windows
-    maxItems: 1
-    deprecated: true
-
-  num-ob-windows:
-    description: number of outbound address translation windows
-    maxItems: 1
-    deprecated: true
+      $ref: /schemas/pci/snps,dw-pcie-common.yaml#/definitions/reg-names
+    allOf:
+      - contains:
+          const: dbi
+      - contains:
+          const: dbi2
+      - contains:
+          const: addr_space
+
+  interrupts:
+    description:
+      There is no mandatory IRQ signals for the normal controller functioning,
+      but in addition to the native set the platforms may have a link- or
+      PM-related IRQs specified.
+    minItems: 1
+    maxItems: 20
+
+  interrupt-names:
+    minItems: 1
+    maxItems: 20
+    items:
+      $ref: /schemas/pci/snps,dw-pcie-common.yaml#/definitions/interrupt-names
+
+  clocks:
+    minItems: 1
+    maxItems: 7
+
+  clock-names:
+    minItems: 1
+    maxItems: 7
+    items:
+      $ref: /schemas/pci/snps,dw-pcie-common.yaml#/definitions/clock-names
+
+  resets:
+    minItems: 1
+    maxItems: 10
+
+  reset-names:
+    minItems: 1
+    maxItems: 10
+    items:
+      $ref: /schemas/pci/snps,dw-pcie-common.yaml#/definitions/reset-names
+
+  max-functions:
+    maximum: 32
 
 required:
+  - compatible
   - reg
   - reg-names
-  - compatible
 
 additionalProperties: true
 
 examples:
   - |
-    bus {
-      #address-cells = <1>;
-      #size-cells = <1>;
-      pcie-ep@dfd00000 {
-        compatible = "snps,dw-pcie-ep";
-        reg = <0xdfc00000 0x0001000>, /* IP registers 1 */
-              <0xdfc01000 0x0001000>, /* IP registers 2 */
-              <0xd0000000 0x2000000>; /* Configuration space */
-        reg-names = "dbi", "dbi2", "addr_space";
-      };
+    pcie-ep@1f052000 {
+      compatible = "vendor,soc-pcie", "snps,dw-pcie-ep-4.60a", "snps,dw-pcie-ep";
+      reg = <0x66000000 0x1000>, <0x66010000 0x10000>, <0x67000000 0x400000>;
+      reg-names = "dbi", "dbi2", "addr_space";
+
+      clocks = <&sys_clk 12>, <&sys_clk 24>;
+      clock-names = "dbi", "ref";
+
+      resets = <&sys_rst 12>, <&sys_rst 24>;
+      reset-names = "dbi", "phy";
+
+      phys = <&pcie_phy>;
+      phy-names = "pcie-phy";
+
+      num-lanes = <4>;
+      max-link-speed = <4>;
+      num-ib-windows = <16>;
+      num-ob-windows = <16>;
     };
+...
diff --git a/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml b/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml
index a5345c494744..426dd9efe2cc 100644
--- a/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml
+++ b/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml
@@ -10,23 +10,39 @@ maintainers:
   - Jingoo Han <jingoohan1@gmail.com>
   - Gustavo Pimentel <gustavo.pimentel@synopsys.com>
 
-description: |
+description:
   Synopsys DesignWare PCIe host controller
 
+# Please create a separate DT-schema for the particular DWC PCIe Root Port
+# controller and make sure it's assigned with the vendor-specific
+# compatible string together with the generic Synopsys DWC PCIe strings so
+# the bindings would be successfully evaluated against that schema.
+select: false
+
 allOf:
   - $ref: /schemas/pci/pci-bus.yaml#
+  - $ref: /schemas/pci/snps,dw-pcie-common.yaml#
 
 properties:
   compatible:
-    anyOf:
-      - {}
-      - const: snps,dw-pcie
+    contains:
+      oneOf:
+        - description:
+            DWC PCIe Root Port controller (IP-core version is explicitly
+            specified in the additional compatible string)
+          items:
+            - pattern: '^snps,dw-pcie-[0-9]+\.[0-9]+a?$'
+            - const: snps,dw-pcie
+        - description:
+            DWC PCIe Root Port controller (IP-core version is either unknown
+            or can be read from the PCIe version register of the PL reg-space)
+          const: snps,dw-pcie
 
   reg:
-    description: |
-      It should contain Data Bus Interface (dbi) and config registers for all
-      versions.
-      For designware core version >= 4.80, it may contain ATU address space.
+    description:
+      At least DBI reg-space and peripheral devices CFG-space outbound window
+      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
 
@@ -34,69 +50,114 @@ properties:
     minItems: 2
     maxItems: 5
     items:
-      enum: [ dbi, dbi2, config, atu, app, elbi, mgmt, ctrl, parf, cfg, link,
-              ulreg, smu, mpu, apb, phy ]
-
-  num-lanes:
-    description: |
-      number of lanes to use (this property should be specified unless
-      the link is brought already up in firmware)
-    maximum: 16
-
-  reset-gpio:
-    description: GPIO pin number of PERST# signal
-    maxItems: 1
-    deprecated: true
-
-  reset-gpios:
-    description: GPIO controlled connection to PERST# signal
-    maxItems: 1
-
-  interrupts: true
-
-  interrupt-names: true
-
-  clocks: true
-
-  snps,enable-cdm-check:
-    type: boolean
-    description: |
-      This is a boolean property and if present enables
-      automatic checking of CDM (Configuration Dependent Module) registers
-      for data corruption. CDM registers include standard PCIe configuration
-      space registers, Port Logic registers, DMA and iATU (internal Address
-      Translation Unit) registers.
+      $ref: /schemas/pci/snps,dw-pcie-common.yaml#/definitions/reg-names
+    allOf:
+      - contains:
+          const: dbi
+      - contains:
+          const: config
+
+  interrupts:
+    description:
+      At least MSI interrupt signal is supposed to be specified for
+      the DWC PCIe host controller.
+    minItems: 1
+    maxItems: 26
+
+  interrupt-names:
+    minItems: 1
+    maxItems: 26
+    items:
+      anyOf:
+        - $ref: /schemas/pci/snps,dw-pcie-common.yaml#/definitions/interrupt-names
+        - $ref: '#/definitions/interrupt-names'
+    allOf:
+      - contains:
+          const: msi
+
+  clocks:
+    minItems: 1
+    maxItems: 7
+
+  clock-names:
+    minItems: 1
+    maxItems: 7
+    items:
+      $ref: /schemas/pci/snps,dw-pcie-common.yaml#/definitions/clock-names
 
-  num-viewport:
-    description: |
-      number of view ports configured in hardware. If a platform
-      does not specify it, the driver autodetects it.
-    deprecated: true
+  resets:
+    minItems: 1
+    maxItems: 10
 
-additionalProperties: true
+  reset-names:
+    minItems: 1
+    maxItems: 10
+    items:
+      $ref: /schemas/pci/snps,dw-pcie-common.yaml#/definitions/reset-names
 
 required:
+  - compatible
   - reg
   - reg-names
-  - compatible
+
+additionalProperties: true
+
+definitions:
+  interrupt-names:
+    description:
+      DWC PCIe Root Port/Complex specific IRQ signal names.
+    oneOf:
+      - description:
+          DSP AXI MSI Interrupt detected. It gets de-asserted when there is
+          no more MSI interrupt pending. The interrupt is relevant to the
+          iMSI-RX - Integrated MSI Receiver (AXI bridge).
+        const: msi
+      - description:
+          Error condition detected and a bit is set in the Root Error Status
+          register of the AER capability. It's asserted when the RC
+          internally generated an error or an error message is received by
+          the RC.
+        const: aer
+      - description:
+          PME message is received by the port. That means having the PME
+          status bit set in the Root Status register (the event is
+          supposed to be unmasked in the Root Control register).
+        const: pme
+      - description:
+          Hot-plug event is detected. That is a bit has been set in the
+          Slot Status register and the corresponding event is enabled in
+          the Slot Control register.
+        const: hp
+      - description:
+          Link Autonomous Bandwidth Status flag has been set in the Link
+          Status register (the event is supposed to be unmasked in the
+          Link Control register).
+        const: bw_au
+      - description:
+          Bandwidth Management Status flag has been set in the Link
+          Status register (the event is supposed to be unmasked in the
+          Link Control register).
+        const: bw_mg
 
 examples:
   - |
-    bus {
-      #address-cells = <1>;
-      #size-cells = <1>;
-      pcie@dfc00000 {
-        device_type = "pci";
-        compatible = "snps,dw-pcie";
-        reg = <0xdfc00000 0x0001000>, /* IP registers */
-              <0xd0000000 0x0002000>; /* Configuration space */
-        reg-names = "dbi", "config";
-        #address-cells = <3>;
-        #size-cells = <2>;
-        ranges = <0x81000000 0 0x00000000 0xde000000 0 0x00010000>,
-                 <0x82000000 0 0xd0400000 0xd0400000 0 0x0d000000>;
-        interrupts = <25>, <24>;
-        #interrupt-cells = <1>;
-        num-lanes = <1>;
-      };
+    pcie@1f052000 {
+      compatible = "vendor,soc-pcie", "snps,dw-pcie-4.60a", "snps,dw-pcie";
+      device_type = "pci";
+      reg = <0x1f052000 0x1000>, <0x1bdbf000 0x1000>;
+      reg-names = "dbi", "config";
+      #address-cells = <3>;
+      #size-cells = <2>;
+      ranges = <0x81000000 0 0x00000000 0x1bdb0000 0 0x00008000>,
+               <0x82000000 0 0x20000000 0x08000000 0 0x13db0000>;
+      bus-range = <0x0 0xff>;
+
+      interrupts = <0 80 4>;
+      interrupt-names = "msi";
+
+      reset-gpios = <&port0 0 1>;
+
+      num-lanes = <4>;
+      max-link-speed = <3>;
     };
+...
diff --git a/Documentation/devicetree/bindings/pci/socionext,uniphier-pcie-ep.yaml b/Documentation/devicetree/bindings/pci/socionext,uniphier-pcie-ep.yaml
index 437e61618d06..841eba762851 100644
--- a/Documentation/devicetree/bindings/pci/socionext,uniphier-pcie-ep.yaml
+++ b/Documentation/devicetree/bindings/pci/socionext,uniphier-pcie-ep.yaml
@@ -16,7 +16,7 @@ maintainers:
   - Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
 
 allOf:
-  - $ref: /schemas/pci/snps,dw-pcie-ep.yaml#
+  - $ref: /schemas/pci/snps,dw-pcie-common.yaml#
 
 properties:
   compatible:
diff --git a/Documentation/devicetree/bindings/pci/toshiba,visconti-pcie.yaml b/Documentation/devicetree/bindings/pci/toshiba,visconti-pcie.yaml
index 30b6396d83c8..f0a3c436c6d1 100644
--- a/Documentation/devicetree/bindings/pci/toshiba,visconti-pcie.yaml
+++ b/Documentation/devicetree/bindings/pci/toshiba,visconti-pcie.yaml
@@ -13,7 +13,7 @@ description:
   Toshiba Visconti5 SoC PCIe host controller is based on the Synopsys DesignWare PCIe IP.
 
 allOf:
-  - $ref: /schemas/pci/snps,dw-pcie.yaml#
+  - $ref: /schemas/pci/snps,dw-pcie-common.yaml#
 
 properties:
   compatible:
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 02/17] dt-bindings: PCI: dwc: Add Baikal-T1 PCIe Root Port bindings
  2022-05-03 21:46 [PATCH v2 00/17] PCI: dwc: Add dma-ranges/YAML-schema/Baikal-T1 support Serge Semin
  2022-05-03 21:46 ` [PATCH v2 01/17] dt-bindings: PCI: dwc: Define common and native DT bindings Serge Semin
@ 2022-05-03 21:46 ` Serge Semin
  2022-05-03 21:46 ` [PATCH v2 03/17] PCI: dwc: Add more verbose link-up message Serge Semin
                   ` (14 subsequent siblings)
  16 siblings, 0 replies; 49+ messages in thread
From: Serge Semin @ 2022-05-03 21:46 UTC (permalink / raw)
  To: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Rob Herring, Krzysztof Kozlowski, Serge Semin
  Cc: Serge Semin, Alexey Malahov, Pavel Parkhomenko, Rob Herring,
	Krzysztof Wilczyński, Frank Li, Manivannan Sadhasivam,
	linux-pci, devicetree, linux-kernel

Baikal-T1 SoC is equipped with DWC PCIe v4.60a Root Port controller, which
link can be trained to work on up to Gen.3 speed over up to x4 lanes. The
controller is supposed to be fed up with four clock sources: DBI
peripheral clock, AXI application Tx/Rx clocks and external PHY/core
reference clock generating the 100MHz signal. In addition to that the
platform provide a way to reset each part of the controller:
sticky/non-sticky bits, host controller core, PIPE interface, PCS/PHY and
Hot/Power reset signal. The Root Port controller is equipped with multiple
IRQ lines like MSI, system AER, PME, HP, Bandwidth change, Link
equalization request and eDMA ones. The registers space is accessed over
the DBI interface. There can be no more than four inbound or outbound iATU
windows configured.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>

---

Changelog v2:
- Rename 'syscon' property to 'baikal,bt1-syscon'.
- Fix the 'compatible' property definition to being more specific about
  what strings are supposed to be used. Due to that we had to add the
  select property to evaluate the schema against the Baikal-T1 PCIe DT
  nodes only.
---
 .../bindings/pci/baikal,bt1-pcie.yaml         | 158 ++++++++++++++++++
 1 file changed, 158 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/baikal,bt1-pcie.yaml

diff --git a/Documentation/devicetree/bindings/pci/baikal,bt1-pcie.yaml b/Documentation/devicetree/bindings/pci/baikal,bt1-pcie.yaml
new file mode 100644
index 000000000000..1eb5d7bcb7c7
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/baikal,bt1-pcie.yaml
@@ -0,0 +1,158 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/baikal,bt1-pcie.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Baikal-T1 PCIe Root Port Controller
+
+maintainers:
+  - Serge Semin <fancer.lancer@gmail.com>
+
+description:
+  Embedded into Baikal-T1 SoC Root Complex controller. It's based on the
+  DWC RC PCIe v4.60a IP-core, which is configured to have just a single Root
+  Port function and is capable of establishing the link up to Gen.3 speed
+  on x4 lanes. It doesn't have embedded clock and reset control module, so
+  the proper interface initialization is supposed to be performed by software.
+
+select:
+  properties:
+    compatible:
+      contains:
+        const: baikal,bt1-pcie
+
+  required:
+    - compatible
+
+allOf:
+  - $ref: /schemas/pci/snps,dw-pcie.yaml#
+
+properties:
+  compatible:
+    items:
+      - const: baikal,bt1-pcie
+      - const: snps,dw-pcie-4.60a
+      - const: snps,dw-pcie
+
+  reg:
+    description:
+      DBI, DBI2 and at least 4KB outbound iATU-capable region.
+    maxItems: 3
+
+  reg-names:
+    minItems: 3
+    maxItems: 3
+    items:
+      enum: [ dbi, dbi2, config ]
+
+  interrupts:
+    description:
+      MSI, AER, PME, Hot-plug, Link Bandwidth Management, Link Equalization
+      request and eight Read/Write eDMA IRQ lines are available.
+    maxItems: 14
+
+  interrupt-names:
+    minItems: 14
+    maxItems: 14
+    items:
+      oneOf:
+        - pattern: '^dma[0-7]$'
+        - enum: [ msi, aer, pme, hp, bw_mg, l_eq ]
+
+  clocks:
+    description:
+      DBI (attached to the APB bus), AXI-bus master and slave interfaces
+      are fed up by the dedicated application clocks. A common reference
+      clock signal is supposed to be attached to the corresponding Ref-pad
+      of the SoC. It will be redistributed amongst the controller core
+      sub-modules (pipe, core, aux, etc).
+    minItems: 4
+    maxItems: 4
+
+  clock-names:
+    minItems: 4
+    maxItems: 4
+    items:
+      enum: [ dbi, mstr, slv, ref ]
+
+  resets:
+    description:
+      A comprehensive controller reset logic is supposed to be implemented
+      by software, so almost all the possible application and core reset
+      signals are exposed via the system CCU module.
+    minItems: 9
+    maxItems: 9
+
+  reset-names:
+    minItems: 9
+    maxItems: 9
+    items:
+      enum: [ mstr, slv, pwr, hot, phy, core, pipe, sticky, non-sticky ]
+
+  baikal,bt1-syscon:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description:
+      Phandle to the Baikal-T1 System Controller DT node. It's required to
+      access some additional PM, Reset-related and LTSSM signals.
+
+  num-lanes:
+    maximum: 4
+
+  max-link-speed:
+    maximum: 3
+
+  num-ob-windows:
+    const: 4
+
+  num-ib-windows:
+    const: 4
+
+required:
+  - compatible
+  - reg
+  - reg-names
+  - interrupts
+  - interrupt-names
+  - clocks
+  - clock-names
+  - resets
+  - reset-names
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    pcie@1f052000 {
+      compatible = "baikal,bt1-pcie", "snps,dw-pcie-4.60a", "snps,dw-pcie";
+      device_type = "pci";
+      reg = <0x1f052000 0x1000>, <0x1f053000 0x1000>, <0x1bdbf000 0x1000>;
+      reg-names = "dbi", "dbi2", "config";
+      #address-cells = <3>;
+      #size-cells = <2>;
+      ranges = <0x81000000 0 0x00000000 0x1bdb0000 0 0x00008000>,
+               <0x82000000 0 0x20000000 0x08000000 0 0x13db0000>;
+      bus-range = <0x0 0xff>;
+
+      interrupts = <0 80 4>, <0 81 4>, <0 82 4>, <0 83 4>,
+                   <0 84 4>, <0 85 4>, <0 86 4>, <0 87 4>,
+                   <0 88 4>, <0 89 4>, <0 90 4>, <0 91 4>,
+                   <0 92 4>, <0 93 4>;
+      interrupt-names = "dma0", "dma1", "dma2", "dma3", "dma4", "dma5", "dma6",
+                        "dma7", "msi", "aer", "pme", "hp", "bw_mg", "l_eq";
+
+      clocks = <&ccu_sys 1>, <&ccu_axi 6>, <&ccu_axi 7>, <&clk_pcie>;
+      clock-names = "dbi", "mstr", "slv", "ref";
+
+      resets = <&ccu_axi 6>, <&ccu_axi 7>, <&ccu_sys 7>, <&ccu_sys 10>,
+               <&ccu_sys 4>, <&ccu_sys 6>, <&ccu_sys 5>, <&ccu_sys 8>,
+               <&ccu_sys 9>;
+      reset-names = "mstr", "slv", "pwr", "hot", "phy", "core", "pipe",
+                    "sticky", "non-sticky";
+
+      reset-gpios = <&port0 0 1>;
+
+      num-lanes = <4>;
+      max-link-speed = <3>;
+    };
+...
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 03/17] PCI: dwc: Add more verbose link-up message
  2022-05-03 21:46 [PATCH v2 00/17] PCI: dwc: Add dma-ranges/YAML-schema/Baikal-T1 support Serge Semin
  2022-05-03 21:46 ` [PATCH v2 01/17] dt-bindings: PCI: dwc: Define common and native DT bindings Serge Semin
  2022-05-03 21:46 ` [PATCH v2 02/17] dt-bindings: PCI: dwc: Add Baikal-T1 PCIe Root Port bindings Serge Semin
@ 2022-05-03 21:46 ` Serge Semin
  2022-05-16 20:18   ` Rob Herring
  2022-05-03 21:46 ` [PATCH v2 04/17] PCI: dwc: Detect iATU settings after getting "addr_space" resource Serge Semin
                   ` (13 subsequent siblings)
  16 siblings, 1 reply; 49+ messages in thread
From: Serge Semin @ 2022-05-03 21:46 UTC (permalink / raw)
  To: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Rob Herring, Krzysztof Wilczyński
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Pavel Parkhomenko,
	Frank Li, Manivannan Sadhasivam, Rob Herring, linux-pci,
	devicetree, linux-kernel

Printing just "link up" isn't that much informative especially when it
comes to working with the PCI Express bus. Even if the link is up, due to
multiple reasons the bus performance can degrade to slower speeds or to
narrower width than both Root Port and its partner is capable of. In that
case it would be handy to know the link specifications as early as
possible. So let's add a more verbose message to the busy-wait link-state
method, which will contain the link speed generation and the PCIe bus
width in case if the link up state is discovered. Otherwise an error will
be printed to the system log.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>

---

Changelog v2:
- Test the error condition first and return straight away if it comes true.
  The typical return is better to be unindented (@Joe).
---
 drivers/pci/controller/dwc/pcie-designware.c | 22 ++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index 6e81264fdfb4..1682f477bf20 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -524,20 +524,30 @@ void dw_pcie_disable_atu(struct dw_pcie *pci, int index,
 
 int dw_pcie_wait_for_link(struct dw_pcie *pci)
 {
+	u32 offset, val;
 	int retries;
 
 	/* Check if the link is up or not */
 	for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
-		if (dw_pcie_link_up(pci)) {
-			dev_info(pci->dev, "Link up\n");
-			return 0;
-		}
+		if (dw_pcie_link_up(pci))
+			break;
+
 		usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
 	}
 
-	dev_info(pci->dev, "Phy link never came up\n");
+	if (retries >= LINK_WAIT_MAX_RETRIES) {
+		dev_err(pci->dev, "Phy link never came up\n");
+		return -ETIMEDOUT;
+	}
+
+	offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+	val = dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKSTA);
 
-	return -ETIMEDOUT;
+	dev_info(pci->dev, "PCIe Gen.%u x%u link up\n",
+		 FIELD_GET(PCI_EXP_LNKSTA_CLS, val),
+		 FIELD_GET(PCI_EXP_LNKSTA_NLW, val));
+
+	return 0;
 }
 EXPORT_SYMBOL_GPL(dw_pcie_wait_for_link);
 
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 04/17] PCI: dwc: Detect iATU settings after getting "addr_space" resource
  2022-05-03 21:46 [PATCH v2 00/17] PCI: dwc: Add dma-ranges/YAML-schema/Baikal-T1 support Serge Semin
                   ` (2 preceding siblings ...)
  2022-05-03 21:46 ` [PATCH v2 03/17] PCI: dwc: Add more verbose link-up message Serge Semin
@ 2022-05-03 21:46 ` Serge Semin
  2022-05-16 20:21   ` Rob Herring
  2022-05-03 21:46 ` [PATCH v2 05/17] PCI: dwc: Convert to using native IP-core versions representation Serge Semin
                   ` (12 subsequent siblings)
  16 siblings, 1 reply; 49+ messages in thread
From: Serge Semin @ 2022-05-03 21:46 UTC (permalink / raw)
  To: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Rob Herring, Krzysztof Wilczyński
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Pavel Parkhomenko,
	Frank Li, Manivannan Sadhasivam, Rob Herring, linux-pci,
	devicetree, linux-kernel

The iATU detection procedure was introduced in the commit 281f1f99cf3a
("PCI: dwc: Detect number of iATU windows"). A bit later the procedure
execution was moved to Host/EP-specific methods in the framework of commit
8bcca2658558 ("PCI: dwc: Move iATU detection earlier"). The later
modification wasn't done in the most optimal way since the "addr_space"
CSR region resource doesn't depend on anything detected in the
dw_pcie_iatu_detect() method. Thus the detection can be postponed to be
executed after the resource request which can fail and make the detection
pointless. It will be also helpful for the dw_pcie_ep_init() method
readability since we are about to add the IP-core version and eDMA module
(a bit later) detection procedures.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>

---

Changelog v2:
- This is a new patch added on v2 iteration of the series.
---
 drivers/pci/controller/dwc/pcie-designware-ep.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 7ad349c32082..c152021d0bf6 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -708,8 +708,6 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
 		}
 	}
 
-	dw_pcie_iatu_detect(pci);
-
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space");
 	if (!res)
 		return -EINVAL;
@@ -717,6 +715,8 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
 	ep->phys_base = res->start;
 	ep->addr_size = resource_size(res);
 
+	dw_pcie_iatu_detect(pci);
+
 	ep->ib_window_map = devm_kcalloc(dev,
 					 BITS_TO_LONGS(pci->num_ib_windows),
 					 sizeof(long),
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 05/17] PCI: dwc: Convert to using native IP-core versions representation
  2022-05-03 21:46 [PATCH v2 00/17] PCI: dwc: Add dma-ranges/YAML-schema/Baikal-T1 support Serge Semin
                   ` (3 preceding siblings ...)
  2022-05-03 21:46 ` [PATCH v2 04/17] PCI: dwc: Detect iATU settings after getting "addr_space" resource Serge Semin
@ 2022-05-03 21:46 ` Serge Semin
  2022-05-16 20:30   ` Rob Herring
  2022-05-16 20:31   ` Rob Herring
  2022-05-03 21:46 ` [PATCH v2 06/17] PCI: dwc: Add IP-core version detection procedure Serge Semin
                   ` (11 subsequent siblings)
  16 siblings, 2 replies; 49+ messages in thread
From: Serge Semin @ 2022-05-03 21:46 UTC (permalink / raw)
  To: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Rob Herring, Krzysztof Wilczyński, Rahul Tanwar,
	Thierry Reding, Jonathan Hunter
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Pavel Parkhomenko,
	Frank Li, Manivannan Sadhasivam, Rob Herring, linux-pci,
	devicetree, linux-kernel, linux-tegra

Since DWC PCIe v4.70a the controller version can be read from the
PORT_LOGIC.PCIE_VERSION_OFF register. Version is represented in the FourCC
format [1]. It's standard versioning approach for the Synopsys DWC
IP-cores. Moreover some of the DWC kernel drivers already make use of it
to fixup version-dependent functionality (See DWC USB3, Stmicro STMMAC or
recent DW SPI driver). In order to preserve the standard version
representation and prevent the data conversion back and forth, we suggest
to preserve the native version representation in the DWC PCIe driver too
in the same way as it has already been done in the rest of the DWC
drivers. IP-core version reading from the CSR will be introduced in the
next commit together with a simple macro-based API to use it.

[1] https://en.wikipedia.org/wiki/FourCC

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
 drivers/pci/controller/dwc/pci-keystone.c    | 12 ++++++------
 drivers/pci/controller/dwc/pcie-designware.c |  8 ++++----
 drivers/pci/controller/dwc/pcie-designware.h | 10 +++++++++-
 drivers/pci/controller/dwc/pcie-intel-gw.c   |  4 ++--
 drivers/pci/controller/dwc/pcie-tegra194.c   |  2 +-
 5 files changed, 22 insertions(+), 14 deletions(-)

diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c
index d10e5fd0f83c..c51018c68b56 100644
--- a/drivers/pci/controller/dwc/pci-keystone.c
+++ b/drivers/pci/controller/dwc/pci-keystone.c
@@ -109,7 +109,7 @@ struct ks_pcie_of_data {
 	enum dw_pcie_device_mode mode;
 	const struct dw_pcie_host_ops *host_ops;
 	const struct dw_pcie_ep_ops *ep_ops;
-	unsigned int version;
+	u32 version;
 };
 
 struct keystone_pcie {
@@ -1069,19 +1069,19 @@ static int ks_pcie_am654_set_mode(struct device *dev,
 
 static const struct ks_pcie_of_data ks_pcie_rc_of_data = {
 	.host_ops = &ks_pcie_host_ops,
-	.version = 0x365A,
+	.version = DW_PCIE_VER_365A,
 };
 
 static const struct ks_pcie_of_data ks_pcie_am654_rc_of_data = {
 	.host_ops = &ks_pcie_am654_host_ops,
 	.mode = DW_PCIE_RC_TYPE,
-	.version = 0x490A,
+	.version = DW_PCIE_VER_490A,
 };
 
 static const struct ks_pcie_of_data ks_pcie_am654_ep_of_data = {
 	.ep_ops = &ks_pcie_am654_ep_ops,
 	.mode = DW_PCIE_EP_TYPE,
-	.version = 0x490A,
+	.version = DW_PCIE_VER_490A,
 };
 
 static const struct of_device_id ks_pcie_of_match[] = {
@@ -1114,12 +1114,12 @@ static int __init ks_pcie_probe(struct platform_device *pdev)
 	struct device_link **link;
 	struct gpio_desc *gpiod;
 	struct resource *res;
-	unsigned int version;
 	void __iomem *base;
 	u32 num_viewport;
 	struct phy **phy;
 	u32 num_lanes;
 	char name[10];
+	u32 version;
 	int ret;
 	int irq;
 	int i;
@@ -1233,7 +1233,7 @@ static int __init ks_pcie_probe(struct platform_device *pdev)
 		goto err_get_sync;
 	}
 
-	if (pci->version >= 0x480A)
+	if (pci->version >= DW_PCIE_VER_480A)
 		ret = ks_pcie_am654_set_mode(dev, mode);
 	else
 		ret = ks_pcie_set_mode(dev);
diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index 1682f477bf20..3ebb7bfee10f 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -289,7 +289,7 @@ static void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, u8 func_no,
 	val = type | PCIE_ATU_FUNC_NUM(func_no);
 	if (upper_32_bits(limit_addr) > upper_32_bits(cpu_addr))
 		val |= PCIE_ATU_INCREASE_REGION_SIZE;
-	if (pci->version == 0x490A)
+	if (pci->version == DW_PCIE_VER_490A)
 		val = dw_pcie_enable_ecrc(val);
 	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, val);
 	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
@@ -336,7 +336,7 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
 			   upper_32_bits(cpu_addr));
 	dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT,
 			   lower_32_bits(limit_addr));
-	if (pci->version >= 0x460A)
+	if (pci->version >= DW_PCIE_VER_460A)
 		dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_LIMIT,
 				   upper_32_bits(limit_addr));
 	dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET,
@@ -345,9 +345,9 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
 			   upper_32_bits(pci_addr));
 	val = type | PCIE_ATU_FUNC_NUM(func_no);
 	if (upper_32_bits(limit_addr) > upper_32_bits(cpu_addr) &&
-	    pci->version >= 0x460A)
+	    pci->version >= DW_PCIE_VER_460A)
 		val |= PCIE_ATU_INCREASE_REGION_SIZE;
-	if (pci->version == 0x490A)
+	if (pci->version == DW_PCIE_VER_490A)
 		val = dw_pcie_enable_ecrc(val);
 	dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, val);
 	dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE);
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index 7d6e9b7576be..5be43c662176 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -20,6 +20,14 @@
 #include <linux/pci-epc.h>
 #include <linux/pci-epf.h>
 
+/* DWC PCIe IP-core versions (native support since v4.70a) */
+#define DW_PCIE_VER_365A		0x3336352a
+#define DW_PCIE_VER_460A		0x3436302a
+#define DW_PCIE_VER_470A		0x3437302a
+#define DW_PCIE_VER_480A		0x3438302a
+#define DW_PCIE_VER_490A		0x3439302a
+#define DW_PCIE_VER_520A		0x3532302a
+
 /* Parameters for the waiting for link up routine */
 #define LINK_WAIT_MAX_RETRIES		10
 #define LINK_WAIT_USLEEP_MIN		90000
@@ -269,7 +277,7 @@ struct dw_pcie {
 	struct pcie_port	pp;
 	struct dw_pcie_ep	ep;
 	const struct dw_pcie_ops *ops;
-	unsigned int		version;
+	u32			version;
 	int			num_lanes;
 	int			link_gen;
 	u8			n_fts[2];
diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c
index 5ba144924ff8..786af2ba379f 100644
--- a/drivers/pci/controller/dwc/pcie-intel-gw.c
+++ b/drivers/pci/controller/dwc/pcie-intel-gw.c
@@ -59,7 +59,7 @@
 #define RESET_INTERVAL_MS		100
 
 struct intel_pcie_soc {
-	unsigned int	pcie_ver;
+	u32	pcie_ver;
 };
 
 struct intel_pcie {
@@ -395,7 +395,7 @@ static const struct dw_pcie_host_ops intel_pcie_dw_ops = {
 };
 
 static const struct intel_pcie_soc pcie_data = {
-	.pcie_ver =		0x520A,
+	.pcie_ver =		DW_PCIE_VER_520A,
 };
 
 static int intel_pcie_probe(struct platform_device *pdev)
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index b1b5f836a806..6f1330ed63e5 100644
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
@@ -1981,7 +1981,7 @@ static int tegra194_pcie_probe(struct platform_device *pdev)
 	pci->ops = &tegra_dw_pcie_ops;
 	pci->n_fts[0] = N_FTS_VAL;
 	pci->n_fts[1] = FTS_VAL;
-	pci->version = 0x490A;
+	pci->version = DW_PCIE_VER_490A;
 
 	pp = &pci->pp;
 	pp->num_vectors = MAX_MSI_IRQS;
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 06/17] PCI: dwc: Add IP-core version detection procedure
  2022-05-03 21:46 [PATCH v2 00/17] PCI: dwc: Add dma-ranges/YAML-schema/Baikal-T1 support Serge Semin
                   ` (4 preceding siblings ...)
  2022-05-03 21:46 ` [PATCH v2 05/17] PCI: dwc: Convert to using native IP-core versions representation Serge Semin
@ 2022-05-03 21:46 ` Serge Semin
  2022-05-16 20:32   ` Rob Herring
  2022-05-03 21:46 ` [PATCH v2 07/17] PCI: dwc: Introduce Synopsys IP-core versions/types interface Serge Semin
                   ` (10 subsequent siblings)
  16 siblings, 1 reply; 49+ messages in thread
From: Serge Semin @ 2022-05-03 21:46 UTC (permalink / raw)
  To: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Rob Herring, Krzysztof Wilczyński
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Pavel Parkhomenko,
	Frank Li, Manivannan Sadhasivam, Rob Herring, linux-pci,
	devicetree, linux-kernel

Since DWC PCIe v4.70a the controller version and version type can be read
from the PORT_LOGIC.PCIE_VERSION_OFF and PORT_LOGIC.PCIE_VERSION_TYPE_OFF
registers respectively. Seeing the generic code has got version-dependent
parts let's use these registers to find out the controller version.  The
detection procedure is executed for both RC and EP modes right after the
platform-specific initialization. We can't do that earlier since the
glue-drivers can perform the DBI-related setups there including the bus
reference clocks activation, without which the CSRs just can't be read.

Note the CSRs content is zero on the older DWC PCIe controller. In that
case we have no choice but to rely on the platform setup.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>

---

Changelog v2:
- Move the IP-core version detection procedure call from
  dw_pcie_ep_init_complete() to dw_pcie_ep_init().
---
 .../pci/controller/dwc/pcie-designware-ep.c   |  2 ++
 .../pci/controller/dwc/pcie-designware-host.c |  2 ++
 drivers/pci/controller/dwc/pcie-designware.c  | 24 +++++++++++++++++++
 drivers/pci/controller/dwc/pcie-designware.h  |  6 +++++
 4 files changed, 34 insertions(+)

diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index c152021d0bf6..1be8bbf35047 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -715,6 +715,8 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
 	ep->phys_base = res->start;
 	ep->addr_size = resource_size(res);
 
+	dw_pcie_version_detect(pci);
+
 	dw_pcie_iatu_detect(pci);
 
 	ep->ib_window_map = devm_kcalloc(dev,
diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index 1c815d3bead9..c74b4587b236 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -404,6 +404,8 @@ int dw_pcie_host_init(struct pcie_port *pp)
 		}
 	}
 
+	dw_pcie_version_detect(pci);
+
 	dw_pcie_iatu_detect(pci);
 
 	dw_pcie_setup_rc(pp);
diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index 3ebb7bfee10f..62f50086aa49 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -16,6 +16,30 @@
 #include "../../pci.h"
 #include "pcie-designware.h"
 
+void dw_pcie_version_detect(struct dw_pcie *pci)
+{
+	u32 ver;
+
+	/* The content of the CSR is zero on DWC PCIe older than v4.70a */
+	ver = dw_pcie_readl_dbi(pci, PCIE_VERSION_NUMBER);
+	if (!ver)
+		return;
+
+	if (pci->version && pci->version != ver)
+		dev_warn(pci->dev, "Versions don't match (%08x != %08x)\n",
+			 pci->version, ver);
+	else
+		pci->version = ver;
+
+	ver = dw_pcie_readl_dbi(pci, PCIE_VERSION_TYPE);
+
+	if (pci->type && pci->type != ver)
+		dev_warn(pci->dev, "Types don't match (%08x != %08x)\n",
+			 pci->type, ver);
+	else
+		pci->type = ver;
+}
+
 /*
  * These interfaces resemble the pci_find_*capability() interfaces, but these
  * are for configuring host controllers, which are bridges *to* PCI devices but
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index 5be43c662176..f70cbdedf5a3 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -85,6 +85,9 @@
 #define PCIE_PORT_MULTI_LANE_CTRL	0x8C0
 #define PORT_MLTI_UPCFG_SUPPORT		BIT(7)
 
+#define PCIE_VERSION_NUMBER		0x8F8
+#define PCIE_VERSION_TYPE		0x8FC
+
 #define PCIE_ATU_VIEWPORT		0x900
 #define PCIE_ATU_REGION_INBOUND		BIT(31)
 #define PCIE_ATU_REGION_OUTBOUND	0
@@ -278,6 +281,7 @@ struct dw_pcie {
 	struct dw_pcie_ep	ep;
 	const struct dw_pcie_ops *ops;
 	u32			version;
+	u32			type;
 	int			num_lanes;
 	int			link_gen;
 	u8			n_fts[2];
@@ -290,6 +294,8 @@ struct dw_pcie {
 #define to_dw_pcie_from_ep(endpoint)   \
 		container_of((endpoint), struct dw_pcie, ep)
 
+void dw_pcie_version_detect(struct dw_pcie *pci);
+
 u8 dw_pcie_find_capability(struct dw_pcie *pci, u8 cap);
 u16 dw_pcie_find_ext_capability(struct dw_pcie *pci, u8 cap);
 
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 07/17] PCI: dwc: Introduce Synopsys IP-core versions/types interface
  2022-05-03 21:46 [PATCH v2 00/17] PCI: dwc: Add dma-ranges/YAML-schema/Baikal-T1 support Serge Semin
                   ` (5 preceding siblings ...)
  2022-05-03 21:46 ` [PATCH v2 06/17] PCI: dwc: Add IP-core version detection procedure Serge Semin
@ 2022-05-03 21:46 ` Serge Semin
  2022-05-16 20:33   ` Rob Herring
  2022-05-03 21:46 ` [PATCH v2 08/17] PCI: dwc: Add host de-initialization callback Serge Semin
                   ` (9 subsequent siblings)
  16 siblings, 1 reply; 49+ messages in thread
From: Serge Semin @ 2022-05-03 21:46 UTC (permalink / raw)
  To: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Rob Herring, Krzysztof Wilczyński
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Pavel Parkhomenko,
	Frank Li, Manivannan Sadhasivam, Rob Herring, linux-pci,
	devicetree, linux-kernel

Instead of manual DW PCIe data version field comparison let's use a handy
macro-based interface in order to shorten out the statements, simplify the
corresponding parts, improve the code readability and maintainability in
perspective when more complex version-based dependencies need to
implemented. Similar approaches have already been implemented in the DWC
USB3 and DW SPI drivers (though with some IP-core evolution specifics).

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
 drivers/pci/controller/dwc/pci-keystone.c    |  2 +-
 drivers/pci/controller/dwc/pcie-designware.c |  8 ++++----
 drivers/pci/controller/dwc/pcie-designware.h | 15 +++++++++++++++
 3 files changed, 20 insertions(+), 5 deletions(-)

diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c
index c51018c68b56..a0874bf4a0b6 100644
--- a/drivers/pci/controller/dwc/pci-keystone.c
+++ b/drivers/pci/controller/dwc/pci-keystone.c
@@ -1233,7 +1233,7 @@ static int __init ks_pcie_probe(struct platform_device *pdev)
 		goto err_get_sync;
 	}
 
-	if (pci->version >= DW_PCIE_VER_480A)
+	if (dw_pcie_ver_is_ge(pci, 480A))
 		ret = ks_pcie_am654_set_mode(dev, mode);
 	else
 		ret = ks_pcie_set_mode(dev);
diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index 62f50086aa49..dd2e4335d85a 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -313,7 +313,7 @@ static void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, u8 func_no,
 	val = type | PCIE_ATU_FUNC_NUM(func_no);
 	if (upper_32_bits(limit_addr) > upper_32_bits(cpu_addr))
 		val |= PCIE_ATU_INCREASE_REGION_SIZE;
-	if (pci->version == DW_PCIE_VER_490A)
+	if (dw_pcie_ver_is(pci, 490A))
 		val = dw_pcie_enable_ecrc(val);
 	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, val);
 	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
@@ -360,7 +360,7 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
 			   upper_32_bits(cpu_addr));
 	dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT,
 			   lower_32_bits(limit_addr));
-	if (pci->version >= DW_PCIE_VER_460A)
+	if (dw_pcie_ver_is_ge(pci, 460A))
 		dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_LIMIT,
 				   upper_32_bits(limit_addr));
 	dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET,
@@ -369,9 +369,9 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
 			   upper_32_bits(pci_addr));
 	val = type | PCIE_ATU_FUNC_NUM(func_no);
 	if (upper_32_bits(limit_addr) > upper_32_bits(cpu_addr) &&
-	    pci->version >= DW_PCIE_VER_460A)
+	    dw_pcie_ver_is_ge(pci, 460A))
 		val |= PCIE_ATU_INCREASE_REGION_SIZE;
-	if (pci->version == DW_PCIE_VER_490A)
+	if (dw_pcie_ver_is(pci, 490A))
 		val = dw_pcie_enable_ecrc(val);
 	dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, val);
 	dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE);
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index f70cbdedf5a3..1868773ecb91 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -28,6 +28,21 @@
 #define DW_PCIE_VER_490A		0x3439302a
 #define DW_PCIE_VER_520A		0x3532302a
 
+#define __dw_pcie_ver_cmp(_pci, _ver, _op) \
+	((_pci)->version _op DW_PCIE_VER_ ## _ver)
+
+#define dw_pcie_ver_is(_pci, _ver) __dw_pcie_ver_cmp(_pci, _ver, ==)
+
+#define dw_pcie_ver_is_ge(_pci, _ver) __dw_pcie_ver_cmp(_pci, _ver, >=)
+
+#define dw_pcie_ver_type_is(_pci, _ver, _type) \
+	(__dw_pcie_ver_cmp(_pci, _ver, ==) && \
+	 __dw_pcie_ver_cmp(_pci, TYPE_ ## _type, ==))
+
+#define dw_pcie_ver_type_is_ge(_pci, _ver, _type) \
+	(__dw_pcie_ver_cmp(_pci, _ver, ==) && \
+	 __dw_pcie_ver_cmp(_pci, TYPE_ ## _type, >=))
+
 /* Parameters for the waiting for link up routine */
 #define LINK_WAIT_MAX_RETRIES		10
 #define LINK_WAIT_USLEEP_MIN		90000
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 08/17] PCI: dwc: Add host de-initialization callback
  2022-05-03 21:46 [PATCH v2 00/17] PCI: dwc: Add dma-ranges/YAML-schema/Baikal-T1 support Serge Semin
                   ` (6 preceding siblings ...)
  2022-05-03 21:46 ` [PATCH v2 07/17] PCI: dwc: Introduce Synopsys IP-core versions/types interface Serge Semin
@ 2022-05-03 21:46 ` Serge Semin
  2022-05-16 20:48   ` Rob Herring
  2022-05-03 21:46 ` [PATCH v2 09/17] PCI: dwc: Drop inbound iATU types enumeration - dw_pcie_as_type Serge Semin
                   ` (8 subsequent siblings)
  16 siblings, 1 reply; 49+ messages in thread
From: Serge Semin @ 2022-05-03 21:46 UTC (permalink / raw)
  To: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Rob Herring, Krzysztof Wilczyński
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Pavel Parkhomenko,
	Frank Li, Manivannan Sadhasivam, Rob Herring, linux-pci,
	devicetree, linux-kernel

Seeing the platform-specific DW PCIe host-initialization is performed from
within the generic dw_pcie_host_init() method by means of the dedicated
dw_pcie_ops.host_init() callback, there must be declared an antagonist
which would perform the corresponding cleanups. Let's add such callback
then. It will be called in the dw_pcie_host_deinit() method and in the
cleanup-on-error path in the dw_pcie_host_init() function.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
 .../pci/controller/dwc/pcie-designware-host.c | 21 ++++++++++++++-----
 drivers/pci/controller/dwc/pcie-designware.h  |  1 +
 2 files changed, 17 insertions(+), 5 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index c74b4587b236..bb1c25c32f2d 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -354,13 +354,14 @@ int dw_pcie_host_init(struct pcie_port *pp)
 			pp->num_vectors = MSI_DEF_NUM_VECTORS;
 		} else if (pp->num_vectors > MAX_MSI_IRQS) {
 			dev_err(dev, "Invalid number of vectors\n");
-			return -EINVAL;
+			ret = -EINVAL;
+			goto err_deinit_host;
 		}
 
 		if (pp->ops->msi_host_init) {
 			ret = pp->ops->msi_host_init(pp);
 			if (ret < 0)
-				return ret;
+				goto err_deinit_host;
 		} else if (pp->has_msi_ctrl) {
 			u32 ctrl, num_ctrls;
 
@@ -372,8 +373,10 @@ int dw_pcie_host_init(struct pcie_port *pp)
 				pp->msi_irq = platform_get_irq_byname_optional(pdev, "msi");
 				if (pp->msi_irq < 0) {
 					pp->msi_irq = platform_get_irq(pdev, 0);
-					if (pp->msi_irq < 0)
-						return pp->msi_irq;
+					if (pp->msi_irq < 0) {
+						ret = pp->msi_irq;
+						goto err_deinit_host;
+					}
 				}
 			}
 
@@ -381,7 +384,7 @@ int dw_pcie_host_init(struct pcie_port *pp)
 
 			ret = dw_pcie_allocate_domains(pp);
 			if (ret)
-				return ret;
+				goto err_deinit_host;
 
 			if (pp->msi_irq > 0)
 				irq_set_chained_handler_and_data(pp->msi_irq,
@@ -434,6 +437,11 @@ int dw_pcie_host_init(struct pcie_port *pp)
 err_free_msi:
 	if (pp->has_msi_ctrl)
 		dw_pcie_free_msi(pp);
+
+err_deinit_host:
+	if (pp->ops->host_deinit)
+		pp->ops->host_deinit(pp);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(dw_pcie_host_init);
@@ -450,6 +458,9 @@ void dw_pcie_host_deinit(struct pcie_port *pp)
 
 	if (pp->has_msi_ctrl)
 		dw_pcie_free_msi(pp);
+
+	if (pp->ops->host_deinit)
+		pp->ops->host_deinit(pp);
 }
 EXPORT_SYMBOL_GPL(dw_pcie_host_deinit);
 
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index 1868773ecb91..bca1d3e83636 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -200,6 +200,7 @@ enum dw_pcie_device_mode {
 
 struct dw_pcie_host_ops {
 	int (*host_init)(struct pcie_port *pp);
+	void (*host_deinit)(struct pcie_port *pp);
 	int (*msi_host_init)(struct pcie_port *pp);
 };
 
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 09/17] PCI: dwc: Drop inbound iATU types enumeration - dw_pcie_as_type
  2022-05-03 21:46 [PATCH v2 00/17] PCI: dwc: Add dma-ranges/YAML-schema/Baikal-T1 support Serge Semin
                   ` (7 preceding siblings ...)
  2022-05-03 21:46 ` [PATCH v2 08/17] PCI: dwc: Add host de-initialization callback Serge Semin
@ 2022-05-03 21:46 ` Serge Semin
  2022-05-03 21:46 ` [PATCH v2 10/17] PCI: dwc: Drop iATU regions enumeration - dw_pcie_region_type Serge Semin
                   ` (7 subsequent siblings)
  16 siblings, 0 replies; 49+ messages in thread
From: Serge Semin @ 2022-05-03 21:46 UTC (permalink / raw)
  To: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Rob Herring, Krzysztof Wilczyński
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Pavel Parkhomenko,
	Frank Li, Manivannan Sadhasivam, Rob Herring, linux-pci,
	devicetree, linux-kernel

There is no point in having an enumeration declared in the driver for the
PCIe end-point. First of all it's redundant since the driver already has a
set of macro declared which describe the available in/out iATU types, thus
having an addition abstraction just needlessly complicates the code.
Secondly checking the passed iATU type for validity within a single driver
is pointless since the driver is supposed to be consistent by its nature.
Finally the outbound iATU type isn't encoded by the denoted enumeration,
thus giving a false impression that the in and out iATU types are
unrelated while they are the same. So to speak let's drop the redundant
dw_pcie_as_type enumeration replacing it with the direct iATU type usage.

While at it, since we are touching the iATU inbound regions config methods
anyway, let's fix the arguments order so the type would be followed by the
address-related parameters. Thus the inbound and outbound iATU setup
methods will look alike. That shall improve the code readability a bit.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../pci/controller/dwc/pcie-designware-ep.c   | 21 +++++------
 drivers/pci/controller/dwc/pcie-designware.c  | 35 +++----------------
 drivers/pci/controller/dwc/pcie-designware.h  |  9 +----
 3 files changed, 15 insertions(+), 50 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 1be8bbf35047..6ce44c12ea1c 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -154,9 +154,8 @@ static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
 	return 0;
 }
 
-static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no,
-				  enum pci_barno bar, dma_addr_t cpu_addr,
-				  enum dw_pcie_as_type as_type)
+static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type,
+				  dma_addr_t cpu_addr, enum pci_barno bar)
 {
 	int ret;
 	u32 free_win;
@@ -168,8 +167,8 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no,
 		return -EINVAL;
 	}
 
-	ret = dw_pcie_prog_inbound_atu(pci, func_no, free_win, bar, cpu_addr,
-				       as_type);
+	ret = dw_pcie_prog_inbound_atu(pci, func_no, free_win, type,
+				       cpu_addr, bar);
 	if (ret < 0) {
 		dev_err(pci->dev, "Failed to program IB window\n");
 		return ret;
@@ -221,27 +220,25 @@ static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
 static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
 			      struct pci_epf_bar *epf_bar)
 {
-	int ret;
 	struct dw_pcie_ep *ep = epc_get_drvdata(epc);
 	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
 	enum pci_barno bar = epf_bar->barno;
 	size_t size = epf_bar->size;
 	int flags = epf_bar->flags;
-	enum dw_pcie_as_type as_type;
-	u32 reg;
 	unsigned int func_offset = 0;
+	int ret, type;
+	u32 reg;
 
 	func_offset = dw_pcie_ep_func_select(ep, func_no);
 
 	reg = PCI_BASE_ADDRESS_0 + (4 * bar) + func_offset;
 
 	if (!(flags & PCI_BASE_ADDRESS_SPACE))
-		as_type = DW_PCIE_AS_MEM;
+		type = PCIE_ATU_TYPE_MEM;
 	else
-		as_type = DW_PCIE_AS_IO;
+		type = PCIE_ATU_TYPE_IO;
 
-	ret = dw_pcie_ep_inbound_atu(ep, func_no, bar,
-				     epf_bar->phys_addr, as_type);
+	ret = dw_pcie_ep_inbound_atu(ep, func_no, type, epf_bar->phys_addr, bar);
 	if (ret)
 		return ret;
 
diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index dd2e4335d85a..4edfc95e0213 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -421,10 +421,9 @@ static void dw_pcie_writel_ib_unroll(struct dw_pcie *pci, u32 index, u32 reg,
 }
 
 static int dw_pcie_prog_inbound_atu_unroll(struct dw_pcie *pci, u8 func_no,
-					   int index, int bar, u64 cpu_addr,
-					   enum dw_pcie_as_type as_type)
+					   int index, int type,
+					   u64 cpu_addr, u8 bar)
 {
-	int type;
 	u32 retries, val;
 
 	dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET,
@@ -432,17 +431,6 @@ static int dw_pcie_prog_inbound_atu_unroll(struct dw_pcie *pci, u8 func_no,
 	dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET,
 				 upper_32_bits(cpu_addr));
 
-	switch (as_type) {
-	case DW_PCIE_AS_MEM:
-		type = PCIE_ATU_TYPE_MEM;
-		break;
-	case DW_PCIE_AS_IO:
-		type = PCIE_ATU_TYPE_IO;
-		break;
-	default:
-		return -EINVAL;
-	}
-
 	dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, type |
 				 PCIE_ATU_FUNC_NUM(func_no));
 	dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
@@ -468,32 +456,19 @@ static int dw_pcie_prog_inbound_atu_unroll(struct dw_pcie *pci, u8 func_no,
 }
 
 int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
-			     int bar, u64 cpu_addr,
-			     enum dw_pcie_as_type as_type)
+			     int type, u64 cpu_addr, u8 bar)
 {
-	int type;
 	u32 retries, val;
 
 	if (pci->iatu_unroll_enabled)
-		return dw_pcie_prog_inbound_atu_unroll(pci, func_no, index, bar,
-						       cpu_addr, as_type);
+		return dw_pcie_prog_inbound_atu_unroll(pci, func_no, index, type,
+						       cpu_addr, bar);
 
 	dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, PCIE_ATU_REGION_INBOUND |
 			   index);
 	dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, lower_32_bits(cpu_addr));
 	dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET, upper_32_bits(cpu_addr));
 
-	switch (as_type) {
-	case DW_PCIE_AS_MEM:
-		type = PCIE_ATU_TYPE_MEM;
-		break;
-	case DW_PCIE_AS_IO:
-		type = PCIE_ATU_TYPE_IO;
-		break;
-	default:
-		return -EINVAL;
-	}
-
 	dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, type |
 			   PCIE_ATU_FUNC_NUM(func_no));
 	dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE |
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index bca1d3e83636..449c5ad92edc 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -227,12 +227,6 @@ struct pcie_port {
 	DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS);
 };
 
-enum dw_pcie_as_type {
-	DW_PCIE_AS_UNKNOWN,
-	DW_PCIE_AS_MEM,
-	DW_PCIE_AS_IO,
-};
-
 struct dw_pcie_ep_ops {
 	void	(*ep_init)(struct dw_pcie_ep *ep);
 	int	(*raise_irq)(struct dw_pcie_ep *ep, u8 func_no,
@@ -331,8 +325,7 @@ void dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index,
 				  int type, u64 cpu_addr, u64 pci_addr,
 				  u64 size);
 int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
-			     int bar, u64 cpu_addr,
-			     enum dw_pcie_as_type as_type);
+			     int type, u64 cpu_addr, u8 bar);
 void dw_pcie_disable_atu(struct dw_pcie *pci, int index,
 			 enum dw_pcie_region_type type);
 void dw_pcie_setup(struct dw_pcie *pci);
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 10/17] PCI: dwc: Drop iATU regions enumeration - dw_pcie_region_type
  2022-05-03 21:46 [PATCH v2 00/17] PCI: dwc: Add dma-ranges/YAML-schema/Baikal-T1 support Serge Semin
                   ` (8 preceding siblings ...)
  2022-05-03 21:46 ` [PATCH v2 09/17] PCI: dwc: Drop inbound iATU types enumeration - dw_pcie_as_type Serge Semin
@ 2022-05-03 21:46 ` Serge Semin
  2022-05-16 20:49   ` Rob Herring
  2022-05-03 21:46 ` [PATCH v2 11/17] PCI: dwc: Simplify in/outbound iATU setup methods Serge Semin
                   ` (6 subsequent siblings)
  16 siblings, 1 reply; 49+ messages in thread
From: Serge Semin @ 2022-05-03 21:46 UTC (permalink / raw)
  To: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Rob Herring, Krzysztof Wilczyński
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Pavel Parkhomenko,
	Frank Li, Manivannan Sadhasivam, Rob Herring, linux-pci,
	devicetree, linux-kernel

There is no point in having the dw_pcie_region_type enumeration for almost
the same reasons as it was stated for dw_pcie_as_type. First of all it's
redundant since the driver already has a set of the macros declared which
describe the possible inbound and outbound iATU regions. Having an
addition abstraction just needlessly complicates the code. Secondly
checking the region type passed to the dw_pcie_disable_atu() method for
validity is pointless since the erroneous situation is just ignored in the
current method implementation. So to speak let's drop the redundant
dw_pcie_region_type enumeration replacing it with the direct iATU
direction macro usage.

Since the dw_pcie_disable_atu() method now directly accepts the
in-/outbound iATU region direction instead of the abstract region type we
need to change the argument name and the arguments order. The later change
makes the function prototype looking more logical since the passed index
indicates an iATU window within the regions with the corresponding
direction.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>

---

Changelog v2:
- Move this patch to being applied before the IB/OB iATU windows setup
  simplification patch (@Rob).
---
 .../pci/controller/dwc/pcie-designware-ep.c   |  4 +--
 .../pci/controller/dwc/pcie-designware-host.c |  2 +-
 drivers/pci/controller/dwc/pcie-designware.c  | 28 +++++--------------
 drivers/pci/controller/dwc/pcie-designware.h  | 13 ++-------
 4 files changed, 13 insertions(+), 34 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 6ce44c12ea1c..68ad3d250450 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -212,7 +212,7 @@ static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
 
 	__dw_pcie_ep_reset_bar(pci, func_no, bar, epf_bar->flags);
 
-	dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_INBOUND);
+	dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, atu_index);
 	clear_bit(atu_index, ep->ib_window_map);
 	ep->epf_bar[bar] = NULL;
 }
@@ -286,7 +286,7 @@ static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
 	if (ret < 0)
 		return;
 
-	dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_OUTBOUND);
+	dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_OB, atu_index);
 	clear_bit(atu_index, ep->ob_window_map);
 }
 
diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index bb1c25c32f2d..ade31a04f9df 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -649,7 +649,7 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
 		 * multiple matches
 		 */
 		for (i = 0; i < pci->num_ob_windows; i++)
-			dw_pcie_disable_atu(pci, i, DW_PCIE_REGION_OUTBOUND);
+			dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_OB, i);
 
 		/* Get last memory resource entry */
 		resource_list_for_each_entry(entry, &pp->bridge->windows) {
diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index 4edfc95e0213..596c249445ec 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -353,7 +353,7 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
 	limit_addr = cpu_addr + size - 1;
 
 	dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT,
-			   PCIE_ATU_REGION_OUTBOUND | index);
+			   PCIE_ATU_REGION_DIR_OB | index);
 	dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_BASE,
 			   lower_32_bits(cpu_addr));
 	dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_BASE,
@@ -464,7 +464,7 @@ int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
 		return dw_pcie_prog_inbound_atu_unroll(pci, func_no, index, type,
 						       cpu_addr, bar);
 
-	dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, PCIE_ATU_REGION_INBOUND |
+	dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, PCIE_ATU_REGION_DIR_IB |
 			   index);
 	dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, lower_32_bits(cpu_addr));
 	dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET, upper_32_bits(cpu_addr));
@@ -491,24 +491,10 @@ int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
 	return -EBUSY;
 }
 
-void dw_pcie_disable_atu(struct dw_pcie *pci, int index,
-			 enum dw_pcie_region_type type)
+void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index)
 {
-	int region;
-
-	switch (type) {
-	case DW_PCIE_REGION_INBOUND:
-		region = PCIE_ATU_REGION_INBOUND;
-		break;
-	case DW_PCIE_REGION_OUTBOUND:
-		region = PCIE_ATU_REGION_OUTBOUND;
-		break;
-	default:
-		return;
-	}
-
 	if (pci->iatu_unroll_enabled) {
-		if (region == PCIE_ATU_REGION_INBOUND) {
+		if (dir == PCIE_ATU_REGION_DIR_IB) {
 			dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
 						 ~(u32)PCIE_ATU_ENABLE);
 		} else {
@@ -516,7 +502,7 @@ void dw_pcie_disable_atu(struct dw_pcie *pci, int index,
 						 ~(u32)PCIE_ATU_ENABLE);
 		}
 	} else {
-		dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, region | index);
+		dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, dir | index);
 		dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, ~(u32)PCIE_ATU_ENABLE);
 	}
 }
@@ -661,7 +647,7 @@ static void dw_pcie_iatu_detect_regions(struct dw_pcie *pci)
 	max_region = dw_pcie_readl_dbi(pci, PCIE_ATU_VIEWPORT) + 1;
 
 	for (i = 0; i < max_region; i++) {
-		dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, PCIE_ATU_REGION_OUTBOUND | i);
+		dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, PCIE_ATU_REGION_DIR_OB | i);
 		dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, 0x11110000);
 		val = dw_pcie_readl_dbi(pci, PCIE_ATU_LOWER_TARGET);
 		if (val == 0x11110000)
@@ -671,7 +657,7 @@ static void dw_pcie_iatu_detect_regions(struct dw_pcie *pci)
 	}
 
 	for (i = 0; i < max_region; i++) {
-		dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, PCIE_ATU_REGION_INBOUND | i);
+		dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, PCIE_ATU_REGION_DIR_IB | i);
 		dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, 0x11110000);
 		val = dw_pcie_readl_dbi(pci, PCIE_ATU_LOWER_TARGET);
 		if (val == 0x11110000)
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index 449c5ad92edc..0bda68f8ffdb 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -104,8 +104,8 @@
 #define PCIE_VERSION_TYPE		0x8FC
 
 #define PCIE_ATU_VIEWPORT		0x900
-#define PCIE_ATU_REGION_INBOUND		BIT(31)
-#define PCIE_ATU_REGION_OUTBOUND	0
+#define PCIE_ATU_REGION_DIR_IB		BIT(31)
+#define PCIE_ATU_REGION_DIR_OB		0
 #define PCIE_ATU_CR1			0x904
 #define PCIE_ATU_INCREASE_REGION_SIZE	BIT(13)
 #define PCIE_ATU_TYPE_MEM		0x0
@@ -185,12 +185,6 @@ struct pcie_port;
 struct dw_pcie;
 struct dw_pcie_ep;
 
-enum dw_pcie_region_type {
-	DW_PCIE_REGION_UNKNOWN,
-	DW_PCIE_REGION_INBOUND,
-	DW_PCIE_REGION_OUTBOUND,
-};
-
 enum dw_pcie_device_mode {
 	DW_PCIE_UNKNOWN_TYPE,
 	DW_PCIE_EP_TYPE,
@@ -326,8 +320,7 @@ void dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index,
 				  u64 size);
 int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
 			     int type, u64 cpu_addr, u8 bar);
-void dw_pcie_disable_atu(struct dw_pcie *pci, int index,
-			 enum dw_pcie_region_type type);
+void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index);
 void dw_pcie_setup(struct dw_pcie *pci);
 void dw_pcie_iatu_detect(struct dw_pcie *pci);
 
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 11/17] PCI: dwc: Simplify in/outbound iATU setup methods
  2022-05-03 21:46 [PATCH v2 00/17] PCI: dwc: Add dma-ranges/YAML-schema/Baikal-T1 support Serge Semin
                   ` (9 preceding siblings ...)
  2022-05-03 21:46 ` [PATCH v2 10/17] PCI: dwc: Drop iATU regions enumeration - dw_pcie_region_type Serge Semin
@ 2022-05-03 21:46 ` Serge Semin
  2022-05-12 14:01   ` Manivannan Sadhasivam
  2022-05-03 21:46 ` [PATCH v2 12/17] PCI: dwc: Add iATU regions size detection procedure Serge Semin
                   ` (5 subsequent siblings)
  16 siblings, 1 reply; 49+ messages in thread
From: Serge Semin @ 2022-05-03 21:46 UTC (permalink / raw)
  To: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Rob Herring, Krzysztof Wilczyński
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Pavel Parkhomenko,
	Frank Li, Manivannan Sadhasivam, Rob Herring, linux-pci,
	devicetree, linux-kernel

From maintainability and scalability points of view it has been wrong to
use different iATU inbound and outbound regions accessors for the viewport
and unrolled versions of the iATU CSRs mapping. Seeing the particular iATU
region-wise registers layout is almost fully compatible for different
IP-core versions, there were no much points in splitting the code up that
way since it was possible to implement a common windows setup methods for
both viewport and unrolled iATU CSRs spaces. While what we can observe in
the current driver implementation of these methods, is a lot of code
duplication, which consequently worsen the code readability,
maintainability and scalability. Note the current implementation is a bit
more performant than the one suggested in this commit since it implies
having less MMIO accesses. But the gain just doesn't worth having the
denoted difficulties especially seeing the iATU setup methods are mainly
called on the DW PCIe controller and peripheral devices initialization
stage.

Here we suggest to move the iATU viewport and unrolled CSR access
specifics in the dw_pcie_readl_atu() and dw_pcie_writel_atu() method, and
convert the dw_pcie_prog_outbound_atu() and
dw_pcie_prog_{ep_}inbound_atu() functions to being generic instead of
having a different methods for each viewport and unrolled types of iATU
CSRs mapping. Nothing complex really. First of all the dw_pcie_readl_atu()
and dw_pcie_writel_atu() are converted to accept relative iATU CSRs
address together with the iATU region direction (inbound or outbound) and
region index. If DW PCIe controller doesn't have the unrolled iATU CSRs
space, then the accessors will need to activate a iATU viewport based on
the specified direction and index, otherwise a base address for the
corresponding region CSRs will be calculated by means of the
PCIE_ATU_UNROLL_BASE() macro. The CSRs macro have been modified in
accordance with that logic in the pcie-designware.h header file.

The rest of the changes in this commit just concern converting the iATU
in-/out-bound setup methods and iATU regions detection procedure to be
compatible with the new accessors semantics. As a result we've dropped the
no more required dw_pcie_prog_outbound_atu_unroll(),
dw_pcie_prog_inbound_atu_unroll() and dw_pcie_iatu_detect_regions_unroll()
methods.

Note aside with the denoted code improvements, there is an additional
positive side effect of this change. If at some point an atomic iATU
configs setup procedure is required, it will be possible to be done with
no much effort just by adding the synchronization into the
dw_pcie_readl_atu() and dw_pcie_writel_atu() accessors.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>

---

Changelog v2:
- Move the iATU region selection procedure into a helper function (@Rob).
- Simplify the iATU region selection procedure by recalculating the base
  address only if the space is unrolled. The iATU viewport base address
  is saved in the pci->atu_base field from now.
---
 drivers/pci/controller/dwc/pcie-designware.c | 293 ++++++-------------
 drivers/pci/controller/dwc/pcie-designware.h |  48 ++-
 2 files changed, 107 insertions(+), 234 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index 596c249445ec..0781fa145b3f 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -205,48 +205,64 @@ void dw_pcie_write_dbi2(struct dw_pcie *pci, u32 reg, size_t size, u32 val)
 		dev_err(pci->dev, "write DBI address failed\n");
 }
 
-static u32 dw_pcie_readl_atu(struct dw_pcie *pci, u32 reg)
+static inline void __iomem *dw_pcie_select_atu(struct dw_pcie *pci, u32 dir,
+					       u32 index)
 {
+	void __iomem *base = pci->atu_base;
+
+	if (pci->iatu_unroll_enabled)
+		base += PCIE_ATU_UNROLL_BASE(dir, index);
+	else
+		dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, dir | index);
+
+	return base;
+}
+
+static u32 dw_pcie_readl_atu(struct dw_pcie *pci, u32 dir, u32 index, u32 reg)
+{
+	void __iomem *base;
 	int ret;
 	u32 val;
 
+	base = dw_pcie_select_atu(pci, dir, index);
+
 	if (pci->ops && pci->ops->read_dbi)
-		return pci->ops->read_dbi(pci, pci->atu_base, reg, 4);
+		return pci->ops->read_dbi(pci, base, reg, 4);
 
-	ret = dw_pcie_read(pci->atu_base + reg, 4, &val);
+	ret = dw_pcie_read(base + reg, 4, &val);
 	if (ret)
 		dev_err(pci->dev, "Read ATU address failed\n");
 
 	return val;
 }
 
-static void dw_pcie_writel_atu(struct dw_pcie *pci, u32 reg, u32 val)
+static void dw_pcie_writel_atu(struct dw_pcie *pci, u32 dir, u32 index,
+			       u32 reg, u32 val)
 {
+	void __iomem *base;
 	int ret;
 
+	base = dw_pcie_select_atu(pci, dir, index);
+
 	if (pci->ops && pci->ops->write_dbi) {
-		pci->ops->write_dbi(pci, pci->atu_base, reg, 4, val);
+		pci->ops->write_dbi(pci, base, reg, 4, val);
 		return;
 	}
 
-	ret = dw_pcie_write(pci->atu_base + reg, 4, val);
+	ret = dw_pcie_write(base + reg, 4, val);
 	if (ret)
 		dev_err(pci->dev, "Write ATU address failed\n");
 }
 
-static u32 dw_pcie_readl_ob_unroll(struct dw_pcie *pci, u32 index, u32 reg)
+static inline u32 dw_pcie_readl_atu_ob(struct dw_pcie *pci, u32 index, u32 reg)
 {
-	u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
-
-	return dw_pcie_readl_atu(pci, offset + reg);
+	return dw_pcie_readl_atu(pci, PCIE_ATU_REGION_DIR_OB, index, reg);
 }
 
-static void dw_pcie_writel_ob_unroll(struct dw_pcie *pci, u32 index, u32 reg,
-				     u32 val)
+static inline void dw_pcie_writel_atu_ob(struct dw_pcie *pci, u32 index, u32 reg,
+					 u32 val)
 {
-	u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
-
-	dw_pcie_writel_atu(pci, offset + reg, val);
+	dw_pcie_writel_atu(pci, PCIE_ATU_REGION_DIR_OB, index, reg, val);
 }
 
 static inline u32 dw_pcie_enable_ecrc(u32 val)
@@ -290,50 +306,6 @@ static inline u32 dw_pcie_enable_ecrc(u32 val)
 	return val | PCIE_ATU_TD;
 }
 
-static void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, u8 func_no,
-					     int index, int type,
-					     u64 cpu_addr, u64 pci_addr,
-					     u64 size)
-{
-	u32 retries, val;
-	u64 limit_addr = cpu_addr + size - 1;
-
-	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_BASE,
-				 lower_32_bits(cpu_addr));
-	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_BASE,
-				 upper_32_bits(cpu_addr));
-	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_LIMIT,
-				 lower_32_bits(limit_addr));
-	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_LIMIT,
-				 upper_32_bits(limit_addr));
-	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET,
-				 lower_32_bits(pci_addr));
-	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET,
-				 upper_32_bits(pci_addr));
-	val = type | PCIE_ATU_FUNC_NUM(func_no);
-	if (upper_32_bits(limit_addr) > upper_32_bits(cpu_addr))
-		val |= PCIE_ATU_INCREASE_REGION_SIZE;
-	if (dw_pcie_ver_is(pci, 490A))
-		val = dw_pcie_enable_ecrc(val);
-	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, val);
-	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
-				 PCIE_ATU_ENABLE);
-
-	/*
-	 * Make sure ATU enable takes effect before any subsequent config
-	 * and I/O accesses.
-	 */
-	for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
-		val = dw_pcie_readl_ob_unroll(pci, index,
-					      PCIE_ATU_UNR_REGION_CTRL2);
-		if (val & PCIE_ATU_ENABLE)
-			return;
-
-		mdelay(LINK_WAIT_IATU);
-	}
-	dev_err(pci->dev, "Outbound iATU is not being enabled\n");
-}
-
 static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
 					int index, int type, u64 cpu_addr,
 					u64 pci_addr, u64 size)
@@ -344,49 +316,46 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
 	if (pci->ops && pci->ops->cpu_addr_fixup)
 		cpu_addr = pci->ops->cpu_addr_fixup(pci, cpu_addr);
 
-	if (pci->iatu_unroll_enabled) {
-		dw_pcie_prog_outbound_atu_unroll(pci, func_no, index, type,
-						 cpu_addr, pci_addr, size);
-		return;
-	}
-
 	limit_addr = cpu_addr + size - 1;
 
-	dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT,
-			   PCIE_ATU_REGION_DIR_OB | index);
-	dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_BASE,
-			   lower_32_bits(cpu_addr));
-	dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_BASE,
-			   upper_32_bits(cpu_addr));
-	dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT,
-			   lower_32_bits(limit_addr));
+	dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_LOWER_BASE,
+			      lower_32_bits(cpu_addr));
+	dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_UPPER_BASE,
+			      upper_32_bits(cpu_addr));
+
+	dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_LIMIT,
+			      lower_32_bits(limit_addr));
 	if (dw_pcie_ver_is_ge(pci, 460A))
-		dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_LIMIT,
-				   upper_32_bits(limit_addr));
-	dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET,
-			   lower_32_bits(pci_addr));
-	dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET,
-			   upper_32_bits(pci_addr));
+		dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_UPPER_LIMIT,
+				      upper_32_bits(limit_addr));
+
+	dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_LOWER_TARGET,
+			      lower_32_bits(pci_addr));
+	dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_UPPER_TARGET,
+			      upper_32_bits(pci_addr));
+
 	val = type | PCIE_ATU_FUNC_NUM(func_no);
 	if (upper_32_bits(limit_addr) > upper_32_bits(cpu_addr) &&
 	    dw_pcie_ver_is_ge(pci, 460A))
 		val |= PCIE_ATU_INCREASE_REGION_SIZE;
 	if (dw_pcie_ver_is(pci, 490A))
 		val = dw_pcie_enable_ecrc(val);
-	dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, val);
-	dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE);
+	dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_REGION_CTRL1, val);
+
+	dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_REGION_CTRL2, PCIE_ATU_ENABLE);
 
 	/*
 	 * Make sure ATU enable takes effect before any subsequent config
 	 * and I/O accesses.
 	 */
 	for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
-		val = dw_pcie_readl_dbi(pci, PCIE_ATU_CR2);
+		val = dw_pcie_readl_atu_ob(pci, index, PCIE_ATU_REGION_CTRL2);
 		if (val & PCIE_ATU_ENABLE)
 			return;
 
 		mdelay(LINK_WAIT_IATU);
 	}
+
 	dev_err(pci->dev, "Outbound iATU is not being enabled\n");
 }
 
@@ -405,54 +374,15 @@ void dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index,
 				    cpu_addr, pci_addr, size);
 }
 
-static u32 dw_pcie_readl_ib_unroll(struct dw_pcie *pci, u32 index, u32 reg)
-{
-	u32 offset = PCIE_GET_ATU_INB_UNR_REG_OFFSET(index);
-
-	return dw_pcie_readl_atu(pci, offset + reg);
-}
-
-static void dw_pcie_writel_ib_unroll(struct dw_pcie *pci, u32 index, u32 reg,
-				     u32 val)
+static inline u32 dw_pcie_readl_atu_ib(struct dw_pcie *pci, u32 index, u32 reg)
 {
-	u32 offset = PCIE_GET_ATU_INB_UNR_REG_OFFSET(index);
-
-	dw_pcie_writel_atu(pci, offset + reg, val);
+	return dw_pcie_readl_atu(pci, PCIE_ATU_REGION_DIR_IB, index, reg);
 }
 
-static int dw_pcie_prog_inbound_atu_unroll(struct dw_pcie *pci, u8 func_no,
-					   int index, int type,
-					   u64 cpu_addr, u8 bar)
+static inline void dw_pcie_writel_atu_ib(struct dw_pcie *pci, u32 index, u32 reg,
+					 u32 val)
 {
-	u32 retries, val;
-
-	dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET,
-				 lower_32_bits(cpu_addr));
-	dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET,
-				 upper_32_bits(cpu_addr));
-
-	dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, type |
-				 PCIE_ATU_FUNC_NUM(func_no));
-	dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
-				 PCIE_ATU_FUNC_NUM_MATCH_EN |
-				 PCIE_ATU_ENABLE |
-				 PCIE_ATU_BAR_MODE_ENABLE | (bar << 8));
-
-	/*
-	 * Make sure ATU enable takes effect before any subsequent config
-	 * and I/O accesses.
-	 */
-	for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
-		val = dw_pcie_readl_ib_unroll(pci, index,
-					      PCIE_ATU_UNR_REGION_CTRL2);
-		if (val & PCIE_ATU_ENABLE)
-			return 0;
-
-		mdelay(LINK_WAIT_IATU);
-	}
-	dev_err(pci->dev, "Inbound iATU is not being enabled\n");
-
-	return -EBUSY;
+	dw_pcie_writel_atu(pci, PCIE_ATU_REGION_DIR_IB, index, reg, val);
 }
 
 int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
@@ -460,51 +390,37 @@ int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
 {
 	u32 retries, val;
 
-	if (pci->iatu_unroll_enabled)
-		return dw_pcie_prog_inbound_atu_unroll(pci, func_no, index, type,
-						       cpu_addr, bar);
+	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_TARGET,
+			      lower_32_bits(cpu_addr));
+	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_TARGET,
+			      upper_32_bits(cpu_addr));
 
-	dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, PCIE_ATU_REGION_DIR_IB |
-			   index);
-	dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, lower_32_bits(cpu_addr));
-	dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET, upper_32_bits(cpu_addr));
-
-	dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, type |
-			   PCIE_ATU_FUNC_NUM(func_no));
-	dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE |
-			   PCIE_ATU_FUNC_NUM_MATCH_EN |
-			   PCIE_ATU_BAR_MODE_ENABLE | (bar << 8));
+	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL1, type |
+			      PCIE_ATU_FUNC_NUM(func_no));
+	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2,
+			      PCIE_ATU_ENABLE | PCIE_ATU_FUNC_NUM_MATCH_EN |
+			      PCIE_ATU_BAR_MODE_ENABLE | (bar << 8));
 
 	/*
 	 * Make sure ATU enable takes effect before any subsequent config
 	 * and I/O accesses.
 	 */
 	for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
-		val = dw_pcie_readl_dbi(pci, PCIE_ATU_CR2);
+		val = dw_pcie_readl_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2);
 		if (val & PCIE_ATU_ENABLE)
 			return 0;
 
 		mdelay(LINK_WAIT_IATU);
 	}
+
 	dev_err(pci->dev, "Inbound iATU is not being enabled\n");
 
-	return -EBUSY;
+	return -ETIMEDOUT;
 }
 
 void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index)
 {
-	if (pci->iatu_unroll_enabled) {
-		if (dir == PCIE_ATU_REGION_DIR_IB) {
-			dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
-						 ~(u32)PCIE_ATU_ENABLE);
-		} else {
-			dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
-						 ~(u32)PCIE_ATU_ENABLE);
-		}
-	} else {
-		dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, dir | index);
-		dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, ~(u32)PCIE_ATU_ENABLE);
-	}
+	dw_pcie_writel_atu(pci, dir, index, PCIE_ATU_REGION_CTRL2, 0);
 }
 
 int dw_pcie_wait_for_link(struct dw_pcie *pci)
@@ -606,63 +522,29 @@ static bool dw_pcie_iatu_unroll_enabled(struct dw_pcie *pci)
 	return false;
 }
 
-static void dw_pcie_iatu_detect_regions_unroll(struct dw_pcie *pci)
-{
-	int max_region, i, ob = 0, ib = 0;
-	u32 val;
-
-	max_region = min((int)pci->atu_size / 512, 256);
-
-	for (i = 0; i < max_region; i++) {
-		dw_pcie_writel_ob_unroll(pci, i, PCIE_ATU_UNR_LOWER_TARGET,
-					0x11110000);
-
-		val = dw_pcie_readl_ob_unroll(pci, i, PCIE_ATU_UNR_LOWER_TARGET);
-		if (val == 0x11110000)
-			ob++;
-		else
-			break;
-	}
-
-	for (i = 0; i < max_region; i++) {
-		dw_pcie_writel_ib_unroll(pci, i, PCIE_ATU_UNR_LOWER_TARGET,
-					0x11110000);
-
-		val = dw_pcie_readl_ib_unroll(pci, i, PCIE_ATU_UNR_LOWER_TARGET);
-		if (val == 0x11110000)
-			ib++;
-		else
-			break;
-	}
-	pci->num_ib_windows = ib;
-	pci->num_ob_windows = ob;
-}
-
 static void dw_pcie_iatu_detect_regions(struct dw_pcie *pci)
 {
-	int max_region, i, ob = 0, ib = 0;
+	int max_region, ob, ib;
 	u32 val;
 
-	dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, 0xFF);
-	max_region = dw_pcie_readl_dbi(pci, PCIE_ATU_VIEWPORT) + 1;
+	if (pci->iatu_unroll_enabled) {
+		max_region = min((int)pci->atu_size / 512, 256);
+	} else {
+		dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, 0xFF);
+		max_region = dw_pcie_readl_dbi(pci, PCIE_ATU_VIEWPORT) + 1;
+	}
 
-	for (i = 0; i < max_region; i++) {
-		dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, PCIE_ATU_REGION_DIR_OB | i);
-		dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, 0x11110000);
-		val = dw_pcie_readl_dbi(pci, PCIE_ATU_LOWER_TARGET);
-		if (val == 0x11110000)
-			ob++;
-		else
+	for (ob = 0; ob < max_region; ob++) {
+		dw_pcie_writel_atu_ob(pci, ob, PCIE_ATU_LOWER_TARGET, 0x11110000);
+		val = dw_pcie_readl_atu_ob(pci, ob, PCIE_ATU_LOWER_TARGET);
+		if (val != 0x11110000)
 			break;
 	}
 
-	for (i = 0; i < max_region; i++) {
-		dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, PCIE_ATU_REGION_DIR_IB | i);
-		dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, 0x11110000);
-		val = dw_pcie_readl_dbi(pci, PCIE_ATU_LOWER_TARGET);
-		if (val == 0x11110000)
-			ib++;
-		else
+	for (ib = 0; ib < max_region; ib++) {
+		dw_pcie_writel_atu_ib(pci, ib, PCIE_ATU_LOWER_TARGET, 0x11110000);
+		val = dw_pcie_readl_atu_ib(pci, ib, PCIE_ATU_LOWER_TARGET);
+		if (val != 0x11110000)
 			break;
 	}
 
@@ -691,12 +573,13 @@ void dw_pcie_iatu_detect(struct dw_pcie *pci)
 		if (!pci->atu_size)
 			/* Pick a minimal default, enough for 8 in and 8 out windows */
 			pci->atu_size = SZ_4K;
-
-		dw_pcie_iatu_detect_regions_unroll(pci);
 	} else {
-		dw_pcie_iatu_detect_regions(pci);
+		pci->atu_base = pci->dbi_base + PCIE_ATU_VIEWPORT_BASE;
+		pci->atu_size = PCIE_ATU_VIEWPORT_SIZE;
 	}
 
+	dw_pcie_iatu_detect_regions(pci);
+
 	dev_info(pci->dev, "iATU unroll: %s\n", pci->iatu_unroll_enabled ?
 		"enabled" : "disabled");
 
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index 0bda68f8ffdb..f5b793caabff 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -103,10 +103,21 @@
 #define PCIE_VERSION_NUMBER		0x8F8
 #define PCIE_VERSION_TYPE		0x8FC
 
+/*
+ * iATU inbound and outbound windows CSRs. Before the IP-core v4.80a each
+ * iATU region CSRs had been indirectly accessible by means of the dedicated
+ * viewport selector. The iATU/eDMA CSRs space was re-designed in DWC PCIe
+ * v4.80a in a way so the viewport was unrolled into the directly accessible
+ * iATU/eDMA CSRs space.
+ */
 #define PCIE_ATU_VIEWPORT		0x900
 #define PCIE_ATU_REGION_DIR_IB		BIT(31)
 #define PCIE_ATU_REGION_DIR_OB		0
-#define PCIE_ATU_CR1			0x904
+#define PCIE_ATU_VIEWPORT_BASE		0x904
+#define PCIE_ATU_UNROLL_BASE(dir, index) \
+	(((index) << 9) | ((dir == PCIE_ATU_REGION_DIR_IB) ? BIT(8) : 0))
+#define PCIE_ATU_VIEWPORT_SIZE		0x2C
+#define PCIE_ATU_REGION_CTRL1		0x000
 #define PCIE_ATU_INCREASE_REGION_SIZE	BIT(13)
 #define PCIE_ATU_TYPE_MEM		0x0
 #define PCIE_ATU_TYPE_IO		0x2
@@ -114,19 +125,19 @@
 #define PCIE_ATU_TYPE_CFG1		0x5
 #define PCIE_ATU_TD			BIT(8)
 #define PCIE_ATU_FUNC_NUM(pf)           ((pf) << 20)
-#define PCIE_ATU_CR2			0x908
+#define PCIE_ATU_REGION_CTRL2		0x004
 #define PCIE_ATU_ENABLE			BIT(31)
 #define PCIE_ATU_BAR_MODE_ENABLE	BIT(30)
 #define PCIE_ATU_FUNC_NUM_MATCH_EN      BIT(19)
-#define PCIE_ATU_LOWER_BASE		0x90C
-#define PCIE_ATU_UPPER_BASE		0x910
-#define PCIE_ATU_LIMIT			0x914
-#define PCIE_ATU_LOWER_TARGET		0x918
+#define PCIE_ATU_LOWER_BASE		0x008
+#define PCIE_ATU_UPPER_BASE		0x00C
+#define PCIE_ATU_LIMIT			0x010
+#define PCIE_ATU_LOWER_TARGET		0x014
 #define PCIE_ATU_BUS(x)			FIELD_PREP(GENMASK(31, 24), x)
 #define PCIE_ATU_DEV(x)			FIELD_PREP(GENMASK(23, 19), x)
 #define PCIE_ATU_FUNC(x)		FIELD_PREP(GENMASK(18, 16), x)
-#define PCIE_ATU_UPPER_TARGET		0x91C
-#define PCIE_ATU_UPPER_LIMIT		0x924
+#define PCIE_ATU_UPPER_TARGET		0x018
+#define PCIE_ATU_UPPER_LIMIT		0x020
 
 #define PCIE_MISC_CONTROL_1_OFF		0x8BC
 #define PCIE_DBI_RO_WR_EN		BIT(0)
@@ -143,19 +154,6 @@
 
 #define PCIE_PL_CHK_REG_ERR_ADDR			0xB28
 
-/*
- * iATU Unroll-specific register definitions
- * From 4.80 core version the address translation will be made by unroll
- */
-#define PCIE_ATU_UNR_REGION_CTRL1	0x00
-#define PCIE_ATU_UNR_REGION_CTRL2	0x04
-#define PCIE_ATU_UNR_LOWER_BASE		0x08
-#define PCIE_ATU_UNR_UPPER_BASE		0x0C
-#define PCIE_ATU_UNR_LOWER_LIMIT	0x10
-#define PCIE_ATU_UNR_LOWER_TARGET	0x14
-#define PCIE_ATU_UNR_UPPER_TARGET	0x18
-#define PCIE_ATU_UNR_UPPER_LIMIT	0x20
-
 /*
  * The default address offset between dbi_base and atu_base. Root controller
  * drivers are not required to initialize atu_base if the offset matches this
@@ -164,13 +162,6 @@
  */
 #define DEFAULT_DBI_ATU_OFFSET (0x3 << 20)
 
-/* Register address builder */
-#define PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(region) \
-		((region) << 9)
-
-#define PCIE_GET_ATU_INB_UNR_REG_OFFSET(region) \
-		(((region) << 9) | BIT(8))
-
 #define MAX_MSI_IRQS			256
 #define MAX_MSI_IRQS_PER_CTRL		32
 #define MAX_MSI_CTRLS			(MAX_MSI_IRQS / MAX_MSI_IRQS_PER_CTRL)
@@ -276,7 +267,6 @@ struct dw_pcie {
 	struct device		*dev;
 	void __iomem		*dbi_base;
 	void __iomem		*dbi_base2;
-	/* Used when iatu_unroll_enabled is true */
 	void __iomem		*atu_base;
 	size_t			atu_size;
 	u32			num_ib_windows;
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 12/17] PCI: dwc: Add iATU regions size detection procedure
  2022-05-03 21:46 [PATCH v2 00/17] PCI: dwc: Add dma-ranges/YAML-schema/Baikal-T1 support Serge Semin
                   ` (10 preceding siblings ...)
  2022-05-03 21:46 ` [PATCH v2 11/17] PCI: dwc: Simplify in/outbound iATU setup methods Serge Semin
@ 2022-05-03 21:46 ` Serge Semin
  2022-05-16 21:01   ` Rob Herring
  2022-05-03 21:46 ` [PATCH v2 13/17] PCI: dwc: Verify in/out regions against iATU constraints Serge Semin
                   ` (4 subsequent siblings)
  16 siblings, 1 reply; 49+ messages in thread
From: Serge Semin @ 2022-05-03 21:46 UTC (permalink / raw)
  To: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Rob Herring, Krzysztof Wilczyński
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Pavel Parkhomenko,
	Frank Li, Manivannan Sadhasivam, Rob Herring, linux-pci,
	devicetree, linux-kernel

Depending on the DWC PCIe RC/EP/DM IP-core configuration parameters the
controllers can be equipped not only with various number of inbound and
outbound iATU windows, but with varied regions settings like alignment
(which is also the minimum window size), minimum and maximum sizes. So to
speak if internal ATU is enabled for the denoted IP-cores then the former
settings will be defined by the CX_ATU_MIN_REGION_SIZE parameter while the
later one will be determined by the CX_ATU_MAX_REGION_SIZE configuration
parameter. Anyway having these parameters used in the driver will help to
verify whether the requested inbound or outbound memory mappings can be
fully created. Currently the driver doesn't perform any corresponding
checking.

Note 1. The extended iATU regions have been supported since DWC PCIe
v4.60a. There is no need in testing the upper limit register availability
for the older cores.

Note 2. The regions alignment is determined with using the fls() method
since the lower four bits of the ATU Limit register can be occupied with
the Circular Buffer Increment setting, which can be initialized with
zeros.

The (dma-)ranges verification will be added a bit later in one of the next
commits.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
 drivers/pci/controller/dwc/pcie-designware.c | 33 +++++++++++++++++---
 drivers/pci/controller/dwc/pcie-designware.h |  2 ++
 2 files changed, 31 insertions(+), 4 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index 0781fa145b3f..1cc5e3b2fa6d 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -8,9 +8,11 @@
  * Author: Jingoo Han <jg1.han@samsung.com>
  */
 
+#include <linux/bitops.h>
 #include <linux/delay.h>
 #include <linux/of.h>
 #include <linux/of_platform.h>
+#include <linux/sizes.h>
 #include <linux/types.h>
 
 #include "../../pci.h"
@@ -525,7 +527,8 @@ static bool dw_pcie_iatu_unroll_enabled(struct dw_pcie *pci)
 static void dw_pcie_iatu_detect_regions(struct dw_pcie *pci)
 {
 	int max_region, ob, ib;
-	u32 val;
+	u32 val, min, dir;
+	u64 max;
 
 	if (pci->iatu_unroll_enabled) {
 		max_region = min((int)pci->atu_size / 512, 256);
@@ -548,8 +551,29 @@ static void dw_pcie_iatu_detect_regions(struct dw_pcie *pci)
 			break;
 	}
 
-	pci->num_ib_windows = ib;
+	if (ob) {
+		dir = PCIE_ATU_REGION_DIR_OB;
+	} else if (ib) {
+		dir = PCIE_ATU_REGION_DIR_IB;
+	} else {
+		dev_err(pci->dev, "No iATU regions found\n");
+		return;
+	}
+
+	dw_pcie_writel_atu(pci, dir, 0, PCIE_ATU_LIMIT, 0x0);
+	min = dw_pcie_readl_atu(pci, dir, 0, PCIE_ATU_LIMIT);
+
+	if (dw_pcie_ver_is_ge(pci, 460A)) {
+		dw_pcie_writel_atu(pci, dir, 0, PCIE_ATU_UPPER_LIMIT, 0xFFFFFFFF);
+		max = dw_pcie_readl_atu(pci, dir, 0, PCIE_ATU_UPPER_LIMIT);
+	} else {
+		max = 0;
+	}
+
 	pci->num_ob_windows = ob;
+	pci->num_ib_windows = ib;
+	pci->region_align = 1 << fls(min);
+	pci->region_limit = (max << 32) | (SZ_4G - 1);
 }
 
 void dw_pcie_iatu_detect(struct dw_pcie *pci)
@@ -583,8 +607,9 @@ void dw_pcie_iatu_detect(struct dw_pcie *pci)
 	dev_info(pci->dev, "iATU unroll: %s\n", pci->iatu_unroll_enabled ?
 		"enabled" : "disabled");
 
-	dev_info(pci->dev, "Detected iATU regions: %u outbound, %u inbound\n",
-		 pci->num_ob_windows, pci->num_ib_windows);
+	dev_info(pci->dev, "iATU regions: %u ob, %u ib, align %uK, limit %lluG\n",
+		 pci->num_ob_windows, pci->num_ib_windows,
+		 pci->region_align / SZ_1K, (pci->region_limit + 1) / SZ_1G);
 }
 
 void dw_pcie_setup(struct dw_pcie *pci)
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index f5b793caabff..faf42f2b2ff3 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -271,6 +271,8 @@ struct dw_pcie {
 	size_t			atu_size;
 	u32			num_ib_windows;
 	u32			num_ob_windows;
+	u32			region_align;
+	u64			region_limit;
 	struct pcie_port	pp;
 	struct dw_pcie_ep	ep;
 	const struct dw_pcie_ops *ops;
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 13/17] PCI: dwc: Verify in/out regions against iATU constraints
  2022-05-03 21:46 [PATCH v2 00/17] PCI: dwc: Add dma-ranges/YAML-schema/Baikal-T1 support Serge Semin
                   ` (11 preceding siblings ...)
  2022-05-03 21:46 ` [PATCH v2 12/17] PCI: dwc: Add iATU regions size detection procedure Serge Semin
@ 2022-05-03 21:46 ` Serge Semin
  2022-05-16 21:07   ` Rob Herring
  2022-05-03 21:46 ` [PATCH v2 14/17] PCI: dwc: Check iATU in/outbound ranges setup methods status Serge Semin
                   ` (3 subsequent siblings)
  16 siblings, 1 reply; 49+ messages in thread
From: Serge Semin @ 2022-05-03 21:46 UTC (permalink / raw)
  To: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Rob Herring, Krzysztof Wilczyński
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Pavel Parkhomenko,
	Frank Li, Manivannan Sadhasivam, Rob Herring, linux-pci,
	devicetree, linux-kernel

Since the DWC PCIe driver private data now contains the iATU inbound and
outbound regions constraints info like alignment, minimum and maximum
limits, we can use them to make the in- and outbound iATU regions setup
methods more strict to the ranges a callee tries to specify.  That will
give us the safer dw_pcie_prog_outbound_atu(),
dw_pcie_prog_ep_outbound_atu() and dw_pcie_prog_inbound_atu() functions.

First of all let's update the outbound ATU entries setup methods to
returning the operation status. The methods will fail either in case if
the range is failed to be activated or the passed region doesn't fulfill
iATU constraints. Secondly the passed to the
dw_pcie_prog_{ep_}outbound_atu() methods region-related parameters are
verified against the detected iATU regions constraints. In particular the
region limit address must not overflow the lower/upper limit CSR RW-fields
otherwise the specified range will be just silently clamped. That
verification will also protect the code from having u64 type overflow.
Secondly let's make sure base address (CPU-address), target address
(PCI-address) and size are properly aligned. Unaligned ranges will be
silently aligned down (addresses) and up (limit) on writing the values to
the corresponding registers, which in it turn may lead to unpredictable
results like ranges virtual overlap. Finally the CPU-address alignment
needs to be verified in the dw_pcie_prog_inbound_atu() method too as the
DWC PCIe RC/EP registers manual demands seeing the lower bits of the in-
and outbound iATU base address are always zeros.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
 drivers/pci/controller/dwc/pcie-designware.c | 39 +++++++++++++-------
 drivers/pci/controller/dwc/pcie-designware.h | 10 ++---
 2 files changed, 30 insertions(+), 19 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index 1cc5e3b2fa6d..747e252c09e6 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -8,6 +8,7 @@
  * Author: Jingoo Han <jg1.han@samsung.com>
  */
 
+#include <linux/align.h>
 #include <linux/bitops.h>
 #include <linux/delay.h>
 #include <linux/of.h>
@@ -308,9 +309,9 @@ static inline u32 dw_pcie_enable_ecrc(u32 val)
 	return val | PCIE_ATU_TD;
 }
 
-static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
-					int index, int type, u64 cpu_addr,
-					u64 pci_addr, u64 size)
+static int __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
+				       int index, int type, u64 cpu_addr,
+				       u64 pci_addr, u64 size)
 {
 	u32 retries, val;
 	u64 limit_addr;
@@ -320,6 +321,13 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
 
 	limit_addr = cpu_addr + size - 1;
 
+	if ((limit_addr & ~pci->region_limit) != (cpu_addr & ~pci->region_limit) ||
+	    !IS_ALIGNED(cpu_addr, pci->region_align) ||
+	    !IS_ALIGNED(pci_addr, pci->region_align) ||
+	    !IS_ALIGNED(size, pci->region_align) || !size) {
+		return -EINVAL;
+	}
+
 	dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_LOWER_BASE,
 			      lower_32_bits(cpu_addr));
 	dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_UPPER_BASE,
@@ -353,27 +361,29 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
 	for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
 		val = dw_pcie_readl_atu_ob(pci, index, PCIE_ATU_REGION_CTRL2);
 		if (val & PCIE_ATU_ENABLE)
-			return;
+			return 0;
 
 		mdelay(LINK_WAIT_IATU);
 	}
 
 	dev_err(pci->dev, "Outbound iATU is not being enabled\n");
+
+	return -ETIMEDOUT;
 }
 
-void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type,
-			       u64 cpu_addr, u64 pci_addr, u64 size)
+int dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type,
+			      u64 cpu_addr, u64 pci_addr, u64 size)
 {
-	__dw_pcie_prog_outbound_atu(pci, 0, index, type,
-				    cpu_addr, pci_addr, size);
+	return __dw_pcie_prog_outbound_atu(pci, 0, index, type,
+					   cpu_addr, pci_addr, size);
 }
 
-void dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index,
-				  int type, u64 cpu_addr, u64 pci_addr,
-				  u64 size)
+int dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index,
+				 int type, u64 cpu_addr, u64 pci_addr,
+				 u64 size)
 {
-	__dw_pcie_prog_outbound_atu(pci, func_no, index, type,
-				    cpu_addr, pci_addr, size);
+	return __dw_pcie_prog_outbound_atu(pci, func_no, index, type,
+					   cpu_addr, pci_addr, size);
 }
 
 static inline u32 dw_pcie_readl_atu_ib(struct dw_pcie *pci, u32 index, u32 reg)
@@ -392,6 +402,9 @@ int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
 {
 	u32 retries, val;
 
+	if (!IS_ALIGNED(cpu_addr, pci->region_align))
+		return -EINVAL;
+
 	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_TARGET,
 			      lower_32_bits(cpu_addr));
 	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_TARGET,
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index faf42f2b2ff3..f0aa2c823b5d 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -304,12 +304,10 @@ void dw_pcie_write_dbi2(struct dw_pcie *pci, u32 reg, size_t size, u32 val);
 int dw_pcie_link_up(struct dw_pcie *pci);
 void dw_pcie_upconfig_setup(struct dw_pcie *pci);
 int dw_pcie_wait_for_link(struct dw_pcie *pci);
-void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index,
-			       int type, u64 cpu_addr, u64 pci_addr,
-			       u64 size);
-void dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index,
-				  int type, u64 cpu_addr, u64 pci_addr,
-				  u64 size);
+int dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type,
+			      u64 cpu_addr, u64 pci_addr, u64 size);
+int dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index,
+				 int type, u64 cpu_addr, u64 pci_addr, u64 size);
 int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
 			     int type, u64 cpu_addr, u8 bar);
 void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index);
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 14/17] PCI: dwc: Check iATU in/outbound ranges setup methods status
  2022-05-03 21:46 [PATCH v2 00/17] PCI: dwc: Add dma-ranges/YAML-schema/Baikal-T1 support Serge Semin
                   ` (12 preceding siblings ...)
  2022-05-03 21:46 ` [PATCH v2 13/17] PCI: dwc: Verify in/out regions against iATU constraints Serge Semin
@ 2022-05-03 21:46 ` Serge Semin
  2022-05-16 21:35   ` Rob Herring
  2022-05-03 21:46 ` [PATCH v2 15/17] PCI: dwc: Introduce dma-ranges property support for RC-host Serge Semin
                   ` (2 subsequent siblings)
  16 siblings, 1 reply; 49+ messages in thread
From: Serge Semin @ 2022-05-03 21:46 UTC (permalink / raw)
  To: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Rob Herring, Krzysztof Wilczyński, Rahul Tanwar
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Pavel Parkhomenko,
	Frank Li, Manivannan Sadhasivam, Rob Herring, linux-pci,
	devicetree, linux-kernel

Let's make the DWC PCIe RC/EP safer and more verbose for the invalid or
failed inbound and outbound iATU windows setups. Needless to say that
silently ignoring iATU regions setup errors may cause unpredictable
errors. For instance if for some reason a cfg or IO window fails to be
activated, then any CFG/IO requested won't reach target PCIe devices and
the corresponding accessors will return platform-specific random values.

First of all we need to convert dw_pcie_ep_outbound_atu() method to check
whether the specified outbound iATU range is successfully setup. That
method is called by the pci_epc_ops.map_addr callback. Thus we'll make the
EP-specific CPU->PCIe memory mappings saver.

Secondly since the iATU outbound range programming method now returns the
operation status, it will be handy to take that status into account in the
pci_ops.{map_bus,read,write} methods. Thus any failed mapping will be
immediately noticeable by the PCIe CFG operations requesters.

Finally we need to convert the dw_pcie_setup_rc() method to returning the
operation status, since the iATU outbound ranges setup procedure may now
fail. It will be especially handy in case if the DW PCIe RC DT-node has
invalid/unsupported (dma-)ranges property. Note since the suggested
modification causes having too wide code indentation, it is reasonable
from maintainability and readability points of view to move the outbound
ranges setup procedure in the separate function.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
 .../pci/controller/dwc/pcie-designware-ep.c   |   9 +-
 .../pci/controller/dwc/pcie-designware-host.c | 149 ++++++++++++------
 drivers/pci/controller/dwc/pcie-designware.h  |   5 +-
 drivers/pci/controller/dwc/pcie-intel-gw.c    |   6 +-
 4 files changed, 112 insertions(+), 57 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 68ad3d250450..c62640201246 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -184,8 +184,9 @@ static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, u8 func_no,
 				   phys_addr_t phys_addr,
 				   u64 pci_addr, size_t size)
 {
-	u32 free_win;
 	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+	u32 free_win;
+	int ret;
 
 	free_win = find_first_zero_bit(ep->ob_window_map, pci->num_ob_windows);
 	if (free_win >= pci->num_ob_windows) {
@@ -193,8 +194,10 @@ static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, u8 func_no,
 		return -EINVAL;
 	}
 
-	dw_pcie_prog_ep_outbound_atu(pci, func_no, free_win, PCIE_ATU_TYPE_MEM,
-				     phys_addr, pci_addr, size);
+	ret = dw_pcie_prog_ep_outbound_atu(pci, func_no, free_win, PCIE_ATU_TYPE_MEM,
+					   phys_addr, pci_addr, size);
+	if (ret)
+		return ret;
 
 	set_bit(free_win, ep->ob_window_map);
 	ep->outbound_addr[free_win] = phys_addr;
diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index ade31a04f9df..7caca6c575a5 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -411,7 +411,9 @@ int dw_pcie_host_init(struct pcie_port *pp)
 
 	dw_pcie_iatu_detect(pci);
 
-	dw_pcie_setup_rc(pp);
+	ret = dw_pcie_setup_rc(pp);
+	if (ret)
+		goto err_free_msi;
 
 	if (!dw_pcie_link_up(pci) && pci->ops && pci->ops->start_link) {
 		ret = pci->ops->start_link(pci);
@@ -467,10 +469,10 @@ EXPORT_SYMBOL_GPL(dw_pcie_host_deinit);
 static void __iomem *dw_pcie_other_conf_map_bus(struct pci_bus *bus,
 						unsigned int devfn, int where)
 {
-	int type;
-	u32 busdev;
 	struct pcie_port *pp = bus->sysdata;
 	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+	int type, ret;
+	u32 busdev;
 
 	/*
 	 * Checking whether the link is up here is a last line of defense
@@ -491,8 +493,10 @@ static void __iomem *dw_pcie_other_conf_map_bus(struct pci_bus *bus,
 	else
 		type = PCIE_ATU_TYPE_CFG1;
 
-
-	dw_pcie_prog_outbound_atu(pci, 0, type, pp->cfg0_base, busdev, pp->cfg0_size);
+	ret = dw_pcie_prog_outbound_atu(pci, 0, type, pp->cfg0_base, busdev,
+					pp->cfg0_size);
+	if (ret)
+		return NULL;
 
 	return pp->va_cfg0_base + where;
 }
@@ -505,12 +509,18 @@ static int dw_pcie_rd_other_conf(struct pci_bus *bus, unsigned int devfn,
 	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
 
 	ret = pci_generic_config_read(bus, devfn, where, size, val);
+	if (ret != PCIBIOS_SUCCESSFUL)
+		return ret;
 
-	if (!ret && pci->io_cfg_atu_shared)
-		dw_pcie_prog_outbound_atu(pci, 0, PCIE_ATU_TYPE_IO, pp->io_base,
-					  pp->io_bus_addr, pp->io_size);
+	if (pci->io_cfg_atu_shared) {
+		ret = dw_pcie_prog_outbound_atu(pci, 0, PCIE_ATU_TYPE_IO,
+						pp->io_base, pp->io_bus_addr,
+						pp->io_size);
+		if (ret)
+			return PCIBIOS_SET_FAILED;
+	}
 
-	return ret;
+	return PCIBIOS_SUCCESSFUL;
 }
 
 static int dw_pcie_wr_other_conf(struct pci_bus *bus, unsigned int devfn,
@@ -521,12 +531,18 @@ static int dw_pcie_wr_other_conf(struct pci_bus *bus, unsigned int devfn,
 	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
 
 	ret = pci_generic_config_write(bus, devfn, where, size, val);
+	if (ret != PCIBIOS_SUCCESSFUL)
+		return ret;
 
-	if (!ret && pci->io_cfg_atu_shared)
-		dw_pcie_prog_outbound_atu(pci, 0, PCIE_ATU_TYPE_IO, pp->io_base,
-					  pp->io_bus_addr, pp->io_size);
+	if (pci->io_cfg_atu_shared) {
+		ret = dw_pcie_prog_outbound_atu(pci, 0, PCIE_ATU_TYPE_IO,
+						pp->io_base, pp->io_bus_addr,
+						pp->io_size);
+		if (ret)
+			return PCIBIOS_SET_FAILED;
+	}
 
-	return ret;
+	return PCIBIOS_SUCCESSFUL;
 }
 
 static struct pci_ops dw_child_pcie_ops = {
@@ -583,10 +599,72 @@ static struct pci_ops dw_pcie_ops = {
 	.write = dw_pcie_wr_own_conf,
 };
 
-void dw_pcie_setup_rc(struct pcie_port *pp)
+static int dw_pcie_iatu_setup(struct pcie_port *pp)
+{
+	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+	struct resource_entry *entry;
+	int i, ret;
+
+	/* Note the very first outbound ATU is used for CFG IOs */
+	if (!pci->num_ob_windows) {
+		dev_err(pci->dev, "No outbound iATU found\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Ensure all outbound windows are disabled before proceeding with
+	 * the MEM/IO ranges setups.
+	 */
+	for (i = 0; i < pci->num_ob_windows; i++)
+		dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_OB, i);
+
+	i = 0;
+	resource_list_for_each_entry(entry, &pp->bridge->windows) {
+		if (resource_type(entry->res) != IORESOURCE_MEM)
+			continue;
+
+		if (pci->num_ob_windows <= ++i)
+			break;
+
+		ret = dw_pcie_prog_outbound_atu(pci, i, PCIE_ATU_TYPE_MEM,
+						entry->res->start,
+						entry->res->start - entry->offset,
+						resource_size(entry->res));
+		if (ret) {
+			dev_err(pci->dev, "Failed to set MEM range %pr\n",
+				entry->res);
+			return ret;
+		}
+	}
+
+	if (pp->io_size) {
+		if (pci->num_ob_windows > ++i) {
+			ret = dw_pcie_prog_outbound_atu(pci, i, PCIE_ATU_TYPE_IO,
+							pp->io_base,
+							pp->io_bus_addr,
+							pp->io_size);
+			if (ret) {
+				dev_err(pci->dev, "Failed to set IO range %pr\n",
+					entry->res);
+				return ret;
+			}
+		} else {
+			pci->io_cfg_atu_shared = true;
+		}
+	}
+
+	if (pci->num_ob_windows <= i)
+		dev_warn(pci->dev, "Resources exceed number of ATU entries (%d)\n",
+			 pci->num_ob_windows);
+
+	return 0;
+}
+
+int dw_pcie_setup_rc(struct pcie_port *pp)
 {
-	u32 val, ctrl, num_ctrls;
 	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+	u32 val, ctrl, num_ctrls;
+	int ret;
 
 	/*
 	 * Enable DBI read-only registers for writing/updating configuration.
@@ -641,42 +719,9 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
 	 * ATU, so we should not program the ATU here.
 	 */
 	if (pp->bridge->child_ops == &dw_child_pcie_ops) {
-		int i, atu_idx = 0;
-		struct resource_entry *entry;
-
-		/*
-		 * Ensure all outbound windows are disabled so there are
-		 * multiple matches
-		 */
-		for (i = 0; i < pci->num_ob_windows; i++)
-			dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_OB, i);
-
-		/* Get last memory resource entry */
-		resource_list_for_each_entry(entry, &pp->bridge->windows) {
-			if (resource_type(entry->res) != IORESOURCE_MEM)
-				continue;
-
-			if (pci->num_ob_windows <= ++atu_idx)
-				break;
-
-			dw_pcie_prog_outbound_atu(pci, atu_idx,
-						  PCIE_ATU_TYPE_MEM, entry->res->start,
-						  entry->res->start - entry->offset,
-						  resource_size(entry->res));
-		}
-
-		if (pp->io_size) {
-			if (pci->num_ob_windows > ++atu_idx)
-				dw_pcie_prog_outbound_atu(pci, atu_idx,
-							  PCIE_ATU_TYPE_IO, pp->io_base,
-							  pp->io_bus_addr, pp->io_size);
-			else
-				pci->io_cfg_atu_shared = true;
-		}
-
-		if (pci->num_ob_windows <= atu_idx)
-			dev_warn(pci->dev, "Resources exceed number of ATU entries (%d)\n",
-				 pci->num_ob_windows);
+		ret = dw_pcie_iatu_setup(pp);
+		if (ret)
+			return ret;
 	}
 
 	dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0);
@@ -689,5 +734,7 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
 	dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
 
 	dw_pcie_dbi_ro_wr_dis(pci);
+
+	return 0;
 }
 EXPORT_SYMBOL_GPL(dw_pcie_setup_rc);
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index f0aa2c823b5d..3f9527537403 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -373,7 +373,7 @@ static inline void dw_pcie_dbi_ro_wr_dis(struct dw_pcie *pci)
 
 #ifdef CONFIG_PCIE_DW_HOST
 irqreturn_t dw_handle_msi_irq(struct pcie_port *pp);
-void dw_pcie_setup_rc(struct pcie_port *pp);
+int dw_pcie_setup_rc(struct pcie_port *pp);
 int dw_pcie_host_init(struct pcie_port *pp);
 void dw_pcie_host_deinit(struct pcie_port *pp);
 int dw_pcie_allocate_domains(struct pcie_port *pp);
@@ -385,8 +385,9 @@ static inline irqreturn_t dw_handle_msi_irq(struct pcie_port *pp)
 	return IRQ_NONE;
 }
 
-static inline void dw_pcie_setup_rc(struct pcie_port *pp)
+static inline int dw_pcie_setup_rc(struct pcie_port *pp)
 {
+	return 0;
 }
 
 static inline int dw_pcie_host_init(struct pcie_port *pp)
diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c
index 786af2ba379f..c3bafaa803de 100644
--- a/drivers/pci/controller/dwc/pcie-intel-gw.c
+++ b/drivers/pci/controller/dwc/pcie-intel-gw.c
@@ -306,7 +306,11 @@ static int intel_pcie_host_setup(struct intel_pcie *pcie)
 	intel_pcie_ltssm_disable(pcie);
 	intel_pcie_link_setup(pcie);
 	intel_pcie_init_n_fts(pci);
-	dw_pcie_setup_rc(&pci->pp);
+
+	ret = dw_pcie_setup_rc(&pci->pp);
+	if (ret)
+		goto app_init_err;
+
 	dw_pcie_upconfig_setup(pci);
 
 	intel_pcie_device_rst_deassert(pcie);
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 15/17] PCI: dwc: Introduce dma-ranges property support for RC-host
  2022-05-03 21:46 [PATCH v2 00/17] PCI: dwc: Add dma-ranges/YAML-schema/Baikal-T1 support Serge Semin
                   ` (13 preceding siblings ...)
  2022-05-03 21:46 ` [PATCH v2 14/17] PCI: dwc: Check iATU in/outbound ranges setup methods status Serge Semin
@ 2022-05-03 21:46 ` Serge Semin
  2022-05-12 13:57   ` Manivannan Sadhasivam
  2022-05-16 21:42   ` Rob Herring
  2022-05-03 21:46 ` [PATCH v2 16/17] PCI: dwc: Introduce generic platform clocks and resets sets Serge Semin
  2022-05-03 21:46 ` [PATCH v2 17/17] PCI: dwc: Add Baikal-T1 PCIe controller support Serge Semin
  16 siblings, 2 replies; 49+ messages in thread
From: Serge Semin @ 2022-05-03 21:46 UTC (permalink / raw)
  To: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Rob Herring, Krzysztof Wilczyński
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Pavel Parkhomenko,
	Frank Li, Manivannan Sadhasivam, Rob Herring, linux-pci,
	devicetree, linux-kernel

In accordance with the generic PCIe Root Port DT-bindings the "dma-ranges"
property has the same format as the "ranges" property. The only difference
is in their semantics. The "dma-ranges" property describes the PCIe-to-CPU
memory mapping in opposite to the CPU-to-PCIe mapping of the "ranges"
property. Even though the DW PCIe controllers are normally equipped with
internal Address Translation Unit which inbound and outbound tables can be
used to implement both properties semantics, it was surprise for me to
discover that the host-related part of the DW PCIe driver currently
supports the "ranges" property only while the "dma-ranges" windows are
just ignored. Having the "dma-ranges" supported in the driver would be
very handy for the platforms, that don't tolerate the 1:1 CPU-PCIe memory
mapping and require customized the PCIe memory layout. So let's fix that
by introducing the "dma-ranges" property support.

First of all we suggest to rename the dw_pcie_prog_inbound_atu() method to
dw_pcie_prog_ep_inbound_atu() and create a new version of the
dw_pcie_prog_inbound_atu() function. Thus we'll have two methods for RC
and EP controllers respectively in the same way as it has been developed
for the outbound ATU setup methods.

Secondly aside with the memory window index and type the new
dw_pcie_prog_inbound_atu() function will accept CPU address, PCIe address
and size as its arguments. These parameters define the PCIe and CPU memory
ranges which will be used to setup the respective inbound ATU mapping. The
passed parameters need to be verified against the ATU ranges constraints
in the same way as it is done for the outbound ranges.

Finally the DMA-ranges detected for the PCIe controller need to be
converted into the inbound ATU entries during the host controller
initialization procedure. It will be done in the framework of the
dw_pcie_iatu_setup() method. Note before setting the inbound ranges up we
need to disable all the inbound ATU entries in order to prevent unexpected
PCIe TLPs translations defined by some third party software like
bootloader.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
 .../pci/controller/dwc/pcie-designware-ep.c   |  4 +-
 .../pci/controller/dwc/pcie-designware-host.c | 32 ++++++++++-
 drivers/pci/controller/dwc/pcie-designware.c  | 57 ++++++++++++++++++-
 drivers/pci/controller/dwc/pcie-designware.h  |  6 +-
 4 files changed, 90 insertions(+), 9 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index c62640201246..9b0540cfa9e8 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -167,8 +167,8 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type,
 		return -EINVAL;
 	}
 
-	ret = dw_pcie_prog_inbound_atu(pci, func_no, free_win, type,
-				       cpu_addr, bar);
+	ret = dw_pcie_prog_ep_inbound_atu(pci, func_no, free_win, type,
+					  cpu_addr, bar);
 	if (ret < 0) {
 		dev_err(pci->dev, "Failed to program IB window\n");
 		return ret;
diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index 7caca6c575a5..9cb406f5c185 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -612,12 +612,15 @@ static int dw_pcie_iatu_setup(struct pcie_port *pp)
 	}
 
 	/*
-	 * Ensure all outbound windows are disabled before proceeding with
-	 * the MEM/IO ranges setups.
+	 * Ensure all out/inbound windows are disabled before proceeding with
+	 * the MEM/IO (dma-)ranges setups.
 	 */
 	for (i = 0; i < pci->num_ob_windows; i++)
 		dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_OB, i);
 
+	for (i = 0; i < pci->num_ib_windows; i++)
+		dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, i);
+
 	i = 0;
 	resource_list_for_each_entry(entry, &pp->bridge->windows) {
 		if (resource_type(entry->res) != IORESOURCE_MEM)
@@ -654,9 +657,32 @@ static int dw_pcie_iatu_setup(struct pcie_port *pp)
 	}
 
 	if (pci->num_ob_windows <= i)
-		dev_warn(pci->dev, "Resources exceed number of ATU entries (%d)\n",
+		dev_warn(pci->dev, "Ranges exceed outbound iATU size (%d)\n",
 			 pci->num_ob_windows);
 
+	i = 0;
+	resource_list_for_each_entry(entry, &pp->bridge->dma_ranges) {
+		if (resource_type(entry->res) != IORESOURCE_MEM)
+			continue;
+
+		if (pci->num_ib_windows <= i)
+			break;
+
+		ret = dw_pcie_prog_inbound_atu(pci, i++, PCIE_ATU_TYPE_MEM,
+					       entry->res->start,
+					       entry->res->start - entry->offset,
+					       resource_size(entry->res));
+		if (ret) {
+			dev_err(pci->dev, "Failed to set DMA range %pr\n",
+				entry->res);
+			return ret;
+		}
+	}
+
+	if (pci->num_ib_windows <= i)
+		dev_warn(pci->dev, "Dma-ranges exceed inbound iATU size (%u)\n",
+			 pci->num_ib_windows);
+
 	return 0;
 }
 
diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index 747e252c09e6..33718ed6c511 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -397,8 +397,61 @@ static inline void dw_pcie_writel_atu_ib(struct dw_pcie *pci, u32 index, u32 reg
 	dw_pcie_writel_atu(pci, PCIE_ATU_REGION_DIR_IB, index, reg, val);
 }
 
-int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
-			     int type, u64 cpu_addr, u8 bar)
+int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,
+			     u64 cpu_addr, u64 pci_addr, u64 size)
+{
+	u64 limit_addr = pci_addr + size - 1;
+	u32 retries, val;
+
+	if ((limit_addr & ~pci->region_limit) != (pci_addr & ~pci->region_limit) ||
+	    !IS_ALIGNED(cpu_addr, pci->region_align) ||
+	    !IS_ALIGNED(pci_addr, pci->region_align) ||
+	    !IS_ALIGNED(size, pci->region_align) || !size) {
+		return -EINVAL;
+	}
+
+	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_BASE,
+			      lower_32_bits(pci_addr));
+	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_BASE,
+			      upper_32_bits(pci_addr));
+
+	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LIMIT,
+			      lower_32_bits(limit_addr));
+	if (dw_pcie_ver_is_ge(pci, 460A))
+		dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_LIMIT,
+				      upper_32_bits(limit_addr));
+
+	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_TARGET,
+			      lower_32_bits(cpu_addr));
+	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_TARGET,
+			      upper_32_bits(cpu_addr));
+
+	val = type;
+	if (upper_32_bits(limit_addr) > upper_32_bits(pci_addr) &&
+	    dw_pcie_ver_is_ge(pci, 460A))
+		val |= PCIE_ATU_INCREASE_REGION_SIZE;
+	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL1, val);
+	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2, PCIE_ATU_ENABLE);
+
+	/*
+	 * Make sure ATU enable takes effect before any subsequent config
+	 * and I/O accesses.
+	 */
+	for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
+		val = dw_pcie_readl_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2);
+		if (val & PCIE_ATU_ENABLE)
+			return 0;
+
+		mdelay(LINK_WAIT_IATU);
+	}
+
+	dev_err(pci->dev, "Inbound iATU is not being enabled\n");
+
+	return -ETIMEDOUT;
+}
+
+int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
+				int type, u64 cpu_addr, u8 bar)
 {
 	u32 retries, val;
 
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index 3f9527537403..03de8f20a2cc 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -308,8 +308,10 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type,
 			      u64 cpu_addr, u64 pci_addr, u64 size);
 int dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index,
 				 int type, u64 cpu_addr, u64 pci_addr, u64 size);
-int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
-			     int type, u64 cpu_addr, u8 bar);
+int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,
+			     u64 cpu_addr, u64 pci_addr, u64 size);
+int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
+				int type, u64 cpu_addr, u8 bar);
 void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index);
 void dw_pcie_setup(struct dw_pcie *pci);
 void dw_pcie_iatu_detect(struct dw_pcie *pci);
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 16/17] PCI: dwc: Introduce generic platform clocks and resets sets
  2022-05-03 21:46 [PATCH v2 00/17] PCI: dwc: Add dma-ranges/YAML-schema/Baikal-T1 support Serge Semin
                   ` (14 preceding siblings ...)
  2022-05-03 21:46 ` [PATCH v2 15/17] PCI: dwc: Introduce dma-ranges property support for RC-host Serge Semin
@ 2022-05-03 21:46 ` Serge Semin
  2022-05-16 22:29   ` Rob Herring
  2022-05-03 21:46 ` [PATCH v2 17/17] PCI: dwc: Add Baikal-T1 PCIe controller support Serge Semin
  16 siblings, 1 reply; 49+ messages in thread
From: Serge Semin @ 2022-05-03 21:46 UTC (permalink / raw)
  To: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Rob Herring, Krzysztof Wilczyński
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Pavel Parkhomenko,
	Frank Li, Manivannan Sadhasivam, Rob Herring, linux-pci,
	devicetree, linux-kernel

Currently almost each platform driver uses its own resets and clocks
naming in order to get the corresponding descriptors. It makes the code
harder to maintain and comprehend especially seeing the DWC PCIe core main
resets and clocks signals set hasn't changed much for about at least one
major IP-core release. So in order to organize things around these signals
we suggest to create a generic interface for them in accordance with the
naming introduced in the DWC PCIe IP-core reference manual:

Clocks:
- DBI - data bus interface clock (on some DWC PCIe platforms it's
  referred as "pclk", "pcie", "sys", "ahb", "cfg", "iface", "gio", "reg",
  "pcie_apb_sys");
- MSTR - AXI-bus master interface clock (some DWC PCIe glue drivers refer
  to this clock as "port", "bus", "pcie_bus",
  "bus_master/master_bus/axi_m", "pcie_aclk");
- SLV - AXI-bus slave interface clock (also called as "port", "bus",
  "pcie_bus", "bus_slave/slave_bus/axi_s", "pcie_aclk",
  "pcie_inbound_axi");
- PIPE - Core-PCS PIPE interface clock coming from external PHY (it's
  normally named by the platform drivers as just "pipe")
- CORE - primary clock of the controller (none of the platform drivers
  declare such a clock but in accordance with the ref. manual the devices
  may have it separately specified);
- AUX - Auxiliary PMC domain clock (it is named by some platforms as
  "pcie_aux" and just "aux")
- REF - Generic reference clock (it is a generic clock source, which can
  be used as a signal source for multiple interfaces, some platforms call
  it as "ref", "general", "pcie_phy", "pcie_phy_ref").

Application resets:
- DBI - Data-bus interface reset (it's CSR interface clock and is normally
  called as "apb" though technically it's not APB but DWC PCIe-specific
  interface);
  apb, sys,
- MSTR -AXI-bus master reset (some platforms call it as "port", "apps",
  "bus", "axi_m");
- SLV - ABI-bus slave reset (some platforms call it as "port", "apps",
  "bus", "axi_s").

Core resets:
- NON_STICKY - Non-sticky CSR flags reset;
- STICKY - sticky CSR flags reset;
- PIPE - PIPE-interface (Core-PCS) logic reset (some platforms call it
  just "pipe");
- CORE - controller primary reset (resets everything except PMC module,
  some platforms refer to this signal as "soft", "pci");
- PHY - PCS/PHY block reset (strictly speaking it is normally connected to
  the out of the external block, but the reference manual says it must be
  available for the PMC working correctly, some existing platforms call it
  as "pciephy", "phy", "link");
- HOT - PMC hot reset signal (also called as sleep");
- PWR - cold reset signal (can be referred as "pwr", "turnoff").

As you can see each platform uses it's own naming for basically the same
set of the signals. In the framework of this commit we suggest to add a
set of the clocks and signals identifiers and corresponding names for each
denoted entity. The platforms will be able to use them to define local
mapping tables between the generic identifiers and the available set of
the clocks and resets. The tables can be then utilized to create the
corresponding bulk-arrays, which in its turn can be passed to the
clock/reset-bulk API methods to easily get/enable/disable/put,
get/reset/assert/deassert/put all the handlers at once or, if it's
required, manipulate with the handlers individually.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
 drivers/pci/controller/dwc/pcie-designware.h | 79 ++++++++++++++++++++
 1 file changed, 79 insertions(+)

diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index 03de8f20a2cc..765d99d5bfaa 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -183,6 +183,35 @@ enum dw_pcie_device_mode {
 	DW_PCIE_RC_TYPE,
 };
 
+enum dw_pcie_clk {
+	DW_PCIE_DBI_CLK,
+	DW_PCIE_MSTR_CLK,
+	DW_PCIE_SLV_CLK,
+	DW_PCIE_PIPE_CLK,
+	DW_PCIE_CORE_CLK,
+	DW_PCIE_AUX_CLK,
+	DW_PCIE_REF_CLK,
+	DW_PCIE_NUM_CLKS
+};
+
+enum dw_pcie_app_rst {
+	DW_PCIE_DBI_RST,
+	DW_PCIE_MSTR_RST,
+	DW_PCIE_SLV_RST,
+	DW_PCIE_NUM_APP_RSTS
+};
+
+enum dw_pcie_core_rst {
+	DW_PCIE_NON_STICKY_RST,
+	DW_PCIE_STICKY_RST,
+	DW_PCIE_CORE_RST,
+	DW_PCIE_PIPE_RST,
+	DW_PCIE_PHY_RST,
+	DW_PCIE_HOT_RST,
+	DW_PCIE_PWR_RST,
+	DW_PCIE_NUM_CORE_RSTS
+};
+
 struct dw_pcie_host_ops {
 	int (*host_init)(struct pcie_port *pp);
 	void (*host_deinit)(struct pcie_port *pp);
@@ -373,6 +402,56 @@ static inline void dw_pcie_dbi_ro_wr_dis(struct dw_pcie *pci)
 	dw_pcie_writel_dbi(pci, reg, val);
 }
 
+static inline const char *dw_pcie_clk_name(enum dw_pcie_clk id)
+{
+	static const char *names[DW_PCIE_NUM_CLKS] = {
+		[DW_PCIE_DBI_CLK] = "dbi",
+		[DW_PCIE_MSTR_CLK] = "mstr",
+		[DW_PCIE_SLV_CLK] = "slv",
+		[DW_PCIE_PIPE_CLK] = "pipe",
+		[DW_PCIE_CORE_CLK] = "core",
+		[DW_PCIE_AUX_CLK] = "aux",
+		[DW_PCIE_REF_CLK] = "ref",
+	};
+
+	if (id >= DW_PCIE_NUM_CLKS)
+		return NULL;
+
+	return names[id];
+}
+
+static inline const char *dw_pcie_app_rst_name(enum dw_pcie_app_rst id)
+{
+	static const char *names[DW_PCIE_NUM_APP_RSTS] = {
+		[DW_PCIE_DBI_RST] = "dbi",
+		[DW_PCIE_MSTR_RST] = "mstr",
+		[DW_PCIE_SLV_RST] = "slv",
+	};
+
+	if (id >= DW_PCIE_NUM_APP_RSTS)
+		return NULL;
+
+	return names[id];
+}
+
+static inline const char *dw_pcie_core_rst_name(enum dw_pcie_core_rst id)
+{
+	static const char *names[DW_PCIE_NUM_CORE_RSTS] = {
+		[DW_PCIE_NON_STICKY_RST] = "non-sticky",
+		[DW_PCIE_STICKY_RST] = "sticky",
+		[DW_PCIE_CORE_RST] = "core",
+		[DW_PCIE_PIPE_RST] = "pipe",
+		[DW_PCIE_PHY_RST] = "phy",
+		[DW_PCIE_HOT_RST] = "hot",
+		[DW_PCIE_PWR_RST] = "pwr",
+	};
+
+	if (id >= DW_PCIE_NUM_CORE_RSTS)
+		return NULL;
+
+	return names[id];
+}
+
 #ifdef CONFIG_PCIE_DW_HOST
 irqreturn_t dw_handle_msi_irq(struct pcie_port *pp);
 int dw_pcie_setup_rc(struct pcie_port *pp);
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 17/17] PCI: dwc: Add Baikal-T1 PCIe controller support
  2022-05-03 21:46 [PATCH v2 00/17] PCI: dwc: Add dma-ranges/YAML-schema/Baikal-T1 support Serge Semin
                   ` (15 preceding siblings ...)
  2022-05-03 21:46 ` [PATCH v2 16/17] PCI: dwc: Introduce generic platform clocks and resets sets Serge Semin
@ 2022-05-03 21:46 ` Serge Semin
  16 siblings, 0 replies; 49+ messages in thread
From: Serge Semin @ 2022-05-03 21:46 UTC (permalink / raw)
  To: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Rob Herring, Krzysztof Wilczyński
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Pavel Parkhomenko,
	Frank Li, Manivannan Sadhasivam, Rob Herring, linux-pci,
	devicetree, linux-kernel

Baikal-T1 SoC is equipped with DWC PCIe v4.60a host controller. It can be
trained to work up to Gen.3 speed over up to x4 lanes. The host controller
is attached to the DW PCIe 3.0 PCS via the PIPE-4 interface, which in its
turn is connected to the DWC 10G PHY. The whole system is supposed to be
fed up with four clock sources: DBI peripheral clock, AXI application
clocks and external PHY/core reference clock generating the 100MHz signal.
In addition to that the platform provide a way to reset each part of the
controller: sticky/non-sticky bits, host controller core, PIPE interface,
PCS/PHY and Hot/Power reset signal. The driver also provides a way to
handle the GPIO-based PERST# signal.

Note due to the Baikal-T1 MMIO peculiarity we have to implement the DBI
interface accessors which make sure the IO operations are dword-aligned.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>

---

Changelog v2:
- Rename 'syscon' property to 'baikal,bt1-syscon'.
---
 drivers/pci/controller/dwc/Kconfig    |   9 +
 drivers/pci/controller/dwc/Makefile   |   1 +
 drivers/pci/controller/dwc/pcie-bt1.c | 639 ++++++++++++++++++++++++++
 3 files changed, 649 insertions(+)
 create mode 100644 drivers/pci/controller/dwc/pcie-bt1.c

diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig
index 62ce3abf0f19..771b8b146623 100644
--- a/drivers/pci/controller/dwc/Kconfig
+++ b/drivers/pci/controller/dwc/Kconfig
@@ -222,6 +222,15 @@ config PCIE_ARTPEC6_EP
 	  Enables support for the PCIe controller in the ARTPEC-6 SoC to work in
 	  endpoint mode. This uses the DesignWare core.
 
+config PCIE_BT1
+	tristate "Baikal-T1 PCIe controller"
+	depends on MIPS_BAIKAL_T1 || COMPILE_TEST
+	depends on PCI_MSI_IRQ_DOMAIN
+	select PCIE_DW_HOST
+	help
+	  Enables support for the PCIe controller in the Baikal-T1 SoC to work
+	  in host mode. It's based on the Synopsys DWC PCIe v4.60a IP-core.
+
 config PCIE_ROCKCHIP_DW_HOST
 	bool "Rockchip DesignWare PCIe controller"
 	select PCIE_DW
diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile
index 8ba7b67f5e50..bf5c311875a1 100644
--- a/drivers/pci/controller/dwc/Makefile
+++ b/drivers/pci/controller/dwc/Makefile
@@ -3,6 +3,7 @@ obj-$(CONFIG_PCIE_DW) += pcie-designware.o
 obj-$(CONFIG_PCIE_DW_HOST) += pcie-designware-host.o
 obj-$(CONFIG_PCIE_DW_EP) += pcie-designware-ep.o
 obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o
+obj-$(CONFIG_PCIE_BT1) += pcie-bt1.o
 obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o
 obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
 obj-$(CONFIG_PCIE_FU740) += pcie-fu740.o
diff --git a/drivers/pci/controller/dwc/pcie-bt1.c b/drivers/pci/controller/dwc/pcie-bt1.c
new file mode 100644
index 000000000000..66ed90603ffb
--- /dev/null
+++ b/drivers/pci/controller/dwc/pcie-bt1.c
@@ -0,0 +1,639 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2021 BAIKAL ELECTRONICS, JSC
+ *
+ * Authors:
+ *   Vadim Vlasov <Vadim.Vlasov@baikalelectronics.ru>
+ *   Serge Semin <Sergey.Semin@baikalelectronics.ru>
+ *
+ * Baikal-T1 PCIe controller driver
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/types.h>
+
+#include "pcie-designware.h"
+
+/* Baikal-T1 System CCU control registers */
+#define BT1_CCU_PCIE_CLKC			0x140
+#define BT1_CCU_PCIE_REQ_PCS_CLK		BIT(16)
+#define BT1_CCU_PCIE_REQ_MAC_CLK		BIT(17)
+#define BT1_CCU_PCIE_REQ_PIPE_CLK		BIT(18)
+
+#define BT1_CCU_PCIE_RSTC			0x144
+#define BT1_CCU_PCIE_REQ_LINK_RST		BIT(13)
+#define BT1_CCU_PCIE_REQ_SMLH_RST		BIT(14)
+#define BT1_CCU_PCIE_REQ_PHY_RST		BIT(16)
+#define BT1_CCU_PCIE_REQ_CORE_RST		BIT(24)
+#define BT1_CCU_PCIE_REQ_STICKY_RST		BIT(26)
+#define BT1_CCU_PCIE_REQ_NSTICKY_RST		BIT(27)
+
+#define BT1_CCU_PCIE_PMSC			0x148
+#define BT1_CCU_PCIE_LTSSM_STATE_MASK		GENMASK(5, 0)
+#define BT1_CCU_PCIE_LTSSM_DET_QUIET		0x00
+#define BT1_CCU_PCIE_LTSSM_DET_ACT		0x01
+#define BT1_CCU_PCIE_LTSSM_POLL_ACT		0x02
+#define BT1_CCU_PCIE_LTSSM_POLL_COMP		0x03
+#define BT1_CCU_PCIE_LTSSM_POLL_CONF		0x04
+#define BT1_CCU_PCIE_LTSSM_PRE_DET_QUIET	0x05
+#define BT1_CCU_PCIE_LTSSM_DET_WAIT		0x06
+#define BT1_CCU_PCIE_LTSSM_CFG_LNKWD_START	0x07
+#define BT1_CCU_PCIE_LTSSM_CFG_LNKWD_ACEPT	0x08
+#define BT1_CCU_PCIE_LTSSM_CFG_LNNUM_WAIT	0x09
+#define BT1_CCU_PCIE_LTSSM_CFG_LNNUM_ACEPT	0x0a
+#define BT1_CCU_PCIE_LTSSM_CFG_COMPLETE		0x0b
+#define BT1_CCU_PCIE_LTSSM_CFG_IDLE		0x0c
+#define BT1_CCU_PCIE_LTSSM_RCVR_LOCK		0x0d
+#define BT1_CCU_PCIE_LTSSM_RCVR_SPEED		0x0e
+#define BT1_CCU_PCIE_LTSSM_RCVR_RCVRCFG		0x0f
+#define BT1_CCU_PCIE_LTSSM_RCVR_IDLE		0x10
+#define BT1_CCU_PCIE_LTSSM_L0			0x11
+#define BT1_CCU_PCIE_LTSSM_L0S			0x12
+#define BT1_CCU_PCIE_LTSSM_L123_SEND_IDLE	0x13
+#define BT1_CCU_PCIE_LTSSM_L1_IDLE		0x14
+#define BT1_CCU_PCIE_LTSSM_L2_IDLE		0x15
+#define BT1_CCU_PCIE_LTSSM_L2_WAKE		0x16
+#define BT1_CCU_PCIE_LTSSM_DIS_ENTRY		0x17
+#define BT1_CCU_PCIE_LTSSM_DIS_IDLE		0x18
+#define BT1_CCU_PCIE_LTSSM_DISABLE		0x19
+#define BT1_CCU_PCIE_LTSSM_LPBK_ENTRY		0x1a
+#define BT1_CCU_PCIE_LTSSM_LPBK_ACTIVE		0x1b
+#define BT1_CCU_PCIE_LTSSM_LPBK_EXIT		0x1c
+#define BT1_CCU_PCIE_LTSSM_LPBK_EXIT_TOUT	0x1d
+#define BT1_CCU_PCIE_LTSSM_HOT_RST_ENTRY	0x1e
+#define BT1_CCU_PCIE_LTSSM_HOT_RST		0x1f
+#define BT1_CCU_PCIE_LTSSM_RCVR_EQ0		0x20
+#define BT1_CCU_PCIE_LTSSM_RCVR_EQ1		0x21
+#define BT1_CCU_PCIE_LTSSM_RCVR_EQ2		0x22
+#define BT1_CCU_PCIE_LTSSM_RCVR_EQ3		0x23
+#define BT1_CCU_PCIE_SMLH_LINKUP		BIT(6)
+#define BT1_CCU_PCIE_RDLH_LINKUP		BIT(7)
+#define BT1_CCU_PCIE_PM_LINKSTATE_L0S		BIT(8)
+#define BT1_CCU_PCIE_PM_LINKSTATE_L1		BIT(9)
+#define BT1_CCU_PCIE_PM_LINKSTATE_L2		BIT(10)
+#define BT1_CCU_PCIE_L1_PENDING			BIT(12)
+#define BT1_CCU_PCIE_REQ_EXIT_L1		BIT(14)
+#define BT1_CCU_PCIE_LTSSM_RCVR_EQ		BIT(15)
+#define BT1_CCU_PCIE_PM_DSTAT_MASK		GENMASK(18, 16)
+#define BT1_CCU_PCIE_PM_PME_EN			BIT(20)
+#define BT1_CCU_PCIE_PM_PME_STATUS		BIT(21)
+#define BT1_CCU_PCIE_AUX_PM_EN			BIT(22)
+#define BT1_CCU_PCIE_AUX_PWR_DET		BIT(23)
+#define BT1_CCU_PCIE_WAKE_DET			BIT(24)
+#define BT1_CCU_PCIE_TURNOFF_REQ		BIT(30)
+#define BT1_CCU_PCIE_TURNOFF_ACK		BIT(31)
+
+#define BT1_CCU_PCIE_GENC			0x14c
+#define BT1_CCU_PCIE_LTSSM_EN			BIT(1)
+#define BT1_CCU_PCIE_DBI2_MODE			BIT(2)
+#define BT1_CCU_PCIE_MGMT_EN			BIT(3)
+#define BT1_CCU_PCIE_RXLANE_FLIP_EN		BIT(16)
+#define BT1_CCU_PCIE_TXLANE_FLIP_EN		BIT(17)
+#define BT1_CCU_PCIE_SLV_XFER_PEND		BIT(24)
+#define BT1_CCU_PCIE_RCV_XFER_PEND		BIT(25)
+#define BT1_CCU_PCIE_DBI_XFER_PEND		BIT(26)
+#define BT1_CCU_PCIE_DMA_XFER_PEND		BIT(27)
+
+#define BT1_CCU_PCIE_LTSSM_LINKUP(_pmsc) \
+({ \
+	int __state = FIELD_GET(BT1_CCU_PCIE_LTSSM_STATE_MASK, _pmsc); \
+	__state >= BT1_CCU_PCIE_LTSSM_L0 && __state <= BT1_CCU_PCIE_LTSSM_L2_WAKE; \
+})
+
+/* Baikal-T1 PCIe specific control registers */
+#define BT1_PCIE_AXI2MGM_LANENUM		0xd04
+#define BT1_PCIE_AXI2MGM_LANESEL_MASK		GENMASK(3, 0)
+
+#define BT1_PCIE_AXI2MGM_ADDRCTL		0xd08
+#define BT1_PCIE_AXI2MGM_PHYREG_ADDR_MASK	GENMASK(20, 0)
+#define BT1_PCIE_AXI2MGM_READ_FLAG		BIT(29)
+#define BT1_PCIE_AXI2MGM_DONE			BIT(30)
+#define BT1_PCIE_AXI2MGM_BUSY			BIT(31)
+
+#define BT1_PCIE_AXI2MGM_WRITEDATA		0xd0c
+#define BT1_PCIE_AXI2MGM_WDATA			GENMASK(15, 0)
+
+#define BT1_PCIE_AXI2MGM_READDATA		0xd10
+#define BT1_PCIE_AXI2MGM_RDATA			GENMASK(15, 0)
+
+/* General Baikal-T1 PCIe interface resources */
+#define BT1_PCIE_NUM_CLKS			ARRAY_SIZE(bt1_pcie_clks)
+#define BT1_PCIE_NUM_APP_RSTS			ARRAY_SIZE(bt1_pcie_app_rsts)
+#define BT1_PCIE_NUM_CORE_RSTS			ARRAY_SIZE(bt1_pcie_core_rsts)
+
+enum bt1_pcie_core_rst {
+	BT1_PCIE_NON_STICKY_RST,
+	BT1_PCIE_STICKY_RST,
+	BT1_PCIE_CORE_RST,
+	BT1_PCIE_PIPE_RST,
+	BT1_PCIE_PHY_RST,
+	BT1_PCIE_HOT_RST,
+	BT1_PCIE_PWR_RST,
+};
+
+static const enum dw_pcie_clk bt1_pcie_clks[] = {
+	DW_PCIE_DBI_CLK, DW_PCIE_MSTR_CLK, DW_PCIE_SLV_CLK, DW_PCIE_REF_CLK
+};
+
+static const enum dw_pcie_app_rst bt1_pcie_app_rsts[] = {
+	DW_PCIE_MSTR_RST, DW_PCIE_SLV_RST
+};
+
+static const enum dw_pcie_core_rst bt1_pcie_core_rsts[] = {
+	[BT1_PCIE_NON_STICKY_RST] = DW_PCIE_NON_STICKY_RST,
+	[BT1_PCIE_STICKY_RST] = DW_PCIE_STICKY_RST,
+	[BT1_PCIE_CORE_RST] = DW_PCIE_CORE_RST,
+	[BT1_PCIE_PIPE_RST] = DW_PCIE_PIPE_RST,
+	[BT1_PCIE_PHY_RST] = DW_PCIE_PHY_RST,
+	[BT1_PCIE_HOT_RST] = DW_PCIE_HOT_RST,
+	[BT1_PCIE_PWR_RST] = DW_PCIE_PWR_RST,
+};
+
+struct bt1_pcie {
+	struct dw_pcie dw;
+	struct platform_device *pdev;
+	struct regmap *sys_regs;
+
+	struct clk_bulk_data clks[BT1_PCIE_NUM_CLKS];
+	struct reset_control_bulk_data app_rsts[BT1_PCIE_NUM_APP_RSTS];
+	struct reset_control_bulk_data core_rsts[BT1_PCIE_NUM_CORE_RSTS];
+	struct gpio_desc *pe_rst;
+};
+#define to_bt1_pcie(_dw) container_of(_dw, struct bt1_pcie, dw)
+
+/*
+ * Baikal-T1 MMIO space must be read/written by the dword-aligned
+ * instructions. Note the methods are optimized to have the dword operations
+ * performed with minimum overhead as the most frequently used ones.
+ */
+static int bt1_pcie_read_mmio(void __iomem *addr, int size, u32 *val)
+{
+	unsigned int ofs = (uintptr_t)addr & 0x3;
+
+	if (!IS_ALIGNED((uintptr_t)addr, size))
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+
+	*val = readl(addr - ofs) >> ofs * BITS_PER_BYTE;
+	if (size == 4) {
+		return PCIBIOS_SUCCESSFUL;
+	} else if (size == 2) {
+		*val &= 0xffff;
+		return PCIBIOS_SUCCESSFUL;
+	} else if (size == 1) {
+		*val &= 0xff;
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	return PCIBIOS_BAD_REGISTER_NUMBER;
+}
+
+static int bt1_pcie_write_mmio(void __iomem *addr, int size, u32 val)
+{
+	unsigned int ofs = (uintptr_t)addr & 0x3;
+	u32 tmp, mask;
+
+	if (!IS_ALIGNED((uintptr_t)addr, size))
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+
+	if (size == 4) {
+		writel(val, addr);
+		return PCIBIOS_SUCCESSFUL;
+	} else if (size == 2 || size == 1) {
+		mask = GENMASK(size * BITS_PER_BYTE - 1, 0);
+		tmp = readl(addr - ofs) & ~(mask << ofs * BITS_PER_BYTE);
+		tmp |= (val & mask) << ofs * BITS_PER_BYTE;
+		writel(tmp, addr - ofs);
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	return PCIBIOS_BAD_REGISTER_NUMBER;
+}
+
+static u32 bt1_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg,
+			     size_t size)
+{
+	int ret;
+	u32 val;
+
+	ret = bt1_pcie_read_mmio(base + reg, size, &val);
+	if (ret != PCIBIOS_SUCCESSFUL) {
+		dev_err(pci->dev, "Read DBI address failed\n");
+		return ~0U;
+	}
+
+	return val;
+}
+
+static void bt1_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg,
+			       size_t size, u32 val)
+{
+	int ret;
+
+	ret = bt1_pcie_write_mmio(base + reg, size, val);
+	if (ret != PCIBIOS_SUCCESSFUL)
+		dev_err(pci->dev, "Write DBI address failed\n");
+}
+
+static void bt1_pcie_write_dbi2(struct dw_pcie *pci, void __iomem *base, u32 reg,
+				size_t size, u32 val)
+{
+	struct bt1_pcie *btpci = to_bt1_pcie(pci);
+	int ret;
+
+	regmap_update_bits(btpci->sys_regs, BT1_CCU_PCIE_GENC,
+			   BT1_CCU_PCIE_DBI2_MODE, BT1_CCU_PCIE_DBI2_MODE);
+
+	ret = bt1_pcie_write_mmio(base + reg, size, val);
+	if (ret != PCIBIOS_SUCCESSFUL)
+		dev_err(pci->dev, "Write DBI2 address failed\n");
+
+	regmap_update_bits(btpci->sys_regs, BT1_CCU_PCIE_GENC,
+			   BT1_CCU_PCIE_DBI2_MODE, 0);
+}
+
+static int bt1_pcie_start_ltssm(struct dw_pcie *pci)
+{
+	struct bt1_pcie *btpci = to_bt1_pcie(pci);
+	u32 val;
+	int ret;
+
+	/*
+	 * Enable LTSSM and make sure it was able to establish both PHY and
+	 * data links. This procedure shall work fine to reach 2.5 GT/s speed.
+	 */
+	regmap_update_bits(btpci->sys_regs, BT1_CCU_PCIE_GENC,
+			   BT1_CCU_PCIE_LTSSM_EN, BT1_CCU_PCIE_LTSSM_EN);
+
+	ret = regmap_read_poll_timeout(btpci->sys_regs, BT1_CCU_PCIE_PMSC, val,
+				       (val & BT1_CCU_PCIE_SMLH_LINKUP),
+				       1000, 1000000);
+	if (ret) {
+		dev_err(pci->dev, "LTSSM failed to set PHY link up\n");
+		return ret;
+	}
+
+	ret = regmap_read_poll_timeout(btpci->sys_regs, BT1_CCU_PCIE_PMSC, val,
+				       (val & BT1_CCU_PCIE_RDLH_LINKUP),
+				       1000, 1000000);
+	if (ret) {
+		dev_err(pci->dev, "LTSSM failed to set data link up\n");
+		return ret;
+	}
+
+	/*
+	 * Activate direct speed change after the link is established in an
+	 * attempt to reach a higher bus performance (up to Gen.3 - 8.0 GT/s).
+	 * This is required at least to get 8.0 GT/s speed.
+	 */
+	val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
+	val |= PORT_LOGIC_SPEED_CHANGE;
+	dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
+
+	ret = regmap_read_poll_timeout(btpci->sys_regs, BT1_CCU_PCIE_PMSC, val,
+				       BT1_CCU_PCIE_LTSSM_LINKUP(val),
+				       1000, 1000000);
+	if (ret)
+		dev_err(pci->dev, "LTSSM failed to get into L0 state\n");
+
+	return ret;
+}
+
+static void bt1_pcie_stop_ltssm(struct dw_pcie *pci)
+{
+	struct bt1_pcie *btpci = to_bt1_pcie(pci);
+
+	regmap_update_bits(btpci->sys_regs, BT1_CCU_PCIE_GENC,
+			   BT1_CCU_PCIE_LTSSM_EN, 0);
+}
+
+struct dw_pcie_ops bt1_pcie_dw_ops = {
+	.read_dbi = bt1_pcie_read_dbi,
+	.write_dbi = bt1_pcie_write_dbi,
+	.write_dbi2 = bt1_pcie_write_dbi2,
+	.start_link = bt1_pcie_start_ltssm,
+	.stop_link = bt1_pcie_stop_ltssm,
+};
+
+static int bt1_pcie_get_res(struct bt1_pcie *btpci)
+{
+	struct device *dev = btpci->dw.dev;
+	int ret;
+
+	/* AXI-interface is configured with 64-bit address bus width */
+	ret = dma_coerce_mask_and_coherent(&btpci->dw.pp.bridge->dev,
+					   DMA_BIT_MASK(64));
+	if (ret) {
+		ret = dma_set_mask_and_coherent(&btpci->dw.pp.bridge->dev,
+						DMA_BIT_MASK(32));
+		if (ret)
+			return ret;
+	}
+
+	/* These CSRs are in MMIO so we won't check the regmap-methods status */
+	btpci->sys_regs =
+		syscon_regmap_lookup_by_phandle(dev->of_node, "baikal,bt1-syscon");
+	if (IS_ERR(btpci->sys_regs))
+		return dev_err_probe(dev, PTR_ERR(btpci->sys_regs),
+				     "Failed to get syscon\n");
+
+	ret = devm_clk_bulk_get(dev, BT1_PCIE_NUM_CLKS, btpci->clks);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to get clocks\n");
+
+	ret = devm_reset_control_bulk_get_exclusive(dev, BT1_PCIE_NUM_APP_RSTS,
+						    btpci->app_rsts);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to get app resets\n");
+
+	ret = devm_reset_control_bulk_get_exclusive(dev, BT1_PCIE_NUM_CORE_RSTS,
+						    btpci->core_rsts);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to get core resets\n");
+
+	btpci->pe_rst = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(btpci->pe_rst))
+		return dev_err_probe(dev, PTR_ERR(btpci->pe_rst),
+				     "Failed to get PERST#\n");
+
+	return 0;
+}
+
+static void bt1_pcie_full_stop_bus(struct bt1_pcie *btpci, bool init)
+{
+	struct device *dev = btpci->dw.dev;
+	int ret;
+
+	/* Disable LTSSM for sure */
+	regmap_update_bits(btpci->sys_regs, BT1_CCU_PCIE_GENC,
+			   BT1_CCU_PCIE_LTSSM_EN, 0);
+
+	/*
+	 * Application reset controls are trigger-based so de-assert the core
+	 * resets only.
+	 */
+	ret = reset_control_bulk_assert(BT1_PCIE_NUM_CORE_RSTS, btpci->core_rsts);
+	if (ret)
+		dev_err(dev, "Failed to assert core resets\n");
+
+	/*
+	 * Clocks are disabled by default at least in accordance with the clk
+	 * enable counter value on init stage.
+	 */
+	if (!init)
+		clk_bulk_disable_unprepare(BT1_PCIE_NUM_CLKS, btpci->clks);
+
+	/* The peripheral devices are unavailable anyway so reset them too */
+	gpiod_set_value_cansleep(btpci->pe_rst, 1);
+
+	/* Make sure the reset is settled */
+	usleep_range(1, 10);
+}
+
+/*
+ * Implements the cold reset procedure in accordance with the reference manual
+ * and available PM signals.
+ */
+static int bt1_pcie_cold_start_bus(struct bt1_pcie *btpci)
+{
+	struct device *dev = btpci->dw.dev;
+	u32 val;
+	int ret;
+
+	/* First get out of the Power/Hot reset state */
+	ret = reset_control_deassert(btpci->core_rsts[BT1_PCIE_PWR_RST].rstc);
+	if (ret) {
+		dev_err(dev, "Failed to deassert PHY reset\n");
+		return ret;
+	}
+
+	ret = reset_control_deassert(btpci->core_rsts[BT1_PCIE_HOT_RST].rstc);
+	if (ret) {
+		dev_err(dev, "Failed to deassert hot reset\n");
+		goto err_assert_pwr_rst;
+	}
+
+	/* Wait for the PM-core to stop requesting the PHY reset */
+	ret = regmap_read_poll_timeout(btpci->sys_regs, BT1_CCU_PCIE_RSTC, val,
+				       !(val & BT1_CCU_PCIE_REQ_PHY_RST), 1, 1000);
+	if (ret) {
+		dev_err(dev, "Timed out waiting for PM to stop PHY resetting\n");
+		goto err_assert_hot_rst;
+	}
+
+	ret = reset_control_deassert(btpci->core_rsts[BT1_PCIE_PHY_RST].rstc);
+	if (ret) {
+		dev_err(dev, "Failed to deassert PHY reset\n");
+		goto err_assert_hot_rst;
+	}
+
+	/* Clocks can be now enabled, but the ref one is crucial at this stage */
+	ret = clk_bulk_prepare_enable(BT1_PCIE_NUM_CLKS, btpci->clks);
+	if (ret) {
+		dev_err(dev, "Failed to enable ref clocks\n");
+		goto err_assert_phy_rst;
+	}
+
+	/* Wait for the PM to stop requesting the controller core reset */
+	ret = regmap_read_poll_timeout(btpci->sys_regs, BT1_CCU_PCIE_RSTC, val,
+				       !(val & BT1_CCU_PCIE_REQ_CORE_RST), 1, 1000);
+	if (ret) {
+		dev_err(dev, "Timed out waiting for PM to stop core resetting\n");
+		goto err_clk_disable;
+	}
+
+	/* PCS-PIPE interface and controller core can be now activated */
+	ret = reset_control_deassert(btpci->core_rsts[BT1_PCIE_PIPE_RST].rstc);
+	if (ret) {
+		dev_err(dev, "Failed to deassert PIPE reset\n");
+		goto err_clk_disable;
+	}
+
+	ret = reset_control_deassert(btpci->core_rsts[BT1_PCIE_CORE_RST].rstc);
+	if (ret) {
+		dev_err(dev, "Failed to deassert core reset\n");
+		goto err_assert_pipe_rst;
+	}
+
+	/* It's recommended to reset the core and application logic together */
+	ret = reset_control_bulk_reset(BT1_PCIE_NUM_APP_RSTS, btpci->app_rsts);
+	if (ret) {
+		dev_err(dev, "Failed to reset app domain\n");
+		goto err_assert_core_rst;
+	}
+
+	/* Sticky/Non-sticky CSR flags can be now unreset too */
+	ret = reset_control_deassert(btpci->core_rsts[BT1_PCIE_STICKY_RST].rstc);
+	if (ret) {
+		dev_err(dev, "Failed to deassert sticky reset\n");
+		goto err_assert_core_rst;
+	}
+
+	ret = reset_control_deassert(btpci->core_rsts[BT1_PCIE_NON_STICKY_RST].rstc);
+	if (ret) {
+		dev_err(dev, "Failed to deassert non-sticky reset\n");
+		goto err_assert_sticky_rst;
+	}
+
+	/* Activate the PCIe bus peripheral devices */
+	gpiod_set_value_cansleep(btpci->pe_rst, 0);
+
+	/* Make sure the state is settled (LTSSM is still disabled though) */
+	usleep_range(1, 10);
+
+	return 0;
+
+err_assert_sticky_rst:
+	reset_control_assert(btpci->core_rsts[BT1_PCIE_STICKY_RST].rstc);
+
+err_assert_core_rst:
+	reset_control_assert(btpci->core_rsts[BT1_PCIE_CORE_RST].rstc);
+
+err_assert_pipe_rst:
+	reset_control_assert(btpci->core_rsts[BT1_PCIE_PIPE_RST].rstc);
+
+err_clk_disable:
+	clk_bulk_disable_unprepare(BT1_PCIE_NUM_CLKS, btpci->clks);
+
+err_assert_phy_rst:
+	reset_control_assert(btpci->core_rsts[BT1_PCIE_PHY_RST].rstc);
+
+err_assert_hot_rst:
+	reset_control_assert(btpci->core_rsts[BT1_PCIE_HOT_RST].rstc);
+
+err_assert_pwr_rst:
+	reset_control_assert(btpci->core_rsts[BT1_PCIE_PWR_RST].rstc);
+
+	return ret;
+}
+
+static int bt1_pcie_host_init(struct pcie_port *pp)
+{
+	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+	struct bt1_pcie *btpci = to_bt1_pcie(pci);
+	int ret;
+
+	ret = bt1_pcie_get_res(btpci);
+	if (ret)
+		return ret;
+
+	bt1_pcie_full_stop_bus(btpci, true);
+
+	return bt1_pcie_cold_start_bus(btpci);
+}
+
+static void bt1_pcie_host_deinit(struct pcie_port *pp)
+{
+	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+	struct bt1_pcie *btpci = to_bt1_pcie(pci);
+
+	bt1_pcie_full_stop_bus(btpci, false);
+}
+
+struct dw_pcie_host_ops bt1_pcie_host_ops = {
+	.host_init = bt1_pcie_host_init,
+	.host_deinit = bt1_pcie_host_deinit,
+};
+
+static struct bt1_pcie *bt1_pcie_create_data(struct platform_device *pdev)
+{
+	struct bt1_pcie *btpci;
+	int i;
+
+	btpci = devm_kzalloc(&pdev->dev, sizeof(*btpci), GFP_KERNEL);
+	if (!btpci)
+		return ERR_PTR(-ENOMEM);
+
+	btpci->pdev = pdev;
+
+	for (i = 0; i < BT1_PCIE_NUM_CLKS; ++i)
+		btpci->clks[i].id = dw_pcie_clk_name(bt1_pcie_clks[i]);
+
+	for (i = 0; i < BT1_PCIE_NUM_APP_RSTS; ++i)
+		btpci->app_rsts[i].id = dw_pcie_app_rst_name(bt1_pcie_app_rsts[i]);
+
+	for (i = 0; i < BT1_PCIE_NUM_CORE_RSTS; ++i)
+		btpci->core_rsts[i].id = dw_pcie_core_rst_name(bt1_pcie_core_rsts[i]);
+
+	platform_set_drvdata(pdev, btpci);
+
+	return btpci;
+}
+
+static int bt1_pcie_add_dw_port(struct bt1_pcie *btpci)
+{
+	struct device *dev = &btpci->pdev->dev;
+	int ret;
+
+	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
+	if (ret)
+		return ret;
+
+	btpci->dw.version = DW_PCIE_VER_460A;
+	btpci->dw.dev = dev;
+	btpci->dw.ops = &bt1_pcie_dw_ops;
+
+	btpci->dw.pp.num_vectors = MAX_MSI_IRQS;
+	btpci->dw.pp.ops = &bt1_pcie_host_ops;
+
+	ret = dw_pcie_host_init(&btpci->dw.pp);
+	if (ret)
+		dev_err_probe(dev, ret, "Failed to initialize DWC PCIe host\n");
+
+	return ret;
+}
+
+static void bt1_pcie_del_dw_port(struct bt1_pcie *btpci)
+{
+	dw_pcie_host_deinit(&btpci->dw.pp);
+}
+
+static int bt1_pcie_probe(struct platform_device *pdev)
+{
+	struct bt1_pcie *btpci;
+
+	btpci = bt1_pcie_create_data(pdev);
+	if (IS_ERR(btpci))
+		return PTR_ERR(btpci);
+
+	return bt1_pcie_add_dw_port(btpci);
+}
+
+static int bt1_pcie_remove(struct platform_device *pdev)
+{
+	struct bt1_pcie *btpci = platform_get_drvdata(pdev);
+
+	bt1_pcie_del_dw_port(btpci);
+
+	return 0;
+}
+
+static const struct of_device_id bt1_pcie_of_match[] = {
+	{ .compatible = "baikal,bt1-pcie" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, bt1_pcie_of_match);
+
+static struct platform_driver bt1_pcie_driver = {
+	.probe = bt1_pcie_probe,
+	.remove = bt1_pcie_remove,
+	.driver = {
+		.name	= "bt1-pcie",
+		.of_match_table = bt1_pcie_of_match,
+	},
+};
+module_platform_driver(bt1_pcie_driver);
+
+MODULE_AUTHOR("Serge Semin <Sergey.Semin@baikalelectronics.ru>");
+MODULE_DESCRIPTION("Baikal-T1 PCIe driver");
+MODULE_LICENSE("GPL v2");
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 15/17] PCI: dwc: Introduce dma-ranges property support for RC-host
  2022-05-03 21:46 ` [PATCH v2 15/17] PCI: dwc: Introduce dma-ranges property support for RC-host Serge Semin
@ 2022-05-12 13:57   ` Manivannan Sadhasivam
  2022-05-12 19:41     ` Serge Semin
  2022-05-16 21:42   ` Rob Herring
  1 sibling, 1 reply; 49+ messages in thread
From: Manivannan Sadhasivam @ 2022-05-12 13:57 UTC (permalink / raw)
  To: Serge Semin
  Cc: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Rob Herring, Krzysztof Wilczyński, Serge Semin,
	Alexey Malahov, Pavel Parkhomenko, Frank Li, Rob Herring,
	linux-pci, devicetree, linux-kernel

On Wed, May 04, 2022 at 12:46:36AM +0300, Serge Semin wrote:
> In accordance with the generic PCIe Root Port DT-bindings the "dma-ranges"
> property has the same format as the "ranges" property. The only difference
> is in their semantics. The "dma-ranges" property describes the PCIe-to-CPU
> memory mapping in opposite to the CPU-to-PCIe mapping of the "ranges"
> property. Even though the DW PCIe controllers are normally equipped with
> internal Address Translation Unit which inbound and outbound tables can be
> used to implement both properties semantics, it was surprise for me to
> discover that the host-related part of the DW PCIe driver currently
> supports the "ranges" property only while the "dma-ranges" windows are
> just ignored. Having the "dma-ranges" supported in the driver would be
> very handy for the platforms, that don't tolerate the 1:1 CPU-PCIe memory
> mapping and require customized the PCIe memory layout. So let's fix that
> by introducing the "dma-ranges" property support.
> 
> First of all we suggest to rename the dw_pcie_prog_inbound_atu() method to
> dw_pcie_prog_ep_inbound_atu() and create a new version of the
> dw_pcie_prog_inbound_atu() function. Thus we'll have two methods for RC
> and EP controllers respectively in the same way as it has been developed
> for the outbound ATU setup methods.
> 
> Secondly aside with the memory window index and type the new
> dw_pcie_prog_inbound_atu() function will accept CPU address, PCIe address
> and size as its arguments. These parameters define the PCIe and CPU memory
> ranges which will be used to setup the respective inbound ATU mapping. The
> passed parameters need to be verified against the ATU ranges constraints
> in the same way as it is done for the outbound ranges.
> 
> Finally the DMA-ranges detected for the PCIe controller need to be
> converted into the inbound ATU entries during the host controller
> initialization procedure. It will be done in the framework of the
> dw_pcie_iatu_setup() method. Note before setting the inbound ranges up we
> need to disable all the inbound ATU entries in order to prevent unexpected
> PCIe TLPs translations defined by some third party software like
> bootloader.
> 
> Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> ---
>  .../pci/controller/dwc/pcie-designware-ep.c   |  4 +-
>  .../pci/controller/dwc/pcie-designware-host.c | 32 ++++++++++-
>  drivers/pci/controller/dwc/pcie-designware.c  | 57 ++++++++++++++++++-
>  drivers/pci/controller/dwc/pcie-designware.h  |  6 +-
>  4 files changed, 90 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
> index c62640201246..9b0540cfa9e8 100644
> --- a/drivers/pci/controller/dwc/pcie-designware-ep.c
> +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
> @@ -167,8 +167,8 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type,
>  		return -EINVAL;
>  	}
>  
> -	ret = dw_pcie_prog_inbound_atu(pci, func_no, free_win, type,
> -				       cpu_addr, bar);
> +	ret = dw_pcie_prog_ep_inbound_atu(pci, func_no, free_win, type,
> +					  cpu_addr, bar);
>  	if (ret < 0) {
>  		dev_err(pci->dev, "Failed to program IB window\n");
>  		return ret;
> diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
> index 7caca6c575a5..9cb406f5c185 100644
> --- a/drivers/pci/controller/dwc/pcie-designware-host.c
> +++ b/drivers/pci/controller/dwc/pcie-designware-host.c
> @@ -612,12 +612,15 @@ static int dw_pcie_iatu_setup(struct pcie_port *pp)
>  	}
>  
>  	/*
> -	 * Ensure all outbound windows are disabled before proceeding with
> -	 * the MEM/IO ranges setups.
> +	 * Ensure all out/inbound windows are disabled before proceeding with
> +	 * the MEM/IO (dma-)ranges setups.
>  	 */
>  	for (i = 0; i < pci->num_ob_windows; i++)
>  		dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_OB, i);
>  
> +	for (i = 0; i < pci->num_ib_windows; i++)
> +		dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, i);
> +
>  	i = 0;
>  	resource_list_for_each_entry(entry, &pp->bridge->windows) {
>  		if (resource_type(entry->res) != IORESOURCE_MEM)
> @@ -654,9 +657,32 @@ static int dw_pcie_iatu_setup(struct pcie_port *pp)
>  	}
>  
>  	if (pci->num_ob_windows <= i)
> -		dev_warn(pci->dev, "Resources exceed number of ATU entries (%d)\n",
> +		dev_warn(pci->dev, "Ranges exceed outbound iATU size (%d)\n",
>  			 pci->num_ob_windows);
>  
> +	i = 0;
> +	resource_list_for_each_entry(entry, &pp->bridge->dma_ranges) {
> +		if (resource_type(entry->res) != IORESOURCE_MEM)
> +			continue;
> +
> +		if (pci->num_ib_windows <= i)
> +			break;
> +
> +		ret = dw_pcie_prog_inbound_atu(pci, i++, PCIE_ATU_TYPE_MEM,
> +					       entry->res->start,
> +					       entry->res->start - entry->offset,
> +					       resource_size(entry->res));
> +		if (ret) {
> +			dev_err(pci->dev, "Failed to set DMA range %pr\n",
> +				entry->res);
> +			return ret;
> +		}
> +	}
> +
> +	if (pci->num_ib_windows <= i)
> +		dev_warn(pci->dev, "Dma-ranges exceed inbound iATU size (%u)\n",
> +			 pci->num_ib_windows);
> +
>  	return 0;
>  }
>  
> diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
> index 747e252c09e6..33718ed6c511 100644
> --- a/drivers/pci/controller/dwc/pcie-designware.c
> +++ b/drivers/pci/controller/dwc/pcie-designware.c
> @@ -397,8 +397,61 @@ static inline void dw_pcie_writel_atu_ib(struct dw_pcie *pci, u32 index, u32 reg
>  	dw_pcie_writel_atu(pci, PCIE_ATU_REGION_DIR_IB, index, reg, val);
>  }
>  
> -int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
> -			     int type, u64 cpu_addr, u8 bar)
> +int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,
> +			     u64 cpu_addr, u64 pci_addr, u64 size)
> +{
> +	u64 limit_addr = pci_addr + size - 1;
> +	u32 retries, val;
> +
> +	if ((limit_addr & ~pci->region_limit) != (pci_addr & ~pci->region_limit) ||
> +	    !IS_ALIGNED(cpu_addr, pci->region_align) ||
> +	    !IS_ALIGNED(pci_addr, pci->region_align) ||
> +	    !IS_ALIGNED(size, pci->region_align) ||

Why do you want the size to be aligned? What if I want to transfer a small size
buffer?

Same question applies to outbound programming as well.

Thanks,
Mani

-- 
மணிவண்ணன் சதாசிவம்

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 11/17] PCI: dwc: Simplify in/outbound iATU setup methods
  2022-05-03 21:46 ` [PATCH v2 11/17] PCI: dwc: Simplify in/outbound iATU setup methods Serge Semin
@ 2022-05-12 14:01   ` Manivannan Sadhasivam
  2022-05-12 19:22     ` Serge Semin
  0 siblings, 1 reply; 49+ messages in thread
From: Manivannan Sadhasivam @ 2022-05-12 14:01 UTC (permalink / raw)
  To: Serge Semin
  Cc: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Rob Herring, Krzysztof Wilczyński, Serge Semin,
	Alexey Malahov, Pavel Parkhomenko, Frank Li, Rob Herring,
	linux-pci, devicetree, linux-kernel

On Wed, May 04, 2022 at 12:46:32AM +0300, Serge Semin wrote:
> From maintainability and scalability points of view it has been wrong to
> use different iATU inbound and outbound regions accessors for the viewport
> and unrolled versions of the iATU CSRs mapping. Seeing the particular iATU
> region-wise registers layout is almost fully compatible for different
> IP-core versions, there were no much points in splitting the code up that
> way since it was possible to implement a common windows setup methods for
> both viewport and unrolled iATU CSRs spaces. While what we can observe in
> the current driver implementation of these methods, is a lot of code
> duplication, which consequently worsen the code readability,
> maintainability and scalability. Note the current implementation is a bit
> more performant than the one suggested in this commit since it implies
> having less MMIO accesses. But the gain just doesn't worth having the
> denoted difficulties especially seeing the iATU setup methods are mainly
> called on the DW PCIe controller and peripheral devices initialization
> stage.
> 
> Here we suggest to move the iATU viewport and unrolled CSR access
> specifics in the dw_pcie_readl_atu() and dw_pcie_writel_atu() method, and
> convert the dw_pcie_prog_outbound_atu() and
> dw_pcie_prog_{ep_}inbound_atu() functions to being generic instead of
> having a different methods for each viewport and unrolled types of iATU
> CSRs mapping. Nothing complex really. First of all the dw_pcie_readl_atu()
> and dw_pcie_writel_atu() are converted to accept relative iATU CSRs
> address together with the iATU region direction (inbound or outbound) and
> region index. If DW PCIe controller doesn't have the unrolled iATU CSRs
> space, then the accessors will need to activate a iATU viewport based on
> the specified direction and index, otherwise a base address for the
> corresponding region CSRs will be calculated by means of the
> PCIE_ATU_UNROLL_BASE() macro. The CSRs macro have been modified in
> accordance with that logic in the pcie-designware.h header file.
> 
> The rest of the changes in this commit just concern converting the iATU
> in-/out-bound setup methods and iATU regions detection procedure to be
> compatible with the new accessors semantics. As a result we've dropped the
> no more required dw_pcie_prog_outbound_atu_unroll(),
> dw_pcie_prog_inbound_atu_unroll() and dw_pcie_iatu_detect_regions_unroll()
> methods.
> 
> Note aside with the denoted code improvements, there is an additional
> positive side effect of this change. If at some point an atomic iATU
> configs setup procedure is required, it will be possible to be done with
> no much effort just by adding the synchronization into the
> dw_pcie_readl_atu() and dw_pcie_writel_atu() accessors.
> 
> Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> 
> ---
> 
> Changelog v2:
> - Move the iATU region selection procedure into a helper function (@Rob).
> - Simplify the iATU region selection procedure by recalculating the base
>   address only if the space is unrolled. The iATU viewport base address
>   is saved in the pci->atu_base field from now.
> ---
>  drivers/pci/controller/dwc/pcie-designware.c | 293 ++++++-------------
>  drivers/pci/controller/dwc/pcie-designware.h |  48 ++-

In this patch, you also need to fix "pcie-tegra194-acpi.c" driver that makes
use of the removed macros.

Thanks,
Mani

-- 
மணிவண்ணன் சதாசிவம்

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 11/17] PCI: dwc: Simplify in/outbound iATU setup methods
  2022-05-12 14:01   ` Manivannan Sadhasivam
@ 2022-05-12 19:22     ` Serge Semin
  0 siblings, 0 replies; 49+ messages in thread
From: Serge Semin @ 2022-05-12 19:22 UTC (permalink / raw)
  To: Manivannan Sadhasivam
  Cc: Serge Semin, Jingoo Han, Gustavo Pimentel, Bjorn Helgaas,
	Lorenzo Pieralisi, Rob Herring, Krzysztof Wilczyński,
	Alexey Malahov, Pavel Parkhomenko, Frank Li, Rob Herring,
	linux-pci, devicetree, linux-kernel

On Thu, May 12, 2022 at 07:31:24PM +0530, Manivannan Sadhasivam wrote:
> On Wed, May 04, 2022 at 12:46:32AM +0300, Serge Semin wrote:
> > From maintainability and scalability points of view it has been wrong to
> > use different iATU inbound and outbound regions accessors for the viewport
> > and unrolled versions of the iATU CSRs mapping. Seeing the particular iATU
> > region-wise registers layout is almost fully compatible for different
> > IP-core versions, there were no much points in splitting the code up that
> > way since it was possible to implement a common windows setup methods for
> > both viewport and unrolled iATU CSRs spaces. While what we can observe in
> > the current driver implementation of these methods, is a lot of code
> > duplication, which consequently worsen the code readability,
> > maintainability and scalability. Note the current implementation is a bit
> > more performant than the one suggested in this commit since it implies
> > having less MMIO accesses. But the gain just doesn't worth having the
> > denoted difficulties especially seeing the iATU setup methods are mainly
> > called on the DW PCIe controller and peripheral devices initialization
> > stage.
> > 
> > Here we suggest to move the iATU viewport and unrolled CSR access
> > specifics in the dw_pcie_readl_atu() and dw_pcie_writel_atu() method, and
> > convert the dw_pcie_prog_outbound_atu() and
> > dw_pcie_prog_{ep_}inbound_atu() functions to being generic instead of
> > having a different methods for each viewport and unrolled types of iATU
> > CSRs mapping. Nothing complex really. First of all the dw_pcie_readl_atu()
> > and dw_pcie_writel_atu() are converted to accept relative iATU CSRs
> > address together with the iATU region direction (inbound or outbound) and
> > region index. If DW PCIe controller doesn't have the unrolled iATU CSRs
> > space, then the accessors will need to activate a iATU viewport based on
> > the specified direction and index, otherwise a base address for the
> > corresponding region CSRs will be calculated by means of the
> > PCIE_ATU_UNROLL_BASE() macro. The CSRs macro have been modified in
> > accordance with that logic in the pcie-designware.h header file.
> > 
> > The rest of the changes in this commit just concern converting the iATU
> > in-/out-bound setup methods and iATU regions detection procedure to be
> > compatible with the new accessors semantics. As a result we've dropped the
> > no more required dw_pcie_prog_outbound_atu_unroll(),
> > dw_pcie_prog_inbound_atu_unroll() and dw_pcie_iatu_detect_regions_unroll()
> > methods.
> > 
> > Note aside with the denoted code improvements, there is an additional
> > positive side effect of this change. If at some point an atomic iATU
> > configs setup procedure is required, it will be possible to be done with
> > no much effort just by adding the synchronization into the
> > dw_pcie_readl_atu() and dw_pcie_writel_atu() accessors.
> > 
> > Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> > 
> > ---
> > 
> > Changelog v2:
> > - Move the iATU region selection procedure into a helper function (@Rob).
> > - Simplify the iATU region selection procedure by recalculating the base
> >   address only if the space is unrolled. The iATU viewport base address
> >   is saved in the pci->atu_base field from now.
> > ---
> >  drivers/pci/controller/dwc/pcie-designware.c | 293 ++++++-------------
> >  drivers/pci/controller/dwc/pcie-designware.h |  48 ++-
> 

> In this patch, you also need to fix "pcie-tegra194-acpi.c" driver that makes
> use of the removed macros.

Right. I've missed the ACPI part since it isn't available on my
platform. Thanks for noticing that.

-Sergey

> 
> Thanks,
> Mani
> 
> -- 
> மணிவண்ணன் சதாசிவம்

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 15/17] PCI: dwc: Introduce dma-ranges property support for RC-host
  2022-05-12 13:57   ` Manivannan Sadhasivam
@ 2022-05-12 19:41     ` Serge Semin
  2022-05-17 17:20       ` Manivannan Sadhasivam
  0 siblings, 1 reply; 49+ messages in thread
From: Serge Semin @ 2022-05-12 19:41 UTC (permalink / raw)
  To: Manivannan Sadhasivam
  Cc: Serge Semin, Jingoo Han, Gustavo Pimentel, Bjorn Helgaas,
	Lorenzo Pieralisi, Rob Herring, Krzysztof Wilczyński,
	Alexey Malahov, Pavel Parkhomenko, Frank Li, Rob Herring,
	linux-pci, devicetree, linux-kernel

On Thu, May 12, 2022 at 07:27:08PM +0530, Manivannan Sadhasivam wrote:
> On Wed, May 04, 2022 at 12:46:36AM +0300, Serge Semin wrote:
> > In accordance with the generic PCIe Root Port DT-bindings the "dma-ranges"
> > property has the same format as the "ranges" property. The only difference
> > is in their semantics. The "dma-ranges" property describes the PCIe-to-CPU
> > memory mapping in opposite to the CPU-to-PCIe mapping of the "ranges"
> > property. Even though the DW PCIe controllers are normally equipped with
> > internal Address Translation Unit which inbound and outbound tables can be
> > used to implement both properties semantics, it was surprise for me to
> > discover that the host-related part of the DW PCIe driver currently
> > supports the "ranges" property only while the "dma-ranges" windows are
> > just ignored. Having the "dma-ranges" supported in the driver would be
> > very handy for the platforms, that don't tolerate the 1:1 CPU-PCIe memory
> > mapping and require customized the PCIe memory layout. So let's fix that
> > by introducing the "dma-ranges" property support.
> > 
> > First of all we suggest to rename the dw_pcie_prog_inbound_atu() method to
> > dw_pcie_prog_ep_inbound_atu() and create a new version of the
> > dw_pcie_prog_inbound_atu() function. Thus we'll have two methods for RC
> > and EP controllers respectively in the same way as it has been developed
> > for the outbound ATU setup methods.
> > 
> > Secondly aside with the memory window index and type the new
> > dw_pcie_prog_inbound_atu() function will accept CPU address, PCIe address
> > and size as its arguments. These parameters define the PCIe and CPU memory
> > ranges which will be used to setup the respective inbound ATU mapping. The
> > passed parameters need to be verified against the ATU ranges constraints
> > in the same way as it is done for the outbound ranges.
> > 
> > Finally the DMA-ranges detected for the PCIe controller need to be
> > converted into the inbound ATU entries during the host controller
> > initialization procedure. It will be done in the framework of the
> > dw_pcie_iatu_setup() method. Note before setting the inbound ranges up we
> > need to disable all the inbound ATU entries in order to prevent unexpected
> > PCIe TLPs translations defined by some third party software like
> > bootloader.
> > 
> > Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> > ---
> >  .../pci/controller/dwc/pcie-designware-ep.c   |  4 +-
> >  .../pci/controller/dwc/pcie-designware-host.c | 32 ++++++++++-
> >  drivers/pci/controller/dwc/pcie-designware.c  | 57 ++++++++++++++++++-
> >  drivers/pci/controller/dwc/pcie-designware.h  |  6 +-
> >  4 files changed, 90 insertions(+), 9 deletions(-)
> > 
> > diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
> > index c62640201246..9b0540cfa9e8 100644
> > --- a/drivers/pci/controller/dwc/pcie-designware-ep.c
> > +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
> > @@ -167,8 +167,8 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type,
> >  		return -EINVAL;
> >  	}
> >  
> > -	ret = dw_pcie_prog_inbound_atu(pci, func_no, free_win, type,
> > -				       cpu_addr, bar);
> > +	ret = dw_pcie_prog_ep_inbound_atu(pci, func_no, free_win, type,
> > +					  cpu_addr, bar);
> >  	if (ret < 0) {
> >  		dev_err(pci->dev, "Failed to program IB window\n");
> >  		return ret;
> > diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
> > index 7caca6c575a5..9cb406f5c185 100644
> > --- a/drivers/pci/controller/dwc/pcie-designware-host.c
> > +++ b/drivers/pci/controller/dwc/pcie-designware-host.c
> > @@ -612,12 +612,15 @@ static int dw_pcie_iatu_setup(struct pcie_port *pp)
> >  	}
> >  
> >  	/*
> > -	 * Ensure all outbound windows are disabled before proceeding with
> > -	 * the MEM/IO ranges setups.
> > +	 * Ensure all out/inbound windows are disabled before proceeding with
> > +	 * the MEM/IO (dma-)ranges setups.
> >  	 */
> >  	for (i = 0; i < pci->num_ob_windows; i++)
> >  		dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_OB, i);
> >  
> > +	for (i = 0; i < pci->num_ib_windows; i++)
> > +		dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, i);
> > +
> >  	i = 0;
> >  	resource_list_for_each_entry(entry, &pp->bridge->windows) {
> >  		if (resource_type(entry->res) != IORESOURCE_MEM)
> > @@ -654,9 +657,32 @@ static int dw_pcie_iatu_setup(struct pcie_port *pp)
> >  	}
> >  
> >  	if (pci->num_ob_windows <= i)
> > -		dev_warn(pci->dev, "Resources exceed number of ATU entries (%d)\n",
> > +		dev_warn(pci->dev, "Ranges exceed outbound iATU size (%d)\n",
> >  			 pci->num_ob_windows);
> >  
> > +	i = 0;
> > +	resource_list_for_each_entry(entry, &pp->bridge->dma_ranges) {
> > +		if (resource_type(entry->res) != IORESOURCE_MEM)
> > +			continue;
> > +
> > +		if (pci->num_ib_windows <= i)
> > +			break;
> > +
> > +		ret = dw_pcie_prog_inbound_atu(pci, i++, PCIE_ATU_TYPE_MEM,
> > +					       entry->res->start,
> > +					       entry->res->start - entry->offset,
> > +					       resource_size(entry->res));
> > +		if (ret) {
> > +			dev_err(pci->dev, "Failed to set DMA range %pr\n",
> > +				entry->res);
> > +			return ret;
> > +		}
> > +	}
> > +
> > +	if (pci->num_ib_windows <= i)
> > +		dev_warn(pci->dev, "Dma-ranges exceed inbound iATU size (%u)\n",
> > +			 pci->num_ib_windows);
> > +
> >  	return 0;
> >  }
> >  
> > diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
> > index 747e252c09e6..33718ed6c511 100644
> > --- a/drivers/pci/controller/dwc/pcie-designware.c
> > +++ b/drivers/pci/controller/dwc/pcie-designware.c
> > @@ -397,8 +397,61 @@ static inline void dw_pcie_writel_atu_ib(struct dw_pcie *pci, u32 index, u32 reg
> >  	dw_pcie_writel_atu(pci, PCIE_ATU_REGION_DIR_IB, index, reg, val);
> >  }
> >  
> > -int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
> > -			     int type, u64 cpu_addr, u8 bar)
> > +int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,
> > +			     u64 cpu_addr, u64 pci_addr, u64 size)
> > +{
> > +	u64 limit_addr = pci_addr + size - 1;
> > +	u32 retries, val;
> > +
> > +	if ((limit_addr & ~pci->region_limit) != (pci_addr & ~pci->region_limit) ||
> > +	    !IS_ALIGNED(cpu_addr, pci->region_align) ||
> > +	    !IS_ALIGNED(pci_addr, pci->region_align) ||
> > +	    !IS_ALIGNED(size, pci->region_align) ||
> 

> Why do you want the size to be aligned? What if I want to transfer a small size
> buffer?
> 
> Same question applies to outbound programming as well.

You can't program a region with the unaligned size by the DW PCIe CSRs
design. The limit address lower bits are read-only and fixed with
one's in accordance with the IP-core synthesize parameter
CX_ATU_MIN_REGION_SIZE. So the mapping is always performed in the
CX_ATU_MIN_REGION_SIZE chunks.

IATU_LIMIT_ADDR_OFF_{IN,OUT}BOUND.LIMIT_ADDR_HW = 
{(CX_ATU_MIN_REGION_SIZE == 65536) ? "0xffff" :
 (CX_ATU_MIN_REGION_SIZE == 32768) ? "0x7fff" :
 (CX_ATU_MIN_REGION_SIZE == 16384) ? "0x3fff" :
 (CX_ATU_MIN_REGION_SIZE == 8192)  ? "0x1fff" :
 (CX_ATU_MIN_REGION_SIZE == 4096)  ? "0xfff" : "0xffff"}

-Sergey

> 
> Thanks,
> Mani
> 
> -- 
> மணிவண்ணன் சதாசிவம்

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 01/17] dt-bindings: PCI: dwc: Define common and native DT bindings
  2022-05-03 21:46 ` [PATCH v2 01/17] dt-bindings: PCI: dwc: Define common and native DT bindings Serge Semin
@ 2022-05-16 20:11   ` Rob Herring
  2022-05-19 10:26     ` Serge Semin
  0 siblings, 1 reply; 49+ messages in thread
From: Rob Herring @ 2022-05-16 20:11 UTC (permalink / raw)
  To: Serge Semin
  Cc: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Richard Zhu, Lucas Stach, Krzysztof Kozlowski, Shawn Guo,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	NXP Linux Team, Xiaowei Song, Binghui Wang, Paul Walmsley,
	Greentime Hu, Palmer Dabbelt, Kunihiko Hayashi, Masami Hiramatsu,
	Nobuhiro Iwamatsu, Serge Semin, Alexey Malahov,
	Pavel Parkhomenko, Krzysztof Wilczyński, Frank Li,
	Manivannan Sadhasivam, linux-pci, devicetree, linux-kernel,
	linux-arm-kernel, linux-riscv

On Wed, May 04, 2022 at 12:46:22AM +0300, Serge Semin wrote:
> Currently both DW PCIe Root Port and End-point DT bindings are too generic
> to be used as a descriptive set the device properties. Yes, it's very handy
> to have them that way so the DT-schemas could be used to evaluate as many
> DW PCIe-related DT-nodes as possible. But at the same time they don't
> provide well defined DW PCIe RP/EP DT interface description thus leaving
> too much flexibility for the new platforms, no encouraging the developers

s/no/not/

> to preserve a compatible interface. It causes having many
> platform-specific DT bindings of the same generic properties.
> 
> Instead of currently implemented approach we suggest to be more
> restrictive and yet preserve some level of flexibility in the DW PCIe
> DT-bindings description. The device tree DT-schema is split up into
> three parts: a common YAML-schema applicable for both DWC Root Port and
> End-point controller configs, DWC PCIe Root Port-specific YAML-schema
> and DWC PCIe End-point-specific YAML-schema, where
> 1) pci/snps,dw-pcie-common.yaml - the common DT-schema describes the most
> generic constraints of the "reg", "interrupts", "clocks", "resets" and
> "phys" properties together with a set of common for both device types
> PCIe/AXI bus properties like a maximum number of lanes or a maximum link
> speed, number of inbound and outbound iATU windows. In addition to that a
> set of schema definitions declared under the "definitions" property with
> "reg", "interrupt", "clock" and "reset" names common for DWC PCIe Root
> Port and End-point devices. They can be used by the successive DT-schemas
> in case they are supposed to be compatible with the generic DWC PCIe
> controller DT-bindings.
> 2) pci/snps,dw-pcie.yaml, pci/snps,dw-pcie-ep.yaml - generic DW PCIe Root
> Port and End-point DT-bindings which aside with the device-specific
> properties set also contain more restrictive constraints. All new DW PCIe
> platforms are supposed to be compatible with one of these bindings by
> using "allOf: " property and additionally defining their own constraints
> to close up the DT-bindings set.
> 
> So to speak in case if a DW PCIe-based device for some reason has too many
> specific properties or it's bindings have already been defined in a
> non-generic way, it's DT-schema is supposed to include 1) YAML-file and
> provide its own constraints. Otherwise the ready-to-use bindings from 2)
> should be utilized. There are only two DT-schemas compatible with 2) at the
> moment are samsung,axynos-pcie.yaml and intel-gw-pcie.yaml. The
> rest of the DW PCIe-related DT-schemas are supposed to use more generic
> DW PCIe DT-bindings - pci/snps,dw-pcie-common.yaml.
> 
> Note the provided here generic properties and their possible values are
> derived from the DW PCIe RC/EP hardware manuals and from the interface
> implemented in the driver. The DT-bindings schemas are created to be as
> full as possible with detailed properties/names description for the
> easier interface comprehension and new platforms bindings development.
> 
> Also note since there are no generic DT-nodes can be found in the kernel
> dts-es which would have a pure "snps,dw-pcie" compatible string, these
> DT-bindings have been created to not be selected by default for
> evaluation. They are supposed to be used by the new vendor-specific
> DT-schemas to at least stop adding new bindings for the same set of DWC
> PCIe signals or properties.
> 
> Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> 
> ---
> 
> If the '$defs' property is used instead of the 'definitions' one, the next
> error will be spotted:
> 
> >  DTC     Documentation/devicetree/bindings/pci/snps,dw-pcie.example.dt.yaml
> >  CHECK   Documentation/devicetree/bindings/pci/snps,dw-pcie.example.dt.yaml
> > /.../snps,dw-pcie.example.dt.yaml: pcie@1f052000: reg-names:0: 'oneOf' conditional failed, one must be fixed:
> >        'dbi' is not of type 'array'
> >        From schema: /.../snps,dw-pcie.yaml
> > /.../snps,dw-pcie.example.dt.yaml: pcie@1f052000: reg-names:1: 'oneOf' conditional failed, one must be fixed:
> >         'config' is not of type 'array'
> >         From schema: /.../snps,dw-pcie.yaml
> > /.../snps,dw-pcie.example.dt.yaml: pcie@1f052000: interrupt-names:0: 'anyOf' conditional failed, one must be fixed:
> >         /.../snps,dw-pcie.example.dt.yaml: pcie@1f052000: interrupt-names:0: 'oneOf' conditional failed, one must be fixed:
> >                 'msi' is not of type 'array'
> >         /../snps,dw-pcie.example.dt.yaml: pcie@1f052000: interrupt-names:0: 'oneOf' conditional failed, one must be fixed:
> >                 'msi' is not of type 'array'
> >         From schema: /.../snps,dw-pcie.yaml
> 
> The problem is caused by the 'def fixup_sub_schema()' method defined in
> the dtschema parser. AFAIU It skips the sub-'$defs'-schemas evaluation thus
> not having them fixed up properly. The next patch solves the denoted

It's actually the opposite. The schemas in $defs are fixed-up, but you 
don't want them to be because your definition entries apply to a single 
entry where-as what the fix-ups do is make everything an array. It's 
debatable whether $defs should be fixed up or not, but I'm pretty sure 
we already have cases depending on the fixups.

> problem:
> --- a/lib.py     2022-04-23 19:51:38.829759258 +0300
> +++ b/lib.py     2022-04-23 20:17:16.218137170 +0300
> @@ -470,7 +470,7 @@
>              for subschema in v:
>                  fixup_sub_schema(subschema, True)
> 
> -        if k not in ['dependentRequired', 'dependentSchemas', 'dependencies', 'properties', 'patternProperties', '$defs']:
> +        if k not in ['dependentRequired', 'dependentSchemas', 'dependencies', 'properties', 'patternProperties']:

This list is all the json-schema keywords which have a dictionary of DT 
properties under them. So $def belongs in the list. 'definitions' is not 
there as it is used in limited places and those that exist don't need 
fixups. Within the kernel tree, only $defs should be used and only if 
really necessary. Primarily, I think it is cases where it saves writing 
the same property schema more than once.


This patch is doing multiple things and should be split up some if 
possible. No doubt splitting the common schema from the 'generic' device 
schema is needed. We've done that in other cases. 

The main part I have issue with is how reg-names and interrupt-names are 
handled. For reg-names currently, if a platform wants to add another 
name, they have to modify the commmon binding. That's not great, but we 
don't want that to happen and don't want to make that easier to do. Part 
of the reason we have so many already is because no one stopped all the 
random variations of the same names. The biggest validation gap I see is 
we don't enforce the order and this patch does nothing to address that.

Rob

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 03/17] PCI: dwc: Add more verbose link-up message
  2022-05-03 21:46 ` [PATCH v2 03/17] PCI: dwc: Add more verbose link-up message Serge Semin
@ 2022-05-16 20:18   ` Rob Herring
  0 siblings, 0 replies; 49+ messages in thread
From: Rob Herring @ 2022-05-16 20:18 UTC (permalink / raw)
  To: Serge Semin
  Cc: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Serge Semin, Alexey Malahov,
	Pavel Parkhomenko, Frank Li, Manivannan Sadhasivam, linux-pci,
	devicetree, linux-kernel

On Wed, May 04, 2022 at 12:46:24AM +0300, Serge Semin wrote:
> Printing just "link up" isn't that much informative especially when it
> comes to working with the PCI Express bus. Even if the link is up, due to
> multiple reasons the bus performance can degrade to slower speeds or to
> narrower width than both Root Port and its partner is capable of. In that
> case it would be handy to know the link specifications as early as
> possible. So let's add a more verbose message to the busy-wait link-state
> method, which will contain the link speed generation and the PCIe bus
> width in case if the link up state is discovered. Otherwise an error will
> be printed to the system log.
> 
> Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> 
> ---
> 
> Changelog v2:
> - Test the error condition first and return straight away if it comes true.
>   The typical return is better to be unindented (@Joe).
> ---
>  drivers/pci/controller/dwc/pcie-designware.c | 22 ++++++++++++++------
>  1 file changed, 16 insertions(+), 6 deletions(-)

Reviewed-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 04/17] PCI: dwc: Detect iATU settings after getting "addr_space" resource
  2022-05-03 21:46 ` [PATCH v2 04/17] PCI: dwc: Detect iATU settings after getting "addr_space" resource Serge Semin
@ 2022-05-16 20:21   ` Rob Herring
  0 siblings, 0 replies; 49+ messages in thread
From: Rob Herring @ 2022-05-16 20:21 UTC (permalink / raw)
  To: Serge Semin
  Cc: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Serge Semin, Alexey Malahov,
	Pavel Parkhomenko, Frank Li, Manivannan Sadhasivam, linux-pci,
	devicetree, linux-kernel

On Wed, May 04, 2022 at 12:46:25AM +0300, Serge Semin wrote:
> The iATU detection procedure was introduced in the commit 281f1f99cf3a
> ("PCI: dwc: Detect number of iATU windows"). A bit later the procedure
> execution was moved to Host/EP-specific methods in the framework of commit
> 8bcca2658558 ("PCI: dwc: Move iATU detection earlier"). The later
> modification wasn't done in the most optimal way since the "addr_space"
> CSR region resource doesn't depend on anything detected in the
> dw_pcie_iatu_detect() method. Thus the detection can be postponed to be
> executed after the resource request which can fail and make the detection
> pointless. It will be also helpful for the dw_pcie_ep_init() method
> readability since we are about to add the IP-core version and eDMA module
> (a bit later) detection procedures.
> 
> Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> 
> ---
> 
> Changelog v2:
> - This is a new patch added on v2 iteration of the series.
> ---
>  drivers/pci/controller/dwc/pcie-designware-ep.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)

Reviewed-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 05/17] PCI: dwc: Convert to using native IP-core versions representation
  2022-05-03 21:46 ` [PATCH v2 05/17] PCI: dwc: Convert to using native IP-core versions representation Serge Semin
@ 2022-05-16 20:30   ` Rob Herring
  2022-05-20  9:29     ` Serge Semin
  2022-05-16 20:31   ` Rob Herring
  1 sibling, 1 reply; 49+ messages in thread
From: Rob Herring @ 2022-05-16 20:30 UTC (permalink / raw)
  To: Serge Semin
  Cc: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rahul Tanwar, Thierry Reding,
	Jonathan Hunter, Serge Semin, Alexey Malahov, Pavel Parkhomenko,
	Frank Li, Manivannan Sadhasivam, linux-pci, devicetree,
	linux-kernel, linux-tegra

On Wed, May 04, 2022 at 12:46:26AM +0300, Serge Semin wrote:
> Since DWC PCIe v4.70a the controller version can be read from the
> PORT_LOGIC.PCIE_VERSION_OFF register. Version is represented in the FourCC
> format [1]. It's standard versioning approach for the Synopsys DWC
> IP-cores. Moreover some of the DWC kernel drivers already make use of it
> to fixup version-dependent functionality (See DWC USB3, Stmicro STMMAC or
> recent DW SPI driver). In order to preserve the standard version
> representation and prevent the data conversion back and forth, we suggest
> to preserve the native version representation in the DWC PCIe driver too
> in the same way as it has already been done in the rest of the DWC
> drivers. IP-core version reading from the CSR will be introduced in the
> next commit together with a simple macro-based API to use it.
> 
> [1] https://en.wikipedia.org/wiki/FourCC
> 
> Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> ---
>  drivers/pci/controller/dwc/pci-keystone.c    | 12 ++++++------
>  drivers/pci/controller/dwc/pcie-designware.c |  8 ++++----
>  drivers/pci/controller/dwc/pcie-designware.h | 10 +++++++++-
>  drivers/pci/controller/dwc/pcie-intel-gw.c   |  4 ++--
>  drivers/pci/controller/dwc/pcie-tegra194.c   |  2 +-
>  5 files changed, 22 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c
> index d10e5fd0f83c..c51018c68b56 100644
> --- a/drivers/pci/controller/dwc/pci-keystone.c
> +++ b/drivers/pci/controller/dwc/pci-keystone.c
> @@ -109,7 +109,7 @@ struct ks_pcie_of_data {
>  	enum dw_pcie_device_mode mode;
>  	const struct dw_pcie_host_ops *host_ops;
>  	const struct dw_pcie_ep_ops *ep_ops;
> -	unsigned int version;
> +	u32 version;
>  };
>  
>  struct keystone_pcie {
> @@ -1069,19 +1069,19 @@ static int ks_pcie_am654_set_mode(struct device *dev,
>  
>  static const struct ks_pcie_of_data ks_pcie_rc_of_data = {
>  	.host_ops = &ks_pcie_host_ops,
> -	.version = 0x365A,
> +	.version = DW_PCIE_VER_365A,
>  };
>  
>  static const struct ks_pcie_of_data ks_pcie_am654_rc_of_data = {
>  	.host_ops = &ks_pcie_am654_host_ops,
>  	.mode = DW_PCIE_RC_TYPE,
> -	.version = 0x490A,
> +	.version = DW_PCIE_VER_490A,
>  };
>  
>  static const struct ks_pcie_of_data ks_pcie_am654_ep_of_data = {
>  	.ep_ops = &ks_pcie_am654_ep_ops,
>  	.mode = DW_PCIE_EP_TYPE,
> -	.version = 0x490A,
> +	.version = DW_PCIE_VER_490A,
>  };
>  
>  static const struct of_device_id ks_pcie_of_match[] = {
> @@ -1114,12 +1114,12 @@ static int __init ks_pcie_probe(struct platform_device *pdev)
>  	struct device_link **link;
>  	struct gpio_desc *gpiod;
>  	struct resource *res;
> -	unsigned int version;
>  	void __iomem *base;
>  	u32 num_viewport;
>  	struct phy **phy;
>  	u32 num_lanes;
>  	char name[10];
> +	u32 version;
>  	int ret;
>  	int irq;
>  	int i;
> @@ -1233,7 +1233,7 @@ static int __init ks_pcie_probe(struct platform_device *pdev)
>  		goto err_get_sync;
>  	}
>  
> -	if (pci->version >= 0x480A)
> +	if (pci->version >= DW_PCIE_VER_480A)
>  		ret = ks_pcie_am654_set_mode(dev, mode);
>  	else
>  		ret = ks_pcie_set_mode(dev);
> diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
> index 1682f477bf20..3ebb7bfee10f 100644
> --- a/drivers/pci/controller/dwc/pcie-designware.c
> +++ b/drivers/pci/controller/dwc/pcie-designware.c
> @@ -289,7 +289,7 @@ static void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, u8 func_no,
>  	val = type | PCIE_ATU_FUNC_NUM(func_no);
>  	if (upper_32_bits(limit_addr) > upper_32_bits(cpu_addr))
>  		val |= PCIE_ATU_INCREASE_REGION_SIZE;
> -	if (pci->version == 0x490A)
> +	if (pci->version == DW_PCIE_VER_490A)
>  		val = dw_pcie_enable_ecrc(val);
>  	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, val);
>  	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
> @@ -336,7 +336,7 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
>  			   upper_32_bits(cpu_addr));
>  	dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT,
>  			   lower_32_bits(limit_addr));
> -	if (pci->version >= 0x460A)
> +	if (pci->version >= DW_PCIE_VER_460A)
>  		dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_LIMIT,
>  				   upper_32_bits(limit_addr));
>  	dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET,
> @@ -345,9 +345,9 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
>  			   upper_32_bits(pci_addr));
>  	val = type | PCIE_ATU_FUNC_NUM(func_no);
>  	if (upper_32_bits(limit_addr) > upper_32_bits(cpu_addr) &&
> -	    pci->version >= 0x460A)
> +	    pci->version >= DW_PCIE_VER_460A)
>  		val |= PCIE_ATU_INCREASE_REGION_SIZE;
> -	if (pci->version == 0x490A)
> +	if (pci->version == DW_PCIE_VER_490A)
>  		val = dw_pcie_enable_ecrc(val);
>  	dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, val);
>  	dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE);
> diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
> index 7d6e9b7576be..5be43c662176 100644
> --- a/drivers/pci/controller/dwc/pcie-designware.h
> +++ b/drivers/pci/controller/dwc/pcie-designware.h
> @@ -20,6 +20,14 @@
>  #include <linux/pci-epc.h>
>  #include <linux/pci-epf.h>
>  
> +/* DWC PCIe IP-core versions (native support since v4.70a) */
> +#define DW_PCIE_VER_365A		0x3336352a
> +#define DW_PCIE_VER_460A		0x3436302a
> +#define DW_PCIE_VER_470A		0x3437302a
> +#define DW_PCIE_VER_480A		0x3438302a
> +#define DW_PCIE_VER_490A		0x3439302a
> +#define DW_PCIE_VER_520A		0x3532302a
> +
>  /* Parameters for the waiting for link up routine */
>  #define LINK_WAIT_MAX_RETRIES		10
>  #define LINK_WAIT_USLEEP_MIN		90000
> @@ -269,7 +277,7 @@ struct dw_pcie {
>  	struct pcie_port	pp;
>  	struct dw_pcie_ep	ep;
>  	const struct dw_pcie_ops *ops;
> -	unsigned int		version;
> +	u32			version;
>  	int			num_lanes;
>  	int			link_gen;
>  	u8			n_fts[2];
> diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c
> index 5ba144924ff8..786af2ba379f 100644
> --- a/drivers/pci/controller/dwc/pcie-intel-gw.c
> +++ b/drivers/pci/controller/dwc/pcie-intel-gw.c
> @@ -59,7 +59,7 @@
>  #define RESET_INTERVAL_MS		100
>  
>  struct intel_pcie_soc {
> -	unsigned int	pcie_ver;
> +	u32	pcie_ver;

This is not used by the Intel driver code, but just passed to the DWC 
core code. Given that and that the IP version is new enough, this should 
be removed one the detection is in place.

>  };
>  
>  struct intel_pcie {
> @@ -395,7 +395,7 @@ static const struct dw_pcie_host_ops intel_pcie_dw_ops = {
>  };
>  
>  static const struct intel_pcie_soc pcie_data = {
> -	.pcie_ver =		0x520A,
> +	.pcie_ver =		DW_PCIE_VER_520A,
>  };
>  
>  static int intel_pcie_probe(struct platform_device *pdev)
> diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
> index b1b5f836a806..6f1330ed63e5 100644
> --- a/drivers/pci/controller/dwc/pcie-tegra194.c
> +++ b/drivers/pci/controller/dwc/pcie-tegra194.c
> @@ -1981,7 +1981,7 @@ static int tegra194_pcie_probe(struct platform_device *pdev)
>  	pci->ops = &tegra_dw_pcie_ops;
>  	pci->n_fts[0] = N_FTS_VAL;
>  	pci->n_fts[1] = FTS_VAL;
> -	pci->version = 0x490A;
> +	pci->version = DW_PCIE_VER_490A;
>  
>  	pp = &pci->pp;
>  	pp->num_vectors = MAX_MSI_IRQS;
> -- 
> 2.35.1
> 
> 

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 05/17] PCI: dwc: Convert to using native IP-core versions representation
  2022-05-03 21:46 ` [PATCH v2 05/17] PCI: dwc: Convert to using native IP-core versions representation Serge Semin
  2022-05-16 20:30   ` Rob Herring
@ 2022-05-16 20:31   ` Rob Herring
  1 sibling, 0 replies; 49+ messages in thread
From: Rob Herring @ 2022-05-16 20:31 UTC (permalink / raw)
  To: Serge Semin
  Cc: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rahul Tanwar, Thierry Reding,
	Jonathan Hunter, Serge Semin, Alexey Malahov, Pavel Parkhomenko,
	Frank Li, Manivannan Sadhasivam, linux-pci, devicetree,
	linux-kernel, linux-tegra

On Wed, May 04, 2022 at 12:46:26AM +0300, Serge Semin wrote:
> Since DWC PCIe v4.70a the controller version can be read from the
> PORT_LOGIC.PCIE_VERSION_OFF register. Version is represented in the FourCC
> format [1]. It's standard versioning approach for the Synopsys DWC
> IP-cores. Moreover some of the DWC kernel drivers already make use of it
> to fixup version-dependent functionality (See DWC USB3, Stmicro STMMAC or
> recent DW SPI driver). In order to preserve the standard version
> representation and prevent the data conversion back and forth, we suggest
> to preserve the native version representation in the DWC PCIe driver too
> in the same way as it has already been done in the rest of the DWC
> drivers. IP-core version reading from the CSR will be introduced in the
> next commit together with a simple macro-based API to use it.
> 
> [1] https://en.wikipedia.org/wiki/FourCC
> 
> Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> ---
>  drivers/pci/controller/dwc/pci-keystone.c    | 12 ++++++------
>  drivers/pci/controller/dwc/pcie-designware.c |  8 ++++----
>  drivers/pci/controller/dwc/pcie-designware.h | 10 +++++++++-
>  drivers/pci/controller/dwc/pcie-intel-gw.c   |  4 ++--
>  drivers/pci/controller/dwc/pcie-tegra194.c   |  2 +-
>  5 files changed, 22 insertions(+), 14 deletions(-)

Reviewed-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 06/17] PCI: dwc: Add IP-core version detection procedure
  2022-05-03 21:46 ` [PATCH v2 06/17] PCI: dwc: Add IP-core version detection procedure Serge Semin
@ 2022-05-16 20:32   ` Rob Herring
  0 siblings, 0 replies; 49+ messages in thread
From: Rob Herring @ 2022-05-16 20:32 UTC (permalink / raw)
  To: Serge Semin
  Cc: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Serge Semin, Alexey Malahov,
	Pavel Parkhomenko, Frank Li, Manivannan Sadhasivam, linux-pci,
	devicetree, linux-kernel

On Wed, May 04, 2022 at 12:46:27AM +0300, Serge Semin wrote:
> Since DWC PCIe v4.70a the controller version and version type can be read
> from the PORT_LOGIC.PCIE_VERSION_OFF and PORT_LOGIC.PCIE_VERSION_TYPE_OFF
> registers respectively. Seeing the generic code has got version-dependent
> parts let's use these registers to find out the controller version.  The
> detection procedure is executed for both RC and EP modes right after the
> platform-specific initialization. We can't do that earlier since the
> glue-drivers can perform the DBI-related setups there including the bus
> reference clocks activation, without which the CSRs just can't be read.
> 
> Note the CSRs content is zero on the older DWC PCIe controller. In that
> case we have no choice but to rely on the platform setup.
> 
> Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> 
> ---
> 
> Changelog v2:
> - Move the IP-core version detection procedure call from
>   dw_pcie_ep_init_complete() to dw_pcie_ep_init().
> ---
>  .../pci/controller/dwc/pcie-designware-ep.c   |  2 ++
>  .../pci/controller/dwc/pcie-designware-host.c |  2 ++
>  drivers/pci/controller/dwc/pcie-designware.c  | 24 +++++++++++++++++++
>  drivers/pci/controller/dwc/pcie-designware.h  |  6 +++++
>  4 files changed, 34 insertions(+)

Reviewed-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 07/17] PCI: dwc: Introduce Synopsys IP-core versions/types interface
  2022-05-03 21:46 ` [PATCH v2 07/17] PCI: dwc: Introduce Synopsys IP-core versions/types interface Serge Semin
@ 2022-05-16 20:33   ` Rob Herring
  0 siblings, 0 replies; 49+ messages in thread
From: Rob Herring @ 2022-05-16 20:33 UTC (permalink / raw)
  To: Serge Semin
  Cc: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Serge Semin, Alexey Malahov,
	Pavel Parkhomenko, Frank Li, Manivannan Sadhasivam, linux-pci,
	devicetree, linux-kernel

On Wed, May 04, 2022 at 12:46:28AM +0300, Serge Semin wrote:
> Instead of manual DW PCIe data version field comparison let's use a handy
> macro-based interface in order to shorten out the statements, simplify the
> corresponding parts, improve the code readability and maintainability in
> perspective when more complex version-based dependencies need to
> implemented. Similar approaches have already been implemented in the DWC
> USB3 and DW SPI drivers (though with some IP-core evolution specifics).
> 
> Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> ---
>  drivers/pci/controller/dwc/pci-keystone.c    |  2 +-
>  drivers/pci/controller/dwc/pcie-designware.c |  8 ++++----
>  drivers/pci/controller/dwc/pcie-designware.h | 15 +++++++++++++++
>  3 files changed, 20 insertions(+), 5 deletions(-)

Reviewed-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 08/17] PCI: dwc: Add host de-initialization callback
  2022-05-03 21:46 ` [PATCH v2 08/17] PCI: dwc: Add host de-initialization callback Serge Semin
@ 2022-05-16 20:48   ` Rob Herring
  2022-05-20 10:20     ` Serge Semin
  0 siblings, 1 reply; 49+ messages in thread
From: Rob Herring @ 2022-05-16 20:48 UTC (permalink / raw)
  To: Serge Semin
  Cc: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Serge Semin, Alexey Malahov,
	Pavel Parkhomenko, Frank Li, Manivannan Sadhasivam, linux-pci,
	devicetree, linux-kernel

On Wed, May 04, 2022 at 12:46:29AM +0300, Serge Semin wrote:
> Seeing the platform-specific DW PCIe host-initialization is performed from
> within the generic dw_pcie_host_init() method by means of the dedicated
> dw_pcie_ops.host_init() callback, there must be declared an antagonist
> which would perform the corresponding cleanups. Let's add such callback
> then. It will be called in the dw_pcie_host_deinit() method and in the
> cleanup-on-error path in the dw_pcie_host_init() function.

I'm not really a fan of .host_init() to begin with as it isn't really 
clear by the name when it is supposed to be called and what init to do. 
The drv probe -> dw_pcie_host_init -> drv .host_init() -> return to drv 
sequence isn't great either. I'd rather see more fine grained and well 
defined hooks. So I'm hesitant to add a host_deinit()...

Rob

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 10/17] PCI: dwc: Drop iATU regions enumeration - dw_pcie_region_type
  2022-05-03 21:46 ` [PATCH v2 10/17] PCI: dwc: Drop iATU regions enumeration - dw_pcie_region_type Serge Semin
@ 2022-05-16 20:49   ` Rob Herring
  0 siblings, 0 replies; 49+ messages in thread
From: Rob Herring @ 2022-05-16 20:49 UTC (permalink / raw)
  To: Serge Semin
  Cc: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Serge Semin, Alexey Malahov,
	Pavel Parkhomenko, Frank Li, Manivannan Sadhasivam, linux-pci,
	devicetree, linux-kernel

On Wed, May 04, 2022 at 12:46:31AM +0300, Serge Semin wrote:
> There is no point in having the dw_pcie_region_type enumeration for almost
> the same reasons as it was stated for dw_pcie_as_type. First of all it's
> redundant since the driver already has a set of the macros declared which
> describe the possible inbound and outbound iATU regions. Having an
> addition abstraction just needlessly complicates the code. Secondly
> checking the region type passed to the dw_pcie_disable_atu() method for
> validity is pointless since the erroneous situation is just ignored in the
> current method implementation. So to speak let's drop the redundant
> dw_pcie_region_type enumeration replacing it with the direct iATU
> direction macro usage.
> 
> Since the dw_pcie_disable_atu() method now directly accepts the
> in-/outbound iATU region direction instead of the abstract region type we
> need to change the argument name and the arguments order. The later change
> makes the function prototype looking more logical since the passed index
> indicates an iATU window within the regions with the corresponding
> direction.
> 
> Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> 
> ---
> 
> Changelog v2:
> - Move this patch to being applied before the IB/OB iATU windows setup
>   simplification patch (@Rob).
> ---
>  .../pci/controller/dwc/pcie-designware-ep.c   |  4 +--
>  .../pci/controller/dwc/pcie-designware-host.c |  2 +-
>  drivers/pci/controller/dwc/pcie-designware.c  | 28 +++++--------------
>  drivers/pci/controller/dwc/pcie-designware.h  | 13 ++-------
>  4 files changed, 13 insertions(+), 34 deletions(-)

Reviewed-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 12/17] PCI: dwc: Add iATU regions size detection procedure
  2022-05-03 21:46 ` [PATCH v2 12/17] PCI: dwc: Add iATU regions size detection procedure Serge Semin
@ 2022-05-16 21:01   ` Rob Herring
  0 siblings, 0 replies; 49+ messages in thread
From: Rob Herring @ 2022-05-16 21:01 UTC (permalink / raw)
  To: Serge Semin
  Cc: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Serge Semin, Alexey Malahov,
	Pavel Parkhomenko, Frank Li, Manivannan Sadhasivam, linux-pci,
	devicetree, linux-kernel

On Wed, May 04, 2022 at 12:46:33AM +0300, Serge Semin wrote:
> Depending on the DWC PCIe RC/EP/DM IP-core configuration parameters the
> controllers can be equipped not only with various number of inbound and
> outbound iATU windows, but with varied regions settings like alignment
> (which is also the minimum window size), minimum and maximum sizes. So to
> speak if internal ATU is enabled for the denoted IP-cores then the former
> settings will be defined by the CX_ATU_MIN_REGION_SIZE parameter while the
> later one will be determined by the CX_ATU_MAX_REGION_SIZE configuration
> parameter. Anyway having these parameters used in the driver will help to
> verify whether the requested inbound or outbound memory mappings can be
> fully created. Currently the driver doesn't perform any corresponding
> checking.
> 
> Note 1. The extended iATU regions have been supported since DWC PCIe
> v4.60a. There is no need in testing the upper limit register availability
> for the older cores.
> 
> Note 2. The regions alignment is determined with using the fls() method
> since the lower four bits of the ATU Limit register can be occupied with
> the Circular Buffer Increment setting, which can be initialized with
> zeros.
> 
> The (dma-)ranges verification will be added a bit later in one of the next
> commits.
> 
> Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> ---
>  drivers/pci/controller/dwc/pcie-designware.c | 33 +++++++++++++++++---
>  drivers/pci/controller/dwc/pcie-designware.h |  2 ++
>  2 files changed, 31 insertions(+), 4 deletions(-)

Reviewed-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 13/17] PCI: dwc: Verify in/out regions against iATU constraints
  2022-05-03 21:46 ` [PATCH v2 13/17] PCI: dwc: Verify in/out regions against iATU constraints Serge Semin
@ 2022-05-16 21:07   ` Rob Herring
  0 siblings, 0 replies; 49+ messages in thread
From: Rob Herring @ 2022-05-16 21:07 UTC (permalink / raw)
  To: Serge Semin
  Cc: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Serge Semin, Alexey Malahov,
	Pavel Parkhomenko, Frank Li, Manivannan Sadhasivam, linux-pci,
	devicetree, linux-kernel

On Wed, May 04, 2022 at 12:46:34AM +0300, Serge Semin wrote:
> Since the DWC PCIe driver private data now contains the iATU inbound and
> outbound regions constraints info like alignment, minimum and maximum
> limits, we can use them to make the in- and outbound iATU regions setup
> methods more strict to the ranges a callee tries to specify.  That will
> give us the safer dw_pcie_prog_outbound_atu(),
> dw_pcie_prog_ep_outbound_atu() and dw_pcie_prog_inbound_atu() functions.
> 
> First of all let's update the outbound ATU entries setup methods to
> returning the operation status. The methods will fail either in case if
> the range is failed to be activated or the passed region doesn't fulfill
> iATU constraints. Secondly the passed to the
> dw_pcie_prog_{ep_}outbound_atu() methods region-related parameters are
> verified against the detected iATU regions constraints. In particular the
> region limit address must not overflow the lower/upper limit CSR RW-fields
> otherwise the specified range will be just silently clamped. That
> verification will also protect the code from having u64 type overflow.
> Secondly let's make sure base address (CPU-address), target address
> (PCI-address) and size are properly aligned. Unaligned ranges will be
> silently aligned down (addresses) and up (limit) on writing the values to
> the corresponding registers, which in it turn may lead to unpredictable
> results like ranges virtual overlap. Finally the CPU-address alignment
> needs to be verified in the dw_pcie_prog_inbound_atu() method too as the
> DWC PCIe RC/EP registers manual demands seeing the lower bits of the in-
> and outbound iATU base address are always zeros.
> 
> Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> ---
>  drivers/pci/controller/dwc/pcie-designware.c | 39 +++++++++++++-------
>  drivers/pci/controller/dwc/pcie-designware.h | 10 ++---
>  2 files changed, 30 insertions(+), 19 deletions(-)

Reviewed-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 14/17] PCI: dwc: Check iATU in/outbound ranges setup methods status
  2022-05-03 21:46 ` [PATCH v2 14/17] PCI: dwc: Check iATU in/outbound ranges setup methods status Serge Semin
@ 2022-05-16 21:35   ` Rob Herring
  2022-05-20 10:36     ` Serge Semin
  0 siblings, 1 reply; 49+ messages in thread
From: Rob Herring @ 2022-05-16 21:35 UTC (permalink / raw)
  To: Serge Semin
  Cc: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rahul Tanwar, Serge Semin,
	Alexey Malahov, Pavel Parkhomenko, Frank Li,
	Manivannan Sadhasivam, linux-pci, devicetree, linux-kernel

On Wed, May 04, 2022 at 12:46:35AM +0300, Serge Semin wrote:
> Let's make the DWC PCIe RC/EP safer and more verbose for the invalid or
> failed inbound and outbound iATU windows setups. Needless to say that
> silently ignoring iATU regions setup errors may cause unpredictable
> errors. For instance if for some reason a cfg or IO window fails to be
> activated, then any CFG/IO requested won't reach target PCIe devices and
> the corresponding accessors will return platform-specific random values.
> 
> First of all we need to convert dw_pcie_ep_outbound_atu() method to check
> whether the specified outbound iATU range is successfully setup. That
> method is called by the pci_epc_ops.map_addr callback. Thus we'll make the
> EP-specific CPU->PCIe memory mappings saver.
> 
> Secondly since the iATU outbound range programming method now returns the
> operation status, it will be handy to take that status into account in the
> pci_ops.{map_bus,read,write} methods. Thus any failed mapping will be
> immediately noticeable by the PCIe CFG operations requesters.
> 
> Finally we need to convert the dw_pcie_setup_rc() method to returning the
> operation status, since the iATU outbound ranges setup procedure may now
> fail. It will be especially handy in case if the DW PCIe RC DT-node has
> invalid/unsupported (dma-)ranges property. Note since the suggested
> modification causes having too wide code indentation, it is reasonable
> from maintainability and readability points of view to move the outbound
> ranges setup procedure in the separate function.
> 
> Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> ---
>  .../pci/controller/dwc/pcie-designware-ep.c   |   9 +-
>  .../pci/controller/dwc/pcie-designware-host.c | 149 ++++++++++++------
>  drivers/pci/controller/dwc/pcie-designware.h  |   5 +-
>  drivers/pci/controller/dwc/pcie-intel-gw.c    |   6 +-

I worry that this could regress some platforms that happened to work 
before. But only one way to find out...

Reviewed-by: Rob Herring <robh@kernel.org>


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 15/17] PCI: dwc: Introduce dma-ranges property support for RC-host
  2022-05-03 21:46 ` [PATCH v2 15/17] PCI: dwc: Introduce dma-ranges property support for RC-host Serge Semin
  2022-05-12 13:57   ` Manivannan Sadhasivam
@ 2022-05-16 21:42   ` Rob Herring
  1 sibling, 0 replies; 49+ messages in thread
From: Rob Herring @ 2022-05-16 21:42 UTC (permalink / raw)
  To: Serge Semin
  Cc: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Serge Semin, Alexey Malahov,
	Pavel Parkhomenko, Frank Li, Manivannan Sadhasivam, linux-pci,
	devicetree, linux-kernel

On Wed, May 04, 2022 at 12:46:36AM +0300, Serge Semin wrote:
> In accordance with the generic PCIe Root Port DT-bindings the "dma-ranges"
> property has the same format as the "ranges" property. The only difference
> is in their semantics. The "dma-ranges" property describes the PCIe-to-CPU
> memory mapping in opposite to the CPU-to-PCIe mapping of the "ranges"
> property. Even though the DW PCIe controllers are normally equipped with
> internal Address Translation Unit which inbound and outbound tables can be
> used to implement both properties semantics, it was surprise for me to
> discover that the host-related part of the DW PCIe driver currently
> supports the "ranges" property only while the "dma-ranges" windows are
> just ignored. Having the "dma-ranges" supported in the driver would be
> very handy for the platforms, that don't tolerate the 1:1 CPU-PCIe memory
> mapping and require customized the PCIe memory layout. So let's fix that
> by introducing the "dma-ranges" property support.
> 
> First of all we suggest to rename the dw_pcie_prog_inbound_atu() method to
> dw_pcie_prog_ep_inbound_atu() and create a new version of the
> dw_pcie_prog_inbound_atu() function. Thus we'll have two methods for RC
> and EP controllers respectively in the same way as it has been developed
> for the outbound ATU setup methods.
> 
> Secondly aside with the memory window index and type the new
> dw_pcie_prog_inbound_atu() function will accept CPU address, PCIe address
> and size as its arguments. These parameters define the PCIe and CPU memory
> ranges which will be used to setup the respective inbound ATU mapping. The
> passed parameters need to be verified against the ATU ranges constraints
> in the same way as it is done for the outbound ranges.
> 
> Finally the DMA-ranges detected for the PCIe controller need to be
> converted into the inbound ATU entries during the host controller
> initialization procedure. It will be done in the framework of the
> dw_pcie_iatu_setup() method. Note before setting the inbound ranges up we
> need to disable all the inbound ATU entries in order to prevent unexpected
> PCIe TLPs translations defined by some third party software like
> bootloader.
> 
> Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> ---
>  .../pci/controller/dwc/pcie-designware-ep.c   |  4 +-
>  .../pci/controller/dwc/pcie-designware-host.c | 32 ++++++++++-
>  drivers/pci/controller/dwc/pcie-designware.c  | 57 ++++++++++++++++++-
>  drivers/pci/controller/dwc/pcie-designware.h  |  6 +-
>  4 files changed, 90 insertions(+), 9 deletions(-)

Reviewed-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 16/17] PCI: dwc: Introduce generic platform clocks and resets sets
  2022-05-03 21:46 ` [PATCH v2 16/17] PCI: dwc: Introduce generic platform clocks and resets sets Serge Semin
@ 2022-05-16 22:29   ` Rob Herring
  2022-05-20 16:02     ` Serge Semin
  0 siblings, 1 reply; 49+ messages in thread
From: Rob Herring @ 2022-05-16 22:29 UTC (permalink / raw)
  To: Serge Semin
  Cc: Jingoo Han, Gustavo Pimentel, Bjorn Helgaas, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Serge Semin, Alexey Malahov,
	Pavel Parkhomenko, Frank Li, Manivannan Sadhasivam, linux-pci,
	devicetree, linux-kernel

On Wed, May 04, 2022 at 12:46:37AM +0300, Serge Semin wrote:
> Currently almost each platform driver uses its own resets and clocks
> naming in order to get the corresponding descriptors. It makes the code
> harder to maintain and comprehend especially seeing the DWC PCIe core main
> resets and clocks signals set hasn't changed much for about at least one
> major IP-core release. So in order to organize things around these signals
> we suggest to create a generic interface for them in accordance with the
> naming introduced in the DWC PCIe IP-core reference manual:
> 
> Clocks:
> - DBI - data bus interface clock (on some DWC PCIe platforms it's
>   referred as "pclk", "pcie", "sys", "ahb", "cfg", "iface", "gio", "reg",
>   "pcie_apb_sys");
> - MSTR - AXI-bus master interface clock (some DWC PCIe glue drivers refer
>   to this clock as "port", "bus", "pcie_bus",
>   "bus_master/master_bus/axi_m", "pcie_aclk");
> - SLV - AXI-bus slave interface clock (also called as "port", "bus",
>   "pcie_bus", "bus_slave/slave_bus/axi_s", "pcie_aclk",
>   "pcie_inbound_axi");
> - PIPE - Core-PCS PIPE interface clock coming from external PHY (it's
>   normally named by the platform drivers as just "pipe")
> - CORE - primary clock of the controller (none of the platform drivers
>   declare such a clock but in accordance with the ref. manual the devices
>   may have it separately specified);
> - AUX - Auxiliary PMC domain clock (it is named by some platforms as
>   "pcie_aux" and just "aux")
> - REF - Generic reference clock (it is a generic clock source, which can
>   be used as a signal source for multiple interfaces, some platforms call
>   it as "ref", "general", "pcie_phy", "pcie_phy_ref").
> 
> Application resets:
> - DBI - Data-bus interface reset (it's CSR interface clock and is normally
>   called as "apb" though technically it's not APB but DWC PCIe-specific
>   interface);
>   apb, sys,
> - MSTR -AXI-bus master reset (some platforms call it as "port", "apps",
>   "bus", "axi_m");
> - SLV - ABI-bus slave reset (some platforms call it as "port", "apps",
>   "bus", "axi_s").
> 
> Core resets:
> - NON_STICKY - Non-sticky CSR flags reset;
> - STICKY - sticky CSR flags reset;
> - PIPE - PIPE-interface (Core-PCS) logic reset (some platforms call it
>   just "pipe");
> - CORE - controller primary reset (resets everything except PMC module,
>   some platforms refer to this signal as "soft", "pci");
> - PHY - PCS/PHY block reset (strictly speaking it is normally connected to
>   the out of the external block, but the reference manual says it must be
>   available for the PMC working correctly, some existing platforms call it
>   as "pciephy", "phy", "link");
> - HOT - PMC hot reset signal (also called as sleep");
> - PWR - cold reset signal (can be referred as "pwr", "turnoff").
> 
> As you can see each platform uses it's own naming for basically the same
> set of the signals. In the framework of this commit we suggest to add a
> set of the clocks and signals identifiers and corresponding names for each
> denoted entity. The platforms will be able to use them to define local
> mapping tables between the generic identifiers and the available set of
> the clocks and resets. The tables can be then utilized to create the
> corresponding bulk-arrays, which in its turn can be passed to the
> clock/reset-bulk API methods to easily get/enable/disable/put,
> get/reset/assert/deassert/put all the handlers at once or, if it's
> required, manipulate with the handlers individually.

No doubt there is way to much variation here (ummm, Qcom!). Some 
standardization of names in (new) bindings would be good. That's where 
we should be defining names IMO.

On the driver side, I'd like to see the DW core handle clocks/resets/phys 
at least for the easy cases of just turn on/off all the clocks and 
toggle all resets. Perhaps even more minimally, move the clk/reset 
struct pointers to the DWC core.

IOW, I'm not sure this patch is really helpful without some of the above 
happening. 

Rob

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 15/17] PCI: dwc: Introduce dma-ranges property support for RC-host
  2022-05-12 19:41     ` Serge Semin
@ 2022-05-17 17:20       ` Manivannan Sadhasivam
  2022-05-18 19:26         ` Serge Semin
  0 siblings, 1 reply; 49+ messages in thread
From: Manivannan Sadhasivam @ 2022-05-17 17:20 UTC (permalink / raw)
  To: Serge Semin
  Cc: Serge Semin, Jingoo Han, Gustavo Pimentel, Bjorn Helgaas,
	Lorenzo Pieralisi, Rob Herring, Krzysztof Wilczyński,
	Alexey Malahov, Pavel Parkhomenko, Frank Li, Rob Herring,
	linux-pci, devicetree, linux-kernel

On Thu, May 12, 2022 at 10:41:35PM +0300, Serge Semin wrote:
> On Thu, May 12, 2022 at 07:27:08PM +0530, Manivannan Sadhasivam wrote:
> > On Wed, May 04, 2022 at 12:46:36AM +0300, Serge Semin wrote:
> > > In accordance with the generic PCIe Root Port DT-bindings the "dma-ranges"
> > > property has the same format as the "ranges" property. The only difference
> > > is in their semantics. The "dma-ranges" property describes the PCIe-to-CPU
> > > memory mapping in opposite to the CPU-to-PCIe mapping of the "ranges"
> > > property. Even though the DW PCIe controllers are normally equipped with
> > > internal Address Translation Unit which inbound and outbound tables can be
> > > used to implement both properties semantics, it was surprise for me to
> > > discover that the host-related part of the DW PCIe driver currently
> > > supports the "ranges" property only while the "dma-ranges" windows are
> > > just ignored. Having the "dma-ranges" supported in the driver would be
> > > very handy for the platforms, that don't tolerate the 1:1 CPU-PCIe memory
> > > mapping and require customized the PCIe memory layout. So let's fix that
> > > by introducing the "dma-ranges" property support.
> > > 
> > > First of all we suggest to rename the dw_pcie_prog_inbound_atu() method to
> > > dw_pcie_prog_ep_inbound_atu() and create a new version of the
> > > dw_pcie_prog_inbound_atu() function. Thus we'll have two methods for RC
> > > and EP controllers respectively in the same way as it has been developed
> > > for the outbound ATU setup methods.
> > > 
> > > Secondly aside with the memory window index and type the new
> > > dw_pcie_prog_inbound_atu() function will accept CPU address, PCIe address
> > > and size as its arguments. These parameters define the PCIe and CPU memory
> > > ranges which will be used to setup the respective inbound ATU mapping. The
> > > passed parameters need to be verified against the ATU ranges constraints
> > > in the same way as it is done for the outbound ranges.
> > > 
> > > Finally the DMA-ranges detected for the PCIe controller need to be
> > > converted into the inbound ATU entries during the host controller
> > > initialization procedure. It will be done in the framework of the
> > > dw_pcie_iatu_setup() method. Note before setting the inbound ranges up we
> > > need to disable all the inbound ATU entries in order to prevent unexpected
> > > PCIe TLPs translations defined by some third party software like
> > > bootloader.
> > > 
> > > Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> > > ---
> > >  .../pci/controller/dwc/pcie-designware-ep.c   |  4 +-
> > >  .../pci/controller/dwc/pcie-designware-host.c | 32 ++++++++++-
> > >  drivers/pci/controller/dwc/pcie-designware.c  | 57 ++++++++++++++++++-
> > >  drivers/pci/controller/dwc/pcie-designware.h  |  6 +-
> > >  4 files changed, 90 insertions(+), 9 deletions(-)
> > > 
> > > diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
> > > index c62640201246..9b0540cfa9e8 100644
> > > --- a/drivers/pci/controller/dwc/pcie-designware-ep.c
> > > +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
> > > @@ -167,8 +167,8 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type,
> > >  		return -EINVAL;
> > >  	}
> > >  
> > > -	ret = dw_pcie_prog_inbound_atu(pci, func_no, free_win, type,
> > > -				       cpu_addr, bar);
> > > +	ret = dw_pcie_prog_ep_inbound_atu(pci, func_no, free_win, type,
> > > +					  cpu_addr, bar);
> > >  	if (ret < 0) {
> > >  		dev_err(pci->dev, "Failed to program IB window\n");
> > >  		return ret;
> > > diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
> > > index 7caca6c575a5..9cb406f5c185 100644
> > > --- a/drivers/pci/controller/dwc/pcie-designware-host.c
> > > +++ b/drivers/pci/controller/dwc/pcie-designware-host.c
> > > @@ -612,12 +612,15 @@ static int dw_pcie_iatu_setup(struct pcie_port *pp)
> > >  	}
> > >  
> > >  	/*
> > > -	 * Ensure all outbound windows are disabled before proceeding with
> > > -	 * the MEM/IO ranges setups.
> > > +	 * Ensure all out/inbound windows are disabled before proceeding with
> > > +	 * the MEM/IO (dma-)ranges setups.
> > >  	 */
> > >  	for (i = 0; i < pci->num_ob_windows; i++)
> > >  		dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_OB, i);
> > >  
> > > +	for (i = 0; i < pci->num_ib_windows; i++)
> > > +		dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, i);
> > > +
> > >  	i = 0;
> > >  	resource_list_for_each_entry(entry, &pp->bridge->windows) {
> > >  		if (resource_type(entry->res) != IORESOURCE_MEM)
> > > @@ -654,9 +657,32 @@ static int dw_pcie_iatu_setup(struct pcie_port *pp)
> > >  	}
> > >  
> > >  	if (pci->num_ob_windows <= i)
> > > -		dev_warn(pci->dev, "Resources exceed number of ATU entries (%d)\n",
> > > +		dev_warn(pci->dev, "Ranges exceed outbound iATU size (%d)\n",
> > >  			 pci->num_ob_windows);
> > >  
> > > +	i = 0;
> > > +	resource_list_for_each_entry(entry, &pp->bridge->dma_ranges) {
> > > +		if (resource_type(entry->res) != IORESOURCE_MEM)
> > > +			continue;
> > > +
> > > +		if (pci->num_ib_windows <= i)
> > > +			break;
> > > +
> > > +		ret = dw_pcie_prog_inbound_atu(pci, i++, PCIE_ATU_TYPE_MEM,
> > > +					       entry->res->start,
> > > +					       entry->res->start - entry->offset,
> > > +					       resource_size(entry->res));
> > > +		if (ret) {
> > > +			dev_err(pci->dev, "Failed to set DMA range %pr\n",
> > > +				entry->res);
> > > +			return ret;
> > > +		}
> > > +	}
> > > +
> > > +	if (pci->num_ib_windows <= i)
> > > +		dev_warn(pci->dev, "Dma-ranges exceed inbound iATU size (%u)\n",
> > > +			 pci->num_ib_windows);
> > > +
> > >  	return 0;
> > >  }
> > >  
> > > diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
> > > index 747e252c09e6..33718ed6c511 100644
> > > --- a/drivers/pci/controller/dwc/pcie-designware.c
> > > +++ b/drivers/pci/controller/dwc/pcie-designware.c
> > > @@ -397,8 +397,61 @@ static inline void dw_pcie_writel_atu_ib(struct dw_pcie *pci, u32 index, u32 reg
> > >  	dw_pcie_writel_atu(pci, PCIE_ATU_REGION_DIR_IB, index, reg, val);
> > >  }
> > >  
> > > -int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
> > > -			     int type, u64 cpu_addr, u8 bar)
> > > +int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,
> > > +			     u64 cpu_addr, u64 pci_addr, u64 size)
> > > +{
> > > +	u64 limit_addr = pci_addr + size - 1;
> > > +	u32 retries, val;
> > > +
> > > +	if ((limit_addr & ~pci->region_limit) != (pci_addr & ~pci->region_limit) ||
> > > +	    !IS_ALIGNED(cpu_addr, pci->region_align) ||
> > > +	    !IS_ALIGNED(pci_addr, pci->region_align) ||
> > > +	    !IS_ALIGNED(size, pci->region_align) ||
> > 
> 
> > Why do you want the size to be aligned? What if I want to transfer a small size
> > buffer?
> > 
> > Same question applies to outbound programming as well.
> 
> You can't program a region with the unaligned size by the DW PCIe CSRs
> design. The limit address lower bits are read-only and fixed with
> one's in accordance with the IP-core synthesize parameter
> CX_ATU_MIN_REGION_SIZE. So the mapping is always performed in the
> CX_ATU_MIN_REGION_SIZE chunks.
> 
> IATU_LIMIT_ADDR_OFF_{IN,OUT}BOUND.LIMIT_ADDR_HW = 
> {(CX_ATU_MIN_REGION_SIZE == 65536) ? "0xffff" :
>  (CX_ATU_MIN_REGION_SIZE == 32768) ? "0x7fff" :
>  (CX_ATU_MIN_REGION_SIZE == 16384) ? "0x3fff" :
>  (CX_ATU_MIN_REGION_SIZE == 8192)  ? "0x1fff" :
>  (CX_ATU_MIN_REGION_SIZE == 4096)  ? "0xfff" : "0xffff"}
> 

Right. Even though the minimum size that could be mapped is 4k, I could still
use that 4k size for mapping small buffers also. So you should not be erroring
out here if the size is not aligned. I know that it is a waste of memory but
that doesn't mean that it won't work.

Thanks,
Mani

> -Sergey
> 
> > 
> > Thanks,
> > Mani
> > 
> > -- 
> > மணிவண்ணன் சதாசிவம்

-- 
மணிவண்ணன் சதாசிவம்

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 15/17] PCI: dwc: Introduce dma-ranges property support for RC-host
  2022-05-17 17:20       ` Manivannan Sadhasivam
@ 2022-05-18 19:26         ` Serge Semin
  2022-05-19  7:40           ` Manivannan Sadhasivam
  0 siblings, 1 reply; 49+ messages in thread
From: Serge Semin @ 2022-05-18 19:26 UTC (permalink / raw)
  To: Manivannan Sadhasivam
  Cc: Serge Semin, Jingoo Han, Gustavo Pimentel, Bjorn Helgaas,
	Lorenzo Pieralisi, Rob Herring, Krzysztof Wilczyński,
	Alexey Malahov, Pavel Parkhomenko, Frank Li, Rob Herring,
	linux-pci, devicetree, linux-kernel

On Tue, May 17, 2022 at 10:50:42PM +0530, Manivannan Sadhasivam wrote:
> On Thu, May 12, 2022 at 10:41:35PM +0300, Serge Semin wrote:
> > On Thu, May 12, 2022 at 07:27:08PM +0530, Manivannan Sadhasivam wrote:
> > > On Wed, May 04, 2022 at 12:46:36AM +0300, Serge Semin wrote:
> > > > In accordance with the generic PCIe Root Port DT-bindings the "dma-ranges"
> > > > property has the same format as the "ranges" property. The only difference
> > > > is in their semantics. The "dma-ranges" property describes the PCIe-to-CPU
> > > > memory mapping in opposite to the CPU-to-PCIe mapping of the "ranges"
> > > > property. Even though the DW PCIe controllers are normally equipped with
> > > > internal Address Translation Unit which inbound and outbound tables can be
> > > > used to implement both properties semantics, it was surprise for me to
> > > > discover that the host-related part of the DW PCIe driver currently
> > > > supports the "ranges" property only while the "dma-ranges" windows are
> > > > just ignored. Having the "dma-ranges" supported in the driver would be
> > > > very handy for the platforms, that don't tolerate the 1:1 CPU-PCIe memory
> > > > mapping and require customized the PCIe memory layout. So let's fix that
> > > > by introducing the "dma-ranges" property support.
> > > > 
> > > > First of all we suggest to rename the dw_pcie_prog_inbound_atu() method to
> > > > dw_pcie_prog_ep_inbound_atu() and create a new version of the
> > > > dw_pcie_prog_inbound_atu() function. Thus we'll have two methods for RC
> > > > and EP controllers respectively in the same way as it has been developed
> > > > for the outbound ATU setup methods.
> > > > 
> > > > Secondly aside with the memory window index and type the new
> > > > dw_pcie_prog_inbound_atu() function will accept CPU address, PCIe address
> > > > and size as its arguments. These parameters define the PCIe and CPU memory
> > > > ranges which will be used to setup the respective inbound ATU mapping. The
> > > > passed parameters need to be verified against the ATU ranges constraints
> > > > in the same way as it is done for the outbound ranges.
> > > > 
> > > > Finally the DMA-ranges detected for the PCIe controller need to be
> > > > converted into the inbound ATU entries during the host controller
> > > > initialization procedure. It will be done in the framework of the
> > > > dw_pcie_iatu_setup() method. Note before setting the inbound ranges up we
> > > > need to disable all the inbound ATU entries in order to prevent unexpected
> > > > PCIe TLPs translations defined by some third party software like
> > > > bootloader.
> > > > 
> > > > Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> > > > ---
> > > >  .../pci/controller/dwc/pcie-designware-ep.c   |  4 +-
> > > >  .../pci/controller/dwc/pcie-designware-host.c | 32 ++++++++++-
> > > >  drivers/pci/controller/dwc/pcie-designware.c  | 57 ++++++++++++++++++-
> > > >  drivers/pci/controller/dwc/pcie-designware.h  |  6 +-
> > > >  4 files changed, 90 insertions(+), 9 deletions(-)
> > > > 
> > > > diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
> > > > index c62640201246..9b0540cfa9e8 100644
> > > > --- a/drivers/pci/controller/dwc/pcie-designware-ep.c
> > > > +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
> > > > @@ -167,8 +167,8 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type,
> > > >  		return -EINVAL;
> > > >  	}
> > > >  
> > > > -	ret = dw_pcie_prog_inbound_atu(pci, func_no, free_win, type,
> > > > -				       cpu_addr, bar);
> > > > +	ret = dw_pcie_prog_ep_inbound_atu(pci, func_no, free_win, type,
> > > > +					  cpu_addr, bar);
> > > >  	if (ret < 0) {
> > > >  		dev_err(pci->dev, "Failed to program IB window\n");
> > > >  		return ret;
> > > > diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
> > > > index 7caca6c575a5..9cb406f5c185 100644
> > > > --- a/drivers/pci/controller/dwc/pcie-designware-host.c
> > > > +++ b/drivers/pci/controller/dwc/pcie-designware-host.c
> > > > @@ -612,12 +612,15 @@ static int dw_pcie_iatu_setup(struct pcie_port *pp)
> > > >  	}
> > > >  
> > > >  	/*
> > > > -	 * Ensure all outbound windows are disabled before proceeding with
> > > > -	 * the MEM/IO ranges setups.
> > > > +	 * Ensure all out/inbound windows are disabled before proceeding with
> > > > +	 * the MEM/IO (dma-)ranges setups.
> > > >  	 */
> > > >  	for (i = 0; i < pci->num_ob_windows; i++)
> > > >  		dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_OB, i);
> > > >  
> > > > +	for (i = 0; i < pci->num_ib_windows; i++)
> > > > +		dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, i);
> > > > +
> > > >  	i = 0;
> > > >  	resource_list_for_each_entry(entry, &pp->bridge->windows) {
> > > >  		if (resource_type(entry->res) != IORESOURCE_MEM)
> > > > @@ -654,9 +657,32 @@ static int dw_pcie_iatu_setup(struct pcie_port *pp)
> > > >  	}
> > > >  
> > > >  	if (pci->num_ob_windows <= i)
> > > > -		dev_warn(pci->dev, "Resources exceed number of ATU entries (%d)\n",
> > > > +		dev_warn(pci->dev, "Ranges exceed outbound iATU size (%d)\n",
> > > >  			 pci->num_ob_windows);
> > > >  
> > > > +	i = 0;
> > > > +	resource_list_for_each_entry(entry, &pp->bridge->dma_ranges) {
> > > > +		if (resource_type(entry->res) != IORESOURCE_MEM)
> > > > +			continue;
> > > > +
> > > > +		if (pci->num_ib_windows <= i)
> > > > +			break;
> > > > +
> > > > +		ret = dw_pcie_prog_inbound_atu(pci, i++, PCIE_ATU_TYPE_MEM,
> > > > +					       entry->res->start,
> > > > +					       entry->res->start - entry->offset,
> > > > +					       resource_size(entry->res));
> > > > +		if (ret) {
> > > > +			dev_err(pci->dev, "Failed to set DMA range %pr\n",
> > > > +				entry->res);
> > > > +			return ret;
> > > > +		}
> > > > +	}
> > > > +
> > > > +	if (pci->num_ib_windows <= i)
> > > > +		dev_warn(pci->dev, "Dma-ranges exceed inbound iATU size (%u)\n",
> > > > +			 pci->num_ib_windows);
> > > > +
> > > >  	return 0;
> > > >  }
> > > >  
> > > > diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
> > > > index 747e252c09e6..33718ed6c511 100644
> > > > --- a/drivers/pci/controller/dwc/pcie-designware.c
> > > > +++ b/drivers/pci/controller/dwc/pcie-designware.c
> > > > @@ -397,8 +397,61 @@ static inline void dw_pcie_writel_atu_ib(struct dw_pcie *pci, u32 index, u32 reg
> > > >  	dw_pcie_writel_atu(pci, PCIE_ATU_REGION_DIR_IB, index, reg, val);
> > > >  }
> > > >  
> > > > -int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
> > > > -			     int type, u64 cpu_addr, u8 bar)
> > > > +int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,
> > > > +			     u64 cpu_addr, u64 pci_addr, u64 size)
> > > > +{
> > > > +	u64 limit_addr = pci_addr + size - 1;
> > > > +	u32 retries, val;
> > > > +
> > > > +	if ((limit_addr & ~pci->region_limit) != (pci_addr & ~pci->region_limit) ||
> > > > +	    !IS_ALIGNED(cpu_addr, pci->region_align) ||
> > > > +	    !IS_ALIGNED(pci_addr, pci->region_align) ||
> > > > +	    !IS_ALIGNED(size, pci->region_align) ||
> > > 
> > 
> > > Why do you want the size to be aligned? What if I want to transfer a small size
> > > buffer?
> > > 
> > > Same question applies to outbound programming as well.
> > 
> > You can't program a region with the unaligned size by the DW PCIe CSRs
> > design. The limit address lower bits are read-only and fixed with
> > one's in accordance with the IP-core synthesize parameter
> > CX_ATU_MIN_REGION_SIZE. So the mapping is always performed in the
> > CX_ATU_MIN_REGION_SIZE chunks.
> > 
> > IATU_LIMIT_ADDR_OFF_{IN,OUT}BOUND.LIMIT_ADDR_HW = 
> > {(CX_ATU_MIN_REGION_SIZE == 65536) ? "0xffff" :
> >  (CX_ATU_MIN_REGION_SIZE == 32768) ? "0x7fff" :
> >  (CX_ATU_MIN_REGION_SIZE == 16384) ? "0x3fff" :
> >  (CX_ATU_MIN_REGION_SIZE == 8192)  ? "0x1fff" :
> >  (CX_ATU_MIN_REGION_SIZE == 4096)  ? "0xfff" : "0xffff"}
> > 
> 

> Right. Even though the minimum size that could be mapped is 4k, I could still
> use that 4k size for mapping small buffers also. So you should not be erroring
> out here if the size is not aligned. 

Why would you need to do that? Even if you do and the operation
doesn't return an error (or at least splash the syslog with a
warning), the hardware would expand the mapping up to the aligned size
anyway. Such implicit behavior would have given your software an
impression that the mapping was performed in the way you asked with
the size you specified so the upper part of the unaligned range is
free to be used for something else. If the range is accessed, instead
of a bus error or silent IO termination it may cause unexpected result
of creating random PCIe bus traffic. So I'd rather have the
code/platform setup fixed right from the start instead of waiting for
the hard to find bug cause.

> I know that it is a waste of memory but that doesn't mean that it won't work.

The correct statement in this case would be "it won't work in a way
you expected, but with the implicit side effect applied to the memory
above the requested one."

-Sergey

> 
> Thanks,
> Mani
> 
> > -Sergey
> > 
> > > 
> > > Thanks,
> > > Mani
> > > 
> > > -- 
> > > மணிவண்ணன் சதாசிவம்
> 
> -- 
> மணிவண்ணன் சதாசிவம்

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 15/17] PCI: dwc: Introduce dma-ranges property support for RC-host
  2022-05-18 19:26         ` Serge Semin
@ 2022-05-19  7:40           ` Manivannan Sadhasivam
  2022-05-19 10:52             ` Serge Semin
  0 siblings, 1 reply; 49+ messages in thread
From: Manivannan Sadhasivam @ 2022-05-19  7:40 UTC (permalink / raw)
  To: Serge Semin
  Cc: Serge Semin, Jingoo Han, Gustavo Pimentel, Bjorn Helgaas,
	Lorenzo Pieralisi, Rob Herring, Krzysztof Wilczyński,
	Alexey Malahov, Pavel Parkhomenko, Frank Li, Rob Herring,
	linux-pci, devicetree, linux-kernel

On Wed, May 18, 2022 at 10:26:23PM +0300, Serge Semin wrote:
> On Tue, May 17, 2022 at 10:50:42PM +0530, Manivannan Sadhasivam wrote:
> > On Thu, May 12, 2022 at 10:41:35PM +0300, Serge Semin wrote:
> > > On Thu, May 12, 2022 at 07:27:08PM +0530, Manivannan Sadhasivam wrote:
> > > > On Wed, May 04, 2022 at 12:46:36AM +0300, Serge Semin wrote:
> > > > > In accordance with the generic PCIe Root Port DT-bindings the "dma-ranges"
> > > > > property has the same format as the "ranges" property. The only difference
> > > > > is in their semantics. The "dma-ranges" property describes the PCIe-to-CPU
> > > > > memory mapping in opposite to the CPU-to-PCIe mapping of the "ranges"
> > > > > property. Even though the DW PCIe controllers are normally equipped with
> > > > > internal Address Translation Unit which inbound and outbound tables can be
> > > > > used to implement both properties semantics, it was surprise for me to
> > > > > discover that the host-related part of the DW PCIe driver currently
> > > > > supports the "ranges" property only while the "dma-ranges" windows are
> > > > > just ignored. Having the "dma-ranges" supported in the driver would be
> > > > > very handy for the platforms, that don't tolerate the 1:1 CPU-PCIe memory
> > > > > mapping and require customized the PCIe memory layout. So let's fix that
> > > > > by introducing the "dma-ranges" property support.
> > > > > 
> > > > > First of all we suggest to rename the dw_pcie_prog_inbound_atu() method to
> > > > > dw_pcie_prog_ep_inbound_atu() and create a new version of the
> > > > > dw_pcie_prog_inbound_atu() function. Thus we'll have two methods for RC
> > > > > and EP controllers respectively in the same way as it has been developed
> > > > > for the outbound ATU setup methods.
> > > > > 
> > > > > Secondly aside with the memory window index and type the new
> > > > > dw_pcie_prog_inbound_atu() function will accept CPU address, PCIe address
> > > > > and size as its arguments. These parameters define the PCIe and CPU memory
> > > > > ranges which will be used to setup the respective inbound ATU mapping. The
> > > > > passed parameters need to be verified against the ATU ranges constraints
> > > > > in the same way as it is done for the outbound ranges.
> > > > > 
> > > > > Finally the DMA-ranges detected for the PCIe controller need to be
> > > > > converted into the inbound ATU entries during the host controller
> > > > > initialization procedure. It will be done in the framework of the
> > > > > dw_pcie_iatu_setup() method. Note before setting the inbound ranges up we
> > > > > need to disable all the inbound ATU entries in order to prevent unexpected
> > > > > PCIe TLPs translations defined by some third party software like
> > > > > bootloader.
> > > > > 
> > > > > Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> > > > > ---
> > > > >  .../pci/controller/dwc/pcie-designware-ep.c   |  4 +-
> > > > >  .../pci/controller/dwc/pcie-designware-host.c | 32 ++++++++++-
> > > > >  drivers/pci/controller/dwc/pcie-designware.c  | 57 ++++++++++++++++++-
> > > > >  drivers/pci/controller/dwc/pcie-designware.h  |  6 +-
> > > > >  4 files changed, 90 insertions(+), 9 deletions(-)
> > > > > 
> > > > > diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
> > > > > index c62640201246..9b0540cfa9e8 100644
> > > > > --- a/drivers/pci/controller/dwc/pcie-designware-ep.c
> > > > > +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
> > > > > @@ -167,8 +167,8 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type,
> > > > >  		return -EINVAL;
> > > > >  	}
> > > > >  
> > > > > -	ret = dw_pcie_prog_inbound_atu(pci, func_no, free_win, type,
> > > > > -				       cpu_addr, bar);
> > > > > +	ret = dw_pcie_prog_ep_inbound_atu(pci, func_no, free_win, type,
> > > > > +					  cpu_addr, bar);
> > > > >  	if (ret < 0) {
> > > > >  		dev_err(pci->dev, "Failed to program IB window\n");
> > > > >  		return ret;
> > > > > diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
> > > > > index 7caca6c575a5..9cb406f5c185 100644
> > > > > --- a/drivers/pci/controller/dwc/pcie-designware-host.c
> > > > > +++ b/drivers/pci/controller/dwc/pcie-designware-host.c
> > > > > @@ -612,12 +612,15 @@ static int dw_pcie_iatu_setup(struct pcie_port *pp)
> > > > >  	}
> > > > >  
> > > > >  	/*
> > > > > -	 * Ensure all outbound windows are disabled before proceeding with
> > > > > -	 * the MEM/IO ranges setups.
> > > > > +	 * Ensure all out/inbound windows are disabled before proceeding with
> > > > > +	 * the MEM/IO (dma-)ranges setups.
> > > > >  	 */
> > > > >  	for (i = 0; i < pci->num_ob_windows; i++)
> > > > >  		dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_OB, i);
> > > > >  
> > > > > +	for (i = 0; i < pci->num_ib_windows; i++)
> > > > > +		dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, i);
> > > > > +
> > > > >  	i = 0;
> > > > >  	resource_list_for_each_entry(entry, &pp->bridge->windows) {
> > > > >  		if (resource_type(entry->res) != IORESOURCE_MEM)
> > > > > @@ -654,9 +657,32 @@ static int dw_pcie_iatu_setup(struct pcie_port *pp)
> > > > >  	}
> > > > >  
> > > > >  	if (pci->num_ob_windows <= i)
> > > > > -		dev_warn(pci->dev, "Resources exceed number of ATU entries (%d)\n",
> > > > > +		dev_warn(pci->dev, "Ranges exceed outbound iATU size (%d)\n",
> > > > >  			 pci->num_ob_windows);
> > > > >  
> > > > > +	i = 0;
> > > > > +	resource_list_for_each_entry(entry, &pp->bridge->dma_ranges) {
> > > > > +		if (resource_type(entry->res) != IORESOURCE_MEM)
> > > > > +			continue;
> > > > > +
> > > > > +		if (pci->num_ib_windows <= i)
> > > > > +			break;
> > > > > +
> > > > > +		ret = dw_pcie_prog_inbound_atu(pci, i++, PCIE_ATU_TYPE_MEM,
> > > > > +					       entry->res->start,
> > > > > +					       entry->res->start - entry->offset,
> > > > > +					       resource_size(entry->res));
> > > > > +		if (ret) {
> > > > > +			dev_err(pci->dev, "Failed to set DMA range %pr\n",
> > > > > +				entry->res);
> > > > > +			return ret;
> > > > > +		}
> > > > > +	}
> > > > > +
> > > > > +	if (pci->num_ib_windows <= i)
> > > > > +		dev_warn(pci->dev, "Dma-ranges exceed inbound iATU size (%u)\n",
> > > > > +			 pci->num_ib_windows);
> > > > > +
> > > > >  	return 0;
> > > > >  }
> > > > >  
> > > > > diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
> > > > > index 747e252c09e6..33718ed6c511 100644
> > > > > --- a/drivers/pci/controller/dwc/pcie-designware.c
> > > > > +++ b/drivers/pci/controller/dwc/pcie-designware.c
> > > > > @@ -397,8 +397,61 @@ static inline void dw_pcie_writel_atu_ib(struct dw_pcie *pci, u32 index, u32 reg
> > > > >  	dw_pcie_writel_atu(pci, PCIE_ATU_REGION_DIR_IB, index, reg, val);
> > > > >  }
> > > > >  
> > > > > -int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
> > > > > -			     int type, u64 cpu_addr, u8 bar)
> > > > > +int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,
> > > > > +			     u64 cpu_addr, u64 pci_addr, u64 size)
> > > > > +{
> > > > > +	u64 limit_addr = pci_addr + size - 1;
> > > > > +	u32 retries, val;
> > > > > +
> > > > > +	if ((limit_addr & ~pci->region_limit) != (pci_addr & ~pci->region_limit) ||
> > > > > +	    !IS_ALIGNED(cpu_addr, pci->region_align) ||
> > > > > +	    !IS_ALIGNED(pci_addr, pci->region_align) ||
> > > > > +	    !IS_ALIGNED(size, pci->region_align) ||
> > > > 
> > > 
> > > > Why do you want the size to be aligned? What if I want to transfer a small size
> > > > buffer?
> > > > 
> > > > Same question applies to outbound programming as well.
> > > 
> > > You can't program a region with the unaligned size by the DW PCIe CSRs
> > > design. The limit address lower bits are read-only and fixed with
> > > one's in accordance with the IP-core synthesize parameter
> > > CX_ATU_MIN_REGION_SIZE. So the mapping is always performed in the
> > > CX_ATU_MIN_REGION_SIZE chunks.
> > > 
> > > IATU_LIMIT_ADDR_OFF_{IN,OUT}BOUND.LIMIT_ADDR_HW = 
> > > {(CX_ATU_MIN_REGION_SIZE == 65536) ? "0xffff" :
> > >  (CX_ATU_MIN_REGION_SIZE == 32768) ? "0x7fff" :
> > >  (CX_ATU_MIN_REGION_SIZE == 16384) ? "0x3fff" :
> > >  (CX_ATU_MIN_REGION_SIZE == 8192)  ? "0x1fff" :
> > >  (CX_ATU_MIN_REGION_SIZE == 4096)  ? "0xfff" : "0xffff"}
> > > 
> > 
> 
> > Right. Even though the minimum size that could be mapped is 4k, I could still
> > use that 4k size for mapping small buffers also. So you should not be erroring
> > out here if the size is not aligned. 
> 
> Why would you need to do that? Even if you do and the operation
> doesn't return an error (or at least splash the syslog with a
> warning), the hardware would expand the mapping up to the aligned size
> anyway. Such implicit behavior would have given your software an
> impression that the mapping was performed in the way you asked with
> the size you specified so the upper part of the unaligned range is
> free to be used for something else. If the range is accessed, instead
> of a bus error or silent IO termination it may cause unexpected result
> of creating random PCIe bus traffic. So I'd rather have the
> code/platform setup fixed right from the start instead of waiting for
> the hard to find bug cause.
> 

The application I'm working on is MHI bus. As per the design, it needs to copy
16byte data to ring buffers in the host memory. If I use iATU, then I
cannot copy those small data with the size alignment.

> > I know that it is a waste of memory but that doesn't mean that it won't work.
> 
> The correct statement in this case would be "it won't work in a way
> you expected, but with the implicit side effect applied to the memory
> above the requested one."
> 

Agree but that would only happen when the application does out of bound
access and in that case the issue is with the application.

Thanks,
Mani

> -Sergey
> 
> > 
> > Thanks,
> > Mani
> > 
> > > -Sergey
> > > 
> > > > 
> > > > Thanks,
> > > > Mani
> > > > 
> > > > -- 
> > > > மணிவண்ணன் சதாசிவம்
> > 
> > -- 
> > மணிவண்ணன் சதாசிவம்

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 01/17] dt-bindings: PCI: dwc: Define common and native DT bindings
  2022-05-16 20:11   ` Rob Herring
@ 2022-05-19 10:26     ` Serge Semin
  0 siblings, 0 replies; 49+ messages in thread
From: Serge Semin @ 2022-05-19 10:26 UTC (permalink / raw)
  To: Rob Herring
  Cc: Serge Semin, Jingoo Han, Gustavo Pimentel, Bjorn Helgaas,
	Lorenzo Pieralisi, Richard Zhu, Lucas Stach, Krzysztof Kozlowski,
	Shawn Guo, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	NXP Linux Team, Xiaowei Song, Binghui Wang, Paul Walmsley,
	Greentime Hu, Palmer Dabbelt, Kunihiko Hayashi, Masami Hiramatsu,
	Nobuhiro Iwamatsu, Alexey Malahov, Pavel Parkhomenko,
	Krzysztof Wilczyński, Frank Li, Manivannan Sadhasivam,
	linux-pci, devicetree, linux-kernel, linux-arm-kernel,
	linux-riscv

On Mon, May 16, 2022 at 03:11:53PM -0500, Rob Herring wrote:
> On Wed, May 04, 2022 at 12:46:22AM +0300, Serge Semin wrote:
> > Currently both DW PCIe Root Port and End-point DT bindings are too generic
> > to be used as a descriptive set the device properties. Yes, it's very handy
> > to have them that way so the DT-schemas could be used to evaluate as many
> > DW PCIe-related DT-nodes as possible. But at the same time they don't
> > provide well defined DW PCIe RP/EP DT interface description thus leaving
> > too much flexibility for the new platforms, no encouraging the developers
> 

> s/no/not/

Thanks. will be fixed in v3.

> 
> > to preserve a compatible interface. It causes having many
> > platform-specific DT bindings of the same generic properties.
> > 
> > Instead of currently implemented approach we suggest to be more
> > restrictive and yet preserve some level of flexibility in the DW PCIe
> > DT-bindings description. The device tree DT-schema is split up into
> > three parts: a common YAML-schema applicable for both DWC Root Port and
> > End-point controller configs, DWC PCIe Root Port-specific YAML-schema
> > and DWC PCIe End-point-specific YAML-schema, where
> > 1) pci/snps,dw-pcie-common.yaml - the common DT-schema describes the most
> > generic constraints of the "reg", "interrupts", "clocks", "resets" and
> > "phys" properties together with a set of common for both device types
> > PCIe/AXI bus properties like a maximum number of lanes or a maximum link
> > speed, number of inbound and outbound iATU windows. In addition to that a
> > set of schema definitions declared under the "definitions" property with
> > "reg", "interrupt", "clock" and "reset" names common for DWC PCIe Root
> > Port and End-point devices. They can be used by the successive DT-schemas
> > in case they are supposed to be compatible with the generic DWC PCIe
> > controller DT-bindings.
> > 2) pci/snps,dw-pcie.yaml, pci/snps,dw-pcie-ep.yaml - generic DW PCIe Root
> > Port and End-point DT-bindings which aside with the device-specific
> > properties set also contain more restrictive constraints. All new DW PCIe
> > platforms are supposed to be compatible with one of these bindings by
> > using "allOf: " property and additionally defining their own constraints
> > to close up the DT-bindings set.
> > 
> > So to speak in case if a DW PCIe-based device for some reason has too many
> > specific properties or it's bindings have already been defined in a
> > non-generic way, it's DT-schema is supposed to include 1) YAML-file and
> > provide its own constraints. Otherwise the ready-to-use bindings from 2)
> > should be utilized. There are only two DT-schemas compatible with 2) at the
> > moment are samsung,axynos-pcie.yaml and intel-gw-pcie.yaml. The
> > rest of the DW PCIe-related DT-schemas are supposed to use more generic
> > DW PCIe DT-bindings - pci/snps,dw-pcie-common.yaml.
> > 
> > Note the provided here generic properties and their possible values are
> > derived from the DW PCIe RC/EP hardware manuals and from the interface
> > implemented in the driver. The DT-bindings schemas are created to be as
> > full as possible with detailed properties/names description for the
> > easier interface comprehension and new platforms bindings development.
> > 
> > Also note since there are no generic DT-nodes can be found in the kernel
> > dts-es which would have a pure "snps,dw-pcie" compatible string, these
> > DT-bindings have been created to not be selected by default for
> > evaluation. They are supposed to be used by the new vendor-specific
> > DT-schemas to at least stop adding new bindings for the same set of DWC
> > PCIe signals or properties.
> > 
> > Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> > 
> > ---
> > 
> > If the '$defs' property is used instead of the 'definitions' one, the next
> > error will be spotted:
> > 
> > >  DTC     Documentation/devicetree/bindings/pci/snps,dw-pcie.example.dt.yaml
> > >  CHECK   Documentation/devicetree/bindings/pci/snps,dw-pcie.example.dt.yaml
> > > /.../snps,dw-pcie.example.dt.yaml: pcie@1f052000: reg-names:0: 'oneOf' conditional failed, one must be fixed:
> > >        'dbi' is not of type 'array'
> > >        From schema: /.../snps,dw-pcie.yaml
> > > /.../snps,dw-pcie.example.dt.yaml: pcie@1f052000: reg-names:1: 'oneOf' conditional failed, one must be fixed:
> > >         'config' is not of type 'array'
> > >         From schema: /.../snps,dw-pcie.yaml
> > > /.../snps,dw-pcie.example.dt.yaml: pcie@1f052000: interrupt-names:0: 'anyOf' conditional failed, one must be fixed:
> > >         /.../snps,dw-pcie.example.dt.yaml: pcie@1f052000: interrupt-names:0: 'oneOf' conditional failed, one must be fixed:
> > >                 'msi' is not of type 'array'
> > >         /../snps,dw-pcie.example.dt.yaml: pcie@1f052000: interrupt-names:0: 'oneOf' conditional failed, one must be fixed:
> > >                 'msi' is not of type 'array'
> > >         From schema: /.../snps,dw-pcie.yaml
> > 
> > The problem is caused by the 'def fixup_sub_schema()' method defined in
> > the dtschema parser. AFAIU It skips the sub-'$defs'-schemas evaluation thus
> > not having them fixed up properly. The next patch solves the denoted
> 

> It's actually the opposite. The schemas in $defs are fixed-up, but you 
> don't want them to be because your definition entries apply to a single 
> entry where-as what the fix-ups do is make everything an array. It's 
> debatable whether $defs should be fixed up or not, but I'm pretty sure 
> we already have cases depending on the fixups.
> 
> > problem:
> > --- a/lib.py     2022-04-23 19:51:38.829759258 +0300
> > +++ b/lib.py     2022-04-23 20:17:16.218137170 +0300
> > @@ -470,7 +470,7 @@
> >              for subschema in v:
> >                  fixup_sub_schema(subschema, True)
> > 
> > -        if k not in ['dependentRequired', 'dependentSchemas', 'dependencies', 'properties', 'patternProperties', '$defs']:
> > +        if k not in ['dependentRequired', 'dependentSchemas', 'dependencies', 'properties', 'patternProperties']:
> 
> This list is all the json-schema keywords which have a dictionary of DT 
> properties under them. So $def belongs in the list. 'definitions' is not 
> there as it is used in limited places and those that exist don't need 
> fixups. Within the kernel tree, only $defs should be used and only if 
> really necessary. Primarily, I think it is cases where it saves writing 
> the same property schema more than once.

Got it. Thanks for clarification.

> 
> 

> This patch is doing multiple things and should be split up some if 
> possible. 

Ok, but we need to settle the suggested modification design before
the next patchset re-submission. The splitting up is a lot of additional work,
while I have already spent so much time on this and on the rest of the
DW PCIe-related patches.

> No doubt splitting the common schema from the 'generic' device 
> schema is needed. We've done that in other cases. 

> 
> The main part I have issue with is how reg-names and interrupt-names are 
> handled. For reg-names currently, if a platform wants to add another 
> name, they have to modify the commmon binding. That's not great, but we 
> don't want that to happen and don't want to make that easier to do.

When it comes to the generic and vendor-specific IP-core device
implementations the approach suggested by me is more flexible, but yet
restrictive enough to cover your case. It is suitable for all already
defined vendor-specific DT bindings, the generic DW PCIe-compatible
bindings, new DT bindings yet to come. Here are the cases it can be
used for:

1) DW PCIe platforms fully compatible with the generic IP-core
schema, but with already defined vendor-specific resource
names. Solution:
$ref: /schemas/pci/snps,dw-pcie-common.yaml#
and define all the vendor-specific resource names. If the
reg/interrupt/clock/reset-names are partly compatible with the
generic schema, then they can be directly referenced from the
"definitions" property in the "oneOf" composition.

2) DW PCIe platforms partly compatible with the generic IP-core
DT schema. Solution:
$ref: /schemas/pci/snps,dw-pcie-common.yaml#
and define all the vendor-specific resource names. Refer to the
generic "reg/interrupt/clock/reset"-names in the particular names
arrays aside with the platform-specific resource names (this especially
concerns the reg-names property, which aside with the standard CSRs
may have additional non-standard CSRs defined).

3) DW PCIe platforms fully compatible with the generic IP-core
DT schema. Solution:
$ref: /schemas/pci/snps,dw-pcie.yaml#
or
$ref: /schemas/pci/snps,dw-pcie-ep.yaml#
If there are resource names which are defined in the generic schema
and are applicable to the platform-specific device (like additional
required names) they can be added to the vendor DT-schema otherwise
the generic schema reference is enough (see the baikal,bt1-pcie.yaml
DT-schema being added in the framework of this patchset).

New DW PCIe platforms are encouraged to use schema 3). In case if
there are resources not specific to the generic DW PCIe IP-core,
they need to have the schema 2) utilized. Since we can't change the
already defined bindings, we have no choice but to use schema 1) for
them. Thus the suggested here schemas can be used to create more
descriptive and restrictive enough DT-schemas hierarchy covering all
the currently defined bindings and new ones.

In anyway regarding what you said in the message above adding new
generic names is possible only in case if they are applicable to the
generic DW PCIe IP-core (almost all of them I've already listed in the
snps,dw-pcie-common.yaml#definitions property) otherwise such names
need to be defined in the platform-specific schema.

Traditional generic schema design when all the generic properties are
directly listed in the generic DT-schema in the corresponding property
is too restrictive to implement the cases 1) and 2). Thus such generic
schema needs to be extended with the non-generic names (or not to have
the names listed in the generic schema at all). It implicitly
makes all the platforms permitting such resources, which isn't true.

> Part 
> of the reason we have so many already is because no one stopped all the 
> random variations of the same names.

Sadly, but you're right. In the framework of this patchset I suggest a
method to organize all old and new DT bindings (for DW PCIe devices)
in a way so them all be using the generic DW PCIe DT-schema at the
most applicable part. 

> The biggest validation gap I see is 
> we don't enforce the order and this patch does nothing to address that.

I happen to have the opposite opinion regarding the order of the
names listed in the *-names properties. Why do you need them to be
defined in a strict order? reg and reg-names are tightly coupled.
The DW PCIe driver refer to the CSRs and IRQs by their names. So the
order is irrelevant in this case. If the order is required, then it
can be constrained in the platform-specific schema.

-Sergey

> 
> Rob

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 15/17] PCI: dwc: Introduce dma-ranges property support for RC-host
  2022-05-19  7:40           ` Manivannan Sadhasivam
@ 2022-05-19 10:52             ` Serge Semin
  2022-05-19 15:21               ` Manivannan Sadhasivam
  0 siblings, 1 reply; 49+ messages in thread
From: Serge Semin @ 2022-05-19 10:52 UTC (permalink / raw)
  To: Manivannan Sadhasivam
  Cc: Serge Semin, Jingoo Han, Gustavo Pimentel, Bjorn Helgaas,
	Lorenzo Pieralisi, Rob Herring, Krzysztof Wilczyński,
	Alexey Malahov, Pavel Parkhomenko, Frank Li, Rob Herring,
	linux-pci, devicetree, linux-kernel

On Thu, May 19, 2022 at 01:10:53PM +0530, Manivannan Sadhasivam wrote:
> On Wed, May 18, 2022 at 10:26:23PM +0300, Serge Semin wrote:
> > On Tue, May 17, 2022 at 10:50:42PM +0530, Manivannan Sadhasivam wrote:
> > > On Thu, May 12, 2022 at 10:41:35PM +0300, Serge Semin wrote:
> > > > On Thu, May 12, 2022 at 07:27:08PM +0530, Manivannan Sadhasivam wrote:
> > > > > On Wed, May 04, 2022 at 12:46:36AM +0300, Serge Semin wrote:
> > > > > > In accordance with the generic PCIe Root Port DT-bindings the "dma-ranges"
> > > > > > property has the same format as the "ranges" property. The only difference
> > > > > > is in their semantics. The "dma-ranges" property describes the PCIe-to-CPU
> > > > > > memory mapping in opposite to the CPU-to-PCIe mapping of the "ranges"
> > > > > > property. Even though the DW PCIe controllers are normally equipped with
> > > > > > internal Address Translation Unit which inbound and outbound tables can be
> > > > > > used to implement both properties semantics, it was surprise for me to
> > > > > > discover that the host-related part of the DW PCIe driver currently
> > > > > > supports the "ranges" property only while the "dma-ranges" windows are
> > > > > > just ignored. Having the "dma-ranges" supported in the driver would be
> > > > > > very handy for the platforms, that don't tolerate the 1:1 CPU-PCIe memory
> > > > > > mapping and require customized the PCIe memory layout. So let's fix that
> > > > > > by introducing the "dma-ranges" property support.
> > > > > > 
> > > > > > First of all we suggest to rename the dw_pcie_prog_inbound_atu() method to
> > > > > > dw_pcie_prog_ep_inbound_atu() and create a new version of the
> > > > > > dw_pcie_prog_inbound_atu() function. Thus we'll have two methods for RC
> > > > > > and EP controllers respectively in the same way as it has been developed
> > > > > > for the outbound ATU setup methods.
> > > > > > 
> > > > > > Secondly aside with the memory window index and type the new
> > > > > > dw_pcie_prog_inbound_atu() function will accept CPU address, PCIe address
> > > > > > and size as its arguments. These parameters define the PCIe and CPU memory
> > > > > > ranges which will be used to setup the respective inbound ATU mapping. The
> > > > > > passed parameters need to be verified against the ATU ranges constraints
> > > > > > in the same way as it is done for the outbound ranges.
> > > > > > 
> > > > > > Finally the DMA-ranges detected for the PCIe controller need to be
> > > > > > converted into the inbound ATU entries during the host controller
> > > > > > initialization procedure. It will be done in the framework of the
> > > > > > dw_pcie_iatu_setup() method. Note before setting the inbound ranges up we
> > > > > > need to disable all the inbound ATU entries in order to prevent unexpected
> > > > > > PCIe TLPs translations defined by some third party software like
> > > > > > bootloader.
> > > > > > 
> > > > > > Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> > > > > > ---
> > > > > >  .../pci/controller/dwc/pcie-designware-ep.c   |  4 +-
> > > > > >  .../pci/controller/dwc/pcie-designware-host.c | 32 ++++++++++-
> > > > > >  drivers/pci/controller/dwc/pcie-designware.c  | 57 ++++++++++++++++++-
> > > > > >  drivers/pci/controller/dwc/pcie-designware.h  |  6 +-
> > > > > >  4 files changed, 90 insertions(+), 9 deletions(-)
> > > > > > 
> > > > > > diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
> > > > > > index c62640201246..9b0540cfa9e8 100644
> > > > > > --- a/drivers/pci/controller/dwc/pcie-designware-ep.c
> > > > > > +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
> > > > > > @@ -167,8 +167,8 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type,
> > > > > >  		return -EINVAL;
> > > > > >  	}
> > > > > >  
> > > > > > -	ret = dw_pcie_prog_inbound_atu(pci, func_no, free_win, type,
> > > > > > -				       cpu_addr, bar);
> > > > > > +	ret = dw_pcie_prog_ep_inbound_atu(pci, func_no, free_win, type,
> > > > > > +					  cpu_addr, bar);
> > > > > >  	if (ret < 0) {
> > > > > >  		dev_err(pci->dev, "Failed to program IB window\n");
> > > > > >  		return ret;
> > > > > > diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
> > > > > > index 7caca6c575a5..9cb406f5c185 100644
> > > > > > --- a/drivers/pci/controller/dwc/pcie-designware-host.c
> > > > > > +++ b/drivers/pci/controller/dwc/pcie-designware-host.c
> > > > > > @@ -612,12 +612,15 @@ static int dw_pcie_iatu_setup(struct pcie_port *pp)
> > > > > >  	}
> > > > > >  
> > > > > >  	/*
> > > > > > -	 * Ensure all outbound windows are disabled before proceeding with
> > > > > > -	 * the MEM/IO ranges setups.
> > > > > > +	 * Ensure all out/inbound windows are disabled before proceeding with
> > > > > > +	 * the MEM/IO (dma-)ranges setups.
> > > > > >  	 */
> > > > > >  	for (i = 0; i < pci->num_ob_windows; i++)
> > > > > >  		dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_OB, i);
> > > > > >  
> > > > > > +	for (i = 0; i < pci->num_ib_windows; i++)
> > > > > > +		dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, i);
> > > > > > +
> > > > > >  	i = 0;
> > > > > >  	resource_list_for_each_entry(entry, &pp->bridge->windows) {
> > > > > >  		if (resource_type(entry->res) != IORESOURCE_MEM)
> > > > > > @@ -654,9 +657,32 @@ static int dw_pcie_iatu_setup(struct pcie_port *pp)
> > > > > >  	}
> > > > > >  
> > > > > >  	if (pci->num_ob_windows <= i)
> > > > > > -		dev_warn(pci->dev, "Resources exceed number of ATU entries (%d)\n",
> > > > > > +		dev_warn(pci->dev, "Ranges exceed outbound iATU size (%d)\n",
> > > > > >  			 pci->num_ob_windows);
> > > > > >  
> > > > > > +	i = 0;
> > > > > > +	resource_list_for_each_entry(entry, &pp->bridge->dma_ranges) {
> > > > > > +		if (resource_type(entry->res) != IORESOURCE_MEM)
> > > > > > +			continue;
> > > > > > +
> > > > > > +		if (pci->num_ib_windows <= i)
> > > > > > +			break;
> > > > > > +
> > > > > > +		ret = dw_pcie_prog_inbound_atu(pci, i++, PCIE_ATU_TYPE_MEM,
> > > > > > +					       entry->res->start,
> > > > > > +					       entry->res->start - entry->offset,
> > > > > > +					       resource_size(entry->res));
> > > > > > +		if (ret) {
> > > > > > +			dev_err(pci->dev, "Failed to set DMA range %pr\n",
> > > > > > +				entry->res);
> > > > > > +			return ret;
> > > > > > +		}
> > > > > > +	}
> > > > > > +
> > > > > > +	if (pci->num_ib_windows <= i)
> > > > > > +		dev_warn(pci->dev, "Dma-ranges exceed inbound iATU size (%u)\n",
> > > > > > +			 pci->num_ib_windows);
> > > > > > +
> > > > > >  	return 0;
> > > > > >  }
> > > > > >  
> > > > > > diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
> > > > > > index 747e252c09e6..33718ed6c511 100644
> > > > > > --- a/drivers/pci/controller/dwc/pcie-designware.c
> > > > > > +++ b/drivers/pci/controller/dwc/pcie-designware.c
> > > > > > @@ -397,8 +397,61 @@ static inline void dw_pcie_writel_atu_ib(struct dw_pcie *pci, u32 index, u32 reg
> > > > > >  	dw_pcie_writel_atu(pci, PCIE_ATU_REGION_DIR_IB, index, reg, val);
> > > > > >  }
> > > > > >  
> > > > > > -int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
> > > > > > -			     int type, u64 cpu_addr, u8 bar)
> > > > > > +int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,
> > > > > > +			     u64 cpu_addr, u64 pci_addr, u64 size)
> > > > > > +{
> > > > > > +	u64 limit_addr = pci_addr + size - 1;
> > > > > > +	u32 retries, val;
> > > > > > +
> > > > > > +	if ((limit_addr & ~pci->region_limit) != (pci_addr & ~pci->region_limit) ||
> > > > > > +	    !IS_ALIGNED(cpu_addr, pci->region_align) ||
> > > > > > +	    !IS_ALIGNED(pci_addr, pci->region_align) ||
> > > > > > +	    !IS_ALIGNED(size, pci->region_align) ||
> > > > > 
> > > > 
> > > > > Why do you want the size to be aligned? What if I want to transfer a small size
> > > > > buffer?
> > > > > 
> > > > > Same question applies to outbound programming as well.
> > > > 
> > > > You can't program a region with the unaligned size by the DW PCIe CSRs
> > > > design. The limit address lower bits are read-only and fixed with
> > > > one's in accordance with the IP-core synthesize parameter
> > > > CX_ATU_MIN_REGION_SIZE. So the mapping is always performed in the
> > > > CX_ATU_MIN_REGION_SIZE chunks.
> > > > 
> > > > IATU_LIMIT_ADDR_OFF_{IN,OUT}BOUND.LIMIT_ADDR_HW = 
> > > > {(CX_ATU_MIN_REGION_SIZE == 65536) ? "0xffff" :
> > > >  (CX_ATU_MIN_REGION_SIZE == 32768) ? "0x7fff" :
> > > >  (CX_ATU_MIN_REGION_SIZE == 16384) ? "0x3fff" :
> > > >  (CX_ATU_MIN_REGION_SIZE == 8192)  ? "0x1fff" :
> > > >  (CX_ATU_MIN_REGION_SIZE == 4096)  ? "0xfff" : "0xffff"}
> > > > 
> > > 
> > 
> > > Right. Even though the minimum size that could be mapped is 4k, I could still
> > > use that 4k size for mapping small buffers also. So you should not be erroring
> > > out here if the size is not aligned. 
> > 
> > Why would you need to do that? Even if you do and the operation
> > doesn't return an error (or at least splash the syslog with a
> > warning), the hardware would expand the mapping up to the aligned size
> > anyway. Such implicit behavior would have given your software an
> > impression that the mapping was performed in the way you asked with
> > the size you specified so the upper part of the unaligned range is
> > free to be used for something else. If the range is accessed, instead
> > of a bus error or silent IO termination it may cause unexpected result
> > of creating random PCIe bus traffic. So I'd rather have the
> > code/platform setup fixed right from the start instead of waiting for
> > the hard to find bug cause.
> > 
> 

> The application I'm working on is MHI bus. As per the design, it needs to copy
> 16byte data to ring buffers in the host memory. If I use iATU, then I
> cannot copy those small data with the size alignment.

First of all I don't see any driver using the DW PCIe iATU mapping
functions directly. They are only utilized in the framework of the
"ranges" and "dma-ranges" DT properties. If the application you are
referring to your private code, then it can't be a justification.
Secondly if your application uses them then what about just extending
the mapping range size while still access the lowest 15 bytes only? In
that case you would create a more comprehensive software which would
be aware of the hardware constraints.

> 
> > > I know that it is a waste of memory but that doesn't mean that it won't work.
> > 
> > The correct statement in this case would be "it won't work in a way
> > you expected, but with the implicit side effect applied to the memory
> > above the requested one."
> > 
> 

> Agree but that would only happen when the application does out of bound
> access and in that case the issue is with the application.

Not only in that case, but anyway how would such application be aware
of the out of bounds access? Returning an error in case if the
requested mapping can't be performed with the specified parameters is
a possible solution. So the application would be aware of the hardware
constraints and be sure it perceives them right. Otherwise the
consequences of the out of bounds access would be very unexpected
since the mapping is performed only for the small buffer.

-Sergey

> 
> Thanks,
> Mani
> 
> > -Sergey
> > 
> > > 
> > > Thanks,
> > > Mani
> > > 
> > > > -Sergey
> > > > 
> > > > > 
> > > > > Thanks,
> > > > > Mani
> > > > > 
> > > > > -- 
> > > > > மணிவண்ணன் சதாசிவம்
> > > 
> > > -- 
> > > மணிவண்ணன் சதாசிவம்

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 15/17] PCI: dwc: Introduce dma-ranges property support for RC-host
  2022-05-19 10:52             ` Serge Semin
@ 2022-05-19 15:21               ` Manivannan Sadhasivam
  2022-05-20 18:21                 ` Serge Semin
  0 siblings, 1 reply; 49+ messages in thread
From: Manivannan Sadhasivam @ 2022-05-19 15:21 UTC (permalink / raw)
  To: Serge Semin
  Cc: Serge Semin, Jingoo Han, Gustavo Pimentel, Bjorn Helgaas,
	Lorenzo Pieralisi, Rob Herring, Krzysztof Wilczyński,
	Alexey Malahov, Pavel Parkhomenko, Frank Li, Rob Herring,
	linux-pci, devicetree, linux-kernel

On Thu, May 19, 2022 at 01:52:38PM +0300, Serge Semin wrote:

[...]

> > > > > > > diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
> > > > > > > index 747e252c09e6..33718ed6c511 100644
> > > > > > > --- a/drivers/pci/controller/dwc/pcie-designware.c
> > > > > > > +++ b/drivers/pci/controller/dwc/pcie-designware.c
> > > > > > > @@ -397,8 +397,61 @@ static inline void dw_pcie_writel_atu_ib(struct dw_pcie *pci, u32 index, u32 reg
> > > > > > >  	dw_pcie_writel_atu(pci, PCIE_ATU_REGION_DIR_IB, index, reg, val);
> > > > > > >  }
> > > > > > >  
> > > > > > > -int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
> > > > > > > -			     int type, u64 cpu_addr, u8 bar)
> > > > > > > +int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,
> > > > > > > +			     u64 cpu_addr, u64 pci_addr, u64 size)
> > > > > > > +{
> > > > > > > +	u64 limit_addr = pci_addr + size - 1;
> > > > > > > +	u32 retries, val;
> > > > > > > +
> > > > > > > +	if ((limit_addr & ~pci->region_limit) != (pci_addr & ~pci->region_limit) ||
> > > > > > > +	    !IS_ALIGNED(cpu_addr, pci->region_align) ||
> > > > > > > +	    !IS_ALIGNED(pci_addr, pci->region_align) ||
> > > > > > > +	    !IS_ALIGNED(size, pci->region_align) ||
> > > > > > 
> > > > > 
> > > > > > Why do you want the size to be aligned? What if I want to transfer a small size
> > > > > > buffer?
> > > > > > 
> > > > > > Same question applies to outbound programming as well.
> > > > > 
> > > > > You can't program a region with the unaligned size by the DW PCIe CSRs
> > > > > design. The limit address lower bits are read-only and fixed with
> > > > > one's in accordance with the IP-core synthesize parameter
> > > > > CX_ATU_MIN_REGION_SIZE. So the mapping is always performed in the
> > > > > CX_ATU_MIN_REGION_SIZE chunks.
> > > > > 
> > > > > IATU_LIMIT_ADDR_OFF_{IN,OUT}BOUND.LIMIT_ADDR_HW = 
> > > > > {(CX_ATU_MIN_REGION_SIZE == 65536) ? "0xffff" :
> > > > >  (CX_ATU_MIN_REGION_SIZE == 32768) ? "0x7fff" :
> > > > >  (CX_ATU_MIN_REGION_SIZE == 16384) ? "0x3fff" :
> > > > >  (CX_ATU_MIN_REGION_SIZE == 8192)  ? "0x1fff" :
> > > > >  (CX_ATU_MIN_REGION_SIZE == 4096)  ? "0xfff" : "0xffff"}
> > > > > 
> > > > 
> > > 
> > > > Right. Even though the minimum size that could be mapped is 4k, I could still
> > > > use that 4k size for mapping small buffers also. So you should not be erroring
> > > > out here if the size is not aligned. 
> > > 
> > > Why would you need to do that? Even if you do and the operation
> > > doesn't return an error (or at least splash the syslog with a
> > > warning), the hardware would expand the mapping up to the aligned size
> > > anyway. Such implicit behavior would have given your software an
> > > impression that the mapping was performed in the way you asked with
> > > the size you specified so the upper part of the unaligned range is
> > > free to be used for something else. If the range is accessed, instead
> > > of a bus error or silent IO termination it may cause unexpected result
> > > of creating random PCIe bus traffic. So I'd rather have the
> > > code/platform setup fixed right from the start instead of waiting for
> > > the hard to find bug cause.
> > > 
> > 
> 
> > The application I'm working on is MHI bus. As per the design, it needs to copy
> > 16byte data to ring buffers in the host memory. If I use iATU, then I
> > cannot copy those small data with the size alignment.
> 
> First of all I don't see any driver using the DW PCIe iATU mapping
> functions directly. They are only utilized in the framework of the
> "ranges" and "dma-ranges" DT properties.

Not true. The PCI_EPF_TEST and PCI_EPF_NTB applications use iATU mpping function
through EPC ops.

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/pci/endpoint/functions/pci-epf-test.c#n250
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/pci/endpoint/pci-epc-core.c#n476

Now that I've referred, we need to check if these drivers still work on top of
your patches. These are not supported on my platform, so perhaps Frank can
test?

> If the application you are
> referring to your private code, then it can't be a justification.

I should have mentioned it, but my application is not private. It is partly
available in linux-next:

https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/tree/drivers/bus/mhi/ep/ring.c#n47
https://git.linaro.org/landing-teams/working/qualcomm/kernel.git/tree/drivers/pci/endpoint/functions/pci-epf-mhi.c?h=tracking-qcomlt-sdx55-drivers#n121

> Secondly if your application uses them then what about just extending
> the mapping range size while still access the lowest 15 bytes only? In
> that case you would create a more comprehensive software which would
> be aware of the hardware constraints.
> 

Hmm... I'm already doing a similar hack for getting the aligned address due to
iATU limitation, but I think doing the same for size should also work.

Thinking again, I agree with the alignment check. Thanks for the explanations.
But let's make sure the existing EPF drivers still work.

Thanks,
Mani

> > 
> > > > I know that it is a waste of memory but that doesn't mean that it won't work.
> > > 
> > > The correct statement in this case would be "it won't work in a way
> > > you expected, but with the implicit side effect applied to the memory
> > > above the requested one."
> > > 
> > 
> 
> > Agree but that would only happen when the application does out of bound
> > access and in that case the issue is with the application.
> 
> Not only in that case, but anyway how would such application be aware
> of the out of bounds access? Returning an error in case if the
> requested mapping can't be performed with the specified parameters is
> a possible solution. So the application would be aware of the hardware
> constraints and be sure it perceives them right. Otherwise the
> consequences of the out of bounds access would be very unexpected
> since the mapping is performed only for the small buffer.
> 
> -Sergey
> 
> > 
> > Thanks,
> > Mani
> > 
> > > -Sergey
> > > 
> > > > 
> > > > Thanks,
> > > > Mani
> > > > 
> > > > > -Sergey
> > > > > 
> > > > > > 
> > > > > > Thanks,
> > > > > > Mani
> > > > > > 
> > > > > > -- 
> > > > > > மணிவண்ணன் சதாசிவம்
> > > > 
> > > > -- 
> > > > மணிவண்ணன் சதாசிவம்

-- 
மணிவண்ணன் சதாசிவம்

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 05/17] PCI: dwc: Convert to using native IP-core versions representation
  2022-05-16 20:30   ` Rob Herring
@ 2022-05-20  9:29     ` Serge Semin
  2022-05-20 15:06       ` Rob Herring
  0 siblings, 1 reply; 49+ messages in thread
From: Serge Semin @ 2022-05-20  9:29 UTC (permalink / raw)
  To: Rob Herring
  Cc: Serge Semin, Jingoo Han, Gustavo Pimentel, Bjorn Helgaas,
	Lorenzo Pieralisi, Krzysztof Wilczyński, Rahul Tanwar,
	Thierry Reding, Jonathan Hunter, Alexey Malahov,
	Pavel Parkhomenko, Frank Li, Manivannan Sadhasivam, linux-pci,
	devicetree, linux-kernel, linux-tegra

On Mon, May 16, 2022 at 03:30:03PM -0500, Rob Herring wrote:
> On Wed, May 04, 2022 at 12:46:26AM +0300, Serge Semin wrote:
> > Since DWC PCIe v4.70a the controller version can be read from the
> > PORT_LOGIC.PCIE_VERSION_OFF register. Version is represented in the FourCC
> > format [1]. It's standard versioning approach for the Synopsys DWC
> > IP-cores. Moreover some of the DWC kernel drivers already make use of it
> > to fixup version-dependent functionality (See DWC USB3, Stmicro STMMAC or
> > recent DW SPI driver). In order to preserve the standard version
> > representation and prevent the data conversion back and forth, we suggest
> > to preserve the native version representation in the DWC PCIe driver too
> > in the same way as it has already been done in the rest of the DWC
> > drivers. IP-core version reading from the CSR will be introduced in the
> > next commit together with a simple macro-based API to use it.
> > 
> > [1] https://en.wikipedia.org/wiki/FourCC
> > 
> > Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> > ---
> >  drivers/pci/controller/dwc/pci-keystone.c    | 12 ++++++------
> >  drivers/pci/controller/dwc/pcie-designware.c |  8 ++++----
> >  drivers/pci/controller/dwc/pcie-designware.h | 10 +++++++++-
> >  drivers/pci/controller/dwc/pcie-intel-gw.c   |  4 ++--
> >  drivers/pci/controller/dwc/pcie-tegra194.c   |  2 +-
> >  5 files changed, 22 insertions(+), 14 deletions(-)
> > 
> > diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c
> > index d10e5fd0f83c..c51018c68b56 100644
> > --- a/drivers/pci/controller/dwc/pci-keystone.c
> > +++ b/drivers/pci/controller/dwc/pci-keystone.c
> > @@ -109,7 +109,7 @@ struct ks_pcie_of_data {
> >  	enum dw_pcie_device_mode mode;
> >  	const struct dw_pcie_host_ops *host_ops;
> >  	const struct dw_pcie_ep_ops *ep_ops;
> > -	unsigned int version;
> > +	u32 version;
> >  };
> >  
> >  struct keystone_pcie {
> > @@ -1069,19 +1069,19 @@ static int ks_pcie_am654_set_mode(struct device *dev,
> >  
> >  static const struct ks_pcie_of_data ks_pcie_rc_of_data = {
> >  	.host_ops = &ks_pcie_host_ops,
> > -	.version = 0x365A,
> > +	.version = DW_PCIE_VER_365A,
> >  };
> >  
> >  static const struct ks_pcie_of_data ks_pcie_am654_rc_of_data = {
> >  	.host_ops = &ks_pcie_am654_host_ops,
> >  	.mode = DW_PCIE_RC_TYPE,
> > -	.version = 0x490A,
> > +	.version = DW_PCIE_VER_490A,
> >  };
> >  
> >  static const struct ks_pcie_of_data ks_pcie_am654_ep_of_data = {
> >  	.ep_ops = &ks_pcie_am654_ep_ops,
> >  	.mode = DW_PCIE_EP_TYPE,
> > -	.version = 0x490A,
> > +	.version = DW_PCIE_VER_490A,
> >  };
> >  
> >  static const struct of_device_id ks_pcie_of_match[] = {
> > @@ -1114,12 +1114,12 @@ static int __init ks_pcie_probe(struct platform_device *pdev)
> >  	struct device_link **link;
> >  	struct gpio_desc *gpiod;
> >  	struct resource *res;
> > -	unsigned int version;
> >  	void __iomem *base;
> >  	u32 num_viewport;
> >  	struct phy **phy;
> >  	u32 num_lanes;
> >  	char name[10];
> > +	u32 version;
> >  	int ret;
> >  	int irq;
> >  	int i;
> > @@ -1233,7 +1233,7 @@ static int __init ks_pcie_probe(struct platform_device *pdev)
> >  		goto err_get_sync;
> >  	}
> >  
> > -	if (pci->version >= 0x480A)
> > +	if (pci->version >= DW_PCIE_VER_480A)
> >  		ret = ks_pcie_am654_set_mode(dev, mode);
> >  	else
> >  		ret = ks_pcie_set_mode(dev);
> > diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
> > index 1682f477bf20..3ebb7bfee10f 100644
> > --- a/drivers/pci/controller/dwc/pcie-designware.c
> > +++ b/drivers/pci/controller/dwc/pcie-designware.c
> > @@ -289,7 +289,7 @@ static void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, u8 func_no,
> >  	val = type | PCIE_ATU_FUNC_NUM(func_no);
> >  	if (upper_32_bits(limit_addr) > upper_32_bits(cpu_addr))
> >  		val |= PCIE_ATU_INCREASE_REGION_SIZE;
> > -	if (pci->version == 0x490A)
> > +	if (pci->version == DW_PCIE_VER_490A)
> >  		val = dw_pcie_enable_ecrc(val);
> >  	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, val);
> >  	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
> > @@ -336,7 +336,7 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
> >  			   upper_32_bits(cpu_addr));
> >  	dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT,
> >  			   lower_32_bits(limit_addr));
> > -	if (pci->version >= 0x460A)
> > +	if (pci->version >= DW_PCIE_VER_460A)
> >  		dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_LIMIT,
> >  				   upper_32_bits(limit_addr));
> >  	dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET,
> > @@ -345,9 +345,9 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
> >  			   upper_32_bits(pci_addr));
> >  	val = type | PCIE_ATU_FUNC_NUM(func_no);
> >  	if (upper_32_bits(limit_addr) > upper_32_bits(cpu_addr) &&
> > -	    pci->version >= 0x460A)
> > +	    pci->version >= DW_PCIE_VER_460A)
> >  		val |= PCIE_ATU_INCREASE_REGION_SIZE;
> > -	if (pci->version == 0x490A)
> > +	if (pci->version == DW_PCIE_VER_490A)
> >  		val = dw_pcie_enable_ecrc(val);
> >  	dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, val);
> >  	dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE);
> > diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
> > index 7d6e9b7576be..5be43c662176 100644
> > --- a/drivers/pci/controller/dwc/pcie-designware.h
> > +++ b/drivers/pci/controller/dwc/pcie-designware.h
> > @@ -20,6 +20,14 @@
> >  #include <linux/pci-epc.h>
> >  #include <linux/pci-epf.h>
> >  
> > +/* DWC PCIe IP-core versions (native support since v4.70a) */
> > +#define DW_PCIE_VER_365A		0x3336352a
> > +#define DW_PCIE_VER_460A		0x3436302a
> > +#define DW_PCIE_VER_470A		0x3437302a
> > +#define DW_PCIE_VER_480A		0x3438302a
> > +#define DW_PCIE_VER_490A		0x3439302a
> > +#define DW_PCIE_VER_520A		0x3532302a
> > +
> >  /* Parameters for the waiting for link up routine */
> >  #define LINK_WAIT_MAX_RETRIES		10
> >  #define LINK_WAIT_USLEEP_MIN		90000
> > @@ -269,7 +277,7 @@ struct dw_pcie {
> >  	struct pcie_port	pp;
> >  	struct dw_pcie_ep	ep;
> >  	const struct dw_pcie_ops *ops;
> > -	unsigned int		version;
> > +	u32			version;
> >  	int			num_lanes;
> >  	int			link_gen;
> >  	u8			n_fts[2];
> > diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c
> > index 5ba144924ff8..786af2ba379f 100644
> > --- a/drivers/pci/controller/dwc/pcie-intel-gw.c
> > +++ b/drivers/pci/controller/dwc/pcie-intel-gw.c
> > @@ -59,7 +59,7 @@
> >  #define RESET_INTERVAL_MS		100
> >  
> >  struct intel_pcie_soc {
> > -	unsigned int	pcie_ver;
> > +	u32	pcie_ver;
> 

> This is not used by the Intel driver code, but just passed to the DWC 
> core code. Given that and that the IP version is new enough, this should 
> be removed one the detection is in place.

Ok. I'll drop it in an additional patch placed after the version
detection patch in the series. What about the Tegra 194 code? Shall I
drop it from there too? I've got DW PCIe 4.90a reference manual here.
It states there are the PCIE_VERSION_NUMBER_OFF and
PCIE_VERSION_TYPE_OFF registers in the port logic reg space. So the
removal shall not cause problems.

-Sergey

> 
> >  };
> >  
> >  struct intel_pcie {
> > @@ -395,7 +395,7 @@ static const struct dw_pcie_host_ops intel_pcie_dw_ops = {
> >  };
> >  
> >  static const struct intel_pcie_soc pcie_data = {
> > -	.pcie_ver =		0x520A,
> > +	.pcie_ver =		DW_PCIE_VER_520A,
> >  };
> >  
> >  static int intel_pcie_probe(struct platform_device *pdev)
> > diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
> > index b1b5f836a806..6f1330ed63e5 100644
> > --- a/drivers/pci/controller/dwc/pcie-tegra194.c
> > +++ b/drivers/pci/controller/dwc/pcie-tegra194.c
> > @@ -1981,7 +1981,7 @@ static int tegra194_pcie_probe(struct platform_device *pdev)
> >  	pci->ops = &tegra_dw_pcie_ops;
> >  	pci->n_fts[0] = N_FTS_VAL;
> >  	pci->n_fts[1] = FTS_VAL;
> > -	pci->version = 0x490A;
> > +	pci->version = DW_PCIE_VER_490A;
> >  
> >  	pp = &pci->pp;
> >  	pp->num_vectors = MAX_MSI_IRQS;
> > -- 
> > 2.35.1
> > 
> > 

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 08/17] PCI: dwc: Add host de-initialization callback
  2022-05-16 20:48   ` Rob Herring
@ 2022-05-20 10:20     ` Serge Semin
  0 siblings, 0 replies; 49+ messages in thread
From: Serge Semin @ 2022-05-20 10:20 UTC (permalink / raw)
  To: Rob Herring
  Cc: Serge Semin, Jingoo Han, Gustavo Pimentel, Bjorn Helgaas,
	Lorenzo Pieralisi, Krzysztof Wilczyński, Alexey Malahov,
	Pavel Parkhomenko, Frank Li, Manivannan Sadhasivam, linux-pci,
	devicetree, linux-kernel

On Mon, May 16, 2022 at 03:48:28PM -0500, Rob Herring wrote:
> On Wed, May 04, 2022 at 12:46:29AM +0300, Serge Semin wrote:
> > Seeing the platform-specific DW PCIe host-initialization is performed from
> > within the generic dw_pcie_host_init() method by means of the dedicated
> > dw_pcie_ops.host_init() callback, there must be declared an antagonist
> > which would perform the corresponding cleanups. Let's add such callback
> > then. It will be called in the dw_pcie_host_deinit() method and in the
> > cleanup-on-error path in the dw_pcie_host_init() function.
> 

> I'm not really a fan of .host_init() to begin with as it isn't really 
> clear by the name when it is supposed to be called and what init to do. 
> The drv probe -> dw_pcie_host_init -> drv .host_init() -> return to drv 
> sequence isn't great either. I'd rather see more fine grained and well 
> defined hooks. So I'm hesitant to add a host_deinit()...

What you say is a matter of another change. This patch just fixes the
already defined init-hook interface. Indeed the initializations
performed in the framework of the init-method need to be cleaned up in
case of the PCIe host probe procedure failure. I can't fix the DW PCIe
platform drivers since they may have some specifics I am not aware of,
but at least the interface needs to be provided for the new drivers.
One such low-level driver is submitted for review in this patchset.

What you suggest is a lot of additional work including preliminary
design settling discussions and consequent review. Alas I don't have
time for this anymore especially seeing the need for such finer grained
hooking isn't justified by neither the current platform drivers nor
the driver submitted by me. Sorry I can't fulfill your request.

-Sergey

> 
> Rob

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 14/17] PCI: dwc: Check iATU in/outbound ranges setup methods status
  2022-05-16 21:35   ` Rob Herring
@ 2022-05-20 10:36     ` Serge Semin
  0 siblings, 0 replies; 49+ messages in thread
From: Serge Semin @ 2022-05-20 10:36 UTC (permalink / raw)
  To: Rob Herring
  Cc: Serge Semin, Jingoo Han, Gustavo Pimentel, Bjorn Helgaas,
	Lorenzo Pieralisi, Krzysztof Wilczyński, Rahul Tanwar,
	Alexey Malahov, Pavel Parkhomenko, Frank Li,
	Manivannan Sadhasivam, linux-pci, devicetree, linux-kernel

On Mon, May 16, 2022 at 04:35:06PM -0500, Rob Herring wrote:
> On Wed, May 04, 2022 at 12:46:35AM +0300, Serge Semin wrote:
> > Let's make the DWC PCIe RC/EP safer and more verbose for the invalid or
> > failed inbound and outbound iATU windows setups. Needless to say that
> > silently ignoring iATU regions setup errors may cause unpredictable
> > errors. For instance if for some reason a cfg or IO window fails to be
> > activated, then any CFG/IO requested won't reach target PCIe devices and
> > the corresponding accessors will return platform-specific random values.
> > 
> > First of all we need to convert dw_pcie_ep_outbound_atu() method to check
> > whether the specified outbound iATU range is successfully setup. That
> > method is called by the pci_epc_ops.map_addr callback. Thus we'll make the
> > EP-specific CPU->PCIe memory mappings saver.
> > 
> > Secondly since the iATU outbound range programming method now returns the
> > operation status, it will be handy to take that status into account in the
> > pci_ops.{map_bus,read,write} methods. Thus any failed mapping will be
> > immediately noticeable by the PCIe CFG operations requesters.
> > 
> > Finally we need to convert the dw_pcie_setup_rc() method to returning the
> > operation status, since the iATU outbound ranges setup procedure may now
> > fail. It will be especially handy in case if the DW PCIe RC DT-node has
> > invalid/unsupported (dma-)ranges property. Note since the suggested
> > modification causes having too wide code indentation, it is reasonable
> > from maintainability and readability points of view to move the outbound
> > ranges setup procedure in the separate function.
> > 
> > Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> > ---
> >  .../pci/controller/dwc/pcie-designware-ep.c   |   9 +-
> >  .../pci/controller/dwc/pcie-designware-host.c | 149 ++++++++++++------
> >  drivers/pci/controller/dwc/pcie-designware.h  |   5 +-
> >  drivers/pci/controller/dwc/pcie-intel-gw.c    |   6 +-
> 

> I worry that this could regress some platforms that happened to work 
> before. But only one way to find out...

You are right. It may especially in the framework of the outbound iATU
windows setup procedure due to the new alignment constraints added in
the previous patch. But in that case the returned failure is well
justified by the consequences of the improper windows setup. Anyway
let's hope the regression won't happen.

> 
> Reviewed-by: Rob Herring <robh@kernel.org>

Thanks.

-Sergey

> 

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 05/17] PCI: dwc: Convert to using native IP-core versions representation
  2022-05-20  9:29     ` Serge Semin
@ 2022-05-20 15:06       ` Rob Herring
  2022-05-20 16:08         ` Serge Semin
  0 siblings, 1 reply; 49+ messages in thread
From: Rob Herring @ 2022-05-20 15:06 UTC (permalink / raw)
  To: Serge Semin
  Cc: Serge Semin, Jingoo Han, Gustavo Pimentel, Bjorn Helgaas,
	Lorenzo Pieralisi, Krzysztof Wilczyński, Rahul Tanwar,
	Thierry Reding, Jonathan Hunter, Alexey Malahov,
	Pavel Parkhomenko, Frank Li, Manivannan Sadhasivam, linux-pci,
	devicetree, linux-kernel, linux-tegra

On Fri, May 20, 2022 at 12:29:54PM +0300, Serge Semin wrote:
> On Mon, May 16, 2022 at 03:30:03PM -0500, Rob Herring wrote:
> > On Wed, May 04, 2022 at 12:46:26AM +0300, Serge Semin wrote:
> > > Since DWC PCIe v4.70a the controller version can be read from the
> > > PORT_LOGIC.PCIE_VERSION_OFF register. Version is represented in the FourCC
> > > format [1]. It's standard versioning approach for the Synopsys DWC
> > > IP-cores. Moreover some of the DWC kernel drivers already make use of it
> > > to fixup version-dependent functionality (See DWC USB3, Stmicro STMMAC or
> > > recent DW SPI driver). In order to preserve the standard version
> > > representation and prevent the data conversion back and forth, we suggest
> > > to preserve the native version representation in the DWC PCIe driver too
> > > in the same way as it has already been done in the rest of the DWC
> > > drivers. IP-core version reading from the CSR will be introduced in the
> > > next commit together with a simple macro-based API to use it.
> > > 
> > > [1] https://en.wikipedia.org/wiki/FourCC
> > > 
> > > Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> > > ---
> > >  drivers/pci/controller/dwc/pci-keystone.c    | 12 ++++++------
> > >  drivers/pci/controller/dwc/pcie-designware.c |  8 ++++----
> > >  drivers/pci/controller/dwc/pcie-designware.h | 10 +++++++++-
> > >  drivers/pci/controller/dwc/pcie-intel-gw.c   |  4 ++--
> > >  drivers/pci/controller/dwc/pcie-tegra194.c   |  2 +-
> > >  5 files changed, 22 insertions(+), 14 deletions(-)
> > > 
> > > diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c
> > > index d10e5fd0f83c..c51018c68b56 100644
> > > --- a/drivers/pci/controller/dwc/pci-keystone.c
> > > +++ b/drivers/pci/controller/dwc/pci-keystone.c
> > > @@ -109,7 +109,7 @@ struct ks_pcie_of_data {
> > >  	enum dw_pcie_device_mode mode;
> > >  	const struct dw_pcie_host_ops *host_ops;
> > >  	const struct dw_pcie_ep_ops *ep_ops;
> > > -	unsigned int version;
> > > +	u32 version;
> > >  };
> > >  
> > >  struct keystone_pcie {
> > > @@ -1069,19 +1069,19 @@ static int ks_pcie_am654_set_mode(struct device *dev,
> > >  
> > >  static const struct ks_pcie_of_data ks_pcie_rc_of_data = {
> > >  	.host_ops = &ks_pcie_host_ops,
> > > -	.version = 0x365A,
> > > +	.version = DW_PCIE_VER_365A,
> > >  };
> > >  
> > >  static const struct ks_pcie_of_data ks_pcie_am654_rc_of_data = {
> > >  	.host_ops = &ks_pcie_am654_host_ops,
> > >  	.mode = DW_PCIE_RC_TYPE,
> > > -	.version = 0x490A,
> > > +	.version = DW_PCIE_VER_490A,
> > >  };
> > >  
> > >  static const struct ks_pcie_of_data ks_pcie_am654_ep_of_data = {
> > >  	.ep_ops = &ks_pcie_am654_ep_ops,
> > >  	.mode = DW_PCIE_EP_TYPE,
> > > -	.version = 0x490A,
> > > +	.version = DW_PCIE_VER_490A,
> > >  };
> > >  
> > >  static const struct of_device_id ks_pcie_of_match[] = {
> > > @@ -1114,12 +1114,12 @@ static int __init ks_pcie_probe(struct platform_device *pdev)
> > >  	struct device_link **link;
> > >  	struct gpio_desc *gpiod;
> > >  	struct resource *res;
> > > -	unsigned int version;
> > >  	void __iomem *base;
> > >  	u32 num_viewport;
> > >  	struct phy **phy;
> > >  	u32 num_lanes;
> > >  	char name[10];
> > > +	u32 version;
> > >  	int ret;
> > >  	int irq;
> > >  	int i;
> > > @@ -1233,7 +1233,7 @@ static int __init ks_pcie_probe(struct platform_device *pdev)
> > >  		goto err_get_sync;
> > >  	}
> > >  
> > > -	if (pci->version >= 0x480A)
> > > +	if (pci->version >= DW_PCIE_VER_480A)
> > >  		ret = ks_pcie_am654_set_mode(dev, mode);
> > >  	else
> > >  		ret = ks_pcie_set_mode(dev);
> > > diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
> > > index 1682f477bf20..3ebb7bfee10f 100644
> > > --- a/drivers/pci/controller/dwc/pcie-designware.c
> > > +++ b/drivers/pci/controller/dwc/pcie-designware.c
> > > @@ -289,7 +289,7 @@ static void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, u8 func_no,
> > >  	val = type | PCIE_ATU_FUNC_NUM(func_no);
> > >  	if (upper_32_bits(limit_addr) > upper_32_bits(cpu_addr))
> > >  		val |= PCIE_ATU_INCREASE_REGION_SIZE;
> > > -	if (pci->version == 0x490A)
> > > +	if (pci->version == DW_PCIE_VER_490A)
> > >  		val = dw_pcie_enable_ecrc(val);
> > >  	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, val);
> > >  	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
> > > @@ -336,7 +336,7 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
> > >  			   upper_32_bits(cpu_addr));
> > >  	dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT,
> > >  			   lower_32_bits(limit_addr));
> > > -	if (pci->version >= 0x460A)
> > > +	if (pci->version >= DW_PCIE_VER_460A)
> > >  		dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_LIMIT,
> > >  				   upper_32_bits(limit_addr));
> > >  	dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET,
> > > @@ -345,9 +345,9 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
> > >  			   upper_32_bits(pci_addr));
> > >  	val = type | PCIE_ATU_FUNC_NUM(func_no);
> > >  	if (upper_32_bits(limit_addr) > upper_32_bits(cpu_addr) &&
> > > -	    pci->version >= 0x460A)
> > > +	    pci->version >= DW_PCIE_VER_460A)
> > >  		val |= PCIE_ATU_INCREASE_REGION_SIZE;
> > > -	if (pci->version == 0x490A)
> > > +	if (pci->version == DW_PCIE_VER_490A)
> > >  		val = dw_pcie_enable_ecrc(val);
> > >  	dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, val);
> > >  	dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE);
> > > diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
> > > index 7d6e9b7576be..5be43c662176 100644
> > > --- a/drivers/pci/controller/dwc/pcie-designware.h
> > > +++ b/drivers/pci/controller/dwc/pcie-designware.h
> > > @@ -20,6 +20,14 @@
> > >  #include <linux/pci-epc.h>
> > >  #include <linux/pci-epf.h>
> > >  
> > > +/* DWC PCIe IP-core versions (native support since v4.70a) */
> > > +#define DW_PCIE_VER_365A		0x3336352a
> > > +#define DW_PCIE_VER_460A		0x3436302a
> > > +#define DW_PCIE_VER_470A		0x3437302a
> > > +#define DW_PCIE_VER_480A		0x3438302a
> > > +#define DW_PCIE_VER_490A		0x3439302a
> > > +#define DW_PCIE_VER_520A		0x3532302a
> > > +
> > >  /* Parameters for the waiting for link up routine */
> > >  #define LINK_WAIT_MAX_RETRIES		10
> > >  #define LINK_WAIT_USLEEP_MIN		90000
> > > @@ -269,7 +277,7 @@ struct dw_pcie {
> > >  	struct pcie_port	pp;
> > >  	struct dw_pcie_ep	ep;
> > >  	const struct dw_pcie_ops *ops;
> > > -	unsigned int		version;
> > > +	u32			version;
> > >  	int			num_lanes;
> > >  	int			link_gen;
> > >  	u8			n_fts[2];
> > > diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c
> > > index 5ba144924ff8..786af2ba379f 100644
> > > --- a/drivers/pci/controller/dwc/pcie-intel-gw.c
> > > +++ b/drivers/pci/controller/dwc/pcie-intel-gw.c
> > > @@ -59,7 +59,7 @@
> > >  #define RESET_INTERVAL_MS		100
> > >  
> > >  struct intel_pcie_soc {
> > > -	unsigned int	pcie_ver;
> > > +	u32	pcie_ver;
> > 
> 
> > This is not used by the Intel driver code, but just passed to the DWC 
> > core code. Given that and that the IP version is new enough, this should 
> > be removed one the detection is in place.
> 
> Ok. I'll drop it in an additional patch placed after the version
> detection patch in the series. What about the Tegra 194 code? Shall I
> drop it from there too? I've got DW PCIe 4.90a reference manual here.
> It states there are the PCIE_VERSION_NUMBER_OFF and
> PCIE_VERSION_TYPE_OFF registers in the port logic reg space. So the
> removal shall not cause problems.

Yes, anywhere we can remove it would be good.

Rob

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 16/17] PCI: dwc: Introduce generic platform clocks and resets sets
  2022-05-16 22:29   ` Rob Herring
@ 2022-05-20 16:02     ` Serge Semin
  0 siblings, 0 replies; 49+ messages in thread
From: Serge Semin @ 2022-05-20 16:02 UTC (permalink / raw)
  To: Rob Herring
  Cc: Serge Semin, Jingoo Han, Gustavo Pimentel, Bjorn Helgaas,
	Lorenzo Pieralisi, Krzysztof Wilczyński, Alexey Malahov,
	Pavel Parkhomenko, Frank Li, Manivannan Sadhasivam, linux-pci,
	devicetree, linux-kernel

On Mon, May 16, 2022 at 05:29:20PM -0500, Rob Herring wrote:
> On Wed, May 04, 2022 at 12:46:37AM +0300, Serge Semin wrote:
> > Currently almost each platform driver uses its own resets and clocks
> > naming in order to get the corresponding descriptors. It makes the code
> > harder to maintain and comprehend especially seeing the DWC PCIe core main
> > resets and clocks signals set hasn't changed much for about at least one
> > major IP-core release. So in order to organize things around these signals
> > we suggest to create a generic interface for them in accordance with the
> > naming introduced in the DWC PCIe IP-core reference manual:
> > 
> > Clocks:
> > - DBI - data bus interface clock (on some DWC PCIe platforms it's
> >   referred as "pclk", "pcie", "sys", "ahb", "cfg", "iface", "gio", "reg",
> >   "pcie_apb_sys");
> > - MSTR - AXI-bus master interface clock (some DWC PCIe glue drivers refer
> >   to this clock as "port", "bus", "pcie_bus",
> >   "bus_master/master_bus/axi_m", "pcie_aclk");
> > - SLV - AXI-bus slave interface clock (also called as "port", "bus",
> >   "pcie_bus", "bus_slave/slave_bus/axi_s", "pcie_aclk",
> >   "pcie_inbound_axi");
> > - PIPE - Core-PCS PIPE interface clock coming from external PHY (it's
> >   normally named by the platform drivers as just "pipe")
> > - CORE - primary clock of the controller (none of the platform drivers
> >   declare such a clock but in accordance with the ref. manual the devices
> >   may have it separately specified);
> > - AUX - Auxiliary PMC domain clock (it is named by some platforms as
> >   "pcie_aux" and just "aux")
> > - REF - Generic reference clock (it is a generic clock source, which can
> >   be used as a signal source for multiple interfaces, some platforms call
> >   it as "ref", "general", "pcie_phy", "pcie_phy_ref").
> > 
> > Application resets:
> > - DBI - Data-bus interface reset (it's CSR interface clock and is normally
> >   called as "apb" though technically it's not APB but DWC PCIe-specific
> >   interface);
> >   apb, sys,
> > - MSTR -AXI-bus master reset (some platforms call it as "port", "apps",
> >   "bus", "axi_m");
> > - SLV - ABI-bus slave reset (some platforms call it as "port", "apps",
> >   "bus", "axi_s").
> > 
> > Core resets:
> > - NON_STICKY - Non-sticky CSR flags reset;
> > - STICKY - sticky CSR flags reset;
> > - PIPE - PIPE-interface (Core-PCS) logic reset (some platforms call it
> >   just "pipe");
> > - CORE - controller primary reset (resets everything except PMC module,
> >   some platforms refer to this signal as "soft", "pci");
> > - PHY - PCS/PHY block reset (strictly speaking it is normally connected to
> >   the out of the external block, but the reference manual says it must be
> >   available for the PMC working correctly, some existing platforms call it
> >   as "pciephy", "phy", "link");
> > - HOT - PMC hot reset signal (also called as sleep");
> > - PWR - cold reset signal (can be referred as "pwr", "turnoff").
> > 
> > As you can see each platform uses it's own naming for basically the same
> > set of the signals. In the framework of this commit we suggest to add a
> > set of the clocks and signals identifiers and corresponding names for each
> > denoted entity. The platforms will be able to use them to define local
> > mapping tables between the generic identifiers and the available set of
> > the clocks and resets. The tables can be then utilized to create the
> > corresponding bulk-arrays, which in its turn can be passed to the
> > clock/reset-bulk API methods to easily get/enable/disable/put,
> > get/reset/assert/deassert/put all the handlers at once or, if it's
> > required, manipulate with the handlers individually.
> 

> No doubt there is way to much variation here (ummm, Qcom!). Some 
> standardization of names in (new) bindings would be good. That's where 
> we should be defining names IMO.

That's what my patchset starts from. See the {reset,clock,reg}-names
properties definitions in the snps,dw-pcie-common.yaml schema being added
in the framework of the patch
[PATCH v2 01/17] dt-bindings: PCI: dwc: Define common and native DT bindings
in this series.

> 
> On the driver side, I'd like to see the DW core handle clocks/resets/phys 
> at least for the easy cases of just turn on/off all the clocks and 
> toggle all resets. Perhaps even more minimally, move the clk/reset 
> struct pointers to the DWC core.


If it was that easy I would have done that in the first place.) Even
though there is well defined set of the clocks and resets a normal DW
PCIe RP/EP controller can have (see the list in the patch log), the
way they are toggled and the signal sources due to the controller
complexity in the most of the cases is platform-dependent.  Just
enabling and de-asserting all of them likely will never work. So such
function would be just useless. In addition to that there are signals
like app_ltssm_enable or phy/link reset request which needs to be
checked at some point of the reset procedure. The way these signals
are available in the system is also platform-dependent (syscon,
additional CSRs blocks, etc). Thirdly there can be PHY viewport CSRs
available in the DW PCIe Port Logic CSRs which can be used on the
platforms to tune the PHY settings up during the cold reset process.
Finally the platform-specific timings between the reset signals
assertion/de-assertion need to be preserved. Taking all of that into
account would cause having a very complicated generic clock/reset
handling procedure.

Synopsys provides a Verilog module called DWC_pcie_clkrst.v/CLK_RST.v
which can be used by the platform engineers to implement the clocks
generation and cold/hot reset without much software interference. But
it isn't always possible to predict the timings being required on the
real silicon. So the hw engineers mainly omit such module and rely on the
software to implement the proper reset procedure, which due to many
reasons (PHY settings, timings, etc) mostly differs from SoC-to-SoC.

So to speak I don't think that we can (should?) create some truly
generic at least cold start procedure due to too many variables at
stake (though the one implemented in my Baikal-T1 PCIe driver, last
patch in the series, follows the cold procedure described in the hw
reference manual). At least IMO it won't be possible without many
hooks inside such method with platform-dependent signals checking and
delays. So the only generic thing that we can indeed implement is to
have the clock and reset structure pointers inside the DWC core and
create a generic platform resources request/release methods. Even in
that case these methods can't be called from the generic
host/end-point probe procedure and need to be invoked from the
platform-specific host_init/host_deinit methods by the platforms
themself. It's because there are so many platforms already
implemented. If I try to consolidate all their resource
request/release parts in a single method, it most likely cause the
regression.

What do you think if I would just create the arrays with pointers to the
generic clk/reset structures in the DWC private data, implement their
request (release won't be required due to devm-methods utilization)
procedure and use it in the framework of my driver only for now?

> 
> IOW, I'm not sure this patch is really helpful without some of the above 
> happening.

Well, I've provided a driver (the last patch in this series) which
design can be used as a reference to implement the clocks/reset
request procedure with a help of the submitted in this patch tables.
But if you are agree with creating a generic resource request method,
the clocks/reset request part of it can be re-designed, simplified
and moved to that method.

-Sergey

> 
> Rob

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 05/17] PCI: dwc: Convert to using native IP-core versions representation
  2022-05-20 15:06       ` Rob Herring
@ 2022-05-20 16:08         ` Serge Semin
  0 siblings, 0 replies; 49+ messages in thread
From: Serge Semin @ 2022-05-20 16:08 UTC (permalink / raw)
  To: Rob Herring
  Cc: Serge Semin, Jingoo Han, Gustavo Pimentel, Bjorn Helgaas,
	Lorenzo Pieralisi, Krzysztof Wilczyński, Rahul Tanwar,
	Thierry Reding, Jonathan Hunter, Alexey Malahov,
	Pavel Parkhomenko, Frank Li, Manivannan Sadhasivam, linux-pci,
	devicetree, linux-kernel, linux-tegra

On Fri, May 20, 2022 at 10:06:09AM -0500, Rob Herring wrote:
> On Fri, May 20, 2022 at 12:29:54PM +0300, Serge Semin wrote:
> > On Mon, May 16, 2022 at 03:30:03PM -0500, Rob Herring wrote:
> > > On Wed, May 04, 2022 at 12:46:26AM +0300, Serge Semin wrote:
> > > > Since DWC PCIe v4.70a the controller version can be read from the
> > > > PORT_LOGIC.PCIE_VERSION_OFF register. Version is represented in the FourCC
> > > > format [1]. It's standard versioning approach for the Synopsys DWC
> > > > IP-cores. Moreover some of the DWC kernel drivers already make use of it
> > > > to fixup version-dependent functionality (See DWC USB3, Stmicro STMMAC or
> > > > recent DW SPI driver). In order to preserve the standard version
> > > > representation and prevent the data conversion back and forth, we suggest
> > > > to preserve the native version representation in the DWC PCIe driver too
> > > > in the same way as it has already been done in the rest of the DWC
> > > > drivers. IP-core version reading from the CSR will be introduced in the
> > > > next commit together with a simple macro-based API to use it.
> > > > 
> > > > [1] https://en.wikipedia.org/wiki/FourCC
> > > > 
> > > > Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> > > > ---
> > > >  drivers/pci/controller/dwc/pci-keystone.c    | 12 ++++++------
> > > >  drivers/pci/controller/dwc/pcie-designware.c |  8 ++++----
> > > >  drivers/pci/controller/dwc/pcie-designware.h | 10 +++++++++-
> > > >  drivers/pci/controller/dwc/pcie-intel-gw.c   |  4 ++--
> > > >  drivers/pci/controller/dwc/pcie-tegra194.c   |  2 +-
> > > >  5 files changed, 22 insertions(+), 14 deletions(-)
> > > > 
> > > > diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c
> > > > index d10e5fd0f83c..c51018c68b56 100644
> > > > --- a/drivers/pci/controller/dwc/pci-keystone.c
> > > > +++ b/drivers/pci/controller/dwc/pci-keystone.c
> > > > @@ -109,7 +109,7 @@ struct ks_pcie_of_data {
> > > >  	enum dw_pcie_device_mode mode;
> > > >  	const struct dw_pcie_host_ops *host_ops;
> > > >  	const struct dw_pcie_ep_ops *ep_ops;
> > > > -	unsigned int version;
> > > > +	u32 version;
> > > >  };
> > > >  
> > > >  struct keystone_pcie {
> > > > @@ -1069,19 +1069,19 @@ static int ks_pcie_am654_set_mode(struct device *dev,
> > > >  
> > > >  static const struct ks_pcie_of_data ks_pcie_rc_of_data = {
> > > >  	.host_ops = &ks_pcie_host_ops,
> > > > -	.version = 0x365A,
> > > > +	.version = DW_PCIE_VER_365A,
> > > >  };
> > > >  
> > > >  static const struct ks_pcie_of_data ks_pcie_am654_rc_of_data = {
> > > >  	.host_ops = &ks_pcie_am654_host_ops,
> > > >  	.mode = DW_PCIE_RC_TYPE,
> > > > -	.version = 0x490A,
> > > > +	.version = DW_PCIE_VER_490A,
> > > >  };
> > > >  
> > > >  static const struct ks_pcie_of_data ks_pcie_am654_ep_of_data = {
> > > >  	.ep_ops = &ks_pcie_am654_ep_ops,
> > > >  	.mode = DW_PCIE_EP_TYPE,
> > > > -	.version = 0x490A,
> > > > +	.version = DW_PCIE_VER_490A,
> > > >  };
> > > >  
> > > >  static const struct of_device_id ks_pcie_of_match[] = {
> > > > @@ -1114,12 +1114,12 @@ static int __init ks_pcie_probe(struct platform_device *pdev)
> > > >  	struct device_link **link;
> > > >  	struct gpio_desc *gpiod;
> > > >  	struct resource *res;
> > > > -	unsigned int version;
> > > >  	void __iomem *base;
> > > >  	u32 num_viewport;
> > > >  	struct phy **phy;
> > > >  	u32 num_lanes;
> > > >  	char name[10];
> > > > +	u32 version;
> > > >  	int ret;
> > > >  	int irq;
> > > >  	int i;
> > > > @@ -1233,7 +1233,7 @@ static int __init ks_pcie_probe(struct platform_device *pdev)
> > > >  		goto err_get_sync;
> > > >  	}
> > > >  
> > > > -	if (pci->version >= 0x480A)
> > > > +	if (pci->version >= DW_PCIE_VER_480A)
> > > >  		ret = ks_pcie_am654_set_mode(dev, mode);
> > > >  	else
> > > >  		ret = ks_pcie_set_mode(dev);
> > > > diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
> > > > index 1682f477bf20..3ebb7bfee10f 100644
> > > > --- a/drivers/pci/controller/dwc/pcie-designware.c
> > > > +++ b/drivers/pci/controller/dwc/pcie-designware.c
> > > > @@ -289,7 +289,7 @@ static void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, u8 func_no,
> > > >  	val = type | PCIE_ATU_FUNC_NUM(func_no);
> > > >  	if (upper_32_bits(limit_addr) > upper_32_bits(cpu_addr))
> > > >  		val |= PCIE_ATU_INCREASE_REGION_SIZE;
> > > > -	if (pci->version == 0x490A)
> > > > +	if (pci->version == DW_PCIE_VER_490A)
> > > >  		val = dw_pcie_enable_ecrc(val);
> > > >  	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, val);
> > > >  	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
> > > > @@ -336,7 +336,7 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
> > > >  			   upper_32_bits(cpu_addr));
> > > >  	dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT,
> > > >  			   lower_32_bits(limit_addr));
> > > > -	if (pci->version >= 0x460A)
> > > > +	if (pci->version >= DW_PCIE_VER_460A)
> > > >  		dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_LIMIT,
> > > >  				   upper_32_bits(limit_addr));
> > > >  	dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET,
> > > > @@ -345,9 +345,9 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
> > > >  			   upper_32_bits(pci_addr));
> > > >  	val = type | PCIE_ATU_FUNC_NUM(func_no);
> > > >  	if (upper_32_bits(limit_addr) > upper_32_bits(cpu_addr) &&
> > > > -	    pci->version >= 0x460A)
> > > > +	    pci->version >= DW_PCIE_VER_460A)
> > > >  		val |= PCIE_ATU_INCREASE_REGION_SIZE;
> > > > -	if (pci->version == 0x490A)
> > > > +	if (pci->version == DW_PCIE_VER_490A)
> > > >  		val = dw_pcie_enable_ecrc(val);
> > > >  	dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, val);
> > > >  	dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE);
> > > > diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
> > > > index 7d6e9b7576be..5be43c662176 100644
> > > > --- a/drivers/pci/controller/dwc/pcie-designware.h
> > > > +++ b/drivers/pci/controller/dwc/pcie-designware.h
> > > > @@ -20,6 +20,14 @@
> > > >  #include <linux/pci-epc.h>
> > > >  #include <linux/pci-epf.h>
> > > >  
> > > > +/* DWC PCIe IP-core versions (native support since v4.70a) */
> > > > +#define DW_PCIE_VER_365A		0x3336352a
> > > > +#define DW_PCIE_VER_460A		0x3436302a
> > > > +#define DW_PCIE_VER_470A		0x3437302a
> > > > +#define DW_PCIE_VER_480A		0x3438302a
> > > > +#define DW_PCIE_VER_490A		0x3439302a
> > > > +#define DW_PCIE_VER_520A		0x3532302a
> > > > +
> > > >  /* Parameters for the waiting for link up routine */
> > > >  #define LINK_WAIT_MAX_RETRIES		10
> > > >  #define LINK_WAIT_USLEEP_MIN		90000
> > > > @@ -269,7 +277,7 @@ struct dw_pcie {
> > > >  	struct pcie_port	pp;
> > > >  	struct dw_pcie_ep	ep;
> > > >  	const struct dw_pcie_ops *ops;
> > > > -	unsigned int		version;
> > > > +	u32			version;
> > > >  	int			num_lanes;
> > > >  	int			link_gen;
> > > >  	u8			n_fts[2];
> > > > diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c
> > > > index 5ba144924ff8..786af2ba379f 100644
> > > > --- a/drivers/pci/controller/dwc/pcie-intel-gw.c
> > > > +++ b/drivers/pci/controller/dwc/pcie-intel-gw.c
> > > > @@ -59,7 +59,7 @@
> > > >  #define RESET_INTERVAL_MS		100
> > > >  
> > > >  struct intel_pcie_soc {
> > > > -	unsigned int	pcie_ver;
> > > > +	u32	pcie_ver;
> > > 
> > 
> > > This is not used by the Intel driver code, but just passed to the DWC 
> > > core code. Given that and that the IP version is new enough, this should 
> > > be removed one the detection is in place.
> > 
> > Ok. I'll drop it in an additional patch placed after the version
> > detection patch in the series. What about the Tegra 194 code? Shall I
> > drop it from there too? I've got DW PCIe 4.90a reference manual here.
> > It states there are the PCIE_VERSION_NUMBER_OFF and
> > PCIE_VERSION_TYPE_OFF registers in the port logic reg space. So the
> > removal shall not cause problems.
> 

> Yes, anywhere we can remove it would be good.

Ok. The modification will concern both Intel GW and Tegra 194 (in two
separate patches). Keystone PCIe platform driver won't be changed
since it uses version ID in before the generic DW PCIe host/EP probe
method invocation.

-Sergey

> 
> Rob

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 15/17] PCI: dwc: Introduce dma-ranges property support for RC-host
  2022-05-19 15:21               ` Manivannan Sadhasivam
@ 2022-05-20 18:21                 ` Serge Semin
  0 siblings, 0 replies; 49+ messages in thread
From: Serge Semin @ 2022-05-20 18:21 UTC (permalink / raw)
  To: Manivannan Sadhasivam
  Cc: Serge Semin, Jingoo Han, Gustavo Pimentel, Bjorn Helgaas,
	Lorenzo Pieralisi, Rob Herring, Krzysztof Wilczyński,
	Alexey Malahov, Pavel Parkhomenko, Frank Li, Rob Herring,
	linux-pci, devicetree, linux-kernel

On Thu, May 19, 2022 at 08:51:16PM +0530, Manivannan Sadhasivam wrote:
> On Thu, May 19, 2022 at 01:52:38PM +0300, Serge Semin wrote:
> 
> [...]
> 
> > > > > > > > diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
> > > > > > > > index 747e252c09e6..33718ed6c511 100644
> > > > > > > > --- a/drivers/pci/controller/dwc/pcie-designware.c
> > > > > > > > +++ b/drivers/pci/controller/dwc/pcie-designware.c
> > > > > > > > @@ -397,8 +397,61 @@ static inline void dw_pcie_writel_atu_ib(struct dw_pcie *pci, u32 index, u32 reg
> > > > > > > >  	dw_pcie_writel_atu(pci, PCIE_ATU_REGION_DIR_IB, index, reg, val);
> > > > > > > >  }
> > > > > > > >  
> > > > > > > > -int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
> > > > > > > > -			     int type, u64 cpu_addr, u8 bar)
> > > > > > > > +int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,
> > > > > > > > +			     u64 cpu_addr, u64 pci_addr, u64 size)
> > > > > > > > +{
> > > > > > > > +	u64 limit_addr = pci_addr + size - 1;
> > > > > > > > +	u32 retries, val;
> > > > > > > > +
> > > > > > > > +	if ((limit_addr & ~pci->region_limit) != (pci_addr & ~pci->region_limit) ||
> > > > > > > > +	    !IS_ALIGNED(cpu_addr, pci->region_align) ||
> > > > > > > > +	    !IS_ALIGNED(pci_addr, pci->region_align) ||
> > > > > > > > +	    !IS_ALIGNED(size, pci->region_align) ||
> > > > > > > 
> > > > > > 
> > > > > > > Why do you want the size to be aligned? What if I want to transfer a small size
> > > > > > > buffer?
> > > > > > > 
> > > > > > > Same question applies to outbound programming as well.
> > > > > > 
> > > > > > You can't program a region with the unaligned size by the DW PCIe CSRs
> > > > > > design. The limit address lower bits are read-only and fixed with
> > > > > > one's in accordance with the IP-core synthesize parameter
> > > > > > CX_ATU_MIN_REGION_SIZE. So the mapping is always performed in the
> > > > > > CX_ATU_MIN_REGION_SIZE chunks.
> > > > > > 
> > > > > > IATU_LIMIT_ADDR_OFF_{IN,OUT}BOUND.LIMIT_ADDR_HW = 
> > > > > > {(CX_ATU_MIN_REGION_SIZE == 65536) ? "0xffff" :
> > > > > >  (CX_ATU_MIN_REGION_SIZE == 32768) ? "0x7fff" :
> > > > > >  (CX_ATU_MIN_REGION_SIZE == 16384) ? "0x3fff" :
> > > > > >  (CX_ATU_MIN_REGION_SIZE == 8192)  ? "0x1fff" :
> > > > > >  (CX_ATU_MIN_REGION_SIZE == 4096)  ? "0xfff" : "0xffff"}
> > > > > > 
> > > > > 
> > > > 
> > > > > Right. Even though the minimum size that could be mapped is 4k, I could still
> > > > > use that 4k size for mapping small buffers also. So you should not be erroring
> > > > > out here if the size is not aligned. 
> > > > 
> > > > Why would you need to do that? Even if you do and the operation
> > > > doesn't return an error (or at least splash the syslog with a
> > > > warning), the hardware would expand the mapping up to the aligned size
> > > > anyway. Such implicit behavior would have given your software an
> > > > impression that the mapping was performed in the way you asked with
> > > > the size you specified so the upper part of the unaligned range is
> > > > free to be used for something else. If the range is accessed, instead
> > > > of a bus error or silent IO termination it may cause unexpected result
> > > > of creating random PCIe bus traffic. So I'd rather have the
> > > > code/platform setup fixed right from the start instead of waiting for
> > > > the hard to find bug cause.
> > > > 
> > > 
> > 
> > > The application I'm working on is MHI bus. As per the design, it needs to copy
> > > 16byte data to ring buffers in the host memory. If I use iATU, then I
> > > cannot copy those small data with the size alignment.
> > 
> > First of all I don't see any driver using the DW PCIe iATU mapping
> > functions directly. They are only utilized in the framework of the
> > "ranges" and "dma-ranges" DT properties.
> 

> Not true. The PCI_EPF_TEST and PCI_EPF_NTB applications use iATU mpping function
> through EPC ops.
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/pci/endpoint/functions/pci-epf-test.c#n250
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/pci/endpoint/pci-epc-core.c#n476
> 
> Now that I've referred, we need to check if these drivers still work on top of
> your patches. These are not supported on my platform, so perhaps Frank can
> test?

Wow, you are right. I've missed the epf drivers completely. They don't
take the buffer size alignment into account. This patch and the
range-related patch most likely will regress both of these drivers.(
Alas they can't be fixed that easily.

1) drivers/pci/endpoint/functions/pci-epf-test.c retrieves size from
the user-space by means of an ioctl implemented in the
drivers/misc/pci_endpoint_test.c char-device driver (see what is
written at the PCI_ENDPOINT_TEST_SIZE offset). It doesn't make
sure that aside with the base address the size needs to be also
aligned.

2) drivers/pci/endpoint/functions/pci-epf-ntb.c uses the size based on
the NTB entity. It's either Door-Bell entry or a Memory Window. If the
Door-bell/MSI-X part can be more or less easily fixed, the MW-part
can't because the PCIe-bus part of the EP-function implementation
doesn't provide such information. After fixing that I would have also
needed to fix the drivers/ntb/hw/epf/ntb_hw_epf.c driver so the
callback method mw_get_align() would return the size alignment
constraint.

So to speak without corresponding HW at hand I won't be able to
successfully fix them.

> 
> > If the application you are
> > referring to your private code, then it can't be a justification.
> 
> I should have mentioned it, but my application is not private. It is partly
> available in linux-next:
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/tree/drivers/bus/mhi/ep/ring.c#n47
> https://git.linaro.org/landing-teams/working/qualcomm/kernel.git/tree/drivers/pci/endpoint/functions/pci-epf-mhi.c?h=tracking-qcomlt-sdx55-drivers#n121
> 
> > Secondly if your application uses them then what about just extending
> > the mapping range size while still access the lowest 15 bytes only? In
> > that case you would create a more comprehensive software which would
> > be aware of the hardware constraints.
> > 
> 

> Hmm... I'm already doing a similar hack for getting the aligned address due to
> iATU limitation, but I think doing the same for size should also work.
> 
> Thinking again, I agree with the alignment check. Thanks for the explanations.
> But let's make sure the existing EPF drivers still work.

Don't bother with re-developing our code. Since I can't fix the
denoted client drivers we have no choice but to do as you say and drop
the size alignment check. Thanks for pointing this out.

-Sergey

> 
> Thanks,
> Mani
> 
> > > 
> > > > > I know that it is a waste of memory but that doesn't mean that it won't work.
> > > > 
> > > > The correct statement in this case would be "it won't work in a way
> > > > you expected, but with the implicit side effect applied to the memory
> > > > above the requested one."
> > > > 
> > > 
> > 
> > > Agree but that would only happen when the application does out of bound
> > > access and in that case the issue is with the application.
> > 
> > Not only in that case, but anyway how would such application be aware
> > of the out of bounds access? Returning an error in case if the
> > requested mapping can't be performed with the specified parameters is
> > a possible solution. So the application would be aware of the hardware
> > constraints and be sure it perceives them right. Otherwise the
> > consequences of the out of bounds access would be very unexpected
> > since the mapping is performed only for the small buffer.
> > 
> > -Sergey
> > 
> > > 
> > > Thanks,
> > > Mani
> > > 
> > > > -Sergey
> > > > 
> > > > > 
> > > > > Thanks,
> > > > > Mani
> > > > > 
> > > > > > -Sergey
> > > > > > 
> > > > > > > 
> > > > > > > Thanks,
> > > > > > > Mani
> > > > > > > 
> > > > > > > -- 
> > > > > > > மணிவண்ணன் சதாசிவம்
> > > > > 
> > > > > -- 
> > > > > மணிவண்ணன் சதாசிவம்
> 
> -- 
> மணிவண்ணன் சதாசிவம்

^ permalink raw reply	[flat|nested] 49+ messages in thread

end of thread, other threads:[~2022-05-20 18:21 UTC | newest]

Thread overview: 49+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-05-03 21:46 [PATCH v2 00/17] PCI: dwc: Add dma-ranges/YAML-schema/Baikal-T1 support Serge Semin
2022-05-03 21:46 ` [PATCH v2 01/17] dt-bindings: PCI: dwc: Define common and native DT bindings Serge Semin
2022-05-16 20:11   ` Rob Herring
2022-05-19 10:26     ` Serge Semin
2022-05-03 21:46 ` [PATCH v2 02/17] dt-bindings: PCI: dwc: Add Baikal-T1 PCIe Root Port bindings Serge Semin
2022-05-03 21:46 ` [PATCH v2 03/17] PCI: dwc: Add more verbose link-up message Serge Semin
2022-05-16 20:18   ` Rob Herring
2022-05-03 21:46 ` [PATCH v2 04/17] PCI: dwc: Detect iATU settings after getting "addr_space" resource Serge Semin
2022-05-16 20:21   ` Rob Herring
2022-05-03 21:46 ` [PATCH v2 05/17] PCI: dwc: Convert to using native IP-core versions representation Serge Semin
2022-05-16 20:30   ` Rob Herring
2022-05-20  9:29     ` Serge Semin
2022-05-20 15:06       ` Rob Herring
2022-05-20 16:08         ` Serge Semin
2022-05-16 20:31   ` Rob Herring
2022-05-03 21:46 ` [PATCH v2 06/17] PCI: dwc: Add IP-core version detection procedure Serge Semin
2022-05-16 20:32   ` Rob Herring
2022-05-03 21:46 ` [PATCH v2 07/17] PCI: dwc: Introduce Synopsys IP-core versions/types interface Serge Semin
2022-05-16 20:33   ` Rob Herring
2022-05-03 21:46 ` [PATCH v2 08/17] PCI: dwc: Add host de-initialization callback Serge Semin
2022-05-16 20:48   ` Rob Herring
2022-05-20 10:20     ` Serge Semin
2022-05-03 21:46 ` [PATCH v2 09/17] PCI: dwc: Drop inbound iATU types enumeration - dw_pcie_as_type Serge Semin
2022-05-03 21:46 ` [PATCH v2 10/17] PCI: dwc: Drop iATU regions enumeration - dw_pcie_region_type Serge Semin
2022-05-16 20:49   ` Rob Herring
2022-05-03 21:46 ` [PATCH v2 11/17] PCI: dwc: Simplify in/outbound iATU setup methods Serge Semin
2022-05-12 14:01   ` Manivannan Sadhasivam
2022-05-12 19:22     ` Serge Semin
2022-05-03 21:46 ` [PATCH v2 12/17] PCI: dwc: Add iATU regions size detection procedure Serge Semin
2022-05-16 21:01   ` Rob Herring
2022-05-03 21:46 ` [PATCH v2 13/17] PCI: dwc: Verify in/out regions against iATU constraints Serge Semin
2022-05-16 21:07   ` Rob Herring
2022-05-03 21:46 ` [PATCH v2 14/17] PCI: dwc: Check iATU in/outbound ranges setup methods status Serge Semin
2022-05-16 21:35   ` Rob Herring
2022-05-20 10:36     ` Serge Semin
2022-05-03 21:46 ` [PATCH v2 15/17] PCI: dwc: Introduce dma-ranges property support for RC-host Serge Semin
2022-05-12 13:57   ` Manivannan Sadhasivam
2022-05-12 19:41     ` Serge Semin
2022-05-17 17:20       ` Manivannan Sadhasivam
2022-05-18 19:26         ` Serge Semin
2022-05-19  7:40           ` Manivannan Sadhasivam
2022-05-19 10:52             ` Serge Semin
2022-05-19 15:21               ` Manivannan Sadhasivam
2022-05-20 18:21                 ` Serge Semin
2022-05-16 21:42   ` Rob Herring
2022-05-03 21:46 ` [PATCH v2 16/17] PCI: dwc: Introduce generic platform clocks and resets sets Serge Semin
2022-05-16 22:29   ` Rob Herring
2022-05-20 16:02     ` Serge Semin
2022-05-03 21:46 ` [PATCH v2 17/17] PCI: dwc: Add Baikal-T1 PCIe controller support Serge Semin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).