Commit c334f9c8 authored by Bjorn Helgaas's avatar Bjorn Helgaas

Merge branches 'pci/host-altera', 'pci/host-imx6', 'pci/host-keystone',...

Merge branches 'pci/host-altera', 'pci/host-imx6', 'pci/host-keystone', 'pci/host-rcar', 'pci/host-tegra', 'pci/host-thunder', 'pci/host-vmd', 'pci/host-xilinx' and 'pci/host-xilinx-nwl' into next

* pci/host-altera:
  PCI: altera: Fix altera_pcie_link_is_up()

* pci/host-imx6:
  PCI: imx6: Add DT bindings to configure PHY Tx driver settings

* pci/host-keystone:
  PCI: keystone: Defer probing if devm_phy_get() returns -EPROBE_DEFER

* pci/host-rcar:
  PCI: rcar: Depend on ARCH_RENESAS, not ARCH_SHMOBILE

* pci/host-tegra:
  PCI: tegra: Remove misleading PHYS_OFFSET
  PCI: tegra: Track bus -> CPU mapping
  PCI: tegra: Remove unused struct tegra_pcie.num_ports field
  PCI: tegra: Implement ->{add,remove}_bus() callbacks
  PCI: Add pci_ops.{add,remove}_bus() callbacks

* pci/host-thunder:
  PCI: thunder: Add driver for ThunderX-pass{1,2} on-chip devices
  PCI: thunder: Add PCIe host driver for ThunderX processors
  PCI: generic: Expose pci_host_common_probe() for use by other drivers
  PCI: generic: Add pci_host_common_probe(), based on gen_pci_probe()
  PCI: generic: Move structure definitions to separate header file

* pci/host-vmd:
  x86/PCI: VMD: Attach VMD resources to parent domain's resource tree
  x86/PCI: VMD: Set bus resource start to 0
  x86/PCI: VMD: Document code for maintainability

* pci/host-xilinx:
  microblaze/PCI: Support generic Xilinx AXI PCIe Host Bridge IP driver
  PCI: xilinx: Update Zynq binding with Microblaze node
  PCI: xilinx: Don't call pci_fixup_irqs() on Microblaze
  PCI: xilinx: Remove dependency on ARM-specific struct hw_pci
  PCI: xilinx: Use of_pci_get_host_bridge_resources() to parse DT

* pci/host-xilinx-nwl:
  PCI: xilinx-nwl: Add support for Xilinx NWL PCIe Host Controller
......@@ -13,6 +13,13 @@ Required properties:
- clock-names: Must include the following additional entries:
- "pcie_phy"
Optional properties:
- fsl,tx-deemph-gen1: Gen1 De-emphasis value. Default: 0
- fsl,tx-deemph-gen2-3p5db: Gen2 (3.5db) De-emphasis value. Default: 0
- fsl,tx-deemph-gen2-6db: Gen2 (6db) De-emphasis value. Default: 20
- fsl,tx-swing-full: Gen2 TX SWING FULL value. Default: 127
- fsl,tx-swing-low: TX launch amplitude swing_low value. Default: 127
Example:
pcie@0x01000000 {
......
* ThunderX PCI host controller for pass-1.x silicon
Firmware-initialized PCI host controller to on-chip devices found on
some Cavium ThunderX processors. These devices have ECAM-based config
access, but the BARs are all at fixed addresses. We handle the fixed
addresses by synthesizing Enhanced Allocation (EA) capabilities for
these devices.
The properties and their meanings are identical to those described in
host-generic-pci.txt except as listed below.
Properties of the host controller node that differ from
host-generic-pci.txt:
- compatible : Must be "cavium,pci-host-thunder-ecam"
Example:
pcie@84b000000000 {
compatible = "cavium,pci-host-thunder-ecam";
device_type = "pci";
msi-parent = <&its>;
msi-map = <0 &its 0x30000 0x10000>;
bus-range = <0 31>;
#size-cells = <2>;
#address-cells = <3>;
#stream-id-cells = <1>;
reg = <0x84b0 0x00000000 0 0x02000000>; /* Configuration space */
ranges = <0x03000000 0x8180 0x00000000 0x8180 0x00000000 0x80 0x00000000>; /* mem ranges */
};
* ThunderX PEM PCIe host controller
Firmware-initialized PCI host controller found on some Cavium
ThunderX processors.
The properties and their meanings are identical to those described in
host-generic-pci.txt except as listed below.
Properties of the host controller node that differ from
host-generic-pci.txt:
- compatible : Must be "cavium,pci-host-thunder-pem"
- reg : Two entries: First the configuration space for down
stream devices base address and size, as accessed
from the parent bus. Second, the register bank of
the PEM device PCIe bridge.
Example:
pci@87e0,c2000000 {
compatible = "cavium,pci-host-thunder-pem";
device_type = "pci";
msi-parent = <&its>;
msi-map = <0 &its 0x10000 0x10000>;
bus-range = <0x8f 0xc7>;
#size-cells = <2>;
#address-cells = <3>;
reg = <0x8880 0x8f000000 0x0 0x39000000>, /* Configuration space */
<0x87e0 0xc2000000 0x0 0x00010000>; /* PEM space */
ranges = <0x01000000 0x00 0x00020000 0x88b0 0x00020000 0x00 0x00010000>, /* I/O */
<0x03000000 0x00 0x10000000 0x8890 0x10000000 0x0f 0xf0000000>, /* mem64 */
<0x43000000 0x10 0x00000000 0x88a0 0x00000000 0x10 0x00000000>, /* mem64-pref */
<0x03000000 0x87e0 0xc2f00000 0x87e0 0xc2000000 0x00 0x00100000>; /* mem64 PEM BAR4 */
#interrupt-cells = <1>;
interrupt-map-mask = <0 0 0 7>;
interrupt-map = <0 0 0 1 &gic0 0 0 0 24 4>, /* INTA */
<0 0 0 2 &gic0 0 0 0 25 4>, /* INTB */
<0 0 0 3 &gic0 0 0 0 26 4>, /* INTC */
<0 0 0 4 &gic0 0 0 0 27 4>; /* INTD */
};
* Xilinx NWL PCIe Root Port Bridge DT description
Required properties:
- compatible: Should contain "xlnx,nwl-pcie-2.11"
- #address-cells: Address representation for root ports, set to <3>
- #size-cells: Size representation for root ports, set to <2>
- #interrupt-cells: specifies the number of cells needed to encode an
interrupt source. The value must be 1.
- reg: Should contain Bridge, PCIe Controller registers location,
configuration space, and length
- reg-names: Must include the following entries:
"breg": bridge registers
"pcireg": PCIe controller registers
"cfg": configuration space region
- device_type: must be "pci"
- interrupts: Should contain NWL PCIe interrupt
- interrupt-names: Must include the following entries:
"msi1, msi0": interrupt asserted when MSI is received
"intx": interrupt asserted when a legacy interrupt is received
"misc": interrupt asserted when miscellaneous is received
- interrupt-map-mask and interrupt-map: standard PCI properties to define the
mapping of the PCI interface to interrupt numbers.
- ranges: ranges for the PCI memory regions (I/O space region is not
supported by hardware)
Please refer to the standard PCI bus binding document for a more
detailed explanation
- msi-controller: indicates that this is MSI controller node
- msi-parent: MSI parent of the root complex itself
- legacy-interrupt-controller: Interrupt controller device node for Legacy interrupts
- interrupt-controller: identifies the node as an interrupt controller
- #interrupt-cells: should be set to 1
- #address-cells: specifies the number of cells needed to encode an
address. The value must be 0.
Example:
++++++++
nwl_pcie: pcie@fd0e0000 {
#address-cells = <3>;
#size-cells = <2>;
compatible = "xlnx,nwl-pcie-2.11";
#interrupt-cells = <1>;
msi-controller;
device_type = "pci";
interrupt-parent = <&gic>;
interrupts = <0 114 4>, <0 115 4>, <0 116 4>, <0 117 4>, <0 118 4>;
interrupt-names = "msi0", "msi1", "intx", "dummy", "misc";
interrupt-map-mask = <0x0 0x0 0x0 0x7>;
interrupt-map = <0x0 0x0 0x0 0x1 &pcie_intc 0x1>,
<0x0 0x0 0x0 0x2 &pcie_intc 0x2>,
<0x0 0x0 0x0 0x3 &pcie_intc 0x3>,
<0x0 0x0 0x0 0x4 &pcie_intc 0x4>;
msi-parent = <&nwl_pcie>;
reg = <0x0 0xfd0e0000 0x0 0x1000>,
<0x0 0xfd480000 0x0 0x1000>,
<0x0 0xe0000000 0x0 0x1000000>;
reg-names = "breg", "pcireg", "cfg";
ranges = <0x02000000 0x00000000 0xe1000000 0x00000000 0xe1000000 0 0x0f000000>;
pcie_intc: legacy-interrupt-controller {
interrupt-controller;
#address-cells = <0>;
#interrupt-cells = <1>;
};
};
......@@ -17,7 +17,7 @@ Required properties:
Please refer to the standard PCI bus binding document for a more
detailed explanation
Optional properties:
Optional properties for Zynq/Microblaze:
- bus-range: PCI bus numbers covered
Interrupt controller child node
......@@ -38,13 +38,13 @@ the four INTx interrupts in ISR and route them to this domain.
Example:
++++++++
Zynq:
pci_express: axi-pcie@50000000 {
#address-cells = <3>;
#size-cells = <2>;
#interrupt-cells = <1>;
compatible = "xlnx,axi-pcie-host-1.00.a";
reg = < 0x50000000 0x10000000 >;
reg = < 0x50000000 0x1000000 >;
device_type = "pci";
interrupts = < 0 52 4 >;
interrupt-map-mask = <0 0 0 7>;
......@@ -60,3 +60,29 @@ Example:
#interrupt-cells = <1>;
};
};
Microblaze:
pci_express: axi-pcie@10000000 {
#address-cells = <3>;
#size-cells = <2>;
#interrupt-cells = <1>;
compatible = "xlnx,axi-pcie-host-1.00.a";
reg = <0x10000000 0x4000000>;
device_type = "pci";
interrupt-parent = <&microblaze_0_intc>;
interrupts = <1 2>;
interrupt-map-mask = <0 0 0 7>;
interrupt-map = <0 0 0 1 &pcie_intc 1>,
<0 0 0 2 &pcie_intc 2>,
<0 0 0 3 &pcie_intc 3>,
<0 0 0 4 &pcie_intc 4>;
ranges = <0x02000000 0x00000000 0x80000000 0x80000000 0x00000000 0x10000000>;
pcie_intc: interrupt-controller {
interrupt-controller;
#address-cells = <0>;
#interrupt-cells = <1>;
};
};
......@@ -8373,6 +8373,7 @@ L: linux-pci@vger.kernel.org
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: Documentation/devicetree/bindings/pci/host-generic-pci.txt
F: drivers/pci/host/pci-host-common.c
F: drivers/pci/host/pci-host-generic.c
PCI DRIVER FOR INTEL VOLUME MANAGEMENT DEVICE (VMD)
......@@ -8418,6 +8419,14 @@ L: linux-arm-msm@vger.kernel.org
S: Maintained
F: drivers/pci/host/*qcom*
PCIE DRIVER FOR CAVIUM THUNDERX
M: David Daney <david.daney@cavium.com>
L: linux-pci@vger.kernel.org
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Supported
F: Documentation/devicetree/bindings/pci/pci-thunder-*
F: drivers/pci/host/pci-thunder-*
PCMCIA SUBSYSTEM
P: Linux PCMCIA Team
L: linux-pcmcia@lists.infradead.org
......
......@@ -267,6 +267,9 @@ config PCI
config PCI_DOMAINS
def_bool PCI
config PCI_DOMAINS_GENERIC
def_bool PCI_DOMAINS
config PCI_SYSCALL
def_bool PCI
......
......@@ -123,17 +123,6 @@ unsigned long pci_address_to_pio(phys_addr_t address)
}
EXPORT_SYMBOL_GPL(pci_address_to_pio);
/*
* Return the domain number for this bus.
*/
int pci_domain_nr(struct pci_bus *bus)
{
struct pci_controller *hose = pci_bus_to_host(bus);
return hose->global_number;
}
EXPORT_SYMBOL(pci_domain_nr);
/* This routine is meant to be used early during boot, when the
* PCI bus numbers have not yet been assigned, and you need to
* issue PCI config cycles to an OF device.
......@@ -863,26 +852,10 @@ void pcibios_setup_bus_devices(struct pci_bus *bus)
void pcibios_fixup_bus(struct pci_bus *bus)
{
/* When called from the generic PCI probe, read PCI<->PCI bridge
* bases. This is -not- called when generating the PCI tree from
* the OF device-tree.
*/
if (bus->self != NULL)
pci_read_bridge_bases(bus);
/* Now fixup the bus bus */
pcibios_setup_bus_self(bus);
/* Now fixup devices on that bus */
pcibios_setup_bus_devices(bus);
/* nothing to do */
}
EXPORT_SYMBOL(pcibios_fixup_bus);
static int skip_isa_ioresource_align(struct pci_dev *dev)
{
return 0;
}
/*
* We need to avoid collisions with `mirrored' VGA ports
* and other strange ISA hardware, so we always want the
......@@ -899,20 +872,18 @@ static int skip_isa_ioresource_align(struct pci_dev *dev)
resource_size_t pcibios_align_resource(void *data, const struct resource *res,
resource_size_t size, resource_size_t align)
{
struct pci_dev *dev = data;
resource_size_t start = res->start;
if (res->flags & IORESOURCE_IO) {
if (skip_isa_ioresource_align(dev))
return start;
if (start & 0x300)
start = (start + 0x3ff) & ~0x3ff;
}
return start;
return res->start;
}
EXPORT_SYMBOL(pcibios_align_resource);
int pcibios_add_device(struct pci_dev *dev)
{
dev->irq = of_irq_parse_and_map_pci(dev, 0, 0);
return 0;
}
EXPORT_SYMBOL(pcibios_add_device);
/*
* Reparent resource children of pr that conflict with res
* under res, and make res replace those children.
......@@ -1333,13 +1304,6 @@ static void pcibios_setup_phb_resources(struct pci_controller *hose,
(unsigned long)hose->io_base_virt - _IO_BASE);
}
struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus)
{
struct pci_controller *hose = bus->sysdata;
return of_node_get(hose->dn);
}
static void pcibios_scan_phb(struct pci_controller *hose)
{
LIST_HEAD(resources);
......
......@@ -503,6 +503,18 @@ static struct pci_ops vmd_ops = {
.write = vmd_pci_write,
};
static void vmd_attach_resources(struct vmd_dev *vmd)
{
vmd->dev->resource[VMD_MEMBAR1].child = &vmd->resources[1];
vmd->dev->resource[VMD_MEMBAR2].child = &vmd->resources[2];
}
static void vmd_detach_resources(struct vmd_dev *vmd)
{
vmd->dev->resource[VMD_MEMBAR1].child = NULL;
vmd->dev->resource[VMD_MEMBAR2].child = NULL;
}
/*
* VMD domains start at 0x1000 to not clash with ACPI _SEG domains.
*/
......@@ -527,11 +539,28 @@ static int vmd_enable_domain(struct vmd_dev *vmd)
res = &vmd->dev->resource[VMD_CFGBAR];
vmd->resources[0] = (struct resource) {
.name = "VMD CFGBAR",
.start = res->start,
.start = 0,
.end = (resource_size(res) >> 20) - 1,
.flags = IORESOURCE_BUS | IORESOURCE_PCI_FIXED,
};
/*
* If the window is below 4GB, clear IORESOURCE_MEM_64 so we can
* put 32-bit resources in the window.
*
* There's no hardware reason why a 64-bit window *couldn't*
* contain a 32-bit resource, but pbus_size_mem() computes the
* bridge window size assuming a 64-bit window will contain no
* 32-bit resources. __pci_assign_resource() enforces that
* artificial restriction to make sure everything will fit.
*
* The only way we could use a 64-bit non-prefechable MEMBAR is
* if its address is <4GB so that we can convert it to a 32-bit
* resource. To be visible to the host OS, all VMD endpoints must
* be initially configured by platform BIOS, which includes setting
* up these resources. We can assume the device is configured
* according to the platform needs.
*/
res = &vmd->dev->resource[VMD_MEMBAR1];
upper_bits = upper_32_bits(res->end);
flags = res->flags & ~IORESOURCE_SIZEALIGN;
......@@ -542,6 +571,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd)
.start = res->start,
.end = res->end,
.flags = flags,
.parent = res,
};
res = &vmd->dev->resource[VMD_MEMBAR2];
......@@ -554,6 +584,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd)
.start = res->start + 0x2000,
.end = res->end,
.flags = flags,
.parent = res,
};
sd->domain = vmd_find_free_domain();
......@@ -578,6 +609,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd)
return -ENODEV;
}
vmd_attach_resources(vmd);
vmd_setup_dma_ops(vmd);
dev_set_msi_domain(&vmd->bus->dev, vmd->irq_domain);
pci_rescan_bus(vmd->bus);
......@@ -674,6 +706,7 @@ static void vmd_remove(struct pci_dev *dev)
{
struct vmd_dev *vmd = pci_get_drvdata(dev);
vmd_detach_resources(vmd);
pci_set_drvdata(dev, NULL);
sysfs_remove_link(&vmd->dev->dev.kobj, "domain");
pci_stop_root_bus(vmd->bus);
......
......@@ -16,6 +16,16 @@ config PCI_MVEBU
depends on ARCH_MVEBU || ARCH_DOVE
depends on OF
config PCIE_XILINX_NWL
bool "NWL PCIe Core"
depends on ARCH_ZYNQMP
select PCI_MSI_IRQ_DOMAIN if PCI_MSI
help
Say 'Y' here if you want kernel support for Xilinx
NWL PCIe controller. The controller can act as Root Port
or End Point. The current option selection will only
support root port enabling.
config PCIE_DW
bool
......@@ -41,7 +51,7 @@ config PCI_TEGRA
config PCI_RCAR_GEN2
bool "Renesas R-Car Gen2 Internal PCI controller"
depends on ARM
depends on ARCH_SHMOBILE || COMPILE_TEST
depends on ARCH_RENESAS || COMPILE_TEST
help
Say Y here if you want internal PCI support on R-Car Gen2 SoC.
There are 3 internal PCI controllers available with a single
......@@ -49,13 +59,17 @@ config PCI_RCAR_GEN2
config PCI_RCAR_GEN2_PCIE
bool "Renesas R-Car PCIe controller"
depends on ARCH_SHMOBILE || (ARM && COMPILE_TEST)
depends on ARCH_RENESAS || (ARM && COMPILE_TEST)
help
Say Y here if you want PCIe controller support on R-Car Gen2 SoCs.
config PCI_HOST_COMMON
bool
config PCI_HOST_GENERIC
bool "Generic PCI host controller"
depends on (ARM || ARM64) && OF
select PCI_HOST_COMMON
help
Say Y here if you want to support a simple generic PCI host
controller, such as the one emulated by kvmtool.
......@@ -81,7 +95,7 @@ config PCI_KEYSTONE
config PCIE_XILINX
bool "Xilinx AXI PCIe host bridge support"
depends on ARCH_ZYNQ
depends on ARCH_ZYNQ || MICROBLAZE
help
Say 'Y' here if you want kernel to support the Xilinx AXI PCIe
Host Bridge driver.
......@@ -191,4 +205,18 @@ config PCIE_QCOM
PCIe controller uses the Designware core plus Qualcomm-specific
hardware wrappers.
config PCI_HOST_THUNDER_PEM
bool "Cavium Thunder PCIe controller to off-chip devices"
depends on OF && ARM64
select PCI_HOST_COMMON
help
Say Y here if you want PCIe support for CN88XX Cavium Thunder SoCs.
config PCI_HOST_THUNDER_ECAM
bool "Cavium Thunder ECAM controller to on-chip devices on pass-1.x silicon"
depends on OF && ARM64
select PCI_HOST_COMMON
help
Say Y here if you want ECAM support for CN88XX-Pass-1.x Cavium Thunder SoCs.
endmenu
......@@ -6,10 +6,12 @@ obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o
obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o
obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
obj-$(CONFIG_PCI_RCAR_GEN2_PCIE) += pcie-rcar.o
obj-$(CONFIG_PCI_HOST_COMMON) += pci-host-common.o
obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o
obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
obj-$(CONFIG_PCIE_XILINX_NWL) += pcie-xilinx-nwl.o
obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
......@@ -22,3 +24,5 @@ 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
obj-$(CONFIG_PCI_HOST_THUNDER_ECAM) += pci-thunder-ecam.o
obj-$(CONFIG_PCI_HOST_THUNDER_PEM) += pci-thunder-pem.o
/*
* 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
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright (C) 2014 ARM Limited
*
* Author: Will Deacon <will.deacon@arm.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_pci.h>
#include <linux/platform_device.h>
#include "pci-host-common.h"
static void gen_pci_release_of_pci_ranges(struct gen_pci *pci)
{
pci_free_resource_list(&pci->resources);
}
static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci)
{
int err, res_valid = 0;
struct device *dev = pci->host.dev.parent;
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;
res_valid |= !(res->flags & IORESOURCE_PREFETCH);
break;
case IORESOURCE_BUS:
pci->cfg.bus_range = res;
default:
continue;
}
err = devm_request_resource(dev, parent, res);
if (err)
goto out_release_res;
}
if (!res_valid) {
dev_err(dev, "non-prefetchable memory resource required\n");
err = -EINVAL;
goto out_release_res;
}
return 0;
out_release_res:
gen_pci_release_of_pci_ranges(pci);
return err;
}
static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
{
int err;
u8 bus_max;
resource_size_t busn;
struct resource *bus_range;
struct device *dev = pci->host.dev.parent;
struct device_node *np = dev->of_node;
u32 sz = 1 << pci->cfg.ops->bus_shift;
err = of_address_to_resource(np, 0, &pci->cfg.res);
if (err) {
dev_err(dev, "missing \"reg\" property\n");
return err;
}
/* Limit the bus-range to fit within reg */
bus_max = pci->cfg.bus_range->start +
(resource_size(&pci->cfg.res) >> pci->cfg.ops->bus_shift) - 1;
pci->cfg.bus_range->end = min_t(resource_size_t,
pci->cfg.bus_range->end, bus_max);
pci->cfg.win = devm_kcalloc(dev, resource_size(pci->cfg.bus_range),
sizeof(*pci->cfg.win), GFP_KERNEL);
if (!pci->cfg.win)
return -ENOMEM;
/* Map our Configuration Space windows */
if (!devm_request_mem_region(dev, pci->cfg.res.start,
resource_size(&pci->cfg.res),
"Configuration Space"))
return -ENOMEM;
bus_range = pci->cfg.bus_range;
for (busn = bus_range->start; busn <= bus_range->end; ++busn) {
u32 idx = busn - bus_range->start;
pci->cfg.win[idx] = devm_ioremap(dev,
pci->cfg.res.start + idx * sz,
sz);
if (!pci->cfg.win[idx])
return -ENOMEM;
}
return 0;
}
int pci_host_common_probe(struct platform_device *pdev,
struct gen_pci *pci)
{
int err;
const char *type;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct pci_bus *bus, *child;
type = of_get_property(np, "device_type", NULL);
if (!type || strcmp(type, "pci")) {
dev_err(dev, "invalid \"device_type\" %s\n", type);
return -EINVAL;
}
of_pci_check_probe_only();
pci->host.dev.parent = dev;
INIT_LIST_HEAD(&pci->host.windows);
INIT_LIST_HEAD(&pci->resources);
/* Parse our PCI ranges and request their resources */
err = gen_pci_parse_request_of_pci_ranges(pci);
if (err)
return err;
/* Parse and map our Configuration Space windows */
err = gen_pci_parse_map_cfg_windows(pci);
if (err) {
gen_pci_release_of_pci_ranges(pci);
return err;
}
/* Do not reassign resources if probe only */
if (!pci_has_flag(PCI_PROBE_ONLY))
pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS);
bus = pci_scan_root_bus(dev, pci->cfg.bus_range->start,
&pci->cfg.ops->ops, pci, &pci->resources);
if (!bus) {
dev_err(dev, "Scanning rootbus failed");
return -ENODEV;
}
pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
if (!pci_has_flag(PCI_PROBE_ONLY)) {
pci_bus_size_bridges(bus);
pci_bus_assign_resources(bus);
list_for_each_entry(child, &bus->children, node)
pcie_bus_configure_settings(child);
}
pci_bus_add_devices(bus);
return 0;
}
MODULE_DESCRIPTION("Generic PCI host driver common code");
MODULE_AUTHOR("Will Deacon <will.deacon@arm.com>");
MODULE_LICENSE("GPL v2");
/*
* 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
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright (C) 2014 ARM Limited
*
* Author: Will Deacon <will.deacon@arm.com>
*/
#ifndef _PCI_HOST_COMMON_H
#define _PCI_HOST_COMMON_H
#include <linux/kernel.h>
#include <linux/platform_device.h>
struct gen_pci_cfg_bus_ops {
u32 bus_shift;
struct pci_ops ops;
};
struct gen_pci_cfg_windows {
struct resource res;
struct resource *bus_range;
void __iomem **win;
struct gen_pci_cfg_bus_ops *ops;
};
struct gen_pci {
struct pci_host_bridge host;
struct gen_pci_cfg_windows cfg;
struct list_head resources;
};
int pci_host_common_probe(struct platform_device *pdev,
struct gen_pci *pci);
#endif /* _PCI_HOST_COMMON_H */
......@@ -25,24 +25,7 @@
#include <linux/of_pci.h>
#include <linux/platform_device.h>
struct gen_pci_cfg_bus_ops {
u32 bus_shift;
struct pci_ops ops;
};
struct gen_pci_cfg_windows {
struct resource res;
struct resource *bus_range;
void __iomem **win;
struct gen_pci_cfg_bus_ops *ops;
};
struct gen_pci {
struct pci_host_bridge host;
struct gen_pci_cfg_windows cfg;
struct list_head resources;
};
#include "pci-host-common.h"
static void __iomem *gen_pci_map_cfg_bus_cam(struct pci_bus *bus,
unsigned int devfn,
......@@ -93,175 +76,19 @@ static const struct of_device_id gen_pci_of_match[] = {
};
MODULE_DEVICE_TABLE(of, gen_pci_of_match);
static void gen_pci_release_of_pci_ranges(struct gen_pci *pci)
{
pci_free_resource_list(&pci->resources);
}
static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci)
{
int err, res_valid = 0;
struct device *dev = pci->host.dev.parent;
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;
res_valid |= !(res->flags & IORESOURCE_PREFETCH);
break;
case IORESOURCE_BUS:
pci->cfg.bus_range = res;
default:
continue;
}
err = devm_request_resource(dev, parent, res);
if (err)
goto out_release_res;
}
if (!res_valid) {
dev_err(dev, "non-prefetchable memory resource required\n");
err = -EINVAL;
goto out_release_res;
}
return 0;
out_release_res:
gen_pci_release_of_pci_ranges(pci);
return err;
}
static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
{
int err;
u8 bus_max;
resource_size_t busn;
struct resource *bus_range;
struct device *dev = pci->host.dev.parent;
struct device_node *np = dev->of_node;
u32 sz = 1 << pci->cfg.ops->bus_shift;
err = of_address_to_resource(np, 0, &pci->cfg.res);
if (err) {
dev_err(dev, "missing \"reg\" property\n");
return err;
}
/* Limit the bus-range to fit within reg */
bus_max = pci->cfg.bus_range->start +
(resource_size(&pci->cfg.res) >> pci->cfg.ops->bus_shift) - 1;
pci->cfg.bus_range->end = min_t(resource_size_t,
pci->cfg.bus_range->end, bus_max);
pci->cfg.win = devm_kcalloc(dev, resource_size(pci->cfg.bus_range),
sizeof(*pci->cfg.win), GFP_KERNEL);
if (!pci->cfg.win)
return -ENOMEM;
/* Map our Configuration Space windows */
if (!devm_request_mem_region(dev, pci->cfg.res.start,
resource_size(&pci->cfg.res),
"Configuration Space"))
return -ENOMEM;
bus_range = pci->cfg.bus_range;
for (busn = bus_range->start; busn <= bus_range->end; ++busn) {
u32 idx = busn - bus_range->start;
pci->cfg.win[idx] = devm_ioremap(dev,
pci->cfg.res.start + idx * sz,
sz);
if (!pci->cfg.win[idx])
return -ENOMEM;
}
return 0;
}
static int gen_pci_probe(struct platform_device *pdev)
{
int err;
const char *type;
const struct of_device_id *of_id;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
const struct of_device_id *of_id;
struct gen_pci *pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
struct pci_bus *bus, *child;
if (!pci)
return -ENOMEM;
type = of_get_property(np, "device_type", NULL);
if (!type || strcmp(type, "pci")) {
dev_err(dev, "invalid \"device_type\" %s\n", type);
return -EINVAL;
}
of_pci_check_probe_only();
of_id = of_match_node(gen_pci_of_match, np);
of_id = of_match_node(gen_pci_of_match, dev->of_node);
pci->cfg.ops = (struct gen_pci_cfg_bus_ops *)of_id->data;
pci->host.dev.parent = dev;
INIT_LIST_HEAD(&pci->host.windows);
INIT_LIST_HEAD(&pci->resources);
/* Parse our PCI ranges and request their resources */
err = gen_pci_parse_request_of_pci_ranges(pci);
if (err)
return err;
/* Parse and map our Configuration Space windows */
err = gen_pci_parse_map_cfg_windows(pci);
if (err) {
gen_pci_release_of_pci_ranges(pci);
return err;
}
/* Do not reassign resources if probe only */
if (!pci_has_flag(PCI_PROBE_ONLY))
pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS);
bus = pci_scan_root_bus(dev, pci->cfg.bus_range->start,
&pci->cfg.ops->ops, pci, &pci->resources);
if (!bus) {
dev_err(dev, "Scanning rootbus failed");
return -ENODEV;
}
pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
if (!pci_has_flag(PCI_PROBE_ONLY)) {
pci_bus_size_bridges(bus);
pci_bus_assign_resources(bus);
list_for_each_entry(child, &bus->children, node)
pcie_bus_configure_settings(child);
}
pci_bus_add_devices(bus);
return 0;
return pci_host_common_probe(pdev, pci);
}
static struct platform_driver gen_pci_driver = {
......
......@@ -39,6 +39,11 @@ struct imx6_pcie {
struct pcie_port pp;
struct regmap *iomuxc_gpr;
void __iomem *mem_base;
u32 tx_deemph_gen1;
u32 tx_deemph_gen2_3p5db;
u32 tx_deemph_gen2_6db;
u32 tx_swing_full;
u32 tx_swing_low;
};
/* PCIe Root Complex registers (memory-mapped) */
......@@ -334,15 +339,20 @@ static void imx6_pcie_init_phy(struct pcie_port *pp)
IMX6Q_GPR12_LOS_LEVEL, 9 << 4);
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
IMX6Q_GPR8_TX_DEEMPH_GEN1, 0 << 0);
IMX6Q_GPR8_TX_DEEMPH_GEN1,
imx6_pcie->tx_deemph_gen1 << 0);
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB, 0 << 6);
IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB,
imx6_pcie->tx_deemph_gen2_3p5db << 6);
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB, 20 << 12);
IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB,
imx6_pcie->tx_deemph_gen2_6db << 12);
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
IMX6Q_GPR8_TX_SWING_FULL, 127 << 18);
IMX6Q_GPR8_TX_SWING_FULL,
imx6_pcie->tx_swing_full << 18);
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
IMX6Q_GPR8_TX_SWING_LOW, 127 << 25);
IMX6Q_GPR8_TX_SWING_LOW,
imx6_pcie->tx_swing_low << 25);
}
static int imx6_pcie_wait_for_link(struct pcie_port *pp)
......@@ -533,6 +543,7 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
struct imx6_pcie *imx6_pcie;
struct pcie_port *pp;
struct resource *dbi_base;
struct device_node *node = pdev->dev.of_node;
int ret;
imx6_pcie = devm_kzalloc(&pdev->dev, sizeof(*imx6_pcie), GFP_KERNEL);
......@@ -585,6 +596,27 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
return PTR_ERR(imx6_pcie->iomuxc_gpr);
}
/* Grab PCIe PHY Tx Settings */
if (of_property_read_u32(node, "fsl,tx-deemph-gen1",
&imx6_pcie->tx_deemph_gen1))
imx6_pcie->tx_deemph_gen1 = 0;
if (of_property_read_u32(node, "fsl,tx-deemph-gen2-3p5db",
&imx6_pcie->tx_deemph_gen2_3p5db))
imx6_pcie->tx_deemph_gen2_3p5db = 0;
if (of_property_read_u32(node, "fsl,tx-deemph-gen2-6db",
&imx6_pcie->tx_deemph_gen2_6db))
imx6_pcie->tx_deemph_gen2_6db = 20;
if (of_property_read_u32(node, "fsl,tx-swing-full",
&imx6_pcie->tx_swing_full))
imx6_pcie->tx_swing_full = 127;
if (of_property_read_u32(node, "fsl,tx-swing-low",
&imx6_pcie->tx_swing_low))
imx6_pcie->tx_swing_low = 127;
ret = imx6_add_pcie_port(pp, pdev);
if (ret < 0)
return ret;
......
......@@ -359,6 +359,9 @@ static int __init ks_pcie_probe(struct platform_device *pdev)
/* initialize SerDes Phy if present */
phy = devm_phy_get(dev, "pcie-phy");
if (PTR_ERR_OR_ZERO(phy) == -EPROBE_DEFER)
return PTR_ERR(phy);
if (!IS_ERR_OR_NULL(phy)) {
ret = phy_init(phy);
if (ret < 0)
......
......@@ -281,6 +281,11 @@ struct tegra_pcie {
struct resource prefetch;
struct resource busn;
struct {
resource_size_t mem;
resource_size_t io;
} offset;
struct clk *pex_clk;
struct clk *afi_clk;
struct clk *pll_e;
......@@ -295,7 +300,6 @@ struct tegra_pcie {
struct tegra_msi msi;
struct list_head ports;
unsigned int num_ports;
u32 xbar_config;
struct regulator_bulk_data *supplies;
......@@ -426,29 +430,36 @@ static struct tegra_pcie_bus *tegra_pcie_bus_alloc(struct tegra_pcie *pcie,
return ERR_PTR(err);
}
/*
* Look up a virtual address mapping for the specified bus number. If no such
* mapping exists, try to create one.
*/
static void __iomem *tegra_pcie_bus_map(struct tegra_pcie *pcie,
unsigned int busnr)
static int tegra_pcie_add_bus(struct pci_bus *bus)
{
struct tegra_pcie_bus *bus;
struct tegra_pcie *pcie = sys_to_pcie(bus->sysdata);
struct tegra_pcie_bus *b;
list_for_each_entry(bus, &pcie->buses, list)
if (bus->nr == busnr)
return (void __iomem *)bus->area->addr;
b = tegra_pcie_bus_alloc(pcie, bus->number);
if (IS_ERR(b))
return PTR_ERR(b);
bus = tegra_pcie_bus_alloc(pcie, busnr);
if (IS_ERR(bus))
return NULL;
list_add_tail(&b->list, &pcie->buses);
list_add_tail(&bus->list, &pcie->buses);
return 0;
}
static void tegra_pcie_remove_bus(struct pci_bus *child)
{
struct tegra_pcie *pcie = sys_to_pcie(child->sysdata);
struct tegra_pcie_bus *bus, *tmp;
return (void __iomem *)bus->area->addr;
list_for_each_entry_safe(bus, tmp, &pcie->buses, list) {
if (bus->nr == child->number) {
vunmap(bus->area->addr);
list_del(&bus->list);
kfree(bus);
break;
}
}
}
static void __iomem *tegra_pcie_conf_address(struct pci_bus *bus,
static void __iomem *tegra_pcie_map_bus(struct pci_bus *bus,
unsigned int devfn,
int where)
{
......@@ -466,7 +477,12 @@ static void __iomem *tegra_pcie_conf_address(struct pci_bus *bus,
}
}
} else {
addr = tegra_pcie_bus_map(pcie, bus->number);
struct tegra_pcie_bus *b;
list_for_each_entry(b, &pcie->buses, list)
if (b->nr == bus->number)
addr = (void __iomem *)b->area->addr;
if (!addr) {
dev_err(pcie->dev,
"failed to map cfg. space for bus %u\n",
......@@ -481,7 +497,9 @@ static void __iomem *tegra_pcie_conf_address(struct pci_bus *bus,
}
static struct pci_ops tegra_pcie_ops = {
.map_bus = tegra_pcie_conf_address,
.add_bus = tegra_pcie_add_bus,
.remove_bus = tegra_pcie_remove_bus,
.map_bus = tegra_pcie_map_bus,
.read = pci_generic_config_read32,
.write = pci_generic_config_write32,
};
......@@ -598,6 +616,17 @@ static int tegra_pcie_setup(int nr, struct pci_sys_data *sys)
struct tegra_pcie *pcie = sys_to_pcie(sys);
int err;
sys->mem_offset = pcie->offset.mem;
sys->io_offset = pcie->offset.io;
err = devm_request_resource(pcie->dev, &pcie->all, &pcie->io);
if (err < 0)
return err;
err = devm_request_resource(pcie->dev, &ioport_resource, &pcie->pio);
if (err < 0)
return err;
err = devm_request_resource(pcie->dev, &pcie->all, &pcie->mem);
if (err < 0)
return err;
......@@ -606,6 +635,7 @@ static int tegra_pcie_setup(int nr, struct pci_sys_data *sys)
if (err)
return err;
pci_add_resource_offset(&sys->resources, &pcie->pio, sys->io_offset);
pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset);
pci_add_resource_offset(&sys->resources, &pcie->prefetch,
sys->mem_offset);
......@@ -741,7 +771,7 @@ static void tegra_pcie_setup_translations(struct tegra_pcie *pcie)
afi_writel(pcie, 0, AFI_FPCI_BAR5);
/* map all upstream transactions as uncached */
afi_writel(pcie, PHYS_OFFSET, AFI_CACHE_BAR0_ST);
afi_writel(pcie, 0, AFI_CACHE_BAR0_ST);
afi_writel(pcie, 0, AFI_CACHE_BAR0_SZ);
afi_writel(pcie, 0, AFI_CACHE_BAR1_ST);
afi_writel(pcie, 0, AFI_CACHE_BAR1_SZ);
......@@ -1601,6 +1631,9 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
switch (res.flags & IORESOURCE_TYPE_BITS) {
case IORESOURCE_IO:
/* Track the bus -> CPU I/O mapping offset. */
pcie->offset.io = res.start - range.pci_addr;
memcpy(&pcie->pio, &res, sizeof(res));
pcie->pio.name = np->full_name;
......@@ -1621,6 +1654,14 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
break;
case IORESOURCE_MEM:
/*
* Track the bus -> CPU memory mapping offset. This
* assumes that the prefetchable and non-prefetchable
* regions will be the last of type IORESOURCE_MEM in
* the ranges property.
* */
pcie->offset.mem = res.start - range.pci_addr;
if (res.flags & IORESOURCE_PREFETCH) {
memcpy(&pcie->prefetch, &res, sizeof(res));
pcie->prefetch.name = "prefetchable";
......
This diff is collapsed.
/*
* 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
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright (C) 2015 - 2016 Cavium, Inc.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_pci.h>
#include <linux/platform_device.h>
#include "pci-host-common.h"
#define PEM_CFG_WR 0x28
#define PEM_CFG_RD 0x30
struct thunder_pem_pci {
struct gen_pci gen_pci;
u32 ea_entry[3];
void __iomem *pem_reg_base;
};
static void __iomem *thunder_pem_map_bus(struct pci_bus *bus,
unsigned int devfn, int where)
{
struct gen_pci *pci = bus->sysdata;
resource_size_t idx = bus->number - pci->cfg.bus_range->start;
return pci->cfg.win[idx] + ((devfn << 16) | where);
}
static int thunder_pem_bridge_read(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *val)
{
u64 read_val;
struct thunder_pem_pci *pem_pci;
struct gen_pci *pci = bus->sysdata;
pem_pci = container_of(pci, struct thunder_pem_pci, gen_pci);
if (devfn != 0 || where >= 2048) {
*val = ~0;
return PCIBIOS_DEVICE_NOT_FOUND;
}
/*
* 32-bit accesses only. Write the address to the low order
* bits of PEM_CFG_RD, then trigger the read by reading back.
* The config data lands in the upper 32-bits of PEM_CFG_RD.
*/
read_val = where & ~3ull;
writeq(read_val, pem_pci->pem_reg_base + PEM_CFG_RD);
read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD);
read_val >>= 32;
/*
* The config space contains some garbage, fix it up. Also
* synthesize an EA capability for the BAR used by MSI-X.
*/
switch (where & ~3) {
case 0x40:
read_val &= 0xffff00ff;
read_val |= 0x00007000; /* Skip MSI CAP */
break;
case 0x70: /* Express Cap */
/* PME interrupt on vector 2*/
read_val |= (2u << 25);
break;
case 0xb0: /* MSI-X Cap */
/* TableSize=4, Next Cap is EA */
read_val &= 0xc00000ff;
read_val |= 0x0003bc00;
break;
case 0xb4:
/* Table offset=0, BIR=0 */
read_val = 0x00000000;
break;
case 0xb8:
/* BPA offset=0xf0000, BIR=0 */
read_val = 0x000f0000;
break;
case 0xbc:
/* EA, 1 entry, no next Cap */
read_val = 0x00010014;
break;
case 0xc0:
/* DW2 for type-1 */
read_val = 0x00000000;
break;
case 0xc4:
/* Entry BEI=0, PP=0x00, SP=0xff, ES=3 */
read_val = 0x80ff0003;
break;
case 0xc8:
read_val = pem_pci->ea_entry[0];
break;
case 0xcc:
read_val = pem_pci->ea_entry[1];
break;
case 0xd0:
read_val = pem_pci->ea_entry[2];
break;
default:
break;
}
read_val >>= (8 * (where & 3));
switch (size) {
case 1:
read_val &= 0xff;
break;
case 2:
read_val &= 0xffff;
break;
default:
break;
}
*val = read_val;
return PCIBIOS_SUCCESSFUL;
}
static int thunder_pem_config_read(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *val)
{
struct gen_pci *pci = bus->sysdata;
if (bus->number < pci->cfg.bus_range->start ||
bus->number > pci->cfg.bus_range->end)
return PCIBIOS_DEVICE_NOT_FOUND;
/*
* The first device on the bus is the PEM PCIe bridge.
* Special case its config access.
*/
if (bus->number == pci->cfg.bus_range->start)
return thunder_pem_bridge_read(bus, devfn, where, size, val);
return pci_generic_config_read(bus, devfn, where, size, val);
}
/*
* Some of the w1c_bits below also include read-only or non-writable
* reserved bits, this makes the code simpler and is OK as the bits
* are not affected by writing zeros to them.
*/
static u32 thunder_pem_bridge_w1c_bits(int where)
{
u32 w1c_bits = 0;
switch (where & ~3) {
case 0x04: /* Command/Status */
case 0x1c: /* Base and I/O Limit/Secondary Status */
w1c_bits = 0xff000000;
break;
case 0x44: /* Power Management Control and Status */
w1c_bits = 0xfffffe00;
break;
case 0x78: /* Device Control/Device Status */
case 0x80: /* Link Control/Link Status */
case 0x88: /* Slot Control/Slot Status */
case 0x90: /* Root Status */
case 0xa0: /* Link Control 2 Registers/Link Status 2 */
w1c_bits = 0xffff0000;
break;
case 0x104: /* Uncorrectable Error Status */
case 0x110: /* Correctable Error Status */
case 0x130: /* Error Status */
case 0x160: /* Link Control 4 */
w1c_bits = 0xffffffff;
break;
default:
break;
}
return w1c_bits;
}
static int thunder_pem_bridge_write(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 val)
{
struct gen_pci *pci = bus->sysdata;
struct thunder_pem_pci *pem_pci;
u64 write_val, read_val;
u32 mask = 0;
pem_pci = container_of(pci, struct thunder_pem_pci, gen_pci);
if (devfn != 0 || where >= 2048)
return PCIBIOS_DEVICE_NOT_FOUND;
/*
* 32-bit accesses only. If the write is for a size smaller
* than 32-bits, we must first read the 32-bit value and merge
* in the desired bits and then write the whole 32-bits back
* out.
*/
switch (size) {
case 1:
read_val = where & ~3ull;
writeq(read_val, pem_pci->pem_reg_base + PEM_CFG_RD);
read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD);
read_val >>= 32;
mask = ~(0xff << (8 * (where & 3)));
read_val &= mask;
val = (val & 0xff) << (8 * (where & 3));
val |= (u32)read_val;
break;
case 2:
read_val = where & ~3ull;
writeq(read_val, pem_pci->pem_reg_base + PEM_CFG_RD);
read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD);
read_val >>= 32;
mask = ~(0xffff << (8 * (where & 3)));
read_val &= mask;
val = (val & 0xffff) << (8 * (where & 3));
val |= (u32)read_val;
break;
default:
break;
}
/*
* By expanding the write width to 32 bits, we may
* inadvertently hit some W1C bits that were not intended to
* be written. Calculate the mask that must be applied to the
* data to be written to avoid these cases.
*/
if (mask) {
u32 w1c_bits = thunder_pem_bridge_w1c_bits(where);
if (w1c_bits) {
mask &= w1c_bits;
val &= ~mask;
}
}
/*
* Low order bits are the config address, the high order 32
* bits are the data to be written.
*/
write_val = where & ~3ull;
write_val |= (((u64)val) << 32);
writeq(write_val, pem_pci->pem_reg_base + PEM_CFG_WR);
return PCIBIOS_SUCCESSFUL;
}
static int thunder_pem_config_write(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 val)
{
struct gen_pci *pci = bus->sysdata;
if (bus->number < pci->cfg.bus_range->start ||
bus->number > pci->cfg.bus_range->end)
return PCIBIOS_DEVICE_NOT_FOUND;
/*
* The first device on the bus is the PEM PCIe bridge.
* Special case its config access.
*/
if (bus->number == pci->cfg.bus_range->start)
return thunder_pem_bridge_write(bus, devfn, where, size, val);
return pci_generic_config_write(bus, devfn, where, size, val);
}
static struct gen_pci_cfg_bus_ops thunder_pem_bus_ops = {
.bus_shift = 24,
.ops = {
.map_bus = thunder_pem_map_bus,
.read = thunder_pem_config_read,
.write = thunder_pem_config_write,
}
};
static const struct of_device_id thunder_pem_of_match[] = {
{ .compatible = "cavium,pci-host-thunder-pem",
.data = &thunder_pem_bus_ops },
{ },
};
MODULE_DEVICE_TABLE(of, thunder_pem_of_match);
static int thunder_pem_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct of_device_id *of_id;
resource_size_t bar4_start;
struct resource *res_pem;
struct thunder_pem_pci *pem_pci;
pem_pci = devm_kzalloc(dev, sizeof(*pem_pci), GFP_KERNEL);
if (!pem_pci)
return -ENOMEM;
of_id = of_match_node(thunder_pem_of_match, dev->of_node);
pem_pci->gen_pci.cfg.ops = (struct gen_pci_cfg_bus_ops *)of_id->data;
/*
* The second register range is the PEM bridge to the PCIe
* bus. It has a different config access method than those
* devices behind the bridge.
*/
res_pem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res_pem) {
dev_err(dev, "missing \"reg[1]\"property\n");
return -EINVAL;
}
pem_pci->pem_reg_base = devm_ioremap(dev, res_pem->start, 0x10000);
if (!pem_pci->pem_reg_base)
return -ENOMEM;
/*
* The MSI-X BAR for the PEM and AER interrupts is located at
* a fixed offset from the PEM register base. Generate a
* fragment of the synthesized Enhanced Allocation capability
* structure here for the BAR.
*/
bar4_start = res_pem->start + 0xf00000;
pem_pci->ea_entry[0] = (u32)bar4_start | 2;
pem_pci->ea_entry[1] = (u32)(res_pem->end - bar4_start) & ~3u;
pem_pci->ea_entry[2] = (u32)(bar4_start >> 32);
return pci_host_common_probe(pdev, &pem_pci->gen_pci);
}
static struct platform_driver thunder_pem_driver = {
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = thunder_pem_of_match,
},
.probe = thunder_pem_probe,
};
module_platform_driver(thunder_pem_driver);
MODULE_DESCRIPTION("Thunder PEM PCIe host driver");
MODULE_LICENSE("GPL v2");
......@@ -40,6 +40,7 @@
#define P2A_INT_ENABLE 0x3070
#define P2A_INT_ENA_ALL 0xf
#define RP_LTSSM 0x3c64
#define RP_LTSSM_MASK 0x1f
#define LTSSM_L0 0xf
/* TLP configuration type 0 and 1 */
......@@ -140,7 +141,7 @@ static void tlp_write_tx(struct altera_pcie *pcie,
static bool altera_pcie_link_is_up(struct altera_pcie *pcie)
{
return !!(cra_readl(pcie, RP_LTSSM) & LTSSM_L0);
return !!((cra_readl(pcie, RP_LTSSM) & RP_LTSSM_MASK) == LTSSM_L0);
}
static bool altera_pcie_valid_config(struct altera_pcie *pcie,
......
This diff is collapsed.
......@@ -94,9 +94,6 @@
/* Number of MSI IRQs */
#define XILINX_NUM_MSI_IRQS 128
/* Number of Memory Resources */
#define XILINX_MAX_NUM_RESOURCES 3
/**
* struct xilinx_pcie_port - PCIe port information
* @reg_base: IO Mapped Register Base
......@@ -105,7 +102,6 @@
* @root_busno: Root Bus number
* @dev: Device pointer
* @irq_domain: IRQ domain pointer
* @bus_range: Bus range
* @resources: Bus Resources
*/
struct xilinx_pcie_port {
......@@ -115,17 +111,11 @@ struct xilinx_pcie_port {
u8 root_busno;
struct device *dev;
struct irq_domain *irq_domain;
struct resource bus_range;
struct list_head resources;
};
static DECLARE_BITMAP(msi_irq_in_use, XILINX_NUM_MSI_IRQS);
static inline struct xilinx_pcie_port *sys_to_pcie(struct pci_sys_data *sys)
{
return sys->private_data;
}
static inline u32 pcie_read(struct xilinx_pcie_port *port, u32 reg)
{
return readl(port->reg_base + reg);
......@@ -167,7 +157,7 @@ static void xilinx_pcie_clear_err_interrupts(struct xilinx_pcie_port *port)
*/
static bool xilinx_pcie_valid_device(struct pci_bus *bus, unsigned int devfn)
{
struct xilinx_pcie_port *port = sys_to_pcie(bus->sysdata);
struct xilinx_pcie_port *port = bus->sysdata;
/* Check if link is up when trying to access downstream ports */
if (bus->number != port->root_busno)
......@@ -200,7 +190,7 @@ static bool xilinx_pcie_valid_device(struct pci_bus *bus, unsigned int devfn)
static void __iomem *xilinx_pcie_map_bus(struct pci_bus *bus,
unsigned int devfn, int where)
{
struct xilinx_pcie_port *port = sys_to_pcie(bus->sysdata);
struct xilinx_pcie_port *port = bus->sysdata;
int relbus;
if (!xilinx_pcie_valid_device(bus, devfn))
......@@ -232,7 +222,7 @@ static void xilinx_pcie_destroy_msi(unsigned int irq)
if (!test_bit(irq, msi_irq_in_use)) {
msi = irq_get_msi_desc(irq);
port = sys_to_pcie(msi_desc_to_pci_sysdata(msi));
port = msi_desc_to_pci_sysdata(msi);
dev_err(port->dev, "Trying to free unused MSI#%d\n", irq);
} else {
clear_bit(irq, msi_irq_in_use);
......@@ -281,7 +271,7 @@ static int xilinx_pcie_msi_setup_irq(struct msi_controller *chip,
struct pci_dev *pdev,
struct msi_desc *desc)
{
struct xilinx_pcie_port *port = sys_to_pcie(pdev->bus->sysdata);
struct xilinx_pcie_port *port = pdev->bus->sysdata;
unsigned int irq;
int hwirq;
struct msi_msg msg;
......@@ -617,138 +607,6 @@ static void xilinx_pcie_init_port(struct xilinx_pcie_port *port)
XILINX_PCIE_REG_RPSC);
}
/**
* xilinx_pcie_setup - Setup memory resources
* @nr: Bus number
* @sys: Per controller structure
*
* Return: '1' on success and error value on failure
*/
static int xilinx_pcie_setup(int nr, struct pci_sys_data *sys)
{
struct xilinx_pcie_port *port = sys_to_pcie(sys);
list_splice_init(&port->resources, &sys->resources);
return 1;
}
/**
* xilinx_pcie_scan_bus - Scan PCIe bus for devices
* @nr: Bus number
* @sys: Per controller structure
*
* Return: Valid Bus pointer on success and NULL on failure
*/
static struct pci_bus *xilinx_pcie_scan_bus(int nr, struct pci_sys_data *sys)
{
struct xilinx_pcie_port *port = sys_to_pcie(sys);
struct pci_bus *bus;
port->root_busno = sys->busnr;
if (IS_ENABLED(CONFIG_PCI_MSI))
bus = pci_scan_root_bus_msi(port->dev, sys->busnr,
&xilinx_pcie_ops, sys,
&sys->resources,
&xilinx_pcie_msi_chip);
else
bus = pci_scan_root_bus(port->dev, sys->busnr,
&xilinx_pcie_ops, sys, &sys->resources);
return bus;
}
/**
* xilinx_pcie_parse_and_add_res - Add resources by parsing ranges
* @port: PCIe port information
*
* Return: '0' on success and error value on failure
*/
static int xilinx_pcie_parse_and_add_res(struct xilinx_pcie_port *port)
{
struct device *dev = port->dev;
struct device_node *node = dev->of_node;
struct resource *mem;
resource_size_t offset;
struct of_pci_range_parser parser;
struct of_pci_range range;
struct resource_entry *win;
int err = 0, mem_resno = 0;
/* Get the ranges */
if (of_pci_range_parser_init(&parser, node)) {
dev_err(dev, "missing \"ranges\" property\n");
return -EINVAL;
}
/* Parse the ranges and add the resources found to the list */
for_each_of_pci_range(&parser, &range) {
if (mem_resno >= XILINX_MAX_NUM_RESOURCES) {
dev_err(dev, "Maximum memory resources exceeded\n");
return -EINVAL;
}
mem = devm_kmalloc(dev, sizeof(*mem), GFP_KERNEL);
if (!mem) {
err = -ENOMEM;
goto free_resources;
}
of_pci_range_to_resource(&range, node, mem);
switch (mem->flags & IORESOURCE_TYPE_BITS) {
case IORESOURCE_MEM:
offset = range.cpu_addr - range.pci_addr;
mem_resno++;
break;
default:
err = -EINVAL;
break;
}
if (err < 0) {
dev_warn(dev, "Invalid resource found %pR\n", mem);
continue;
}
err = request_resource(&iomem_resource, mem);
if (err)
goto free_resources;
pci_add_resource_offset(&port->resources, mem, offset);
}
/* Get the bus range */
if (of_pci_parse_bus_range(node, &port->bus_range)) {
u32 val = pcie_read(port, XILINX_PCIE_REG_BIR);
u8 last;
last = (val & XILINX_PCIE_BIR_ECAM_SZ_MASK) >>
XILINX_PCIE_BIR_ECAM_SZ_SHIFT;
port->bus_range = (struct resource) {
.name = node->name,
.start = 0,
.end = last,
.flags = IORESOURCE_BUS,
};
}
/* Register bus resource */
pci_add_resource(&port->resources, &port->bus_range);
return 0;
free_resources:
release_child_resources(&iomem_resource);
resource_list_for_each_entry(win, &port->resources)
devm_kfree(dev, win->res);
pci_free_resource_list(&port->resources);
return err;
}
/**
* xilinx_pcie_parse_dt - Parse Device tree
* @port: PCIe port information
......@@ -800,9 +658,12 @@ static int xilinx_pcie_parse_dt(struct xilinx_pcie_port *port)
static int xilinx_pcie_probe(struct platform_device *pdev)
{
struct xilinx_pcie_port *port;
struct hw_pci hw;
struct device *dev = &pdev->dev;
struct pci_bus *bus;
int err;
resource_size_t iobase = 0;
LIST_HEAD(res);
if (!dev->of_node)
return -ENODEV;
......@@ -827,34 +688,28 @@ static int xilinx_pcie_probe(struct platform_device *pdev)
return err;
}
/*
* Parse PCI ranges, configuration bus range and
* request their resources
*/
INIT_LIST_HEAD(&port->resources);
err = xilinx_pcie_parse_and_add_res(port);
err = of_pci_get_host_bridge_resources(dev->of_node, 0, 0xff, &res,
&iobase);
if (err) {
dev_err(dev, "Failed adding resources\n");
dev_err(dev, "Getting bridge resources failed\n");
return err;
}
platform_set_drvdata(pdev, port);
/* Register the device */
memset(&hw, 0, sizeof(hw));
hw = (struct hw_pci) {
.nr_controllers = 1,
.private_data = (void **)&port,
.setup = xilinx_pcie_setup,
.map_irq = of_irq_parse_and_map_pci,
.scan = xilinx_pcie_scan_bus,
.ops = &xilinx_pcie_ops,
};
bus = pci_create_root_bus(&pdev->dev, 0,
&xilinx_pcie_ops, port, &res);
if (!bus)
return -ENOMEM;
#ifdef CONFIG_PCI_MSI
xilinx_pcie_msi_chip.dev = port->dev;
bus->msi = &xilinx_pcie_msi_chip;
#endif
pci_common_init_dev(dev, &hw);
pci_scan_child_bus(bus);
pci_assign_unassigned_bus_resources(bus);
#ifndef CONFIG_MICROBLAZE
pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
#endif
pci_bus_add_devices(bus);
platform_set_drvdata(pdev, port);
return 0;
}
......
......@@ -757,6 +757,12 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent,
pcibios_add_bus(child);
if (child->ops->add_bus) {
ret = child->ops->add_bus(child);
if (WARN_ON(ret < 0))
dev_err(&child->dev, "failed to add bus: %d\n", ret);
}
/* Create legacy_io and legacy_mem files for this bus */
pci_create_legacy_files(child);
......
......@@ -54,6 +54,10 @@ void pci_remove_bus(struct pci_bus *bus)
pci_bus_release_busn_res(bus);
up_write(&pci_bus_sem);
pci_remove_legacy_files(bus);
if (bus->ops->remove_bus)
bus->ops->remove_bus(bus);
pcibios_remove_bus(bus);
device_unregister(&bus->dev);
}
......
......@@ -572,6 +572,8 @@ static inline int pcibios_err_to_errno(int err)
/* Low-level architecture-dependent routines */
struct pci_ops {
int (*add_bus)(struct pci_bus *bus);
void (*remove_bus)(struct pci_bus *bus);
void __iomem *(*map_bus)(struct pci_bus *bus, unsigned int devfn, int where);
int (*read)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val);
int (*write)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment