-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branches 'pci/host-generic' and 'pci/host-versatile' into next
* pci/host-generic: of/pci: Free resources on failure in of_pci_get_host_bridge_resources() * pci/host-versatile: PCI: versatile: Add DT-based ARM Versatile PB PCIe host driver ARM: dts: versatile: add PCI controller binding PCI: versatile: Add DT docs for ARM Versatile PB PCIe driver
- Loading branch information
Showing
7 changed files
with
350 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
* ARM Versatile Platform Baseboard PCI interface | ||
|
||
PCI host controller found on the ARM Versatile PB board's FPGA. | ||
|
||
Required properties: | ||
- compatible: should contain "arm,versatile-pci" to identify the Versatile PCI | ||
controller. | ||
- reg: base addresses and lengths of the pci controller. There must be 3 | ||
entries: | ||
- Versatile-specific registers | ||
- Self Config space | ||
- Config space | ||
- #address-cells: set to <3> | ||
- #size-cells: set to <2> | ||
- device_type: set to "pci" | ||
- bus-range: set to <0 0xff> | ||
- ranges: ranges for the PCI memory and I/O regions | ||
- #interrupt-cells: set to <1> | ||
- interrupt-map-mask and interrupt-map: standard PCI properties to define | ||
the mapping of the PCI interface to interrupt numbers. | ||
|
||
Example: | ||
|
||
pci-controller@10001000 { | ||
compatible = "arm,versatile-pci"; | ||
device_type = "pci"; | ||
reg = <0x10001000 0x1000 | ||
0x41000000 0x10000 | ||
0x42000000 0x100000>; | ||
bus-range = <0 0xff>; | ||
#address-cells = <3>; | ||
#size-cells = <2>; | ||
#interrupt-cells = <1>; | ||
|
||
ranges = <0x01000000 0 0x00000000 0x43000000 0 0x00010000 /* downstream I/O */ | ||
0x02000000 0 0x50000000 0x50000000 0 0x10000000 /* non-prefetchable memory */ | ||
0x42000000 0 0x60000000 0x60000000 0 0x10000000>; /* prefetchable memory */ | ||
|
||
interrupt-map-mask = <0x1800 0 0 7>; | ||
interrupt-map = <0x1800 0 0 1 &sic 28 | ||
0x1800 0 0 2 &sic 29 | ||
0x1800 0 0 3 &sic 30 | ||
0x1800 0 0 4 &sic 27 | ||
|
||
0x1000 0 0 1 &sic 27 | ||
0x1000 0 0 2 &sic 28 | ||
0x1000 0 0 3 &sic 29 | ||
0x1000 0 0 4 &sic 30 | ||
|
||
0x0800 0 0 1 &sic 30 | ||
0x0800 0 0 2 &sic 27 | ||
0x0800 0 0 3 &sic 28 | ||
0x0800 0 0 4 &sic 29 | ||
|
||
0x0000 0 0 1 &sic 29 | ||
0x0000 0 0 2 &sic 30 | ||
0x0000 0 0 3 &sic 27 | ||
0x0000 0 0 4 &sic 28>; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,237 @@ | ||
/* | ||
* Copyright 2004 Koninklijke Philips Electronics NV | ||
* | ||
* Conversion to platform driver and DT: | ||
* Copyright 2014 Linaro Ltd. | ||
* | ||
* This software is licensed under the terms of the GNU General Public | ||
* License version 2, as published by the Free Software Foundation, and | ||
* may be copied, distributed, and modified under those terms. | ||
* | ||
* 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. | ||
* | ||
* 14/04/2005 Initial version, colin.king@philips.com | ||
*/ | ||
#include <linux/kernel.h> | ||
#include <linux/module.h> | ||
#include <linux/of_address.h> | ||
#include <linux/of_pci.h> | ||
#include <linux/of_platform.h> | ||
#include <linux/pci.h> | ||
#include <linux/platform_device.h> | ||
|
||
static void __iomem *versatile_pci_base; | ||
static void __iomem *versatile_cfg_base[2]; | ||
|
||
#define PCI_IMAP(m) (versatile_pci_base + ((m) * 4)) | ||
#define PCI_SMAP(m) (versatile_pci_base + 0x14 + ((m) * 4)) | ||
#define PCI_SELFID (versatile_pci_base + 0xc) | ||
|
||
#define VP_PCI_DEVICE_ID 0x030010ee | ||
#define VP_PCI_CLASS_ID 0x0b400000 | ||
|
||
static u32 pci_slot_ignore; | ||
|
||
static int __init versatile_pci_slot_ignore(char *str) | ||
{ | ||
int retval; | ||
int slot; | ||
|
||
while ((retval = get_option(&str, &slot))) { | ||
if ((slot < 0) || (slot > 31)) | ||
pr_err("Illegal slot value: %d\n", slot); | ||
else | ||
pci_slot_ignore |= (1 << slot); | ||
} | ||
return 1; | ||
} | ||
__setup("pci_slot_ignore=", versatile_pci_slot_ignore); | ||
|
||
|
||
static void __iomem *versatile_map_bus(struct pci_bus *bus, | ||
unsigned int devfn, int offset) | ||
{ | ||
unsigned int busnr = bus->number; | ||
|
||
if (pci_slot_ignore & (1 << PCI_SLOT(devfn))) | ||
return NULL; | ||
|
||
return versatile_cfg_base[1] + ((busnr << 16) | (devfn << 8) | offset); | ||
} | ||
|
||
static struct pci_ops pci_versatile_ops = { | ||
.map_bus = versatile_map_bus, | ||
.read = pci_generic_config_read32, | ||
.write = pci_generic_config_write, | ||
}; | ||
|
||
static int versatile_pci_parse_request_of_pci_ranges(struct device *dev, | ||
struct list_head *res) | ||
{ | ||
int err, mem = 1, res_valid = 0; | ||
struct device_node *np = dev->of_node; | ||
resource_size_t iobase; | ||
struct pci_host_bridge_window *win; | ||
|
||
err = of_pci_get_host_bridge_resources(np, 0, 0xff, res, &iobase); | ||
if (err) | ||
return err; | ||
|
||
list_for_each_entry(win, res, list) { | ||
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); | ||
|
||
writel(res->start >> 28, PCI_IMAP(mem)); | ||
writel(PHYS_OFFSET >> 28, PCI_SMAP(mem)); | ||
mem++; | ||
|
||
break; | ||
case IORESOURCE_BUS: | ||
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: | ||
pci_free_resource_list(res); | ||
return err; | ||
} | ||
|
||
/* Unused, temporary to satisfy ARM arch code */ | ||
struct pci_sys_data sys; | ||
|
||
static int versatile_pci_probe(struct platform_device *pdev) | ||
{ | ||
struct resource *res; | ||
int ret, i, myslot = -1; | ||
u32 val; | ||
void __iomem *local_pci_cfg_base; | ||
struct pci_bus *bus; | ||
LIST_HEAD(pci_res); | ||
|
||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
if (!res) | ||
return -ENODEV; | ||
versatile_pci_base = devm_ioremap_resource(&pdev->dev, res); | ||
|
||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
if (!res) | ||
return -ENODEV; | ||
versatile_cfg_base[0] = devm_ioremap_resource(&pdev->dev, res); | ||
|
||
res = platform_get_resource(pdev, IORESOURCE_MEM, 2); | ||
if (!res) | ||
return -ENODEV; | ||
versatile_cfg_base[1] = devm_ioremap_resource(&pdev->dev, res); | ||
|
||
ret = versatile_pci_parse_request_of_pci_ranges(&pdev->dev, &pci_res); | ||
if (ret) | ||
return ret; | ||
|
||
/* | ||
* We need to discover the PCI core first to configure itself | ||
* before the main PCI probing is performed | ||
*/ | ||
for (i = 0; i < 32; i++) { | ||
if ((readl(versatile_cfg_base[0] + (i << 11) + PCI_VENDOR_ID) == VP_PCI_DEVICE_ID) && | ||
(readl(versatile_cfg_base[0] + (i << 11) + PCI_CLASS_REVISION) == VP_PCI_CLASS_ID)) { | ||
myslot = i; | ||
break; | ||
} | ||
} | ||
if (myslot == -1) { | ||
dev_err(&pdev->dev, "Cannot find PCI core!\n"); | ||
return -EIO; | ||
} | ||
/* | ||
* Do not to map Versatile FPGA PCI device into memory space | ||
*/ | ||
pci_slot_ignore |= (1 << myslot); | ||
|
||
dev_info(&pdev->dev, "PCI core found (slot %d)\n", myslot); | ||
|
||
writel(myslot, PCI_SELFID); | ||
local_pci_cfg_base = versatile_cfg_base[1] + (myslot << 11); | ||
|
||
val = readl(local_pci_cfg_base + PCI_COMMAND); | ||
val |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE; | ||
writel(val, local_pci_cfg_base + PCI_COMMAND); | ||
|
||
/* | ||
* Configure the PCI inbound memory windows to be 1:1 mapped to SDRAM | ||
*/ | ||
writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_0); | ||
writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_1); | ||
writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_2); | ||
|
||
/* | ||
* For many years the kernel and QEMU were symbiotically buggy | ||
* in that they both assumed the same broken IRQ mapping. | ||
* QEMU therefore attempts to auto-detect old broken kernels | ||
* so that they still work on newer QEMU as they did on old | ||
* QEMU. Since we now use the correct (ie matching-hardware) | ||
* IRQ mapping we write a definitely different value to a | ||
* PCI_INTERRUPT_LINE register to tell QEMU that we expect | ||
* real hardware behaviour and it need not be backwards | ||
* compatible for us. This write is harmless on real hardware. | ||
*/ | ||
writel(0, versatile_cfg_base[0] + PCI_INTERRUPT_LINE); | ||
|
||
pci_add_flags(PCI_ENABLE_PROC_DOMAINS); | ||
pci_add_flags(PCI_REASSIGN_ALL_BUS | PCI_REASSIGN_ALL_RSRC); | ||
|
||
bus = pci_scan_root_bus(&pdev->dev, 0, &pci_versatile_ops, &sys, &pci_res); | ||
if (!bus) | ||
return -ENOMEM; | ||
|
||
pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci); | ||
pci_assign_unassigned_bus_resources(bus); | ||
|
||
return 0; | ||
} | ||
|
||
static const struct of_device_id versatile_pci_of_match[] = { | ||
{ .compatible = "arm,versatile-pci", }, | ||
{ }, | ||
}; | ||
MODULE_DEVICE_TABLE(of, versatile_pci_of_match); | ||
|
||
static struct platform_driver versatile_pci_driver = { | ||
.driver = { | ||
.name = "versatile-pci", | ||
.of_match_table = versatile_pci_of_match, | ||
}, | ||
.probe = versatile_pci_probe, | ||
}; | ||
module_platform_driver(versatile_pci_driver); | ||
|
||
MODULE_DESCRIPTION("Versatile PCI driver"); | ||
MODULE_LICENSE("GPL v2"); |