Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a640874b authored by Olof Johansson's avatar Olof Johansson
Browse files

Merge tag 'pcie-3.11-2' of git://git.infradead.org/users/jcooper/linux into next/soc

PCI-e driver for mvebu.

* tag 'pcie-3.11-2' of git://git.infradead.org/users/jcooper/linux

:
  pci: mvebu: fix return value check in mvebu_pcie_probe()
  arm: mvebu: PCIe support is now available on mvebu
  pci: PCIe driver for Marvell Armada 370/XP systems
  clk: mvebu: add more PCIe clocks for Armada XP
  clk: mvebu: create parent-child relation for PCIe clocks on Armada 370
  of/pci: Add of_pci_parse_bus_range() function
  of/pci: Add of_pci_get_devfn() function
  of/pci: Provide support for parsing PCI DT ranges property

Signed-off-by: default avatarOlof Johansson <olof@lixom.net>
parents 48cba51d 3d9939c9
Loading
Loading
Loading
Loading
+220 −0
Original line number Diff line number Diff line
* 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";
	};
};
+2 −0
Original line number Diff line number Diff line
@@ -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

+12 −6
Original line number Diff line number Diff line
@@ -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 },
+67 −0
Original line number Diff line number Diff line
@@ -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 */

/*
+54 −5
Original line number Diff line number Diff line
@@ -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(&reg[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);
Loading