Commit d34efd22 authored by Bjorn Helgaas's avatar Bjorn Helgaas

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
parents f77b3244 af099eab
......@@ -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.
......
This diff is collapsed.
......@@ -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);
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);
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;
list_splice_init(resources, &bridge->windows);
bridge->sysdata = sysdata;
bridge->busnr = bus;
bridge->ops = ops;
bridge->msi = msi;
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);
}
error = pci_register_host_bridge(bridge);
if (error < 0)
goto err_out;
down_write(&pci_bus_sem);
list_add_tail(&b->node, &pci_root_buses);
up_write(&pci_bus_sem);
return bridge->bus;
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,
......
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