From patchwork Thu Mar 24 01:37:19 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 12793839 X-Patchwork-Delegate: lorenzo.pieralisi@arm.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 35F96C4332F for ; Mon, 28 Mar 2022 15:24:37 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237183AbiC1P0M (ORCPT ); Mon, 28 Mar 2022 11:26:12 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45326 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237146AbiC1PZ4 (ORCPT ); Mon, 28 Mar 2022 11:25:56 -0400 Received: from mail.baikalelectronics.ru (mail.baikalelectronics.com [87.245.175.226]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 97A0622511; Mon, 28 Mar 2022 08:24:12 -0700 (PDT) Received: from mail.baikalelectronics.ru (unknown [192.168.51.25]) by mail.baikalelectronics.ru (Postfix) with ESMTP id E8D6439CB7F; Thu, 24 Mar 2022 04:37:48 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mail.baikalelectronics.ru E8D6439CB7F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baikalelectronics.ru; s=mail; t=1648085869; bh=m8ge0j6w4fRAgO0H2GK3vu1XJo7cjeYZ3gyjCgKpYsU=; h=From:To:CC:Subject:Date:In-Reply-To:References:From; b=DH/Rx6Dv2DjwVmAzQqG46IqfTWJFJbqbJLmjGn+drbXwkWlxHSkMBKmjLEWxxnrN9 +kt2hd3Mn9d4anaXG9Eepiw5ctJkwQgParNUXPoPYhh8KxlNiatNhPHVvOWhNkKyQR xDamXL1osg6JfoTJSeuMVdoAW93Rh9ryX+rvXaVg= Received: from localhost (192.168.168.10) by mail (192.168.51.25) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Thu, 24 Mar 2022 04:37:48 +0300 From: Serge Semin To: Jingoo Han , Gustavo Pimentel , Bjorn Helgaas , Richard Zhu , Lucas Stach , Rob Herring , Shawn Guo , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , NXP Linux Team , Xiaowei Song , Binghui Wang , Paul Walmsley , Greentime Hu , Palmer Dabbelt , Nobuhiro Iwamatsu CC: Serge Semin , Serge Semin , Alexey Malahov , Pavel Parkhomenko , Lorenzo Pieralisi , Rob Herring , =?utf-8?q?Krzysztof_Wilczy=C5=84ski?= , Frank Li , Manivannan Sadhasivam , , , , , Subject: [PATCH 01/16] dt-bindings: PCI: dwc: Define generic and native DT bindings Date: Thu, 24 Mar 2022 04:37:19 +0300 Message-ID: <20220324013734.18234-2-Sergey.Semin@baikalelectronics.ru> In-Reply-To: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> References: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org 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. 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 "$defs" 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 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 --- Rob, if you comment the "select: false" line out you'll get a lot of "'*' is not of type 'array'" errors on the dt_bindings_check command execution. I have spend tons of time trying to figure out what was wrong, but still failed to find out a solution. For some reason the "$ref"-ed schemas defined under the "$defs" basic property are considered by the parser as defining an array. No matter whether I specified the "type: string" keyword or not, as long as there is "enum" or "oneOf" keyword used in the "*-names" property definition in "$defs" it's considered as describing an array thus causing the errors like: > 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 As soon as I manually moved the same "$defs"-schemas into the places they are referenced to, the errors disappeared and the DT-schemas validation worked as expected by the semantics. As I see it there must be some bug in the parser, otherwise I am out of ideas how to implement the suggested in this patch design pattern. Rob, could you take a look at the DT-schemas and help me out with debugging the problem described above? --- .../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 | 298 ++++++++++++++++++ .../bindings/pci/snps,dw-pcie-ep.yaml | 143 ++++++--- .../devicetree/bindings/pci/snps,dw-pcie.yaml | 189 +++++++---- .../bindings/pci/toshiba,visconti-pcie.yaml | 2 +- 7 files changed, 524 insertions(+), 121 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 643a6333b07b..ccb5cfdf869f 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 392f0ab488c2..437614257935 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 - Greentime Hu 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..27fe1f5c450f --- /dev/null +++ b/Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml @@ -0,0 +1,298 @@ +# 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 + - Gustavo Pimentel + +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 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 + +$defs: + 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 + the 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, which + are selected by setting CDM/ELBI = 1 and CS2 = 0. + enum: [ elbi, appl, app ] + - 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: + 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: [ link, 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..c3bbe90264dc 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: 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: snps,dw-pcie-common.yaml#/$defs/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: snps,dw-pcie-common.yaml#/$defs/interrupt-names + + clocks: + minItems: 1 + maxItems: 7 + + clock-names: + minItems: 1 + maxItems: 7 + items: + $ref: snps,dw-pcie-common.yaml#/$defs/clock-names + + resets: + minItems: 1 + maxItems: 10 + + reset-names: + minItems: 1 + maxItems: 10 + items: + $ref: snps,dw-pcie-common.yaml#/$defs/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..6c397ae3c71a 100644 --- a/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/snps,dw-pcie.yaml @@ -13,20 +13,36 @@ maintainers: 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 this schema. +select: false + allOf: - $ref: /schemas/pci/pci-bus.yaml# + - $ref: 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,116 @@ 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: snps,dw-pcie-common.yaml#/$defs/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: snps,dw-pcie-common.yaml#/$defs/interrupt-names + - $ref: '#/$defs/interrupt-names' + allOf: + - contains: + const: msi + + clocks: + minItems: 1 + maxItems: 7 + + clock-names: + minItems: 1 + maxItems: 7 + items: + $ref: snps,dw-pcie-common.yaml#/$defs/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: snps,dw-pcie-common.yaml#/$defs/reset-names required: + - compatible - reg - reg-names - - compatible + - interrupts + - interrupt-names + +additionalProperties: true + +$defs: + 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/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: From patchwork Thu Mar 24 01:37:20 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 12793826 X-Patchwork-Delegate: lorenzo.pieralisi@arm.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id BA2EEC433FE for ; Mon, 28 Mar 2022 15:24:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235921AbiC1PZv (ORCPT ); Mon, 28 Mar 2022 11:25:51 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44738 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232394AbiC1PZu (ORCPT ); Mon, 28 Mar 2022 11:25:50 -0400 Received: from mail.baikalelectronics.ru (mail.baikalelectronics.com [87.245.175.226]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id B1E091F63E; Mon, 28 Mar 2022 08:24:08 -0700 (PDT) Received: from mail.baikalelectronics.ru (unknown [192.168.51.25]) by mail.baikalelectronics.ru (Postfix) with ESMTP id AC95E1E4920; Thu, 24 Mar 2022 04:37:49 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mail.baikalelectronics.ru AC95E1E4920 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baikalelectronics.ru; s=mail; t=1648085869; bh=fFFXY6fc+neLhZ7RKAfQ7NRj0Ug9JDpw+U5GeFCAV2I=; h=From:To:CC:Subject:Date:In-Reply-To:References:From; b=bktRlkRxx9ngmRg9Rnb5rox7S15QW1TZ7qu8efJF/OGoERR84KdM8NY5aGkdANAWR W9NN1/HsbREdEIkhgHH7hUkvLosyz5tH3NVnBeaNJZYTU4fHLO5cLaaz6TAiksMCWZ aYnw4Vit4Mp5xERnfhO0TUjaezq6bf4BCtjxxFVU= Received: from localhost (192.168.168.10) by mail (192.168.51.25) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Thu, 24 Mar 2022 04:37:49 +0300 From: Serge Semin To: Jingoo Han , Gustavo Pimentel , Bjorn Helgaas , Rob Herring , Serge Semin CC: Serge Semin , Alexey Malahov , Pavel Parkhomenko , Lorenzo Pieralisi , Rob Herring , =?utf-8?q?Krzysztof_Wilczy=C5=84ski?= , Frank Li , Manivannan Sadhasivam , , , Subject: [PATCH 02/16] dt-bindings: PCI: dwc: Add Baikal-T1 PCIe Root Port bindings Date: Thu, 24 Mar 2022 04:37:20 +0300 Message-ID: <20220324013734.18234-3-Sergey.Semin@baikalelectronics.ru> In-Reply-To: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> References: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org 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 --- Rob, this DT-bindings most likely will fail the dt_bindings_check evaluations with the "'*' is not of type 'array'" errors for the same reason described in the previous patch. Let's discuss the problem there. --- .../bindings/pci/baikal,bt1-pcie.yaml | 148 ++++++++++++++++++ 1 file changed, 148 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..f34438330a86 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/baikal,bt1-pcie.yaml @@ -0,0 +1,148 @@ +# 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 + +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. + +allOf: + - $ref: /schemas/pci/pci-bus.yaml# + - $ref: snps,dw-pcie.yaml# + +properties: + compatible: + contains: + const: baikal,bt1-pcie + + reg: + description: + DBI, DBI2 and at least 4KB outbound iATU-capable region are available. + 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 ] + + 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>; + }; +... From patchwork Thu Mar 24 01:37:21 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 12793828 X-Patchwork-Delegate: lorenzo.pieralisi@arm.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E0C11C43219 for ; Mon, 28 Mar 2022 15:24:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236410AbiC1PZw (ORCPT ); Mon, 28 Mar 2022 11:25:52 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44796 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233949AbiC1PZu (ORCPT ); Mon, 28 Mar 2022 11:25:50 -0400 Received: from mail.baikalelectronics.ru (mail.baikalelectronics.com [87.245.175.226]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id B212A21E16; Mon, 28 Mar 2022 08:24:08 -0700 (PDT) Received: from mail.baikalelectronics.ru (unknown [192.168.51.25]) by mail.baikalelectronics.ru (Postfix) with ESMTP id 62E071E4921; Thu, 24 Mar 2022 04:37:50 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mail.baikalelectronics.ru 62E071E4921 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baikalelectronics.ru; s=mail; t=1648085870; bh=5poCL7Fl1MRxC/ab4KM0VzOdI02Q6TWhW/r62tXhxsA=; h=From:To:CC:Subject:Date:In-Reply-To:References:From; b=kr4NQTc2nSNnZN+FLv4FIXAzsX7PU4WFDuZVX0+rlIiEQ438h3ToAG5NInlTxlqK2 b0ogBWmeXzP+HsIXMh3Yr4VgID/OzoIL11znRV8eiynbZGoXiA9JOtiXeBPrvdjNRO tLfa2w2bWFEfhQixdp24kT5Bp03+w7D3t23on4Dc= Received: from localhost (192.168.168.10) by mail (192.168.51.25) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Thu, 24 Mar 2022 04:37:50 +0300 From: Serge Semin To: Jingoo Han , Gustavo Pimentel , Bjorn Helgaas , Lorenzo Pieralisi , Rob Herring , =?utf-8?q?Krzysztof_Wilczy=C5=84ski?= CC: Serge Semin , Serge Semin , Alexey Malahov , Pavel Parkhomenko , Frank Li , Manivannan Sadhasivam , Rob Herring , , , Subject: [PATCH 03/16] PCI: dwc: Add more verbose link-up message Date: Thu, 24 Mar 2022 04:37:21 +0300 Message-ID: <20220324013734.18234-4-Sergey.Semin@baikalelectronics.ru> In-Reply-To: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> References: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org 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 --- drivers/pci/controller/dwc/pcie-designware.c | 22 +++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 6e81264fdfb4..f1693e25afcb 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -528,14 +528,26 @@ int dw_pcie_wait_for_link(struct dw_pcie *pci) /* 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) { + u32 offset, val; + + offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); + val = dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKSTA); + + 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; + } + + dev_err(pci->dev, "Phy link never came up\n"); return -ETIMEDOUT; } From patchwork Thu Mar 24 01:37:22 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 12793829 X-Patchwork-Delegate: lorenzo.pieralisi@arm.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E0827C433FE for ; Mon, 28 Mar 2022 15:24:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236649AbiC1PZw (ORCPT ); Mon, 28 Mar 2022 11:25:52 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44822 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235831AbiC1PZv (ORCPT ); Mon, 28 Mar 2022 11:25:51 -0400 Received: from mail.baikalelectronics.ru (mail.baikalelectronics.com [87.245.175.226]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id B1EFB20F43; Mon, 28 Mar 2022 08:24:08 -0700 (PDT) Received: from mail.baikalelectronics.ru (unknown [192.168.51.25]) by mail.baikalelectronics.ru (Postfix) with ESMTP id 74BB41E4922; Thu, 24 Mar 2022 04:37:51 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mail.baikalelectronics.ru 74BB41E4922 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baikalelectronics.ru; s=mail; t=1648085871; bh=tJpnuJm4c0TW37HoGssMgug6vK/irHkBZYOfQQxofNM=; h=From:To:CC:Subject:Date:In-Reply-To:References:From; b=CBNoqDaTnF8d4W4kvIUVX8JOxrjV7ffItSdIQyQNInKaSSptanC+JQZ4+JFn6JvPZ LsUcXpXLBLjC5ejkvfZlhcsl7UUYoxk5B2uPVeQAP6gHarWqMPL1QL8OcbAvK6oQwX OnTgcHC90AcRL74Fcb/yOuO7DEtEVRtnM3tro788= Received: from localhost (192.168.168.10) by mail (192.168.51.25) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Thu, 24 Mar 2022 04:37:51 +0300 From: Serge Semin To: Jingoo Han , Gustavo Pimentel , Bjorn Helgaas , Lorenzo Pieralisi , Rob Herring , =?utf-8?q?Krzysztof_Wilczy=C5=84ski?= , Rahul Tanwar , Thierry Reding , Jonathan Hunter CC: Serge Semin , Serge Semin , Alexey Malahov , Pavel Parkhomenko , Frank Li , Manivannan Sadhasivam , Rob Herring , , , , Subject: [PATCH 04/16] PCI: dwc: Convert to using native IP-core versions representation Date: Thu, 24 Mar 2022 04:37:22 +0300 Message-ID: <20220324013734.18234-5-Sergey.Semin@baikalelectronics.ru> In-Reply-To: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> References: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org 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 --- 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 1c2ee4e13f1c..ec65355dd29b 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 f1693e25afcb..c21373c6cb51 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 #include +/* 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; From patchwork Thu Mar 24 01:37:23 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 12793842 X-Patchwork-Delegate: lorenzo.pieralisi@arm.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id CE182C4332F for ; Mon, 28 Mar 2022 15:24:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237424AbiC1P0g (ORCPT ); Mon, 28 Mar 2022 11:26:36 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46570 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237392AbiC1P0J (ORCPT ); Mon, 28 Mar 2022 11:26:09 -0400 Received: from mail.baikalelectronics.ru (mail.baikalelectronics.com [87.245.175.226]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 462E21FA55; Mon, 28 Mar 2022 08:24:13 -0700 (PDT) Received: from mail.baikalelectronics.ru (unknown [192.168.51.25]) by mail.baikalelectronics.ru (Postfix) with ESMTP id 5EB0C1E4923; Thu, 24 Mar 2022 04:37:52 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mail.baikalelectronics.ru 5EB0C1E4923 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baikalelectronics.ru; s=mail; t=1648085872; bh=i8/8fLcZUXbXg67aSAjVRqmL8MzIGLx2dw5JpgWmYX8=; h=From:To:CC:Subject:Date:In-Reply-To:References:From; b=bA0h9hLl7MP/I1G5OEbHLaqxYZu89pAzQIAk2mJsklz854E9hcGVO2UVPNnklG+Zg bVcnza/LmIEdgFT7ppap3kRjOlw4YHR+JBiYjMDHSENoQQpgeE3KZeDqR8RVXBiRVf yra6hstTZJrL1F0RD3gB2BfTdjN3qHVpWAVw0XFk= Received: from localhost (192.168.168.10) by mail (192.168.51.25) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Thu, 24 Mar 2022 04:37:52 +0300 From: Serge Semin To: Jingoo Han , Gustavo Pimentel , Bjorn Helgaas , Lorenzo Pieralisi , Rob Herring , =?utf-8?q?Krzysztof_Wilczy=C5=84ski?= CC: Serge Semin , Serge Semin , Alexey Malahov , Pavel Parkhomenko , Frank Li , Manivannan Sadhasivam , Rob Herring , , , Subject: [PATCH 05/16] PCI: dwc: Add IP-core version detection procedure Date: Thu, 24 Mar 2022 04:37:23 +0300 Message-ID: <20220324013734.18234-6-Sergey.Semin@baikalelectronics.ru> In-Reply-To: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> References: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org 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 --- .../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 7c9315fffe24..3b981d13cca9 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -645,6 +645,8 @@ int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep) u32 reg; int i; + dw_pcie_version_detect(pci); + hdr_type = dw_pcie_readb_dbi(pci, PCI_HEADER_TYPE) & PCI_HEADER_TYPE_MASK; if (hdr_type != PCI_HEADER_TYPE_NORMAL) { diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 8364ea234e88..8f0d473ff770 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -398,6 +398,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 c21373c6cb51..49c494d82042 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); From patchwork Thu Mar 24 01:37:24 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 12793827 X-Patchwork-Delegate: lorenzo.pieralisi@arm.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6D87AC4332F for ; Mon, 28 Mar 2022 15:24:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236149AbiC1PZw (ORCPT ); Mon, 28 Mar 2022 11:25:52 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44794 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235716AbiC1PZu (ORCPT ); Mon, 28 Mar 2022 11:25:50 -0400 Received: from mail.baikalelectronics.ru (mail.baikalelectronics.com [87.245.175.226]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id B1E7C1FA55; Mon, 28 Mar 2022 08:24:08 -0700 (PDT) Received: from mail.baikalelectronics.ru (unknown [192.168.51.25]) by mail.baikalelectronics.ru (Postfix) with ESMTP id 433211E4924; Thu, 24 Mar 2022 04:37:53 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mail.baikalelectronics.ru 433211E4924 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baikalelectronics.ru; s=mail; t=1648085873; bh=6CL1zvQSdfUwCZE8IBob0c16fWf+m5n9Xre0iS0ETF8=; h=From:To:CC:Subject:Date:In-Reply-To:References:From; b=K1ZE5vhR4GVRfQvKFc46ycU4LyYIZMAs8ce9lEeRepE1CVEd8cFT4uxWffZFsinPs ZJduq9wsBt7MvzevmiXd1gjEgkHS06KpgDDnRHl+kQJ9sOddgate6YpUZ8uKtxYKFL YolYIzI/ZxFKZdU+wB7vSMi/GQe0n2ODKBnHypMU= Received: from localhost (192.168.168.10) by mail (192.168.51.25) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Thu, 24 Mar 2022 04:37:53 +0300 From: Serge Semin To: Jingoo Han , Gustavo Pimentel , Bjorn Helgaas , Lorenzo Pieralisi , Rob Herring , =?utf-8?q?Krzysztof_Wilczy=C5=84ski?= CC: Serge Semin , Serge Semin , Alexey Malahov , Pavel Parkhomenko , Frank Li , Manivannan Sadhasivam , Rob Herring , , , Subject: [PATCH 06/16] PCI: dwc: Introduce Synopsys IP-core versions/types interface Date: Thu, 24 Mar 2022 04:37:24 +0300 Message-ID: <20220324013734.18234-7-Sergey.Semin@baikalelectronics.ru> In-Reply-To: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> References: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org 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 --- 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 ec65355dd29b..8fbebeb923b8 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 49c494d82042..d5ce19625769 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 From patchwork Thu Mar 24 01:37:25 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 12793834 X-Patchwork-Delegate: lorenzo.pieralisi@arm.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8A33CC433F5 for ; Mon, 28 Mar 2022 15:24:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237160AbiC1PZ4 (ORCPT ); Mon, 28 Mar 2022 11:25:56 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45082 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236701AbiC1PZy (ORCPT ); Mon, 28 Mar 2022 11:25:54 -0400 Received: from mail.baikalelectronics.ru (mail.baikalelectronics.com [87.245.175.226]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 31D2021E16; Mon, 28 Mar 2022 08:24:11 -0700 (PDT) Received: from mail.baikalelectronics.ru (unknown [192.168.51.25]) by mail.baikalelectronics.ru (Postfix) with ESMTP id 00CDF1E4925; Thu, 24 Mar 2022 04:37:54 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mail.baikalelectronics.ru 00CDF1E4925 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baikalelectronics.ru; s=mail; t=1648085874; bh=aHKH/Se4RYpqfGYBZz3KHRmEZDGqpu682MJw8JgKJKQ=; h=From:To:CC:Subject:Date:In-Reply-To:References:From; b=E0rU9OGoW76lpgW8j7jaW28UlvbBzX6GWJmTCfHnoQSKv1ZJ2MfXsnotXsX68nOi+ N6wyjlk/Wp/GFzEkrshT/2eXUoAL164b+h/9QPYUAh7sVso/QXMwqWwEnL5g90J0C5 Oep29BVsydKbR7FbIIaxcBQfrxbo9H0/a60cZF/w= Received: from localhost (192.168.168.10) by mail (192.168.51.25) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Thu, 24 Mar 2022 04:37:53 +0300 From: Serge Semin To: Jingoo Han , Gustavo Pimentel , Bjorn Helgaas , Lorenzo Pieralisi , Rob Herring , =?utf-8?q?Krzysztof_Wilczy=C5=84ski?= CC: Serge Semin , Serge Semin , Alexey Malahov , Pavel Parkhomenko , Frank Li , Manivannan Sadhasivam , Rob Herring , , , Subject: [PATCH 07/16] PCI: dwc: Add host de-initialization callback Date: Thu, 24 Mar 2022 04:37:25 +0300 Message-ID: <20220324013734.18234-8-Sergey.Semin@baikalelectronics.ru> In-Reply-To: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> References: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org 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 --- .../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 8f0d473ff770..602cf4fe502b 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -354,20 +354,23 @@ 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_host_deinit; } if (pp->ops->msi_host_init) { ret = pp->ops->msi_host_init(pp); if (ret < 0) - return ret; + goto err_host_deinit; } else if (pp->has_msi_ctrl) { if (!pp->msi_irq) { 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_host_deinit; + } } } @@ -375,7 +378,7 @@ int dw_pcie_host_init(struct pcie_port *pp) ret = dw_pcie_allocate_domains(pp); if (ret) - return ret; + goto err_host_deinit; if (pp->msi_irq > 0) irq_set_chained_handler_and_data(pp->msi_irq, @@ -428,6 +431,11 @@ int dw_pcie_host_init(struct pcie_port *pp) err_free_msi: if (pp->has_msi_ctrl) dw_pcie_free_msi(pp); + +err_host_deinit: + if (pp->ops->host_deinit) + pp->ops->host_deinit(pp); + return ret; } EXPORT_SYMBOL_GPL(dw_pcie_host_init); @@ -444,6 +452,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); }; From patchwork Thu Mar 24 01:37:26 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 12793832 X-Patchwork-Delegate: lorenzo.pieralisi@arm.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 79D8EC35274 for ; Mon, 28 Mar 2022 15:24:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236760AbiC1PZy (ORCPT ); Mon, 28 Mar 2022 11:25:54 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44932 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236516AbiC1PZw (ORCPT ); Mon, 28 Mar 2022 11:25:52 -0400 Received: from mail.baikalelectronics.ru (mail.baikalelectronics.com [87.245.175.226]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 70E781F63E; Mon, 28 Mar 2022 08:24:11 -0700 (PDT) Received: from mail.baikalelectronics.ru (unknown [192.168.51.25]) by mail.baikalelectronics.ru (Postfix) with ESMTP id 0FA201E4926; Thu, 24 Mar 2022 04:37:55 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mail.baikalelectronics.ru 0FA201E4926 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baikalelectronics.ru; s=mail; t=1648085875; bh=fzWBessojaDhJfCKXZFdY7ORBS7FG8LumKb/yK55pnQ=; h=From:To:CC:Subject:Date:In-Reply-To:References:From; b=AXIQbY/iHV+kQugnH4mWh/jF5iabsqUhGQquBASSdGeLJmz1zt2WEx+8qJgSyx00j i+O1KzysflcjduxMIKU28vT0bBYOoag00H99M6E8WbDmD2NcfoW5iVfXCsg6pLVYon HSm03zUXYaDOlMEcRn/ZzkUtdFksslEU57yJldkA= Received: from localhost (192.168.168.10) by mail (192.168.51.25) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Thu, 24 Mar 2022 04:37:54 +0300 From: Serge Semin To: Jingoo Han , Gustavo Pimentel , Bjorn Helgaas , Lorenzo Pieralisi , Rob Herring , =?utf-8?q?Krzysztof_Wilczy=C5=84ski?= CC: Serge Semin , Serge Semin , Alexey Malahov , Pavel Parkhomenko , Frank Li , Manivannan Sadhasivam , Rob Herring , , , Subject: [PATCH 08/16] PCI: dwc: Drop inbound iATU types enumeration - dw_pcie_as_type Date: Thu, 24 Mar 2022 04:37:26 +0300 Message-ID: <20220324013734.18234-9-Sergey.Semin@baikalelectronics.ru> In-Reply-To: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> References: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org 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 Reviewed-by: Rob Herring --- .../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 3b981d13cca9..3bd9026071e8 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 d5ce19625769..b983128584ff 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); From patchwork Thu Mar 24 01:37:27 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 12793836 X-Patchwork-Delegate: lorenzo.pieralisi@arm.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D93C7C46467 for ; Mon, 28 Mar 2022 15:24:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237229AbiC1PZ6 (ORCPT ); Mon, 28 Mar 2022 11:25:58 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45188 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236826AbiC1PZz (ORCPT ); Mon, 28 Mar 2022 11:25:55 -0400 Received: from mail.baikalelectronics.ru (mail.baikalelectronics.com [87.245.175.226]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 3151F20F43; Mon, 28 Mar 2022 08:24:11 -0700 (PDT) Received: from mail.baikalelectronics.ru (unknown [192.168.51.25]) by mail.baikalelectronics.ru (Postfix) with ESMTP id 9CDE71E4927; Thu, 24 Mar 2022 04:37:55 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mail.baikalelectronics.ru 9CDE71E4927 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baikalelectronics.ru; s=mail; t=1648085875; bh=YHDoXGqf6E8cNElJu1pzjTb4ae5gfASGH9rwV01LRwM=; h=From:To:CC:Subject:Date:In-Reply-To:References:From; b=cl9w4alUyc2X20wszG/pxiIY4jAayLXou+HZF+RNuR499PpAie5YMfmil2KlHkCIO HAcxcYg4l92KTVBkZ/SXuR5Qqd4375SVJ8FiL43H14d8qNs0o+ngUpxy0aHUepxHNn PiEmdFQDIFlqQnw4EiU/Da/Zi7dTsYkhOG3HdBI4= Received: from localhost (192.168.168.10) by mail (192.168.51.25) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Thu, 24 Mar 2022 04:37:55 +0300 From: Serge Semin To: Jingoo Han , Gustavo Pimentel , Bjorn Helgaas , Lorenzo Pieralisi , Rob Herring , =?utf-8?q?Krzysztof_Wilczy=C5=84ski?= CC: Serge Semin , Serge Semin , Alexey Malahov , Pavel Parkhomenko , Frank Li , Manivannan Sadhasivam , Rob Herring , , , Subject: [PATCH 09/16] PCI: dwc: Simplify in/outbound iATU setup methods Date: Thu, 24 Mar 2022 04:37:27 +0300 Message-ID: <20220324013734.18234-10-Sergey.Semin@baikalelectronics.ru> In-Reply-To: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> References: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org 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 --- drivers/pci/controller/dwc/pcie-designware.c | 301 ++++++------------- drivers/pci/controller/dwc/pcie-designware.h | 50 ++- 2 files changed, 112 insertions(+), 239 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index b983128584ff..f1aa6e2e85fe 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -205,48 +205,67 @@ 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 u32 dw_pcie_readl_atu(struct dw_pcie *pci, u32 dir, u32 region, u32 reg) { + void __iomem *base; int ret; u32 val; + if (pci->iatu_unroll_enabled) { + base = pci->atu_base; + reg = reg + PCIE_ATU_UNROLL_BASE(dir, region); + } else { + base = pci->dbi_base; + reg = reg + PCIE_ATU_VIEWPORT_BASE; + + dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, dir | region); + } + 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 region, + u32 reg, u32 val) { + void __iomem *base; int ret; + if (pci->iatu_unroll_enabled) { + base = pci->atu_base; + reg = reg + PCIE_ATU_UNROLL_BASE(dir, region); + } else { + base = pci->dbi_base; + reg = reg + PCIE_ATU_VIEWPORT_BASE; + + dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, dir | region); + } + 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 region, 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, region, 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 region, 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, region, reg, val); } static inline u32 dw_pcie_enable_ecrc(u32 val) @@ -290,50 +309,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 +319,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_OUTBOUND | 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 +377,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 region, 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, region, 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 region, 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, region, reg, val); } int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index, @@ -460,65 +393,51 @@ 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_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)); + 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_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, int index, enum dw_pcie_region_type type) { - int region; + u32 dir; switch (type) { case DW_PCIE_REGION_INBOUND: - region = PCIE_ATU_REGION_INBOUND; + dir = PCIE_ATU_REGION_DIR_IB; break; case DW_PCIE_REGION_OUTBOUND: - region = PCIE_ATU_REGION_OUTBOUND; + dir = PCIE_ATU_REGION_DIR_OB; break; default: return; } - if (pci->iatu_unroll_enabled) { - if (region == PCIE_ATU_REGION_INBOUND) { - 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, region | 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) @@ -622,63 +541,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_OUTBOUND | 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_INBOUND | 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; } @@ -707,12 +592,10 @@ 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); } + 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 449c5ad92edc..6adf0c957c3b 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -103,10 +103,20 @@ #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_INBOUND BIT(31) -#define PCIE_ATU_REGION_OUTBOUND 0 -#define PCIE_ATU_CR1 0x904 +#define PCIE_ATU_REGION_DIR_IB BIT(31) +#define PCIE_ATU_REGION_DIR_OB 0 +#define PCIE_ATU_VIEWPORT_BASE 0x904 +#define PCIE_ATU_UNROLL_BASE(dir, region) \ + (((region) << 9) | ((dir == PCIE_ATU_REGION_DIR_IB) ? BIT(8) : 0)) +#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 +124,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 +153,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 +161,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) From patchwork Thu Mar 24 01:37:28 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 12793835 X-Patchwork-Delegate: lorenzo.pieralisi@arm.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2F641C43219 for ; Mon, 28 Mar 2022 15:24:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237195AbiC1PZ5 (ORCPT ); Mon, 28 Mar 2022 11:25:57 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45190 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236912AbiC1PZz (ORCPT ); Mon, 28 Mar 2022 11:25:55 -0400 Received: from mail.baikalelectronics.ru (mail.baikalelectronics.com [87.245.175.226]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 97B6723140; Mon, 28 Mar 2022 08:24:12 -0700 (PDT) Received: from mail.baikalelectronics.ru (unknown [192.168.51.25]) by mail.baikalelectronics.ru (Postfix) with ESMTP id 95C241E4929; Thu, 24 Mar 2022 04:37:56 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mail.baikalelectronics.ru 95C241E4929 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baikalelectronics.ru; s=mail; t=1648085876; bh=JUzxfKi0MQDTXGgN5Cp017DVBJQxZSN3g4muSQcPmsg=; h=From:To:CC:Subject:Date:In-Reply-To:References:From; b=dP3AhX5KBEPxHZxHwo+WqaXRCLjEJvSQ4+saFBlsJRINv2STOgx42KXYwwviEs73e M6bJynpSnwQuFJKEkV0UbxenbHOg5uuw0pE4ep38KvYTRJ46KvZzoIBDedd6atguDz zBsnxcO5tKgFcmA/Yu0tqmKjSZPqJzd8nADCCa+0= Received: from localhost (192.168.168.10) by mail (192.168.51.25) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Thu, 24 Mar 2022 04:37:56 +0300 From: Serge Semin To: Jingoo Han , Gustavo Pimentel , Bjorn Helgaas , Lorenzo Pieralisi , Rob Herring , =?utf-8?q?Krzysztof_Wilczy=C5=84ski?= CC: Serge Semin , Serge Semin , Alexey Malahov , Pavel Parkhomenko , Frank Li , Manivannan Sadhasivam , Rob Herring , , , Subject: [PATCH 10/16] PCI: dwc: Drop iATU regions enumeration - dw_pcie_region_type Date: Thu, 24 Mar 2022 04:37:28 +0300 Message-ID: <20220324013734.18234-11-Sergey.Semin@baikalelectronics.ru> In-Reply-To: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> References: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org 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 macro declared which describe the possible inbound and outbound iATU regions. Having an addition abstraction just needlessly complicates the code. Secondly checking the region index passed to the dw_pcie_disable_atu() method for validity is pointless since the erroneous situation will be just ignored in the current code implementation. So to speak let's drop the redundant dw_pcie_region_type enumeration replacing it with the direct iATU direction macro usage. While at it we suggest to convert the dw_pcie_disable_atu() method to being more consistent with the dw_pcie_readl_atu{_ib}() and dw_pcie_readl_atu{_ob}() functions by having the direction parameter specified ahead of the region index. Thus the code will be a little bit more pleasant to read. Signed-off-by: Serge Semin Reviewed-by: Rob Herring --- drivers/pci/controller/dwc/pcie-designware-ep.c | 4 ++-- .../pci/controller/dwc/pcie-designware-host.c | 2 +- drivers/pci/controller/dwc/pcie-designware.c | 16 +--------------- drivers/pci/controller/dwc/pcie-designware.h | 9 +-------- 4 files changed, 5 insertions(+), 26 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 3bd9026071e8..83ceba84b79d 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 602cf4fe502b..e9aa3d8539d8 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -644,7 +644,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 f1aa6e2e85fe..ce360986609f 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -421,22 +421,8 @@ int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index, return -ETIMEDOUT; } -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) { - u32 dir; - - switch (type) { - case DW_PCIE_REGION_INBOUND: - dir = PCIE_ATU_REGION_DIR_IB; - break; - case DW_PCIE_REGION_OUTBOUND: - dir = PCIE_ATU_REGION_DIR_OB; - break; - default: - return; - } - dw_pcie_writel_atu(pci, dir, index, PCIE_ATU_REGION_CTRL2, 0); } diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 6adf0c957c3b..203f9dfb9048 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -175,12 +175,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, @@ -316,8 +310,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); From patchwork Thu Mar 24 01:37:29 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 12793841 X-Patchwork-Delegate: lorenzo.pieralisi@arm.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2A4EAC433F5 for ; Mon, 28 Mar 2022 15:24:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237653AbiC1P0g (ORCPT ); Mon, 28 Mar 2022 11:26:36 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46572 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237402AbiC1P0J (ORCPT ); Mon, 28 Mar 2022 11:26:09 -0400 Received: from mail.baikalelectronics.ru (mail.baikalelectronics.com [87.245.175.226]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 4D8C147AE6; Mon, 28 Mar 2022 08:24:13 -0700 (PDT) Received: from mail.baikalelectronics.ru (unknown [192.168.51.25]) by mail.baikalelectronics.ru (Postfix) with ESMTP id 5B0841E492A; Thu, 24 Mar 2022 04:37:57 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mail.baikalelectronics.ru 5B0841E492A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baikalelectronics.ru; s=mail; t=1648085877; bh=ZdyFIuOegkUM/W6mKG5sgvVKndEgEJ/SoD+JyhCWl8Q=; h=From:To:CC:Subject:Date:In-Reply-To:References:From; b=rjjUv6sp1ZhnhkcSnkd8RYYK6PygWyRwOIPq/pwtXksXzM6LXLSjva+A0p8dqrn7E bieNXGWfDhHwbd4nUfTbQ620Z5TCS96WLY0QSDkVkWg2UD2dPx9w006uLHl8XpxPcs ZQcZpjW82dnUyPC8MPNg/4Th31O4V8VXp3OIQCtc= Received: from localhost (192.168.168.10) by mail (192.168.51.25) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Thu, 24 Mar 2022 04:37:57 +0300 From: Serge Semin To: Jingoo Han , Gustavo Pimentel , Bjorn Helgaas , Lorenzo Pieralisi , Rob Herring , =?utf-8?q?Krzysztof_Wilczy=C5=84ski?= CC: Serge Semin , Serge Semin , Alexey Malahov , Pavel Parkhomenko , Frank Li , Manivannan Sadhasivam , Rob Herring , , , Subject: [PATCH 11/16] PCI: dwc: Add iATU regions size detection procedure Date: Thu, 24 Mar 2022 04:37:29 +0300 Message-ID: <20220324013734.18234-12-Sergey.Semin@baikalelectronics.ru> In-Reply-To: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> References: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org 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 --- 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 ce360986609f..c218b1fe4dad 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -8,9 +8,11 @@ * Author: Jingoo Han */ +#include #include #include #include +#include #include #include "../../pci.h" @@ -530,7 +532,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); @@ -553,8 +556,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) @@ -585,8 +609,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 203f9dfb9048..8b601a6a6090 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; From patchwork Thu Mar 24 01:37:30 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 12793837 X-Patchwork-Delegate: lorenzo.pieralisi@arm.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 06E28C433FE for ; Mon, 28 Mar 2022 15:24:37 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237471AbiC1P0L (ORCPT ); Mon, 28 Mar 2022 11:26:11 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45328 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237134AbiC1PZ4 (ORCPT ); Mon, 28 Mar 2022 11:25:56 -0400 Received: from mail.baikalelectronics.ru (mail.baikalelectronics.com [87.245.175.226]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 465D321E3E; Mon, 28 Mar 2022 08:24:13 -0700 (PDT) Received: from mail.baikalelectronics.ru (unknown [192.168.51.25]) by mail.baikalelectronics.ru (Postfix) with ESMTP id 1A53A1E492B; Thu, 24 Mar 2022 04:37:58 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mail.baikalelectronics.ru 1A53A1E492B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baikalelectronics.ru; s=mail; t=1648085878; bh=pcwhlvPohQh7Qat7puGKhGpj8aOlAc8s7r4TSh7jh6g=; h=From:To:CC:Subject:Date:In-Reply-To:References:From; b=Qofvpoaczg/USv9QkltORohpkUn73JYO/HXrOJPCpThzlqVbvehD9Y3KWq+fex9Ru IUq4uG3a1xrtPc6jrY/ePmo3e78fHi7vKzRWrrE8Q1n6d/cIvA8+wSdnA4hwKI2zMA 4if/dyDmHxVnC5WVhzlQ4/OjeL2tWKaJclRw1sWQ= Received: from localhost (192.168.168.10) by mail (192.168.51.25) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Thu, 24 Mar 2022 04:37:57 +0300 From: Serge Semin To: Jingoo Han , Gustavo Pimentel , Bjorn Helgaas , Lorenzo Pieralisi , Rob Herring , =?utf-8?q?Krzysztof_Wilczy=C5=84ski?= CC: Serge Semin , Serge Semin , Alexey Malahov , Pavel Parkhomenko , Frank Li , Manivannan Sadhasivam , Rob Herring , , , Subject: [PATCH 12/16] PCI: dwc: Verify in/out regions against iATU constraints Date: Thu, 24 Mar 2022 04:37:30 +0300 Message-ID: <20220324013734.18234-13-Sergey.Semin@baikalelectronics.ru> In-Reply-To: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> References: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org 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 --- 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 c218b1fe4dad..2a7f23a2045c 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -8,6 +8,7 @@ * Author: Jingoo Han */ +#include #include #include #include @@ -311,9 +312,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; @@ -323,6 +324,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, @@ -356,27 +364,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 region, u32 reg) @@ -395,6 +405,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 8b601a6a6090..bcda63393390 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); From patchwork Thu Mar 24 01:37:31 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 12793840 X-Patchwork-Delegate: lorenzo.pieralisi@arm.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id DDF89C433EF for ; Mon, 28 Mar 2022 15:24:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237355AbiC1P0Q (ORCPT ); Mon, 28 Mar 2022 11:26:16 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46544 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237383AbiC1P0J (ORCPT ); Mon, 28 Mar 2022 11:26:09 -0400 Received: from mail.baikalelectronics.ru (mail.baikalelectronics.com [87.245.175.226]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 46747220F1; Mon, 28 Mar 2022 08:24:13 -0700 (PDT) Received: from mail.baikalelectronics.ru (unknown [192.168.51.25]) by mail.baikalelectronics.ru (Postfix) with ESMTP id 1C8D81E492C; Thu, 24 Mar 2022 04:37:59 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mail.baikalelectronics.ru 1C8D81E492C DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baikalelectronics.ru; s=mail; t=1648085879; bh=RAVOBp8pSvaiE0vBHBwQL+jri/8jYx0WLIgd8A47joA=; h=From:To:CC:Subject:Date:In-Reply-To:References:From; b=A2ItgoMzszU2XfDHXtKphRwFD5e4Czc7lOF3mwls0mRnlJLJqtEnSvHbFbu/wMkAO PiJ0rHZ/+4lEi27gdIAg5LFsYJ4STFVFGfly6O33DNyV2mz8jWjMwM6tNVLVw3eLMh fqL83KxyA93x/n5JWpkcGAcFD0CnkRZLQMb2tpiA= Received: from localhost (192.168.168.10) by mail (192.168.51.25) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Thu, 24 Mar 2022 04:37:58 +0300 From: Serge Semin To: Jingoo Han , Gustavo Pimentel , Bjorn Helgaas , Lorenzo Pieralisi , Rob Herring , =?utf-8?q?Krzysztof_Wilczy=C5=84ski?= , Rahul Tanwar CC: Serge Semin , Serge Semin , Alexey Malahov , Pavel Parkhomenko , Frank Li , Manivannan Sadhasivam , Rob Herring , , , Subject: [PATCH 13/16] PCI: dwc: Check iATU in/outbound ranges setup methods status Date: Thu, 24 Mar 2022 04:37:31 +0300 Message-ID: <20220324013734.18234-14-Sergey.Semin@baikalelectronics.ru> In-Reply-To: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> References: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org 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 --- .../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 83ceba84b79d..47ed9256b87c 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 e9aa3d8539d8..41c673c31940 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -405,7 +405,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); @@ -461,10 +463,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 @@ -485,8 +487,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; } @@ -499,12 +503,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, @@ -515,12 +525,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 = { @@ -577,10 +593,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. @@ -636,42 +714,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); @@ -684,5 +729,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 bcda63393390..15fce8fd33df 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); From patchwork Thu Mar 24 01:37:32 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 12793833 X-Patchwork-Delegate: lorenzo.pieralisi@arm.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4D60DC4332F for ; Mon, 28 Mar 2022 15:24:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236518AbiC1PZ4 (ORCPT ); Mon, 28 Mar 2022 11:25:56 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45084 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236703AbiC1PZy (ORCPT ); Mon, 28 Mar 2022 11:25:54 -0400 Received: from mail.baikalelectronics.ru (mail.baikalelectronics.com [87.245.175.226]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 9749622290; Mon, 28 Mar 2022 08:24:12 -0700 (PDT) Received: from mail.baikalelectronics.ru (unknown [192.168.51.25]) by mail.baikalelectronics.ru (Postfix) with ESMTP id F1F671E492D; Thu, 24 Mar 2022 04:37:59 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mail.baikalelectronics.ru F1F671E492D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baikalelectronics.ru; s=mail; t=1648085879; bh=JNjQ9LsyEVBUO6+Fr1N/WeQmcj9ncW1Bt526HuRe/EY=; h=From:To:CC:Subject:Date:In-Reply-To:References:From; b=LeVGk5SrU2q79lO1su3NVBS3uQa008JQYs+TfOI/9UFVbEMIaIBgxzRz5dYNEVxk/ 9HL93IskT9bxx1FCkjcOaVHqY4MSSwcxD/buEkb2cZXyiTV5SMQf59LpCXh3IeRClh 6mFGdsYmXRU0zpav6+vjbV7ouatSPuTW10tj8yNE= Received: from localhost (192.168.168.10) by mail (192.168.51.25) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Thu, 24 Mar 2022 04:37:59 +0300 From: Serge Semin To: Jingoo Han , Gustavo Pimentel , Bjorn Helgaas , Lorenzo Pieralisi , Rob Herring , =?utf-8?q?Krzysztof_Wilczy=C5=84ski?= CC: Serge Semin , Serge Semin , Alexey Malahov , Pavel Parkhomenko , Frank Li , Manivannan Sadhasivam , Rob Herring , , , Subject: [PATCH 14/16] PCI: dwc: Introduce dma-ranges property support for RC-host Date: Thu, 24 Mar 2022 04:37:32 +0300 Message-ID: <20220324013734.18234-15-Sergey.Semin@baikalelectronics.ru> In-Reply-To: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> References: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org 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 --- .../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 47ed9256b87c..23401f17e8f0 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 41c673c31940..715a13b90e43 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -606,12 +606,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) @@ -648,9 +651,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 2a7f23a2045c..4a95a7b112e9 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -400,8 +400,61 @@ static inline void dw_pcie_writel_atu_ib(struct dw_pcie *pci, u32 region, u32 re dw_pcie_writel_atu(pci, PCIE_ATU_REGION_DIR_IB, region, 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 15fce8fd33df..ade854217332 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); From patchwork Thu Mar 24 01:37:33 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 12793838 X-Patchwork-Delegate: lorenzo.pieralisi@arm.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 26E25C433F5 for ; Mon, 28 Mar 2022 15:24:37 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237419AbiC1P0J (ORCPT ); Mon, 28 Mar 2022 11:26:09 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45332 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237117AbiC1PZ4 (ORCPT ); Mon, 28 Mar 2022 11:25:56 -0400 Received: from mail.baikalelectronics.ru (mail.baikalelectronics.com [87.245.175.226]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 9775022292; Mon, 28 Mar 2022 08:24:12 -0700 (PDT) Received: from mail.baikalelectronics.ru (unknown [192.168.51.25]) by mail.baikalelectronics.ru (Postfix) with ESMTP id 939141E492E; Thu, 24 Mar 2022 04:38:00 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mail.baikalelectronics.ru 939141E492E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baikalelectronics.ru; s=mail; t=1648085880; bh=wAaAg+6hqTSQ8r3XLn10uvT2kCn+m7caW29p6koiMuQ=; h=From:To:CC:Subject:Date:In-Reply-To:References:From; b=hxl05juv+i8muXCWg119p9XKWnfLEuF6HwYxCd32AtRHD2Jh8PAMVJfjrPgCjfyNi y60AkxJTDmmG1pncAk1YN1ZpaXMSaw8sX7Nbd2ni0T5vdIIFRYOC/KR17K7bjwO6RL MCGkIXLU+wMTqZB1mQZ5QJpc32Ei7M46z2iTMq+k= Received: from localhost (192.168.168.10) by mail (192.168.51.25) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Thu, 24 Mar 2022 04:38:00 +0300 From: Serge Semin To: Jingoo Han , Gustavo Pimentel , Bjorn Helgaas , Lorenzo Pieralisi , Rob Herring , =?utf-8?q?Krzysztof_Wilczy=C5=84ski?= CC: Serge Semin , Serge Semin , Alexey Malahov , Pavel Parkhomenko , Frank Li , Manivannan Sadhasivam , Rob Herring , , , Subject: [PATCH 15/16] PCI: dwc: Introduce generic platform clocks and resets sets Date: Thu, 24 Mar 2022 04:37:33 +0300 Message-ID: <20220324013734.18234-16-Sergey.Semin@baikalelectronics.ru> In-Reply-To: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> References: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org 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 --- 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 ade854217332..11c52d2eaf79 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -182,6 +182,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); From patchwork Thu Mar 24 01:37:34 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 12793831 X-Patchwork-Delegate: lorenzo.pieralisi@arm.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C74E8C4167B for ; Mon, 28 Mar 2022 15:24:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236684AbiC1PZx (ORCPT ); Mon, 28 Mar 2022 11:25:53 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44912 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236277AbiC1PZw (ORCPT ); Mon, 28 Mar 2022 11:25:52 -0400 Received: from mail.baikalelectronics.ru (mail.baikalelectronics.com [87.245.175.226]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id B1F7621823; Mon, 28 Mar 2022 08:24:08 -0700 (PDT) Received: from mail.baikalelectronics.ru (unknown [192.168.51.25]) by mail.baikalelectronics.ru (Postfix) with ESMTP id A133B1E492F; Thu, 24 Mar 2022 04:38:01 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mail.baikalelectronics.ru A133B1E492F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baikalelectronics.ru; s=mail; t=1648085881; bh=j3ugxfclMIHWPxva7dSIBFeJTGU2CFuK3jg+xZXybn4=; h=From:To:CC:Subject:Date:In-Reply-To:References:From; b=Fr0ytzbqocY/pOBijN+LJZ45FSTBlOCbFYE2mUlWr0jZ2El4odKzvF6RIiR1KQxCs HBk89Ym0LLt6Mf7ULPCinhrD9YYV1PmgK3FXdhSr4061pT6m84XXnrNu+kOtlL5nM+ KC4icqjcXN4i6sz5lWr+Bnv8QGAib4e8ylfmBQow= Received: from localhost (192.168.168.10) by mail (192.168.51.25) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Thu, 24 Mar 2022 04:38:01 +0300 From: Serge Semin To: Jingoo Han , Gustavo Pimentel , Bjorn Helgaas , Lorenzo Pieralisi , Rob Herring , =?utf-8?q?Krzysztof_Wilczy=C5=84ski?= CC: Serge Semin , Serge Semin , Alexey Malahov , Pavel Parkhomenko , Frank Li , Manivannan Sadhasivam , Rob Herring , , , Subject: [PATCH 16/16] PCI: dwc: Add Baikal-T1 PCIe controller support Date: Thu, 24 Mar 2022 04:37:34 +0300 Message-ID: <20220324013734.18234-17-Sergey.Semin@baikalelectronics.ru> In-Reply-To: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> References: <20220324013734.18234-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org 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 is dword-aligned. Signed-off-by: Serge Semin --- drivers/pci/controller/dwc/Kconfig | 9 + drivers/pci/controller/dwc/Makefile | 1 + drivers/pci/controller/dwc/pcie-bt1.c | 638 ++++++++++++++++++++++++++ 3 files changed, 648 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..8cb6a9b3e39d --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-bt1.c @@ -0,0 +1,638 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021 BAIKAL ELECTRONICS, JSC + * + * Authors: + * Vadim Vlasov + * Serge Semin + * + * Baikal-T1 PCIe controller driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, "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 "); +MODULE_DESCRIPTION("Baikal-T1 PCIe driver"); +MODULE_LICENSE("GPL v2");