Merge branch 'pci/host-tegra' into next
* pci/host-tegra: arm64: tegra: Enable PCIe on Jetson TX1 arm64: tegra: Add PCIe host bridge on Tegra210 PCI: tegra: Enable the driver on 64-bit ARM PCI: tegra: Add Tegra210 support PCI: tegra: Implement PCA enable workaround dt-bindings: pci: tegra: Add Tegra210 support PCI: tegra: Use new pci_register_host_bridge() interface PCI: Export host bridge registration interface PCI: Allow driver-specific data in host bridge PCI: Add pci_register_host_bridge() interface
This commit is contained in:
commit
d34efd22ac
|
@ -110,6 +110,20 @@ Power supplies for Tegra124:
|
|||
- avdd-pll-erefe-supply: Power supply for PLLE (shared with USB3). Must
|
||||
supply 1.05 V.
|
||||
|
||||
Power supplies for Tegra210:
|
||||
- Required:
|
||||
- avdd-pll-uerefe-supply: Power supply for PLLE (shared with USB3). Must
|
||||
supply 1.05 V.
|
||||
- hvddio-pex-supply: High-voltage supply for PCIe I/O and PCIe output
|
||||
clocks. Must supply 1.8 V.
|
||||
- dvddio-pex-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
|
||||
- dvdd-pex-pll-supply: Power supply for dedicated (internal) PCIe PLL. Must
|
||||
supply 1.05 V.
|
||||
- hvdd-pex-pll-e-supply: High-voltage supply for PLLE (shared with USB3).
|
||||
Must supply 3.3 V.
|
||||
- vddio-pex-ctl-supply: Power supply for PCIe control I/O partition. Must
|
||||
supply 1.8 V.
|
||||
|
||||
Root ports are defined as subnodes of the PCIe controller node.
|
||||
|
||||
Required properties:
|
||||
|
@ -436,3 +450,99 @@ Board DTS:
|
|||
status = "okay";
|
||||
};
|
||||
};
|
||||
|
||||
Tegra210:
|
||||
---------
|
||||
|
||||
SoC DTSI:
|
||||
|
||||
pcie-controller@01003000 {
|
||||
compatible = "nvidia,tegra210-pcie";
|
||||
device_type = "pci";
|
||||
reg = <0x0 0x01003000 0x0 0x00000800 /* PADS registers */
|
||||
0x0 0x01003800 0x0 0x00000800 /* AFI registers */
|
||||
0x0 0x02000000 0x0 0x10000000>; /* configuration space */
|
||||
reg-names = "pads", "afi", "cs";
|
||||
interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>, /* controller interrupt */
|
||||
<GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>; /* MSI interrupt */
|
||||
interrupt-names = "intr", "msi";
|
||||
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-map-mask = <0 0 0 0>;
|
||||
interrupt-map = <0 0 0 0 &gic GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
|
||||
|
||||
bus-range = <0x00 0xff>;
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
|
||||
ranges = <0x82000000 0 0x01000000 0x0 0x01000000 0 0x00001000 /* port 0 configuration space */
|
||||
0x82000000 0 0x01001000 0x0 0x01001000 0 0x00001000 /* port 1 configuration space */
|
||||
0x81000000 0 0x0 0x0 0x12000000 0 0x00010000 /* downstream I/O (64 KiB) */
|
||||
0x82000000 0 0x13000000 0x0 0x13000000 0 0x0d000000 /* non-prefetchable memory (208 MiB) */
|
||||
0xc2000000 0 0x20000000 0x0 0x20000000 0 0x20000000>; /* prefetchable memory (512 MiB) */
|
||||
|
||||
clocks = <&tegra_car TEGRA210_CLK_PCIE>,
|
||||
<&tegra_car TEGRA210_CLK_AFI>,
|
||||
<&tegra_car TEGRA210_CLK_PLL_E>,
|
||||
<&tegra_car TEGRA210_CLK_CML0>;
|
||||
clock-names = "pex", "afi", "pll_e", "cml";
|
||||
resets = <&tegra_car 70>,
|
||||
<&tegra_car 72>,
|
||||
<&tegra_car 74>;
|
||||
reset-names = "pex", "afi", "pcie_x";
|
||||
status = "disabled";
|
||||
|
||||
pci@1,0 {
|
||||
device_type = "pci";
|
||||
assigned-addresses = <0x82000800 0 0x01000000 0 0x1000>;
|
||||
reg = <0x000800 0 0 0 0>;
|
||||
status = "disabled";
|
||||
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
nvidia,num-lanes = <4>;
|
||||
};
|
||||
|
||||
pci@2,0 {
|
||||
device_type = "pci";
|
||||
assigned-addresses = <0x82001000 0 0x01001000 0 0x1000>;
|
||||
reg = <0x001000 0 0 0 0>;
|
||||
status = "disabled";
|
||||
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
nvidia,num-lanes = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
Board DTS:
|
||||
|
||||
pcie-controller@01003000 {
|
||||
status = "okay";
|
||||
|
||||
avdd-pll-uerefe-supply = <&avdd_1v05_pll>;
|
||||
hvddio-pex-supply = <&vdd_1v8>;
|
||||
dvddio-pex-supply = <&vdd_pex_1v05>;
|
||||
dvdd-pex-pll-supply = <&vdd_pex_1v05>;
|
||||
hvdd-pex-pll-e-supply = <&vdd_1v8>;
|
||||
vddio-pex-ctl-supply = <&vdd_1v8>;
|
||||
|
||||
pci@1,0 {
|
||||
phys = <&{/padctl@7009f000/pads/pcie/lanes/pcie-0}>,
|
||||
<&{/padctl@7009f000/pads/pcie/lanes/pcie-1}>,
|
||||
<&{/padctl@7009f000/pads/pcie/lanes/pcie-2}>,
|
||||
<&{/padctl@7009f000/pads/pcie/lanes/pcie-3}>;
|
||||
phy-names = "pcie-0", "pcie-1", "pcie-2", "pcie-3";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
pci@2,0 {
|
||||
phys = <&{/padctl@7009f000/pads/pcie/lanes/pcie-4}>;
|
||||
phy-names = "pcie-0";
|
||||
status = "okay";
|
||||
};
|
||||
};
|
||||
|
|
|
@ -7,6 +7,32 @@
|
|||
model = "NVIDIA Jetson TX1 Developer Kit";
|
||||
compatible = "nvidia,p2371-2180", "nvidia,tegra210";
|
||||
|
||||
pcie-controller@01003000 {
|
||||
status = "okay";
|
||||
|
||||
avdd-pll-uerefe-supply = <&avdd_1v05_pll>;
|
||||
hvddio-pex-supply = <&vdd_1v8>;
|
||||
dvddio-pex-supply = <&vdd_pex_1v05>;
|
||||
dvdd-pex-pll-supply = <&vdd_pex_1v05>;
|
||||
hvdd-pex-pll-e-supply = <&vdd_1v8>;
|
||||
vddio-pex-ctl-supply = <&vdd_1v8>;
|
||||
|
||||
pci@1,0 {
|
||||
phys = <&{/padctl@7009f000/pads/pcie/lanes/pcie-0}>,
|
||||
<&{/padctl@7009f000/pads/pcie/lanes/pcie-1}>,
|
||||
<&{/padctl@7009f000/pads/pcie/lanes/pcie-2}>,
|
||||
<&{/padctl@7009f000/pads/pcie/lanes/pcie-3}>;
|
||||
phy-names = "pcie-0", "pcie-1", "pcie-2", "pcie-3";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
pci@2,0 {
|
||||
phys = <&{/padctl@7009f000/pads/pcie/lanes/pcie-4}>;
|
||||
phy-names = "pcie-0";
|
||||
status = "okay";
|
||||
};
|
||||
};
|
||||
|
||||
host1x@50000000 {
|
||||
dsi@54300000 {
|
||||
status = "okay";
|
||||
|
|
|
@ -11,6 +11,69 @@
|
|||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
pcie-controller@01003000 {
|
||||
compatible = "nvidia,tegra210-pcie";
|
||||
device_type = "pci";
|
||||
reg = <0x0 0x01003000 0x0 0x00000800 /* PADS registers */
|
||||
0x0 0x01003800 0x0 0x00000800 /* AFI registers */
|
||||
0x0 0x02000000 0x0 0x10000000>; /* configuration space */
|
||||
reg-names = "pads", "afi", "cs";
|
||||
interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>, /* controller interrupt */
|
||||
<GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>; /* MSI interrupt */
|
||||
interrupt-names = "intr", "msi";
|
||||
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-map-mask = <0 0 0 0>;
|
||||
interrupt-map = <0 0 0 0 &gic GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
|
||||
|
||||
bus-range = <0x00 0xff>;
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
|
||||
ranges = <0x82000000 0 0x01000000 0x0 0x01000000 0 0x00001000 /* port 0 configuration space */
|
||||
0x82000000 0 0x01001000 0x0 0x01001000 0 0x00001000 /* port 1 configuration space */
|
||||
0x81000000 0 0x0 0x0 0x12000000 0 0x00010000 /* downstream I/O (64 KiB) */
|
||||
0x82000000 0 0x13000000 0x0 0x13000000 0 0x0d000000 /* non-prefetchable memory (208 MiB) */
|
||||
0xc2000000 0 0x20000000 0x0 0x20000000 0 0x20000000>; /* prefetchable memory (512 MiB) */
|
||||
|
||||
clocks = <&tegra_car TEGRA210_CLK_PCIE>,
|
||||
<&tegra_car TEGRA210_CLK_AFI>,
|
||||
<&tegra_car TEGRA210_CLK_PLL_E>,
|
||||
<&tegra_car TEGRA210_CLK_CML0>;
|
||||
clock-names = "pex", "afi", "pll_e", "cml";
|
||||
resets = <&tegra_car 70>,
|
||||
<&tegra_car 72>,
|
||||
<&tegra_car 74>;
|
||||
reset-names = "pex", "afi", "pcie_x";
|
||||
status = "disabled";
|
||||
|
||||
pci@1,0 {
|
||||
device_type = "pci";
|
||||
assigned-addresses = <0x82000800 0 0x01000000 0 0x1000>;
|
||||
reg = <0x000800 0 0 0 0>;
|
||||
status = "disabled";
|
||||
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
nvidia,num-lanes = <4>;
|
||||
};
|
||||
|
||||
pci@2,0 {
|
||||
device_type = "pci";
|
||||
assigned-addresses = <0x82001000 0 0x01001000 0 0x1000>;
|
||||
reg = <0x001000 0 0 0 0>;
|
||||
status = "disabled";
|
||||
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
nvidia,num-lanes = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
host1x@50000000 {
|
||||
compatible = "nvidia,tegra210-host1x", "simple-bus";
|
||||
reg = <0x0 0x50000000 0x0 0x00034000>;
|
||||
|
|
|
@ -69,7 +69,7 @@ config PCI_IMX6
|
|||
|
||||
config PCI_TEGRA
|
||||
bool "NVIDIA Tegra PCIe controller"
|
||||
depends on ARCH_TEGRA && !ARM64
|
||||
depends on ARCH_TEGRA
|
||||
help
|
||||
Say Y here if you want support for the PCIe host controller found
|
||||
on NVIDIA Tegra SoCs.
|
||||
|
|
|
@ -51,10 +51,6 @@
|
|||
#include <soc/tegra/cpuidle.h>
|
||||
#include <soc/tegra/pmc.h>
|
||||
|
||||
#include <asm/mach/irq.h>
|
||||
#include <asm/mach/map.h>
|
||||
#include <asm/mach/pci.h>
|
||||
|
||||
#define INT_PCI_MSI_NR (8 * 32)
|
||||
|
||||
/* register definitions */
|
||||
|
@ -188,6 +184,9 @@
|
|||
#define RP_VEND_XP 0x00000f00
|
||||
#define RP_VEND_XP_DL_UP (1 << 30)
|
||||
|
||||
#define RP_VEND_CTL2 0x00000fa8
|
||||
#define RP_VEND_CTL2_PCA_ENABLE (1 << 7)
|
||||
|
||||
#define RP_PRIV_MISC 0x00000fe0
|
||||
#define RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT (0xe << 0)
|
||||
#define RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT (0xf << 0)
|
||||
|
@ -252,6 +251,7 @@ struct tegra_pcie_soc {
|
|||
bool has_intr_prsnt_sense;
|
||||
bool has_cml_clk;
|
||||
bool has_gen2;
|
||||
bool force_pca_enable;
|
||||
};
|
||||
|
||||
static inline struct tegra_msi *to_tegra_msi(struct msi_controller *chip)
|
||||
|
@ -322,11 +322,6 @@ struct tegra_pcie_bus {
|
|||
unsigned int nr;
|
||||
};
|
||||
|
||||
static inline struct tegra_pcie *sys_to_pcie(struct pci_sys_data *sys)
|
||||
{
|
||||
return sys->private_data;
|
||||
}
|
||||
|
||||
static inline void afi_writel(struct tegra_pcie *pcie, u32 value,
|
||||
unsigned long offset)
|
||||
{
|
||||
|
@ -385,8 +380,7 @@ static struct tegra_pcie_bus *tegra_pcie_bus_alloc(struct tegra_pcie *pcie,
|
|||
unsigned int busnr)
|
||||
{
|
||||
struct device *dev = pcie->dev;
|
||||
pgprot_t prot = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
|
||||
L_PTE_XN | L_PTE_MT_DEV_SHARED | L_PTE_SHARED);
|
||||
pgprot_t prot = pgprot_device(PAGE_KERNEL);
|
||||
phys_addr_t cs = pcie->cs->start;
|
||||
struct tegra_pcie_bus *bus;
|
||||
unsigned int i;
|
||||
|
@ -430,7 +424,8 @@ free:
|
|||
|
||||
static int tegra_pcie_add_bus(struct pci_bus *bus)
|
||||
{
|
||||
struct tegra_pcie *pcie = sys_to_pcie(bus->sysdata);
|
||||
struct pci_host_bridge *host = pci_find_host_bridge(bus);
|
||||
struct tegra_pcie *pcie = pci_host_bridge_priv(host);
|
||||
struct tegra_pcie_bus *b;
|
||||
|
||||
b = tegra_pcie_bus_alloc(pcie, bus->number);
|
||||
|
@ -444,7 +439,8 @@ static int tegra_pcie_add_bus(struct pci_bus *bus)
|
|||
|
||||
static void tegra_pcie_remove_bus(struct pci_bus *child)
|
||||
{
|
||||
struct tegra_pcie *pcie = sys_to_pcie(child->sysdata);
|
||||
struct pci_host_bridge *host = pci_find_host_bridge(child);
|
||||
struct tegra_pcie *pcie = pci_host_bridge_priv(host);
|
||||
struct tegra_pcie_bus *bus, *tmp;
|
||||
|
||||
list_for_each_entry_safe(bus, tmp, &pcie->buses, list) {
|
||||
|
@ -461,7 +457,8 @@ static void __iomem *tegra_pcie_map_bus(struct pci_bus *bus,
|
|||
unsigned int devfn,
|
||||
int where)
|
||||
{
|
||||
struct tegra_pcie *pcie = sys_to_pcie(bus->sysdata);
|
||||
struct pci_host_bridge *host = pci_find_host_bridge(bus);
|
||||
struct tegra_pcie *pcie = pci_host_bridge_priv(host);
|
||||
struct device *dev = pcie->dev;
|
||||
void __iomem *addr = NULL;
|
||||
|
||||
|
@ -558,6 +555,12 @@ static void tegra_pcie_port_enable(struct tegra_pcie_port *port)
|
|||
afi_writel(port->pcie, value, ctrl);
|
||||
|
||||
tegra_pcie_port_reset(port);
|
||||
|
||||
if (soc->force_pca_enable) {
|
||||
value = readl(port->base + RP_VEND_CTL2);
|
||||
value |= RP_VEND_CTL2_PCA_ENABLE;
|
||||
writel(value, port->base + RP_VEND_CTL2);
|
||||
}
|
||||
}
|
||||
|
||||
static void tegra_pcie_port_disable(struct tegra_pcie_port *port)
|
||||
|
@ -610,39 +613,31 @@ static void tegra_pcie_relax_enable(struct pci_dev *dev)
|
|||
}
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_relax_enable);
|
||||
|
||||
static int tegra_pcie_setup(int nr, struct pci_sys_data *sys)
|
||||
static int tegra_pcie_request_resources(struct tegra_pcie *pcie)
|
||||
{
|
||||
struct tegra_pcie *pcie = sys_to_pcie(sys);
|
||||
struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie);
|
||||
struct list_head *windows = &host->windows;
|
||||
struct device *dev = pcie->dev;
|
||||
int err;
|
||||
|
||||
sys->mem_offset = pcie->offset.mem;
|
||||
sys->io_offset = pcie->offset.io;
|
||||
pci_add_resource_offset(windows, &pcie->pio, pcie->offset.io);
|
||||
pci_add_resource_offset(windows, &pcie->mem, pcie->offset.mem);
|
||||
pci_add_resource_offset(windows, &pcie->prefetch, pcie->offset.mem);
|
||||
pci_add_resource(windows, &pcie->busn);
|
||||
|
||||
err = devm_request_resource(dev, &iomem_resource, &pcie->io);
|
||||
err = devm_request_pci_bus_resources(dev, windows);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = pci_remap_iospace(&pcie->pio, pcie->io.start);
|
||||
if (!err)
|
||||
pci_add_resource_offset(&sys->resources, &pcie->pio,
|
||||
sys->io_offset);
|
||||
pci_remap_iospace(&pcie->pio, pcie->io.start);
|
||||
|
||||
pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset);
|
||||
pci_add_resource_offset(&sys->resources, &pcie->prefetch,
|
||||
sys->mem_offset);
|
||||
pci_add_resource(&sys->resources, &pcie->busn);
|
||||
|
||||
err = devm_request_pci_bus_resources(dev, &sys->resources);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_pcie_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
|
||||
{
|
||||
struct tegra_pcie *pcie = sys_to_pcie(pdev->bus->sysdata);
|
||||
struct pci_host_bridge *host = pci_find_host_bridge(pdev->bus);
|
||||
struct tegra_pcie *pcie = pci_host_bridge_priv(host);
|
||||
int irq;
|
||||
|
||||
tegra_cpuidle_pcie_irqs_in_use();
|
||||
|
@ -1499,10 +1494,11 @@ static const struct irq_domain_ops msi_domain_ops = {
|
|||
|
||||
static int tegra_pcie_enable_msi(struct tegra_pcie *pcie)
|
||||
{
|
||||
struct device *dev = pcie->dev;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie);
|
||||
struct platform_device *pdev = to_platform_device(pcie->dev);
|
||||
const struct tegra_pcie_soc *soc = pcie->soc;
|
||||
struct tegra_msi *msi = &pcie->msi;
|
||||
struct device *dev = pcie->dev;
|
||||
unsigned long base;
|
||||
int err;
|
||||
u32 reg;
|
||||
|
@ -1559,6 +1555,8 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie)
|
|||
reg |= AFI_INTR_MASK_MSI_MASK;
|
||||
afi_writel(pcie, reg, AFI_INTR_MASK);
|
||||
|
||||
host->msi = &msi->chip;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
|
@ -1609,7 +1607,8 @@ static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes,
|
|||
struct device *dev = pcie->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
|
||||
if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) {
|
||||
if (of_device_is_compatible(np, "nvidia,tegra124-pcie") ||
|
||||
of_device_is_compatible(np, "nvidia,tegra210-pcie")) {
|
||||
switch (lanes) {
|
||||
case 0x0000104:
|
||||
dev_info(dev, "4x1, 1x1 configuration\n");
|
||||
|
@ -1730,7 +1729,22 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask)
|
|||
struct device_node *np = dev->of_node;
|
||||
unsigned int i = 0;
|
||||
|
||||
if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) {
|
||||
if (of_device_is_compatible(np, "nvidia,tegra210-pcie")) {
|
||||
pcie->num_supplies = 6;
|
||||
|
||||
pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies,
|
||||
sizeof(*pcie->supplies),
|
||||
GFP_KERNEL);
|
||||
if (!pcie->supplies)
|
||||
return -ENOMEM;
|
||||
|
||||
pcie->supplies[i++].supply = "avdd-pll-uerefe";
|
||||
pcie->supplies[i++].supply = "hvddio-pex";
|
||||
pcie->supplies[i++].supply = "dvddio-pex";
|
||||
pcie->supplies[i++].supply = "dvdd-pex-pll";
|
||||
pcie->supplies[i++].supply = "hvdd-pex-pll-e";
|
||||
pcie->supplies[i++].supply = "vddio-pex-ctl";
|
||||
} else if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) {
|
||||
pcie->num_supplies = 7;
|
||||
|
||||
pcie->supplies = devm_kcalloc(dev, pcie->num_supplies,
|
||||
|
@ -2021,11 +2035,10 @@ retry:
|
|||
return false;
|
||||
}
|
||||
|
||||
static int tegra_pcie_enable(struct tegra_pcie *pcie)
|
||||
static void tegra_pcie_enable_ports(struct tegra_pcie *pcie)
|
||||
{
|
||||
struct device *dev = pcie->dev;
|
||||
struct tegra_pcie_port *port, *tmp;
|
||||
struct hw_pci hw;
|
||||
|
||||
list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
|
||||
dev_info(dev, "probing port %u, using %u lanes\n",
|
||||
|
@ -2041,21 +2054,6 @@ static int tegra_pcie_enable(struct tegra_pcie *pcie)
|
|||
tegra_pcie_port_disable(port);
|
||||
tegra_pcie_port_free(port);
|
||||
}
|
||||
|
||||
memset(&hw, 0, sizeof(hw));
|
||||
|
||||
#ifdef CONFIG_PCI_MSI
|
||||
hw.msi_ctrl = &pcie->msi.chip;
|
||||
#endif
|
||||
|
||||
hw.nr_controllers = 1;
|
||||
hw.private_data = (void **)&pcie;
|
||||
hw.setup = tegra_pcie_setup;
|
||||
hw.map_irq = tegra_pcie_map_irq;
|
||||
hw.ops = &tegra_pcie_ops;
|
||||
|
||||
pci_common_init_dev(dev, &hw);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct tegra_pcie_soc tegra20_pcie = {
|
||||
|
@ -2069,6 +2067,7 @@ static const struct tegra_pcie_soc tegra20_pcie = {
|
|||
.has_intr_prsnt_sense = false,
|
||||
.has_cml_clk = false,
|
||||
.has_gen2 = false,
|
||||
.force_pca_enable = false,
|
||||
};
|
||||
|
||||
static const struct tegra_pcie_soc tegra30_pcie = {
|
||||
|
@ -2083,6 +2082,7 @@ static const struct tegra_pcie_soc tegra30_pcie = {
|
|||
.has_intr_prsnt_sense = true,
|
||||
.has_cml_clk = true,
|
||||
.has_gen2 = false,
|
||||
.force_pca_enable = false,
|
||||
};
|
||||
|
||||
static const struct tegra_pcie_soc tegra124_pcie = {
|
||||
|
@ -2096,9 +2096,25 @@ static const struct tegra_pcie_soc tegra124_pcie = {
|
|||
.has_intr_prsnt_sense = true,
|
||||
.has_cml_clk = true,
|
||||
.has_gen2 = true,
|
||||
.force_pca_enable = false,
|
||||
};
|
||||
|
||||
static const struct tegra_pcie_soc tegra210_pcie = {
|
||||
.num_ports = 2,
|
||||
.msi_base_shift = 8,
|
||||
.pads_pll_ctl = PADS_PLL_CTL_TEGRA30,
|
||||
.tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN,
|
||||
.pads_refclk_cfg0 = 0x90b890b8,
|
||||
.has_pex_clkreq_en = true,
|
||||
.has_pex_bias_ctrl = true,
|
||||
.has_intr_prsnt_sense = true,
|
||||
.has_cml_clk = true,
|
||||
.has_gen2 = true,
|
||||
.force_pca_enable = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id tegra_pcie_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra210-pcie", .data = &tegra210_pcie },
|
||||
{ .compatible = "nvidia,tegra124-pcie", .data = &tegra124_pcie },
|
||||
{ .compatible = "nvidia,tegra30-pcie", .data = &tegra30_pcie },
|
||||
{ .compatible = "nvidia,tegra20-pcie", .data = &tegra20_pcie },
|
||||
|
@ -2217,13 +2233,17 @@ remove:
|
|||
static int tegra_pcie_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct pci_host_bridge *host;
|
||||
struct tegra_pcie *pcie;
|
||||
struct pci_bus *child;
|
||||
int err;
|
||||
|
||||
pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
|
||||
if (!pcie)
|
||||
host = pci_alloc_host_bridge(sizeof(*pcie));
|
||||
if (!host)
|
||||
return -ENOMEM;
|
||||
|
||||
pcie = pci_host_bridge_priv(host);
|
||||
|
||||
pcie->soc = of_device_get_match_data(dev);
|
||||
INIT_LIST_HEAD(&pcie->buses);
|
||||
INIT_LIST_HEAD(&pcie->ports);
|
||||
|
@ -2243,6 +2263,10 @@ static int tegra_pcie_probe(struct platform_device *pdev)
|
|||
if (err)
|
||||
goto put_resources;
|
||||
|
||||
err = tegra_pcie_request_resources(pcie);
|
||||
if (err)
|
||||
goto put_resources;
|
||||
|
||||
/* setup the AFI address translations */
|
||||
tegra_pcie_setup_translations(pcie);
|
||||
|
||||
|
@ -2254,12 +2278,30 @@ static int tegra_pcie_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
err = tegra_pcie_enable(pcie);
|
||||
tegra_pcie_enable_ports(pcie);
|
||||
|
||||
pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS);
|
||||
host->busnr = pcie->busn.start;
|
||||
host->dev.parent = &pdev->dev;
|
||||
host->ops = &tegra_pcie_ops;
|
||||
|
||||
err = pci_register_host_bridge(host);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "failed to enable PCIe ports: %d\n", err);
|
||||
dev_err(dev, "failed to register host: %d\n", err);
|
||||
goto disable_msi;
|
||||
}
|
||||
|
||||
pci_scan_child_bus(host->bus);
|
||||
|
||||
pci_fixup_irqs(pci_common_swizzle, tegra_pcie_map_irq);
|
||||
pci_bus_size_bridges(host->bus);
|
||||
pci_bus_assign_resources(host->bus);
|
||||
|
||||
list_for_each_entry(child, &host->bus->children, node)
|
||||
pcie_bus_configure_settings(child);
|
||||
|
||||
pci_bus_add_devices(host->bus);
|
||||
|
||||
if (IS_ENABLED(CONFIG_DEBUG_FS)) {
|
||||
err = tegra_pcie_debugfs_init(pcie);
|
||||
if (err < 0)
|
||||
|
|
|
@ -522,18 +522,19 @@ static void pci_release_host_bridge_dev(struct device *dev)
|
|||
kfree(bridge);
|
||||
}
|
||||
|
||||
static struct pci_host_bridge *pci_alloc_host_bridge(struct pci_bus *b)
|
||||
struct pci_host_bridge *pci_alloc_host_bridge(size_t priv)
|
||||
{
|
||||
struct pci_host_bridge *bridge;
|
||||
|
||||
bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
|
||||
bridge = kzalloc(sizeof(*bridge) + priv, GFP_KERNEL);
|
||||
if (!bridge)
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(&bridge->windows);
|
||||
bridge->bus = b;
|
||||
|
||||
return bridge;
|
||||
}
|
||||
EXPORT_SYMBOL(pci_alloc_host_bridge);
|
||||
|
||||
static const unsigned char pcix_bus_speed[] = {
|
||||
PCI_SPEED_UNKNOWN, /* 0 */
|
||||
|
@ -718,6 +719,123 @@ static void pci_set_bus_msi_domain(struct pci_bus *bus)
|
|||
dev_set_msi_domain(&bus->dev, d);
|
||||
}
|
||||
|
||||
int pci_register_host_bridge(struct pci_host_bridge *bridge)
|
||||
{
|
||||
struct device *parent = bridge->dev.parent;
|
||||
struct resource_entry *window, *n;
|
||||
struct pci_bus *bus, *b;
|
||||
resource_size_t offset;
|
||||
LIST_HEAD(resources);
|
||||
struct resource *res;
|
||||
char addr[64], *fmt;
|
||||
const char *name;
|
||||
int err;
|
||||
|
||||
bus = pci_alloc_bus(NULL);
|
||||
if (!bus)
|
||||
return -ENOMEM;
|
||||
|
||||
bridge->bus = bus;
|
||||
|
||||
/* temporarily move resources off the list */
|
||||
list_splice_init(&bridge->windows, &resources);
|
||||
bus->sysdata = bridge->sysdata;
|
||||
bus->msi = bridge->msi;
|
||||
bus->ops = bridge->ops;
|
||||
bus->number = bus->busn_res.start = bridge->busnr;
|
||||
#ifdef CONFIG_PCI_DOMAINS_GENERIC
|
||||
bus->domain_nr = pci_bus_find_domain_nr(bus, parent);
|
||||
#endif
|
||||
|
||||
b = pci_find_bus(pci_domain_nr(bus), bridge->busnr);
|
||||
if (b) {
|
||||
/* If we already got to this bus through a different bridge, ignore it */
|
||||
dev_dbg(&b->dev, "bus already known\n");
|
||||
err = -EEXIST;
|
||||
goto free;
|
||||
}
|
||||
|
||||
dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(bus),
|
||||
bridge->busnr);
|
||||
|
||||
err = pcibios_root_bridge_prepare(bridge);
|
||||
if (err)
|
||||
goto free;
|
||||
|
||||
err = device_register(&bridge->dev);
|
||||
if (err)
|
||||
put_device(&bridge->dev);
|
||||
|
||||
bus->bridge = get_device(&bridge->dev);
|
||||
device_enable_async_suspend(bus->bridge);
|
||||
pci_set_bus_of_node(bus);
|
||||
pci_set_bus_msi_domain(bus);
|
||||
|
||||
if (!parent)
|
||||
set_dev_node(bus->bridge, pcibus_to_node(bus));
|
||||
|
||||
bus->dev.class = &pcibus_class;
|
||||
bus->dev.parent = bus->bridge;
|
||||
|
||||
dev_set_name(&bus->dev, "%04x:%02x", pci_domain_nr(bus), bus->number);
|
||||
name = dev_name(&bus->dev);
|
||||
|
||||
err = device_register(&bus->dev);
|
||||
if (err)
|
||||
goto unregister;
|
||||
|
||||
pcibios_add_bus(bus);
|
||||
|
||||
/* Create legacy_io and legacy_mem files for this bus */
|
||||
pci_create_legacy_files(bus);
|
||||
|
||||
if (parent)
|
||||
dev_info(parent, "PCI host bridge to bus %s\n", name);
|
||||
else
|
||||
pr_info("PCI host bridge to bus %s\n", name);
|
||||
|
||||
/* Add initial resources to the bus */
|
||||
resource_list_for_each_entry_safe(window, n, &resources) {
|
||||
list_move_tail(&window->node, &bridge->windows);
|
||||
offset = window->offset;
|
||||
res = window->res;
|
||||
|
||||
if (res->flags & IORESOURCE_BUS)
|
||||
pci_bus_insert_busn_res(bus, bus->number, res->end);
|
||||
else
|
||||
pci_bus_add_resource(bus, res, 0);
|
||||
|
||||
if (offset) {
|
||||
if (resource_type(res) == IORESOURCE_IO)
|
||||
fmt = " (bus address [%#06llx-%#06llx])";
|
||||
else
|
||||
fmt = " (bus address [%#010llx-%#010llx])";
|
||||
|
||||
snprintf(addr, sizeof(addr), fmt,
|
||||
(unsigned long long)(res->start - offset),
|
||||
(unsigned long long)(res->end - offset));
|
||||
} else
|
||||
addr[0] = '\0';
|
||||
|
||||
dev_info(&bus->dev, "root bus resource %pR%s\n", res, addr);
|
||||
}
|
||||
|
||||
down_write(&pci_bus_sem);
|
||||
list_add_tail(&bus->node, &pci_root_buses);
|
||||
up_write(&pci_bus_sem);
|
||||
|
||||
return 0;
|
||||
|
||||
unregister:
|
||||
put_device(&bridge->dev);
|
||||
device_unregister(&bridge->dev);
|
||||
|
||||
free:
|
||||
kfree(bus);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(pci_register_host_bridge);
|
||||
|
||||
static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent,
|
||||
struct pci_dev *bridge, int busnr)
|
||||
{
|
||||
|
@ -2131,113 +2249,43 @@ void __weak pcibios_remove_bus(struct pci_bus *bus)
|
|||
{
|
||||
}
|
||||
|
||||
struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
|
||||
struct pci_ops *ops, void *sysdata, struct list_head *resources)
|
||||
static struct pci_bus *pci_create_root_bus_msi(struct device *parent,
|
||||
int bus, struct pci_ops *ops, void *sysdata,
|
||||
struct list_head *resources, struct msi_controller *msi)
|
||||
{
|
||||
int error;
|
||||
struct pci_host_bridge *bridge;
|
||||
struct pci_bus *b, *b2;
|
||||
struct resource_entry *window, *n;
|
||||
struct resource *res;
|
||||
resource_size_t offset;
|
||||
char bus_addr[64];
|
||||
char *fmt;
|
||||
|
||||
b = pci_alloc_bus(NULL);
|
||||
if (!b)
|
||||
return NULL;
|
||||
|
||||
b->sysdata = sysdata;
|
||||
b->ops = ops;
|
||||
b->number = b->busn_res.start = bus;
|
||||
#ifdef CONFIG_PCI_DOMAINS_GENERIC
|
||||
b->domain_nr = pci_bus_find_domain_nr(b, parent);
|
||||
#endif
|
||||
b2 = pci_find_bus(pci_domain_nr(b), bus);
|
||||
if (b2) {
|
||||
/* If we already got to this bus through a different bridge, ignore it */
|
||||
dev_dbg(&b2->dev, "bus already known\n");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
bridge = pci_alloc_host_bridge(b);
|
||||
bridge = pci_alloc_host_bridge(0);
|
||||
if (!bridge)
|
||||
goto err_out;
|
||||
return NULL;
|
||||
|
||||
bridge->dev.parent = parent;
|
||||
bridge->dev.release = pci_release_host_bridge_dev;
|
||||
dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(b), bus);
|
||||
error = pcibios_root_bridge_prepare(bridge);
|
||||
if (error) {
|
||||
kfree(bridge);
|
||||
|
||||
list_splice_init(resources, &bridge->windows);
|
||||
bridge->sysdata = sysdata;
|
||||
bridge->busnr = bus;
|
||||
bridge->ops = ops;
|
||||
bridge->msi = msi;
|
||||
|
||||
error = pci_register_host_bridge(bridge);
|
||||
if (error < 0)
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
error = device_register(&bridge->dev);
|
||||
if (error) {
|
||||
put_device(&bridge->dev);
|
||||
goto err_out;
|
||||
}
|
||||
b->bridge = get_device(&bridge->dev);
|
||||
device_enable_async_suspend(b->bridge);
|
||||
pci_set_bus_of_node(b);
|
||||
pci_set_bus_msi_domain(b);
|
||||
return bridge->bus;
|
||||
|
||||
if (!parent)
|
||||
set_dev_node(b->bridge, pcibus_to_node(b));
|
||||
|
||||
b->dev.class = &pcibus_class;
|
||||
b->dev.parent = b->bridge;
|
||||
dev_set_name(&b->dev, "%04x:%02x", pci_domain_nr(b), bus);
|
||||
error = device_register(&b->dev);
|
||||
if (error)
|
||||
goto class_dev_reg_err;
|
||||
|
||||
pcibios_add_bus(b);
|
||||
|
||||
/* Create legacy_io and legacy_mem files for this bus */
|
||||
pci_create_legacy_files(b);
|
||||
|
||||
if (parent)
|
||||
dev_info(parent, "PCI host bridge to bus %s\n", dev_name(&b->dev));
|
||||
else
|
||||
printk(KERN_INFO "PCI host bridge to bus %s\n", dev_name(&b->dev));
|
||||
|
||||
/* Add initial resources to the bus */
|
||||
resource_list_for_each_entry_safe(window, n, resources) {
|
||||
list_move_tail(&window->node, &bridge->windows);
|
||||
res = window->res;
|
||||
offset = window->offset;
|
||||
if (res->flags & IORESOURCE_BUS)
|
||||
pci_bus_insert_busn_res(b, bus, res->end);
|
||||
else
|
||||
pci_bus_add_resource(b, res, 0);
|
||||
if (offset) {
|
||||
if (resource_type(res) == IORESOURCE_IO)
|
||||
fmt = " (bus address [%#06llx-%#06llx])";
|
||||
else
|
||||
fmt = " (bus address [%#010llx-%#010llx])";
|
||||
snprintf(bus_addr, sizeof(bus_addr), fmt,
|
||||
(unsigned long long) (res->start - offset),
|
||||
(unsigned long long) (res->end - offset));
|
||||
} else
|
||||
bus_addr[0] = '\0';
|
||||
dev_info(&b->dev, "root bus resource %pR%s\n", res, bus_addr);
|
||||
}
|
||||
|
||||
down_write(&pci_bus_sem);
|
||||
list_add_tail(&b->node, &pci_root_buses);
|
||||
up_write(&pci_bus_sem);
|
||||
|
||||
return b;
|
||||
|
||||
class_dev_reg_err:
|
||||
put_device(&bridge->dev);
|
||||
device_unregister(&bridge->dev);
|
||||
err_out:
|
||||
kfree(b);
|
||||
kfree(bridge);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
|
||||
struct pci_ops *ops, void *sysdata, struct list_head *resources)
|
||||
{
|
||||
return pci_create_root_bus_msi(parent, bus, ops, sysdata, resources,
|
||||
NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_create_root_bus);
|
||||
|
||||
int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int bus_max)
|
||||
|
@ -2318,12 +2366,10 @@ struct pci_bus *pci_scan_root_bus_msi(struct device *parent, int bus,
|
|||
break;
|
||||
}
|
||||
|
||||
b = pci_create_root_bus(parent, bus, ops, sysdata, resources);
|
||||
b = pci_create_root_bus_msi(parent, bus, ops, sysdata, resources, msi);
|
||||
if (!b)
|
||||
return NULL;
|
||||
|
||||
b->msi = msi;
|
||||
|
||||
if (!found) {
|
||||
dev_info(&b->dev,
|
||||
"No busn resource found for root bus, will use [bus %02x-ff]\n",
|
||||
|
|
|
@ -420,9 +420,13 @@ static inline int pci_channel_offline(struct pci_dev *pdev)
|
|||
struct pci_host_bridge {
|
||||
struct device dev;
|
||||
struct pci_bus *bus; /* root bus */
|
||||
struct pci_ops *ops;
|
||||
void *sysdata;
|
||||
int busnr;
|
||||
struct list_head windows; /* resource_entry */
|
||||
void (*release_fn)(struct pci_host_bridge *);
|
||||
void *release_data;
|
||||
struct msi_controller *msi;
|
||||
unsigned int ignore_reset_delay:1; /* for entire hierarchy */
|
||||
/* Resource alignment requirements */
|
||||
resource_size_t (*align_resource)(struct pci_dev *dev,
|
||||
|
@ -430,10 +434,23 @@ struct pci_host_bridge {
|
|||
resource_size_t start,
|
||||
resource_size_t size,
|
||||
resource_size_t align);
|
||||
unsigned long private[0] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
#define to_pci_host_bridge(n) container_of(n, struct pci_host_bridge, dev)
|
||||
|
||||
static inline void *pci_host_bridge_priv(struct pci_host_bridge *bridge)
|
||||
{
|
||||
return (void *)bridge->private;
|
||||
}
|
||||
|
||||
static inline struct pci_host_bridge *pci_host_bridge_from_priv(void *priv)
|
||||
{
|
||||
return container_of(priv, struct pci_host_bridge, private);
|
||||
}
|
||||
|
||||
struct pci_host_bridge *pci_alloc_host_bridge(size_t priv);
|
||||
int pci_register_host_bridge(struct pci_host_bridge *bridge);
|
||||
struct pci_host_bridge *pci_find_host_bridge(struct pci_bus *bus);
|
||||
|
||||
void pci_set_host_bridge_release(struct pci_host_bridge *bridge,
|
||||
|
|
Loading…
Reference in New Issue