Merge branch 'remotes/lorenzo/pci/dwc'

- Fix n_fts[] array overrun (Vidya Sagar)

- Don't advertise PTM Responder role for Endpoints (Vidya Sagar)

- Fix qcom "reset assert" error message (Manivannan Sadhasivam)

- Downgrade "link didn't come up" message to dev_info (Vidya Sagar)

- Initialize PHY before deasserting core reset so the link comes up on
  boards where the PHY provides the reference clock (this was a regression
  in v6.0) (Sascha Hauer)

- Switch histb to the gpiod API (Dmitry Torokhov)

- Fix imx6sx and imx8mq clock names in DT binding (Serge Semin)

- Fix visconti MSI interrupt in DT binding (Serge Semin)

- Consolidate reset-gpio, cdm, windows info in common DT shared by both
  Root Port and Endpoint bindings (Serge Semin)

- Remove bus node from DT examples (Serge Semin)

- Add common phys, phy-names to DT (Serge Semin)

- Add default max-link-speed of Gen5 to DT (Serge Semin)

- Apply generic schema for generic device  (Serge Semin)

- Add default max-functions of 32 to DT (Serge Semin)

- Add common interrupts, interrupt-names to DT (Serge Semin)

- Add common regs, reg-names to DT (Serge Semin)

- Add common clocks, resets to DT (Serge Semin)

- Add dma-coherent to DT (Serge Semin)

- Apply common schema to Rockchip DT (Serge Semin)

- Add Baikal-T1 DT bindings (Serge Semin)

- Add dma-ranges support in DesignWare core (Serge Semin)

- Add dw_pcie_cap_is() for testing controller capabilities (Serge Semin)

- Add generic resources getter to DesignWare core (Serge Semin)

- Combine iATU detection procedures (Serge Semin)

- Add generic clock and reset names to DesignWare core (Serge Semin)

- Add Baikal-T1 PCIe controller driver (Serge Semin)

* remotes/lorenzo/pci/dwc:
  PCI: dwc: Add Baikal-T1 PCIe controller support
  PCI: dwc: Introduce generic platform clocks and resets
  PCI: dwc: Combine iATU detection procedures
  PCI: dwc: Introduce generic resources getter
  PCI: dwc: Introduce generic controller capabilities interface
  PCI: dwc: Introduce dma-ranges property support for RC-host
  dt-bindings: PCI: dwc: Add Baikal-T1 PCIe Root Port bindings
  dt-bindings: PCI: dwc: Apply common schema to Rockchip DW PCIe nodes
  dt-bindings: PCI: dwc: Add dma-coherent property
  dt-bindings: PCI: dwc: Add clocks/resets common properties
  dt-bindings: PCI: dwc: Add reg/reg-names common properties
  dt-bindings: PCI: dwc: Add interrupts/interrupt-names common properties
  dt-bindings: PCI: dwc: Add max-functions EP property
  dt-bindings: PCI: dwc: Apply generic schema for generic device only
  dt-bindings: PCI: dwc: Add max-link-speed common property
  dt-bindings: PCI: dwc: Add phys/phy-names common properties
  dt-bindings: PCI: dwc: Remove bus node from the examples
  dt-bindings: PCI: dwc: Detach common RP/EP DT bindings
  dt-bindings: visconti-pcie: Fix interrupts array max constraints
  dt-bindings: imx6q-pcie: Fix clock names for imx6sx and imx8mq
  PCI: histb: Switch to using gpiod API
  PCI: imx6: Initialize PHY before deasserting core reset
  PCI: dwc: Use dev_info for PCIe link down event logging
  PCI: qcom: Fix error message for reset_control_assert()
  PCI: designware-ep: Disable PTM capabilities for EP mode
  PCI: Add PCI_PTM_CAP_RES macro
  PCI: dwc: Fix n_fts[] array overrun
This commit is contained in:
Bjorn Helgaas 2022-12-10 10:36:37 -06:00
commit 29a3e5aedc
18 changed files with 1839 additions and 246 deletions

View File

@ -0,0 +1,168 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/pci/baikal,bt1-pcie.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Baikal-T1 PCIe Root Port Controller
maintainers:
- Serge Semin <fancer.lancer@gmail.com>
description:
Embedded into Baikal-T1 SoC Root Complex controller with a single port
activated. 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. There four in- and four outbound iATU regions
which can be used to emit all required TLP types on the PCIe bus.
allOf:
- $ref: /schemas/pci/snps,dw-pcie.yaml#
properties:
compatible:
const: baikal,bt1-pcie
reg:
description:
DBI, DBI2 and at least 4KB outbound iATU-capable region for the
peripheral devices CFG-space access.
maxItems: 3
reg-names:
items:
- const: dbi
- const: dbi2
- const: 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:
items:
- const: dma0
- const: dma1
- const: dma2
- const: dma3
- const: dma4
- const: dma5
- const: dma6
- const: dma7
- const: msi
- const: aer
- const: pme
- const: hp
- const: bw_mg
- const: 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).
maxItems: 4
clock-names:
items:
- const: dbi
- const: mstr
- const: slv
- const: 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.
maxItems: 9
reset-names:
items:
- const: mstr
- const: slv
- const: pwr
- const: hot
- const: phy
- const: core
- const: pipe
- const: sticky
- const: non-sticky
baikal,bt1-syscon:
$ref: /schemas/types.yaml#/definitions/phandle
description:
Phandle to the Baikal-T1 System Controller DT node. It's required to
access some additional PM, Reset-related and LTSSM signals.
num-lanes:
maximum: 4
max-link-speed:
maximum: 3
required:
- compatible
- reg
- reg-names
- interrupts
- interrupt-names
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/mips-gic.h>
#include <dt-bindings/gpio/gpio.h>
pcie@1f052000 {
compatible = "baikal,bt1-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 = <GIC_SHARED 80 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SHARED 81 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SHARED 82 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SHARED 83 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SHARED 84 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SHARED 85 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SHARED 86 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SHARED 87 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SHARED 88 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SHARED 89 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SHARED 90 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SHARED 91 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SHARED 92 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SHARED 93 IRQ_TYPE_LEVEL_HIGH>;
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 GPIO_ACTIVE_LOW>;
num-lanes = <4>;
max-link-speed = <3>;
};
...

View File

@ -14,9 +14,6 @@ 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.
allOf:
- $ref: /schemas/pci/snps,dw-pcie.yaml#
properties:
compatible:
enum:
@ -61,7 +58,7 @@ properties:
- const: pcie
- const: pcie_bus
- const: pcie_phy
- const: pcie_inbound_axi for imx6sx-pcie, pcie_aux for imx8mq-pcie
- enum: [ pcie_inbound_axi, pcie_aux ]
num-lanes:
const: 1
@ -175,6 +172,47 @@ required:
- clocks
- clock-names
allOf:
- $ref: /schemas/pci/snps,dw-pcie.yaml#
- if:
properties:
compatible:
contains:
const: fsl,imx6sx-pcie
then:
properties:
clock-names:
items:
- {}
- {}
- {}
- const: pcie_inbound_axi
- if:
properties:
compatible:
contains:
const: fsl,imx8mq-pcie
then:
properties:
clock-names:
items:
- {}
- {}
- {}
- const: pcie_aux
- if:
properties:
compatible:
not:
contains:
enum:
- fsl,imx6sx-pcie
- fsl,imx8mq-pcie
then:
properties:
clock-names:
maxItems: 3
unevaluatedProperties: false
examples:

View File

@ -14,10 +14,10 @@ maintainers:
description: |+
RK3568 SoC PCIe host controller is based on the Synopsys DesignWare
PCIe IP and thus inherits all the common properties defined in
designware-pcie.txt.
snps,dw-pcie.yaml.
allOf:
- $ref: /schemas/pci/pci-bus.yaml#
- $ref: /schemas/pci/snps,dw-pcie.yaml#
properties:
compatible:

View File

@ -0,0 +1,266 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/pci/snps,dw-pcie-common.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Synopsys DWC PCIe RP/EP controller
maintainers:
- Jingoo Han <jingoohan1@gmail.com>
- Gustavo Pimentel <gustavo.pimentel@synopsys.com>
description:
Generic Synopsys DesignWare PCIe Root Port and Endpoint controller
properties.
select: false
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 for each activated function, while the rest of the sub-spaces
are common for all of them (if there are more than one).
minItems: 2
maxItems: 6
reg-names:
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
Endpoint 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
interrupt-names:
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
clock-names:
minItems: 1
maxItems: 7
items:
oneOf:
- 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.
const: dbi
- description:
Application AXI-bus Master interface clock. Basically this is
a clock for the controller DMA interface (PCI-to-CPU).
const: mstr
- description:
Application AXI-bus Slave interface clock. This is a clock for
the CPU-to-PCI memory IO interface.
const: slv
- 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
- description:
Vendor-specific clock names. Consider using the generic names
above for new bindings.
oneOf:
- description: See native 'dbi' clock for details
enum: [ pcie, pcie_apb_sys, aclk_dbi ]
- description: See native 'mstr/slv' clock for details
enum: [ pcie_bus, pcie_inbound_axi, pcie_aclk, aclk_mst, aclk_slv ]
- description: See native 'pipe' clock for details
enum: [ pcie_phy, pcie_phy_ref, link ]
- description: See native 'aux' clock for details
enum: [ pcie_aux ]
- description: See native 'ref' clock for details.
enum: [ gio ]
- description: See nativs 'phy_reg' clock for details
enum: [ pcie_apb_phy, pclk ]
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-names:
minItems: 1
maxItems: 10
items:
oneOf:
- 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: Application-dependent interface reset
const: app
- 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
- description:
Vendor-specific reset names. Consider using the generic names
above for new bindings.
oneOf:
- description: See native 'app' reset for details
enum: [ apps, gio, apb ]
- description: See native 'phy' reset for details
enum: [ pciephy, link ]
- description: See native 'pwr' reset for details
enum: [ turnoff ]
phys:
description:
There can be up to the number of possible lanes PHYs specified placed in
the phandle array in the line-based order. Obviously each the specified
PHYs are supposed to be able to work in the PCIe mode with a speed
implied by the DWC PCIe controller they are attached to.
minItems: 1
maxItems: 16
phy-names:
minItems: 1
maxItems: 16
oneOf:
- description: Generic PHY names
items:
pattern: '^pcie[0-9]+$'
- description:
Vendor-specific PHY names. Consider using the generic
names above for new bindings.
items:
oneOf:
- pattern: '^pcie(-?phy[0-9]*)?$'
- pattern: '^p2u-[0-7]$'
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
max-link-speed:
maximum: 5
num-lanes:
description:
Number of PCIe link lanes to use. Can be omitted if the already brought
up link is supposed to be preserved.
maximum: 16
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.
dma-coherent: true
additionalProperties: true
...

View File

@ -13,76 +13,182 @@ maintainers:
description: |
Synopsys DesignWare PCIe host controller endpoint
allOf:
- $ref: /schemas/pci/pci-ep.yaml#
# Please create a separate DT-schema for your DWC PCIe Endpoint controller
# and make sure it's assigned with the vendor-specific compatible string.
select:
properties:
compatible:
anyOf:
- {}
- const: snps,dw-pcie-ep
const: snps,dw-pcie-ep
required:
- compatible
allOf:
- $ref: /schemas/pci/pci-ep.yaml#
- $ref: /schemas/pci/snps,dw-pcie-common.yaml#
properties:
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:
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: 2
maxItems: 4
maxItems: 5
reg-names:
minItems: 2
maxItems: 4
maxItems: 5
items:
enum: [dbi, dbi2, config, atu, addr_space, link, atu_dma, appl]
oneOf:
- description:
Basic DWC PCIe controller configuration-space accessible over
the DBI interface. This memory space is either activated with
CDM/ELBI = 0 and CS2 = 0 or is a contiguous memory region
with all spaces. Note iATU/eDMA CSRs are indirectly accessible
via the PL viewports on the DWC PCIe controllers older than
v4.80a.
const: dbi
- description:
Shadow DWC PCIe config-space registers. This space is selected
by setting CDM/ELBI = 0 and CS2 = 1. This is an intermix of
the PCI-SIG PCIe CFG-space with the shadow registers for some
PCI Header space, PCI Standard and Extended Structures. It's
mainly relevant for the end-point controller configuration,
but still there are some shadow registers available for the
Root Port mode too.
const: dbi2
- description:
External Local Bus registers. It's an application-dependent
registers normally defined by the platform engineers. The space
can be selected by setting CDM/ELBI = 1 and CS2 = 0 wires or can
be accessed over some platform-specific means (for instance
as a part of a system controller).
enum: [ elbi, 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.
const: atu
- description:
Platform-specific eDMA registers. Some platforms may have eDMA
CSRs mapped in a non-standard base address. The registers offset
can be changed or the MS/LS-bits of the address can be attached
in an additional RTL block before the MEM-IO transactions reach
the DW PCIe slave interface.
const: dma
- 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.
const: phy
- description:
Outbound iATU-capable memory-region which will be used to
generate various application-specific traffic on the PCIe bus
hierarchy. It's usage scenario depends on the endpoint
functionality, for instance it can be used to create MSI(X)
messages.
const: addr_space
- description:
Vendor-specific CSR names. Consider using the generic names above
for new bindings.
oneOf:
- description: See native 'elbi/app' CSR region for details.
enum: [ link, appl ]
- description: See native 'atu' CSR region for details.
enum: [ atu_dma ]
allOf:
- contains:
const: dbi
- contains:
const: addr_space
reset-gpio:
description: GPIO pin number of PERST# signal
maxItems: 1
deprecated: true
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
reset-gpios:
description: GPIO controlled connection to PERST# signal
maxItems: 1
interrupt-names:
minItems: 1
maxItems: 20
items:
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 has detected an
uncorrectable error.
const: sft_ue
- description:
Application-specific IRQ raised depending on the vendor-specific
events basis.
const: app
- description:
Vendor-specific IRQ names. Consider using the generic names above
for new bindings.
oneOf:
- description: See native "app" IRQ for details
enum: [ intr ]
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:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 256
description: number of inbound address translation windows
deprecated: true
num-ob-windows:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 256
description: number of outbound address translation windows
deprecated: true
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";
};
interrupts = <23>, <24>;
interrupt-names = "dma0", "dma1";
clocks = <&sys_clk 12>, <&sys_clk 24>;
clock-names = "dbi", "ref";
resets = <&sys_rst 12>, <&sys_rst 24>;
reset-names = "dbi", "phy";
phys = <&pcie_phy0>, <&pcie_phy1>, <&pcie_phy2>, <&pcie_phy3>;
phy-names = "pcie0", "pcie1", "pcie2", "pcie3";
max-link-speed = <3>;
max-functions = /bits/ 8 <4>;
};

View File

@ -13,20 +13,25 @@ maintainers:
description: |
Synopsys DesignWare PCIe host controller
allOf:
- $ref: /schemas/pci/pci-bus.yaml#
# Please create a separate DT-schema for your DWC PCIe Root Port controller
# and make sure it's assigned with the vendor-specific compatible string.
select:
properties:
compatible:
anyOf:
- {}
- const: snps,dw-pcie
const: snps,dw-pcie
required:
- compatible
allOf:
- $ref: /schemas/pci/pci-bus.yaml#
- $ref: /schemas/pci/snps,dw-pcie-common.yaml#
properties:
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,62 +39,174 @@ properties:
minItems: 2
maxItems: 5
items:
enum: [ dbi, dbi2, config, atu, atu_dma, app, appl, elbi, mgmt, ctrl,
parf, cfg, link, ulreg, smu, mpu, apb, phy ]
oneOf:
- description:
Basic DWC PCIe controller configuration-space accessible over
the DBI interface. This memory space is either activated with
CDM/ELBI = 0 and CS2 = 0 or is a contiguous memory region
with all spaces. Note iATU/eDMA CSRs are indirectly accessible
via the PL viewports on the DWC PCIe controllers older than
v4.80a.
const: dbi
- description:
Shadow DWC PCIe config-space registers. This space is selected
by setting CDM/ELBI = 0 and CS2 = 1. This is an intermix of
the PCI-SIG PCIe CFG-space with the shadow registers for some
PCI Header space, PCI Standard and Extended Structures. It's
mainly relevant for the end-point controller configuration,
but still there are some shadow registers available for the
Root Port mode too.
const: dbi2
- description:
External Local Bus registers. It's an application-dependent
registers normally defined by the platform engineers. The space
can be selected by setting CDM/ELBI = 1 and CS2 = 0 wires or can
be accessed over some platform-specific means (for instance
as a part of a system controller).
enum: [ elbi, 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.
const: atu
- description:
Platform-specific eDMA registers. Some platforms may have eDMA
CSRs mapped in a non-standard base address. The registers offset
can be changed or the MS/LS-bits of the address can be attached
in an additional RTL block before the MEM-IO transactions reach
the DW PCIe slave interface.
const: dma
- 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.
const: phy
- description:
Outbound iATU-capable memory-region which will be used to access
the peripheral PCIe devices configuration space.
const: config
- description:
Vendor-specific CSR names. Consider using the generic names above
for new bindings.
oneOf:
- description: See native 'elbi/app' CSR region for details.
enum: [ apb, mgmt, link, ulreg, appl ]
- description: See native 'atu' CSR region for details.
enum: [ atu_dma ]
- description: Syscon-related CSR regions.
enum: [ smu, mpu ]
allOf:
- contains:
const: dbi
- contains:
const: config
num-lanes:
description: |
number of lanes to use (this property should be specified unless
the link is brought already up in firmware)
maximum: 16
interrupts:
description:
DWC PCIe Root Port/Complex specific IRQ signals. At least MSI interrupt
signal is supposed to be specified for the host controller.
minItems: 1
maxItems: 26
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.
num-viewport:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 256
description: |
number of view ports configured in hardware. If a platform
does not specify it, the driver autodetects it.
deprecated: true
interrupt-names:
minItems: 1
maxItems: 26
items:
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 has detected an
uncorrectable error.
const: sft_ue
- description:
Application-specific IRQ raised depending on the vendor-specific
events basis.
const: app
- 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:
Legacy A/B/C/D interrupt signal. Basically it's triggered by
receiving a Assert_INT{A,B,C,D}/Desassert_INT{A,B,C,D} message
from the downstream device.
pattern: "^int(a|b|c|d)$"
- description:
Error condition detected and a flag 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
- description:
Vendor-specific IRQ names. Consider using the generic names above
for new bindings.
oneOf:
- description: See native "app" IRQ for details
enum: [ intr ]
allOf:
- contains:
const: msi
additionalProperties: true
required:
- compatible
- reg
- reg-names
- compatible
examples:
- |
bus {
#address-cells = <1>;
#size-cells = <1>;
pcie@dfc00000 {
device_type = "pci";
compatible = "snps,dw-pcie";
device_type = "pci";
reg = <0xdfc00000 0x0001000>, /* IP registers */
<0xd0000000 0x0002000>; /* Configuration space */
reg-names = "dbi", "config";
@ -97,8 +214,17 @@ examples:
#size-cells = <2>;
ranges = <0x81000000 0 0x00000000 0xde000000 0 0x00010000>,
<0x82000000 0 0xd0400000 0xd0400000 0 0x0d000000>;
bus-range = <0x0 0xff>;
interrupts = <25>, <24>;
interrupt-names = "msi", "hp";
#interrupt-cells = <1>;
reset-gpios = <&port0 0 1>;
phys = <&pcie_phy>;
phy-names = "pcie";
num-lanes = <1>;
};
max-link-speed = <3>;
};

View File

@ -36,7 +36,7 @@ properties:
- const: mpu
interrupts:
maxItems: 1
maxItems: 2
clocks:
items:
@ -94,8 +94,9 @@ examples:
#interrupt-cells = <1>;
ranges = <0x81000000 0 0x40000000 0 0x40000000 0 0x00010000>,
<0x82000000 0 0x50000000 0 0x50000000 0 0x20000000>;
interrupts = <GIC_SPI 215 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "intr";
interrupts = <GIC_SPI 211 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 215 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "msi", "intr";
interrupt-map-mask = <0 0 0 7>;
interrupt-map =
<0 0 0 1 &gic GIC_SPI 215 IRQ_TYPE_LEVEL_HIGH

View File

@ -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

View File

@ -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

View File

@ -952,12 +952,6 @@ static int imx6_pcie_host_init(struct dw_pcie_rp *pp)
}
}
ret = imx6_pcie_deassert_core_reset(imx6_pcie);
if (ret < 0) {
dev_err(dev, "pcie deassert core reset failed: %d\n", ret);
goto err_phy_off;
}
if (imx6_pcie->phy) {
ret = phy_power_on(imx6_pcie->phy);
if (ret) {
@ -965,6 +959,13 @@ static int imx6_pcie_host_init(struct dw_pcie_rp *pp)
goto err_phy_off;
}
}
ret = imx6_pcie_deassert_core_reset(imx6_pcie);
if (ret < 0) {
dev_err(dev, "pcie deassert core reset failed: %d\n", ret);
goto err_phy_off;
}
imx6_setup_phy_mpll(imx6_pcie);
return 0;

View File

@ -0,0 +1,643 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2021 BAIKAL ELECTRONICS, JSC
*
* Authors:
* Vadim Vlasov <Vadim.Vlasov@baikalelectronics.ru>
* Serge Semin <Sergey.Semin@baikalelectronics.ru>
*
* Baikal-T1 PCIe controller driver
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/types.h>
#include "pcie-designware.h"
/* Baikal-T1 System CCU control registers */
#define BT1_CCU_PCIE_CLKC 0x140
#define BT1_CCU_PCIE_REQ_PCS_CLK BIT(16)
#define BT1_CCU_PCIE_REQ_MAC_CLK BIT(17)
#define BT1_CCU_PCIE_REQ_PIPE_CLK BIT(18)
#define BT1_CCU_PCIE_RSTC 0x144
#define BT1_CCU_PCIE_REQ_LINK_RST BIT(13)
#define BT1_CCU_PCIE_REQ_SMLH_RST BIT(14)
#define BT1_CCU_PCIE_REQ_PHY_RST BIT(16)
#define BT1_CCU_PCIE_REQ_CORE_RST BIT(24)
#define BT1_CCU_PCIE_REQ_STICKY_RST BIT(26)
#define BT1_CCU_PCIE_REQ_NSTICKY_RST BIT(27)
#define BT1_CCU_PCIE_PMSC 0x148
#define BT1_CCU_PCIE_LTSSM_STATE_MASK GENMASK(5, 0)
#define BT1_CCU_PCIE_LTSSM_DET_QUIET 0x00
#define BT1_CCU_PCIE_LTSSM_DET_ACT 0x01
#define BT1_CCU_PCIE_LTSSM_POLL_ACT 0x02
#define BT1_CCU_PCIE_LTSSM_POLL_COMP 0x03
#define BT1_CCU_PCIE_LTSSM_POLL_CONF 0x04
#define BT1_CCU_PCIE_LTSSM_PRE_DET_QUIET 0x05
#define BT1_CCU_PCIE_LTSSM_DET_WAIT 0x06
#define BT1_CCU_PCIE_LTSSM_CFG_LNKWD_START 0x07
#define BT1_CCU_PCIE_LTSSM_CFG_LNKWD_ACEPT 0x08
#define BT1_CCU_PCIE_LTSSM_CFG_LNNUM_WAIT 0x09
#define BT1_CCU_PCIE_LTSSM_CFG_LNNUM_ACEPT 0x0a
#define BT1_CCU_PCIE_LTSSM_CFG_COMPLETE 0x0b
#define BT1_CCU_PCIE_LTSSM_CFG_IDLE 0x0c
#define BT1_CCU_PCIE_LTSSM_RCVR_LOCK 0x0d
#define BT1_CCU_PCIE_LTSSM_RCVR_SPEED 0x0e
#define BT1_CCU_PCIE_LTSSM_RCVR_RCVRCFG 0x0f
#define BT1_CCU_PCIE_LTSSM_RCVR_IDLE 0x10
#define BT1_CCU_PCIE_LTSSM_L0 0x11
#define BT1_CCU_PCIE_LTSSM_L0S 0x12
#define BT1_CCU_PCIE_LTSSM_L123_SEND_IDLE 0x13
#define BT1_CCU_PCIE_LTSSM_L1_IDLE 0x14
#define BT1_CCU_PCIE_LTSSM_L2_IDLE 0x15
#define BT1_CCU_PCIE_LTSSM_L2_WAKE 0x16
#define BT1_CCU_PCIE_LTSSM_DIS_ENTRY 0x17
#define BT1_CCU_PCIE_LTSSM_DIS_IDLE 0x18
#define BT1_CCU_PCIE_LTSSM_DISABLE 0x19
#define BT1_CCU_PCIE_LTSSM_LPBK_ENTRY 0x1a
#define BT1_CCU_PCIE_LTSSM_LPBK_ACTIVE 0x1b
#define BT1_CCU_PCIE_LTSSM_LPBK_EXIT 0x1c
#define BT1_CCU_PCIE_LTSSM_LPBK_EXIT_TOUT 0x1d
#define BT1_CCU_PCIE_LTSSM_HOT_RST_ENTRY 0x1e
#define BT1_CCU_PCIE_LTSSM_HOT_RST 0x1f
#define BT1_CCU_PCIE_LTSSM_RCVR_EQ0 0x20
#define BT1_CCU_PCIE_LTSSM_RCVR_EQ1 0x21
#define BT1_CCU_PCIE_LTSSM_RCVR_EQ2 0x22
#define BT1_CCU_PCIE_LTSSM_RCVR_EQ3 0x23
#define BT1_CCU_PCIE_SMLH_LINKUP BIT(6)
#define BT1_CCU_PCIE_RDLH_LINKUP BIT(7)
#define BT1_CCU_PCIE_PM_LINKSTATE_L0S BIT(8)
#define BT1_CCU_PCIE_PM_LINKSTATE_L1 BIT(9)
#define BT1_CCU_PCIE_PM_LINKSTATE_L2 BIT(10)
#define BT1_CCU_PCIE_L1_PENDING BIT(12)
#define BT1_CCU_PCIE_REQ_EXIT_L1 BIT(14)
#define BT1_CCU_PCIE_LTSSM_RCVR_EQ BIT(15)
#define BT1_CCU_PCIE_PM_DSTAT_MASK GENMASK(18, 16)
#define BT1_CCU_PCIE_PM_PME_EN BIT(20)
#define BT1_CCU_PCIE_PM_PME_STATUS BIT(21)
#define BT1_CCU_PCIE_AUX_PM_EN BIT(22)
#define BT1_CCU_PCIE_AUX_PWR_DET BIT(23)
#define BT1_CCU_PCIE_WAKE_DET BIT(24)
#define BT1_CCU_PCIE_TURNOFF_REQ BIT(30)
#define BT1_CCU_PCIE_TURNOFF_ACK BIT(31)
#define BT1_CCU_PCIE_GENC 0x14c
#define BT1_CCU_PCIE_LTSSM_EN BIT(1)
#define BT1_CCU_PCIE_DBI2_MODE BIT(2)
#define BT1_CCU_PCIE_MGMT_EN BIT(3)
#define BT1_CCU_PCIE_RXLANE_FLIP_EN BIT(16)
#define BT1_CCU_PCIE_TXLANE_FLIP_EN BIT(17)
#define BT1_CCU_PCIE_SLV_XFER_PEND BIT(24)
#define BT1_CCU_PCIE_RCV_XFER_PEND BIT(25)
#define BT1_CCU_PCIE_DBI_XFER_PEND BIT(26)
#define BT1_CCU_PCIE_DMA_XFER_PEND BIT(27)
#define BT1_CCU_PCIE_LTSSM_LINKUP(_pmsc) \
({ \
int __state = FIELD_GET(BT1_CCU_PCIE_LTSSM_STATE_MASK, _pmsc); \
__state >= BT1_CCU_PCIE_LTSSM_L0 && __state <= BT1_CCU_PCIE_LTSSM_L2_WAKE; \
})
/* Baikal-T1 PCIe specific control registers */
#define BT1_PCIE_AXI2MGM_LANENUM 0xd04
#define BT1_PCIE_AXI2MGM_LANESEL_MASK GENMASK(3, 0)
#define BT1_PCIE_AXI2MGM_ADDRCTL 0xd08
#define BT1_PCIE_AXI2MGM_PHYREG_ADDR_MASK GENMASK(20, 0)
#define BT1_PCIE_AXI2MGM_READ_FLAG BIT(29)
#define BT1_PCIE_AXI2MGM_DONE BIT(30)
#define BT1_PCIE_AXI2MGM_BUSY BIT(31)
#define BT1_PCIE_AXI2MGM_WRITEDATA 0xd0c
#define BT1_PCIE_AXI2MGM_WDATA GENMASK(15, 0)
#define BT1_PCIE_AXI2MGM_READDATA 0xd10
#define BT1_PCIE_AXI2MGM_RDATA GENMASK(15, 0)
/* Generic Baikal-T1 PCIe interface resources */
#define BT1_PCIE_NUM_APP_CLKS ARRAY_SIZE(bt1_pcie_app_clks)
#define BT1_PCIE_NUM_CORE_CLKS ARRAY_SIZE(bt1_pcie_core_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)
/* PCIe bus setup delays and timeouts */
#define BT1_PCIE_RST_DELAY_MS 100
#define BT1_PCIE_RUN_DELAY_US 100
#define BT1_PCIE_REQ_DELAY_US 1
#define BT1_PCIE_REQ_TIMEOUT_US 1000
#define BT1_PCIE_LNK_DELAY_US 1000
#define BT1_PCIE_LNK_TIMEOUT_US 1000000
static const enum dw_pcie_app_clk bt1_pcie_app_clks[] = {
DW_PCIE_DBI_CLK, DW_PCIE_MSTR_CLK, DW_PCIE_SLV_CLK,
};
static const enum dw_pcie_core_clk bt1_pcie_core_clks[] = {
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[] = {
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,
};
struct bt1_pcie {
struct dw_pcie dw;
struct platform_device *pdev;
struct regmap *sys_regs;
};
#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 -EINVAL;
*val = readl(addr - ofs) >> ofs * BITS_PER_BYTE;
if (size == 4) {
return 0;
} else if (size == 2) {
*val &= 0xffff;
return 0;
} else if (size == 1) {
*val &= 0xff;
return 0;
}
return -EINVAL;
}
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 -EINVAL;
if (size == 4) {
writel(val, addr);
return 0;
} 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 0;
}
return -EINVAL;
}
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) {
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)
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)
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_link(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),
BT1_PCIE_LNK_DELAY_US, BT1_PCIE_LNK_TIMEOUT_US);
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),
BT1_PCIE_LNK_DELAY_US, BT1_PCIE_LNK_TIMEOUT_US);
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),
BT1_PCIE_LNK_DELAY_US, BT1_PCIE_LNK_TIMEOUT_US);
if (ret)
dev_err(pci->dev, "LTSSM failed to get into L0 state\n");
return ret;
}
static void bt1_pcie_stop_link(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);
}
static const struct dw_pcie_ops bt1_pcie_ops = {
.read_dbi = bt1_pcie_read_dbi,
.write_dbi = bt1_pcie_write_dbi,
.write_dbi2 = bt1_pcie_write_dbi2,
.start_link = bt1_pcie_start_link,
.stop_link = bt1_pcie_stop_link,
};
static struct pci_ops bt1_pci_ops = {
.map_bus = dw_pcie_own_conf_map_bus,
.read = pci_generic_config_read32,
.write = pci_generic_config_write32,
};
static int bt1_pcie_get_resources(struct bt1_pcie *btpci)
{
struct device *dev = btpci->dw.dev;
int i;
/* DBI access is supposed to be performed by the dword-aligned IOs */
btpci->dw.pp.bridge->ops = &bt1_pci_ops;
/* These CSRs are in MMIO so we won't check the regmap-methods status */
btpci->sys_regs =
syscon_regmap_lookup_by_phandle(dev->of_node, "baikal,bt1-syscon");
if (IS_ERR(btpci->sys_regs))
return dev_err_probe(dev, PTR_ERR(btpci->sys_regs),
"Failed to get syscon\n");
/* Make sure all the required resources have been specified */
for (i = 0; i < BT1_PCIE_NUM_APP_CLKS; i++) {
if (!btpci->dw.app_clks[bt1_pcie_app_clks[i]].clk) {
dev_err(dev, "App clocks set is incomplete\n");
return -ENOENT;
}
}
for (i = 0; i < BT1_PCIE_NUM_CORE_CLKS; i++) {
if (!btpci->dw.core_clks[bt1_pcie_core_clks[i]].clk) {
dev_err(dev, "Core clocks set is incomplete\n");
return -ENOENT;
}
}
for (i = 0; i < BT1_PCIE_NUM_APP_RSTS; i++) {
if (!btpci->dw.app_rsts[bt1_pcie_app_rsts[i]].rstc) {
dev_err(dev, "App resets set is incomplete\n");
return -ENOENT;
}
}
for (i = 0; i < BT1_PCIE_NUM_CORE_RSTS; i++) {
if (!btpci->dw.core_rsts[bt1_pcie_core_rsts[i]].rstc) {
dev_err(dev, "Core resets set is incomplete\n");
return -ENOENT;
}
}
return 0;
}
static void bt1_pcie_full_stop_bus(struct bt1_pcie *btpci, bool init)
{
struct device *dev = btpci->dw.dev;
struct dw_pcie *pci = &btpci->dw;
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 assert the core
* resets only.
*/
ret = reset_control_bulk_assert(DW_PCIE_NUM_CORE_RSTS, pci->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(DW_PCIE_NUM_CORE_CLKS, pci->core_clks);
clk_bulk_disable_unprepare(DW_PCIE_NUM_APP_CLKS, pci->app_clks);
}
/* The peripheral devices are unavailable anyway so reset them too */
gpiod_set_value_cansleep(pci->pe_rst, 1);
/* Make sure all the resets are settled */
msleep(BT1_PCIE_RST_DELAY_MS);
}
/*
* 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;
struct dw_pcie *pci = &btpci->dw;
u32 val;
int ret;
/* First get out of the Power/Hot reset state */
ret = reset_control_deassert(pci->core_rsts[DW_PCIE_PWR_RST].rstc);
if (ret) {
dev_err(dev, "Failed to deassert PHY reset\n");
return ret;
}
ret = reset_control_deassert(pci->core_rsts[DW_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),
BT1_PCIE_REQ_DELAY_US, BT1_PCIE_REQ_TIMEOUT_US);
if (ret) {
dev_err(dev, "Timed out waiting for PM to stop PHY resetting\n");
goto err_assert_hot_rst;
}
ret = reset_control_deassert(pci->core_rsts[DW_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(DW_PCIE_NUM_APP_CLKS, pci->app_clks);
if (ret) {
dev_err(dev, "Failed to enable app clocks\n");
goto err_assert_phy_rst;
}
ret = clk_bulk_prepare_enable(DW_PCIE_NUM_CORE_CLKS, pci->core_clks);
if (ret) {
dev_err(dev, "Failed to enable ref clocks\n");
goto err_disable_app_clk;
}
/* 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),
BT1_PCIE_REQ_DELAY_US, BT1_PCIE_REQ_TIMEOUT_US);
if (ret) {
dev_err(dev, "Timed out waiting for PM to stop core resetting\n");
goto err_disable_core_clk;
}
/* PCS-PIPE interface and controller core can be now activated */
ret = reset_control_deassert(pci->core_rsts[DW_PCIE_PIPE_RST].rstc);
if (ret) {
dev_err(dev, "Failed to deassert PIPE reset\n");
goto err_disable_core_clk;
}
ret = reset_control_deassert(pci->core_rsts[DW_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(DW_PCIE_NUM_APP_RSTS, pci->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(pci->core_rsts[DW_PCIE_STICKY_RST].rstc);
if (ret) {
dev_err(dev, "Failed to deassert sticky reset\n");
goto err_assert_core_rst;
}
ret = reset_control_deassert(pci->core_rsts[DW_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(pci->pe_rst, 0);
/* Make sure the state is settled (LTSSM is still disabled though) */
usleep_range(BT1_PCIE_RUN_DELAY_US, BT1_PCIE_RUN_DELAY_US + 100);
return 0;
err_assert_sticky_rst:
reset_control_assert(pci->core_rsts[DW_PCIE_STICKY_RST].rstc);
err_assert_core_rst:
reset_control_assert(pci->core_rsts[DW_PCIE_CORE_RST].rstc);
err_assert_pipe_rst:
reset_control_assert(pci->core_rsts[DW_PCIE_PIPE_RST].rstc);
err_disable_core_clk:
clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, pci->core_clks);
err_disable_app_clk:
clk_bulk_disable_unprepare(DW_PCIE_NUM_APP_CLKS, pci->app_clks);
err_assert_phy_rst:
reset_control_assert(pci->core_rsts[DW_PCIE_PHY_RST].rstc);
err_assert_hot_rst:
reset_control_assert(pci->core_rsts[DW_PCIE_HOT_RST].rstc);
err_assert_pwr_rst:
reset_control_assert(pci->core_rsts[DW_PCIE_PWR_RST].rstc);
return ret;
}
static int bt1_pcie_host_init(struct dw_pcie_rp *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_resources(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 dw_pcie_rp *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);
}
static const 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;
btpci = devm_kzalloc(&pdev->dev, sizeof(*btpci), GFP_KERNEL);
if (!btpci)
return ERR_PTR(-ENOMEM);
btpci->pdev = pdev;
platform_set_drvdata(pdev, btpci);
return btpci;
}
static int bt1_pcie_add_port(struct bt1_pcie *btpci)
{
struct device *dev = &btpci->pdev->dev;
int ret;
btpci->dw.version = DW_PCIE_VER_460A;
btpci->dw.dev = dev;
btpci->dw.ops = &bt1_pcie_ops;
btpci->dw.pp.num_vectors = MAX_MSI_IRQS;
btpci->dw.pp.ops = &bt1_pcie_host_ops;
dw_pcie_cap_set(&btpci->dw, REQ_RES);
ret = dw_pcie_host_init(&btpci->dw.pp);
return dev_err_probe(dev, ret, "Failed to initialize DWC PCIe host\n");
}
static void bt1_pcie_del_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_port(btpci);
}
static int bt1_pcie_remove(struct platform_device *pdev)
{
struct bt1_pcie *btpci = platform_get_drvdata(pdev);
bt1_pcie_del_port(btpci);
return 0;
}
static const struct of_device_id bt1_pcie_of_match[] = {
{ .compatible = "baikal,bt1-pcie" },
{},
};
MODULE_DEVICE_TABLE(of, bt1_pcie_of_match);
static struct platform_driver bt1_pcie_driver = {
.probe = bt1_pcie_probe,
.remove = bt1_pcie_remove,
.driver = {
.name = "bt1-pcie",
.of_match_table = bt1_pcie_of_match,
},
};
module_platform_driver(bt1_pcie_driver);
MODULE_AUTHOR("Serge Semin <Sergey.Semin@baikalelectronics.ru>");
MODULE_DESCRIPTION("Baikal-T1 PCIe driver");
MODULE_LICENSE("GPL");

View File

@ -13,8 +13,6 @@
#include <linux/pci-epc.h>
#include <linux/pci-epf.h>
#include "../../pci.h"
void dw_pcie_ep_linkup(struct dw_pcie_ep *ep)
{
struct pci_epc *epc = ep->epc;
@ -171,7 +169,7 @@ 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,
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");
@ -643,7 +641,7 @@ static unsigned int dw_pcie_ep_find_ext_capability(struct dw_pcie *pci, int cap)
int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep)
{
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
unsigned int offset;
unsigned int offset, ptm_cap_base;
unsigned int nbars;
u8 hdr_type;
u32 reg;
@ -659,6 +657,7 @@ int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep)
}
offset = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR);
ptm_cap_base = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_PTM);
dw_pcie_dbi_ro_wr_en(pci);
@ -671,6 +670,22 @@ int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep)
dw_pcie_writel_dbi(pci, offset + PCI_REBAR_CAP, 0x0);
}
/*
* PTM responder capability can be disabled only after disabling
* PTM root capability.
*/
if (ptm_cap_base) {
dw_pcie_dbi_ro_wr_en(pci);
reg = dw_pcie_readl_dbi(pci, ptm_cap_base + PCI_PTM_CAP);
reg &= ~PCI_PTM_CAP_ROOT;
dw_pcie_writel_dbi(pci, ptm_cap_base + PCI_PTM_CAP, reg);
reg = dw_pcie_readl_dbi(pci, ptm_cap_base + PCI_PTM_CAP);
reg &= ~(PCI_PTM_CAP_RES | PCI_PTM_GRANULARITY_MASK);
dw_pcie_writel_dbi(pci, ptm_cap_base + PCI_PTM_CAP, reg);
dw_pcie_dbi_ro_wr_dis(pci);
}
dw_pcie_setup(pci);
dw_pcie_dbi_ro_wr_dis(pci);
@ -694,23 +709,9 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
INIT_LIST_HEAD(&ep->func_list);
if (!pci->dbi_base) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
pci->dbi_base = devm_pci_remap_cfg_resource(dev, res);
if (IS_ERR(pci->dbi_base))
return PTR_ERR(pci->dbi_base);
}
if (!pci->dbi_base2) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi2");
if (!res) {
pci->dbi_base2 = pci->dbi_base + SZ_4K;
} else {
pci->dbi_base2 = devm_pci_remap_cfg_resource(dev, res);
if (IS_ERR(pci->dbi_base2))
return PTR_ERR(pci->dbi_base2);
}
}
ret = dw_pcie_get_resources(pci);
if (ret)
return ret;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space");
if (!res)
@ -739,9 +740,6 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
return -ENOMEM;
ep->outbound_addr = addr;
if (pci->link_gen < 1)
pci->link_gen = of_pci_get_max_link_speed(np);
epc = devm_pci_epc_create(dev, &epc_ops);
if (IS_ERR(epc)) {
dev_err(dev, "Failed to create epc device\n");

View File

@ -16,7 +16,6 @@
#include <linux/pci_regs.h>
#include <linux/platform_device.h>
#include "../../pci.h"
#include "pcie-designware.h"
static struct pci_ops dw_pcie_ops;
@ -395,6 +394,10 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
raw_spin_lock_init(&pp->lock);
ret = dw_pcie_get_resources(pci);
if (ret)
return ret;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config");
if (res) {
pp->cfg0_size = resource_size(res);
@ -408,13 +411,6 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
return -ENODEV;
}
if (!pci->dbi_base) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
pci->dbi_base = devm_pci_remap_cfg_resource(dev, res);
if (IS_ERR(pci->dbi_base))
return PTR_ERR(pci->dbi_base);
}
bridge = devm_pci_alloc_host_bridge(dev, 0);
if (!bridge)
return -ENOMEM;
@ -429,9 +425,6 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
pp->io_base = pci_pio_to_address(win->res->start);
}
if (pci->link_gen < 1)
pci->link_gen = of_pci_get_max_link_speed(np);
/* Set default bus ops */
bridge->ops = &dw_pcie_ops;
bridge->child_ops = &dw_child_pcie_ops;
@ -643,12 +636,15 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *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)
@ -685,9 +681,32 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *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;
}

View File

@ -10,7 +10,10 @@
#include <linux/align.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/ioport.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/sizes.h>
@ -19,6 +22,148 @@
#include "../../pci.h"
#include "pcie-designware.h"
static const char * const dw_pcie_app_clks[DW_PCIE_NUM_APP_CLKS] = {
[DW_PCIE_DBI_CLK] = "dbi",
[DW_PCIE_MSTR_CLK] = "mstr",
[DW_PCIE_SLV_CLK] = "slv",
};
static const char * const dw_pcie_core_clks[DW_PCIE_NUM_CORE_CLKS] = {
[DW_PCIE_PIPE_CLK] = "pipe",
[DW_PCIE_CORE_CLK] = "core",
[DW_PCIE_AUX_CLK] = "aux",
[DW_PCIE_REF_CLK] = "ref",
};
static const char * const dw_pcie_app_rsts[DW_PCIE_NUM_APP_RSTS] = {
[DW_PCIE_DBI_RST] = "dbi",
[DW_PCIE_MSTR_RST] = "mstr",
[DW_PCIE_SLV_RST] = "slv",
};
static const char * const dw_pcie_core_rsts[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",
};
static int dw_pcie_get_clocks(struct dw_pcie *pci)
{
int i, ret;
for (i = 0; i < DW_PCIE_NUM_APP_CLKS; i++)
pci->app_clks[i].id = dw_pcie_app_clks[i];
for (i = 0; i < DW_PCIE_NUM_CORE_CLKS; i++)
pci->core_clks[i].id = dw_pcie_core_clks[i];
ret = devm_clk_bulk_get_optional(pci->dev, DW_PCIE_NUM_APP_CLKS,
pci->app_clks);
if (ret)
return ret;
return devm_clk_bulk_get_optional(pci->dev, DW_PCIE_NUM_CORE_CLKS,
pci->core_clks);
}
static int dw_pcie_get_resets(struct dw_pcie *pci)
{
int i, ret;
for (i = 0; i < DW_PCIE_NUM_APP_RSTS; i++)
pci->app_rsts[i].id = dw_pcie_app_rsts[i];
for (i = 0; i < DW_PCIE_NUM_CORE_RSTS; i++)
pci->core_rsts[i].id = dw_pcie_core_rsts[i];
ret = devm_reset_control_bulk_get_optional_shared(pci->dev,
DW_PCIE_NUM_APP_RSTS,
pci->app_rsts);
if (ret)
return ret;
ret = devm_reset_control_bulk_get_optional_exclusive(pci->dev,
DW_PCIE_NUM_CORE_RSTS,
pci->core_rsts);
if (ret)
return ret;
pci->pe_rst = devm_gpiod_get_optional(pci->dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(pci->pe_rst))
return PTR_ERR(pci->pe_rst);
return 0;
}
int dw_pcie_get_resources(struct dw_pcie *pci)
{
struct platform_device *pdev = to_platform_device(pci->dev);
struct device_node *np = dev_of_node(pci->dev);
struct resource *res;
int ret;
if (!pci->dbi_base) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
pci->dbi_base = devm_pci_remap_cfg_resource(pci->dev, res);
if (IS_ERR(pci->dbi_base))
return PTR_ERR(pci->dbi_base);
}
/* DBI2 is mainly useful for the endpoint controller */
if (!pci->dbi_base2) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi2");
if (res) {
pci->dbi_base2 = devm_pci_remap_cfg_resource(pci->dev, res);
if (IS_ERR(pci->dbi_base2))
return PTR_ERR(pci->dbi_base2);
} else {
pci->dbi_base2 = pci->dbi_base + SZ_4K;
}
}
/* For non-unrolled iATU/eDMA platforms this range will be ignored */
if (!pci->atu_base) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "atu");
if (res) {
pci->atu_size = resource_size(res);
pci->atu_base = devm_ioremap_resource(pci->dev, res);
if (IS_ERR(pci->atu_base))
return PTR_ERR(pci->atu_base);
} else {
pci->atu_base = pci->dbi_base + DEFAULT_DBI_ATU_OFFSET;
}
}
/* Set a default value suitable for at most 8 in and 8 out windows */
if (!pci->atu_size)
pci->atu_size = SZ_4K;
/* LLDD is supposed to manually switch the clocks and resets state */
if (dw_pcie_cap_is(pci, REQ_RES)) {
ret = dw_pcie_get_clocks(pci);
if (ret)
return ret;
ret = dw_pcie_get_resets(pci);
if (ret)
return ret;
}
if (pci->link_gen < 1)
pci->link_gen = of_pci_get_max_link_speed(np);
of_property_read_u32(np, "num-lanes", &pci->num_lanes);
if (of_property_read_bool(np, "snps,enable-cdm-check"))
dw_pcie_cap_set(pci, CDM_CHECK);
return 0;
}
void dw_pcie_version_detect(struct dw_pcie *pci)
{
u32 ver;
@ -211,7 +356,7 @@ void dw_pcie_write_dbi2(struct dw_pcie *pci, u32 reg, size_t size, u32 val)
static inline void __iomem *dw_pcie_select_atu(struct dw_pcie *pci, u32 dir,
u32 index)
{
if (pci->iatu_unroll_enabled)
if (dw_pcie_cap_is(pci, IATU_UNROLL))
return pci->atu_base + PCIE_ATU_UNROLL_BASE(dir, index);
dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, dir | index);
@ -393,7 +538,59 @@ static inline void dw_pcie_writel_atu_ib(struct dw_pcie *pci, u32 index, u32 reg
dw_pcie_writel_atu(pci, PCIE_ATU_REGION_DIR_IB, index, reg, val);
}
int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
int 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) || !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;
@ -448,7 +645,7 @@ int dw_pcie_wait_for_link(struct dw_pcie *pci)
}
if (retries >= LINK_WAIT_MAX_RETRIES) {
dev_err(pci->dev, "Phy link never came up\n");
dev_info(pci->dev, "Phy link never came up\n");
return -ETIMEDOUT;
}
@ -522,26 +719,21 @@ static void dw_pcie_link_set_max_speed(struct dw_pcie *pci, u32 link_gen)
}
static bool dw_pcie_iatu_unroll_enabled(struct dw_pcie *pci)
{
u32 val;
val = dw_pcie_readl_dbi(pci, PCIE_ATU_VIEWPORT);
if (val == 0xffffffff)
return true;
return false;
}
static void dw_pcie_iatu_detect_regions(struct dw_pcie *pci)
void dw_pcie_iatu_detect(struct dw_pcie *pci)
{
int max_region, ob, ib;
u32 val, min, dir;
u64 max;
if (pci->iatu_unroll_enabled) {
val = dw_pcie_readl_dbi(pci, PCIE_ATU_VIEWPORT);
if (val == 0xFFFFFFFF) {
dw_pcie_cap_set(pci, IATU_UNROLL);
max_region = min((int)pci->atu_size / 512, 256);
} else {
pci->atu_base = pci->dbi_base + PCIE_ATU_VIEWPORT_BASE;
pci->atu_size = PCIE_ATU_VIEWPORT_SIZE;
dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, 0xFF);
max_region = dw_pcie_readl_dbi(pci, PCIE_ATU_VIEWPORT) + 1;
}
@ -583,46 +775,15 @@ static void dw_pcie_iatu_detect_regions(struct dw_pcie *pci)
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)
{
struct platform_device *pdev = to_platform_device(pci->dev);
pci->iatu_unroll_enabled = dw_pcie_iatu_unroll_enabled(pci);
if (pci->iatu_unroll_enabled) {
if (!pci->atu_base) {
struct resource *res =
platform_get_resource_byname(pdev, IORESOURCE_MEM, "atu");
if (res) {
pci->atu_size = resource_size(res);
pci->atu_base = devm_ioremap_resource(pci->dev, res);
}
if (!pci->atu_base || IS_ERR(pci->atu_base))
pci->atu_base = pci->dbi_base + DEFAULT_DBI_ATU_OFFSET;
}
if (!pci->atu_size)
/* Pick a minimal default, enough for 8 in and 8 out windows */
pci->atu_size = SZ_4K;
} else {
pci->atu_base = pci->dbi_base + PCIE_ATU_VIEWPORT_BASE;
pci->atu_size = PCIE_ATU_VIEWPORT_SIZE;
}
dw_pcie_iatu_detect_regions(pci);
dev_info(pci->dev, "iATU unroll: %s\n", pci->iatu_unroll_enabled ?
"enabled" : "disabled");
dev_info(pci->dev, "iATU regions: %u ob, %u ib, align %uK, limit %lluG\n",
dev_info(pci->dev, "iATU: unroll %s, %u ob, %u ib, align %uK, limit %lluG\n",
dw_pcie_cap_is(pci, IATU_UNROLL) ? "T" : "F",
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)
{
struct device_node *np = pci->dev->of_node;
u32 val;
if (pci->link_gen > 0)
@ -641,7 +802,7 @@ void dw_pcie_setup(struct dw_pcie *pci)
if (pci->n_fts[1]) {
val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
val &= ~PORT_LOGIC_N_FTS_MASK;
val |= pci->n_fts[pci->link_gen - 1];
val |= pci->n_fts[1];
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
}
@ -650,14 +811,13 @@ void dw_pcie_setup(struct dw_pcie *pci)
val |= PORT_LINK_DLL_LINK_EN;
dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val);
if (of_property_read_bool(np, "snps,enable-cdm-check")) {
if (dw_pcie_cap_is(pci, CDM_CHECK)) {
val = dw_pcie_readl_dbi(pci, PCIE_PL_CHK_REG_CONTROL_STATUS);
val |= PCIE_PL_CHK_REG_CHK_REG_CONTINUOUS |
PCIE_PL_CHK_REG_CHK_REG_START;
dw_pcie_writel_dbi(pci, PCIE_PL_CHK_REG_CONTROL_STATUS, val);
}
of_property_read_u32(np, "num-lanes", &pci->num_lanes);
if (!pci->num_lanes) {
dev_dbg(pci->dev, "Using h/w default number of lanes\n");
return;

View File

@ -12,10 +12,14 @@
#define _PCIE_DESIGNWARE_H
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/gpio/consumer.h>
#include <linux/irq.h>
#include <linux/msi.h>
#include <linux/pci.h>
#include <linux/reset.h>
#include <linux/pci-epc.h>
#include <linux/pci-epf.h>
@ -43,6 +47,17 @@
(__dw_pcie_ver_cmp(_pci, _ver, ==) && \
__dw_pcie_ver_cmp(_pci, TYPE_ ## _type, >=))
/* DWC PCIe controller capabilities */
#define DW_PCIE_CAP_REQ_RES 0
#define DW_PCIE_CAP_IATU_UNROLL 1
#define DW_PCIE_CAP_CDM_CHECK 2
#define dw_pcie_cap_is(_pci, _cap) \
test_bit(DW_PCIE_CAP_ ## _cap, &(_pci)->caps)
#define dw_pcie_cap_set(_pci, _cap) \
set_bit(DW_PCIE_CAP_ ## _cap, &(_pci)->caps)
/* Parameters for the waiting for link up routine */
#define LINK_WAIT_MAX_RETRIES 10
#define LINK_WAIT_USLEEP_MIN 90000
@ -222,6 +237,39 @@ enum dw_pcie_device_mode {
DW_PCIE_RC_TYPE,
};
enum dw_pcie_app_clk {
DW_PCIE_DBI_CLK,
DW_PCIE_MSTR_CLK,
DW_PCIE_SLV_CLK,
DW_PCIE_NUM_APP_CLKS
};
enum dw_pcie_core_clk {
DW_PCIE_PIPE_CLK,
DW_PCIE_CORE_CLK,
DW_PCIE_AUX_CLK,
DW_PCIE_REF_CLK,
DW_PCIE_NUM_CORE_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 dw_pcie_rp *pp);
void (*host_deinit)(struct dw_pcie_rp *pp);
@ -317,10 +365,15 @@ struct dw_pcie {
const struct dw_pcie_ops *ops;
u32 version;
u32 type;
unsigned long caps;
int num_lanes;
int link_gen;
u8 n_fts[2];
bool iatu_unroll_enabled: 1;
struct clk_bulk_data app_clks[DW_PCIE_NUM_APP_CLKS];
struct clk_bulk_data core_clks[DW_PCIE_NUM_CORE_CLKS];
struct reset_control_bulk_data app_rsts[DW_PCIE_NUM_APP_RSTS];
struct reset_control_bulk_data core_rsts[DW_PCIE_NUM_CORE_RSTS];
struct gpio_desc *pe_rst;
};
#define to_dw_pcie_from_pp(port) container_of((port), struct dw_pcie, pp)
@ -328,6 +381,8 @@ struct dw_pcie {
#define to_dw_pcie_from_ep(endpoint) \
container_of((endpoint), struct dw_pcie, ep)
int dw_pcie_get_resources(struct dw_pcie *pci);
void dw_pcie_version_detect(struct dw_pcie *pci);
u8 dw_pcie_find_capability(struct dw_pcie *pci, u8 cap);
@ -346,7 +401,9 @@ 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 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);

View File

@ -10,11 +10,11 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/pci.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
@ -60,7 +60,7 @@ struct histb_pcie {
struct reset_control *sys_reset;
struct reset_control *bus_reset;
void __iomem *ctrl;
int reset_gpio;
struct gpio_desc *reset_gpio;
struct regulator *vpcie;
};
@ -212,8 +212,8 @@ static void histb_pcie_host_disable(struct histb_pcie *hipcie)
clk_disable_unprepare(hipcie->sys_clk);
clk_disable_unprepare(hipcie->bus_clk);
if (gpio_is_valid(hipcie->reset_gpio))
gpio_set_value_cansleep(hipcie->reset_gpio, 0);
if (hipcie->reset_gpio)
gpiod_set_value_cansleep(hipcie->reset_gpio, 1);
if (hipcie->vpcie)
regulator_disable(hipcie->vpcie);
@ -235,8 +235,8 @@ static int histb_pcie_host_enable(struct dw_pcie_rp *pp)
}
}
if (gpio_is_valid(hipcie->reset_gpio))
gpio_set_value_cansleep(hipcie->reset_gpio, 1);
if (hipcie->reset_gpio)
gpiod_set_value_cansleep(hipcie->reset_gpio, 0);
ret = clk_prepare_enable(hipcie->bus_clk);
if (ret) {
@ -298,10 +298,7 @@ static int histb_pcie_probe(struct platform_device *pdev)
struct histb_pcie *hipcie;
struct dw_pcie *pci;
struct dw_pcie_rp *pp;
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
enum of_gpio_flags of_flags;
unsigned long flag = GPIOF_DIR_OUT;
int ret;
hipcie = devm_kzalloc(dev, sizeof(*hipcie), GFP_KERNEL);
@ -336,17 +333,19 @@ static int histb_pcie_probe(struct platform_device *pdev)
hipcie->vpcie = NULL;
}
hipcie->reset_gpio = of_get_named_gpio_flags(np,
"reset-gpios", 0, &of_flags);
if (of_flags & OF_GPIO_ACTIVE_LOW)
flag |= GPIOF_ACTIVE_LOW;
if (gpio_is_valid(hipcie->reset_gpio)) {
ret = devm_gpio_request_one(dev, hipcie->reset_gpio,
flag, "PCIe device power control");
hipcie->reset_gpio = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_HIGH);
ret = PTR_ERR_OR_ZERO(hipcie->reset_gpio);
if (ret) {
dev_err(dev, "unable to request gpio\n");
dev_err(dev, "unable to request reset gpio: %d\n", ret);
return ret;
}
ret = gpiod_set_consumer_name(hipcie->reset_gpio,
"PCIe device power control");
if (ret) {
dev_err(dev, "unable to set reset gpio name: %d\n", ret);
return ret;
}
hipcie->aux_clk = devm_clk_get(dev, "aux");

View File

@ -1236,7 +1236,7 @@ static int qcom_pcie_init_2_7_0(struct qcom_pcie *pcie)
ret = reset_control_assert(res->pci_reset);
if (ret < 0) {
dev_err(dev, "cannot deassert pci reset\n");
dev_err(dev, "cannot assert pci reset\n");
goto err_disable_clocks;
}

View File

@ -1058,6 +1058,7 @@
/* Precision Time Measurement */
#define PCI_PTM_CAP 0x04 /* PTM Capability */
#define PCI_PTM_CAP_REQ 0x00000001 /* Requester capable */
#define PCI_PTM_CAP_RES 0x00000002 /* Responder capable */
#define PCI_PTM_CAP_ROOT 0x00000004 /* Root capable */
#define PCI_PTM_GRANULARITY_MASK 0x0000FF00 /* Clock granularity */
#define PCI_PTM_CTRL 0x08 /* PTM Control */