提交 d4342156 编写于 作者: L Linus Torvalds

Merge tag 'pci-v4.5-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci

Pull PCI updates from Bjorn Helgaas:
 "PCI changes for the v4.5 merge window:

  Enumeration:
   - Simplify config space size computation (Bjorn Helgaas)
   - Avoid iterating through ROM outside the resource window (Edward O'Callaghan)
   - Support PCIe devices with short cfg_size (Jason S. McMullan)
   - Add Netronome vendor and device IDs (Jason S. McMullan)
   - Limit config space size for Netronome NFP6000 family (Jason S. McMullan)
   - Add Netronome NFP4000 PF device ID (Simon Horman)
   - Limit config space size for Netronome NFP4000 (Simon Horman)
   - Print warnings for all invalid expansion ROM headers (Vladis Dronov)

  Resource management:
   - Fix minimum allocation address overwrite (Christoph Biedl)

  PCI device hotplug:
   - acpiphp_ibm: Fix null dereferences on null ibm_slot (Colin Ian King)
   - pciehp: Always protect pciehp_disable_slot() with hotplug mutex (Guenter Roeck)
   - shpchp: Constify hpc_ops structure (Julia Lawall)
   - ibmphp: Remove unneeded NULL test (Julia Lawall)

  Power management:
   - Make ASPM sysfs link_state_store() consistent with link_state_show() (Andy Lutomirski)

  Virtualization
   - Add function 1 DMA alias quirk for Lite-On/Plextor M6e/Marvell 88SS9183 (Tim Sander)

  MSI:
   - Remove empty pci_msi_init_pci_dev() (Bjorn Helgaas)
   - Mark PCIe/PCI (MSI) IRQ cascade handlers as IRQF_NO_THREAD (Grygorii Strashko)
   - Initialize MSI capability for all architectures (Guilherme G. Piccoli)
   - Relax msi_domain_alloc() to support parentless MSI irqdomains (Liu Jiang)

  ARM Versatile host bridge driver:
   - Remove unused pci_sys_data structures (Lorenzo Pieralisi)

  Broadcom iProc host bridge driver:
   - Hide CONFIG_PCIE_IPROC (Arnd Bergmann)
   - Do not use 0x in front of %pap (Dmitry V. Krivenok)
   - Update iProc PCIe device tree binding (Ray Jui)
   - Add PAXC interface support (Ray Jui)
   - Add iProc PCIe MSI device tree binding (Ray Jui)
   - Add iProc PCIe MSI support (Ray Jui)

  Freescale i.MX6 host bridge driver:
   - Use gpio_set_value_cansleep() (Fabio Estevam)
   - Add support for active-low reset GPIO (Petr Štetiar)

  HiSilicon host bridge driver:
   - Add support for HiSilicon Hip06 PCIe host controllers (Gabriele Paoloni)

  Intel VMD host bridge driver:
   - Export irq_domain_set_info() for module use (Keith Busch)
   - x86/PCI: Allow DMA ops specific to a PCI domain (Keith Busch)
   - Use 32 bit PCI domain numbers (Keith Busch)
   - Add driver for Intel Volume Management Device (VMD) (Keith Busch)

  Qualcomm host bridge driver:
   - Document PCIe devicetree bindings (Stanimir Varbanov)
   - Add Qualcomm PCIe controller driver (Stanimir Varbanov)
   - dts: apq8064: add PCIe devicetree node (Stanimir Varbanov)
   - dts: ifc6410: enable PCIe DT node for this board (Stanimir Varbanov)

  Renesas R-Car host bridge driver:
   - Add support for R-Car H3 to pcie-rcar (Harunobu Kurokawa)
   - Allow DT to override default window settings (Phil Edworthy)
   - Convert to DT resource parsing API (Phil Edworthy)
   - Revert "PCI: rcar: Build pcie-rcar.c only on ARM" (Phil Edworthy)
   - Remove unused pci_sys_data struct from pcie-rcar (Phil Edworthy)
   - Add runtime PM support to pcie-rcar (Phil Edworthy)
   - Add Gen2 PHY setup to pcie-rcar (Phil Edworthy)
   - Add gen2 fallback compatibility string for pci-rcar-gen2 (Simon Horman)
   - Add gen2 fallback compatibility string for pcie-rcar (Simon Horman)

  Synopsys DesignWare host bridge driver:
   - Simplify control flow (Bjorn Helgaas)
   - Make config accessor override checking symmetric (Bjorn Helgaas)
   - Ensure ATU is enabled before IO/conf space accesses (Stanimir Varbanov)

  Miscellaneous:
   - Add of_pci_get_host_bridge_resources() stub (Arnd Bergmann)
   - Check for PCI_HEADER_TYPE_BRIDGE equality, not bitmask (Bjorn Helgaas)
   - Fix all whitespace issues (Bogicevic Sasa)
   - x86/PCI: Simplify pci_bios_{read,write} (Geliang Tang)
   - Use to_pci_dev() instead of open-coding it (Geliang Tang)
   - Use kobj_to_dev() instead of open-coding it (Geliang Tang)
   - Use list_for_each_entry() to simplify code (Geliang Tang)
   - Fix typos in <linux/msi.h> (Thomas Petazzoni)
   - x86/PCI: Clarify AMD Fam10h config access restrictions comment (Tomasz Nowicki)"

* tag 'pci-v4.5-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci: (58 commits)
  PCI: Add function 1 DMA alias quirk for Lite-On/Plextor M6e/Marvell 88SS9183
  PCI: Limit config space size for Netronome NFP4000
  PCI: Add Netronome NFP4000 PF device ID
  x86/PCI: Add driver for Intel Volume Management Device (VMD)
  PCI/AER: Use 32 bit PCI domain numbers
  x86/PCI: Allow DMA ops specific to a PCI domain
  irqdomain: Export irq_domain_set_info() for module use
  PCI: host: Add of_pci_get_host_bridge_resources() stub
  genirq/MSI: Relax msi_domain_alloc() to support parentless MSI irqdomains
  PCI: rcar: Add Gen2 PHY setup to pcie-rcar
  PCI: rcar: Add runtime PM support to pcie-rcar
  PCI: designware: Make config accessor override checking symmetric
  PCI: ibmphp: Remove unneeded NULL test
  ARM: dts: ifc6410: enable PCIe DT node for this board
  ARM: dts: apq8064: add PCIe devicetree node
  PCI: hotplug: Use list_for_each_entry() to simplify code
  PCI: rcar: Remove unused pci_sys_data struct from pcie-rcar
  PCI: hisi: Add support for HiSilicon Hip06 PCIe host controllers
  PCI: Avoid iterating through memory outside the resource window
  PCI: acpiphp_ibm: Fix null dereferences on null ibm_slot
  ...
* Broadcom iProc PCIe controller with the platform bus interface
Required properties:
- compatible: Must be "brcm,iproc-pcie"
- compatible: Must be "brcm,iproc-pcie" for PAXB, or "brcm,iproc-pcie-paxc"
for PAXC. PAXB-based root complex is used for external endpoint devices.
PAXC-based root complex is connected to emulated endpoint devices
internal to the ASIC
- reg: base address and length of the PCIe controller I/O register space
- #interrupt-cells: set to <1>
- interrupt-map-mask and interrupt-map, standard PCI properties to define the
......@@ -32,6 +35,28 @@ Optional:
- brcm,pcie-ob-oarr-size: Some iProc SoCs need the OARR size bit to be set to
increase the outbound window size
MSI support (optional):
For older platforms without MSI integrated in the GIC, iProc PCIe core provides
an event queue based MSI support. The iProc MSI uses host memories to store
MSI posted writes in the event queues
- msi-parent: Link to the device node of the MSI controller. On newer iProc
platforms, the MSI controller may be gicv2m or gicv3-its. On older iProc
platforms without MSI support in its interrupt controller, one may use the
event queue based MSI support integrated within the iProc PCIe core.
When the iProc event queue based MSI is used, one needs to define the
following properties in the MSI device node:
- compatible: Must be "brcm,iproc-msi"
- msi-controller: claims itself as an MSI controller
- interrupt-parent: Link to its parent interrupt device
- interrupts: List of interrupt IDs from its parent interrupt device
Optional properties:
- brcm,pcie-msi-inten: Needs to be present for some older iProc platforms that
require the interrupt enable registers to be set explicitly to enable MSI
Example:
pcie0: pcie@18012000 {
compatible = "brcm,iproc-pcie";
......@@ -58,6 +83,19 @@ Example:
brcm,pcie-ob-oarr-size;
brcm,pcie-ob-axi-offset = <0x00000000>;
brcm,pcie-ob-window-size = <256>;
msi-parent = <&msi0>;
/* iProc event queue based MSI */
msi0: msi@18012000 {
compatible = "brcm,iproc-msi";
msi-controller;
interrupt-parent = <&gic>;
interrupts = <GIC_SPI 96 IRQ_TYPE_NONE>,
<GIC_SPI 97 IRQ_TYPE_NONE>,
<GIC_SPI 98 IRQ_TYPE_NONE>,
<GIC_SPI 99 IRQ_TYPE_NONE>,
};
};
pcie1: pcie@18013000 {
......
HiSilicon PCIe host bridge DT description
HiSilicon Hip05 and Hip06 PCIe host bridge DT description
HiSilicon PCIe host controller is based on Designware PCI core.
It shares common functions with PCIe Designware core driver and inherits
......@@ -7,8 +7,8 @@ Documentation/devicetree/bindings/pci/designware-pci.txt.
Additional properties are described here:
Required properties:
- compatible: Should contain "hisilicon,hip05-pcie".
Required properties
- compatible: Should contain "hisilicon,hip05-pcie" or "hisilicon,hip06-pcie".
- reg: Should contain rc_dbi, config registers location and length.
- reg-names: Must include the following entries:
"rc_dbi": controller configuration registers;
......@@ -20,7 +20,7 @@ Optional properties:
- status: Either "ok" or "disabled".
- dma-coherent: Present if DMA operations are coherent.
Example:
Hip05 Example (note that Hip06 is the same except compatible):
pcie@0xb0080000 {
compatible = "hisilicon,hip05-pcie", "snps,dw-pcie";
reg = <0 0xb0080000 0 0x10000>, <0x220 0x00000000 0 0x2000>;
......
......@@ -8,7 +8,14 @@ OHCI and EHCI controllers.
Required properties:
- compatible: "renesas,pci-r8a7790" for the R8A7790 SoC;
"renesas,pci-r8a7791" for the R8A7791 SoC;
"renesas,pci-r8a7794" for the R8A7794 SoC.
"renesas,pci-r8a7794" for the R8A7794 SoC;
"renesas,pci-rcar-gen2" for a generic R-Car Gen2 compatible device
When compatible with the generic version, nodes must list the
SoC-specific version corresponding to the platform first
followed by the generic version.
- reg: A list of physical regions to access the device: the first is
the operational registers for the OHCI/EHCI controllers and the
second is for the bridge configuration and control registers.
......@@ -24,10 +31,15 @@ Required properties:
- interrupt-map-mask: standard property that helps to define the interrupt
mapping.
Optional properties:
- dma-ranges: a single range for the inbound memory region. If not supplied,
defaults to 1GiB at 0x40000000. Note there are hardware restrictions on the
allowed combinations of address and size.
Example SoC configuration:
pci0: pci@ee090000 {
compatible = "renesas,pci-r8a7790";
compatible = "renesas,pci-r8a7790", "renesas,pci-rcar-gen2";
clocks = <&mstp7_clks R8A7790_CLK_EHCI>;
reg = <0x0 0xee090000 0x0 0xc00>,
<0x0 0xee080000 0x0 0x1100>;
......@@ -38,6 +50,7 @@ Example SoC configuration:
#address-cells = <3>;
#size-cells = <2>;
#interrupt-cells = <1>;
dma-ranges = <0x42000000 0 0x40000000 0 0x40000000 0 0x40000000>;
interrupt-map-mask = <0xff00 0 0 0x7>;
interrupt-map = <0x0000 0 0 1 &gic 0 108 IRQ_TYPE_LEVEL_HIGH
0x0800 0 0 1 &gic 0 108 IRQ_TYPE_LEVEL_HIGH
......
* Qualcomm PCI express root complex
- compatible:
Usage: required
Value type: <stringlist>
Definition: Value should contain
- "qcom,pcie-ipq8064" for ipq8064
- "qcom,pcie-apq8064" for apq8064
- "qcom,pcie-apq8084" for apq8084
- reg:
Usage: required
Value type: <prop-encoded-array>
Definition: Register ranges as listed in the reg-names property
- reg-names:
Usage: required
Value type: <stringlist>
Definition: Must include the following entries
- "parf" Qualcomm specific registers
- "dbi" Designware PCIe registers
- "elbi" External local bus interface registers
- "config" PCIe configuration space
- device_type:
Usage: required
Value type: <string>
Definition: Should be "pci". As specified in designware-pcie.txt
- #address-cells:
Usage: required
Value type: <u32>
Definition: Should be 3. As specified in designware-pcie.txt
- #size-cells:
Usage: required
Value type: <u32>
Definition: Should be 2. As specified in designware-pcie.txt
- ranges:
Usage: required
Value type: <prop-encoded-array>
Definition: As specified in designware-pcie.txt
- interrupts:
Usage: required
Value type: <prop-encoded-array>
Definition: MSI interrupt
- interrupt-names:
Usage: required
Value type: <stringlist>
Definition: Should contain "msi"
- #interrupt-cells:
Usage: required
Value type: <u32>
Definition: Should be 1. As specified in designware-pcie.txt
- interrupt-map-mask:
Usage: required
Value type: <prop-encoded-array>
Definition: As specified in designware-pcie.txt
- interrupt-map:
Usage: required
Value type: <prop-encoded-array>
Definition: As specified in designware-pcie.txt
- clocks:
Usage: required
Value type: <prop-encoded-array>
Definition: List of phandle and clock specifier pairs as listed
in clock-names property
- clock-names:
Usage: required
Value type: <stringlist>
Definition: Should contain the following entries
- "iface" Configuration AHB clock
- clock-names:
Usage: required for ipq/apq8064
Value type: <stringlist>
Definition: Should contain the following entries
- "core" Clocks the pcie hw block
- "phy" Clocks the pcie PHY block
- clock-names:
Usage: required for apq8084
Value type: <stringlist>
Definition: Should contain the following entries
- "aux" Auxiliary (AUX) clock
- "bus_master" Master AXI clock
- "bus_slave" Slave AXI clock
- resets:
Usage: required
Value type: <prop-encoded-array>
Definition: List of phandle and reset specifier pairs as listed
in reset-names property
- reset-names:
Usage: required for ipq/apq8064
Value type: <stringlist>
Definition: Should contain the following entries
- "axi" AXI reset
- "ahb" AHB reset
- "por" POR reset
- "pci" PCI reset
- "phy" PHY reset
- reset-names:
Usage: required for apq8084
Value type: <stringlist>
Definition: Should contain the following entries
- "core" Core reset
- power-domains:
Usage: required for apq8084
Value type: <prop-encoded-array>
Definition: A phandle and power domain specifier pair to the
power domain which is responsible for collapsing
and restoring power to the peripheral
- vdda-supply:
Usage: required
Value type: <phandle>
Definition: A phandle to the core analog power supply
- vdda_phy-supply:
Usage: required for ipq/apq8064
Value type: <phandle>
Definition: A phandle to the analog power supply for PHY
- vdda_refclk-supply:
Usage: required for ipq/apq8064
Value type: <phandle>
Definition: A phandle to the analog power supply for IC which generates
reference clock
- phys:
Usage: required for apq8084
Value type: <phandle>
Definition: List of phandle(s) as listed in phy-names property
- phy-names:
Usage: required for apq8084
Value type: <stringlist>
Definition: Should contain "pciephy"
- <name>-gpios:
Usage: optional
Value type: <prop-encoded-array>
Definition: List of phandle and gpio specifier pairs. Should contain
- "perst-gpios" PCIe endpoint reset signal line
- "wake-gpios" PCIe endpoint wake signal line
* Example for ipq/apq8064
pcie@1b500000 {
compatible = "qcom,pcie-apq8064", "qcom,pcie-ipq8064", "snps,dw-pcie";
reg = <0x1b500000 0x1000
0x1b502000 0x80
0x1b600000 0x100
0x0ff00000 0x100000>;
reg-names = "dbi", "elbi", "parf", "config";
device_type = "pci";
linux,pci-domain = <0>;
bus-range = <0x00 0xff>;
num-lanes = <1>;
#address-cells = <3>;
#size-cells = <2>;
ranges = <0x81000000 0 0 0x0fe00000 0 0x00100000 /* I/O */
0x82000000 0 0 0x08000000 0 0x07e00000>; /* memory */
interrupts = <GIC_SPI 238 IRQ_TYPE_NONE>;
interrupt-names = "msi";
#interrupt-cells = <1>;
interrupt-map-mask = <0 0 0 0x7>;
interrupt-map = <0 0 0 1 &intc 0 36 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
<0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
<0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
<0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
clocks = <&gcc PCIE_A_CLK>,
<&gcc PCIE_H_CLK>,
<&gcc PCIE_PHY_CLK>;
clock-names = "core", "iface", "phy";
resets = <&gcc PCIE_ACLK_RESET>,
<&gcc PCIE_HCLK_RESET>,
<&gcc PCIE_POR_RESET>,
<&gcc PCIE_PCI_RESET>,
<&gcc PCIE_PHY_RESET>;
reset-names = "axi", "ahb", "por", "pci", "phy";
pinctrl-0 = <&pcie_pins_default>;
pinctrl-names = "default";
};
* Example for apq8084
pcie0@fc520000 {
compatible = "qcom,pcie-apq8084", "snps,dw-pcie";
reg = <0xfc520000 0x2000>,
<0xff000000 0x1000>,
<0xff001000 0x1000>,
<0xff002000 0x2000>;
reg-names = "parf", "dbi", "elbi", "config";
device_type = "pci";
linux,pci-domain = <0>;
bus-range = <0x00 0xff>;
num-lanes = <1>;
#address-cells = <3>;
#size-cells = <2>;
ranges = <0x81000000 0 0 0xff200000 0 0x00100000 /* I/O */
0x82000000 0 0x00300000 0xff300000 0 0x00d00000>; /* memory */
interrupts = <GIC_SPI 243 IRQ_TYPE_NONE>;
interrupt-names = "msi";
#interrupt-cells = <1>;
interrupt-map-mask = <0 0 0 0x7>;
interrupt-map = <0 0 0 1 &intc 0 244 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
<0 0 0 2 &intc 0 245 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
<0 0 0 3 &intc 0 247 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
<0 0 0 4 &intc 0 248 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
clocks = <&gcc GCC_PCIE_0_CFG_AHB_CLK>,
<&gcc GCC_PCIE_0_MSTR_AXI_CLK>,
<&gcc GCC_PCIE_0_SLV_AXI_CLK>,
<&gcc GCC_PCIE_0_AUX_CLK>;
clock-names = "iface", "master_bus", "slave_bus", "aux";
resets = <&gcc GCC_PCIE_0_BCR>;
reset-names = "core";
power-domains = <&gcc PCIE0_GDSC>;
vdda-supply = <&pma8084_l3>;
phys = <&pciephy0>;
phy-names = "pciephy";
perst-gpio = <&tlmm 70 GPIO_ACTIVE_LOW>;
pinctrl-0 = <&pcie0_pins_default>;
pinctrl-names = "default";
};
* Renesas RCar PCIe interface
Required properties:
- compatible: should contain one of the following
"renesas,pcie-r8a7779", "renesas,pcie-r8a7790", "renesas,pcie-r8a7791"
compatible: "renesas,pcie-r8a7779" for the R8A7779 SoC;
"renesas,pcie-r8a7790" for the R8A7790 SoC;
"renesas,pcie-r8a7791" for the R8A7791 SoC;
"renesas,pcie-r8a7795" for the R8A7795 SoC;
"renesas,pcie-rcar-gen2" for a generic R-Car Gen2 compatible device.
When compatible with the generic version, nodes must list the
SoC-specific version corresponding to the platform first
followed by the generic version.
- reg: base address and length of the pcie controller registers.
- #address-cells: set to <3>
- #size-cells: set to <2>
......@@ -25,7 +33,7 @@ Example:
SoC specific DT Entry:
pcie: pcie@fe000000 {
compatible = "renesas,pcie-r8a7791";
compatible = "renesas,pcie-r8a7791", "renesas,pcie-rcar-gen2";
reg = <0 0xfe000000 0 0x80000>;
#address-cells = <3>;
#size-cells = <2>;
......
......@@ -8317,6 +8317,12 @@ S: Maintained
F: Documentation/devicetree/bindings/pci/host-generic-pci.txt
F: drivers/pci/host/pci-host-generic.c
PCI DRIVER FOR INTEL VOLUME MANAGEMENT DEVICE (VMD)
M: Keith Busch <keith.busch@intel.com>
L: linux-pci@vger.kernel.org
S: Supported
F: arch/x86/pci/vmd.c
PCIE DRIVER FOR ST SPEAR13XX
M: Pratyush Anand <pratyush.anand@gmail.com>
L: linux-pci@vger.kernel.org
......@@ -8341,11 +8347,19 @@ F: drivers/pci/host/pci-xgene-msi.c
PCIE DRIVER FOR HISILICON
M: Zhou Wang <wangzhou1@hisilicon.com>
M: Gabriele Paoloni <gabriele.paoloni@huawei.com>
L: linux-pci@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/pci/hisilicon-pcie.txt
F: drivers/pci/host/pcie-hisi.c
PCIE DRIVER FOR QUALCOMM MSM
M: Stanimir Varbanov <svarbanov@mm-sol.com>
L: linux-pci@vger.kernel.org
L: linux-arm-msm@vger.kernel.org
S: Maintained
F: drivers/pci/host/*qcom*
PCMCIA SUBSYSTEM
P: Linux PCMCIA Team
L: linux-pcmcia@lists.infradead.org
......
......@@ -47,6 +47,18 @@
bias-disable;
};
};
pcie_pins: pcie_pinmux {
mux {
pins = "gpio27";
function = "gpio";
};
conf {
pins = "gpio27";
drive-strength = <12>;
bias-disable;
};
};
};
rpm@108000 {
......@@ -123,6 +135,10 @@
lvs1 {
bias-pull-down;
};
lvs6 {
bias-pull-down;
};
};
};
......@@ -231,6 +247,16 @@
status = "okay";
};
pci@1b500000 {
status = "ok";
vdda-supply = <&pm8921_s3>;
vdda_phy-supply = <&pm8921_lvs6>;
vdda_refclk-supply = <&ext_3p3v>;
pinctrl-0 = <&pcie_pins>;
pinctrl-names = "default";
perst-gpio = <&tlmm_pinmux 27 GPIO_ACTIVE_LOW>;
};
qcom,ssbi@500000 {
pmic@0 {
gpio@150 {
......
......@@ -785,5 +785,41 @@
compatible = "qcom,tcsr-apq8064", "syscon";
reg = <0x1a400000 0x100>;
};
pcie: pci@1b500000 {
compatible = "qcom,pcie-apq8064", "snps,dw-pcie";
reg = <0x1b500000 0x1000
0x1b502000 0x80
0x1b600000 0x100
0x0ff00000 0x100000>;
reg-names = "dbi", "elbi", "parf", "config";
device_type = "pci";
linux,pci-domain = <0>;
bus-range = <0x00 0xff>;
num-lanes = <1>;
#address-cells = <3>;
#size-cells = <2>;
ranges = <0x81000000 0 0 0x0fe00000 0 0x00100000 /* I/O */
0x82000000 0 0 0x08000000 0 0x07e00000>; /* memory */
interrupts = <GIC_SPI 238 IRQ_TYPE_NONE>;
interrupt-names = "msi";
#interrupt-cells = <1>;
interrupt-map-mask = <0 0 0 0x7>;
interrupt-map = <0 0 0 1 &intc 0 36 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
<0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
<0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
<0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
clocks = <&gcc PCIE_A_CLK>,
<&gcc PCIE_H_CLK>,
<&gcc PCIE_PHY_REF_CLK>;
clock-names = "core", "iface", "phy";
resets = <&gcc PCIE_ACLK_RESET>,
<&gcc PCIE_HCLK_RESET>,
<&gcc PCIE_POR_RESET>,
<&gcc PCIE_PCI_RESET>,
<&gcc PCIE_PHY_RESET>;
reset-names = "axi", "ahb", "por", "pci", "phy";
status = "disabled";
};
};
};
......@@ -400,7 +400,7 @@ static void *eeh_rmv_device(void *data, void *userdata)
* support EEH. So we just care about PCI devices for
* simplicity here.
*/
if (!dev || (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE))
if (!dev || (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE))
return NULL;
/*
......
......@@ -187,9 +187,6 @@ struct pci_dev *of_create_pci_dev(struct device_node *node,
pci_device_add(dev, bus);
/* Setup MSI caps & disable MSI/MSI-X interrupts */
pci_msi_setup_pci_dev(dev);
return dev;
}
EXPORT_SYMBOL(of_create_pci_dev);
......
......@@ -2699,6 +2699,19 @@ config PMC_ATOM
def_bool y
depends on PCI
config VMD
depends on PCI_MSI
tristate "Volume Management Device Driver"
default N
---help---
Adds support for the Intel Volume Management Device (VMD). VMD is a
secondary PCI host bridge that allows PCI Express root ports,
and devices attached to them, to be removed from the default
PCI domain and placed within the VMD domain. This provides
more bus resources than are otherwise possible with a
single domain. If you know your system provides one of these and
has devices attached to it, say Y; if you are not sure, say N.
source "net/Kconfig"
source "drivers/Kconfig"
......
......@@ -10,6 +10,16 @@ struct dev_archdata {
#endif
};
#if defined(CONFIG_X86_DEV_DMA_OPS) && defined(CONFIG_PCI_DOMAINS)
struct dma_domain {
struct list_head node;
struct dma_map_ops *dma_ops;
int domain_nr;
};
void add_dma_domain(struct dma_domain *domain);
void del_dma_domain(struct dma_domain *domain);
#endif
struct pdev_archdata {
};
......
......@@ -129,6 +129,11 @@ struct irq_alloc_info {
unsigned long uv_offset;
char *uv_name;
};
#endif
#if IS_ENABLED(CONFIG_VMD)
struct {
struct msi_desc *desc;
};
#endif
};
};
......
......@@ -151,11 +151,11 @@ extern struct list_head pci_mmcfg_list;
#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20)
/*
* AMD Fam10h CPUs are buggy, and cannot access MMIO config space
* on their northbrige except through the * %eax register. As such, you MUST
* NOT use normal IOMEM accesses, you need to only use the magic mmio-config
* accessor functions.
* In fact just use pci_config_*, nothing else please.
* On AMD Fam10h CPUs, all PCI MMIO configuration space accesses must use
* %eax. No other source or target registers may be used. The following
* mmio_config_* accessors enforce this. See "BIOS and Kernel Developer's
* Guide (BKDG) For AMD Family 10h Processors", rev. 3.48, sec 2.11.1,
* "MMIO Configuration Coding Requirements".
*/
static inline unsigned char mmio_config_readb(void __iomem *pos)
{
......
......@@ -23,6 +23,8 @@ obj-y += bus_numa.o
obj-$(CONFIG_AMD_NB) += amd_bus.o
obj-$(CONFIG_PCI_CNB20LE_QUIRK) += broadcom_bus.o
obj-$(CONFIG_VMD) += vmd.o
ifeq ($(CONFIG_PCI_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
endif
......@@ -641,6 +641,43 @@ unsigned int pcibios_assign_all_busses(void)
return (pci_probe & PCI_ASSIGN_ALL_BUSSES) ? 1 : 0;
}
#if defined(CONFIG_X86_DEV_DMA_OPS) && defined(CONFIG_PCI_DOMAINS)
static LIST_HEAD(dma_domain_list);
static DEFINE_SPINLOCK(dma_domain_list_lock);
void add_dma_domain(struct dma_domain *domain)
{
spin_lock(&dma_domain_list_lock);
list_add(&domain->node, &dma_domain_list);
spin_unlock(&dma_domain_list_lock);
}
EXPORT_SYMBOL_GPL(add_dma_domain);
void del_dma_domain(struct dma_domain *domain)
{
spin_lock(&dma_domain_list_lock);
list_del(&domain->node);
spin_unlock(&dma_domain_list_lock);
}
EXPORT_SYMBOL_GPL(del_dma_domain);
static void set_dma_domain_ops(struct pci_dev *pdev)
{
struct dma_domain *domain;
spin_lock(&dma_domain_list_lock);
list_for_each_entry(domain, &dma_domain_list, node) {
if (pci_domain_nr(pdev->bus) == domain->domain_nr) {
pdev->dev.archdata.dma_ops = domain->dma_ops;
break;
}
}
spin_unlock(&dma_domain_list_lock);
}
#else
static void set_dma_domain_ops(struct pci_dev *pdev) {}
#endif
int pcibios_add_device(struct pci_dev *dev)
{
struct setup_data *data;
......@@ -670,6 +707,7 @@ int pcibios_add_device(struct pci_dev *dev)
pa_data = data->next;
iounmap(data);
}
set_dma_domain_ops(dev);
return 0;
}
......
......@@ -180,6 +180,7 @@ static int pci_bios_read(unsigned int seg, unsigned int bus,
unsigned long result = 0;
unsigned long flags;
unsigned long bx = (bus << 8) | devfn;
u16 number = 0, mask = 0;
WARN_ON(seg);
if (!value || (bus > 255) || (devfn > 255) || (reg > 255))
......@@ -189,53 +190,35 @@ static int pci_bios_read(unsigned int seg, unsigned int bus,
switch (len) {
case 1:
__asm__("lcall *(%%esi); cld\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
"1:"
: "=c" (*value),
"=a" (result)
: "1" (PCIBIOS_READ_CONFIG_BYTE),
"b" (bx),
"D" ((long)reg),
"S" (&pci_indirect));
/*
* Zero-extend the result beyond 8 bits, do not trust the
* BIOS having done it:
*/
*value &= 0xff;
number = PCIBIOS_READ_CONFIG_BYTE;
mask = 0xff;
break;
case 2:
__asm__("lcall *(%%esi); cld\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
"1:"
: "=c" (*value),
"=a" (result)
: "1" (PCIBIOS_READ_CONFIG_WORD),
"b" (bx),
"D" ((long)reg),
"S" (&pci_indirect));
/*
* Zero-extend the result beyond 16 bits, do not trust the
* BIOS having done it:
*/
*value &= 0xffff;
number = PCIBIOS_READ_CONFIG_WORD;
mask = 0xffff;
break;
case 4:
__asm__("lcall *(%%esi); cld\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
"1:"
: "=c" (*value),
"=a" (result)
: "1" (PCIBIOS_READ_CONFIG_DWORD),
"b" (bx),
"D" ((long)reg),
"S" (&pci_indirect));
number = PCIBIOS_READ_CONFIG_DWORD;
break;
}
__asm__("lcall *(%%esi); cld\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
"1:"
: "=c" (*value),
"=a" (result)
: "1" (number),
"b" (bx),
"D" ((long)reg),
"S" (&pci_indirect));
/*
* Zero-extend the result beyond 8 or 16 bits, do not trust the
* BIOS having done it:
*/
if (mask)
*value &= mask;
raw_spin_unlock_irqrestore(&pci_config_lock, flags);
return (int)((result & 0xff00) >> 8);
......@@ -247,6 +230,7 @@ static int pci_bios_write(unsigned int seg, unsigned int bus,
unsigned long result = 0;
unsigned long flags;
unsigned long bx = (bus << 8) | devfn;
u16 number = 0;
WARN_ON(seg);
if ((bus > 255) || (devfn > 255) || (reg > 255))
......@@ -256,43 +240,27 @@ static int pci_bios_write(unsigned int seg, unsigned int bus,
switch (len) {
case 1:
__asm__("lcall *(%%esi); cld\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
"1:"
: "=a" (result)
: "0" (PCIBIOS_WRITE_CONFIG_BYTE),
"c" (value),
"b" (bx),
"D" ((long)reg),
"S" (&pci_indirect));
number = PCIBIOS_WRITE_CONFIG_BYTE;
break;
case 2:
__asm__("lcall *(%%esi); cld\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
"1:"
: "=a" (result)
: "0" (PCIBIOS_WRITE_CONFIG_WORD),
"c" (value),
"b" (bx),
"D" ((long)reg),
"S" (&pci_indirect));
number = PCIBIOS_WRITE_CONFIG_WORD;
break;
case 4:
__asm__("lcall *(%%esi); cld\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
"1:"
: "=a" (result)
: "0" (PCIBIOS_WRITE_CONFIG_DWORD),
"c" (value),
"b" (bx),
"D" ((long)reg),
"S" (&pci_indirect));
number = PCIBIOS_WRITE_CONFIG_DWORD;
break;
}
__asm__("lcall *(%%esi); cld\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
"1:"
: "=a" (result)
: "0" (number),
"c" (value),
"b" (bx),
"D" ((long)reg),
"S" (&pci_indirect));
raw_spin_unlock_irqrestore(&pci_config_lock, flags);
return (int)((result & 0xff00) >> 8);
......
/*
* Volume Management Device driver
* Copyright (c) 2015, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/msi.h>
#include <linux/pci.h>
#include <linux/rculist.h>
#include <linux/rcupdate.h>
#include <asm/irqdomain.h>
#include <asm/device.h>
#include <asm/msi.h>
#include <asm/msidef.h>
#define VMD_CFGBAR 0
#define VMD_MEMBAR1 2
#define VMD_MEMBAR2 4
/*
* Lock for manipulating VMD IRQ lists.
*/
static DEFINE_RAW_SPINLOCK(list_lock);
/**
* struct vmd_irq - private data to map driver IRQ to the VMD shared vector
* @node: list item for parent traversal.
* @rcu: RCU callback item for freeing.
* @irq: back pointer to parent.
* @virq: the virtual IRQ value provided to the requesting driver.
*
* Every MSI/MSI-X IRQ requested for a device in a VMD domain will be mapped to
* a VMD IRQ using this structure.
*/
struct vmd_irq {
struct list_head node;
struct rcu_head rcu;
struct vmd_irq_list *irq;
unsigned int virq;
};
/**
* struct vmd_irq_list - list of driver requested IRQs mapping to a VMD vector
* @irq_list: the list of irq's the VMD one demuxes to.
* @vmd_vector: the h/w IRQ assigned to the VMD.
* @index: index into the VMD MSI-X table; used for message routing.
* @count: number of child IRQs assigned to this vector; used to track
* sharing.
*/
struct vmd_irq_list {
struct list_head irq_list;
struct vmd_dev *vmd;
unsigned int vmd_vector;
unsigned int index;
unsigned int count;
};
struct vmd_dev {
struct pci_dev *dev;
spinlock_t cfg_lock;
char __iomem *cfgbar;
int msix_count;
struct msix_entry *msix_entries;
struct vmd_irq_list *irqs;
struct pci_sysdata sysdata;
struct resource resources[3];
struct irq_domain *irq_domain;
struct pci_bus *bus;
#ifdef CONFIG_X86_DEV_DMA_OPS
struct dma_map_ops dma_ops;
struct dma_domain dma_domain;
#endif
};
static inline struct vmd_dev *vmd_from_bus(struct pci_bus *bus)
{
return container_of(bus->sysdata, struct vmd_dev, sysdata);
}
/*
* Drivers managing a device in a VMD domain allocate their own IRQs as before,
* but the MSI entry for the hardware it's driving will be programmed with a
* destination ID for the VMD MSI-X table. The VMD muxes interrupts in its
* domain into one of its own, and the VMD driver de-muxes these for the
* handlers sharing that VMD IRQ. The vmd irq_domain provides the operations
* and irq_chip to set this up.
*/
static void vmd_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
{
struct vmd_irq *vmdirq = data->chip_data;
struct vmd_irq_list *irq = vmdirq->irq;
msg->address_hi = MSI_ADDR_BASE_HI;
msg->address_lo = MSI_ADDR_BASE_LO | MSI_ADDR_DEST_ID(irq->index);
msg->data = 0;
}
/*
* We rely on MSI_FLAG_USE_DEF_CHIP_OPS to set the IRQ mask/unmask ops.
*/
static void vmd_irq_enable(struct irq_data *data)
{
struct vmd_irq *vmdirq = data->chip_data;
raw_spin_lock(&list_lock);
list_add_tail_rcu(&vmdirq->node, &vmdirq->irq->irq_list);
raw_spin_unlock(&list_lock);
data->chip->irq_unmask(data);
}
static void vmd_irq_disable(struct irq_data *data)
{
struct vmd_irq *vmdirq = data->chip_data;
data->chip->irq_mask(data);
raw_spin_lock(&list_lock);
list_del_rcu(&vmdirq->node);
raw_spin_unlock(&list_lock);
}
/*
* XXX: Stubbed until we develop acceptable way to not create conflicts with
* other devices sharing the same vector.
*/
static int vmd_irq_set_affinity(struct irq_data *data,
const struct cpumask *dest, bool force)
{
return -EINVAL;
}
static struct irq_chip vmd_msi_controller = {
.name = "VMD-MSI",
.irq_enable = vmd_irq_enable,
.irq_disable = vmd_irq_disable,
.irq_compose_msi_msg = vmd_compose_msi_msg,
.irq_set_affinity = vmd_irq_set_affinity,
};
static irq_hw_number_t vmd_get_hwirq(struct msi_domain_info *info,
msi_alloc_info_t *arg)
{
return 0;
}
/*
* XXX: We can be even smarter selecting the best IRQ once we solve the
* affinity problem.
*/
static struct vmd_irq_list *vmd_next_irq(struct vmd_dev *vmd)
{
int i, best = 0;
raw_spin_lock(&list_lock);
for (i = 1; i < vmd->msix_count; i++)
if (vmd->irqs[i].count < vmd->irqs[best].count)
best = i;
vmd->irqs[best].count++;
raw_spin_unlock(&list_lock);
return &vmd->irqs[best];
}
static int vmd_msi_init(struct irq_domain *domain, struct msi_domain_info *info,
unsigned int virq, irq_hw_number_t hwirq,
msi_alloc_info_t *arg)
{
struct vmd_dev *vmd = vmd_from_bus(msi_desc_to_pci_dev(arg->desc)->bus);
struct vmd_irq *vmdirq = kzalloc(sizeof(*vmdirq), GFP_KERNEL);
if (!vmdirq)
return -ENOMEM;
INIT_LIST_HEAD(&vmdirq->node);
vmdirq->irq = vmd_next_irq(vmd);
vmdirq->virq = virq;
irq_domain_set_info(domain, virq, vmdirq->irq->vmd_vector, info->chip,
vmdirq, handle_simple_irq, vmd, NULL);
return 0;
}
static void vmd_msi_free(struct irq_domain *domain,
struct msi_domain_info *info, unsigned int virq)
{
struct vmd_irq *vmdirq = irq_get_chip_data(virq);
/* XXX: Potential optimization to rebalance */
raw_spin_lock(&list_lock);
vmdirq->irq->count--;
raw_spin_unlock(&list_lock);
kfree_rcu(vmdirq, rcu);
}
static int vmd_msi_prepare(struct irq_domain *domain, struct device *dev,
int nvec, msi_alloc_info_t *arg)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct vmd_dev *vmd = vmd_from_bus(pdev->bus);
if (nvec > vmd->msix_count)
return vmd->msix_count;
memset(arg, 0, sizeof(*arg));
return 0;
}
static void vmd_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc)
{
arg->desc = desc;
}
static struct msi_domain_ops vmd_msi_domain_ops = {
.get_hwirq = vmd_get_hwirq,
.msi_init = vmd_msi_init,
.msi_free = vmd_msi_free,
.msi_prepare = vmd_msi_prepare,
.set_desc = vmd_set_desc,
};
static struct msi_domain_info vmd_msi_domain_info = {
.flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
MSI_FLAG_PCI_MSIX,
.ops = &vmd_msi_domain_ops,
.chip = &vmd_msi_controller,
};
#ifdef CONFIG_X86_DEV_DMA_OPS
/*
* VMD replaces the requester ID with its own. DMA mappings for devices in a
* VMD domain need to be mapped for the VMD, not the device requiring
* the mapping.
*/
static struct device *to_vmd_dev(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct vmd_dev *vmd = vmd_from_bus(pdev->bus);
return &vmd->dev->dev;
}
static struct dma_map_ops *vmd_dma_ops(struct device *dev)
{
return to_vmd_dev(dev)->archdata.dma_ops;
}
static void *vmd_alloc(struct device *dev, size_t size, dma_addr_t *addr,
gfp_t flag, struct dma_attrs *attrs)
{
return vmd_dma_ops(dev)->alloc(to_vmd_dev(dev), size, addr, flag,
attrs);
}
static void vmd_free(struct device *dev, size_t size, void *vaddr,
dma_addr_t addr, struct dma_attrs *attrs)
{
return vmd_dma_ops(dev)->free(to_vmd_dev(dev), size, vaddr, addr,
attrs);
}
static int vmd_mmap(struct device *dev, struct vm_area_struct *vma,
void *cpu_addr, dma_addr_t addr, size_t size,
struct dma_attrs *attrs)
{
return vmd_dma_ops(dev)->mmap(to_vmd_dev(dev), vma, cpu_addr, addr,
size, attrs);
}
static int vmd_get_sgtable(struct device *dev, struct sg_table *sgt,
void *cpu_addr, dma_addr_t addr, size_t size,
struct dma_attrs *attrs)
{
return vmd_dma_ops(dev)->get_sgtable(to_vmd_dev(dev), sgt, cpu_addr,
addr, size, attrs);
}
static dma_addr_t vmd_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size,
enum dma_data_direction dir,
struct dma_attrs *attrs)
{
return vmd_dma_ops(dev)->map_page(to_vmd_dev(dev), page, offset, size,
dir, attrs);
}
static void vmd_unmap_page(struct device *dev, dma_addr_t addr, size_t size,
enum dma_data_direction dir, struct dma_attrs *attrs)
{
vmd_dma_ops(dev)->unmap_page(to_vmd_dev(dev), addr, size, dir, attrs);
}
static int vmd_map_sg(struct device *dev, struct scatterlist *sg, int nents,
enum dma_data_direction dir, struct dma_attrs *attrs)
{
return vmd_dma_ops(dev)->map_sg(to_vmd_dev(dev), sg, nents, dir, attrs);
}
static void vmd_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
enum dma_data_direction dir, struct dma_attrs *attrs)
{
vmd_dma_ops(dev)->unmap_sg(to_vmd_dev(dev), sg, nents, dir, attrs);
}
static void vmd_sync_single_for_cpu(struct device *dev, dma_addr_t addr,
size_t size, enum dma_data_direction dir)
{
vmd_dma_ops(dev)->sync_single_for_cpu(to_vmd_dev(dev), addr, size, dir);
}
static void vmd_sync_single_for_device(struct device *dev, dma_addr_t addr,
size_t size, enum dma_data_direction dir)
{
vmd_dma_ops(dev)->sync_single_for_device(to_vmd_dev(dev), addr, size,
dir);
}
static void vmd_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg,
int nents, enum dma_data_direction dir)
{
vmd_dma_ops(dev)->sync_sg_for_cpu(to_vmd_dev(dev), sg, nents, dir);
}
static void vmd_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
int nents, enum dma_data_direction dir)
{
vmd_dma_ops(dev)->sync_sg_for_device(to_vmd_dev(dev), sg, nents, dir);
}
static int vmd_mapping_error(struct device *dev, dma_addr_t addr)
{
return vmd_dma_ops(dev)->mapping_error(to_vmd_dev(dev), addr);
}
static int vmd_dma_supported(struct device *dev, u64 mask)
{
return vmd_dma_ops(dev)->dma_supported(to_vmd_dev(dev), mask);
}
#ifdef ARCH_HAS_DMA_GET_REQUIRED_MASK
static u64 vmd_get_required_mask(struct device *dev)
{
return vmd_dma_ops(dev)->get_required_mask(to_vmd_dev(dev));
}
#endif
static void vmd_teardown_dma_ops(struct vmd_dev *vmd)
{
struct dma_domain *domain = &vmd->dma_domain;
if (vmd->dev->dev.archdata.dma_ops)
del_dma_domain(domain);
}
#define ASSIGN_VMD_DMA_OPS(source, dest, fn) \
do { \
if (source->fn) \
dest->fn = vmd_##fn; \
} while (0)
static void vmd_setup_dma_ops(struct vmd_dev *vmd)
{
const struct dma_map_ops *source = vmd->dev->dev.archdata.dma_ops;
struct dma_map_ops *dest = &vmd->dma_ops;
struct dma_domain *domain = &vmd->dma_domain;
domain->domain_nr = vmd->sysdata.domain;
domain->dma_ops = dest;
if (!source)
return;
ASSIGN_VMD_DMA_OPS(source, dest, alloc);
ASSIGN_VMD_DMA_OPS(source, dest, free);
ASSIGN_VMD_DMA_OPS(source, dest, mmap);
ASSIGN_VMD_DMA_OPS(source, dest, get_sgtable);
ASSIGN_VMD_DMA_OPS(source, dest, map_page);
ASSIGN_VMD_DMA_OPS(source, dest, unmap_page);
ASSIGN_VMD_DMA_OPS(source, dest, map_sg);
ASSIGN_VMD_DMA_OPS(source, dest, unmap_sg);
ASSIGN_VMD_DMA_OPS(source, dest, sync_single_for_cpu);
ASSIGN_VMD_DMA_OPS(source, dest, sync_single_for_device);
ASSIGN_VMD_DMA_OPS(source, dest, sync_sg_for_cpu);
ASSIGN_VMD_DMA_OPS(source, dest, sync_sg_for_device);
ASSIGN_VMD_DMA_OPS(source, dest, mapping_error);
ASSIGN_VMD_DMA_OPS(source, dest, dma_supported);
#ifdef ARCH_HAS_DMA_GET_REQUIRED_MASK
ASSIGN_VMD_DMA_OPS(source, dest, get_required_mask);
#endif
add_dma_domain(domain);
}
#undef ASSIGN_VMD_DMA_OPS
#else
static void vmd_teardown_dma_ops(struct vmd_dev *vmd) {}
static void vmd_setup_dma_ops(struct vmd_dev *vmd) {}
#endif
static char __iomem *vmd_cfg_addr(struct vmd_dev *vmd, struct pci_bus *bus,
unsigned int devfn, int reg, int len)
{
char __iomem *addr = vmd->cfgbar +
(bus->number << 20) + (devfn << 12) + reg;
if ((addr - vmd->cfgbar) + len >=
resource_size(&vmd->dev->resource[VMD_CFGBAR]))
return NULL;
return addr;
}
/*
* CPU may deadlock if config space is not serialized on some versions of this
* hardware, so all config space access is done under a spinlock.
*/
static int vmd_pci_read(struct pci_bus *bus, unsigned int devfn, int reg,
int len, u32 *value)
{
struct vmd_dev *vmd = vmd_from_bus(bus);
char __iomem *addr = vmd_cfg_addr(vmd, bus, devfn, reg, len);
unsigned long flags;
int ret = 0;
if (!addr)
return -EFAULT;
spin_lock_irqsave(&vmd->cfg_lock, flags);
switch (len) {
case 1:
*value = readb(addr);
break;
case 2:
*value = readw(addr);
break;
case 4:
*value = readl(addr);
break;
default:
ret = -EINVAL;
break;
}
spin_unlock_irqrestore(&vmd->cfg_lock, flags);
return ret;
}
/*
* VMD h/w converts non-posted config writes to posted memory writes. The
* read-back in this function forces the completion so it returns only after
* the config space was written, as expected.
*/
static int vmd_pci_write(struct pci_bus *bus, unsigned int devfn, int reg,
int len, u32 value)
{
struct vmd_dev *vmd = vmd_from_bus(bus);
char __iomem *addr = vmd_cfg_addr(vmd, bus, devfn, reg, len);
unsigned long flags;
int ret = 0;
if (!addr)
return -EFAULT;
spin_lock_irqsave(&vmd->cfg_lock, flags);
switch (len) {
case 1:
writeb(value, addr);
readb(addr);
break;
case 2:
writew(value, addr);
readw(addr);
break;
case 4:
writel(value, addr);
readl(addr);
break;
default:
ret = -EINVAL;
break;
}
spin_unlock_irqrestore(&vmd->cfg_lock, flags);
return ret;
}
static struct pci_ops vmd_ops = {
.read = vmd_pci_read,
.write = vmd_pci_write,
};
/*
* VMD domains start at 0x1000 to not clash with ACPI _SEG domains.
*/
static int vmd_find_free_domain(void)
{
int domain = 0xffff;
struct pci_bus *bus = NULL;
while ((bus = pci_find_next_bus(bus)) != NULL)
domain = max_t(int, domain, pci_domain_nr(bus));
return domain + 1;
}
static int vmd_enable_domain(struct vmd_dev *vmd)
{
struct pci_sysdata *sd = &vmd->sysdata;
struct resource *res;
u32 upper_bits;
unsigned long flags;
LIST_HEAD(resources);
res = &vmd->dev->resource[VMD_CFGBAR];
vmd->resources[0] = (struct resource) {
.name = "VMD CFGBAR",
.start = res->start,
.end = (resource_size(res) >> 20) - 1,
.flags = IORESOURCE_BUS | IORESOURCE_PCI_FIXED,
};
res = &vmd->dev->resource[VMD_MEMBAR1];
upper_bits = upper_32_bits(res->end);
flags = res->flags & ~IORESOURCE_SIZEALIGN;
if (!upper_bits)
flags &= ~IORESOURCE_MEM_64;
vmd->resources[1] = (struct resource) {
.name = "VMD MEMBAR1",
.start = res->start,
.end = res->end,
.flags = flags,
};
res = &vmd->dev->resource[VMD_MEMBAR2];
upper_bits = upper_32_bits(res->end);
flags = res->flags & ~IORESOURCE_SIZEALIGN;
if (!upper_bits)
flags &= ~IORESOURCE_MEM_64;
vmd->resources[2] = (struct resource) {
.name = "VMD MEMBAR2",
.start = res->start + 0x2000,
.end = res->end,
.flags = flags,
};
sd->domain = vmd_find_free_domain();
if (sd->domain < 0)
return sd->domain;
sd->node = pcibus_to_node(vmd->dev->bus);
vmd->irq_domain = pci_msi_create_irq_domain(NULL, &vmd_msi_domain_info,
NULL);
if (!vmd->irq_domain)
return -ENODEV;
pci_add_resource(&resources, &vmd->resources[0]);
pci_add_resource(&resources, &vmd->resources[1]);
pci_add_resource(&resources, &vmd->resources[2]);
vmd->bus = pci_create_root_bus(&vmd->dev->dev, 0, &vmd_ops, sd,
&resources);
if (!vmd->bus) {
pci_free_resource_list(&resources);
irq_domain_remove(vmd->irq_domain);
return -ENODEV;
}
vmd_setup_dma_ops(vmd);
dev_set_msi_domain(&vmd->bus->dev, vmd->irq_domain);
pci_rescan_bus(vmd->bus);
WARN(sysfs_create_link(&vmd->dev->dev.kobj, &vmd->bus->dev.kobj,
"domain"), "Can't create symlink to domain\n");
return 0;
}
static irqreturn_t vmd_irq(int irq, void *data)
{
struct vmd_irq_list *irqs = data;
struct vmd_irq *vmdirq;
rcu_read_lock();
list_for_each_entry_rcu(vmdirq, &irqs->irq_list, node)
generic_handle_irq(vmdirq->virq);
rcu_read_unlock();
return IRQ_HANDLED;
}
static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
struct vmd_dev *vmd;
int i, err;
if (resource_size(&dev->resource[VMD_CFGBAR]) < (1 << 20))
return -ENOMEM;
vmd = devm_kzalloc(&dev->dev, sizeof(*vmd), GFP_KERNEL);
if (!vmd)
return -ENOMEM;
vmd->dev = dev;
err = pcim_enable_device(dev);
if (err < 0)
return err;
vmd->cfgbar = pcim_iomap(dev, VMD_CFGBAR, 0);
if (!vmd->cfgbar)
return -ENOMEM;
pci_set_master(dev);
if (dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(64)) &&
dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32)))
return -ENODEV;
vmd->msix_count = pci_msix_vec_count(dev);
if (vmd->msix_count < 0)
return -ENODEV;
vmd->irqs = devm_kcalloc(&dev->dev, vmd->msix_count, sizeof(*vmd->irqs),
GFP_KERNEL);
if (!vmd->irqs)
return -ENOMEM;
vmd->msix_entries = devm_kcalloc(&dev->dev, vmd->msix_count,
sizeof(*vmd->msix_entries),
GFP_KERNEL);
if (!vmd->msix_entries)
return -ENOMEM;
for (i = 0; i < vmd->msix_count; i++)
vmd->msix_entries[i].entry = i;
vmd->msix_count = pci_enable_msix_range(vmd->dev, vmd->msix_entries, 1,
vmd->msix_count);
if (vmd->msix_count < 0)
return vmd->msix_count;
for (i = 0; i < vmd->msix_count; i++) {
INIT_LIST_HEAD(&vmd->irqs[i].irq_list);
vmd->irqs[i].vmd_vector = vmd->msix_entries[i].vector;
vmd->irqs[i].index = i;
err = devm_request_irq(&dev->dev, vmd->irqs[i].vmd_vector,
vmd_irq, 0, "vmd", &vmd->irqs[i]);
if (err)
return err;
}
spin_lock_init(&vmd->cfg_lock);
pci_set_drvdata(dev, vmd);
err = vmd_enable_domain(vmd);
if (err)
return err;
dev_info(&vmd->dev->dev, "Bound to PCI domain %04x\n",
vmd->sysdata.domain);
return 0;
}
static void vmd_remove(struct pci_dev *dev)
{
struct vmd_dev *vmd = pci_get_drvdata(dev);
pci_set_drvdata(dev, NULL);
sysfs_remove_link(&vmd->dev->dev.kobj, "domain");
pci_stop_root_bus(vmd->bus);
pci_remove_root_bus(vmd->bus);
vmd_teardown_dma_ops(vmd);
irq_domain_remove(vmd->irq_domain);
}
#ifdef CONFIG_PM
static int vmd_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
pci_save_state(pdev);
return 0;
}
static int vmd_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
pci_restore_state(pdev);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(vmd_dev_pm_ops, vmd_suspend, vmd_resume);
static const struct pci_device_id vmd_ids[] = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x201d),},
{0,}
};
MODULE_DEVICE_TABLE(pci, vmd_ids);
static struct pci_driver vmd_drv = {
.name = "vmd",
.id_table = vmd_ids,
.probe = vmd_probe,
.remove = vmd_remove,
.driver = {
.pm = &vmd_dev_pm_ops,
},
};
module_pci_driver(vmd_drv);
MODULE_AUTHOR("Intel Corporation");
MODULE_LICENSE("GPL v2");
MODULE_VERSION("0.6");
......@@ -25,7 +25,7 @@ DEFINE_RAW_SPINLOCK(pci_lock);
#define PCI_word_BAD (pos & 1)
#define PCI_dword_BAD (pos & 3)
#define PCI_OP_READ(size,type,len) \
#define PCI_OP_READ(size, type, len) \
int pci_bus_read_config_##size \
(struct pci_bus *bus, unsigned int devfn, int pos, type *value) \
{ \
......@@ -40,7 +40,7 @@ int pci_bus_read_config_##size \
return res; \
}
#define PCI_OP_WRITE(size,type,len) \
#define PCI_OP_WRITE(size, type, len) \
int pci_bus_write_config_##size \
(struct pci_bus *bus, unsigned int devfn, int pos, type value) \
{ \
......@@ -231,7 +231,7 @@ static noinline void pci_wait_cfg(struct pci_dev *dev)
}
/* Returns 0 on success, negative values indicate error. */
#define PCI_USER_READ_CONFIG(size,type) \
#define PCI_USER_READ_CONFIG(size, type) \
int pci_user_read_config_##size \
(struct pci_dev *dev, int pos, type *val) \
{ \
......@@ -251,7 +251,7 @@ int pci_user_read_config_##size \
EXPORT_SYMBOL_GPL(pci_user_read_config_##size);
/* Returns 0 on success, negative values indicate error. */
#define PCI_USER_WRITE_CONFIG(size,type) \
#define PCI_USER_WRITE_CONFIG(size, type) \
int pci_user_write_config_##size \
(struct pci_dev *dev, int pos, type val) \
{ \
......
......@@ -140,6 +140,8 @@ static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res,
type_mask |= IORESOURCE_TYPE_BITS;
pci_bus_for_each_resource(bus, r, i) {
resource_size_t min_used = min;
if (!r)
continue;
......@@ -163,12 +165,12 @@ static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res,
* overrides "min".
*/
if (avail.start)
min = avail.start;
min_used = avail.start;
max = avail.end;
/* Ok, try it out.. */
ret = allocate_resource(r, res, size, min, max,
ret = allocate_resource(r, res, size, min_used, max,
align, alignf, alignf_data);
if (ret == 0)
return 0;
......
......@@ -49,8 +49,7 @@ config PCI_RCAR_GEN2
config PCI_RCAR_GEN2_PCIE
bool "Renesas R-Car PCIe controller"
depends on ARM
depends on ARCH_SHMOBILE || COMPILE_TEST
depends on ARCH_SHMOBILE || (ARM && COMPILE_TEST)
help
Say Y here if you want PCIe controller support on R-Car Gen2 SoCs.
......@@ -119,13 +118,11 @@ config PCI_VERSATILE
depends on ARCH_VERSATILE
config PCIE_IPROC
tristate "Broadcom iProc PCIe controller"
depends on OF && (ARM || ARM64)
default n
tristate
help
This enables the iProc PCIe core controller support for Broadcom's
iProc family of SoCs. An appropriate bus interface driver also needs
to be enabled
iProc family of SoCs. An appropriate bus interface driver needs
to be enabled to select this.
config PCIE_IPROC_PLATFORM
tristate "Broadcom iProc PCIe platform bus driver"
......@@ -148,6 +145,16 @@ config PCIE_IPROC_BCMA
Say Y here if you want to use the Broadcom iProc PCIe controller
through the BCMA bus interface
config PCIE_IPROC_MSI
bool "Broadcom iProc PCIe MSI support"
depends on PCIE_IPROC_PLATFORM || PCIE_IPROC_BCMA
depends on PCI_MSI
select PCI_MSI_IRQ_DOMAIN
default ARCH_BCM_IPROC
help
Say Y here if you want to enable MSI support for Broadcom's iProc
PCIe controller
config PCIE_ALTERA
bool "Altera PCIe controller"
depends on ARM || NIOS2
......@@ -167,10 +174,21 @@ config PCIE_ALTERA_MSI
config PCI_HISI
depends on OF && ARM64
bool "HiSilicon SoC HIP05 PCIe controller"
bool "HiSilicon Hip05 and Hip06 SoCs PCIe controllers"
select PCIEPORTBUS
select PCIE_DW
help
Say Y here if you want PCIe controller support on HiSilicon HIP05 SoC
Say Y here if you want PCIe controller support on HiSilicon
Hip05 and Hip06 SoCs
config PCIE_QCOM
bool "Qualcomm PCIe controller"
depends on ARCH_QCOM && OF
select PCIE_DW
select PCIEPORTBUS
help
Say Y here to enable PCIe controller support on Qualcomm SoCs. The
PCIe controller uses the Designware core plus Qualcomm-specific
hardware wrappers.
endmenu
......@@ -15,8 +15,10 @@ obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
obj-$(CONFIG_PCIE_IPROC_MSI) += pcie-iproc-msi.o
obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o
obj-$(CONFIG_PCIE_IPROC_BCMA) += pcie-iproc-bcma.o
obj-$(CONFIG_PCIE_ALTERA) += pcie-altera.o
obj-$(CONFIG_PCIE_ALTERA_MSI) += pcie-altera-msi.o
obj-$(CONFIG_PCI_HISI) += pcie-hisi.o
obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o
......@@ -302,7 +302,8 @@ static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx,
}
ret = devm_request_irq(&pdev->dev, pp->irq,
dra7xx_pcie_msi_irq_handler, IRQF_SHARED,
dra7xx_pcie_msi_irq_handler,
IRQF_SHARED | IRQF_NO_THREAD,
"dra7-pcie-msi", pp);
if (ret) {
dev_err(&pdev->dev, "failed to request irq\n");
......
......@@ -522,7 +522,8 @@ static int __init exynos_add_pcie_port(struct pcie_port *pp,
ret = devm_request_irq(&pdev->dev, pp->msi_irq,
exynos_pcie_msi_irq_handler,
IRQF_SHARED, "exynos-pcie", pp);
IRQF_SHARED | IRQF_NO_THREAD,
"exynos-pcie", pp);
if (ret) {
dev_err(&pdev->dev, "failed to request msi irq\n");
return ret;
......
......@@ -38,16 +38,7 @@ struct gen_pci_cfg_windows {
struct gen_pci_cfg_bus_ops *ops;
};
/*
* ARM pcibios functions expect the ARM struct pci_sys_data as the PCI
* sysdata. Add pci_sys_data as the first element in struct gen_pci so
* that when we use a gen_pci pointer as sysdata, it is also a pointer to
* a struct pci_sys_data.
*/
struct gen_pci {
#ifdef CONFIG_ARM
struct pci_sys_data sys;
#endif
struct pci_host_bridge host;
struct gen_pci_cfg_windows cfg;
struct list_head resources;
......
......@@ -32,7 +32,7 @@
#define to_imx6_pcie(x) container_of(x, struct imx6_pcie, pp)
struct imx6_pcie {
int reset_gpio;
struct gpio_desc *reset_gpio;
struct clk *pcie_bus;
struct clk *pcie_phy;
struct clk *pcie;
......@@ -122,7 +122,7 @@ static int pcie_phy_wait_ack(void __iomem *dbi_base, int addr)
}
/* Read from the 16-bit PCIe PHY control registers (not memory-mapped) */
static int pcie_phy_read(void __iomem *dbi_base, int addr , int *data)
static int pcie_phy_read(void __iomem *dbi_base, int addr, int *data)
{
u32 val, phy_ctl;
int ret;
......@@ -287,10 +287,10 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
usleep_range(200, 500);
/* Some boards don't have PCIe reset GPIO. */
if (gpio_is_valid(imx6_pcie->reset_gpio)) {
gpio_set_value(imx6_pcie->reset_gpio, 0);
if (imx6_pcie->reset_gpio) {
gpiod_set_value_cansleep(imx6_pcie->reset_gpio, 0);
msleep(100);
gpio_set_value(imx6_pcie->reset_gpio, 1);
gpiod_set_value_cansleep(imx6_pcie->reset_gpio, 1);
}
return 0;
......@@ -537,7 +537,8 @@ static int __init imx6_add_pcie_port(struct pcie_port *pp,
ret = devm_request_irq(&pdev->dev, pp->msi_irq,
imx6_pcie_msi_handler,
IRQF_SHARED, "mx6-pcie-msi", pp);
IRQF_SHARED | IRQF_NO_THREAD,
"mx6-pcie-msi", pp);
if (ret) {
dev_err(&pdev->dev, "failed to request MSI irq\n");
return ret;
......@@ -560,7 +561,6 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
{
struct imx6_pcie *imx6_pcie;
struct pcie_port *pp;
struct device_node *np = pdev->dev.of_node;
struct resource *dbi_base;
int ret;
......@@ -581,15 +581,8 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
return PTR_ERR(pp->dbi_base);
/* Fetch GPIOs */
imx6_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
if (gpio_is_valid(imx6_pcie->reset_gpio)) {
ret = devm_gpio_request_one(&pdev->dev, imx6_pcie->reset_gpio,
GPIOF_OUT_INIT_LOW, "PCIe reset");
if (ret) {
dev_err(&pdev->dev, "unable to get reset gpio\n");
return ret;
}
}
imx6_pcie->reset_gpio = devm_gpiod_get_optional(&pdev->dev, "reset",
GPIOD_OUT_LOW);
/* Fetch clocks */
imx6_pcie->pcie_phy = devm_clk_get(&pdev->dev, "pcie_phy");
......
......@@ -15,6 +15,7 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_pci.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
......@@ -102,6 +103,8 @@ struct rcar_pci_priv {
unsigned busnr;
int irq;
unsigned long window_size;
unsigned long window_addr;
unsigned long window_pci;
};
/* PCI configuration space operations */
......@@ -239,8 +242,8 @@ static int rcar_pci_setup(int nr, struct pci_sys_data *sys)
RCAR_PCI_ARBITER_PCIBP_MODE;
iowrite32(val, reg + RCAR_PCI_ARBITER_CTR_REG);
/* PCI-AHB mapping: 0x40000000 base */
iowrite32(0x40000000 | RCAR_PCIAHB_PREFETCH16,
/* PCI-AHB mapping */
iowrite32(priv->window_addr | RCAR_PCIAHB_PREFETCH16,
reg + RCAR_PCIAHB_WIN1_CTR_REG);
/* AHB-PCI mapping: OHCI/EHCI registers */
......@@ -251,7 +254,7 @@ static int rcar_pci_setup(int nr, struct pci_sys_data *sys)
iowrite32(RCAR_AHBPCI_WIN1_HOST | RCAR_AHBPCI_WIN_CTR_CFG,
reg + RCAR_AHBPCI_WIN1_CTR_REG);
/* Set PCI-AHB Window1 address */
iowrite32(0x40000000 | PCI_BASE_ADDRESS_MEM_PREFETCH,
iowrite32(priv->window_pci | PCI_BASE_ADDRESS_MEM_PREFETCH,
reg + PCI_BASE_ADDRESS_1);
/* Set AHB-PCI bridge PCI communication area address */
val = priv->cfg_res->start + RCAR_AHBPCI_PCICOM_OFFSET;
......@@ -284,6 +287,64 @@ static struct pci_ops rcar_pci_ops = {
.write = pci_generic_config_write,
};
static int pci_dma_range_parser_init(struct of_pci_range_parser *parser,
struct device_node *node)
{
const int na = 3, ns = 2;
int rlen;
parser->node = node;
parser->pna = of_n_addr_cells(node);
parser->np = parser->pna + na + ns;
parser->range = of_get_property(node, "dma-ranges", &rlen);
if (!parser->range)
return -ENOENT;
parser->end = parser->range + rlen / sizeof(__be32);
return 0;
}
static int rcar_pci_parse_map_dma_ranges(struct rcar_pci_priv *pci,
struct device_node *np)
{
struct of_pci_range range;
struct of_pci_range_parser parser;
int index = 0;
/* Failure to parse is ok as we fall back to defaults */
if (pci_dma_range_parser_init(&parser, np))
return 0;
/* Get the dma-ranges from DT */
for_each_of_pci_range(&parser, &range) {
/* Hardware only allows one inbound 32-bit range */
if (index)
return -EINVAL;
pci->window_addr = (unsigned long)range.cpu_addr;
pci->window_pci = (unsigned long)range.pci_addr;
pci->window_size = (unsigned long)range.size;
/* Catch HW limitations */
if (!(range.flags & IORESOURCE_PREFETCH)) {
dev_err(pci->dev, "window must be prefetchable\n");
return -EINVAL;
}
if (pci->window_addr) {
u32 lowaddr = 1 << (ffs(pci->window_addr) - 1);
if (lowaddr < pci->window_size) {
dev_err(pci->dev, "invalid window size/addr\n");
return -EINVAL;
}
}
index++;
}
return 0;
}
static int rcar_pci_probe(struct platform_device *pdev)
{
struct resource *cfg_res, *mem_res;
......@@ -329,6 +390,9 @@ static int rcar_pci_probe(struct platform_device *pdev)
return priv->irq;
}
/* default window addr and size if not specified in DT */
priv->window_addr = 0x40000000;
priv->window_pci = 0x40000000;
priv->window_size = SZ_1G;
if (pdev->dev.of_node) {
......@@ -344,6 +408,12 @@ static int rcar_pci_probe(struct platform_device *pdev)
priv->busnr = busnr.start;
if (busnr.end != busnr.start)
dev_warn(&pdev->dev, "only one bus number supported\n");
ret = rcar_pci_parse_map_dma_ranges(priv, pdev->dev.of_node);
if (ret < 0) {
dev_err(&pdev->dev, "failed to parse dma-range\n");
return ret;
}
} else {
priv->busnr = pdev->id;
}
......@@ -360,6 +430,7 @@ static int rcar_pci_probe(struct platform_device *pdev)
}
static struct of_device_id rcar_pci_of_match[] = {
{ .compatible = "renesas,pci-rcar-gen2", },
{ .compatible = "renesas,pci-r8a7790", },
{ .compatible = "renesas,pci-r8a7791", },
{ .compatible = "renesas,pci-r8a7794", },
......
......@@ -1288,7 +1288,7 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie)
msi->irq = err;
err = request_irq(msi->irq, tegra_pcie_msi_irq, 0,
err = request_irq(msi->irq, tegra_pcie_msi_irq, IRQF_NO_THREAD,
tegra_msi_irq_chip.name, pcie);
if (err < 0) {
dev_err(&pdev->dev, "failed to request IRQ: %d\n", err);
......
......@@ -125,9 +125,6 @@ static int versatile_pci_parse_request_of_pci_ranges(struct device *dev,
return err;
}
/* Unused, temporary to satisfy ARM arch code */
struct pci_sys_data sys;
static int versatile_pci_probe(struct platform_device *pdev)
{
struct resource *res;
......@@ -208,7 +205,7 @@ static int versatile_pci_probe(struct platform_device *pdev)
pci_add_flags(PCI_ENABLE_PROC_DOMAINS);
pci_add_flags(PCI_REASSIGN_ALL_BUS | PCI_REASSIGN_ALL_RSRC);
bus = pci_scan_root_bus(&pdev->dev, 0, &pci_versatile_ops, &sys, &pci_res);
bus = pci_scan_root_bus(&pdev->dev, 0, &pci_versatile_ops, NULL, &pci_res);
if (!bus)
return -ENOMEM;
......
......@@ -128,32 +128,26 @@ static inline void dw_pcie_writel_rc(struct pcie_port *pp, u32 val, u32 reg)
static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
u32 *val)
{
int ret;
if (pp->ops->rd_own_conf)
ret = pp->ops->rd_own_conf(pp, where, size, val);
else
ret = dw_pcie_cfg_read(pp->dbi_base + where, size, val);
return pp->ops->rd_own_conf(pp, where, size, val);
return ret;
return dw_pcie_cfg_read(pp->dbi_base + where, size, val);
}
static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
u32 val)
{
int ret;
if (pp->ops->wr_own_conf)
ret = pp->ops->wr_own_conf(pp, where, size, val);
else
ret = dw_pcie_cfg_write(pp->dbi_base + where, size, val);
return pp->ops->wr_own_conf(pp, where, size, val);
return ret;
return dw_pcie_cfg_write(pp->dbi_base + where, size, val);
}
static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index,
int type, u64 cpu_addr, u64 pci_addr, u32 size)
{
u32 val;
dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | index,
PCIE_ATU_VIEWPORT);
dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr), PCIE_ATU_LOWER_BASE);
......@@ -164,6 +158,12 @@ static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index,
dw_pcie_writel_rc(pp, upper_32_bits(pci_addr), PCIE_ATU_UPPER_TARGET);
dw_pcie_writel_rc(pp, type, PCIE_ATU_CR1);
dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
/*
* Make sure ATU enable takes effect before any subsequent config
* and I/O accesses.
*/
dw_pcie_readl_rc(pp, PCIE_ATU_CR2, &val);
}
static struct irq_chip dw_msi_irq_chip = {
......@@ -384,8 +384,8 @@ int dw_pcie_link_up(struct pcie_port *pp)
{
if (pp->ops->link_up)
return pp->ops->link_up(pp);
else
return 0;
return 0;
}
static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
......@@ -571,6 +571,9 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
u64 cpu_addr;
void __iomem *va_cfg_base;
if (pp->ops->rd_other_conf)
return pp->ops->rd_other_conf(pp, bus, devfn, where, size, val);
busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
PCIE_ATU_FUNC(PCI_FUNC(devfn));
......@@ -605,6 +608,9 @@ static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
u64 cpu_addr;
void __iomem *va_cfg_base;
if (pp->ops->wr_other_conf)
return pp->ops->wr_other_conf(pp, bus, devfn, where, size, val);
busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
PCIE_ATU_FUNC(PCI_FUNC(devfn));
......@@ -658,46 +664,30 @@ static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
int size, u32 *val)
{
struct pcie_port *pp = bus->sysdata;
int ret;
if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) {
*val = 0xffffffff;
return PCIBIOS_DEVICE_NOT_FOUND;
}
if (bus->number != pp->root_bus_nr)
if (pp->ops->rd_other_conf)
ret = pp->ops->rd_other_conf(pp, bus, devfn,
where, size, val);
else
ret = dw_pcie_rd_other_conf(pp, bus, devfn,
where, size, val);
else
ret = dw_pcie_rd_own_conf(pp, where, size, val);
if (bus->number == pp->root_bus_nr)
return dw_pcie_rd_own_conf(pp, where, size, val);
return ret;
return dw_pcie_rd_other_conf(pp, bus, devfn, where, size, val);
}
static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
int where, int size, u32 val)
{
struct pcie_port *pp = bus->sysdata;
int ret;
if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0)
return PCIBIOS_DEVICE_NOT_FOUND;
if (bus->number != pp->root_bus_nr)
if (pp->ops->wr_other_conf)
ret = pp->ops->wr_other_conf(pp, bus, devfn,
where, size, val);
else
ret = dw_pcie_wr_other_conf(pp, bus, devfn,
where, size, val);
else
ret = dw_pcie_wr_own_conf(pp, where, size, val);
if (bus->number == pp->root_bus_nr)
return dw_pcie_wr_own_conf(pp, where, size, val);
return ret;
return dw_pcie_wr_other_conf(pp, bus, devfn, where, size, val);
}
static struct pci_ops dw_pcie_ops = {
......
/*
* PCIe host controller driver for HiSilicon Hip05 SoC
* PCIe host controller driver for HiSilicon SoCs
*
* Copyright (C) 2015 HiSilicon Co., Ltd. http://www.hisilicon.com
*
* Author: Zhou Wang <wangzhou1@hisilicon.com>
* Dacai Zhu <zhudacai@hisilicon.com>
* Authors: Zhou Wang <wangzhou1@hisilicon.com>
* Dacai Zhu <zhudacai@hisilicon.com>
* Gabriele Paoloni <gabriele.paoloni@huawei.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -16,21 +17,31 @@
#include <linux/of_address.h>
#include <linux/of_pci.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include "pcie-designware.h"
#define PCIE_SUBCTRL_SYS_STATE4_REG 0x6818
#define PCIE_LTSSM_LINKUP_STATE 0x11
#define PCIE_LTSSM_STATE_MASK 0x3F
#define PCIE_LTSSM_LINKUP_STATE 0x11
#define PCIE_LTSSM_STATE_MASK 0x3F
#define PCIE_SUBCTRL_SYS_STATE4_REG 0x6818
#define PCIE_SYS_STATE4 0x31c
#define PCIE_HIP06_CTRL_OFF 0x1000
#define to_hisi_pcie(x) container_of(x, struct hisi_pcie, pp)
struct hisi_pcie;
struct pcie_soc_ops {
int (*hisi_pcie_link_up)(struct hisi_pcie *pcie);
};
struct hisi_pcie {
struct regmap *subctrl;
void __iomem *reg_base;
u32 port_id;
struct pcie_port pp;
struct pcie_soc_ops *soc_ops;
};
static inline void hisi_pcie_apb_writel(struct hisi_pcie *pcie,
......@@ -44,7 +55,7 @@ static inline u32 hisi_pcie_apb_readl(struct hisi_pcie *pcie, u32 reg)
return readl(pcie->reg_base + reg);
}
/* Hip05 PCIe host only supports 32-bit config access */
/* HipXX PCIe host only supports 32-bit config access */
static int hisi_pcie_cfg_read(struct pcie_port *pp, int where, int size,
u32 *val)
{
......@@ -69,7 +80,7 @@ static int hisi_pcie_cfg_read(struct pcie_port *pp, int where, int size,
return PCIBIOS_SUCCESSFUL;
}
/* Hip05 PCIe host only supports 32-bit config access */
/* HipXX PCIe host only supports 32-bit config access */
static int hisi_pcie_cfg_write(struct pcie_port *pp, int where, int size,
u32 val)
{
......@@ -96,10 +107,9 @@ static int hisi_pcie_cfg_write(struct pcie_port *pp, int where, int size,
return PCIBIOS_SUCCESSFUL;
}
static int hisi_pcie_link_up(struct pcie_port *pp)
static int hisi_pcie_link_up_hip05(struct hisi_pcie *hisi_pcie)
{
u32 val;
struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp);
regmap_read(hisi_pcie->subctrl, PCIE_SUBCTRL_SYS_STATE4_REG +
0x100 * hisi_pcie->port_id, &val);
......@@ -107,6 +117,23 @@ static int hisi_pcie_link_up(struct pcie_port *pp)
return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE);
}
static int hisi_pcie_link_up_hip06(struct hisi_pcie *hisi_pcie)
{
u32 val;
val = hisi_pcie_apb_readl(hisi_pcie, PCIE_HIP06_CTRL_OFF +
PCIE_SYS_STATE4);
return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE);
}
static int hisi_pcie_link_up(struct pcie_port *pp)
{
struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp);
return hisi_pcie->soc_ops->hisi_pcie_link_up(hisi_pcie);
}
static struct pcie_host_ops hisi_pcie_host_ops = {
.rd_own_conf = hisi_pcie_cfg_read,
.wr_own_conf = hisi_pcie_cfg_write,
......@@ -145,7 +172,9 @@ static int hisi_pcie_probe(struct platform_device *pdev)
{
struct hisi_pcie *hisi_pcie;
struct pcie_port *pp;
const struct of_device_id *match;
struct resource *reg;
struct device_driver *driver;
int ret;
hisi_pcie = devm_kzalloc(&pdev->dev, sizeof(*hisi_pcie), GFP_KERNEL);
......@@ -154,6 +183,10 @@ static int hisi_pcie_probe(struct platform_device *pdev)
pp = &hisi_pcie->pp;
pp->dev = &pdev->dev;
driver = (pdev->dev).driver;
match = of_match_device(driver->of_match_table, &pdev->dev);
hisi_pcie->soc_ops = (struct pcie_soc_ops *) match->data;
hisi_pcie->subctrl =
syscon_regmap_lookup_by_compatible("hisilicon,pcie-sas-subctrl");
......@@ -182,11 +215,27 @@ static int hisi_pcie_probe(struct platform_device *pdev)
return 0;
}
static struct pcie_soc_ops hip05_ops = {
&hisi_pcie_link_up_hip05
};
static struct pcie_soc_ops hip06_ops = {
&hisi_pcie_link_up_hip06
};
static const struct of_device_id hisi_pcie_of_match[] = {
{.compatible = "hisilicon,hip05-pcie",},
{
.compatible = "hisilicon,hip05-pcie",
.data = (void *) &hip05_ops,
},
{
.compatible = "hisilicon,hip06-pcie",
.data = (void *) &hip06_ops,
},
{},
};
MODULE_DEVICE_TABLE(of, hisi_pcie_of_match);
static struct platform_driver hisi_pcie_driver = {
......@@ -198,3 +247,8 @@ static struct platform_driver hisi_pcie_driver = {
};
module_platform_driver(hisi_pcie_driver);
MODULE_AUTHOR("Zhou Wang <wangzhou1@hisilicon.com>");
MODULE_AUTHOR("Dacai Zhu <zhudacai@hisilicon.com>");
MODULE_AUTHOR("Gabriele Paoloni <gabriele.paoloni@huawei.com>");
MODULE_LICENSE("GPL v2");
......@@ -55,6 +55,7 @@ static int iproc_pcie_bcma_probe(struct bcma_device *bdev)
bcma_set_drvdata(bdev, pcie);
pcie->base = bdev->io_addr;
pcie->base_addr = bdev->addr;
res_mem.start = bdev->addr_s[0];
res_mem.end = bdev->addr_s[0] + SZ_128M - 1;
......
/*
* Copyright (C) 2015 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/interrupt.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
#include <linux/msi.h>
#include <linux/of_irq.h>
#include <linux/of_pci.h>
#include <linux/pci.h>
#include "pcie-iproc.h"
#define IPROC_MSI_INTR_EN_SHIFT 11
#define IPROC_MSI_INTR_EN BIT(IPROC_MSI_INTR_EN_SHIFT)
#define IPROC_MSI_INT_N_EVENT_SHIFT 1
#define IPROC_MSI_INT_N_EVENT BIT(IPROC_MSI_INT_N_EVENT_SHIFT)
#define IPROC_MSI_EQ_EN_SHIFT 0
#define IPROC_MSI_EQ_EN BIT(IPROC_MSI_EQ_EN_SHIFT)
#define IPROC_MSI_EQ_MASK 0x3f
/* Max number of GIC interrupts */
#define NR_HW_IRQS 6
/* Number of entries in each event queue */
#define EQ_LEN 64
/* Size of each event queue memory region */
#define EQ_MEM_REGION_SIZE SZ_4K
/* Size of each MSI address region */
#define MSI_MEM_REGION_SIZE SZ_4K
enum iproc_msi_reg {
IPROC_MSI_EQ_PAGE = 0,
IPROC_MSI_EQ_PAGE_UPPER,
IPROC_MSI_PAGE,
IPROC_MSI_PAGE_UPPER,
IPROC_MSI_CTRL,
IPROC_MSI_EQ_HEAD,
IPROC_MSI_EQ_TAIL,
IPROC_MSI_INTS_EN,
IPROC_MSI_REG_SIZE,
};
struct iproc_msi;
/**
* iProc MSI group
*
* One MSI group is allocated per GIC interrupt, serviced by one iProc MSI
* event queue.
*
* @msi: pointer to iProc MSI data
* @gic_irq: GIC interrupt
* @eq: Event queue number
*/
struct iproc_msi_grp {
struct iproc_msi *msi;
int gic_irq;
unsigned int eq;
};
/**
* iProc event queue based MSI
*
* Only meant to be used on platforms without MSI support integrated into the
* GIC.
*
* @pcie: pointer to iProc PCIe data
* @reg_offsets: MSI register offsets
* @grps: MSI groups
* @nr_irqs: number of total interrupts connected to GIC
* @nr_cpus: number of toal CPUs
* @has_inten_reg: indicates the MSI interrupt enable register needs to be
* set explicitly (required for some legacy platforms)
* @bitmap: MSI vector bitmap
* @bitmap_lock: lock to protect access to the MSI bitmap
* @nr_msi_vecs: total number of MSI vectors
* @inner_domain: inner IRQ domain
* @msi_domain: MSI IRQ domain
* @nr_eq_region: required number of 4K aligned memory region for MSI event
* queues
* @nr_msi_region: required number of 4K aligned address region for MSI posted
* writes
* @eq_cpu: pointer to allocated memory region for MSI event queues
* @eq_dma: DMA address of MSI event queues
* @msi_addr: MSI address
*/
struct iproc_msi {
struct iproc_pcie *pcie;
const u16 (*reg_offsets)[IPROC_MSI_REG_SIZE];
struct iproc_msi_grp *grps;
int nr_irqs;
int nr_cpus;
bool has_inten_reg;
unsigned long *bitmap;
struct mutex bitmap_lock;
unsigned int nr_msi_vecs;
struct irq_domain *inner_domain;
struct irq_domain *msi_domain;
unsigned int nr_eq_region;
unsigned int nr_msi_region;
void *eq_cpu;
dma_addr_t eq_dma;
phys_addr_t msi_addr;
};
static const u16 iproc_msi_reg_paxb[NR_HW_IRQS][IPROC_MSI_REG_SIZE] = {
{ 0x200, 0x2c0, 0x204, 0x2c4, 0x210, 0x250, 0x254, 0x208 },
{ 0x200, 0x2c0, 0x204, 0x2c4, 0x214, 0x258, 0x25c, 0x208 },
{ 0x200, 0x2c0, 0x204, 0x2c4, 0x218, 0x260, 0x264, 0x208 },
{ 0x200, 0x2c0, 0x204, 0x2c4, 0x21c, 0x268, 0x26c, 0x208 },
{ 0x200, 0x2c0, 0x204, 0x2c4, 0x220, 0x270, 0x274, 0x208 },
{ 0x200, 0x2c0, 0x204, 0x2c4, 0x224, 0x278, 0x27c, 0x208 },
};
static const u16 iproc_msi_reg_paxc[NR_HW_IRQS][IPROC_MSI_REG_SIZE] = {
{ 0xc00, 0xc04, 0xc08, 0xc0c, 0xc40, 0xc50, 0xc60 },
{ 0xc10, 0xc14, 0xc18, 0xc1c, 0xc44, 0xc54, 0xc64 },
{ 0xc20, 0xc24, 0xc28, 0xc2c, 0xc48, 0xc58, 0xc68 },
{ 0xc30, 0xc34, 0xc38, 0xc3c, 0xc4c, 0xc5c, 0xc6c },
};
static inline u32 iproc_msi_read_reg(struct iproc_msi *msi,
enum iproc_msi_reg reg,
unsigned int eq)
{
struct iproc_pcie *pcie = msi->pcie;
return readl_relaxed(pcie->base + msi->reg_offsets[eq][reg]);
}
static inline void iproc_msi_write_reg(struct iproc_msi *msi,
enum iproc_msi_reg reg,
int eq, u32 val)
{
struct iproc_pcie *pcie = msi->pcie;
writel_relaxed(val, pcie->base + msi->reg_offsets[eq][reg]);
}
static inline u32 hwirq_to_group(struct iproc_msi *msi, unsigned long hwirq)
{
return (hwirq % msi->nr_irqs);
}
static inline unsigned int iproc_msi_addr_offset(struct iproc_msi *msi,
unsigned long hwirq)
{
if (msi->nr_msi_region > 1)
return hwirq_to_group(msi, hwirq) * MSI_MEM_REGION_SIZE;
else
return hwirq_to_group(msi, hwirq) * sizeof(u32);
}
static inline unsigned int iproc_msi_eq_offset(struct iproc_msi *msi, u32 eq)
{
if (msi->nr_eq_region > 1)
return eq * EQ_MEM_REGION_SIZE;
else
return eq * EQ_LEN * sizeof(u32);
}
static struct irq_chip iproc_msi_irq_chip = {
.name = "iProc-MSI",
};
static struct msi_domain_info iproc_msi_domain_info = {
.flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
MSI_FLAG_PCI_MSIX,
.chip = &iproc_msi_irq_chip,
};
/*
* In iProc PCIe core, each MSI group is serviced by a GIC interrupt and a
* dedicated event queue. Each MSI group can support up to 64 MSI vectors.
*
* The number of MSI groups varies between different iProc SoCs. The total
* number of CPU cores also varies. To support MSI IRQ affinity, we
* distribute GIC interrupts across all available CPUs. MSI vector is moved
* from one GIC interrupt to another to steer to the target CPU.
*
* Assuming:
* - the number of MSI groups is M
* - the number of CPU cores is N
* - M is always a multiple of N
*
* Total number of raw MSI vectors = M * 64
* Total number of supported MSI vectors = (M * 64) / N
*/
static inline int hwirq_to_cpu(struct iproc_msi *msi, unsigned long hwirq)
{
return (hwirq % msi->nr_cpus);
}
static inline unsigned long hwirq_to_canonical_hwirq(struct iproc_msi *msi,
unsigned long hwirq)
{
return (hwirq - hwirq_to_cpu(msi, hwirq));
}
static int iproc_msi_irq_set_affinity(struct irq_data *data,
const struct cpumask *mask, bool force)
{
struct iproc_msi *msi = irq_data_get_irq_chip_data(data);
int target_cpu = cpumask_first(mask);
int curr_cpu;
curr_cpu = hwirq_to_cpu(msi, data->hwirq);
if (curr_cpu == target_cpu)
return IRQ_SET_MASK_OK_DONE;
/* steer MSI to the target CPU */
data->hwirq = hwirq_to_canonical_hwirq(msi, data->hwirq) + target_cpu;
return IRQ_SET_MASK_OK;
}
static void iproc_msi_irq_compose_msi_msg(struct irq_data *data,
struct msi_msg *msg)
{
struct iproc_msi *msi = irq_data_get_irq_chip_data(data);
dma_addr_t addr;
addr = msi->msi_addr + iproc_msi_addr_offset(msi, data->hwirq);
msg->address_lo = lower_32_bits(addr);
msg->address_hi = upper_32_bits(addr);
msg->data = data->hwirq;
}
static struct irq_chip iproc_msi_bottom_irq_chip = {
.name = "MSI",
.irq_set_affinity = iproc_msi_irq_set_affinity,
.irq_compose_msi_msg = iproc_msi_irq_compose_msi_msg,
};
static int iproc_msi_irq_domain_alloc(struct irq_domain *domain,
unsigned int virq, unsigned int nr_irqs,
void *args)
{
struct iproc_msi *msi = domain->host_data;
int hwirq;
mutex_lock(&msi->bitmap_lock);
/* Allocate 'nr_cpus' number of MSI vectors each time */
hwirq = bitmap_find_next_zero_area(msi->bitmap, msi->nr_msi_vecs, 0,
msi->nr_cpus, 0);
if (hwirq < msi->nr_msi_vecs) {
bitmap_set(msi->bitmap, hwirq, msi->nr_cpus);
} else {
mutex_unlock(&msi->bitmap_lock);
return -ENOSPC;
}
mutex_unlock(&msi->bitmap_lock);
irq_domain_set_info(domain, virq, hwirq, &iproc_msi_bottom_irq_chip,
domain->host_data, handle_simple_irq, NULL, NULL);
return 0;
}
static void iproc_msi_irq_domain_free(struct irq_domain *domain,
unsigned int virq, unsigned int nr_irqs)
{
struct irq_data *data = irq_domain_get_irq_data(domain, virq);
struct iproc_msi *msi = irq_data_get_irq_chip_data(data);
unsigned int hwirq;
mutex_lock(&msi->bitmap_lock);
hwirq = hwirq_to_canonical_hwirq(msi, data->hwirq);
bitmap_clear(msi->bitmap, hwirq, msi->nr_cpus);
mutex_unlock(&msi->bitmap_lock);
irq_domain_free_irqs_parent(domain, virq, nr_irqs);
}
static const struct irq_domain_ops msi_domain_ops = {
.alloc = iproc_msi_irq_domain_alloc,
.free = iproc_msi_irq_domain_free,
};
static inline u32 decode_msi_hwirq(struct iproc_msi *msi, u32 eq, u32 head)
{
u32 *msg, hwirq;
unsigned int offs;
offs = iproc_msi_eq_offset(msi, eq) + head * sizeof(u32);
msg = (u32 *)(msi->eq_cpu + offs);
hwirq = *msg & IPROC_MSI_EQ_MASK;
/*
* Since we have multiple hwirq mapped to a single MSI vector,
* now we need to derive the hwirq at CPU0. It can then be used to
* mapped back to virq.
*/
return hwirq_to_canonical_hwirq(msi, hwirq);
}
static void iproc_msi_handler(struct irq_desc *desc)
{
struct irq_chip *chip = irq_desc_get_chip(desc);
struct iproc_msi_grp *grp;
struct iproc_msi *msi;
struct iproc_pcie *pcie;
u32 eq, head, tail, nr_events;
unsigned long hwirq;
int virq;
chained_irq_enter(chip, desc);
grp = irq_desc_get_handler_data(desc);
msi = grp->msi;
pcie = msi->pcie;
eq = grp->eq;
/*
* iProc MSI event queue is tracked by head and tail pointers. Head
* pointer indicates the next entry (MSI data) to be consumed by SW in
* the queue and needs to be updated by SW. iProc MSI core uses the
* tail pointer as the next data insertion point.
*
* Entries between head and tail pointers contain valid MSI data. MSI
* data is guaranteed to be in the event queue memory before the tail
* pointer is updated by the iProc MSI core.
*/
head = iproc_msi_read_reg(msi, IPROC_MSI_EQ_HEAD,
eq) & IPROC_MSI_EQ_MASK;
do {
tail = iproc_msi_read_reg(msi, IPROC_MSI_EQ_TAIL,
eq) & IPROC_MSI_EQ_MASK;
/*
* Figure out total number of events (MSI data) to be
* processed.
*/
nr_events = (tail < head) ?
(EQ_LEN - (head - tail)) : (tail - head);
if (!nr_events)
break;
/* process all outstanding events */
while (nr_events--) {
hwirq = decode_msi_hwirq(msi, eq, head);
virq = irq_find_mapping(msi->inner_domain, hwirq);
generic_handle_irq(virq);
head++;
head %= EQ_LEN;
}
/*
* Now all outstanding events have been processed. Update the
* head pointer.
*/
iproc_msi_write_reg(msi, IPROC_MSI_EQ_HEAD, eq, head);
/*
* Now go read the tail pointer again to see if there are new
* oustanding events that came in during the above window.
*/
} while (true);
chained_irq_exit(chip, desc);
}
static void iproc_msi_enable(struct iproc_msi *msi)
{
int i, eq;
u32 val;
/* Program memory region for each event queue */
for (i = 0; i < msi->nr_eq_region; i++) {
dma_addr_t addr = msi->eq_dma + (i * EQ_MEM_REGION_SIZE);
iproc_msi_write_reg(msi, IPROC_MSI_EQ_PAGE, i,
lower_32_bits(addr));
iproc_msi_write_reg(msi, IPROC_MSI_EQ_PAGE_UPPER, i,
upper_32_bits(addr));
}
/* Program address region for MSI posted writes */
for (i = 0; i < msi->nr_msi_region; i++) {
phys_addr_t addr = msi->msi_addr + (i * MSI_MEM_REGION_SIZE);
iproc_msi_write_reg(msi, IPROC_MSI_PAGE, i,
lower_32_bits(addr));
iproc_msi_write_reg(msi, IPROC_MSI_PAGE_UPPER, i,
upper_32_bits(addr));
}
for (eq = 0; eq < msi->nr_irqs; eq++) {
/* Enable MSI event queue */
val = IPROC_MSI_INTR_EN | IPROC_MSI_INT_N_EVENT |
IPROC_MSI_EQ_EN;
iproc_msi_write_reg(msi, IPROC_MSI_CTRL, eq, val);
/*
* Some legacy platforms require the MSI interrupt enable
* register to be set explicitly.
*/
if (msi->has_inten_reg) {
val = iproc_msi_read_reg(msi, IPROC_MSI_INTS_EN, eq);
val |= BIT(eq);
iproc_msi_write_reg(msi, IPROC_MSI_INTS_EN, eq, val);
}
}
}
static void iproc_msi_disable(struct iproc_msi *msi)
{
u32 eq, val;
for (eq = 0; eq < msi->nr_irqs; eq++) {
if (msi->has_inten_reg) {
val = iproc_msi_read_reg(msi, IPROC_MSI_INTS_EN, eq);
val &= ~BIT(eq);
iproc_msi_write_reg(msi, IPROC_MSI_INTS_EN, eq, val);
}
val = iproc_msi_read_reg(msi, IPROC_MSI_CTRL, eq);
val &= ~(IPROC_MSI_INTR_EN | IPROC_MSI_INT_N_EVENT |
IPROC_MSI_EQ_EN);
iproc_msi_write_reg(msi, IPROC_MSI_CTRL, eq, val);
}
}
static int iproc_msi_alloc_domains(struct device_node *node,
struct iproc_msi *msi)
{
msi->inner_domain = irq_domain_add_linear(NULL, msi->nr_msi_vecs,
&msi_domain_ops, msi);
if (!msi->inner_domain)
return -ENOMEM;
msi->msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node),
&iproc_msi_domain_info,
msi->inner_domain);
if (!msi->msi_domain) {
irq_domain_remove(msi->inner_domain);
return -ENOMEM;
}
return 0;
}
static void iproc_msi_free_domains(struct iproc_msi *msi)
{
if (msi->msi_domain)
irq_domain_remove(msi->msi_domain);
if (msi->inner_domain)
irq_domain_remove(msi->inner_domain);
}
static void iproc_msi_irq_free(struct iproc_msi *msi, unsigned int cpu)
{
int i;
for (i = cpu; i < msi->nr_irqs; i += msi->nr_cpus) {
irq_set_chained_handler_and_data(msi->grps[i].gic_irq,
NULL, NULL);
}
}
static int iproc_msi_irq_setup(struct iproc_msi *msi, unsigned int cpu)
{
int i, ret;
cpumask_var_t mask;
struct iproc_pcie *pcie = msi->pcie;
for (i = cpu; i < msi->nr_irqs; i += msi->nr_cpus) {
irq_set_chained_handler_and_data(msi->grps[i].gic_irq,
iproc_msi_handler,
&msi->grps[i]);
/* Dedicate GIC interrupt to each CPU core */
if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
cpumask_clear(mask);
cpumask_set_cpu(cpu, mask);
ret = irq_set_affinity(msi->grps[i].gic_irq, mask);
if (ret)
dev_err(pcie->dev,
"failed to set affinity for IRQ%d\n",
msi->grps[i].gic_irq);
free_cpumask_var(mask);
} else {
dev_err(pcie->dev, "failed to alloc CPU mask\n");
ret = -EINVAL;
}
if (ret) {
/* Free all configured/unconfigured IRQs */
iproc_msi_irq_free(msi, cpu);
return ret;
}
}
return 0;
}
int iproc_msi_init(struct iproc_pcie *pcie, struct device_node *node)
{
struct iproc_msi *msi;
int i, ret;
unsigned int cpu;
if (!of_device_is_compatible(node, "brcm,iproc-msi"))
return -ENODEV;
if (!of_find_property(node, "msi-controller", NULL))
return -ENODEV;
if (pcie->msi)
return -EBUSY;
msi = devm_kzalloc(pcie->dev, sizeof(*msi), GFP_KERNEL);
if (!msi)
return -ENOMEM;
msi->pcie = pcie;
pcie->msi = msi;
msi->msi_addr = pcie->base_addr;
mutex_init(&msi->bitmap_lock);
msi->nr_cpus = num_possible_cpus();
msi->nr_irqs = of_irq_count(node);
if (!msi->nr_irqs) {
dev_err(pcie->dev, "found no MSI GIC interrupt\n");
return -ENODEV;
}
if (msi->nr_irqs > NR_HW_IRQS) {
dev_warn(pcie->dev, "too many MSI GIC interrupts defined %d\n",
msi->nr_irqs);
msi->nr_irqs = NR_HW_IRQS;
}
if (msi->nr_irqs < msi->nr_cpus) {
dev_err(pcie->dev,
"not enough GIC interrupts for MSI affinity\n");
return -EINVAL;
}
if (msi->nr_irqs % msi->nr_cpus != 0) {
msi->nr_irqs -= msi->nr_irqs % msi->nr_cpus;
dev_warn(pcie->dev, "Reducing number of interrupts to %d\n",
msi->nr_irqs);
}
switch (pcie->type) {
case IPROC_PCIE_PAXB:
msi->reg_offsets = iproc_msi_reg_paxb;
msi->nr_eq_region = 1;
msi->nr_msi_region = 1;
break;
case IPROC_PCIE_PAXC:
msi->reg_offsets = iproc_msi_reg_paxc;
msi->nr_eq_region = msi->nr_irqs;
msi->nr_msi_region = msi->nr_irqs;
break;
default:
dev_err(pcie->dev, "incompatible iProc PCIe interface\n");
return -EINVAL;
}
if (of_find_property(node, "brcm,pcie-msi-inten", NULL))
msi->has_inten_reg = true;
msi->nr_msi_vecs = msi->nr_irqs * EQ_LEN;
msi->bitmap = devm_kcalloc(pcie->dev, BITS_TO_LONGS(msi->nr_msi_vecs),
sizeof(*msi->bitmap), GFP_KERNEL);
if (!msi->bitmap)
return -ENOMEM;
msi->grps = devm_kcalloc(pcie->dev, msi->nr_irqs, sizeof(*msi->grps),
GFP_KERNEL);
if (!msi->grps)
return -ENOMEM;
for (i = 0; i < msi->nr_irqs; i++) {
unsigned int irq = irq_of_parse_and_map(node, i);
if (!irq) {
dev_err(pcie->dev, "unable to parse/map interrupt\n");
ret = -ENODEV;
goto free_irqs;
}
msi->grps[i].gic_irq = irq;
msi->grps[i].msi = msi;
msi->grps[i].eq = i;
}
/* Reserve memory for event queue and make sure memories are zeroed */
msi->eq_cpu = dma_zalloc_coherent(pcie->dev,
msi->nr_eq_region * EQ_MEM_REGION_SIZE,
&msi->eq_dma, GFP_KERNEL);
if (!msi->eq_cpu) {
ret = -ENOMEM;
goto free_irqs;
}
ret = iproc_msi_alloc_domains(node, msi);
if (ret) {
dev_err(pcie->dev, "failed to create MSI domains\n");
goto free_eq_dma;
}
for_each_online_cpu(cpu) {
ret = iproc_msi_irq_setup(msi, cpu);
if (ret)
goto free_msi_irq;
}
iproc_msi_enable(msi);
return 0;
free_msi_irq:
for_each_online_cpu(cpu)
iproc_msi_irq_free(msi, cpu);
iproc_msi_free_domains(msi);
free_eq_dma:
dma_free_coherent(pcie->dev, msi->nr_eq_region * EQ_MEM_REGION_SIZE,
msi->eq_cpu, msi->eq_dma);
free_irqs:
for (i = 0; i < msi->nr_irqs; i++) {
if (msi->grps[i].gic_irq)
irq_dispose_mapping(msi->grps[i].gic_irq);
}
pcie->msi = NULL;
return ret;
}
EXPORT_SYMBOL(iproc_msi_init);
void iproc_msi_exit(struct iproc_pcie *pcie)
{
struct iproc_msi *msi = pcie->msi;
unsigned int i, cpu;
if (!msi)
return;
iproc_msi_disable(msi);
for_each_online_cpu(cpu)
iproc_msi_irq_free(msi, cpu);
iproc_msi_free_domains(msi);
dma_free_coherent(pcie->dev, msi->nr_eq_region * EQ_MEM_REGION_SIZE,
msi->eq_cpu, msi->eq_dma);
for (i = 0; i < msi->nr_irqs; i++) {
if (msi->grps[i].gic_irq)
irq_dispose_mapping(msi->grps[i].gic_irq);
}
}
EXPORT_SYMBOL(iproc_msi_exit);
......@@ -26,8 +26,21 @@
#include "pcie-iproc.h"
static const struct of_device_id iproc_pcie_of_match_table[] = {
{
.compatible = "brcm,iproc-pcie",
.data = (int *)IPROC_PCIE_PAXB,
}, {
.compatible = "brcm,iproc-pcie-paxc",
.data = (int *)IPROC_PCIE_PAXC,
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, iproc_pcie_of_match_table);
static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
{
const struct of_device_id *of_id;
struct iproc_pcie *pcie;
struct device_node *np = pdev->dev.of_node;
struct resource reg;
......@@ -35,11 +48,16 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
LIST_HEAD(res);
int ret;
of_id = of_match_device(iproc_pcie_of_match_table, &pdev->dev);
if (!of_id)
return -EINVAL;
pcie = devm_kzalloc(&pdev->dev, sizeof(struct iproc_pcie), GFP_KERNEL);
if (!pcie)
return -ENOMEM;
pcie->dev = &pdev->dev;
pcie->type = (enum iproc_pcie_type)of_id->data;
platform_set_drvdata(pdev, pcie);
ret = of_address_to_resource(np, 0, &reg);
......@@ -53,6 +71,7 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
dev_err(pcie->dev, "unable to map controller registers\n");
return -ENOMEM;
}
pcie->base_addr = reg.start;
if (of_property_read_bool(np, "brcm,pcie-ob")) {
u32 val;
......@@ -114,12 +133,6 @@ static int iproc_pcie_pltfm_remove(struct platform_device *pdev)
return iproc_pcie_remove(pcie);
}
static const struct of_device_id iproc_pcie_of_match_table[] = {
{ .compatible = "brcm,iproc-pcie", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, iproc_pcie_of_match_table);
static struct platform_driver iproc_pcie_pltfm_driver = {
.driver = {
.name = "iproc-pcie",
......
......@@ -30,20 +30,16 @@
#include "pcie-iproc.h"
#define CLK_CONTROL_OFFSET 0x000
#define EP_PERST_SOURCE_SELECT_SHIFT 2
#define EP_PERST_SOURCE_SELECT BIT(EP_PERST_SOURCE_SELECT_SHIFT)
#define EP_MODE_SURVIVE_PERST_SHIFT 1
#define EP_MODE_SURVIVE_PERST BIT(EP_MODE_SURVIVE_PERST_SHIFT)
#define RC_PCIE_RST_OUTPUT_SHIFT 0
#define RC_PCIE_RST_OUTPUT BIT(RC_PCIE_RST_OUTPUT_SHIFT)
#define PAXC_RESET_MASK 0x7f
#define CFG_IND_ADDR_OFFSET 0x120
#define CFG_IND_ADDR_MASK 0x00001ffc
#define CFG_IND_DATA_OFFSET 0x124
#define CFG_ADDR_OFFSET 0x1f8
#define CFG_ADDR_BUS_NUM_SHIFT 20
#define CFG_ADDR_BUS_NUM_MASK 0x0ff00000
#define CFG_ADDR_DEV_NUM_SHIFT 15
......@@ -55,12 +51,8 @@
#define CFG_ADDR_CFG_TYPE_SHIFT 0
#define CFG_ADDR_CFG_TYPE_MASK 0x00000003
#define CFG_DATA_OFFSET 0x1fc
#define SYS_RC_INTX_EN 0x330
#define SYS_RC_INTX_MASK 0xf
#define PCIE_LINK_STATUS_OFFSET 0xf0c
#define PCIE_PHYLINKUP_SHIFT 3
#define PCIE_PHYLINKUP BIT(PCIE_PHYLINKUP_SHIFT)
#define PCIE_DL_ACTIVE_SHIFT 2
......@@ -71,12 +63,54 @@
#define OARR_SIZE_CFG_SHIFT 1
#define OARR_SIZE_CFG BIT(OARR_SIZE_CFG_SHIFT)
#define OARR_LO(window) (0xd20 + (window) * 8)
#define OARR_HI(window) (0xd24 + (window) * 8)
#define OMAP_LO(window) (0xd40 + (window) * 8)
#define OMAP_HI(window) (0xd44 + (window) * 8)
#define MAX_NUM_OB_WINDOWS 2
#define MAX_NUM_PAXC_PF 4
#define IPROC_PCIE_REG_INVALID 0xffff
enum iproc_pcie_reg {
IPROC_PCIE_CLK_CTRL = 0,
IPROC_PCIE_CFG_IND_ADDR,
IPROC_PCIE_CFG_IND_DATA,
IPROC_PCIE_CFG_ADDR,
IPROC_PCIE_CFG_DATA,
IPROC_PCIE_INTX_EN,
IPROC_PCIE_OARR_LO,
IPROC_PCIE_OARR_HI,
IPROC_PCIE_OMAP_LO,
IPROC_PCIE_OMAP_HI,
IPROC_PCIE_LINK_STATUS,
};
/* iProc PCIe PAXB registers */
static const u16 iproc_pcie_reg_paxb[] = {
[IPROC_PCIE_CLK_CTRL] = 0x000,
[IPROC_PCIE_CFG_IND_ADDR] = 0x120,
[IPROC_PCIE_CFG_IND_DATA] = 0x124,
[IPROC_PCIE_CFG_ADDR] = 0x1f8,
[IPROC_PCIE_CFG_DATA] = 0x1fc,
[IPROC_PCIE_INTX_EN] = 0x330,
[IPROC_PCIE_OARR_LO] = 0xd20,
[IPROC_PCIE_OARR_HI] = 0xd24,
[IPROC_PCIE_OMAP_LO] = 0xd40,
[IPROC_PCIE_OMAP_HI] = 0xd44,
[IPROC_PCIE_LINK_STATUS] = 0xf0c,
};
/* iProc PCIe PAXC v1 registers */
static const u16 iproc_pcie_reg_paxc[] = {
[IPROC_PCIE_CLK_CTRL] = 0x000,
[IPROC_PCIE_CFG_IND_ADDR] = 0x1f0,
[IPROC_PCIE_CFG_IND_DATA] = 0x1f4,
[IPROC_PCIE_CFG_ADDR] = 0x1f8,
[IPROC_PCIE_CFG_DATA] = 0x1fc,
[IPROC_PCIE_INTX_EN] = IPROC_PCIE_REG_INVALID,
[IPROC_PCIE_OARR_LO] = IPROC_PCIE_REG_INVALID,
[IPROC_PCIE_OARR_HI] = IPROC_PCIE_REG_INVALID,
[IPROC_PCIE_OMAP_LO] = IPROC_PCIE_REG_INVALID,
[IPROC_PCIE_OMAP_HI] = IPROC_PCIE_REG_INVALID,
[IPROC_PCIE_LINK_STATUS] = IPROC_PCIE_REG_INVALID,
};
static inline struct iproc_pcie *iproc_data(struct pci_bus *bus)
{
......@@ -91,6 +125,65 @@ static inline struct iproc_pcie *iproc_data(struct pci_bus *bus)
return pcie;
}
static inline bool iproc_pcie_reg_is_invalid(u16 reg_offset)
{
return !!(reg_offset == IPROC_PCIE_REG_INVALID);
}
static inline u16 iproc_pcie_reg_offset(struct iproc_pcie *pcie,
enum iproc_pcie_reg reg)
{
return pcie->reg_offsets[reg];
}
static inline u32 iproc_pcie_read_reg(struct iproc_pcie *pcie,
enum iproc_pcie_reg reg)
{
u16 offset = iproc_pcie_reg_offset(pcie, reg);
if (iproc_pcie_reg_is_invalid(offset))
return 0;
return readl(pcie->base + offset);
}
static inline void iproc_pcie_write_reg(struct iproc_pcie *pcie,
enum iproc_pcie_reg reg, u32 val)
{
u16 offset = iproc_pcie_reg_offset(pcie, reg);
if (iproc_pcie_reg_is_invalid(offset))
return;
writel(val, pcie->base + offset);
}
static inline void iproc_pcie_ob_write(struct iproc_pcie *pcie,
enum iproc_pcie_reg reg,
unsigned window, u32 val)
{
u16 offset = iproc_pcie_reg_offset(pcie, reg);
if (iproc_pcie_reg_is_invalid(offset))
return;
writel(val, pcie->base + offset + (window * 8));
}
static inline bool iproc_pcie_device_is_valid(struct iproc_pcie *pcie,
unsigned int slot,
unsigned int fn)
{
if (slot > 0)
return false;
/* PAXC can only support limited number of functions */
if (pcie->type == IPROC_PCIE_PAXC && fn >= MAX_NUM_PAXC_PF)
return false;
return true;
}
/**
* Note access to the configuration registers are protected at the higher layer
* by 'pci_lock' in drivers/pci/access.c
......@@ -104,28 +197,34 @@ static void __iomem *iproc_pcie_map_cfg_bus(struct pci_bus *bus,
unsigned fn = PCI_FUNC(devfn);
unsigned busno = bus->number;
u32 val;
u16 offset;
if (!iproc_pcie_device_is_valid(pcie, slot, fn))
return NULL;
/* root complex access */
if (busno == 0) {
if (slot >= 1)
iproc_pcie_write_reg(pcie, IPROC_PCIE_CFG_IND_ADDR,
where & CFG_IND_ADDR_MASK);
offset = iproc_pcie_reg_offset(pcie, IPROC_PCIE_CFG_IND_DATA);
if (iproc_pcie_reg_is_invalid(offset))
return NULL;
writel(where & CFG_IND_ADDR_MASK,
pcie->base + CFG_IND_ADDR_OFFSET);
return (pcie->base + CFG_IND_DATA_OFFSET);
else
return (pcie->base + offset);
}
if (fn > 1)
return NULL;
/* EP device access */
val = (busno << CFG_ADDR_BUS_NUM_SHIFT) |
(slot << CFG_ADDR_DEV_NUM_SHIFT) |
(fn << CFG_ADDR_FUNC_NUM_SHIFT) |
(where & CFG_ADDR_REG_NUM_MASK) |
(1 & CFG_ADDR_CFG_TYPE_MASK);
writel(val, pcie->base + CFG_ADDR_OFFSET);
return (pcie->base + CFG_DATA_OFFSET);
iproc_pcie_write_reg(pcie, IPROC_PCIE_CFG_ADDR, val);
offset = iproc_pcie_reg_offset(pcie, IPROC_PCIE_CFG_DATA);
if (iproc_pcie_reg_is_invalid(offset))
return NULL;
else
return (pcie->base + offset);
}
static struct pci_ops iproc_pcie_ops = {
......@@ -138,18 +237,29 @@ static void iproc_pcie_reset(struct iproc_pcie *pcie)
{
u32 val;
if (pcie->type == IPROC_PCIE_PAXC) {
val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL);
val &= ~PAXC_RESET_MASK;
iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
udelay(100);
val |= PAXC_RESET_MASK;
iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
udelay(100);
return;
}
/*
* Select perst_b signal as reset source. Put the device into reset,
* and then bring it out of reset
*/
val = readl(pcie->base + CLK_CONTROL_OFFSET);
val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL);
val &= ~EP_PERST_SOURCE_SELECT & ~EP_MODE_SURVIVE_PERST &
~RC_PCIE_RST_OUTPUT;
writel(val, pcie->base + CLK_CONTROL_OFFSET);
iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
udelay(250);
val |= RC_PCIE_RST_OUTPUT;
writel(val, pcie->base + CLK_CONTROL_OFFSET);
iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
msleep(100);
}
......@@ -160,7 +270,14 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus)
u16 pos, link_status;
bool link_is_active = false;
val = readl(pcie->base + PCIE_LINK_STATUS_OFFSET);
/*
* PAXC connects to emulated endpoint devices directly and does not
* have a Serdes. Therefore skip the link detection logic here.
*/
if (pcie->type == IPROC_PCIE_PAXC)
return 0;
val = iproc_pcie_read_reg(pcie, IPROC_PCIE_LINK_STATUS);
if (!(val & PCIE_PHYLINKUP) || !(val & PCIE_DL_ACTIVE)) {
dev_err(pcie->dev, "PHY or data link is INACTIVE!\n");
return -ENODEV;
......@@ -221,7 +338,7 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus)
static void iproc_pcie_enable(struct iproc_pcie *pcie)
{
writel(SYS_RC_INTX_MASK, pcie->base + SYS_RC_INTX_EN);
iproc_pcie_write_reg(pcie, IPROC_PCIE_INTX_EN, SYS_RC_INTX_MASK);
}
/**
......@@ -245,7 +362,7 @@ static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr,
if (size > max_size) {
dev_err(pcie->dev,
"res size 0x%pap exceeds max supported size 0x%llx\n",
"res size %pap exceeds max supported size 0x%llx\n",
&size, max_size);
return -EINVAL;
}
......@@ -272,11 +389,15 @@ static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr,
axi_addr -= ob->axi_offset;
for (i = 0; i < MAX_NUM_OB_WINDOWS; i++) {
writel(lower_32_bits(axi_addr) | OARR_VALID |
(ob->set_oarr_size ? 1 : 0), pcie->base + OARR_LO(i));
writel(upper_32_bits(axi_addr), pcie->base + OARR_HI(i));
writel(lower_32_bits(pci_addr), pcie->base + OMAP_LO(i));
writel(upper_32_bits(pci_addr), pcie->base + OMAP_HI(i));
iproc_pcie_ob_write(pcie, IPROC_PCIE_OARR_LO, i,
lower_32_bits(axi_addr) | OARR_VALID |
(ob->set_oarr_size ? 1 : 0));
iproc_pcie_ob_write(pcie, IPROC_PCIE_OARR_HI, i,
upper_32_bits(axi_addr));
iproc_pcie_ob_write(pcie, IPROC_PCIE_OMAP_LO, i,
lower_32_bits(pci_addr));
iproc_pcie_ob_write(pcie, IPROC_PCIE_OMAP_HI, i,
upper_32_bits(pci_addr));
size -= ob->window_size;
if (size == 0)
......@@ -319,6 +440,26 @@ static int iproc_pcie_map_ranges(struct iproc_pcie *pcie,
return 0;
}
static int iproc_pcie_msi_enable(struct iproc_pcie *pcie)
{
struct device_node *msi_node;
msi_node = of_parse_phandle(pcie->dev->of_node, "msi-parent", 0);
if (!msi_node)
return -ENODEV;
/*
* If another MSI controller is being used, the call below should fail
* but that is okay
*/
return iproc_msi_init(pcie, msi_node);
}
static void iproc_pcie_msi_disable(struct iproc_pcie *pcie)
{
iproc_msi_exit(pcie);
}
int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
{
int ret;
......@@ -340,6 +481,19 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
goto err_exit_phy;
}
switch (pcie->type) {
case IPROC_PCIE_PAXB:
pcie->reg_offsets = iproc_pcie_reg_paxb;
break;
case IPROC_PCIE_PAXC:
pcie->reg_offsets = iproc_pcie_reg_paxc;
break;
default:
dev_err(pcie->dev, "incompatible iProc PCIe interface\n");
ret = -EINVAL;
goto err_power_off_phy;
}
iproc_pcie_reset(pcie);
if (pcie->need_ob_cfg) {
......@@ -373,6 +527,10 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
iproc_pcie_enable(pcie);
if (IS_ENABLED(CONFIG_PCI_MSI))
if (iproc_pcie_msi_enable(pcie))
dev_info(pcie->dev, "not using iProc MSI\n");
pci_scan_child_bus(bus);
pci_assign_unassigned_bus_resources(bus);
pci_fixup_irqs(pci_common_swizzle, pcie->map_irq);
......@@ -397,6 +555,8 @@ int iproc_pcie_remove(struct iproc_pcie *pcie)
pci_stop_root_bus(pcie->root_bus);
pci_remove_root_bus(pcie->root_bus);
iproc_pcie_msi_disable(pcie);
phy_power_off(pcie->phy);
phy_exit(pcie->phy);
......
......@@ -14,6 +14,20 @@
#ifndef _PCIE_IPROC_H
#define _PCIE_IPROC_H
/**
* iProc PCIe interface type
*
* PAXB is the wrapper used in root complex that can be connected to an
* external endpoint device.
*
* PAXC is the wrapper used in root complex dedicated for internal emulated
* endpoint devices.
*/
enum iproc_pcie_type {
IPROC_PCIE_PAXB = 0,
IPROC_PCIE_PAXC,
};
/**
* iProc PCIe outbound mapping
* @set_oarr_size: indicates the OARR size bit needs to be set
......@@ -27,21 +41,30 @@ struct iproc_pcie_ob {
resource_size_t window_size;
};
struct iproc_msi;
/**
* iProc PCIe device
*
* @dev: pointer to device data structure
* @type: iProc PCIe interface type
* @reg_offsets: register offsets
* @base: PCIe host controller I/O register base
* @base_addr: PCIe host controller register base physical address
* @sysdata: Per PCI controller data (ARM-specific)
* @root_bus: pointer to root bus
* @phy: optional PHY device that controls the Serdes
* @irqs: interrupt IDs
* @map_irq: function callback to map interrupts
* @need_ob_cfg: indidates SW needs to configure the outbound mapping window
* @need_ob_cfg: indicates SW needs to configure the outbound mapping window
* @ob: outbound mapping parameters
* @msi: MSI data
*/
struct iproc_pcie {
struct device *dev;
enum iproc_pcie_type type;
const u16 *reg_offsets;
void __iomem *base;
phys_addr_t base_addr;
#ifdef CONFIG_ARM
struct pci_sys_data sysdata;
#endif
......@@ -50,9 +73,24 @@ struct iproc_pcie {
int (*map_irq)(const struct pci_dev *, u8, u8);
bool need_ob_cfg;
struct iproc_pcie_ob ob;
struct iproc_msi *msi;
};
int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res);
int iproc_pcie_remove(struct iproc_pcie *pcie);
#ifdef CONFIG_PCIE_IPROC_MSI
int iproc_msi_init(struct iproc_pcie *pcie, struct device_node *node);
void iproc_msi_exit(struct iproc_pcie *pcie);
#else
static inline int iproc_msi_init(struct iproc_pcie *pcie,
struct device_node *node)
{
return -ENODEV;
}
static inline void iproc_msi_exit(struct iproc_pcie *pcie)
{
}
#endif
#endif /* _PCIE_IPROC_H */
/*
* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
* Copyright 2015 Linaro Limited.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/phy/phy.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/types.h>
#include "pcie-designware.h"
#define PCIE20_PARF_PHY_CTRL 0x40
#define PCIE20_PARF_PHY_REFCLK 0x4C
#define PCIE20_PARF_DBI_BASE_ADDR 0x168
#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16c
#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178
#define PCIE20_ELBI_SYS_CTRL 0x04
#define PCIE20_ELBI_SYS_CTRL_LT_ENABLE BIT(0)
#define PCIE20_CAP 0x70
#define PERST_DELAY_US 1000
struct qcom_pcie_resources_v0 {
struct clk *iface_clk;
struct clk *core_clk;
struct clk *phy_clk;
struct reset_control *pci_reset;
struct reset_control *axi_reset;
struct reset_control *ahb_reset;
struct reset_control *por_reset;
struct reset_control *phy_reset;
struct regulator *vdda;
struct regulator *vdda_phy;
struct regulator *vdda_refclk;
};
struct qcom_pcie_resources_v1 {
struct clk *iface;
struct clk *aux;
struct clk *master_bus;
struct clk *slave_bus;
struct reset_control *core;
struct regulator *vdda;
};
union qcom_pcie_resources {
struct qcom_pcie_resources_v0 v0;
struct qcom_pcie_resources_v1 v1;
};
struct qcom_pcie;
struct qcom_pcie_ops {
int (*get_resources)(struct qcom_pcie *pcie);
int (*init)(struct qcom_pcie *pcie);
void (*deinit)(struct qcom_pcie *pcie);
};
struct qcom_pcie {
struct pcie_port pp;
struct device *dev;
union qcom_pcie_resources res;
void __iomem *parf;
void __iomem *dbi;
void __iomem *elbi;
struct phy *phy;
struct gpio_desc *reset;
struct qcom_pcie_ops *ops;
};
#define to_qcom_pcie(x) container_of(x, struct qcom_pcie, pp)
static void qcom_ep_reset_assert(struct qcom_pcie *pcie)
{
gpiod_set_value(pcie->reset, 1);
usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500);
}
static void qcom_ep_reset_deassert(struct qcom_pcie *pcie)
{
gpiod_set_value(pcie->reset, 0);
usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500);
}
static irqreturn_t qcom_pcie_msi_irq_handler(int irq, void *arg)
{
struct pcie_port *pp = arg;
return dw_handle_msi_irq(pp);
}
static int qcom_pcie_establish_link(struct qcom_pcie *pcie)
{
struct device *dev = pcie->dev;
unsigned int retries = 0;
u32 val;
if (dw_pcie_link_up(&pcie->pp))
return 0;
/* enable link training */
val = readl(pcie->elbi + PCIE20_ELBI_SYS_CTRL);
val |= PCIE20_ELBI_SYS_CTRL_LT_ENABLE;
writel(val, pcie->elbi + PCIE20_ELBI_SYS_CTRL);
do {
if (dw_pcie_link_up(&pcie->pp))
return 0;
usleep_range(250, 1000);
} while (retries < 200);
dev_warn(dev, "phy link never came up\n");
return -ETIMEDOUT;
}
static int qcom_pcie_get_resources_v0(struct qcom_pcie *pcie)
{
struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
struct device *dev = pcie->dev;
res->vdda = devm_regulator_get(dev, "vdda");
if (IS_ERR(res->vdda))
return PTR_ERR(res->vdda);
res->vdda_phy = devm_regulator_get(dev, "vdda_phy");
if (IS_ERR(res->vdda_phy))
return PTR_ERR(res->vdda_phy);
res->vdda_refclk = devm_regulator_get(dev, "vdda_refclk");
if (IS_ERR(res->vdda_refclk))
return PTR_ERR(res->vdda_refclk);
res->iface_clk = devm_clk_get(dev, "iface");
if (IS_ERR(res->iface_clk))
return PTR_ERR(res->iface_clk);
res->core_clk = devm_clk_get(dev, "core");
if (IS_ERR(res->core_clk))
return PTR_ERR(res->core_clk);
res->phy_clk = devm_clk_get(dev, "phy");
if (IS_ERR(res->phy_clk))
return PTR_ERR(res->phy_clk);
res->pci_reset = devm_reset_control_get(dev, "pci");
if (IS_ERR(res->pci_reset))
return PTR_ERR(res->pci_reset);
res->axi_reset = devm_reset_control_get(dev, "axi");
if (IS_ERR(res->axi_reset))
return PTR_ERR(res->axi_reset);
res->ahb_reset = devm_reset_control_get(dev, "ahb");
if (IS_ERR(res->ahb_reset))
return PTR_ERR(res->ahb_reset);
res->por_reset = devm_reset_control_get(dev, "por");
if (IS_ERR(res->por_reset))
return PTR_ERR(res->por_reset);
res->phy_reset = devm_reset_control_get(dev, "phy");
if (IS_ERR(res->phy_reset))
return PTR_ERR(res->phy_reset);
return 0;
}
static int qcom_pcie_get_resources_v1(struct qcom_pcie *pcie)
{
struct qcom_pcie_resources_v1 *res = &pcie->res.v1;
struct device *dev = pcie->dev;
res->vdda = devm_regulator_get(dev, "vdda");
if (IS_ERR(res->vdda))
return PTR_ERR(res->vdda);
res->iface = devm_clk_get(dev, "iface");
if (IS_ERR(res->iface))
return PTR_ERR(res->iface);
res->aux = devm_clk_get(dev, "aux");
if (IS_ERR(res->aux))
return PTR_ERR(res->aux);
res->master_bus = devm_clk_get(dev, "master_bus");
if (IS_ERR(res->master_bus))
return PTR_ERR(res->master_bus);
res->slave_bus = devm_clk_get(dev, "slave_bus");
if (IS_ERR(res->slave_bus))
return PTR_ERR(res->slave_bus);
res->core = devm_reset_control_get(dev, "core");
if (IS_ERR(res->core))
return PTR_ERR(res->core);
return 0;
}
static void qcom_pcie_deinit_v0(struct qcom_pcie *pcie)
{
struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
reset_control_assert(res->pci_reset);
reset_control_assert(res->axi_reset);
reset_control_assert(res->ahb_reset);
reset_control_assert(res->por_reset);
reset_control_assert(res->pci_reset);
clk_disable_unprepare(res->iface_clk);
clk_disable_unprepare(res->core_clk);
clk_disable_unprepare(res->phy_clk);
regulator_disable(res->vdda);
regulator_disable(res->vdda_phy);
regulator_disable(res->vdda_refclk);
}
static int qcom_pcie_init_v0(struct qcom_pcie *pcie)
{
struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
struct device *dev = pcie->dev;
u32 val;
int ret;
ret = regulator_enable(res->vdda);
if (ret) {
dev_err(dev, "cannot enable vdda regulator\n");
return ret;
}
ret = regulator_enable(res->vdda_refclk);
if (ret) {
dev_err(dev, "cannot enable vdda_refclk regulator\n");
goto err_refclk;
}
ret = regulator_enable(res->vdda_phy);
if (ret) {
dev_err(dev, "cannot enable vdda_phy regulator\n");
goto err_vdda_phy;
}
ret = reset_control_assert(res->ahb_reset);
if (ret) {
dev_err(dev, "cannot assert ahb reset\n");
goto err_assert_ahb;
}
ret = clk_prepare_enable(res->iface_clk);
if (ret) {
dev_err(dev, "cannot prepare/enable iface clock\n");
goto err_assert_ahb;
}
ret = clk_prepare_enable(res->phy_clk);
if (ret) {
dev_err(dev, "cannot prepare/enable phy clock\n");
goto err_clk_phy;
}
ret = clk_prepare_enable(res->core_clk);
if (ret) {
dev_err(dev, "cannot prepare/enable core clock\n");
goto err_clk_core;
}
ret = reset_control_deassert(res->ahb_reset);
if (ret) {
dev_err(dev, "cannot deassert ahb reset\n");
goto err_deassert_ahb;
}
/* enable PCIe clocks and resets */
val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL);
val &= ~BIT(0);
writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL);
/* enable external reference clock */
val = readl(pcie->parf + PCIE20_PARF_PHY_REFCLK);
val |= BIT(16);
writel(val, pcie->parf + PCIE20_PARF_PHY_REFCLK);
ret = reset_control_deassert(res->phy_reset);
if (ret) {
dev_err(dev, "cannot deassert phy reset\n");
return ret;
}
ret = reset_control_deassert(res->pci_reset);
if (ret) {
dev_err(dev, "cannot deassert pci reset\n");
return ret;
}
ret = reset_control_deassert(res->por_reset);
if (ret) {
dev_err(dev, "cannot deassert por reset\n");
return ret;
}
ret = reset_control_deassert(res->axi_reset);
if (ret) {
dev_err(dev, "cannot deassert axi reset\n");
return ret;
}
/* wait for clock acquisition */
usleep_range(1000, 1500);
return 0;
err_deassert_ahb:
clk_disable_unprepare(res->core_clk);
err_clk_core:
clk_disable_unprepare(res->phy_clk);
err_clk_phy:
clk_disable_unprepare(res->iface_clk);
err_assert_ahb:
regulator_disable(res->vdda_phy);
err_vdda_phy:
regulator_disable(res->vdda_refclk);
err_refclk:
regulator_disable(res->vdda);
return ret;
}
static void qcom_pcie_deinit_v1(struct qcom_pcie *pcie)
{
struct qcom_pcie_resources_v1 *res = &pcie->res.v1;
reset_control_assert(res->core);
clk_disable_unprepare(res->slave_bus);
clk_disable_unprepare(res->master_bus);
clk_disable_unprepare(res->iface);
clk_disable_unprepare(res->aux);
regulator_disable(res->vdda);
}
static int qcom_pcie_init_v1(struct qcom_pcie *pcie)
{
struct qcom_pcie_resources_v1 *res = &pcie->res.v1;
struct device *dev = pcie->dev;
int ret;
ret = reset_control_deassert(res->core);
if (ret) {
dev_err(dev, "cannot deassert core reset\n");
return ret;
}
ret = clk_prepare_enable(res->aux);
if (ret) {
dev_err(dev, "cannot prepare/enable aux clock\n");
goto err_res;
}
ret = clk_prepare_enable(res->iface);
if (ret) {
dev_err(dev, "cannot prepare/enable iface clock\n");
goto err_aux;
}
ret = clk_prepare_enable(res->master_bus);
if (ret) {
dev_err(dev, "cannot prepare/enable master_bus clock\n");
goto err_iface;
}
ret = clk_prepare_enable(res->slave_bus);
if (ret) {
dev_err(dev, "cannot prepare/enable slave_bus clock\n");
goto err_master;
}
ret = regulator_enable(res->vdda);
if (ret) {
dev_err(dev, "cannot enable vdda regulator\n");
goto err_slave;
}
/* change DBI base address */
writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR);
if (IS_ENABLED(CONFIG_PCI_MSI)) {
u32 val = readl(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT);
val |= BIT(31);
writel(val, pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT);
}
return 0;
err_slave:
clk_disable_unprepare(res->slave_bus);
err_master:
clk_disable_unprepare(res->master_bus);
err_iface:
clk_disable_unprepare(res->iface);
err_aux:
clk_disable_unprepare(res->aux);
err_res:
reset_control_assert(res->core);
return ret;
}
static int qcom_pcie_link_up(struct pcie_port *pp)
{
struct qcom_pcie *pcie = to_qcom_pcie(pp);
u16 val = readw(pcie->dbi + PCIE20_CAP + PCI_EXP_LNKSTA);
return !!(val & PCI_EXP_LNKSTA_DLLLA);
}
static void qcom_pcie_host_init(struct pcie_port *pp)
{
struct qcom_pcie *pcie = to_qcom_pcie(pp);
int ret;
qcom_ep_reset_assert(pcie);
ret = pcie->ops->init(pcie);
if (ret)
goto err_deinit;
ret = phy_power_on(pcie->phy);
if (ret)
goto err_deinit;
dw_pcie_setup_rc(pp);
if (IS_ENABLED(CONFIG_PCI_MSI))
dw_pcie_msi_init(pp);
qcom_ep_reset_deassert(pcie);
ret = qcom_pcie_establish_link(pcie);
if (ret)
goto err;
return;
err:
qcom_ep_reset_assert(pcie);
phy_power_off(pcie->phy);
err_deinit:
pcie->ops->deinit(pcie);
}
static int qcom_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
u32 *val)
{
/* the device class is not reported correctly from the register */
if (where == PCI_CLASS_REVISION && size == 4) {
*val = readl(pp->dbi_base + PCI_CLASS_REVISION);
*val &= 0xff; /* keep revision id */
*val |= PCI_CLASS_BRIDGE_PCI << 16;
return PCIBIOS_SUCCESSFUL;
}
return dw_pcie_cfg_read(pp->dbi_base + where, size, val);
}
static struct pcie_host_ops qcom_pcie_dw_ops = {
.link_up = qcom_pcie_link_up,
.host_init = qcom_pcie_host_init,
.rd_own_conf = qcom_pcie_rd_own_conf,
};
static const struct qcom_pcie_ops ops_v0 = {
.get_resources = qcom_pcie_get_resources_v0,
.init = qcom_pcie_init_v0,
.deinit = qcom_pcie_deinit_v0,
};
static const struct qcom_pcie_ops ops_v1 = {
.get_resources = qcom_pcie_get_resources_v1,
.init = qcom_pcie_init_v1,
.deinit = qcom_pcie_deinit_v1,
};
static int qcom_pcie_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
struct qcom_pcie *pcie;
struct pcie_port *pp;
int ret;
pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
if (!pcie)
return -ENOMEM;
pcie->ops = (struct qcom_pcie_ops *)of_device_get_match_data(dev);
pcie->dev = dev;
pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_LOW);
if (IS_ERR(pcie->reset))
return PTR_ERR(pcie->reset);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf");
pcie->parf = devm_ioremap_resource(dev, res);
if (IS_ERR(pcie->parf))
return PTR_ERR(pcie->parf);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
pcie->dbi = devm_ioremap_resource(dev, res);
if (IS_ERR(pcie->dbi))
return PTR_ERR(pcie->dbi);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi");
pcie->elbi = devm_ioremap_resource(dev, res);
if (IS_ERR(pcie->elbi))
return PTR_ERR(pcie->elbi);
pcie->phy = devm_phy_optional_get(dev, "pciephy");
if (IS_ERR(pcie->phy))
return PTR_ERR(pcie->phy);
ret = pcie->ops->get_resources(pcie);
if (ret)
return ret;
pp = &pcie->pp;
pp->dev = dev;
pp->dbi_base = pcie->dbi;
pp->root_bus_nr = -1;
pp->ops = &qcom_pcie_dw_ops;
if (IS_ENABLED(CONFIG_PCI_MSI)) {
pp->msi_irq = platform_get_irq_byname(pdev, "msi");
if (pp->msi_irq < 0)
return pp->msi_irq;
ret = devm_request_irq(dev, pp->msi_irq,
qcom_pcie_msi_irq_handler,
IRQF_SHARED, "qcom-pcie-msi", pp);
if (ret) {
dev_err(dev, "cannot request msi irq\n");
return ret;
}
}
ret = phy_init(pcie->phy);
if (ret)
return ret;
ret = dw_pcie_host_init(pp);
if (ret) {
dev_err(dev, "cannot initialize host\n");
return ret;
}
platform_set_drvdata(pdev, pcie);
return 0;
}
static int qcom_pcie_remove(struct platform_device *pdev)
{
struct qcom_pcie *pcie = platform_get_drvdata(pdev);
qcom_ep_reset_assert(pcie);
phy_power_off(pcie->phy);
phy_exit(pcie->phy);
pcie->ops->deinit(pcie);
return 0;
}
static const struct of_device_id qcom_pcie_match[] = {
{ .compatible = "qcom,pcie-ipq8064", .data = &ops_v0 },
{ .compatible = "qcom,pcie-apq8064", .data = &ops_v0 },
{ .compatible = "qcom,pcie-apq8084", .data = &ops_v1 },
{ }
};
MODULE_DEVICE_TABLE(of, qcom_pcie_match);
static struct platform_driver qcom_pcie_driver = {
.probe = qcom_pcie_probe,
.remove = qcom_pcie_remove,
.driver = {
.name = "qcom-pcie",
.of_match_table = qcom_pcie_match,
},
};
module_platform_driver(qcom_pcie_driver);
MODULE_AUTHOR("Stanimir Varbanov <svarbanov@mm-sol.com>");
MODULE_DESCRIPTION("Qualcomm PCIe root complex driver");
MODULE_LICENSE("GPL v2");
......@@ -26,6 +26,7 @@
#include <linux/of_platform.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#define DRV_NAME "rcar-pcie"
......@@ -94,6 +95,11 @@
#define H1_PCIEPHYDOUTR 0x040014
#define H1_PCIEPHYSR 0x040018
/* R-Car Gen2 PHY */
#define GEN2_PCIEPHYADDR 0x780
#define GEN2_PCIEPHYDATA 0x784
#define GEN2_PCIEPHYCTRL 0x78c
#define INT_PCI_MSI_NR 32
#define RCONF(x) (PCICONF(0)+(x))
......@@ -108,8 +114,6 @@
#define RCAR_PCI_MAX_RESOURCES 4
#define MAX_NR_INBOUND_MAPS 6
static unsigned long global_io_offset;
struct rcar_msi {
DECLARE_BITMAP(used, INT_PCI_MSI_NR);
struct irq_domain *domain;
......@@ -126,20 +130,10 @@ static inline struct rcar_msi *to_rcar_msi(struct msi_controller *chip)
}
/* Structure representing the PCIe interface */
/*
* ARM pcibios functions expect the ARM struct pci_sys_data as the PCI
* sysdata. Add pci_sys_data as the first element in struct gen_pci so
* that when we use a gen_pci pointer as sysdata, it is also a pointer to
* a struct pci_sys_data.
*/
struct rcar_pcie {
#ifdef CONFIG_ARM
struct pci_sys_data sys;
#endif
struct device *dev;
void __iomem *base;
struct resource res[RCAR_PCI_MAX_RESOURCES];
struct resource busn;
struct list_head resources;
int root_bus_nr;
struct clk *clk;
struct clk *bus_clk;
......@@ -323,10 +317,9 @@ static struct pci_ops rcar_pcie_ops = {
.write = rcar_pcie_write_conf,
};
static void rcar_pcie_setup_window(int win, struct rcar_pcie *pcie)
static void rcar_pcie_setup_window(int win, struct rcar_pcie *pcie,
struct resource *res)
{
struct resource *res = &pcie->res[win];
/* Setup PCIe address space mappings for each resource */
resource_size_t size;
resource_size_t res_start;
......@@ -359,31 +352,33 @@ static void rcar_pcie_setup_window(int win, struct rcar_pcie *pcie)
rcar_pci_write_reg(pcie, mask, PCIEPTCTLR(win));
}
static int rcar_pcie_setup(struct list_head *resource, struct rcar_pcie *pcie)
static int rcar_pcie_setup(struct list_head *resource, struct rcar_pcie *pci)
{
struct resource *res;
int i;
pcie->root_bus_nr = pcie->busn.start;
struct resource_entry *win;
int i = 0;
/* Setup PCI resources */
for (i = 0; i < RCAR_PCI_MAX_RESOURCES; i++) {
resource_list_for_each_entry(win, &pci->resources) {
struct resource *res = win->res;
res = &pcie->res[i];
if (!res->flags)
continue;
rcar_pcie_setup_window(i, pcie);
if (res->flags & IORESOURCE_IO) {
phys_addr_t io_start = pci_pio_to_address(res->start);
pci_ioremap_io(global_io_offset, io_start);
global_io_offset += SZ_64K;
switch (resource_type(res)) {
case IORESOURCE_IO:
case IORESOURCE_MEM:
rcar_pcie_setup_window(i, pci, res);
i++;
break;
case IORESOURCE_BUS:
pci->root_bus_nr = res->start;
break;
default:
continue;
}
pci_add_resource(resource, res);
}
pci_add_resource(resource, &pcie->busn);
return 1;
}
......@@ -578,6 +573,26 @@ static int rcar_pcie_hw_init_h1(struct rcar_pcie *pcie)
return -ETIMEDOUT;
}
static int rcar_pcie_hw_init_gen2(struct rcar_pcie *pcie)
{
/*
* These settings come from the R-Car Series, 2nd Generation User's
* Manual, section 50.3.1 (2) Initialization of the physical layer.
*/
rcar_pci_write_reg(pcie, 0x000f0030, GEN2_PCIEPHYADDR);
rcar_pci_write_reg(pcie, 0x00381203, GEN2_PCIEPHYDATA);
rcar_pci_write_reg(pcie, 0x00000001, GEN2_PCIEPHYCTRL);
rcar_pci_write_reg(pcie, 0x00000006, GEN2_PCIEPHYCTRL);
rcar_pci_write_reg(pcie, 0x000f0054, GEN2_PCIEPHYADDR);
/* The following value is for DC connection, no termination resistor */
rcar_pci_write_reg(pcie, 0x13802007, GEN2_PCIEPHYDATA);
rcar_pci_write_reg(pcie, 0x00000001, GEN2_PCIEPHYCTRL);
rcar_pci_write_reg(pcie, 0x00000006, GEN2_PCIEPHYCTRL);
return rcar_pcie_hw_init(pcie);
}
static int rcar_msi_alloc(struct rcar_msi *chip)
{
int msi;
......@@ -720,14 +735,16 @@ static int rcar_pcie_enable_msi(struct rcar_pcie *pcie)
/* Two irqs are for MSI, but they are also used for non-MSI irqs */
err = devm_request_irq(&pdev->dev, msi->irq1, rcar_pcie_msi_irq,
IRQF_SHARED, rcar_msi_irq_chip.name, pcie);
IRQF_SHARED | IRQF_NO_THREAD,
rcar_msi_irq_chip.name, pcie);
if (err < 0) {
dev_err(&pdev->dev, "failed to request IRQ: %d\n", err);
goto err;
}
err = devm_request_irq(&pdev->dev, msi->irq2, rcar_pcie_msi_irq,
IRQF_SHARED, rcar_msi_irq_chip.name, pcie);
IRQF_SHARED | IRQF_NO_THREAD,
rcar_msi_irq_chip.name, pcie);
if (err < 0) {
dev_err(&pdev->dev, "failed to request IRQ: %d\n", err);
goto err;
......@@ -917,20 +934,71 @@ static int rcar_pcie_parse_map_dma_ranges(struct rcar_pcie *pcie,
static const struct of_device_id rcar_pcie_of_match[] = {
{ .compatible = "renesas,pcie-r8a7779", .data = rcar_pcie_hw_init_h1 },
{ .compatible = "renesas,pcie-r8a7790", .data = rcar_pcie_hw_init },
{ .compatible = "renesas,pcie-r8a7791", .data = rcar_pcie_hw_init },
{ .compatible = "renesas,pcie-rcar-gen2", .data = rcar_pcie_hw_init_gen2 },
{ .compatible = "renesas,pcie-r8a7790", .data = rcar_pcie_hw_init_gen2 },
{ .compatible = "renesas,pcie-r8a7791", .data = rcar_pcie_hw_init_gen2 },
{ .compatible = "renesas,pcie-r8a7795", .data = rcar_pcie_hw_init },
{},
};
MODULE_DEVICE_TABLE(of, rcar_pcie_of_match);
static void rcar_pcie_release_of_pci_ranges(struct rcar_pcie *pci)
{
pci_free_resource_list(&pci->resources);
}
static int rcar_pcie_parse_request_of_pci_ranges(struct rcar_pcie *pci)
{
int err;
struct device *dev = pci->dev;
struct device_node *np = dev->of_node;
resource_size_t iobase;
struct resource_entry *win;
err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pci->resources, &iobase);
if (err)
return err;
resource_list_for_each_entry(win, &pci->resources) {
struct resource *parent, *res = win->res;
switch (resource_type(res)) {
case IORESOURCE_IO:
parent = &ioport_resource;
err = pci_remap_iospace(res, iobase);
if (err) {
dev_warn(dev, "error %d: failed to map resource %pR\n",
err, res);
continue;
}
break;
case IORESOURCE_MEM:
parent = &iomem_resource;
break;
case IORESOURCE_BUS:
default:
continue;
}
err = devm_request_resource(dev, parent, res);
if (err)
goto out_release_res;
}
return 0;
out_release_res:
rcar_pcie_release_of_pci_ranges(pci);
return err;
}
static int rcar_pcie_probe(struct platform_device *pdev)
{
struct rcar_pcie *pcie;
unsigned int data;
struct of_pci_range range;
struct of_pci_range_parser parser;
const struct of_device_id *of_id;
int err, win = 0;
int err;
int (*hw_init_fn)(struct rcar_pcie *);
pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
......@@ -940,16 +1008,9 @@ static int rcar_pcie_probe(struct platform_device *pdev)
pcie->dev = &pdev->dev;
platform_set_drvdata(pdev, pcie);
/* Get the bus range */
if (of_pci_parse_bus_range(pdev->dev.of_node, &pcie->busn)) {
dev_err(&pdev->dev, "failed to parse bus-range property\n");
return -EINVAL;
}
INIT_LIST_HEAD(&pcie->resources);
if (of_pci_range_parser_init(&parser, pdev->dev.of_node)) {
dev_err(&pdev->dev, "missing ranges property\n");
return -EINVAL;
}
rcar_pcie_parse_request_of_pci_ranges(pcie);
err = rcar_pcie_get_resources(pdev, pcie);
if (err < 0) {
......@@ -957,46 +1018,55 @@ static int rcar_pcie_probe(struct platform_device *pdev)
return err;
}
for_each_of_pci_range(&parser, &range) {
err = of_pci_range_to_resource(&range, pdev->dev.of_node,
&pcie->res[win++]);
if (err < 0)
return err;
if (win > RCAR_PCI_MAX_RESOURCES)
break;
}
err = rcar_pcie_parse_map_dma_ranges(pcie, pdev->dev.of_node);
if (err)
return err;
if (IS_ENABLED(CONFIG_PCI_MSI)) {
err = rcar_pcie_enable_msi(pcie);
if (err < 0) {
dev_err(&pdev->dev,
"failed to enable MSI support: %d\n",
err);
return err;
}
}
of_id = of_match_device(rcar_pcie_of_match, pcie->dev);
if (!of_id || !of_id->data)
return -EINVAL;
hw_init_fn = of_id->data;
pm_runtime_enable(pcie->dev);
err = pm_runtime_get_sync(pcie->dev);
if (err < 0) {
dev_err(pcie->dev, "pm_runtime_get_sync failed\n");
goto err_pm_disable;
}
/* Failure to get a link might just be that no cards are inserted */
err = hw_init_fn(pcie);
if (err) {
dev_info(&pdev->dev, "PCIe link down\n");
return 0;
err = 0;
goto err_pm_put;
}
data = rcar_pci_read_reg(pcie, MACSR);
dev_info(&pdev->dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f);
return rcar_pcie_enable(pcie);
if (IS_ENABLED(CONFIG_PCI_MSI)) {
err = rcar_pcie_enable_msi(pcie);
if (err < 0) {
dev_err(&pdev->dev,
"failed to enable MSI support: %d\n",
err);
goto err_pm_put;
}
}
err = rcar_pcie_enable(pcie);
if (err)
goto err_pm_put;
return 0;
err_pm_put:
pm_runtime_put(pcie->dev);
err_pm_disable:
pm_runtime_disable(pcie->dev);
return err;
}
static struct platform_driver rcar_pcie_driver = {
......
......@@ -279,7 +279,8 @@ static int spear13xx_add_pcie_port(struct pcie_port *pp,
return -ENODEV;
}
ret = devm_request_irq(dev, pp->irq, spear13xx_pcie_irq_handler,
IRQF_SHARED, "spear1340-pcie", pp);
IRQF_SHARED | IRQF_NO_THREAD,
"spear1340-pcie", pp);
if (ret) {
dev_err(dev, "failed to request irq %d\n", pp->irq);
return ret;
......
......@@ -781,7 +781,8 @@ static int xilinx_pcie_parse_dt(struct xilinx_pcie_port *port)
port->irq = irq_of_parse_and_map(node, 0);
err = devm_request_irq(dev, port->irq, xilinx_pcie_intr_handler,
IRQF_SHARED, "xilinx-pcie", port);
IRQF_SHARED | IRQF_NO_THREAD,
"xilinx-pcie", port);
if (err) {
dev_err(dev, "unable to request irq %d\n", port->irq);
return err;
......
......@@ -36,10 +36,10 @@
#define MY_NAME "acpi_pcihp"
#define dbg(fmt, arg...) do { if (debug_acpi) printk(KERN_DEBUG "%s: %s: " fmt , MY_NAME , __func__ , ## arg); } while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg)
#define dbg(fmt, arg...) do { if (debug_acpi) printk(KERN_DEBUG "%s: %s: " fmt, MY_NAME, __func__, ## arg); } while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME, ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME, ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME, ## arg)
#define METHOD_NAME__SUN "_SUN"
#define METHOD_NAME_OSHP "OSHP"
......@@ -132,7 +132,7 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev, u32 flags)
while (handle) {
acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
dbg("Trying to get hotplug control for %s \n",
dbg("Trying to get hotplug control for %s\n",
(char *)string.pointer);
status = acpi_run_oshp(handle);
if (ACPI_SUCCESS(status))
......
......@@ -181,7 +181,7 @@ struct acpiphp_attention_info
/* function prototypes */
/* acpiphp_core.c */
int acpiphp_register_attention(struct acpiphp_attention_info*info);
int acpiphp_register_attention(struct acpiphp_attention_info *info);
int acpiphp_unregister_attention(struct acpiphp_attention_info *info);
int acpiphp_register_hotplug_slot(struct acpiphp_slot *slot, unsigned int sun);
void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *slot);
......
......@@ -63,13 +63,13 @@ MODULE_LICENSE("GPL");
MODULE_PARM_DESC(disable, "disable acpiphp driver");
module_param_named(disable, acpiphp_disabled, bool, 0444);
static int enable_slot (struct hotplug_slot *slot);
static int disable_slot (struct hotplug_slot *slot);
static int set_attention_status (struct hotplug_slot *slot, u8 value);
static int get_power_status (struct hotplug_slot *slot, u8 *value);
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
static int enable_slot(struct hotplug_slot *slot);
static int disable_slot(struct hotplug_slot *slot);
static int set_attention_status(struct hotplug_slot *slot, u8 value);
static int get_power_status(struct hotplug_slot *slot, u8 *value);
static int get_attention_status(struct hotplug_slot *slot, u8 *value);
static int get_latch_status(struct hotplug_slot *slot, u8 *value);
static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
static struct hotplug_slot_ops acpi_hotplug_slot_ops = {
.enable_slot = enable_slot,
......
......@@ -707,7 +707,7 @@ static void acpiphp_sanitize_bus(struct pci_bus *bus)
unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM;
list_for_each_entry_safe_reverse(dev, tmp, &bus->devices, bus_list) {
for (i=0; i<PCI_BRIDGE_RESOURCES; i++) {
for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) {
struct resource *res = &dev->resource[i];
if ((res->flags & type_mask) && !res->start &&
res->end) {
......
......@@ -154,7 +154,8 @@ static union apci_descriptor *ibm_slot_from_id(int id)
ibm_slot_done:
if (ret) {
ret = kmalloc(sizeof(union apci_descriptor), GFP_KERNEL);
memcpy(ret, des, sizeof(union apci_descriptor));
if (ret)
memcpy(ret, des, sizeof(union apci_descriptor));
}
kfree(table);
return ret;
......@@ -175,8 +176,13 @@ static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status)
acpi_status stat;
unsigned long long rc;
union apci_descriptor *ibm_slot;
int id = hpslot_to_sun(slot);
ibm_slot = ibm_slot_from_id(hpslot_to_sun(slot));
ibm_slot = ibm_slot_from_id(id);
if (!ibm_slot) {
pr_err("APLS null ACPI descriptor for slot %d\n", id);
return -ENODEV;
}
pr_debug("%s: set slot %d (%d) attention status to %d\n", __func__,
ibm_slot->slot.slot_num, ibm_slot->slot.slot_id,
......@@ -215,8 +221,13 @@ static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status)
static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status)
{
union apci_descriptor *ibm_slot;
int id = hpslot_to_sun(slot);
ibm_slot = ibm_slot_from_id(hpslot_to_sun(slot));
ibm_slot = ibm_slot_from_id(id);
if (!ibm_slot) {
pr_err("APLS null ACPI descriptor for slot %d\n", id);
return -ENODEV;
}
if (ibm_slot->slot.attn & 0xa0 || ibm_slot->slot.status[1] & 0x08)
*status = 1;
......@@ -325,7 +336,7 @@ static int ibm_get_table_from_acpi(char **bufp)
}
size = 0;
for (i=0; i<package->package.count; i++) {
for (i = 0; i < package->package.count; i++) {
memcpy(&lbuf[size],
package->package.elements[i].buffer.pointer,
package->package.elements[i].buffer.length);
......
......@@ -52,13 +52,13 @@ struct slot {
};
struct cpci_hp_controller_ops {
int (*query_enum) (void);
int (*enable_irq) (void);
int (*disable_irq) (void);
int (*check_irq) (void *dev_id);
int (*hardware_test) (struct slot *slot, u32 value);
u8 (*get_power) (struct slot *slot);
int (*set_power) (struct slot *slot, int value);
int (*query_enum)(void);
int (*enable_irq)(void);
int (*disable_irq)(void);
int (*check_irq)(void *dev_id);
int (*hardware_test)(struct slot *slot, u32 value);
u8 (*get_power)(struct slot *slot);
int (*set_power)(struct slot *slot, int value);
};
struct cpci_hp_controller {
......
......@@ -45,12 +45,12 @@
#define dbg(format, arg...) \
do { \
if (cpci_debug) \
printk (KERN_DEBUG "%s: " format "\n", \
MY_NAME , ## arg); \
printk(KERN_DEBUG "%s: " format "\n", \
MY_NAME, ## arg); \
} while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME, ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME, ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME, ## arg)
/* local variables */
static DECLARE_RWSEM(list_rwsem);
......@@ -238,21 +238,21 @@ cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
* with the pci_hotplug subsystem.
*/
for (i = first; i <= last; ++i) {
slot = kzalloc(sizeof (struct slot), GFP_KERNEL);
slot = kzalloc(sizeof(struct slot), GFP_KERNEL);
if (!slot) {
status = -ENOMEM;
goto error;
}
hotplug_slot =
kzalloc(sizeof (struct hotplug_slot), GFP_KERNEL);
kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
if (!hotplug_slot) {
status = -ENOMEM;
goto error_slot;
}
slot->hotplug_slot = hotplug_slot;
info = kzalloc(sizeof (struct hotplug_slot_info), GFP_KERNEL);
info = kzalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
if (!info) {
status = -ENOMEM;
goto error_hpslot;
......
......@@ -38,12 +38,12 @@ extern int cpci_debug;
#define dbg(format, arg...) \
do { \
if (cpci_debug) \
printk (KERN_DEBUG "%s: " format "\n", \
MY_NAME , ## arg); \
printk(KERN_DEBUG "%s: " format "\n", \
MY_NAME, ## arg); \
} while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME, ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME, ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME, ## arg)
u8 cpci_get_attention_status(struct slot *slot)
......
......@@ -54,12 +54,12 @@
#define dbg(format, arg...) \
do { \
if (debug) \
printk (KERN_DEBUG "%s: " format "\n", \
MY_NAME , ## arg); \
printk(KERN_DEBUG "%s: " format "\n", \
MY_NAME, ## arg); \
} while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME, ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME, ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME, ## arg)
/* local variables */
static bool debug;
......@@ -164,7 +164,7 @@ static int __init cpcihp_generic_init(void)
bus = dev->subordinate;
pci_dev_put(dev);
memset(&generic_hpc, 0, sizeof (struct cpci_hp_controller));
memset(&generic_hpc, 0, sizeof(struct cpci_hp_controller));
generic_hpc_ops.query_enum = query_enum;
generic_hpc.ops = &generic_hpc_ops;
......
......@@ -49,12 +49,12 @@
#define dbg(format, arg...) \
do { \
if (debug) \
printk (KERN_DEBUG "%s: " format "\n", \
MY_NAME , ## arg); \
printk(KERN_DEBUG "%s: " format "\n", \
MY_NAME, ## arg); \
} while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME, ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME, ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME, ## arg)
/* local variables */
static bool debug;
......@@ -204,7 +204,7 @@ static int zt5550_hc_disable_irq(void)
return 0;
}
static int zt5550_hc_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
static int zt5550_hc_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
int status;
......@@ -214,7 +214,7 @@ static int zt5550_hc_init_one (struct pci_dev *pdev, const struct pci_device_id
dbg("returned from zt5550_hc_config");
memset(&zt5550_hpc, 0, sizeof (struct cpci_hp_controller));
memset(&zt5550_hpc, 0, sizeof(struct cpci_hp_controller));
zt5550_hpc_ops.query_enum = zt5550_hc_query_enum;
zt5550_hpc.ops = &zt5550_hpc_ops;
if (!poll) {
......
......@@ -36,10 +36,10 @@
#define MY_NAME "cpqphp"
#define dbg(fmt, arg...) do { if (cpqhp_debug) printk(KERN_DEBUG "%s: " fmt , MY_NAME , ## arg); } while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg)
#define dbg(fmt, arg...) do { if (cpqhp_debug) printk(KERN_DEBUG "%s: " fmt, MY_NAME, ## arg); } while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME, ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME, ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME, ## arg)
......@@ -424,7 +424,7 @@ int cpqhp_process_SS(struct controller *ctrl, struct pci_func *func);
int cpqhp_hardware_test(struct controller *ctrl, int test_num);
/* resource functions */
int cpqhp_resource_sort_and_combine (struct pci_resource **head);
int cpqhp_resource_sort_and_combine(struct pci_resource **head);
/* pci functions */
int cpqhp_set_irq(u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num);
......@@ -685,7 +685,7 @@ static inline int cpq_get_latch_status(struct controller *ctrl,
u8 hp_slot;
hp_slot = slot->device - ctrl->slot_device_offset;
dbg("%s: slot->device = %d, ctrl->slot_device_offset = %d \n",
dbg("%s: slot->device = %d, ctrl->slot_device_offset = %d\n",
__func__, slot->device, ctrl->slot_device_offset);
status = (readl(ctrl->hpc_reg + INT_INPUT_CLEAR) & (0x01L << hp_slot));
......@@ -712,7 +712,7 @@ static inline int get_presence_status(struct controller *ctrl,
static inline int wait_for_ctrl_irq(struct controller *ctrl)
{
DECLARE_WAITQUEUE(wait, current);
DECLARE_WAITQUEUE(wait, current);
int retval = 0;
dbg("%s - start\n", __func__);
......
......@@ -291,7 +291,7 @@ static void release_slot(struct hotplug_slot *hotplug_slot)
kfree(slot);
}
static int ctrl_slot_cleanup (struct controller *ctrl)
static int ctrl_slot_cleanup(struct controller *ctrl)
{
struct slot *old_slot, *next_slot;
......@@ -301,7 +301,7 @@ static int ctrl_slot_cleanup (struct controller *ctrl)
while (old_slot) {
/* memory will be freed by the release_slot callback */
next_slot = old_slot->next;
pci_hp_deregister (old_slot->hotplug_slot);
pci_hp_deregister(old_slot->hotplug_slot);
old_slot = next_slot;
}
......@@ -413,9 +413,9 @@ cpqhp_set_attention_status(struct controller *ctrl, struct pci_func *func,
mutex_lock(&ctrl->crit_sect);
if (status == 1)
amber_LED_on (ctrl, hp_slot);
amber_LED_on(ctrl, hp_slot);
else if (status == 0)
amber_LED_off (ctrl, hp_slot);
amber_LED_off(ctrl, hp_slot);
else {
/* Done with exclusive hardware access */
mutex_unlock(&ctrl->crit_sect);
......@@ -425,7 +425,7 @@ cpqhp_set_attention_status(struct controller *ctrl, struct pci_func *func,
set_SOGO(ctrl);
/* Wait for SOBS to be unset */
wait_for_ctrl_irq (ctrl);
wait_for_ctrl_irq(ctrl);
/* Done with exclusive hardware access */
mutex_unlock(&ctrl->crit_sect);
......@@ -439,7 +439,7 @@ cpqhp_set_attention_status(struct controller *ctrl, struct pci_func *func,
* @hotplug_slot: slot to change LED on
* @status: LED control flag
*/
static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status)
static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
{
struct pci_func *slot_func;
struct slot *slot = hotplug_slot->private;
......@@ -610,7 +610,7 @@ static int ctrl_slot_setup(struct controller *ctrl,
u8 ctrl_slot;
u32 tempdword;
char name[SLOT_NAME_SIZE];
void __iomem *slot_entry= NULL;
void __iomem *slot_entry = NULL;
int result;
dbg("%s\n", __func__);
......@@ -755,7 +755,7 @@ static int one_time_init(void)
if (cpqhp_debug)
pci_print_IRQ_route();
dbg("Initialize + Start the notification mechanism \n");
dbg("Initialize + Start the notification mechanism\n");
retval = cpqhp_event_start_thread();
if (retval)
......@@ -772,7 +772,7 @@ static int one_time_init(void)
/* Map rom address */
cpqhp_rom_start = ioremap(ROM_PHY_ADDR, ROM_PHY_LEN);
if (!cpqhp_rom_start) {
err ("Could not ioremap memory region for ROM\n");
err("Could not ioremap memory region for ROM\n");
retval = -EIO;
goto error;
}
......@@ -786,7 +786,7 @@ static int one_time_init(void)
smbios_table = detect_SMBIOS_pointer(cpqhp_rom_start,
cpqhp_rom_start + ROM_PHY_LEN);
if (!smbios_table) {
err ("Could not find the SMBIOS pointer in memory\n");
err("Could not find the SMBIOS pointer in memory\n");
retval = -EIO;
goto error_rom_start;
}
......@@ -794,7 +794,7 @@ static int one_time_init(void)
smbios_start = ioremap(readl(smbios_table + ST_ADDRESS),
readw(smbios_table + ST_LENGTH));
if (!smbios_start) {
err ("Could not ioremap memory region taken from SMBIOS values\n");
err("Could not ioremap memory region taken from SMBIOS values\n");
retval = -EIO;
goto error_smbios_start;
}
......@@ -1181,7 +1181,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
* Finish setting up the hot plug ctrl device
*/
ctrl->slot_device_offset = readb(ctrl->hpc_reg + SLOT_MASK) >> 4;
dbg("NumSlots %d \n", ctrl->slot_device_offset);
dbg("NumSlots %d\n", ctrl->slot_device_offset);
ctrl->next_event = 0;
......@@ -1198,7 +1198,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
writel(0xFFFFFFFFL, ctrl->hpc_reg + INT_MASK);
/* set up the interrupt */
dbg("HPC interrupt = %d \n", ctrl->interrupt);
dbg("HPC interrupt = %d\n", ctrl->interrupt);
if (request_irq(ctrl->interrupt, cpqhp_ctrl_intr,
IRQF_SHARED, MY_NAME, ctrl)) {
err("Can't get irq %d for the hotplug pci controller\n",
......@@ -1321,7 +1321,7 @@ static void __exit unload_cpqphpd(void)
while (ctrl) {
if (ctrl->hpc_reg) {
u16 misc;
rc = read_slot_enable (ctrl);
rc = read_slot_enable(ctrl);
writeb(0, ctrl->hpc_reg + SLOT_SERR);
writel(0xFFFFFFC0L | ~rc, ctrl->hpc_reg + INT_MASK);
......@@ -1361,7 +1361,7 @@ static void __exit unload_cpqphpd(void)
kfree(tres);
}
kfree (ctrl->pci_bus);
kfree(ctrl->pci_bus);
tctrl = ctrl;
ctrl = ctrl->next;
......@@ -1446,7 +1446,7 @@ static int __init cpqhpc_init(void)
cpqhp_debug = debug;
info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
cpqhp_initialize_debugfs();
result = pci_register_driver(&cpqhpc_driver);
dbg("pci_register_driver = %d\n", result);
......
此差异已折叠。
......@@ -114,10 +114,10 @@ static u32 add_byte(u32 **p_buffer, u8 value, u32 *used, u32 *avail)
if ((*used + 1) > *avail)
return(1);
*((u8*)*p_buffer) = value;
tByte = (u8**)p_buffer;
*((u8 *)*p_buffer) = value;
tByte = (u8 **)p_buffer;
(*tByte)++;
*used+=1;
*used += 1;
return(0);
}
......@@ -129,7 +129,7 @@ static u32 add_dword(u32 **p_buffer, u32 value, u32 *used, u32 *avail)
**p_buffer = value;
(*p_buffer)++;
*used+=4;
*used += 4;
return(0);
}
......@@ -141,7 +141,7 @@ static u32 add_dword(u32 **p_buffer, u32 value, u32 *used, u32 *avail)
*
* returns 0 for non-Compaq ROM, 1 for Compaq ROM
*/
static int check_for_compaq_ROM (void __iomem *rom_start)
static int check_for_compaq_ROM(void __iomem *rom_start)
{
u8 temp1, temp2, temp3, temp4, temp5, temp6;
int result = 0;
......@@ -160,12 +160,12 @@ static int check_for_compaq_ROM (void __iomem *rom_start)
(temp6 == 'Q')) {
result = 1;
}
dbg ("%s - returned %d\n", __func__, result);
dbg("%s - returned %d\n", __func__, result);
return result;
}
static u32 access_EV (u16 operation, u8 *ev_name, u8 *buffer, u32 *buf_size)
static u32 access_EV(u16 operation, u8 *ev_name, u8 *buffer, u32 *buf_size)
{
unsigned long flags;
int op = operation;
......@@ -197,7 +197,7 @@ static u32 access_EV (u16 operation, u8 *ev_name, u8 *buffer, u32 *buf_size)
*
* Read the hot plug Resource Table from NVRAM
*/
static int load_HRT (void __iomem *rom_start)
static int load_HRT(void __iomem *rom_start)
{
u32 available;
u32 temp_dword;
......@@ -232,7 +232,7 @@ static int load_HRT (void __iomem *rom_start)
*
* Save the hot plug Resource Table in NVRAM
*/
static u32 store_HRT (void __iomem *rom_start)
static u32 store_HRT(void __iomem *rom_start)
{
u32 *buffer;
u32 *pFill;
......@@ -252,7 +252,7 @@ static u32 store_HRT (void __iomem *rom_start)
if (!check_for_compaq_ROM(rom_start))
return(1);
buffer = (u32*) evbuffer;
buffer = (u32 *) evbuffer;
if (!buffer)
return(1);
......@@ -306,7 +306,7 @@ static u32 store_HRT (void __iomem *rom_start)
loop = 0;
while (resNode) {
loop ++;
loop++;
/* base */
rc = add_dword(&pFill, resNode->base, &usedbytes, &available);
......@@ -331,7 +331,7 @@ static u32 store_HRT (void __iomem *rom_start)
loop = 0;
while (resNode) {
loop ++;
loop++;
/* base */
rc = add_dword(&pFill, resNode->base, &usedbytes, &available);
......@@ -356,7 +356,7 @@ static u32 store_HRT (void __iomem *rom_start)
loop = 0;
while (resNode) {
loop ++;
loop++;
/* base */
rc = add_dword(&pFill, resNode->base, &usedbytes, &available);
......@@ -381,7 +381,7 @@ static u32 store_HRT (void __iomem *rom_start)
loop = 0;
while (resNode) {
loop ++;
loop++;
/* base */
rc = add_dword(&pFill, resNode->base, &usedbytes, &available);
......@@ -408,7 +408,7 @@ static u32 store_HRT (void __iomem *rom_start)
temp_dword = usedbytes;
rc = access_EV(WRITE_EV, "CQTHPS", (u8*) buffer, &temp_dword);
rc = access_EV(WRITE_EV, "CQTHPS", (u8 *) buffer, &temp_dword);
dbg("usedbytes = 0x%x, length = 0x%x\n", usedbytes, temp_dword);
......@@ -423,7 +423,7 @@ static u32 store_HRT (void __iomem *rom_start)
}
void compaq_nvram_init (void __iomem *rom_start)
void compaq_nvram_init(void __iomem *rom_start)
{
if (rom_start)
compaq_int15_entry_point = (rom_start + ROM_INT15_PHY_ADDR - ROM_PHY_ADDR);
......@@ -435,7 +435,7 @@ void compaq_nvram_init (void __iomem *rom_start)
}
int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
int compaq_nvram_load(void __iomem *rom_start, struct controller *ctrl)
{
u8 bus, device, function;
u8 nummem, numpmem, numio, numbus;
......@@ -451,7 +451,7 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
if (!evbuffer_init) {
/* Read the resource list information in from NVRAM */
if (load_HRT(rom_start))
memset (evbuffer, 0, 1024);
memset(evbuffer, 0, 1024);
evbuffer_init = 1;
}
......@@ -472,7 +472,7 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
p_byte += 3;
if (p_byte > ((u8*)p_EV_header + evbuffer_length))
if (p_byte > ((u8 *)p_EV_header + evbuffer_length))
return 2;
bus = p_ev_ctrl->bus;
......@@ -489,20 +489,20 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
p_byte += 4;
if (p_byte > ((u8*)p_EV_header + evbuffer_length))
if (p_byte > ((u8 *)p_EV_header + evbuffer_length))
return 2;
/* Skip forward to the next entry */
p_byte += (nummem + numpmem + numio + numbus) * 8;
if (p_byte > ((u8*)p_EV_header + evbuffer_length))
if (p_byte > ((u8 *)p_EV_header + evbuffer_length))
return 2;
p_ev_ctrl = (struct ev_hrt_ctrl *) p_byte;
p_byte += 3;
if (p_byte > ((u8*)p_EV_header + evbuffer_length))
if (p_byte > ((u8 *)p_EV_header + evbuffer_length))
return 2;
bus = p_ev_ctrl->bus;
......@@ -517,7 +517,7 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
p_byte += 4;
if (p_byte > ((u8*)p_EV_header + evbuffer_length))
if (p_byte > ((u8 *)p_EV_header + evbuffer_length))
return 2;
while (nummem--) {
......@@ -526,20 +526,20 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
if (!mem_node)
break;
mem_node->base = *(u32*)p_byte;
dbg("mem base = %8.8x\n",mem_node->base);
mem_node->base = *(u32 *)p_byte;
dbg("mem base = %8.8x\n", mem_node->base);
p_byte += 4;
if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) {
kfree(mem_node);
return 2;
}
mem_node->length = *(u32*)p_byte;
dbg("mem length = %8.8x\n",mem_node->length);
mem_node->length = *(u32 *)p_byte;
dbg("mem length = %8.8x\n", mem_node->length);
p_byte += 4;
if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) {
kfree(mem_node);
return 2;
}
......@@ -554,20 +554,20 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
if (!p_mem_node)
break;
p_mem_node->base = *(u32*)p_byte;
dbg("pre-mem base = %8.8x\n",p_mem_node->base);
p_mem_node->base = *(u32 *)p_byte;
dbg("pre-mem base = %8.8x\n", p_mem_node->base);
p_byte += 4;
if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) {
kfree(p_mem_node);
return 2;
}
p_mem_node->length = *(u32*)p_byte;
dbg("pre-mem length = %8.8x\n",p_mem_node->length);
p_mem_node->length = *(u32 *)p_byte;
dbg("pre-mem length = %8.8x\n", p_mem_node->length);
p_byte += 4;
if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) {
kfree(p_mem_node);
return 2;
}
......@@ -582,20 +582,20 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
if (!io_node)
break;
io_node->base = *(u32*)p_byte;
dbg("io base = %8.8x\n",io_node->base);
io_node->base = *(u32 *)p_byte;
dbg("io base = %8.8x\n", io_node->base);
p_byte += 4;
if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) {
kfree(io_node);
return 2;
}
io_node->length = *(u32*)p_byte;
dbg("io length = %8.8x\n",io_node->length);
io_node->length = *(u32 *)p_byte;
dbg("io length = %8.8x\n", io_node->length);
p_byte += 4;
if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) {
kfree(io_node);
return 2;
}
......@@ -610,18 +610,18 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
if (!bus_node)
break;
bus_node->base = *(u32*)p_byte;
bus_node->base = *(u32 *)p_byte;
p_byte += 4;
if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) {
kfree(bus_node);
return 2;
}
bus_node->length = *(u32*)p_byte;
bus_node->length = *(u32 *)p_byte;
p_byte += 4;
if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) {
kfree(bus_node);
return 2;
}
......@@ -650,7 +650,7 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
}
int compaq_nvram_store (void __iomem *rom_start)
int compaq_nvram_store(void __iomem *rom_start)
{
int rc = 1;
......
此差异已折叠。
......@@ -39,7 +39,7 @@
#include "cpqphp.h"
static DEFINE_MUTEX(cpqphp_mutex);
static int show_ctrl (struct controller *ctrl, char *buf)
static int show_ctrl(struct controller *ctrl, char *buf)
{
char *out = buf;
int index;
......@@ -77,7 +77,7 @@ static int show_ctrl (struct controller *ctrl, char *buf)
return out - buf;
}
static int show_dev (struct controller *ctrl, char *buf)
static int show_dev(struct controller *ctrl, char *buf)
{
char *out = buf;
int index;
......@@ -119,7 +119,7 @@ static int show_dev (struct controller *ctrl, char *buf)
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
slot=slot->next;
slot = slot->next;
}
return out - buf;
......
......@@ -39,11 +39,11 @@ extern int ibmphp_debug;
#else
#define MY_NAME THIS_MODULE->name
#endif
#define debug(fmt, arg...) do { if (ibmphp_debug == 1) printk(KERN_DEBUG "%s: " fmt , MY_NAME , ## arg); } while (0)
#define debug_pci(fmt, arg...) do { if (ibmphp_debug) printk(KERN_DEBUG "%s: " fmt , MY_NAME , ## arg); } while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg)
#define debug(fmt, arg...) do { if (ibmphp_debug == 1) printk(KERN_DEBUG "%s: " fmt, MY_NAME, ## arg); } while (0)
#define debug_pci(fmt, arg...) do { if (ibmphp_debug) printk(KERN_DEBUG "%s: " fmt, MY_NAME, ## arg); } while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME, ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME, ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME, ## arg)
/* EBDA stuff */
......@@ -603,7 +603,7 @@ void ibmphp_hpc_stop_poll_thread(void);
#define SLOT_CONNECT(s) ((u8) ((s & HPC_SLOT_CONNECT) \
? HPC_SLOT_DISCONNECTED : HPC_SLOT_CONNECTED))
#define SLOT_ATTN(s,es) ((u8) ((es & HPC_SLOT_BLINK_ATTN) \
#define SLOT_ATTN(s, es) ((u8) ((es & HPC_SLOT_BLINK_ATTN) \
? HPC_SLOT_ATTN_BLINK \
: ((s & HPC_SLOT_ATTN) ? HPC_SLOT_ATTN_ON : HPC_SLOT_ATTN_OFF)))
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册