diff options
| -rw-r--r-- | Documentation/devicetree/bindings/pci/mvebu-pci.txt | 220 | ||||
| -rw-r--r-- | arch/arm/mach-mvebu/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/clk/mvebu/clk-gating-ctrl.c | 18 | ||||
| -rw-r--r-- | drivers/of/address.c | 67 | ||||
| -rw-r--r-- | drivers/of/of_pci.c | 59 | ||||
| -rw-r--r-- | drivers/pci/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/pci/Makefile | 3 | ||||
| -rw-r--r-- | drivers/pci/host/Kconfig | 8 | ||||
| -rw-r--r-- | drivers/pci/host/Makefile | 1 | ||||
| -rw-r--r-- | drivers/pci/host/pci-mvebu.c | 880 | ||||
| -rw-r--r-- | include/linux/of_address.h | 48 | ||||
| -rw-r--r-- | include/linux/of_pci.h | 2 | 
12 files changed, 1299 insertions, 11 deletions
| diff --git a/Documentation/devicetree/bindings/pci/mvebu-pci.txt b/Documentation/devicetree/bindings/pci/mvebu-pci.txt new file mode 100644 index 000000000000..eb69d92c23e1 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/mvebu-pci.txt @@ -0,0 +1,220 @@ +* Marvell EBU PCIe interfaces + +Mandatory properties: +- compatible: one of the following values: +    marvell,armada-370-pcie +    marvell,armada-xp-pcie +- #address-cells, set to <3> +- #size-cells, set to <2> +- #interrupt-cells, set to <1> +- bus-range: PCI bus numbers covered +- device_type, set to "pci" +- ranges: ranges for the PCI memory and I/O regions, as well as the +  MMIO registers to control the PCIe interfaces. + +In addition, the Device Tree node must have sub-nodes describing each +PCIe interface, having the following mandatory properties: +- reg: used only for interrupt mapping, so only the first four bytes +  are used to refer to the correct bus number and device number. +- assigned-addresses: reference to the MMIO registers used to control +  this PCIe interface. +- clocks: the clock associated to this PCIe interface +- marvell,pcie-port: the physical PCIe port number +- status: either "disabled" or "okay" +- device_type, set to "pci" +- #address-cells, set to <3> +- #size-cells, set to <2> +- #interrupt-cells, set to <1> +- ranges, empty property. +- interrupt-map-mask and interrupt-map, standard PCI properties to +  define the mapping of the PCIe interface to interrupt numbers. + +and the following optional properties: +- marvell,pcie-lane: the physical PCIe lane number, for ports having +  multiple lanes. If this property is not found, we assume that the +  value is 0. + +Example: + +pcie-controller { +	compatible = "marvell,armada-xp-pcie"; +	status = "disabled"; +	device_type = "pci"; + +	#address-cells = <3>; +	#size-cells = <2>; + +	bus-range = <0x00 0xff>; + +	ranges = <0x82000000 0 0xd0040000 0xd0040000 0 0x00002000   /* Port 0.0 registers */ +		  0x82000000 0 0xd0042000 0xd0042000 0 0x00002000   /* Port 2.0 registers */ +		  0x82000000 0 0xd0044000 0xd0044000 0 0x00002000   /* Port 0.1 registers */ +		  0x82000000 0 0xd0048000 0xd0048000 0 0x00002000   /* Port 0.2 registers */ +		  0x82000000 0 0xd004c000 0xd004c000 0 0x00002000   /* Port 0.3 registers */ +		  0x82000000 0 0xd0080000 0xd0080000 0 0x00002000   /* Port 1.0 registers */ +		  0x82000000 0 0xd0082000 0xd0082000 0 0x00002000   /* Port 3.0 registers */ +		  0x82000000 0 0xd0084000 0xd0084000 0 0x00002000   /* Port 1.1 registers */ +		  0x82000000 0 0xd0088000 0xd0088000 0 0x00002000   /* Port 1.2 registers */ +		  0x82000000 0 0xd008c000 0xd008c000 0 0x00002000   /* Port 1.3 registers */ +		  0x82000000 0 0xe0000000 0xe0000000 0 0x08000000   /* non-prefetchable memory */ +		  0x81000000 0 0	  0xe8000000 0 0x00100000>; /* downstream I/O */ + +	pcie@1,0 { +		device_type = "pci"; +		assigned-addresses = <0x82000800 0 0xd0040000 0 0x2000>; +		reg = <0x0800 0 0 0 0>; +		#address-cells = <3>; +		#size-cells = <2>; +		#interrupt-cells = <1>; +		ranges; +		interrupt-map-mask = <0 0 0 0>; +		interrupt-map = <0 0 0 0 &mpic 58>; +		marvell,pcie-port = <0>; +		marvell,pcie-lane = <0>; +		clocks = <&gateclk 5>; +		status = "disabled"; +	}; + +	pcie@2,0 { +		device_type = "pci"; +		assigned-addresses = <0x82001000 0 0xd0044000 0 0x2000>; +		reg = <0x1000 0 0 0 0>; +		#address-cells = <3>; +		#size-cells = <2>; +		#interrupt-cells = <1>; +		ranges; +		interrupt-map-mask = <0 0 0 0>; +		interrupt-map = <0 0 0 0 &mpic 59>; +		marvell,pcie-port = <0>; +		marvell,pcie-lane = <1>; +		clocks = <&gateclk 6>; +		status = "disabled"; +	}; + +	pcie@3,0 { +		device_type = "pci"; +		assigned-addresses = <0x82001800 0 0xd0048000 0 0x2000>; +		reg = <0x1800 0 0 0 0>; +		#address-cells = <3>; +		#size-cells = <2>; +		#interrupt-cells = <1>; +		ranges; +		interrupt-map-mask = <0 0 0 0>; +		interrupt-map = <0 0 0 0 &mpic 60>; +		marvell,pcie-port = <0>; +		marvell,pcie-lane = <2>; +		clocks = <&gateclk 7>; +		status = "disabled"; +	}; + +	pcie@4,0 { +		device_type = "pci"; +		assigned-addresses = <0x82002000 0 0xd004c000 0 0x2000>; +		reg = <0x2000 0 0 0 0>; +		#address-cells = <3>; +		#size-cells = <2>; +		#interrupt-cells = <1>; +		ranges; +		interrupt-map-mask = <0 0 0 0>; +		interrupt-map = <0 0 0 0 &mpic 61>; +		marvell,pcie-port = <0>; +		marvell,pcie-lane = <3>; +		clocks = <&gateclk 8>; +		status = "disabled"; +	}; + +	pcie@5,0 { +		device_type = "pci"; +		assigned-addresses = <0x82002800 0 0xd0080000 0 0x2000>; +		reg = <0x2800 0 0 0 0>; +		#address-cells = <3>; +		#size-cells = <2>; +		#interrupt-cells = <1>; +		ranges; +		interrupt-map-mask = <0 0 0 0>; +		interrupt-map = <0 0 0 0 &mpic 62>; +		marvell,pcie-port = <1>; +		marvell,pcie-lane = <0>; +		clocks = <&gateclk 9>; +		status = "disabled"; +	}; + +	pcie@6,0 { +		device_type = "pci"; +		assigned-addresses = <0x82003000 0 0xd0084000 0 0x2000>; +		reg = <0x3000 0 0 0 0>; +		#address-cells = <3>; +		#size-cells = <2>; +		#interrupt-cells = <1>; +		ranges; +		interrupt-map-mask = <0 0 0 0>; +		interrupt-map = <0 0 0 0 &mpic 63>; +		marvell,pcie-port = <1>; +		marvell,pcie-lane = <1>; +		clocks = <&gateclk 10>; +		status = "disabled"; +	}; + +	pcie@7,0 { +		device_type = "pci"; +		assigned-addresses = <0x82003800 0 0xd0088000 0 0x2000>; +		reg = <0x3800 0 0 0 0>; +		#address-cells = <3>; +		#size-cells = <2>; +		#interrupt-cells = <1>; +		ranges; +		interrupt-map-mask = <0 0 0 0>; +		interrupt-map = <0 0 0 0 &mpic 64>; +		marvell,pcie-port = <1>; +		marvell,pcie-lane = <2>; +		clocks = <&gateclk 11>; +		status = "disabled"; +	}; + +	pcie@8,0 { +		device_type = "pci"; +		assigned-addresses = <0x82004000 0 0xd008c000 0 0x2000>; +		reg = <0x4000 0 0 0 0>; +		#address-cells = <3>; +		#size-cells = <2>; +		#interrupt-cells = <1>; +		ranges; +		interrupt-map-mask = <0 0 0 0>; +		interrupt-map = <0 0 0 0 &mpic 65>; +		marvell,pcie-port = <1>; +		marvell,pcie-lane = <3>; +		clocks = <&gateclk 12>; +		status = "disabled"; +	}; +	pcie@9,0 { +		device_type = "pci"; +		assigned-addresses = <0x82004800 0 0xd0042000 0 0x2000>; +		reg = <0x4800 0 0 0 0>; +		#address-cells = <3>; +		#size-cells = <2>; +		#interrupt-cells = <1>; +		ranges; +		interrupt-map-mask = <0 0 0 0>; +		interrupt-map = <0 0 0 0 &mpic 99>; +		marvell,pcie-port = <2>; +		marvell,pcie-lane = <0>; +		clocks = <&gateclk 26>; +		status = "disabled"; +	}; + +	pcie@10,0 { +		device_type = "pci"; +		assigned-addresses = <0x82005000 0 0xd0082000 0 0x2000>; +		reg = <0x5000 0 0 0 0>; +		#address-cells = <3>; +		#size-cells = <2>; +		#interrupt-cells = <1>; +		ranges; +		interrupt-map-mask = <0 0 0 0>; +		interrupt-map = <0 0 0 0 &mpic 103>; +		marvell,pcie-port = <3>; +		marvell,pcie-lane = <0>; +		clocks = <&gateclk 27>; +		status = "disabled"; +	}; +}; diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig index 80a8bcacd9d5..fb827ac9d906 100644 --- a/arch/arm/mach-mvebu/Kconfig +++ b/arch/arm/mach-mvebu/Kconfig @@ -16,6 +16,8 @@ config ARCH_MVEBU  	select MVEBU_MBUS  	select ZONE_DMA if ARM_LPAE  	select ARCH_REQUIRE_GPIOLIB +	select MIGHT_HAVE_PCI +	select PCI_QUIRKS if PCI  if ARCH_MVEBU diff --git a/drivers/clk/mvebu/clk-gating-ctrl.c b/drivers/clk/mvebu/clk-gating-ctrl.c index ebf141d4374b..2f037235bdbd 100644 --- a/drivers/clk/mvebu/clk-gating-ctrl.c +++ b/drivers/clk/mvebu/clk-gating-ctrl.c @@ -119,8 +119,8 @@ static const struct mvebu_soc_descr __initconst armada_370_gating_descr[] = {  	{ "pex1_en", NULL,  2 },  	{ "ge1", NULL, 3 },  	{ "ge0", NULL, 4 }, -	{ "pex0", NULL, 5 }, -	{ "pex1", NULL, 9 }, +	{ "pex0", "pex0_en", 5 }, +	{ "pex1", "pex1_en", 9 },  	{ "sata0", NULL, 15 },  	{ "sdio", NULL, 17 },  	{ "tdm", NULL, 25 }, @@ -137,10 +137,14 @@ static const struct mvebu_soc_descr __initconst armada_xp_gating_descr[] = {  	{ "ge2", NULL,  2 },  	{ "ge1", NULL, 3 },  	{ "ge0", NULL, 4 }, -	{ "pex0", NULL, 5 }, -	{ "pex1", NULL, 6 }, -	{ "pex2", NULL, 7 }, -	{ "pex3", NULL, 8 }, +	{ "pex00", NULL, 5 }, +	{ "pex01", NULL, 6 }, +	{ "pex02", NULL, 7 }, +	{ "pex03", NULL, 8 }, +	{ "pex10", NULL, 9 }, +	{ "pex11", NULL, 10 }, +	{ "pex12", NULL, 11 }, +	{ "pex13", NULL, 12 },  	{ "bp", NULL, 13 },  	{ "sata0lnk", NULL, 14 },  	{ "sata0", "sata0lnk", 15 }, @@ -152,6 +156,8 @@ static const struct mvebu_soc_descr __initconst armada_xp_gating_descr[] = {  	{ "xor0", NULL, 22 },  	{ "crypto", NULL, 23 },  	{ "tdm", NULL, 25 }, +	{ "pex20", NULL, 26 }, +	{ "pex30", NULL, 27 },  	{ "xor1", NULL, 28 },  	{ "sata1lnk", NULL, 29 },  	{ "sata1", "sata1lnk", 30 }, diff --git a/drivers/of/address.c b/drivers/of/address.c index 04da786c84d2..fdd0636a987d 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -227,6 +227,73 @@ int of_pci_address_to_resource(struct device_node *dev, int bar,  	return __of_address_to_resource(dev, addrp, size, flags, NULL, r);  }  EXPORT_SYMBOL_GPL(of_pci_address_to_resource); + +int of_pci_range_parser_init(struct of_pci_range_parser *parser, +				struct device_node *node) +{ +	const int na = 3, ns = 2; +	int rlen; + +	parser->node = node; +	parser->pna = of_n_addr_cells(node); +	parser->np = parser->pna + na + ns; + +	parser->range = of_get_property(node, "ranges", &rlen); +	if (parser->range == NULL) +		return -ENOENT; + +	parser->end = parser->range + rlen / sizeof(__be32); + +	return 0; +} +EXPORT_SYMBOL_GPL(of_pci_range_parser_init); + +struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser, +						struct of_pci_range *range) +{ +	const int na = 3, ns = 2; + +	if (!range) +		return NULL; + +	if (!parser->range || parser->range + parser->np > parser->end) +		return NULL; + +	range->pci_space = parser->range[0]; +	range->flags = of_bus_pci_get_flags(parser->range); +	range->pci_addr = of_read_number(parser->range + 1, ns); +	range->cpu_addr = of_translate_address(parser->node, +				parser->range + na); +	range->size = of_read_number(parser->range + parser->pna + na, ns); + +	parser->range += parser->np; + +	/* Now consume following elements while they are contiguous */ +	while (parser->range + parser->np <= parser->end) { +		u32 flags, pci_space; +		u64 pci_addr, cpu_addr, size; + +		pci_space = be32_to_cpup(parser->range); +		flags = of_bus_pci_get_flags(parser->range); +		pci_addr = of_read_number(parser->range + 1, ns); +		cpu_addr = of_translate_address(parser->node, +				parser->range + na); +		size = of_read_number(parser->range + parser->pna + na, ns); + +		if (flags != range->flags) +			break; +		if (pci_addr != range->pci_addr + range->size || +		    cpu_addr != range->cpu_addr + range->size) +			break; + +		range->size += size; +		parser->range += parser->np; +	} + +	return range; +} +EXPORT_SYMBOL_GPL(of_pci_range_parser_one); +  #endif /* CONFIG_PCI */  /* diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c index 13e37e2d8ec1..42c687a820ac 100644 --- a/drivers/of/of_pci.c +++ b/drivers/of/of_pci.c @@ -5,14 +5,15 @@  #include <asm/prom.h>  static inline int __of_pci_pci_compare(struct device_node *node, -				       unsigned int devfn) +				       unsigned int data)  { -	unsigned int size; -	const __be32 *reg = of_get_property(node, "reg", &size); +	int devfn; -	if (!reg || size < 5 * sizeof(__be32)) +	devfn = of_pci_get_devfn(node); +	if (devfn < 0)  		return 0; -	return ((be32_to_cpup(®[0]) >> 8) & 0xff) == devfn; + +	return devfn == data;  }  struct device_node *of_pci_find_child_device(struct device_node *parent, @@ -40,3 +41,51 @@ struct device_node *of_pci_find_child_device(struct device_node *parent,  	return NULL;  }  EXPORT_SYMBOL_GPL(of_pci_find_child_device); + +/** + * of_pci_get_devfn() - Get device and function numbers for a device node + * @np: device node + * + * Parses a standard 5-cell PCI resource and returns an 8-bit value that can + * be passed to the PCI_SLOT() and PCI_FUNC() macros to extract the device + * and function numbers respectively. On error a negative error code is + * returned. + */ +int of_pci_get_devfn(struct device_node *np) +{ +	unsigned int size; +	const __be32 *reg; + +	reg = of_get_property(np, "reg", &size); + +	if (!reg || size < 5 * sizeof(__be32)) +		return -EINVAL; + +	return (be32_to_cpup(reg) >> 8) & 0xff; +} +EXPORT_SYMBOL_GPL(of_pci_get_devfn); + +/** + * of_pci_parse_bus_range() - parse the bus-range property of a PCI device + * @node: device node + * @res: address to a struct resource to return the bus-range + * + * Returns 0 on success or a negative error-code on failure. + */ +int of_pci_parse_bus_range(struct device_node *node, struct resource *res) +{ +	const __be32 *values; +	int len; + +	values = of_get_property(node, "bus-range", &len); +	if (!values || len < sizeof(*values) * 2) +		return -EINVAL; + +	res->name = node->name; +	res->start = be32_to_cpup(values++); +	res->end = be32_to_cpup(values); +	res->flags = IORESOURCE_BUS; + +	return 0; +} +EXPORT_SYMBOL_GPL(of_pci_parse_bus_range); diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 6d51aa68ec7a..ac45398ebb8e 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -119,3 +119,5 @@ config PCI_IOAPIC  config PCI_LABEL  	def_bool y if (DMI || ACPI)  	select NLS + +source "drivers/pci/host/Kconfig" diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 0c3efcffa83b..6ebf5bf8e7a7 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -67,3 +67,6 @@ obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o  obj-$(CONFIG_OF) += of.o  ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG + +# PCI host controller drivers +obj-y += host/ diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig new file mode 100644 index 000000000000..6918fbc92c02 --- /dev/null +++ b/drivers/pci/host/Kconfig @@ -0,0 +1,8 @@ +menu "PCI host controller drivers" +	depends on PCI + +config PCI_MVEBU +	bool "Marvell EBU PCIe controller" +	depends on ARCH_MVEBU + +endmenu diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile new file mode 100644 index 000000000000..5ea2d8bf013a --- /dev/null +++ b/drivers/pci/host/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c new file mode 100644 index 000000000000..9236ac0bd17b --- /dev/null +++ b/drivers/pci/host/pci-mvebu.c @@ -0,0 +1,880 @@ +/* + * PCIe driver for Marvell Armada 370 and Armada XP SoCs + * + * This file is licensed under the terms of the GNU General Public + * License version 2.  This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/mbus.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/of_address.h> +#include <linux/of_pci.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> + +/* + * PCIe unit register offsets. + */ +#define PCIE_DEV_ID_OFF		0x0000 +#define PCIE_CMD_OFF		0x0004 +#define PCIE_DEV_REV_OFF	0x0008 +#define PCIE_BAR_LO_OFF(n)	(0x0010 + ((n) << 3)) +#define PCIE_BAR_HI_OFF(n)	(0x0014 + ((n) << 3)) +#define PCIE_HEADER_LOG_4_OFF	0x0128 +#define PCIE_BAR_CTRL_OFF(n)	(0x1804 + (((n) - 1) * 4)) +#define PCIE_WIN04_CTRL_OFF(n)	(0x1820 + ((n) << 4)) +#define PCIE_WIN04_BASE_OFF(n)	(0x1824 + ((n) << 4)) +#define PCIE_WIN04_REMAP_OFF(n)	(0x182c + ((n) << 4)) +#define PCIE_WIN5_CTRL_OFF	0x1880 +#define PCIE_WIN5_BASE_OFF	0x1884 +#define PCIE_WIN5_REMAP_OFF	0x188c +#define PCIE_CONF_ADDR_OFF	0x18f8 +#define  PCIE_CONF_ADDR_EN		0x80000000 +#define  PCIE_CONF_REG(r)		((((r) & 0xf00) << 16) | ((r) & 0xfc)) +#define  PCIE_CONF_BUS(b)		(((b) & 0xff) << 16) +#define  PCIE_CONF_DEV(d)		(((d) & 0x1f) << 11) +#define  PCIE_CONF_FUNC(f)		(((f) & 0x7) << 8) +#define  PCIE_CONF_ADDR(bus, devfn, where) \ +	(PCIE_CONF_BUS(bus) | PCIE_CONF_DEV(PCI_SLOT(devfn))    | \ +	 PCIE_CONF_FUNC(PCI_FUNC(devfn)) | PCIE_CONF_REG(where) | \ +	 PCIE_CONF_ADDR_EN) +#define PCIE_CONF_DATA_OFF	0x18fc +#define PCIE_MASK_OFF		0x1910 +#define  PCIE_MASK_ENABLE_INTS          0x0f000000 +#define PCIE_CTRL_OFF		0x1a00 +#define  PCIE_CTRL_X1_MODE		0x0001 +#define PCIE_STAT_OFF		0x1a04 +#define  PCIE_STAT_BUS                  0xff00 +#define  PCIE_STAT_LINK_DOWN		BIT(0) +#define PCIE_DEBUG_CTRL         0x1a60 +#define  PCIE_DEBUG_SOFT_RESET		BIT(20) + +/* + * This product ID is registered by Marvell, and used when the Marvell + * SoC is not the root complex, but an endpoint on the PCIe bus. It is + * therefore safe to re-use this PCI ID for our emulated PCI-to-PCI + * bridge. + */ +#define MARVELL_EMULATED_PCI_PCI_BRIDGE_ID 0x7846 + +/* PCI configuration space of a PCI-to-PCI bridge */ +struct mvebu_sw_pci_bridge { +	u16 vendor; +	u16 device; +	u16 command; +	u16 status; +	u16 class; +	u8 interface; +	u8 revision; +	u8 bist; +	u8 header_type; +	u8 latency_timer; +	u8 cache_line_size; +	u32 bar[2]; +	u8 primary_bus; +	u8 secondary_bus; +	u8 subordinate_bus; +	u8 secondary_latency_timer; +	u8 iobase; +	u8 iolimit; +	u16 secondary_status; +	u16 membase; +	u16 memlimit; +	u16 prefmembase; +	u16 prefmemlimit; +	u32 prefbaseupper; +	u32 preflimitupper; +	u16 iobaseupper; +	u16 iolimitupper; +	u8 cappointer; +	u8 reserved1; +	u16 reserved2; +	u32 romaddr; +	u8 intline; +	u8 intpin; +	u16 bridgectrl; +}; + +struct mvebu_pcie_port; + +/* Structure representing all PCIe interfaces */ +struct mvebu_pcie { +	struct platform_device *pdev; +	struct mvebu_pcie_port *ports; +	struct resource io; +	struct resource realio; +	struct resource mem; +	struct resource busn; +	int nports; +}; + +/* Structure representing one PCIe interface */ +struct mvebu_pcie_port { +	char *name; +	void __iomem *base; +	spinlock_t conf_lock; +	int haslink; +	u32 port; +	u32 lane; +	int devfn; +	struct clk *clk; +	struct mvebu_sw_pci_bridge bridge; +	struct device_node *dn; +	struct mvebu_pcie *pcie; +	phys_addr_t memwin_base; +	size_t memwin_size; +	phys_addr_t iowin_base; +	size_t iowin_size; +}; + +static bool mvebu_pcie_link_up(struct mvebu_pcie_port *port) +{ +	return !(readl(port->base + PCIE_STAT_OFF) & PCIE_STAT_LINK_DOWN); +} + +static void mvebu_pcie_set_local_bus_nr(struct mvebu_pcie_port *port, int nr) +{ +	u32 stat; + +	stat = readl(port->base + PCIE_STAT_OFF); +	stat &= ~PCIE_STAT_BUS; +	stat |= nr << 8; +	writel(stat, port->base + PCIE_STAT_OFF); +} + +/* + * Setup PCIE BARs and Address Decode Wins: + * BAR[0,2] -> disabled, BAR[1] -> covers all DRAM banks + * WIN[0-3] -> DRAM bank[0-3] + */ +static void __init mvebu_pcie_setup_wins(struct mvebu_pcie_port *port) +{ +	const struct mbus_dram_target_info *dram; +	u32 size; +	int i; + +	dram = mv_mbus_dram_info(); + +	/* First, disable and clear BARs and windows. */ +	for (i = 1; i < 3; i++) { +		writel(0, port->base + PCIE_BAR_CTRL_OFF(i)); +		writel(0, port->base + PCIE_BAR_LO_OFF(i)); +		writel(0, port->base + PCIE_BAR_HI_OFF(i)); +	} + +	for (i = 0; i < 5; i++) { +		writel(0, port->base + PCIE_WIN04_CTRL_OFF(i)); +		writel(0, port->base + PCIE_WIN04_BASE_OFF(i)); +		writel(0, port->base + PCIE_WIN04_REMAP_OFF(i)); +	} + +	writel(0, port->base + PCIE_WIN5_CTRL_OFF); +	writel(0, port->base + PCIE_WIN5_BASE_OFF); +	writel(0, port->base + PCIE_WIN5_REMAP_OFF); + +	/* Setup windows for DDR banks.  Count total DDR size on the fly. */ +	size = 0; +	for (i = 0; i < dram->num_cs; i++) { +		const struct mbus_dram_window *cs = dram->cs + i; + +		writel(cs->base & 0xffff0000, +		       port->base + PCIE_WIN04_BASE_OFF(i)); +		writel(0, port->base + PCIE_WIN04_REMAP_OFF(i)); +		writel(((cs->size - 1) & 0xffff0000) | +			(cs->mbus_attr << 8) | +			(dram->mbus_dram_target_id << 4) | 1, +		       port->base + PCIE_WIN04_CTRL_OFF(i)); + +		size += cs->size; +	} + +	/* Round up 'size' to the nearest power of two. */ +	if ((size & (size - 1)) != 0) +		size = 1 << fls(size); + +	/* Setup BAR[1] to all DRAM banks. */ +	writel(dram->cs[0].base, port->base + PCIE_BAR_LO_OFF(1)); +	writel(0, port->base + PCIE_BAR_HI_OFF(1)); +	writel(((size - 1) & 0xffff0000) | 1, +	       port->base + PCIE_BAR_CTRL_OFF(1)); +} + +static void __init mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) +{ +	u16 cmd; +	u32 mask; + +	/* Point PCIe unit MBUS decode windows to DRAM space. */ +	mvebu_pcie_setup_wins(port); + +	/* Master + slave enable. */ +	cmd = readw(port->base + PCIE_CMD_OFF); +	cmd |= PCI_COMMAND_IO; +	cmd |= PCI_COMMAND_MEMORY; +	cmd |= PCI_COMMAND_MASTER; +	writew(cmd, port->base + PCIE_CMD_OFF); + +	/* Enable interrupt lines A-D. */ +	mask = readl(port->base + PCIE_MASK_OFF); +	mask |= PCIE_MASK_ENABLE_INTS; +	writel(mask, port->base + PCIE_MASK_OFF); +} + +static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port, +				 struct pci_bus *bus, +				 u32 devfn, int where, int size, u32 *val) +{ +	writel(PCIE_CONF_ADDR(bus->number, devfn, where), +	       port->base + PCIE_CONF_ADDR_OFF); + +	*val = readl(port->base + PCIE_CONF_DATA_OFF); + +	if (size == 1) +		*val = (*val >> (8 * (where & 3))) & 0xff; +	else if (size == 2) +		*val = (*val >> (8 * (where & 3))) & 0xffff; + +	return PCIBIOS_SUCCESSFUL; +} + +static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port, +				 struct pci_bus *bus, +				 u32 devfn, int where, int size, u32 val) +{ +	int ret = PCIBIOS_SUCCESSFUL; + +	writel(PCIE_CONF_ADDR(bus->number, devfn, where), +	       port->base + PCIE_CONF_ADDR_OFF); + +	if (size == 4) +		writel(val, port->base + PCIE_CONF_DATA_OFF); +	else if (size == 2) +		writew(val, port->base + PCIE_CONF_DATA_OFF + (where & 3)); +	else if (size == 1) +		writeb(val, port->base + PCIE_CONF_DATA_OFF + (where & 3)); +	else +		ret = PCIBIOS_BAD_REGISTER_NUMBER; + +	return ret; +} + +static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) +{ +	phys_addr_t iobase; + +	/* Are the new iobase/iolimit values invalid? */ +	if (port->bridge.iolimit < port->bridge.iobase || +	    port->bridge.iolimitupper < port->bridge.iobaseupper) { + +		/* If a window was configured, remove it */ +		if (port->iowin_base) { +			mvebu_mbus_del_window(port->iowin_base, +					      port->iowin_size); +			port->iowin_base = 0; +			port->iowin_size = 0; +		} + +		return; +	} + +	/* +	 * We read the PCI-to-PCI bridge emulated registers, and +	 * calculate the base address and size of the address decoding +	 * window to setup, according to the PCI-to-PCI bridge +	 * specifications. iobase is the bus address, port->iowin_base +	 * is the CPU address. +	 */ +	iobase = ((port->bridge.iobase & 0xF0) << 8) | +		(port->bridge.iobaseupper << 16); +	port->iowin_base = port->pcie->io.start + iobase; +	port->iowin_size = ((0xFFF | ((port->bridge.iolimit & 0xF0) << 8) | +			    (port->bridge.iolimitupper << 16)) - +			    iobase); + +	mvebu_mbus_add_window_remap_flags(port->name, port->iowin_base, +					  port->iowin_size, +					  iobase, +					  MVEBU_MBUS_PCI_IO); + +	pci_ioremap_io(iobase, port->iowin_base); +} + +static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) +{ +	/* Are the new membase/memlimit values invalid? */ +	if (port->bridge.memlimit < port->bridge.membase) { + +		/* If a window was configured, remove it */ +		if (port->memwin_base) { +			mvebu_mbus_del_window(port->memwin_base, +					      port->memwin_size); +			port->memwin_base = 0; +			port->memwin_size = 0; +		} + +		return; +	} + +	/* +	 * We read the PCI-to-PCI bridge emulated registers, and +	 * calculate the base address and size of the address decoding +	 * window to setup, according to the PCI-to-PCI bridge +	 * specifications. +	 */ +	port->memwin_base  = ((port->bridge.membase & 0xFFF0) << 16); +	port->memwin_size  = +		(((port->bridge.memlimit & 0xFFF0) << 16) | 0xFFFFF) - +		port->memwin_base; + +	mvebu_mbus_add_window_remap_flags(port->name, port->memwin_base, +					  port->memwin_size, +					  MVEBU_MBUS_NO_REMAP, +					  MVEBU_MBUS_PCI_MEM); +} + +/* + * Initialize the configuration space of the PCI-to-PCI bridge + * associated with the given PCIe interface. + */ +static void mvebu_sw_pci_bridge_init(struct mvebu_pcie_port *port) +{ +	struct mvebu_sw_pci_bridge *bridge = &port->bridge; + +	memset(bridge, 0, sizeof(struct mvebu_sw_pci_bridge)); + +	bridge->status = PCI_STATUS_CAP_LIST; +	bridge->class = PCI_CLASS_BRIDGE_PCI; +	bridge->vendor = PCI_VENDOR_ID_MARVELL; +	bridge->device = MARVELL_EMULATED_PCI_PCI_BRIDGE_ID; +	bridge->header_type = PCI_HEADER_TYPE_BRIDGE; +	bridge->cache_line_size = 0x10; + +	/* We support 32 bits I/O addressing */ +	bridge->iobase = PCI_IO_RANGE_TYPE_32; +	bridge->iolimit = PCI_IO_RANGE_TYPE_32; +} + +/* + * Read the configuration space of the PCI-to-PCI bridge associated to + * the given PCIe interface. + */ +static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port, +				  unsigned int where, int size, u32 *value) +{ +	struct mvebu_sw_pci_bridge *bridge = &port->bridge; + +	switch (where & ~3) { +	case PCI_VENDOR_ID: +		*value = bridge->device << 16 | bridge->vendor; +		break; + +	case PCI_COMMAND: +		*value = bridge->status << 16 | bridge->command; +		break; + +	case PCI_CLASS_REVISION: +		*value = bridge->class << 16 | bridge->interface << 8 | +			 bridge->revision; +		break; + +	case PCI_CACHE_LINE_SIZE: +		*value = bridge->bist << 24 | bridge->header_type << 16 | +			 bridge->latency_timer << 8 | bridge->cache_line_size; +		break; + +	case PCI_BASE_ADDRESS_0 ... PCI_BASE_ADDRESS_1: +		*value = bridge->bar[((where & ~3) - PCI_BASE_ADDRESS_0) / 4]; +		break; + +	case PCI_PRIMARY_BUS: +		*value = (bridge->secondary_latency_timer << 24 | +			  bridge->subordinate_bus         << 16 | +			  bridge->secondary_bus           <<  8 | +			  bridge->primary_bus); +		break; + +	case PCI_IO_BASE: +		*value = (bridge->secondary_status << 16 | +			  bridge->iolimit          <<  8 | +			  bridge->iobase); +		break; + +	case PCI_MEMORY_BASE: +		*value = (bridge->memlimit << 16 | bridge->membase); +		break; + +	case PCI_PREF_MEMORY_BASE: +		*value = (bridge->prefmemlimit << 16 | bridge->prefmembase); +		break; + +	case PCI_PREF_BASE_UPPER32: +		*value = bridge->prefbaseupper; +		break; + +	case PCI_PREF_LIMIT_UPPER32: +		*value = bridge->preflimitupper; +		break; + +	case PCI_IO_BASE_UPPER16: +		*value = (bridge->iolimitupper << 16 | bridge->iobaseupper); +		break; + +	case PCI_ROM_ADDRESS1: +		*value = 0; +		break; + +	default: +		*value = 0xffffffff; +		return PCIBIOS_BAD_REGISTER_NUMBER; +	} + +	if (size == 2) +		*value = (*value >> (8 * (where & 3))) & 0xffff; +	else if (size == 1) +		*value = (*value >> (8 * (where & 3))) & 0xff; + +	return PCIBIOS_SUCCESSFUL; +} + +/* Write to the PCI-to-PCI bridge configuration space */ +static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port, +				     unsigned int where, int size, u32 value) +{ +	struct mvebu_sw_pci_bridge *bridge = &port->bridge; +	u32 mask, reg; +	int err; + +	if (size == 4) +		mask = 0x0; +	else if (size == 2) +		mask = ~(0xffff << ((where & 3) * 8)); +	else if (size == 1) +		mask = ~(0xff << ((where & 3) * 8)); +	else +		return PCIBIOS_BAD_REGISTER_NUMBER; + +	err = mvebu_sw_pci_bridge_read(port, where & ~3, 4, ®); +	if (err) +		return err; + +	value = (reg & mask) | value << ((where & 3) * 8); + +	switch (where & ~3) { +	case PCI_COMMAND: +		bridge->command = value & 0xffff; +		bridge->status = value >> 16; +		break; + +	case PCI_BASE_ADDRESS_0 ... PCI_BASE_ADDRESS_1: +		bridge->bar[((where & ~3) - PCI_BASE_ADDRESS_0) / 4] = value; +		break; + +	case PCI_IO_BASE: +		/* +		 * We also keep bit 1 set, it is a read-only bit that +		 * indicates we support 32 bits addressing for the +		 * I/O +		 */ +		bridge->iobase = (value & 0xff) | PCI_IO_RANGE_TYPE_32; +		bridge->iolimit = ((value >> 8) & 0xff) | PCI_IO_RANGE_TYPE_32; +		bridge->secondary_status = value >> 16; +		mvebu_pcie_handle_iobase_change(port); +		break; + +	case PCI_MEMORY_BASE: +		bridge->membase = value & 0xffff; +		bridge->memlimit = value >> 16; +		mvebu_pcie_handle_membase_change(port); +		break; + +	case PCI_PREF_MEMORY_BASE: +		bridge->prefmembase = value & 0xffff; +		bridge->prefmemlimit = value >> 16; +		break; + +	case PCI_PREF_BASE_UPPER32: +		bridge->prefbaseupper = value; +		break; + +	case PCI_PREF_LIMIT_UPPER32: +		bridge->preflimitupper = value; +		break; + +	case PCI_IO_BASE_UPPER16: +		bridge->iobaseupper = value & 0xffff; +		bridge->iolimitupper = value >> 16; +		mvebu_pcie_handle_iobase_change(port); +		break; + +	case PCI_PRIMARY_BUS: +		bridge->primary_bus             = value & 0xff; +		bridge->secondary_bus           = (value >> 8) & 0xff; +		bridge->subordinate_bus         = (value >> 16) & 0xff; +		bridge->secondary_latency_timer = (value >> 24) & 0xff; +		mvebu_pcie_set_local_bus_nr(port, bridge->secondary_bus); +		break; + +	default: +		break; +	} + +	return PCIBIOS_SUCCESSFUL; +} + +static inline struct mvebu_pcie *sys_to_pcie(struct pci_sys_data *sys) +{ +	return sys->private_data; +} + +static struct mvebu_pcie_port * +mvebu_pcie_find_port(struct mvebu_pcie *pcie, struct pci_bus *bus, +		     int devfn) +{ +	int i; + +	for (i = 0; i < pcie->nports; i++) { +		struct mvebu_pcie_port *port = &pcie->ports[i]; +		if (bus->number == 0 && port->devfn == devfn) +			return port; +		if (bus->number != 0 && +		    port->bridge.secondary_bus == bus->number) +			return port; +	} + +	return NULL; +} + +/* PCI configuration space write function */ +static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn, +			      int where, int size, u32 val) +{ +	struct mvebu_pcie *pcie = sys_to_pcie(bus->sysdata); +	struct mvebu_pcie_port *port; +	unsigned long flags; +	int ret; + +	port = mvebu_pcie_find_port(pcie, bus, devfn); +	if (!port) +		return PCIBIOS_DEVICE_NOT_FOUND; + +	/* Access the emulated PCI-to-PCI bridge */ +	if (bus->number == 0) +		return mvebu_sw_pci_bridge_write(port, where, size, val); + +	if (!port->haslink || PCI_SLOT(devfn) != 0) +		return PCIBIOS_DEVICE_NOT_FOUND; + +	/* Access the real PCIe interface */ +	spin_lock_irqsave(&port->conf_lock, flags); +	ret = mvebu_pcie_hw_wr_conf(port, bus, +				    PCI_DEVFN(1, PCI_FUNC(devfn)), +				    where, size, val); +	spin_unlock_irqrestore(&port->conf_lock, flags); + +	return ret; +} + +/* PCI configuration space read function */ +static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, +			      int size, u32 *val) +{ +	struct mvebu_pcie *pcie = sys_to_pcie(bus->sysdata); +	struct mvebu_pcie_port *port; +	unsigned long flags; +	int ret; + +	port = mvebu_pcie_find_port(pcie, bus, devfn); +	if (!port) { +		*val = 0xffffffff; +		return PCIBIOS_DEVICE_NOT_FOUND; +	} + +	/* Access the emulated PCI-to-PCI bridge */ +	if (bus->number == 0) +		return mvebu_sw_pci_bridge_read(port, where, size, val); + +	if (!port->haslink || PCI_SLOT(devfn) != 0) { +		*val = 0xffffffff; +		return PCIBIOS_DEVICE_NOT_FOUND; +	} + +	/* Access the real PCIe interface */ +	spin_lock_irqsave(&port->conf_lock, flags); +	ret = mvebu_pcie_hw_rd_conf(port, bus, +				    PCI_DEVFN(1, PCI_FUNC(devfn)), +				    where, size, val); +	spin_unlock_irqrestore(&port->conf_lock, flags); + +	return ret; +} + +static struct pci_ops mvebu_pcie_ops = { +	.read = mvebu_pcie_rd_conf, +	.write = mvebu_pcie_wr_conf, +}; + +static int __init mvebu_pcie_setup(int nr, struct pci_sys_data *sys) +{ +	struct mvebu_pcie *pcie = sys_to_pcie(sys); +	int i; + +	pci_add_resource_offset(&sys->resources, &pcie->realio, sys->io_offset); +	pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset); +	pci_add_resource(&sys->resources, &pcie->busn); + +	for (i = 0; i < pcie->nports; i++) { +		struct mvebu_pcie_port *port = &pcie->ports[i]; +		mvebu_pcie_setup_hw(port); +	} + +	return 1; +} + +static int __init mvebu_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +{ +	struct of_irq oirq; +	int ret; + +	ret = of_irq_map_pci(dev, &oirq); +	if (ret) +		return ret; + +	return irq_create_of_mapping(oirq.controller, oirq.specifier, +				     oirq.size); +} + +static struct pci_bus *mvebu_pcie_scan_bus(int nr, struct pci_sys_data *sys) +{ +	struct mvebu_pcie *pcie = sys_to_pcie(sys); +	struct pci_bus *bus; + +	bus = pci_create_root_bus(&pcie->pdev->dev, sys->busnr, +				  &mvebu_pcie_ops, sys, &sys->resources); +	if (!bus) +		return NULL; + +	pci_scan_child_bus(bus); + +	return bus; +} + +resource_size_t mvebu_pcie_align_resource(struct pci_dev *dev, +					  const struct resource *res, +					  resource_size_t start, +					  resource_size_t size, +					  resource_size_t align) +{ +	if (dev->bus->number != 0) +		return start; + +	/* +	 * On the PCI-to-PCI bridge side, the I/O windows must have at +	 * least a 64 KB size and be aligned on their size, and the +	 * memory windows must have at least a 1 MB size and be +	 * aligned on their size +	 */ +	if (res->flags & IORESOURCE_IO) +		return round_up(start, max((resource_size_t)SZ_64K, size)); +	else if (res->flags & IORESOURCE_MEM) +		return round_up(start, max((resource_size_t)SZ_1M, size)); +	else +		return start; +} + +static void __init mvebu_pcie_enable(struct mvebu_pcie *pcie) +{ +	struct hw_pci hw; + +	memset(&hw, 0, sizeof(hw)); + +	hw.nr_controllers = 1; +	hw.private_data   = (void **)&pcie; +	hw.setup          = mvebu_pcie_setup; +	hw.scan           = mvebu_pcie_scan_bus; +	hw.map_irq        = mvebu_pcie_map_irq; +	hw.ops            = &mvebu_pcie_ops; +	hw.align_resource = mvebu_pcie_align_resource; + +	pci_common_init(&hw); +} + +/* + * Looks up the list of register addresses encoded into the reg = + * <...> property for one that matches the given port/lane. Once + * found, maps it. + */ +static void __iomem * __init +mvebu_pcie_map_registers(struct platform_device *pdev, +			 struct device_node *np, +			 struct mvebu_pcie_port *port) +{ +	struct resource regs; +	int ret = 0; + +	ret = of_address_to_resource(np, 0, ®s); +	if (ret) +		return NULL; + +	return devm_request_and_ioremap(&pdev->dev, ®s); +} + +static int __init mvebu_pcie_probe(struct platform_device *pdev) +{ +	struct mvebu_pcie *pcie; +	struct device_node *np = pdev->dev.of_node; +	struct of_pci_range range; +	struct of_pci_range_parser parser; +	struct device_node *child; +	int i, ret; + +	pcie = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_pcie), +			    GFP_KERNEL); +	if (!pcie) +		return -ENOMEM; + +	pcie->pdev = pdev; + +	if (of_pci_range_parser_init(&parser, np)) +		return -EINVAL; + +	/* Get the I/O and memory ranges from DT */ +	for_each_of_pci_range(&parser, &range) { +		unsigned long restype = range.flags & IORESOURCE_TYPE_BITS; +		if (restype == IORESOURCE_IO) { +			of_pci_range_to_resource(&range, np, &pcie->io); +			of_pci_range_to_resource(&range, np, &pcie->realio); +			pcie->io.name = "I/O"; +			pcie->realio.start = max_t(resource_size_t, +						   PCIBIOS_MIN_IO, +						   range.pci_addr); +			pcie->realio.end = min_t(resource_size_t, +						 IO_SPACE_LIMIT, +						 range.pci_addr + range.size); +		} +		if (restype == IORESOURCE_MEM) { +			of_pci_range_to_resource(&range, np, &pcie->mem); +			pcie->mem.name = "MEM"; +		} +	} + +	/* Get the bus range */ +	ret = of_pci_parse_bus_range(np, &pcie->busn); +	if (ret) { +		dev_err(&pdev->dev, "failed to parse bus-range property: %d\n", +			ret); +		return ret; +	} + +	for_each_child_of_node(pdev->dev.of_node, child) { +		if (!of_device_is_available(child)) +			continue; +		pcie->nports++; +	} + +	pcie->ports = devm_kzalloc(&pdev->dev, pcie->nports * +				   sizeof(struct mvebu_pcie_port), +				   GFP_KERNEL); +	if (!pcie->ports) +		return -ENOMEM; + +	i = 0; +	for_each_child_of_node(pdev->dev.of_node, child) { +		struct mvebu_pcie_port *port = &pcie->ports[i]; + +		if (!of_device_is_available(child)) +			continue; + +		port->pcie = pcie; + +		if (of_property_read_u32(child, "marvell,pcie-port", +					 &port->port)) { +			dev_warn(&pdev->dev, +				 "ignoring PCIe DT node, missing pcie-port property\n"); +			continue; +		} + +		if (of_property_read_u32(child, "marvell,pcie-lane", +					 &port->lane)) +			port->lane = 0; + +		port->name = kasprintf(GFP_KERNEL, "pcie%d.%d", +				       port->port, port->lane); + +		port->devfn = of_pci_get_devfn(child); +		if (port->devfn < 0) +			continue; + +		port->base = mvebu_pcie_map_registers(pdev, child, port); +		if (!port->base) { +			dev_err(&pdev->dev, "PCIe%d.%d: cannot map registers\n", +				port->port, port->lane); +			continue; +		} + +		if (mvebu_pcie_link_up(port)) { +			port->haslink = 1; +			dev_info(&pdev->dev, "PCIe%d.%d: link up\n", +				 port->port, port->lane); +		} else { +			port->haslink = 0; +			dev_info(&pdev->dev, "PCIe%d.%d: link down\n", +				 port->port, port->lane); +		} + +		port->clk = of_clk_get_by_name(child, NULL); +		if (IS_ERR(port->clk)) { +			dev_err(&pdev->dev, "PCIe%d.%d: cannot get clock\n", +			       port->port, port->lane); +			iounmap(port->base); +			port->haslink = 0; +			continue; +		} + +		port->dn = child; + +		clk_prepare_enable(port->clk); +		spin_lock_init(&port->conf_lock); + +		mvebu_sw_pci_bridge_init(port); + +		i++; +	} + +	mvebu_pcie_enable(pcie); + +	return 0; +} + +static const struct of_device_id mvebu_pcie_of_match_table[] = { +	{ .compatible = "marvell,armada-xp-pcie", }, +	{ .compatible = "marvell,armada-370-pcie", }, +	{}, +}; +MODULE_DEVICE_TABLE(of, mvebu_pcie_of_match_table); + +static struct platform_driver mvebu_pcie_driver = { +	.driver = { +		.owner = THIS_MODULE, +		.name = "mvebu-pcie", +		.of_match_table = +		   of_match_ptr(mvebu_pcie_of_match_table), +	}, +}; + +static int __init mvebu_pcie_init(void) +{ +	return platform_driver_probe(&mvebu_pcie_driver, +				     mvebu_pcie_probe); +} + +subsys_initcall(mvebu_pcie_init); + +MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>"); +MODULE_DESCRIPTION("Marvell EBU PCIe driver"); +MODULE_LICENSE("GPLv2"); diff --git a/include/linux/of_address.h b/include/linux/of_address.h index 0506eb53519b..4c2e6f26432c 100644 --- a/include/linux/of_address.h +++ b/include/linux/of_address.h @@ -4,6 +4,36 @@  #include <linux/errno.h>  #include <linux/of.h> +struct of_pci_range_parser { +	struct device_node *node; +	const __be32 *range; +	const __be32 *end; +	int np; +	int pna; +}; + +struct of_pci_range { +	u32 pci_space; +	u64 pci_addr; +	u64 cpu_addr; +	u64 size; +	u32 flags; +}; + +#define for_each_of_pci_range(parser, range) \ +	for (; of_pci_range_parser_one(parser, range);) + +static inline void of_pci_range_to_resource(struct of_pci_range *range, +					    struct device_node *np, +					    struct resource *res) +{ +	res->flags = range->flags; +	res->start = range->cpu_addr; +	res->end = range->cpu_addr + range->size - 1; +	res->parent = res->child = res->sibling = NULL; +	res->name = np->full_name; +} +  #ifdef CONFIG_OF_ADDRESS  extern u64 of_translate_address(struct device_node *np, const __be32 *addr);  extern bool of_can_translate_address(struct device_node *dev); @@ -27,6 +57,11 @@ static inline unsigned long pci_address_to_pio(phys_addr_t addr) { return -1; }  #define pci_address_to_pio pci_address_to_pio  #endif +extern int of_pci_range_parser_init(struct of_pci_range_parser *parser, +			struct device_node *node); +extern struct of_pci_range *of_pci_range_parser_one( +					struct of_pci_range_parser *parser, +					struct of_pci_range *range);  #else /* CONFIG_OF_ADDRESS */  #ifndef of_address_to_resource  static inline int of_address_to_resource(struct device_node *dev, int index, @@ -53,6 +88,19 @@ static inline const __be32 *of_get_address(struct device_node *dev, int index,  {  	return NULL;  } + +static inline int of_pci_range_parser_init(struct of_pci_range_parser *parser, +			struct device_node *node) +{ +	return -1; +} + +static inline struct of_pci_range *of_pci_range_parser_one( +					struct of_pci_range_parser *parser, +					struct of_pci_range *range) +{ +	return NULL; +}  #endif /* CONFIG_OF_ADDRESS */ diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h index bb115deb7612..7a04826018c0 100644 --- a/include/linux/of_pci.h +++ b/include/linux/of_pci.h @@ -10,5 +10,7 @@ int of_irq_map_pci(const struct pci_dev *pdev, struct of_irq *out_irq);  struct device_node;  struct device_node *of_pci_find_child_device(struct device_node *parent,  					     unsigned int devfn); +int of_pci_get_devfn(struct device_node *np); +int of_pci_parse_bus_range(struct device_node *node, struct resource *res);  #endif | 
