mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH v2 00/19] PCI and Tegra series revamp
@ 2014-10-04 17:40 Lucas Stach
  2014-10-04 17:40 ` [PATCH v2 01/19] MIPS: malta: fix pci IO resource assignment Lucas Stach
                   ` (19 more replies)
  0 siblings, 20 replies; 21+ messages in thread
From: Lucas Stach @ 2014-10-04 17:40 UTC (permalink / raw)
  To: barebox

Ok, I got around to work a bit more on this. I now
actually finished the rtl8169 network driver, so
this series yields working PCI attached ethernet
on Tegra30 Beaver.

For Jetson K1 some work is left to setup the PHYs, but
that shouldn't be far out. 

While working on this I stumbled upon a few more issues,
so I decided to just roll the fixes into this series
and resend.

Lucas Stach (19):
  MIPS: malta: fix pci IO resource assignment
  pci: split out device init
  pci: add resource enum
  pci: properly populate prefetchable BARs
  pci: setup bridges and traverse buses behind them
  pci: defer device registration until after bridge setup
  pci: prettyprint device names
  pci: track parent<->child relationship
  commands: lspci: go down into subordinate busses
  clk: tegra: add PLLE setup functions
  clk: tegra30: add PCIe clocks
  i2c: tegra: move to fs initcall
  ARM: tegra: beaver: enable PEX voltage rail
  tegra: pmc: add powerdomain handling
  of: import pci range parser from linux
  pci: add Tegra host controller driver
  ARM: tegra: advertise PCI support
  net: add rtl8169 driver
  ARM: tegra: enable network related options in defconfig

 arch/arm/Kconfig                                   |    1 +
 arch/arm/boards/nvidia-beaver/board.c              |   13 +-
 arch/arm/configs/tegra_v7_defconfig                |    8 +
 arch/arm/mach-tegra/include/mach/tegra-powergate.h |   93 ++
 arch/arm/mach-tegra/include/mach/tegra20-car.h     |    2 +
 arch/arm/mach-tegra/include/mach/tegra30-car.h     |    2 +
 arch/arm/mach-tegra/tegra20-pmc.c                  |  139 ++-
 arch/mips/mach-malta/pci.c                         |    4 +-
 commands/lspci.c                                   |   25 +-
 drivers/clk/tegra/clk-pll.c                        |  116 ++
 drivers/clk/tegra/clk-tegra30.c                    |   31 +
 drivers/clk/tegra/clk.h                            |    6 +
 drivers/i2c/busses/i2c-tegra.c                     |    2 +-
 drivers/net/Kconfig                                |    8 +
 drivers/net/Makefile                               |    1 +
 drivers/net/rtl8169.c                              |  566 ++++++++++
 drivers/of/Kconfig                                 |    1 +
 drivers/of/address.c                               |   68 ++
 drivers/pci/Kconfig                                |    7 +
 drivers/pci/Makefile                               |    1 +
 drivers/pci/bus.c                                  |    3 +-
 drivers/pci/pci-tegra.c                            | 1193 ++++++++++++++++++++
 drivers/pci/pci.c                                  |  276 +++--
 include/linux/pci.h                                |    9 +
 include/linux/pci_regs.h                           |   28 +
 include/of_address.h                               |   57 +
 26 files changed, 2573 insertions(+), 87 deletions(-)
 create mode 100644 arch/arm/mach-tegra/include/mach/tegra-powergate.h
 create mode 100644 drivers/net/rtl8169.c
 create mode 100644 drivers/pci/pci-tegra.c

-- 
1.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v2 01/19] MIPS: malta: fix pci IO resource assignment
  2014-10-04 17:40 [PATCH v2 00/19] PCI and Tegra series revamp Lucas Stach
@ 2014-10-04 17:40 ` Lucas Stach
  2014-10-04 17:40 ` [PATCH v2 02/19] pci: split out device init Lucas Stach
                   ` (18 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Lucas Stach @ 2014-10-04 17:40 UTC (permalink / raw)
  To: barebox

Does the same thing as the Linux kernel now.

Signed-off-by: Lucas Stach <dev@lynxeye.de>
---
I tested this with qemu-malta and at least barebox is now
again able to enumerate all 4 PCI devices.
---
 arch/mips/mach-malta/pci.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/mips/mach-malta/pci.c b/arch/mips/mach-malta/pci.c
index 9035175..47c0e22 100644
--- a/arch/mips/mach-malta/pci.c
+++ b/arch/mips/mach-malta/pci.c
@@ -151,7 +151,7 @@ static struct pci_controller gt64120_controller = {
 
 static int pcibios_init(void)
 {
-	resource_size_t start, end, map, start1, end1, map1, mask, res_end;
+	resource_size_t start, end, map, start1, end1, map1, mask;
 
 	/*
 	 * Due to a bug in the Galileo system controller, we need
@@ -207,7 +207,7 @@ static int pcibios_init(void)
 	BUG_ON((start & GT_PCI_HD_MSK) != (map & GT_PCI_HD_MSK) &&
 		mask != ~((mask & -mask) - 1));
 	gt64120_io_resource.start = map & mask;
-	res_end = (map & mask) | ~mask;
+	gt64120_io_resource.end = (map & mask) | ~mask;
 	gt64120_controller.io_offset = 0;
 	/* Addresses are 36-bit, so do shifts in the destinations. */
 	gt64120_io_resource.start <<= GT_PCI_DCRM_SHF;
-- 
1.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v2 02/19] pci: split out device init
  2014-10-04 17:40 [PATCH v2 00/19] PCI and Tegra series revamp Lucas Stach
  2014-10-04 17:40 ` [PATCH v2 01/19] MIPS: malta: fix pci IO resource assignment Lucas Stach
@ 2014-10-04 17:40 ` Lucas Stach
  2014-10-04 17:40 ` [PATCH v2 03/19] pci: add resource enum Lucas Stach
                   ` (17 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Lucas Stach @ 2014-10-04 17:40 UTC (permalink / raw)
  To: barebox

To make it reusable and the code more readable.

Signed-off-by: Lucas Stach <dev@lynxeye.de>
---
 drivers/pci/pci.c | 137 +++++++++++++++++++++++++++++-------------------------
 1 file changed, 74 insertions(+), 63 deletions(-)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index a1b7680..ef998dc 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -12,6 +12,8 @@ static struct pci_controller *hose_head, **hose_tail = &hose_head;
 LIST_HEAD(pci_root_buses);
 EXPORT_SYMBOL(pci_root_buses);
 static u8 bus_index;
+static resource_size_t last_mem;
+static resource_size_t last_io;
 
 static struct pci_bus *pci_alloc_bus(void)
 {
@@ -45,6 +47,10 @@ void register_pci_controller(struct pci_controller *hose)
 
 	if (hose->set_busno)
 		hose->set_busno(hose, bus->number);
+
+	last_mem = bus->resource[0]->start;
+	last_io = bus->resource[1]->start;
+
 	pci_scan_bus(bus);
 
 	list_add_tail(&bus->node, &pci_root_buses);
@@ -111,27 +117,80 @@ static struct pci_dev *alloc_pci_dev(void)
 	return dev;
 }
 
+static void setup_device(struct pci_dev *dev, int max_bar)
+{
+	int bar, size;
+	u32 mask;
+	u8 cmd;
+
+	pci_read_config_byte(dev, PCI_COMMAND, &cmd);
+	pci_write_config_byte(dev, PCI_COMMAND,
+			      cmd & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY));
+
+	for (bar = 0; bar < max_bar; bar++) {
+		resource_size_t last_addr;
+
+		pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, 0xfffffffe);
+		pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, &mask);
+
+		if (mask == 0 || mask == 0xffffffff) {
+			DBG("  PCI: pbar%d set bad mask\n", bar);
+			continue;
+		}
+
+		if (mask & 0x01) { /* IO */
+			size = -(mask & 0xfffffffe);
+			DBG("  PCI: pbar%d: mask=%08x io %d bytes\n", bar, mask, size);
+			if (last_mem + size > dev->bus->resource[0]->end) {
+				DBG("BAR does not fit within bus IO res\n");
+				return;
+			}
+			pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, last_io);
+			dev->resource[bar].flags = IORESOURCE_IO;
+			last_addr = last_io;
+			last_io += size;
+		} else { /* MEM */
+			size = -(mask & 0xfffffff0);
+			DBG("  PCI: pbar%d: mask=%08x memory %d bytes\n", bar, mask, size);
+			if (last_mem + size > dev->bus->resource[0]->end) {
+				DBG("BAR does not fit within bus mem res\n");
+				return;
+			}
+			pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, last_mem);
+			dev->resource[bar].flags = IORESOURCE_MEM;
+			last_addr = last_mem;
+			last_mem += size;
+
+			if ((mask & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
+			    PCI_BASE_ADDRESS_MEM_TYPE_64) {
+				dev->resource[bar].flags |= IORESOURCE_MEM_64;
+				pci_write_config_dword(dev,
+				       PCI_BASE_ADDRESS_1 + bar * 4, 0);
+			}
+		}
+
+		dev->resource[bar].start = last_addr;
+		dev->resource[bar].end = last_addr + size - 1;
+		if (dev->resource[bar].flags & IORESOURCE_MEM_64)
+			bar++;
+	}
+
+	pci_write_config_byte(dev, PCI_COMMAND, cmd);
+	list_add_tail(&dev->bus_list, &dev->bus->devices);
+	pci_register_device(dev);
+}
+
 unsigned int pci_scan_bus(struct pci_bus *bus)
 {
+	struct pci_dev *dev;
 	unsigned int devfn, l, max, class;
 	unsigned char cmd, tmp, hdr_type, is_multi = 0;
-	struct pci_dev *dev;
-	resource_size_t last_mem;
-	resource_size_t last_io;
-
-	/* FIXME: use res_start() */
-	last_mem = bus->resource[0]->start;
-	last_io = bus->resource[1]->start;
 
 	DBG("pci_scan_bus for bus %d\n", bus->number);
 	DBG(" last_io = 0x%08x, last_mem = 0x%08x\n", last_io, last_mem);
 	max = bus->secondary;
 
 	for (devfn = 0; devfn < 0xff; ++devfn) {
-		int bar;
-		u32 old_bar, mask;
-		int size;
-
 		if (PCI_FUNC(devfn) && !is_multi) {
 			/* not a multi-function device */
 			continue;
@@ -169,6 +228,8 @@ unsigned int pci_scan_bus(struct pci_bus *bus)
 		dev->hdr_type = hdr_type;
 
 		DBG("PCI: class = %08x, hdr_type = %08x\n", class, hdr_type);
+		DBG("PCI: %02x:%02x [%04x:%04x]\n", bus->number, dev->devfn,
+		    dev->vendor, dev->device);
 
 		switch (hdr_type & 0x7f) {		    /* header type */
 		case PCI_HEADER_TYPE_NORMAL:		    /* standard header */
@@ -181,6 +242,8 @@ unsigned int pci_scan_bus(struct pci_bus *bus)
 			 */
 			pci_read_config_dword(dev, PCI_ROM_ADDRESS, &l);
 			dev->rom_address = (l == 0xffffffff) ? 0 : l;
+
+			setup_device(dev, 6);
 			break;
 		default:				    /* unknown header */
 		bad:
@@ -189,62 +252,10 @@ unsigned int pci_scan_bus(struct pci_bus *bus)
 			continue;
 		}
 
-		DBG("PCI: %02x:%02x [%04x/%04x]\n", bus->number, dev->devfn, dev->vendor, dev->device);
-
 		if (class == PCI_CLASS_BRIDGE_HOST) {
 			DBG("PCI: skip pci host bridge\n");
 			continue;
 		}
-
-		pci_read_config_byte(dev, PCI_COMMAND, &cmd);
-		pci_write_config_byte(dev, PCI_COMMAND,
-				      cmd & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY));
-
-		for (bar = 0; bar < 6; bar++) {
-			resource_size_t last_addr;
-
-			pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, &old_bar);
-			pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, 0xfffffffe);
-			pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, &mask);
-			pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, old_bar);
-
-			if (mask == 0 || mask == 0xffffffff) {
-				DBG("  PCI: pbar%d set bad mask\n", bar);
-				continue;
-			}
-
-			if (mask & 0x01) { /* IO */
-				size = -(mask & 0xfffffffe);
-				DBG("  PCI: pbar%d: mask=%08x io %d bytes\n", bar, mask, size);
-				pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, last_io);
-				dev->resource[bar].flags = IORESOURCE_IO;
-				last_addr = last_io;
-				last_io += size;
-			} else { /* MEM */
-				size = -(mask & 0xfffffff0);
-				DBG("  PCI: pbar%d: mask=%08x memory %d bytes\n", bar, mask, size);
-				pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, last_mem);
-				dev->resource[bar].flags = IORESOURCE_MEM;
-				last_addr = last_mem;
-				last_mem += size;
-
-				if ((mask & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
-				    PCI_BASE_ADDRESS_MEM_TYPE_64) {
-					dev->resource[bar].flags |= IORESOURCE_MEM_64;
-					pci_write_config_dword(dev,
-					       PCI_BASE_ADDRESS_1 + bar * 4, 0);
-				}
-			}
-
-			dev->resource[bar].start = last_addr;
-			dev->resource[bar].end = last_addr + size - 1;
-			if (dev->resource[bar].flags & IORESOURCE_MEM_64)
-				bar++;
-		}
-
-		pci_write_config_byte(dev, PCI_COMMAND, cmd);
-		list_add_tail(&dev->bus_list, &bus->devices);
-		pci_register_device(dev);
 	}
 
 	/*
-- 
1.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v2 03/19] pci: add resource enum
  2014-10-04 17:40 [PATCH v2 00/19] PCI and Tegra series revamp Lucas Stach
  2014-10-04 17:40 ` [PATCH v2 01/19] MIPS: malta: fix pci IO resource assignment Lucas Stach
  2014-10-04 17:40 ` [PATCH v2 02/19] pci: split out device init Lucas Stach
@ 2014-10-04 17:40 ` Lucas Stach
  2014-10-04 17:40 ` [PATCH v2 04/19] pci: properly populate prefetchable BARs Lucas Stach
                   ` (16 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Lucas Stach @ 2014-10-04 17:40 UTC (permalink / raw)
  To: barebox

Makes things way clearer than juggling numbers.

Signed-off-by: Lucas Stach <dev@lynxeye.de>
---
 drivers/pci/pci.c   | 14 ++++++++------
 include/linux/pci.h |  6 ++++++
 2 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index ef998dc..e9f0cb5 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -41,15 +41,15 @@ void register_pci_controller(struct pci_controller *hose)
 	hose->bus = bus;
 	bus->host = hose;
 	bus->ops = hose->pci_ops;
-	bus->resource[0] = hose->mem_resource;
-	bus->resource[1] = hose->io_resource;
+	bus->resource[PCI_BUS_RESOURCE_MEM] = hose->mem_resource;
+	bus->resource[PCI_BUS_RESOURCE_IO] = hose->io_resource;
 	bus->number = bus_index++;
 
 	if (hose->set_busno)
 		hose->set_busno(hose, bus->number);
 
-	last_mem = bus->resource[0]->start;
-	last_io = bus->resource[1]->start;
+	last_mem = bus->resource[PCI_BUS_RESOURCE_MEM]->start;
+	last_io = bus->resource[PCI_BUS_RESOURCE_IO]->start;
 
 	pci_scan_bus(bus);
 
@@ -141,7 +141,8 @@ static void setup_device(struct pci_dev *dev, int max_bar)
 		if (mask & 0x01) { /* IO */
 			size = -(mask & 0xfffffffe);
 			DBG("  PCI: pbar%d: mask=%08x io %d bytes\n", bar, mask, size);
-			if (last_mem + size > dev->bus->resource[0]->end) {
+			if (last_io + size >
+			    dev->bus->resource[PCI_BUS_RESOURCE_IO]->end) {
 				DBG("BAR does not fit within bus IO res\n");
 				return;
 			}
@@ -152,7 +153,8 @@ static void setup_device(struct pci_dev *dev, int max_bar)
 		} else { /* MEM */
 			size = -(mask & 0xfffffff0);
 			DBG("  PCI: pbar%d: mask=%08x memory %d bytes\n", bar, mask, size);
-			if (last_mem + size > dev->bus->resource[0]->end) {
+			if (last_mem + size >
+			    dev->bus->resource[PCI_BUS_RESOURCE_MEM]->end) {
 				DBG("BAR does not fit within bus mem res\n");
 				return;
 			}
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 0ec1320..f5ef588 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -114,6 +114,12 @@ struct pci_dev {
 };
 #define	to_pci_dev(dev) container_of(dev, struct pci_dev, dev)
 
+enum {
+	PCI_BUS_RESOURCE_IO = 0,
+	PCI_BUS_RESOURCE_MEM = 1,
+	PCI_BUS_RESOURCE_MEM_PREF = 2,
+	PCI_BUS_RESOURCE_BUSN = 3,
+};
 struct pci_bus {
 	struct pci_controller *host;	/* associated host controller */
 	struct list_head node;		/* node in list of buses */
-- 
1.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v2 04/19] pci: properly populate prefetchable BARs
  2014-10-04 17:40 [PATCH v2 00/19] PCI and Tegra series revamp Lucas Stach
                   ` (2 preceding siblings ...)
  2014-10-04 17:40 ` [PATCH v2 03/19] pci: add resource enum Lucas Stach
@ 2014-10-04 17:40 ` Lucas Stach
  2014-10-04 17:40 ` [PATCH v2 05/19] pci: setup bridges and traverse buses behind them Lucas Stach
                   ` (15 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Lucas Stach @ 2014-10-04 17:40 UTC (permalink / raw)
  To: barebox

Some host controllers provide a prefetchable
memory area and devices will prefer this for
some of their BARs.

Signed-off-by: Lucas Stach <dev@lynxeye.de>
---
 drivers/pci/pci.c   | 59 ++++++++++++++++++++++++++++++++++++++++-------------
 include/linux/pci.h |  1 +
 2 files changed, 46 insertions(+), 14 deletions(-)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index e9f0cb5..115d8a3 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -13,6 +13,7 @@ LIST_HEAD(pci_root_buses);
 EXPORT_SYMBOL(pci_root_buses);
 static u8 bus_index;
 static resource_size_t last_mem;
+static resource_size_t last_mem_pref;
 static resource_size_t last_io;
 
 static struct pci_bus *pci_alloc_bus(void)
@@ -42,14 +43,27 @@ void register_pci_controller(struct pci_controller *hose)
 	bus->host = hose;
 	bus->ops = hose->pci_ops;
 	bus->resource[PCI_BUS_RESOURCE_MEM] = hose->mem_resource;
+	bus->resource[PCI_BUS_RESOURCE_MEM_PREF] = hose->mem_pref_resource;
 	bus->resource[PCI_BUS_RESOURCE_IO] = hose->io_resource;
 	bus->number = bus_index++;
 
 	if (hose->set_busno)
 		hose->set_busno(hose, bus->number);
 
-	last_mem = bus->resource[PCI_BUS_RESOURCE_MEM]->start;
-	last_io = bus->resource[PCI_BUS_RESOURCE_IO]->start;
+	if (bus->resource[PCI_BUS_RESOURCE_MEM])
+		last_mem = bus->resource[PCI_BUS_RESOURCE_MEM]->start;
+	else
+		last_mem = 0;
+
+	if (bus->resource[PCI_BUS_RESOURCE_MEM])
+		last_mem_pref = bus->resource[PCI_BUS_RESOURCE_MEM_PREF]->start;
+	else
+		last_mem_pref = 0;
+
+	if (bus->resource[PCI_BUS_RESOURCE_IO])
+		last_io = bus->resource[PCI_BUS_RESOURCE_IO]->start;
+	else
+		last_io = 0;
 
 	pci_scan_bus(bus);
 
@@ -150,31 +164,46 @@ static void setup_device(struct pci_dev *dev, int max_bar)
 			dev->resource[bar].flags = IORESOURCE_IO;
 			last_addr = last_io;
 			last_io += size;
-		} else { /* MEM */
+		} else if ((mask & PCI_BASE_ADDRESS_MEM_PREFETCH) &&
+		           last_mem_pref) /* prefetchable MEM */ {
 			size = -(mask & 0xfffffff0);
-			DBG("  PCI: pbar%d: mask=%08x memory %d bytes\n", bar, mask, size);
+			DBG("  PCI: pbar%d: mask=%08x P memory %d bytes\n",
+			    bar, mask, size);
+			if (last_mem_pref + size >
+			    dev->bus->resource[PCI_BUS_RESOURCE_MEM_PREF]->end) {
+				DBG("BAR does not fit within bus p-mem res\n");
+				return;
+			}
+			pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, last_mem_pref);
+			dev->resource[bar].flags = IORESOURCE_MEM |
+			                           IORESOURCE_PREFETCH;
+			last_addr = last_mem_pref;
+			last_mem_pref += size;
+		} else { /* non-prefetch MEM */
+			size = -(mask & 0xfffffff0);
+			DBG("  PCI: pbar%d: mask=%08x NP memory %d bytes\n",
+			    bar, mask, size);
 			if (last_mem + size >
 			    dev->bus->resource[PCI_BUS_RESOURCE_MEM]->end) {
-				DBG("BAR does not fit within bus mem res\n");
+				DBG("BAR does not fit within bus np-mem res\n");
 				return;
 			}
 			pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + bar * 4, last_mem);
 			dev->resource[bar].flags = IORESOURCE_MEM;
 			last_addr = last_mem;
 			last_mem += size;
-
-			if ((mask & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
-			    PCI_BASE_ADDRESS_MEM_TYPE_64) {
-				dev->resource[bar].flags |= IORESOURCE_MEM_64;
-				pci_write_config_dword(dev,
-				       PCI_BASE_ADDRESS_1 + bar * 4, 0);
-			}
 		}
 
 		dev->resource[bar].start = last_addr;
 		dev->resource[bar].end = last_addr + size - 1;
-		if (dev->resource[bar].flags & IORESOURCE_MEM_64)
+
+		if ((mask & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
+		    PCI_BASE_ADDRESS_MEM_TYPE_64) {
+			dev->resource[bar].flags |= IORESOURCE_MEM_64;
+			pci_write_config_dword(dev,
+			       PCI_BASE_ADDRESS_1 + bar * 4, 0);
 			bar++;
+		}
 	}
 
 	pci_write_config_byte(dev, PCI_COMMAND, cmd);
@@ -189,7 +218,9 @@ unsigned int pci_scan_bus(struct pci_bus *bus)
 	unsigned char cmd, tmp, hdr_type, is_multi = 0;
 
 	DBG("pci_scan_bus for bus %d\n", bus->number);
-	DBG(" last_io = 0x%08x, last_mem = 0x%08x\n", last_io, last_mem);
+	DBG(" last_io = 0x%08x, last_mem = 0x%08x, last_mem_pref = 0x%08x\n",
+	    last_io, last_mem, last_mem_pref);
+
 	max = bus->secondary;
 
 	for (devfn = 0; devfn < 0xff; ++devfn) {
diff --git a/include/linux/pci.h b/include/linux/pci.h
index f5ef588..932acf0 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -162,6 +162,7 @@ struct pci_controller {
 
 	struct pci_ops *pci_ops;
 	struct resource *mem_resource;
+	struct resource *mem_pref_resource;
 	unsigned long mem_offset;
 	struct resource *io_resource;
 	unsigned long io_offset;
-- 
1.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v2 05/19] pci: setup bridges and traverse buses behind them
  2014-10-04 17:40 [PATCH v2 00/19] PCI and Tegra series revamp Lucas Stach
                   ` (3 preceding siblings ...)
  2014-10-04 17:40 ` [PATCH v2 04/19] pci: properly populate prefetchable BARs Lucas Stach
@ 2014-10-04 17:40 ` Lucas Stach
  2014-10-04 17:40 ` [PATCH v2 06/19] pci: defer device registration until after bridge setup Lucas Stach
                   ` (14 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Lucas Stach @ 2014-10-04 17:40 UTC (permalink / raw)
  To: barebox

Signed-off-by: Lucas Stach <dev@lynxeye.de>
---
 drivers/pci/pci.c        | 92 ++++++++++++++++++++++++++++++++++++++++++++++--
 include/linux/pci_regs.h | 28 +++++++++++++++
 2 files changed, 117 insertions(+), 3 deletions(-)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 115d8a3..59f942d 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -211,9 +211,73 @@ static void setup_device(struct pci_dev *dev, int max_bar)
 	pci_register_device(dev);
 }
 
+static void prescan_setup_bridge(struct pci_dev *dev)
+{
+	u16 cmdstat;
+
+	pci_read_config_word(dev, PCI_COMMAND, &cmdstat);
+
+	/* Configure bus number registers */
+	pci_write_config_byte(dev, PCI_PRIMARY_BUS, dev->bus->number);
+	pci_write_config_byte(dev, PCI_SECONDARY_BUS, dev->subordinate->number);
+	pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, 0xff);
+
+	if (last_mem) {
+		/* Set up memory and I/O filter limits, assume 32-bit I/O space */
+		pci_write_config_word(dev, PCI_MEMORY_BASE,
+				      (last_mem & 0xfff00000) >> 16);
+		cmdstat |= PCI_COMMAND_MEMORY;
+	}
+
+	if (last_mem_pref) {
+		/* Set up memory and I/O filter limits, assume 32-bit I/O space */
+		pci_write_config_word(dev, PCI_PREF_MEMORY_BASE,
+				      (last_mem_pref & 0xfff00000) >> 16);
+		cmdstat |= PCI_COMMAND_MEMORY;
+	} else {
+
+		/* We don't support prefetchable memory for now, so disable */
+		pci_write_config_word(dev, PCI_PREF_MEMORY_BASE, 0x1000);
+		pci_write_config_word(dev, PCI_PREF_MEMORY_LIMIT, 0x0);
+	}
+
+	if (last_io) {
+		pci_write_config_byte(dev, PCI_IO_BASE,
+				      (last_io & 0x0000f000) >> 8);
+		pci_write_config_word(dev, PCI_IO_BASE_UPPER16,
+				      (last_io & 0xffff0000) >> 16);
+		cmdstat |= PCI_COMMAND_IO;
+	}
+
+	/* Enable memory and I/O accesses, enable bus master */
+	pci_write_config_word(dev, PCI_COMMAND, cmdstat | PCI_COMMAND_MASTER);
+}
+
+static void postscan_setup_bridge(struct pci_dev *dev)
+{
+	/* limit subordinate to last used bus number */
+	pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, bus_index - 1);
+
+	if (last_mem)
+		pci_write_config_word(dev, PCI_MEMORY_LIMIT,
+				      ((last_mem - 1) & 0xfff00000) >> 16);
+
+	if (last_mem_pref)
+		pci_write_config_word(dev, PCI_PREF_MEMORY_LIMIT,
+				      ((last_mem_pref - 1) & 0xfff00000) >> 16);
+
+	if (last_io) {
+		pci_write_config_byte(dev, PCI_IO_LIMIT,
+				((last_io - 1) & 0x0000f000) >> 8);
+		pci_write_config_word(dev, PCI_IO_LIMIT_UPPER16,
+				((last_io - 1) & 0xffff0000) >> 16);
+	}
+}
+
 unsigned int pci_scan_bus(struct pci_bus *bus)
 {
 	struct pci_dev *dev;
+	struct pci_bus *child_bus;
 	unsigned int devfn, l, max, class;
 	unsigned char cmd, tmp, hdr_type, is_multi = 0;
 
@@ -264,8 +328,8 @@ unsigned int pci_scan_bus(struct pci_bus *bus)
 		DBG("PCI: %02x:%02x [%04x:%04x]\n", bus->number, dev->devfn,
 		    dev->vendor, dev->device);
 
-		switch (hdr_type & 0x7f) {		    /* header type */
-		case PCI_HEADER_TYPE_NORMAL:		    /* standard header */
+		switch (hdr_type & 0x7f) {
+		case PCI_HEADER_TYPE_NORMAL:
 			if (class == PCI_CLASS_BRIDGE_PCI)
 				goto bad;
 
@@ -278,7 +342,28 @@ unsigned int pci_scan_bus(struct pci_bus *bus)
 
 			setup_device(dev, 6);
 			break;
-		default:				    /* unknown header */
+		case PCI_HEADER_TYPE_BRIDGE:
+			setup_device(dev, 2);
+
+			child_bus = pci_alloc_bus();
+			/* inherit parent properties */
+			child_bus->host = bus->host;
+			child_bus->ops = bus->host->pci_ops;
+			child_bus->resource[PCI_BUS_RESOURCE_MEM] =
+				bus->resource[PCI_BUS_RESOURCE_MEM];
+			child_bus->resource[PCI_BUS_RESOURCE_MEM_PREF] =
+				bus->resource[PCI_BUS_RESOURCE_MEM_PREF];
+			child_bus->resource[PCI_BUS_RESOURCE_IO] =
+				bus->resource[PCI_BUS_RESOURCE_IO];
+			child_bus->number = bus_index++;
+			list_add_tail(&child_bus->node, &bus->children);
+			dev->subordinate = child_bus;
+
+			prescan_setup_bridge(dev);
+			pci_scan_bus(child_bus);
+			postscan_setup_bridge(dev);
+			break;
+		default:
 		bad:
 			printk(KERN_ERR "PCI: %02x:%02x [%04x/%04x/%06x] has unknown header type %02x, ignoring.\n",
 			       bus->number, dev->devfn, dev->vendor, dev->device, class, hdr_type);
@@ -298,6 +383,7 @@ unsigned int pci_scan_bus(struct pci_bus *bus)
 	 *
 	 * Return how far we've got finding sub-buses.
 	 */
+	max = bus_index;
 	DBG("PCI: pci_scan_bus returning with max=%02x\n", max);
 
 	return max;
diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h
index 14a3ed3..8669fc7 100644
--- a/include/linux/pci_regs.h
+++ b/include/linux/pci_regs.h
@@ -107,4 +107,32 @@
 #define  PCI_ROM_ADDRESS_ENABLE	0x01
 #define PCI_ROM_ADDRESS_MASK	(~0x7ffUL)
 
+/* Header type 1 (PCI-to-PCI bridges) */
+#define PCI_PRIMARY_BUS		0x18	/* Primary bus number */
+#define PCI_SECONDARY_BUS	0x19	/* Secondary bus number */
+#define PCI_SUBORDINATE_BUS	0x1a	/* Highest bus number behind the bridge */
+#define PCI_SEC_LATENCY_TIMER	0x1b	/* Latency timer for secondary interface */
+#define PCI_IO_BASE		0x1c	/* I/O range behind the bridge */
+#define PCI_IO_LIMIT		0x1d
+#define  PCI_IO_RANGE_TYPE_MASK	0x0fUL	/* I/O bridging type */
+#define  PCI_IO_RANGE_TYPE_16	0x00
+#define  PCI_IO_RANGE_TYPE_32	0x01
+#define  PCI_IO_RANGE_MASK	(~0x0fUL) /* Standard 4K I/O windows */
+#define  PCI_IO_1K_RANGE_MASK	(~0x03UL) /* Intel 1K I/O windows */
+#define PCI_SEC_STATUS		0x1e	/* Secondary status register, only bit 14 used */
+#define PCI_MEMORY_BASE		0x20	/* Memory range behind */
+#define PCI_MEMORY_LIMIT	0x22
+#define  PCI_MEMORY_RANGE_TYPE_MASK 0x0fUL
+#define  PCI_MEMORY_RANGE_MASK	(~0x0fUL)
+#define PCI_PREF_MEMORY_BASE	0x24	/* Prefetchable memory range behind */
+#define PCI_PREF_MEMORY_LIMIT	0x26
+#define  PCI_PREF_RANGE_TYPE_MASK 0x0fUL
+#define  PCI_PREF_RANGE_TYPE_32	0x00
+#define  PCI_PREF_RANGE_TYPE_64	0x01
+#define  PCI_PREF_RANGE_MASK	(~0x0fUL)
+#define PCI_PREF_BASE_UPPER32	0x28	/* Upper half of prefetchable memory range */
+#define PCI_PREF_LIMIT_UPPER32	0x2c
+#define PCI_IO_BASE_UPPER16	0x30	/* Upper half of I/O addresses */
+#define PCI_IO_LIMIT_UPPER16	0x32
+
 #endif /* LINUX_PCI_REGS_H */
-- 
1.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v2 06/19] pci: defer device registration until after bridge setup
  2014-10-04 17:40 [PATCH v2 00/19] PCI and Tegra series revamp Lucas Stach
                   ` (4 preceding siblings ...)
  2014-10-04 17:40 ` [PATCH v2 05/19] pci: setup bridges and traverse buses behind them Lucas Stach
@ 2014-10-04 17:40 ` Lucas Stach
  2014-10-04 17:40 ` [PATCH v2 07/19] pci: prettyprint device names Lucas Stach
                   ` (13 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Lucas Stach @ 2014-10-04 17:40 UTC (permalink / raw)
  To: barebox

Otherwise the drivers for a device may probe before the
devices parent bridge is fully configured, which leads
to errors when accessing the BARs.

Signed-off-by: Lucas Stach <dev@lynxeye.de>
---
 drivers/pci/pci.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 59f942d..943d0e8 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -208,7 +208,6 @@ static void setup_device(struct pci_dev *dev, int max_bar)
 
 	pci_write_config_byte(dev, PCI_COMMAND, cmd);
 	list_add_tail(&dev->bus_list, &dev->bus->devices);
-	pci_register_device(dev);
 }
 
 static void prescan_setup_bridge(struct pci_dev *dev)
@@ -362,6 +361,11 @@ unsigned int pci_scan_bus(struct pci_bus *bus)
 			prescan_setup_bridge(dev);
 			pci_scan_bus(child_bus);
 			postscan_setup_bridge(dev);
+			/* first activate bridge then all devices on it's bus */
+			pci_register_device(dev);
+			list_for_each_entry(dev, &child_bus->devices, bus_list)
+				if (!dev->subordinate)
+					pci_register_device(dev);
 			break;
 		default:
 		bad:
-- 
1.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v2 07/19] pci: prettyprint device names
  2014-10-04 17:40 [PATCH v2 00/19] PCI and Tegra series revamp Lucas Stach
                   ` (5 preceding siblings ...)
  2014-10-04 17:40 ` [PATCH v2 06/19] pci: defer device registration until after bridge setup Lucas Stach
@ 2014-10-04 17:40 ` Lucas Stach
  2014-10-04 17:40 ` [PATCH v2 08/19] pci: track parent<->child relationship Lucas Stach
                   ` (12 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Lucas Stach @ 2014-10-04 17:40 UTC (permalink / raw)
  To: barebox

Signed-off-by: Lucas Stach <dev@lynxeye.de>
---
 drivers/pci/bus.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 8215ee5..866ab08 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -86,7 +86,8 @@ int pci_register_device(struct pci_dev *pdev)
 	struct device_d *dev = &pdev->dev;
 	int ret;
 
-	strcpy(dev->name, "pci");
+	snprintf(dev->name, MAX_DRIVER_NAME, "pci-%04x:%04x.",
+	         pdev->vendor, pdev->device);
 	dev->bus = &pci_bus;
 	dev->id = DEVICE_ID_DYNAMIC;
 
-- 
1.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v2 08/19] pci: track parent<->child relationship
  2014-10-04 17:40 [PATCH v2 00/19] PCI and Tegra series revamp Lucas Stach
                   ` (6 preceding siblings ...)
  2014-10-04 17:40 ` [PATCH v2 07/19] pci: prettyprint device names Lucas Stach
@ 2014-10-04 17:40 ` Lucas Stach
  2014-10-04 17:40 ` [PATCH v2 09/19] commands: lspci: go down into subordinate busses Lucas Stach
                   ` (11 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Lucas Stach @ 2014-10-04 17:40 UTC (permalink / raw)
  To: barebox

So that PCI devices hang down from bridges and root
bridges down from the PCI host controller when
calling devinfo.

Signed-off-by: Lucas Stach <dev@lynxeye.de>
---
 drivers/pci/pci.c   | 4 ++++
 include/linux/pci.h | 2 ++
 2 files changed, 6 insertions(+)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 943d0e8..ba9d097 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -40,6 +40,7 @@ void register_pci_controller(struct pci_controller *hose)
 
 	bus = pci_alloc_bus();
 	hose->bus = bus;
+	bus->parent = hose->parent;
 	bus->host = hose;
 	bus->ops = hose->pci_ops;
 	bus->resource[PCI_BUS_RESOURCE_MEM] = hose->mem_resource;
@@ -309,6 +310,7 @@ unsigned int pci_scan_bus(struct pci_bus *bus)
 		dev->devfn = devfn;
 		dev->vendor = l & 0xffff;
 		dev->device = (l >> 16) & 0xffff;
+		dev->dev.parent = bus->parent;
 
 		/* non-destructively determine if device can be a master: */
 		pci_read_config_byte(dev, PCI_COMMAND, &cmd);
@@ -354,6 +356,8 @@ unsigned int pci_scan_bus(struct pci_bus *bus)
 				bus->resource[PCI_BUS_RESOURCE_MEM_PREF];
 			child_bus->resource[PCI_BUS_RESOURCE_IO] =
 				bus->resource[PCI_BUS_RESOURCE_IO];
+
+			child_bus->parent = &dev->dev;
 			child_bus->number = bus_index++;
 			list_add_tail(&child_bus->node, &bus->children);
 			dev->subordinate = child_bus;
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 932acf0..3d0e73b 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -122,6 +122,7 @@ enum {
 };
 struct pci_bus {
 	struct pci_controller *host;	/* associated host controller */
+	struct device_d *parent;
 	struct list_head node;		/* node in list of buses */
 	struct list_head children;	/* list of child buses */
 	struct list_head devices;	/* list of devices on this bus */
@@ -158,6 +159,7 @@ extern struct pci_ops *pci_ops;
  */
 struct pci_controller {
 	struct pci_controller *next;
+	struct device_d *parent;
 	struct pci_bus *bus;
 
 	struct pci_ops *pci_ops;
-- 
1.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v2 09/19] commands: lspci: go down into subordinate busses
  2014-10-04 17:40 [PATCH v2 00/19] PCI and Tegra series revamp Lucas Stach
                   ` (7 preceding siblings ...)
  2014-10-04 17:40 ` [PATCH v2 08/19] pci: track parent<->child relationship Lucas Stach
@ 2014-10-04 17:40 ` Lucas Stach
  2014-10-04 17:40 ` [PATCH v2 10/19] clk: tegra: add PLLE setup functions Lucas Stach
                   ` (10 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Lucas Stach @ 2014-10-04 17:40 UTC (permalink / raw)
  To: barebox

This way we also list devices that are behind
bridges.

Signed-off-by: Lucas Stach <dev@lynxeye.de>
---
 commands/lspci.c | 25 ++++++++++++++++---------
 1 file changed, 16 insertions(+), 9 deletions(-)

diff --git a/commands/lspci.c b/commands/lspci.c
index fdf0269..27edd5d 100644
--- a/commands/lspci.c
+++ b/commands/lspci.c
@@ -20,10 +20,24 @@
 #include <complete.h>
 #include <linux/pci.h>
 
+static void traverse_bus(struct pci_bus *bus)
+{
+	struct pci_dev *dev;
+
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+		printf("%02x:%02x.%1x %04x: %04x:%04x (rev %02x)\n",
+		       dev->bus->number, PCI_SLOT(dev->devfn),
+		       PCI_FUNC(dev->devfn), (dev->class >> 8) & 0xffff,
+		       dev->vendor, dev->device, dev->revision);
+
+		if (dev->subordinate)
+			traverse_bus(dev->subordinate);
+	}
+}
+
 static int do_lspci(int argc, char *argv[])
 {
 	struct pci_bus *root_bus;
-	struct pci_dev *dev;
 
 	if (list_empty(&pci_root_buses)) {
 		printf("No PCI bus detected\n");
@@ -31,14 +45,7 @@ static int do_lspci(int argc, char *argv[])
 	}
 
 	list_for_each_entry(root_bus, &pci_root_buses, node) {
-		list_for_each_entry(dev, &root_bus->devices, bus_list) {
-			printf("%02x: %04x: %04x:%04x (rev %02x)\n",
-				      dev->devfn,
-				      (dev->class >> 8) & 0xffff,
-				      dev->vendor,
-				      dev->device,
-				      dev->revision);
-		}
+		traverse_bus(root_bus);
 	}
 
 	return 0;
-- 
1.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v2 10/19] clk: tegra: add PLLE setup functions
  2014-10-04 17:40 [PATCH v2 00/19] PCI and Tegra series revamp Lucas Stach
                   ` (8 preceding siblings ...)
  2014-10-04 17:40 ` [PATCH v2 09/19] commands: lspci: go down into subordinate busses Lucas Stach
@ 2014-10-04 17:40 ` Lucas Stach
  2014-10-04 17:40 ` [PATCH v2 11/19] clk: tegra30: add PCIe clocks Lucas Stach
                   ` (9 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Lucas Stach @ 2014-10-04 17:40 UTC (permalink / raw)
  To: barebox

From: Lucas Stach <l.stach@pengutronix.de>

Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
---
 arch/arm/mach-tegra/include/mach/tegra30-car.h |   2 +
 drivers/clk/tegra/clk-pll.c                    | 116 +++++++++++++++++++++++++
 drivers/clk/tegra/clk.h                        |   6 ++
 3 files changed, 124 insertions(+)

diff --git a/arch/arm/mach-tegra/include/mach/tegra30-car.h b/arch/arm/mach-tegra/include/mach/tegra30-car.h
index c8f6c9f..7fb2238 100644
--- a/arch/arm/mach-tegra/include/mach/tegra30-car.h
+++ b/arch/arm/mach-tegra/include/mach/tegra30-car.h
@@ -33,3 +33,5 @@
 #define CRC_RST_DEV_V_CLR		0x434
 
 #define CRC_CLK_OUT_ENB_V_SET		0x440
+
+#define CRC_PLLE_AUX			0x48c
diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c
index c18c67f..bff5651 100644
--- a/drivers/clk/tegra/clk-pll.c
+++ b/drivers/clk/tegra/clk-pll.c
@@ -17,12 +17,15 @@
  */
 
 #include <common.h>
+#include <clock.h>
 #include <io.h>
 #include <malloc.h>
 #include <asm-generic/div64.h>
 #include <linux/clk.h>
 #include <linux/err.h>
 
+#include <mach/iomap.h>
+
 #include "clk.h"
 
 #define PLL_BASE_BYPASS		BIT(31)
@@ -393,6 +396,108 @@ const struct clk_ops tegra_clk_pll_ops = {
 	.set_rate = clk_pll_set_rate,
 };
 
+static unsigned long clk_plle_recalc_rate(struct clk *hw,
+					 unsigned long parent_rate)
+{
+	struct tegra_clk_pll *pll = to_clk_pll(hw);
+	u32 val = pll_readl_base(pll);
+	u32 divn = 0, divm = 0, divp = 0;
+	u64 rate = parent_rate;
+
+	divp = (val >> 16) & 0x3f;
+	divn = (val >> 8) & (0xff);
+	divm = (val >> 0) & (0xff);
+	divm *= divp;
+
+	rate *= divn;
+	do_div(rate, divm);
+	return rate;
+}
+
+static int clk_plle_training(struct tegra_clk_pll *pll)
+{
+	u32 val;
+
+	/*
+	 * PLLE is already disabled, and setup cleared;
+	 * create falling edge on PLLE IDDQ input.
+	 */
+	val = readl(TEGRA_PMC_BASE + PMC_SATA_PWRGT);
+	val |= PMC_SATA_PWRGT_PLLE_IDDQ_VALUE;
+	writel(val, TEGRA_PMC_BASE + PMC_SATA_PWRGT);
+
+	val = readl(TEGRA_PMC_BASE + PMC_SATA_PWRGT);
+	val |= PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL;
+	writel(val, TEGRA_PMC_BASE + PMC_SATA_PWRGT);
+
+	val = readl(TEGRA_PMC_BASE + PMC_SATA_PWRGT);
+	val &= ~PMC_SATA_PWRGT_PLLE_IDDQ_VALUE;
+	writel(val, TEGRA_PMC_BASE + PMC_SATA_PWRGT);
+
+	return wait_on_timeout(100 * MSECOND,
+			(pll_readl_misc(pll) & PLLE_MISC_READY));
+}
+
+static int clk_plle_enable(struct clk *hw)
+{
+	struct tegra_clk_pll *pll = to_clk_pll(hw);
+	unsigned long input_rate = clk_get_rate(clk_get_parent(hw));
+	struct tegra_clk_pll_freq_table sel;
+	u32 val;
+	int err;
+
+	if (_get_table_rate(hw, &sel, pll->fixed_rate, input_rate))
+		return -EINVAL;
+
+	clk_pll_disable(hw);
+
+	val = pll_readl_misc(pll);
+	val &= ~(PLLE_MISC_LOCK_ENABLE | PLLE_MISC_SETUP_MASK);
+	pll_writel_misc(val, pll);
+
+	val = pll_readl_misc(pll);
+	if (!(val & PLLE_MISC_READY)) {
+		err = clk_plle_training(pll);
+		if (err)
+			return err;
+	}
+
+	/* configure dividers */
+	val = pll_readl_base(pll);
+	val &= ~(0x3fffff);
+	val &= ~(PLLE_BASE_DIVCML_WIDTH << PLLE_BASE_DIVCML_SHIFT);
+	val |= sel.m << 0;
+	val |= sel.n << 8;
+	val |= sel.p << 16;
+	val |= sel.cpcon << PLLE_BASE_DIVCML_SHIFT;
+	pll_writel_base(val, pll);
+
+	val = pll_readl_misc(pll);
+	val |= PLLE_MISC_SETUP_VALUE;
+	val |= PLLE_MISC_LOCK_ENABLE;
+	pll_writel_misc(val, pll);
+
+	val = readl(pll->clk_base + PLLE_SS_CTRL);
+	val |= PLLE_SS_DISABLE;
+	writel(val, pll->clk_base + PLLE_SS_CTRL);
+
+	val = pll_readl_base(pll);
+	val |= (PLL_BASE_BYPASS | PLL_BASE_ENABLE);
+	pll_writel_base(val, pll);
+
+	clk_pll_wait_for_lock(pll, pll->clk_base + pll->params->base_reg,
+			      pll->params->lock_bit_idx);
+
+	return 0;
+}
+
+const struct clk_ops tegra_clk_plle_ops = {
+	.recalc_rate = clk_plle_recalc_rate,
+	.is_enabled = clk_pll_is_enabled,
+	.disable = clk_pll_disable,
+	.enable = clk_plle_enable,
+};
+
 static struct clk *_tegra_clk_register_pll(const char *name,
 		const char *parent_name, void __iomem *clk_base,
 		unsigned long flags, unsigned long fixed_rate,
@@ -447,3 +552,14 @@ struct clk *tegra_clk_register_pll(const char *name, const char *parent_name,
 			flags, fixed_rate, pll_params, pll_flags, freq_table,
 			&tegra_clk_pll_ops);
 }
+
+struct clk *tegra_clk_register_plle(const char *name, const char *parent_name,
+		void __iomem *clk_base,
+		unsigned long flags, unsigned long fixed_rate,
+		struct tegra_clk_pll_params *pll_params, u8 pll_flags,
+		struct tegra_clk_pll_freq_table *freq_table)
+{
+	return _tegra_clk_register_pll(name, parent_name, clk_base,
+			flags, fixed_rate, pll_params, pll_flags, freq_table,
+			&tegra_clk_plle_ops);
+}
diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
index d5d0730..85777a8 100644
--- a/drivers/clk/tegra/clk.h
+++ b/drivers/clk/tegra/clk.h
@@ -101,6 +101,12 @@ struct clk *tegra_clk_register_pll(const char *name, const char *parent_name,
 		struct tegra_clk_pll_params *pll_params, u8 pll_flags,
 		struct tegra_clk_pll_freq_table *freq_table);
 
+struct clk *tegra_clk_register_plle(const char *name, const char *parent_name,
+		void __iomem *clk_base,
+		unsigned long flags, unsigned long fixed_rate,
+		struct tegra_clk_pll_params *pll_params, u8 pll_flags,
+		struct tegra_clk_pll_freq_table *freq_table);
+
 /* struct tegra_clk_pll_out - PLL output divider */
 struct tegra_clk_pll_out {
 	struct clk	hw;
-- 
1.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v2 11/19] clk: tegra30: add PCIe clocks
  2014-10-04 17:40 [PATCH v2 00/19] PCI and Tegra series revamp Lucas Stach
                   ` (9 preceding siblings ...)
  2014-10-04 17:40 ` [PATCH v2 10/19] clk: tegra: add PLLE setup functions Lucas Stach
@ 2014-10-04 17:40 ` Lucas Stach
  2014-10-04 17:40 ` [PATCH v2 12/19] i2c: tegra: move to fs initcall Lucas Stach
                   ` (8 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Lucas Stach @ 2014-10-04 17:40 UTC (permalink / raw)
  To: barebox

From: Lucas Stach <l.stach@pengutronix.de>

Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
---
 arch/arm/mach-tegra/include/mach/tegra20-car.h |  2 ++
 drivers/clk/tegra/clk-tegra30.c                | 31 ++++++++++++++++++++++++++
 2 files changed, 33 insertions(+)

diff --git a/arch/arm/mach-tegra/include/mach/tegra20-car.h b/arch/arm/mach-tegra/include/mach/tegra20-car.h
index 161e3d8..5a35f21 100644
--- a/arch/arm/mach-tegra/include/mach/tegra20-car.h
+++ b/arch/arm/mach-tegra/include/mach/tegra20-car.h
@@ -49,6 +49,8 @@
 #define CRC_CLK_OUT_ENB_H		0x014
 #define CRC_CLK_OUT_ENB_H_DVC		(1 << 15)
 
+#define CRC_CLK_OUT_ENB_U		0x018
+
 #define CRC_CCLK_BURST_POLICY		0x020
 #define CRC_CCLK_BURST_POLICY_SYS_STATE_SHIFT	28
 #define CRC_CCLK_BURST_POLICY_SYS_STATE_FIQ	8
diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c
index ed6d736..9997ab9 100644
--- a/drivers/clk/tegra/clk-tegra30.c
+++ b/drivers/clk/tegra/clk-tegra30.c
@@ -130,6 +130,13 @@ static struct tegra_clk_pll_freq_table pll_u_freq_table[] = {
 	{ 0, 0, 0, 0, 0, 0 },
 };
 
+static struct tegra_clk_pll_freq_table pll_e_freq_table[] = {
+	/* PLLE special case: use cpcon field to store cml divider value */
+	{ 12000000,  100000000, 150, 1,  18, 11},
+	{ 216000000, 100000000, 200, 18, 24, 13},
+	{ 0, 0, 0, 0, 0, 0 },
+};
+
 /* PLL parameters */
 static struct tegra_clk_pll_params pll_c_params = {
 	.input_min = 2000000,
@@ -201,6 +208,19 @@ static struct tegra_clk_pll_params pll_u_params = {
 	.lock_delay = 1000,
 };
 
+static struct tegra_clk_pll_params pll_e_params = {
+	.input_min = 12000000,
+	.input_max = 216000000,
+	.cf_min = 12000000,
+	.cf_max = 12000000,
+	.vco_min = 1200000000,
+	.vco_max = 2400000000U,
+	.base_reg = CRC_PLLE_BASE,
+	.misc_reg = CRC_PLLE_MISC,
+	.lock_enable_bit_idx = CRC_PLLE_MISC_LOCK_ENABLE,
+	.lock_delay = 300,
+};
+
 static void tegra30_pll_init(void)
 {
 	/* PLLC */
@@ -251,6 +271,11 @@ static void tegra30_pll_init(void)
 	clks[TEGRA30_CLK_PLL_U] = tegra_clk_register_pll("pll_u", "pll_ref",
 			car_base, 0, 0, &pll_u_params, TEGRA_PLLU |
 			TEGRA_PLL_HAS_CPCON, pll_u_freq_table);
+
+	/* PLLE */
+	clks[TEGRA30_CLK_PLL_E] = tegra_clk_register_plle("pll_e", "pll_ref",
+			car_base, 0, 100000000, &pll_e_params,
+			TEGRA_PLL_FIXED | TEGRA_PLL_USE_LOCK, pll_e_freq_table);
 }
 
 static const char *mux_pllpcm_clkm[] = {"pll_p", "pll_c", "pll_m", "clk_m"};
@@ -278,6 +303,12 @@ static void tegra30_periph_init(void)
 			mux_pllpcm_clkm, ARRAY_SIZE(mux_pllpcm_clkm), car_base,
 			CRC_CLK_SOURCE_UARTE, TEGRA30_CLK_UARTE,
 			TEGRA_PERIPH_ON_APB);
+	clks[TEGRA30_CLK_PCIE] = clk_gate("pcie", "clk_m",
+			car_base + CRC_CLK_OUT_ENB_U, 6, 0, 0);
+	clks[TEGRA30_CLK_AFI] = clk_gate("afi", "clk_m",
+			car_base + CRC_CLK_OUT_ENB_U, 8, 0, 0);
+	clks[TEGRA30_CLK_CML0] = clk_gate("cml0", "pll_e",
+			car_base + CRC_PLLE_AUX, 0, 0, 0);
 
 	/* peripheral clocks with a divider */
 	clks[TEGRA30_CLK_MSELECT] = tegra_clk_register_periph("mselect",
-- 
1.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v2 12/19] i2c: tegra: move to fs initcall
  2014-10-04 17:40 [PATCH v2 00/19] PCI and Tegra series revamp Lucas Stach
                   ` (10 preceding siblings ...)
  2014-10-04 17:40 ` [PATCH v2 11/19] clk: tegra30: add PCIe clocks Lucas Stach
@ 2014-10-04 17:40 ` Lucas Stach
  2014-10-04 17:40 ` [PATCH v2 13/19] ARM: tegra: beaver: enable PEX voltage rail Lucas Stach
                   ` (7 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Lucas Stach @ 2014-10-04 17:40 UTC (permalink / raw)
  To: barebox

From: Lucas Stach <l.stach@pengutronix.de>

i2c is needed to enable voltage rails that are later
needed by other drivers.

Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
---
 arch/arm/boards/nvidia-beaver/board.c | 2 +-
 drivers/i2c/busses/i2c-tegra.c        | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm/boards/nvidia-beaver/board.c b/arch/arm/boards/nvidia-beaver/board.c
index e87594d..e67d2aa 100644
--- a/arch/arm/boards/nvidia-beaver/board.c
+++ b/arch/arm/boards/nvidia-beaver/board.c
@@ -35,4 +35,4 @@ static int nvidia_beaver_devices_init(void)
 
 	return 0;
 }
-device_initcall(nvidia_beaver_devices_init);
+fs_initcall(nvidia_beaver_devices_init);
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index c52da18..f793cbe 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -696,4 +696,4 @@ static struct driver_d tegra_i2c_driver = {
 	.probe   = tegra_i2c_probe,
 	.of_compatible = DRV_OF_COMPAT(tegra_i2c_compatible),
 };
-device_platform_driver(tegra_i2c_driver);
+register_driver_macro(fs, platform, tegra_i2c_driver);
-- 
1.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v2 13/19] ARM: tegra: beaver: enable PEX voltage rail
  2014-10-04 17:40 [PATCH v2 00/19] PCI and Tegra series revamp Lucas Stach
                   ` (11 preceding siblings ...)
  2014-10-04 17:40 ` [PATCH v2 12/19] i2c: tegra: move to fs initcall Lucas Stach
@ 2014-10-04 17:40 ` Lucas Stach
  2014-10-04 17:40 ` [PATCH v2 14/19] tegra: pmc: add powerdomain handling Lucas Stach
                   ` (6 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Lucas Stach @ 2014-10-04 17:40 UTC (permalink / raw)
  To: barebox

From: Lucas Stach <l.stach@pengutronix.de>

Supply for the PCIe PLL.

Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
---
 arch/arm/boards/nvidia-beaver/board.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boards/nvidia-beaver/board.c b/arch/arm/boards/nvidia-beaver/board.c
index e67d2aa..20707d8 100644
--- a/arch/arm/boards/nvidia-beaver/board.c
+++ b/arch/arm/boards/nvidia-beaver/board.c
@@ -15,8 +15,10 @@
  */
 
 #include <common.h>
-#include <init.h>
+#include <dt-bindings/gpio/tegra-gpio.h>
+#include <gpio.h>
 #include <i2c/i2c.h>
+#include <init.h>
 
 static int nvidia_beaver_devices_init(void)
 {
@@ -33,6 +35,13 @@ static int nvidia_beaver_devices_init(void)
 	data = 0x65;
 	i2c_write_reg(&client, 0x32, &data, 1);
 
+	/* TPS659110: LDO1_REG = 1.05v, ACTIVE to PEX */
+	data = 0x15;
+	i2c_write_reg(&client, 0x30, &data, 1);
+
+	/* enable SYS_3V3_PEXS */
+	gpio_direction_output(TEGRA_GPIO(L, 7), 1);
+
 	return 0;
 }
 fs_initcall(nvidia_beaver_devices_init);
-- 
1.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v2 14/19] tegra: pmc: add powerdomain handling
  2014-10-04 17:40 [PATCH v2 00/19] PCI and Tegra series revamp Lucas Stach
                   ` (12 preceding siblings ...)
  2014-10-04 17:40 ` [PATCH v2 13/19] ARM: tegra: beaver: enable PEX voltage rail Lucas Stach
@ 2014-10-04 17:40 ` Lucas Stach
  2014-10-04 17:40 ` [PATCH v2 15/19] of: import pci range parser from linux Lucas Stach
                   ` (5 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Lucas Stach @ 2014-10-04 17:40 UTC (permalink / raw)
  To: barebox

From: Lucas Stach <l.stach@pengutronix.de>

In order to use some devices we first have to power
up their power domain. Add support to do this in a
generic way.

Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
---
 arch/arm/mach-tegra/include/mach/tegra-powergate.h |  93 ++++++++++++++
 arch/arm/mach-tegra/tegra20-pmc.c                  | 139 ++++++++++++++++++++-
 2 files changed, 229 insertions(+), 3 deletions(-)
 create mode 100644 arch/arm/mach-tegra/include/mach/tegra-powergate.h

diff --git a/arch/arm/mach-tegra/include/mach/tegra-powergate.h b/arch/arm/mach-tegra/include/mach/tegra-powergate.h
new file mode 100644
index 0000000..e32250a
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/tegra-powergate.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2010 Google, Inc
+ *
+ * Author:
+ *	Colin Cross <ccross@google.com>
+ *
+ * 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.
+ *
+ */
+
+#ifndef _MACH_TEGRA_POWERGATE_H_
+#define _MACH_TEGRA_POWERGATE_H_
+
+struct clk;
+struct reset_control;
+
+#define TEGRA_POWERGATE_CPU	0
+#define TEGRA_POWERGATE_3D	1
+#define TEGRA_POWERGATE_VENC	2
+#define TEGRA_POWERGATE_PCIE	3
+#define TEGRA_POWERGATE_VDEC	4
+#define TEGRA_POWERGATE_L2	5
+#define TEGRA_POWERGATE_MPE	6
+#define TEGRA_POWERGATE_HEG	7
+#define TEGRA_POWERGATE_SATA	8
+#define TEGRA_POWERGATE_CPU1	9
+#define TEGRA_POWERGATE_CPU2	10
+#define TEGRA_POWERGATE_CPU3	11
+#define TEGRA_POWERGATE_CELP	12
+#define TEGRA_POWERGATE_3D1	13
+#define TEGRA_POWERGATE_CPU0	14
+#define TEGRA_POWERGATE_C0NC	15
+#define TEGRA_POWERGATE_C1NC	16
+#define TEGRA_POWERGATE_SOR	17
+#define TEGRA_POWERGATE_DIS	18
+#define TEGRA_POWERGATE_DISB	19
+#define TEGRA_POWERGATE_XUSBA	20
+#define TEGRA_POWERGATE_XUSBB	21
+#define TEGRA_POWERGATE_XUSBC	22
+#define TEGRA_POWERGATE_VIC	23
+#define TEGRA_POWERGATE_IRAM	24
+
+#define TEGRA_POWERGATE_3D0	TEGRA_POWERGATE_3D
+
+#define TEGRA_IO_RAIL_CSIA	0
+#define TEGRA_IO_RAIL_CSIB	1
+#define TEGRA_IO_RAIL_DSI	2
+#define TEGRA_IO_RAIL_MIPI_BIAS	3
+#define TEGRA_IO_RAIL_PEX_BIAS	4
+#define TEGRA_IO_RAIL_PEX_CLK1	5
+#define TEGRA_IO_RAIL_PEX_CLK2	6
+#define TEGRA_IO_RAIL_USB0	9
+#define TEGRA_IO_RAIL_USB1	10
+#define TEGRA_IO_RAIL_USB2	11
+#define TEGRA_IO_RAIL_USB_BIAS	12
+#define TEGRA_IO_RAIL_NAND	13
+#define TEGRA_IO_RAIL_UART	14
+#define TEGRA_IO_RAIL_BB	15
+#define TEGRA_IO_RAIL_AUDIO	17
+#define TEGRA_IO_RAIL_HSIC	19
+#define TEGRA_IO_RAIL_COMP	22
+#define TEGRA_IO_RAIL_HDMI	28
+#define TEGRA_IO_RAIL_PEX_CNTRL	32
+#define TEGRA_IO_RAIL_SDMMC1	33
+#define TEGRA_IO_RAIL_SDMMC3	34
+#define TEGRA_IO_RAIL_SDMMC4	35
+#define TEGRA_IO_RAIL_CAM	36
+#define TEGRA_IO_RAIL_RES	37
+#define TEGRA_IO_RAIL_HV	38
+#define TEGRA_IO_RAIL_DSIB	39
+#define TEGRA_IO_RAIL_DSIC	40
+#define TEGRA_IO_RAIL_DSID	41
+#define TEGRA_IO_RAIL_CSIE	44
+#define TEGRA_IO_RAIL_LVDS	57
+#define TEGRA_IO_RAIL_SYS_DDC	58
+
+int tegra_powergate_is_powered(int id);
+int tegra_powergate_power_on(int id);
+int tegra_powergate_power_off(int id);
+int tegra_powergate_remove_clamping(int id);
+
+/* Must be called with clk disabled, and returns with clk enabled */
+int tegra_powergate_sequence_power_up(int id, struct clk *clk,
+				      struct reset_control *rst);
+
+#endif /* _MACH_TEGRA_POWERGATE_H_ */
diff --git a/arch/arm/mach-tegra/tegra20-pmc.c b/arch/arm/mach-tegra/tegra20-pmc.c
index d868094..4cd01ff 100644
--- a/arch/arm/mach-tegra/tegra20-pmc.c
+++ b/arch/arm/mach-tegra/tegra20-pmc.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Lucas Stach <l.stach@pengutronix.de>
+ * Copyright (C) 2013-2014 Lucas Stach <l.stach@pengutronix.de>
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -19,15 +19,19 @@
  * @brief Device driver for the Tegra 20 power management controller.
  */
 
-#include <common.h>
 #include <command.h>
+#include <common.h>
 #include <init.h>
 #include <io.h>
 #include <linux/err.h>
-
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <mach/lowlevel.h>
+#include <mach/tegra-powergate.h>
 #include <mach/tegra20-pmc.h>
 
 static void __iomem *pmc_base;
+static int tegra_num_powerdomains;
 
 /* main SoC reset trigger */
 void __noreturn reset_cpu(ulong addr)
@@ -38,6 +42,133 @@ void __noreturn reset_cpu(ulong addr)
 }
 EXPORT_SYMBOL(reset_cpu);
 
+static int tegra_powergate_set(int id, bool new_state)
+{
+	bool status;
+
+	status = readl(pmc_base + PMC_PWRGATE_STATUS) & (1 << id);
+
+	if (status == new_state) {
+		return 0;
+	}
+
+	writel(PMC_PWRGATE_TOGGLE_START | id, pmc_base + PMC_PWRGATE_TOGGLE);
+
+	return 0;
+}
+
+int tegra_powergate_power_on(int id)
+{
+	if (id < 0 || id >= tegra_num_powerdomains)
+		return -EINVAL;
+
+	return tegra_powergate_set(id, true);
+}
+
+int tegra_powergate_power_off(int id)
+{
+	if (id < 0 || id >= tegra_num_powerdomains)
+		return -EINVAL;
+
+	return tegra_powergate_set(id, false);
+}
+EXPORT_SYMBOL(tegra_powergate_power_off);
+
+int tegra_powergate_is_powered(int id)
+{
+	u32 status;
+
+	if (id < 0 || id >= tegra_num_powerdomains)
+		return -EINVAL;
+
+	status = readl(pmc_base + PMC_PWRGATE_STATUS) & (1 << id);
+	return !!status;
+}
+
+int tegra_powergate_remove_clamping(int id)
+{
+	u32 mask;
+
+	if (id < 0 || id >= tegra_num_powerdomains)
+		return -EINVAL;
+
+	/*
+	 * Tegra 2 has a bug where PCIE and VDE clamping masks are
+	 * swapped relatively to the partition ids
+	 */
+	if (id == TEGRA_POWERGATE_VDEC)
+		mask = (1 << TEGRA_POWERGATE_PCIE);
+	else if (id == TEGRA_POWERGATE_PCIE)
+		mask = (1 << TEGRA_POWERGATE_VDEC);
+	else
+		mask = (1 << id);
+
+	writel(mask, pmc_base + PMC_REMOVE_CLAMPING_CMD);
+
+	return 0;
+}
+EXPORT_SYMBOL(tegra_powergate_remove_clamping);
+
+/* Must be called with clk disabled, and returns with clk enabled */
+int tegra_powergate_sequence_power_up(int id, struct clk *clk,
+					struct reset_control *rst)
+{
+	int ret;
+
+	reset_control_assert(rst);
+
+	ret = tegra_powergate_power_on(id);
+	if (ret)
+		goto err_power;
+
+	ret = clk_enable(clk);
+	if (ret)
+		goto err_clk;
+
+	udelay(10);
+
+	ret = tegra_powergate_remove_clamping(id);
+	if (ret)
+		goto err_clamp;
+
+	udelay(10);
+	reset_control_deassert(rst);
+
+	return 0;
+
+err_clamp:
+	clk_disable(clk);
+err_clk:
+	tegra_powergate_power_off(id);
+err_power:
+	return ret;
+}
+EXPORT_SYMBOL(tegra_powergate_sequence_power_up);
+
+static int tegra_powergate_init(void)
+{
+	switch (tegra_get_chiptype()) {
+	case TEGRA20:
+		tegra_num_powerdomains = 7;
+		break;
+	case TEGRA30:
+		tegra_num_powerdomains = 14;
+		break;
+	case TEGRA114:
+		tegra_num_powerdomains = 23;
+		break;
+	case TEGRA124:
+		tegra_num_powerdomains = 25;
+		break;
+	default:
+		/* Unknown Tegra variant. Disable powergating */
+		tegra_num_powerdomains = 0;
+		break;
+	}
+
+	return 0;
+}
+
 static int tegra20_pmc_probe(struct device_d *dev)
 {
 	pmc_base = dev_request_mem_region(dev, 0);
@@ -46,6 +177,8 @@ static int tegra20_pmc_probe(struct device_d *dev)
 		return PTR_ERR(pmc_base);
 	}
 
+	tegra_powergate_init();
+
 	return 0;
 }
 
-- 
1.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v2 15/19] of: import pci range parser from linux
  2014-10-04 17:40 [PATCH v2 00/19] PCI and Tegra series revamp Lucas Stach
                   ` (13 preceding siblings ...)
  2014-10-04 17:40 ` [PATCH v2 14/19] tegra: pmc: add powerdomain handling Lucas Stach
@ 2014-10-04 17:40 ` Lucas Stach
  2014-10-04 17:40 ` [PATCH v2 16/19] pci: add Tegra host controller driver Lucas Stach
                   ` (4 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Lucas Stach @ 2014-10-04 17:40 UTC (permalink / raw)
  To: barebox

Signed-off-by: Lucas Stach <dev@lynxeye.de>
---
 drivers/of/Kconfig   |  1 +
 drivers/of/address.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 include/of_address.h | 57 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 126 insertions(+)

diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 97a1d93..97378ab 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -30,6 +30,7 @@ config OF_GPIO
 config OF_PCI
 	bool
 	depends on PCI
+	select OF_ADDRESS_PCI
 	help
 	  OpenFirmware PCI bus accessors
 
diff --git a/drivers/of/address.c b/drivers/of/address.c
index b3cbb15..8018d78 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -179,6 +179,74 @@ static int of_bus_pci_translate(__be32 *addr, u64 offset, int na)
 }
 #endif /* CONFIG_OF_ADDRESS_PCI */
 
+#ifdef CONFIG_OF_PCI
+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_OF_PCI */
+
 /*
  * Array of bus specific translators
  */
diff --git a/include/of_address.h b/include/of_address.h
index 9022ab7..ebf3ec2 100644
--- a/include/of_address.h
+++ b/include/of_address.h
@@ -4,6 +4,38 @@
 #include <common.h>
 #include <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 = NULL;
+	INIT_LIST_HEAD(&res->children);
+	INIT_LIST_HEAD(&res->sibling);
+	res->name = np->full_name;
+}
+
 #ifndef pci_address_to_pio
 static inline unsigned long pci_address_to_pio(phys_addr_t addr) { return -1; }
 #endif
@@ -69,4 +101,29 @@ static inline void __iomem *of_iomap(struct device_node *np, int index)
 
 #endif /* CONFIG_OFTREE */
 
+#ifdef CONFIG_OF_PCI
+
+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
+
+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_PCI */
+
 #endif /* __OF_ADDRESS_H */
-- 
1.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v2 16/19] pci: add Tegra host controller driver
  2014-10-04 17:40 [PATCH v2 00/19] PCI and Tegra series revamp Lucas Stach
                   ` (14 preceding siblings ...)
  2014-10-04 17:40 ` [PATCH v2 15/19] of: import pci range parser from linux Lucas Stach
@ 2014-10-04 17:40 ` Lucas Stach
  2014-10-04 17:40 ` [PATCH v2 17/19] ARM: tegra: advertise PCI support Lucas Stach
                   ` (3 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Lucas Stach @ 2014-10-04 17:40 UTC (permalink / raw)
  To: barebox

Only tested on Tegra30 for now.

Signed-off-by: Lucas Stach <dev@lynxeye.de>
---
 drivers/pci/Kconfig     |    7 +
 drivers/pci/Makefile    |    1 +
 drivers/pci/pci-tegra.c | 1193 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1201 insertions(+)
 create mode 100644 drivers/pci/pci-tegra.c

diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index d17a151..0e9308e 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -30,6 +30,13 @@ config PCI_MVEBU
 	select OF_PCI
 	select PCI
 
+config PCI_TEGRA
+	bool "NVDIA Tegra PCIe driver"
+	depends on ARCH_TEGRA
+	select OF_ADDRESS_PCI
+	select OF_PCI
+	select PCI
+
 endmenu
 
 endif
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 4423531..b93b28a 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -8,3 +8,4 @@ ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG
 CPPFLAGS += $(ccflags-y)
 
 obj-$(CONFIG_PCI_MVEBU)	+= pci-mvebu.o pci-mvebu-phy.o
+obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o
diff --git a/drivers/pci/pci-tegra.c b/drivers/pci/pci-tegra.c
new file mode 100644
index 0000000..07bb251
--- /dev/null
+++ b/drivers/pci/pci-tegra.c
@@ -0,0 +1,1193 @@
+/*
+ * Copyright (C) 2014 Lucas Stach <l.stach@pengutronix.de>
+ *
+ * based on code
+ * Copyright (c) 2010, CompuLab, Ltd.
+ * Copyright (c) 2008-2009, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <common.h>
+#include <clock.h>
+#include <malloc.h>
+#include <io.h>
+#include <init.h>
+#include <asm/mmu.h>
+
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <of_address.h>
+#include <of_pci.h>
+#include <linux/pci.h>
+#include <linux/reset.h>
+#include <sizes.h>
+#include <mach/tegra-powergate.h>
+#include <regulator.h>
+
+/* register definitions */
+
+#define AFI_AXI_BAR0_SZ	0x00
+#define AFI_AXI_BAR1_SZ	0x04
+#define AFI_AXI_BAR2_SZ	0x08
+#define AFI_AXI_BAR3_SZ	0x0c
+#define AFI_AXI_BAR4_SZ	0x10
+#define AFI_AXI_BAR5_SZ	0x14
+
+#define AFI_AXI_BAR0_START	0x18
+#define AFI_AXI_BAR1_START	0x1c
+#define AFI_AXI_BAR2_START	0x20
+#define AFI_AXI_BAR3_START	0x24
+#define AFI_AXI_BAR4_START	0x28
+#define AFI_AXI_BAR5_START	0x2c
+
+#define AFI_FPCI_BAR0	0x30
+#define AFI_FPCI_BAR1	0x34
+#define AFI_FPCI_BAR2	0x38
+#define AFI_FPCI_BAR3	0x3c
+#define AFI_FPCI_BAR4	0x40
+#define AFI_FPCI_BAR5	0x44
+
+#define AFI_CACHE_BAR0_SZ	0x48
+#define AFI_CACHE_BAR0_ST	0x4c
+#define AFI_CACHE_BAR1_SZ	0x50
+#define AFI_CACHE_BAR1_ST	0x54
+
+#define AFI_MSI_BAR_SZ		0x60
+#define AFI_MSI_FPCI_BAR_ST	0x64
+#define AFI_MSI_AXI_BAR_ST	0x68
+
+#define AFI_MSI_VEC0		0x6c
+#define AFI_MSI_VEC1		0x70
+#define AFI_MSI_VEC2		0x74
+#define AFI_MSI_VEC3		0x78
+#define AFI_MSI_VEC4		0x7c
+#define AFI_MSI_VEC5		0x80
+#define AFI_MSI_VEC6		0x84
+#define AFI_MSI_VEC7		0x88
+
+#define AFI_MSI_EN_VEC0		0x8c
+#define AFI_MSI_EN_VEC1		0x90
+#define AFI_MSI_EN_VEC2		0x94
+#define AFI_MSI_EN_VEC3		0x98
+#define AFI_MSI_EN_VEC4		0x9c
+#define AFI_MSI_EN_VEC5		0xa0
+#define AFI_MSI_EN_VEC6		0xa4
+#define AFI_MSI_EN_VEC7		0xa8
+
+#define AFI_CONFIGURATION		0xac
+#define  AFI_CONFIGURATION_EN_FPCI	(1 << 0)
+
+#define AFI_FPCI_ERROR_MASKS	0xb0
+
+#define AFI_INTR_MASK		0xb4
+#define  AFI_INTR_MASK_INT_MASK	(1 << 0)
+#define  AFI_INTR_MASK_MSI_MASK	(1 << 8)
+
+#define AFI_INTR_CODE			0xb8
+#define  AFI_INTR_CODE_MASK		0xf
+#define  AFI_INTR_AXI_SLAVE_ERROR	1
+#define  AFI_INTR_AXI_DECODE_ERROR	2
+#define  AFI_INTR_TARGET_ABORT		3
+#define  AFI_INTR_MASTER_ABORT		4
+#define  AFI_INTR_INVALID_WRITE		5
+#define  AFI_INTR_LEGACY		6
+#define  AFI_INTR_FPCI_DECODE_ERROR	7
+
+#define AFI_INTR_SIGNATURE	0xbc
+#define AFI_UPPER_FPCI_ADDRESS	0xc0
+#define AFI_SM_INTR_ENABLE	0xc4
+#define  AFI_SM_INTR_INTA_ASSERT	(1 << 0)
+#define  AFI_SM_INTR_INTB_ASSERT	(1 << 1)
+#define  AFI_SM_INTR_INTC_ASSERT	(1 << 2)
+#define  AFI_SM_INTR_INTD_ASSERT	(1 << 3)
+#define  AFI_SM_INTR_INTA_DEASSERT	(1 << 4)
+#define  AFI_SM_INTR_INTB_DEASSERT	(1 << 5)
+#define  AFI_SM_INTR_INTC_DEASSERT	(1 << 6)
+#define  AFI_SM_INTR_INTD_DEASSERT	(1 << 7)
+
+#define AFI_AFI_INTR_ENABLE		0xc8
+#define  AFI_INTR_EN_INI_SLVERR		(1 << 0)
+#define  AFI_INTR_EN_INI_DECERR		(1 << 1)
+#define  AFI_INTR_EN_TGT_SLVERR		(1 << 2)
+#define  AFI_INTR_EN_TGT_DECERR		(1 << 3)
+#define  AFI_INTR_EN_TGT_WRERR		(1 << 4)
+#define  AFI_INTR_EN_DFPCI_DECERR	(1 << 5)
+#define  AFI_INTR_EN_AXI_DECERR		(1 << 6)
+#define  AFI_INTR_EN_FPCI_TIMEOUT	(1 << 7)
+#define  AFI_INTR_EN_PRSNT_SENSE	(1 << 8)
+
+#define AFI_PCIE_CONFIG					0x0f8
+#define  AFI_PCIE_CONFIG_PCIE_DISABLE(x)		(1 << ((x) + 1))
+#define  AFI_PCIE_CONFIG_PCIE_DISABLE_ALL		0xe
+#define  AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK	(0xf << 20)
+#define  AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE	(0x0 << 20)
+#define  AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420	(0x0 << 20)
+#define  AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL	(0x1 << 20)
+#define  AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222	(0x1 << 20)
+#define  AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411	(0x2 << 20)
+
+#define AFI_FUSE			0x104
+#define  AFI_FUSE_PCIE_T0_GEN2_DIS	(1 << 2)
+
+#define AFI_PEX0_CTRL			0x110
+#define AFI_PEX1_CTRL			0x118
+#define AFI_PEX2_CTRL			0x128
+#define  AFI_PEX_CTRL_RST		(1 << 0)
+#define  AFI_PEX_CTRL_CLKREQ_EN		(1 << 1)
+#define  AFI_PEX_CTRL_REFCLK_EN		(1 << 3)
+
+#define AFI_PEXBIAS_CTRL_0		0x168
+
+#define RP_VEND_XP	0x00000F00
+#define  RP_VEND_XP_DL_UP	(1 << 30)
+
+#define RP_LINK_CONTROL_STATUS			0x00000090
+#define  RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE	0x20000000
+#define  RP_LINK_CONTROL_STATUS_LINKSTAT_MASK	0x3fff0000
+
+#define PADS_CTL_SEL		0x0000009C
+
+#define PADS_CTL		0x000000A0
+#define  PADS_CTL_IDDQ_1L	(1 << 0)
+#define  PADS_CTL_TX_DATA_EN_1L	(1 << 6)
+#define  PADS_CTL_RX_DATA_EN_1L	(1 << 10)
+
+#define PADS_PLL_CTL_TEGRA20			0x000000B8
+#define PADS_PLL_CTL_TEGRA30			0x000000B4
+#define  PADS_PLL_CTL_RST_B4SM			(1 << 1)
+#define  PADS_PLL_CTL_LOCKDET			(1 << 8)
+#define  PADS_PLL_CTL_REFCLK_MASK		(0x3 << 16)
+#define  PADS_PLL_CTL_REFCLK_INTERNAL_CML	(0 << 16)
+#define  PADS_PLL_CTL_REFCLK_INTERNAL_CMOS	(1 << 16)
+#define  PADS_PLL_CTL_REFCLK_EXTERNAL		(2 << 16)
+#define  PADS_PLL_CTL_TXCLKREF_MASK		(0x1 << 20)
+#define  PADS_PLL_CTL_TXCLKREF_DIV10		(0 << 20)
+#define  PADS_PLL_CTL_TXCLKREF_DIV5		(1 << 20)
+#define  PADS_PLL_CTL_TXCLKREF_BUF_EN		(1 << 22)
+
+#define PADS_REFCLK_CFG0			0x000000C8
+#define PADS_REFCLK_CFG1			0x000000CC
+
+/*
+ * Fields in PADS_REFCLK_CFG*. Those registers form an array of 16-bit
+ * entries, one entry per PCIe port. These field definitions and desired
+ * values aren't in the TRM, but do come from NVIDIA.
+ */
+#define PADS_REFCLK_CFG_TERM_SHIFT		2  /* 6:2 */
+#define PADS_REFCLK_CFG_E_TERM_SHIFT		7
+#define PADS_REFCLK_CFG_PREDI_SHIFT		8  /* 11:8 */
+#define PADS_REFCLK_CFG_DRVI_SHIFT		12 /* 15:12 */
+
+/* Default value provided by HW engineering is 0xfa5c */
+#define PADS_REFCLK_CFG_VALUE \
+	( \
+		(0x17 << PADS_REFCLK_CFG_TERM_SHIFT)   | \
+		(0    << PADS_REFCLK_CFG_E_TERM_SHIFT) | \
+		(0xa  << PADS_REFCLK_CFG_PREDI_SHIFT)  | \
+		(0xf  << PADS_REFCLK_CFG_DRVI_SHIFT)     \
+	)
+
+/* used to differentiate between Tegra SoC generations */
+struct tegra_pcie_soc_data {
+	unsigned int num_ports;
+	unsigned int msi_base_shift;
+	u32 pads_pll_ctl;
+	u32 tx_ref_sel;
+	bool has_pex_clkreq_en;
+	bool has_pex_bias_ctrl;
+	bool has_intr_prsnt_sense;
+	bool has_avdd_supply;
+	bool has_cml_clk;
+};
+
+struct tegra_pcie {
+	struct device_d *dev;
+	struct pci_controller pci;
+
+	void __iomem *pads;
+	void __iomem *afi;
+	void __iomem *cs;
+
+	struct list_head buses;
+
+	struct resource io;
+	struct resource mem;
+	struct resource prefetch;
+	struct resource busn;
+	struct resource *cs_res;
+
+	struct clk *pex_clk;
+	struct clk *afi_clk;
+	struct clk *pll_e;
+	struct clk *cml_clk;
+
+	struct reset_control *pex_rst;
+	struct reset_control *afi_rst;
+	struct reset_control *pcie_xrst;
+
+	struct list_head ports;
+	unsigned int num_ports;
+	u32 xbar_config;
+
+	struct regulator *pex_clk_supply;
+	struct regulator *vdd_supply;
+	struct regulator *avdd_supply;
+
+	const struct tegra_pcie_soc_data *soc_data;
+};
+
+struct tegra_pcie_port {
+	struct tegra_pcie *pcie;
+	struct list_head list;
+	struct resource regs;
+	void __iomem *base;
+	unsigned int index;
+	unsigned int lanes;
+};
+
+static inline struct tegra_pcie *host_to_pcie(struct pci_controller *host)
+{
+	return container_of(host, struct tegra_pcie, pci);
+}
+
+static inline void afi_writel(struct tegra_pcie *pcie, u32 value,
+			      unsigned long offset)
+{
+	writel(value, pcie->afi + offset);
+}
+
+static inline u32 afi_readl(struct tegra_pcie *pcie, unsigned long offset)
+{
+	return readl(pcie->afi + offset);
+}
+
+static inline void pads_writel(struct tegra_pcie *pcie, u32 value,
+			       unsigned long offset)
+{
+	writel(value, pcie->pads + offset);
+}
+
+static inline u32 pads_readl(struct tegra_pcie *pcie, unsigned long offset)
+{
+	return readl(pcie->pads + offset);
+}
+
+static void __iomem *tegra_pcie_conf_address(struct pci_bus *bus,
+					     unsigned int devfn,
+					     int where)
+{
+	struct tegra_pcie *pcie = host_to_pcie(bus->host);
+	void __iomem *addr = NULL;
+
+	if (bus->number == 0) {
+		unsigned int slot = PCI_SLOT(devfn);
+		struct tegra_pcie_port *port;
+
+		list_for_each_entry(port, &pcie->ports, list) {
+			if (port->index + 1 == slot) {
+				addr = (void __force __iomem *)
+					port->regs.start + (where & ~3);
+				break;
+			}
+		}
+	} else {
+		addr = pcie->cs;
+
+		addr += ((where & 0xf00) << 16) | (bus->number << 16) |
+			(PCI_SLOT(devfn) << 11) | (PCI_FUNC(devfn) << 8) |
+			(where & 0xfc);
+	}
+
+	return addr;
+}
+
+static int tegra_pcie_read_conf(struct pci_bus *bus, unsigned int devfn,
+				int where, int size, u32 *value)
+{
+	void __iomem *addr;
+
+	addr = tegra_pcie_conf_address(bus, devfn, where);
+	if (!addr) {
+		*value = 0xffffffff;
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	}
+
+	*value = readl(addr);
+
+	if (size == 1)
+		*value = (*value >> (8 * (where & 3))) & 0xff;
+	else if (size == 2)
+		*value = (*value >> (8 * (where & 3))) & 0xffff;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int tegra_pcie_write_conf(struct pci_bus *bus, unsigned int devfn,
+				 int where, int size, u32 value)
+{
+	void __iomem *addr;
+	u32 mask, tmp;
+
+	addr = tegra_pcie_conf_address(bus, devfn, where);
+	if (!addr)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	if (size == 4) {
+		writel(value, addr);
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	if (size == 2)
+		mask = ~(0xffff << ((where & 0x3) * 8));
+	else if (size == 1)
+		mask = ~(0xff << ((where & 0x3) * 8));
+	else
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+
+	tmp = readl(addr) & mask;
+	tmp |= value << ((where & 0x3) * 8);
+	writel(tmp, addr);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int tegra_pcie_res_start(struct pci_bus *bus, resource_size_t res_addr)
+{
+	return res_addr;
+}
+
+static struct pci_ops tegra_pcie_ops = {
+	.read = tegra_pcie_read_conf,
+	.write = tegra_pcie_write_conf,
+	.res_start = tegra_pcie_res_start,
+};
+
+static unsigned long tegra_pcie_port_get_pex_ctrl(struct tegra_pcie_port *port)
+{
+	unsigned long ret = 0;
+
+	switch (port->index) {
+	case 0:
+		ret = AFI_PEX0_CTRL;
+		break;
+
+	case 1:
+		ret = AFI_PEX1_CTRL;
+		break;
+
+	case 2:
+		ret = AFI_PEX2_CTRL;
+		break;
+	}
+
+	return ret;
+}
+
+static void tegra_pcie_port_reset(struct tegra_pcie_port *port)
+{
+	unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port);
+	unsigned long value;
+
+	/* pulse reset signal */
+	value = afi_readl(port->pcie, ctrl);
+	value &= ~AFI_PEX_CTRL_RST;
+	afi_writel(port->pcie, value, ctrl);
+
+	mdelay(1);
+
+	value = afi_readl(port->pcie, ctrl);
+	value |= AFI_PEX_CTRL_RST;
+	afi_writel(port->pcie, value, ctrl);
+}
+
+static void tegra_pcie_port_enable(struct tegra_pcie_port *port)
+{
+	const struct tegra_pcie_soc_data *soc = port->pcie->soc_data;
+	unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port);
+	unsigned long value;
+
+	/* enable reference clock */
+	value = afi_readl(port->pcie, ctrl);
+	value |= AFI_PEX_CTRL_REFCLK_EN;
+
+	if (soc->has_pex_clkreq_en)
+		value |= AFI_PEX_CTRL_CLKREQ_EN;
+
+	afi_writel(port->pcie, value, ctrl);
+
+	tegra_pcie_port_reset(port);
+}
+
+static void tegra_pcie_port_disable(struct tegra_pcie_port *port)
+{
+	unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port);
+	unsigned long value;
+
+	/* assert port reset */
+	value = afi_readl(port->pcie, ctrl);
+	value &= ~AFI_PEX_CTRL_RST;
+	afi_writel(port->pcie, value, ctrl);
+
+	/* disable reference clock */
+	value = afi_readl(port->pcie, ctrl);
+	value &= ~AFI_PEX_CTRL_REFCLK_EN;
+	afi_writel(port->pcie, value, ctrl);
+}
+
+static void tegra_pcie_port_free(struct tegra_pcie_port *port)
+{
+	list_del(&port->list);
+	kfree(port);
+}
+
+#if 0
+static void tegra_pcie_fixup_bridge(struct pci_dev *dev)
+{
+	u16 reg;
+
+	if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE) {
+		pci_read_config_word(dev, PCI_COMMAND, &reg);
+		reg |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+			PCI_COMMAND_MASTER | PCI_COMMAND_SERR);
+		pci_write_config_word(dev, PCI_COMMAND, reg);
+	}
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_fixup_bridge);
+
+/* Tegra PCIE root complex wrongly reports device class */
+static void tegra_pcie_fixup_class(struct pci_dev *dev)
+{
+	dev->class = PCI_CLASS_BRIDGE_PCI << 8;
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf0, tegra_pcie_fixup_class);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf1, tegra_pcie_fixup_class);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0e1c, tegra_pcie_fixup_class);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0e1d, tegra_pcie_fixup_class);
+
+/* Tegra PCIE requires relaxed ordering */
+static void tegra_pcie_relax_enable(struct pci_dev *dev)
+{
+	pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_RELAX_EN);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_relax_enable);
+#endif
+
+/*
+ * FPCI map is as follows:
+ * - 0xfdfc000000: I/O space
+ * - 0xfdfe000000: type 0 configuration space
+ * - 0xfdff000000: type 1 configuration space
+ * - 0xfe00000000: type 0 extended configuration space
+ * - 0xfe10000000: type 1 extended configuration space
+ */
+static void tegra_pcie_setup_translations(struct tegra_pcie *pcie)
+{
+	u32 fpci_bar, size, axi_address;
+
+	/* Bar 0: type 1 extended configuration space */
+	fpci_bar = 0xfe100000;
+	size = resource_size(pcie->cs_res);
+	axi_address = pcie->cs_res->start;
+	afi_writel(pcie, axi_address, AFI_AXI_BAR0_START);
+	afi_writel(pcie, size >> 12, AFI_AXI_BAR0_SZ);
+	afi_writel(pcie, fpci_bar, AFI_FPCI_BAR0);
+
+	/* Bar 1: downstream IO bar */
+	fpci_bar = 0xfdfc0000;
+	size = resource_size(&pcie->io);
+	axi_address = pcie->io.start;
+	afi_writel(pcie, axi_address, AFI_AXI_BAR1_START);
+	afi_writel(pcie, size >> 12, AFI_AXI_BAR1_SZ);
+	afi_writel(pcie, fpci_bar, AFI_FPCI_BAR1);
+
+	/* Bar 2: prefetchable memory BAR */
+	fpci_bar = (((pcie->prefetch.start >> 12) & 0x0fffffff) << 4) | 0x1;
+	size = resource_size(&pcie->prefetch);
+	axi_address = pcie->prefetch.start;
+	afi_writel(pcie, axi_address, AFI_AXI_BAR2_START);
+	afi_writel(pcie, size >> 12, AFI_AXI_BAR2_SZ);
+	afi_writel(pcie, fpci_bar, AFI_FPCI_BAR2);
+
+	/* Bar 3: non prefetchable memory BAR */
+	fpci_bar = (((pcie->mem.start >> 12) & 0x0fffffff) << 4) | 0x1;
+	size = resource_size(&pcie->mem);
+	axi_address = pcie->mem.start;
+	afi_writel(pcie, axi_address, AFI_AXI_BAR3_START);
+	afi_writel(pcie, size >> 12, AFI_AXI_BAR3_SZ);
+	afi_writel(pcie, fpci_bar, AFI_FPCI_BAR3);
+
+	/* NULL out the remaining BARs as they are not used */
+	afi_writel(pcie, 0, AFI_AXI_BAR4_START);
+	afi_writel(pcie, 0, AFI_AXI_BAR4_SZ);
+	afi_writel(pcie, 0, AFI_FPCI_BAR4);
+
+	afi_writel(pcie, 0, AFI_AXI_BAR5_START);
+	afi_writel(pcie, 0, AFI_AXI_BAR5_SZ);
+	afi_writel(pcie, 0, AFI_FPCI_BAR5);
+
+	/* map all upstream transactions as uncached */
+	/* FIXME: is 0 ok for BAR0 start ??? */
+	afi_writel(pcie, 0, AFI_CACHE_BAR0_ST);
+	afi_writel(pcie, 0, AFI_CACHE_BAR0_SZ);
+	afi_writel(pcie, 0, AFI_CACHE_BAR1_ST);
+	afi_writel(pcie, 0, AFI_CACHE_BAR1_SZ);
+
+	/* MSI translations are setup only when needed */
+	afi_writel(pcie, 0, AFI_MSI_FPCI_BAR_ST);
+	afi_writel(pcie, 0, AFI_MSI_BAR_SZ);
+	afi_writel(pcie, 0, AFI_MSI_AXI_BAR_ST);
+	afi_writel(pcie, 0, AFI_MSI_BAR_SZ);
+}
+
+static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
+{
+	const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+	struct tegra_pcie_port *port;
+	unsigned long value;
+
+	/* power down PCIe slot clock bias pad */
+	if (soc->has_pex_bias_ctrl)
+		afi_writel(pcie, 0, AFI_PEXBIAS_CTRL_0);
+
+	/* configure mode and disable all ports */
+	value = afi_readl(pcie, AFI_PCIE_CONFIG);
+	value &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK;
+	value |= AFI_PCIE_CONFIG_PCIE_DISABLE_ALL | pcie->xbar_config;
+
+	list_for_each_entry(port, &pcie->ports, list)
+		value &= ~AFI_PCIE_CONFIG_PCIE_DISABLE(port->index);
+
+	afi_writel(pcie, value, AFI_PCIE_CONFIG);
+
+	value = afi_readl(pcie, AFI_FUSE);
+	value |= AFI_FUSE_PCIE_T0_GEN2_DIS;
+	afi_writel(pcie, value, AFI_FUSE);
+
+	/* initialize internal PHY, enable up to 16 PCIE lanes */
+	pads_writel(pcie, 0x0, PADS_CTL_SEL);
+
+	/* override IDDQ to 1 on all 4 lanes */
+	value = pads_readl(pcie, PADS_CTL);
+	value |= PADS_CTL_IDDQ_1L;
+	pads_writel(pcie, value, PADS_CTL);
+
+	/*
+	 * Set up PHY PLL inputs select PLLE output as refclock,
+	 * set TX ref sel to div10 (not div5).
+	 */
+	value = pads_readl(pcie, soc->pads_pll_ctl);
+	value &= ~(PADS_PLL_CTL_REFCLK_MASK | PADS_PLL_CTL_TXCLKREF_MASK);
+	value |= PADS_PLL_CTL_REFCLK_INTERNAL_CML | soc->tx_ref_sel;
+	pads_writel(pcie, value, soc->pads_pll_ctl);
+
+	/* take PLL out of reset  */
+	value = pads_readl(pcie, soc->pads_pll_ctl);
+	value |= PADS_PLL_CTL_RST_B4SM;
+	pads_writel(pcie, value, soc->pads_pll_ctl);
+
+	/* Configure the reference clock driver */
+	value = PADS_REFCLK_CFG_VALUE | (PADS_REFCLK_CFG_VALUE << 16);
+	pads_writel(pcie, value, PADS_REFCLK_CFG0);
+	if (soc->num_ports > 2)
+		pads_writel(pcie, PADS_REFCLK_CFG_VALUE, PADS_REFCLK_CFG1);
+
+	/* wait for the PLL to lock */
+	if (wait_on_timeout(300 * MSECOND,
+		(pads_readl(pcie, soc->pads_pll_ctl) & PADS_PLL_CTL_LOCKDET))) {
+		pr_err("Tegra PCIe error: timeout waiting for PLL\n");
+		return -EBUSY;
+	}
+
+	/* turn off IDDQ override */
+	value = pads_readl(pcie, PADS_CTL);
+	value &= ~PADS_CTL_IDDQ_1L;
+	pads_writel(pcie, value, PADS_CTL);
+
+	/* enable TX/RX data */
+	value = pads_readl(pcie, PADS_CTL);
+	value |= PADS_CTL_TX_DATA_EN_1L | PADS_CTL_RX_DATA_EN_1L;
+	pads_writel(pcie, value, PADS_CTL);
+
+	/* take the PCIe interface module out of reset */
+	reset_control_deassert(pcie->pcie_xrst);
+
+	/* finally enable PCIe */
+	value = afi_readl(pcie, AFI_CONFIGURATION);
+	value |= AFI_CONFIGURATION_EN_FPCI;
+	afi_writel(pcie, value, AFI_CONFIGURATION);
+
+	value = AFI_INTR_EN_INI_SLVERR | AFI_INTR_EN_INI_DECERR |
+		AFI_INTR_EN_TGT_SLVERR | AFI_INTR_EN_TGT_DECERR |
+		AFI_INTR_EN_TGT_WRERR | AFI_INTR_EN_DFPCI_DECERR;
+
+	if (soc->has_intr_prsnt_sense)
+		value |= AFI_INTR_EN_PRSNT_SENSE;
+
+	afi_writel(pcie, value, AFI_AFI_INTR_ENABLE);
+	afi_writel(pcie, 0xffffffff, AFI_SM_INTR_ENABLE);
+
+	/* don't enable MSI for now, only when needed */
+	afi_writel(pcie, AFI_INTR_MASK_INT_MASK, AFI_INTR_MASK);
+
+	/* disable all exceptions */
+	afi_writel(pcie, 0, AFI_FPCI_ERROR_MASKS);
+
+	return 0;
+}
+
+static void tegra_pcie_power_off(struct tegra_pcie *pcie)
+{
+	const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+	int err;
+
+	/* TODO: disable and unprepare clocks? */
+
+	reset_control_assert(pcie->pcie_xrst);
+	reset_control_assert(pcie->afi_rst);
+	reset_control_assert(pcie->pex_rst);
+
+	tegra_powergate_power_off(TEGRA_POWERGATE_PCIE);
+
+	if (soc->has_avdd_supply) {
+		err = regulator_disable(pcie->avdd_supply);
+		if (err < 0)
+			dev_warn(pcie->dev,
+				 "failed to disable AVDD regulator: %d\n",
+				 err);
+	}
+
+	err = regulator_disable(pcie->pex_clk_supply);
+	if (err < 0)
+		dev_warn(pcie->dev, "failed to disable pex-clk regulator: %d\n",
+			 err);
+
+	err = regulator_disable(pcie->vdd_supply);
+	if (err < 0)
+		dev_warn(pcie->dev, "failed to disable VDD regulator: %d\n",
+			 err);
+}
+
+static int tegra_pcie_power_on(struct tegra_pcie *pcie)
+{
+	const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+	int err;
+
+	reset_control_assert(pcie->pcie_xrst);
+	reset_control_assert(pcie->afi_rst);
+	reset_control_assert(pcie->pex_rst);
+
+	tegra_powergate_power_off(TEGRA_POWERGATE_PCIE);
+
+	/* enable regulators */
+	err = regulator_enable(pcie->vdd_supply);
+	if (err < 0) {
+		dev_err(pcie->dev, "failed to enable VDD regulator: %d\n", err);
+		return err;
+	}
+
+	err = regulator_enable(pcie->pex_clk_supply);
+	if (err < 0) {
+		dev_err(pcie->dev, "failed to enable pex-clk regulator: %d\n",
+			err);
+		return err;
+	}
+
+	if (soc->has_avdd_supply) {
+		err = regulator_enable(pcie->avdd_supply);
+		if (err < 0) {
+			dev_err(pcie->dev,
+				"failed to enable AVDD regulator: %d\n",
+				err);
+			return err;
+		}
+	}
+
+	err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE,
+						pcie->pex_clk,
+						pcie->pex_rst);
+	if (err) {
+		dev_err(pcie->dev, "powerup sequence failed: %d\n", err);
+		return err;
+	}
+
+	reset_control_deassert(pcie->afi_rst);
+
+	err = clk_enable(pcie->afi_clk);
+	if (err < 0) {
+		dev_err(pcie->dev, "failed to enable AFI clock: %d\n", err);
+		return err;
+	}
+
+	if (soc->has_cml_clk) {
+		err = clk_enable(pcie->cml_clk);
+		if (err < 0) {
+			dev_err(pcie->dev, "failed to enable CML clock: %d\n",
+				err);
+			return err;
+		}
+	}
+
+	err = clk_enable(pcie->pll_e);
+	if (err < 0) {
+		dev_err(pcie->dev, "failed to enable PLLE clock: %d\n", err);
+		return err;
+	}
+
+	return 0;
+}
+
+static int tegra_pcie_clocks_get(struct tegra_pcie *pcie)
+{
+	const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+
+	pcie->pex_clk = clk_get(pcie->dev, "pex");
+	if (IS_ERR(pcie->pex_clk))
+		return PTR_ERR(pcie->pex_clk);
+
+	pcie->afi_clk = clk_get(pcie->dev, "afi");
+	if (IS_ERR(pcie->afi_clk))
+		return PTR_ERR(pcie->afi_clk);
+
+	pcie->pll_e = clk_get(pcie->dev, "pll_e");
+	if (IS_ERR(pcie->pll_e))
+		return PTR_ERR(pcie->pll_e);
+
+	if (soc->has_cml_clk) {
+		pcie->cml_clk = clk_get(pcie->dev, "cml");
+		if (IS_ERR(pcie->cml_clk))
+			return PTR_ERR(pcie->cml_clk);
+	}
+
+	return 0;
+}
+
+static int tegra_pcie_resets_get(struct tegra_pcie *pcie)
+{
+	pcie->pex_rst = reset_control_get(pcie->dev, "pex");
+	if (IS_ERR(pcie->pex_rst))
+		return PTR_ERR(pcie->pex_rst);
+
+	pcie->afi_rst = reset_control_get(pcie->dev, "afi");
+	if (IS_ERR(pcie->afi_rst))
+		return PTR_ERR(pcie->afi_rst);
+
+	pcie->pcie_xrst = reset_control_get(pcie->dev, "pcie_x");
+	if (IS_ERR(pcie->pcie_xrst))
+		return PTR_ERR(pcie->pcie_xrst);
+
+	return 0;
+}
+
+static int tegra_pcie_get_resources(struct tegra_pcie *pcie)
+{
+	struct device_d *dev = pcie->dev;
+	int err;
+
+	err = tegra_pcie_clocks_get(pcie);
+	if (err) {
+		dev_err(dev, "failed to get clocks: %d\n", err);
+		return err;
+	}
+
+	err = tegra_pcie_resets_get(pcie);
+	if (err) {
+		dev_err(dev, "failed to get resets: %d\n", err);
+		return err;
+	}
+
+	err = tegra_pcie_power_on(pcie);
+	if (err) {
+		dev_err(dev, "failed to power up: %d\n", err);
+		return err;
+	}
+
+	pcie->pads = dev_request_mem_region_by_name(dev, "pads");
+	if (IS_ERR(pcie->pads)) {
+		err = PTR_ERR(pcie->pads);
+		goto poweroff;
+	}
+
+	pcie->afi = dev_request_mem_region_by_name(dev, "afi");
+	if (IS_ERR(pcie->afi)) {
+		err = PTR_ERR(pcie->afi);
+		goto poweroff;
+	}
+
+	pcie->cs_res = dev_get_resource_by_name(dev, IORESOURCE_MEM, "cs");
+	pcie->cs = dev_request_mem_region_by_name(dev, "cs");
+	if (IS_ERR(pcie->cs)) {
+		err = PTR_ERR(pcie->cs);
+		goto poweroff;
+	}
+
+	return 0;
+
+poweroff:
+	tegra_pcie_power_off(pcie);
+	return err;
+}
+
+static int tegra_pcie_put_resources(struct tegra_pcie *pcie)
+{
+	tegra_pcie_power_off(pcie);
+	return 0;
+}
+
+static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes,
+				      u32 *xbar)
+{
+	struct device_node *np = pcie->dev->device_node;
+
+	if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) {
+		switch (lanes) {
+		case 0x00000204:
+			dev_info(pcie->dev, "4x1, 2x1 configuration\n");
+			*xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420;
+			return 0;
+
+		case 0x00020202:
+			dev_info(pcie->dev, "2x3 configuration\n");
+			*xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222;
+			return 0;
+
+		case 0x00010104:
+			dev_info(pcie->dev, "4x1, 1x2 configuration\n");
+			*xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411;
+			return 0;
+		}
+	} else if (of_device_is_compatible(np, "nvidia,tegra20-pcie")) {
+		switch (lanes) {
+		case 0x00000004:
+			dev_info(pcie->dev, "single-mode configuration\n");
+			*xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE;
+			return 0;
+
+		case 0x00000202:
+			dev_info(pcie->dev, "dual-mode configuration\n");
+			*xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL;
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
+{
+	const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+	struct device_node *np = pcie->dev->device_node, *port;
+	struct of_pci_range_parser parser;
+	struct of_pci_range range;
+	struct resource *rp_res;
+	struct resource res;
+	u32 lanes = 0;
+	int err;
+
+	if (of_pci_range_parser_init(&parser, np)) {
+		dev_err(pcie->dev, "missing \"ranges\" property\n");
+		return -EINVAL;
+	}
+
+	pcie->vdd_supply = regulator_get(pcie->dev, "vdd");
+	if (IS_ERR(pcie->vdd_supply))
+		return PTR_ERR(pcie->vdd_supply);
+
+	pcie->pex_clk_supply = regulator_get(pcie->dev, "pex-clk");
+	if (IS_ERR(pcie->pex_clk_supply))
+		return PTR_ERR(pcie->pex_clk_supply);
+
+	if (soc->has_avdd_supply) {
+		pcie->avdd_supply = regulator_get(pcie->dev, "avdd");
+		if (IS_ERR(pcie->avdd_supply))
+			return PTR_ERR(pcie->avdd_supply);
+	}
+
+	for_each_of_pci_range(&parser, &range) {
+		of_pci_range_to_resource(&range, np, &res);
+
+		switch (res.flags & IORESOURCE_TYPE_BITS) {
+		case IORESOURCE_IO:
+			memcpy(&pcie->io, &res, sizeof(res));
+			pcie->io.name = "I/O";
+			break;
+
+		case IORESOURCE_MEM:
+			if (res.flags & IORESOURCE_PREFETCH) {
+				memcpy(&pcie->prefetch, &res, sizeof(res));
+				pcie->prefetch.name = "PREFETCH";
+			} else {
+				memcpy(&pcie->mem, &res, sizeof(res));
+				pcie->mem.name = "MEM";
+			}
+			break;
+		}
+	}
+#if 0
+	err = of_pci_parse_bus_range(np, &pcie->busn);
+	if (err < 0) {
+		dev_err(pcie->dev, "failed to parse ranges property: %d\n",
+			err);
+		pcie->busn.name = np->name;
+		pcie->busn.start = 0;
+		pcie->busn.end = 0xff;
+		pcie->busn.flags = IORESOURCE_BUS;
+	}
+#endif
+	/* parse root ports */
+	for_each_child_of_node(np, port) {
+		struct tegra_pcie_port *rp;
+		unsigned int index;
+		u32 value;
+
+		err = of_pci_get_devfn(port);
+		if (err < 0) {
+			dev_err(pcie->dev, "failed to parse address: %d\n",
+				err);
+			return err;
+		}
+
+		index = PCI_SLOT(err);
+
+		if (index < 1 || index > soc->num_ports) {
+			dev_err(pcie->dev, "invalid port number: %d\n", index);
+			return -EINVAL;
+		}
+
+		index--;
+
+		err = of_property_read_u32(port, "nvidia,num-lanes", &value);
+		if (err < 0) {
+			dev_err(pcie->dev, "failed to parse # of lanes: %d\n",
+				err);
+			return err;
+		}
+
+		if (value > 16) {
+			dev_err(pcie->dev, "invalid # of lanes: %u\n", value);
+			return -EINVAL;
+		}
+
+		lanes |= value << (index << 3);
+
+		if (!of_device_is_available(port))
+			continue;
+
+		rp = kzalloc(sizeof(*rp), GFP_KERNEL);
+		if (!rp)
+			return -ENOMEM;
+
+		err = of_address_to_resource(port, 0, &rp->regs);
+		if (err < 0) {
+			dev_err(pcie->dev, "failed to parse address: %d\n",
+				err);
+			return err;
+		}
+
+		/*
+		 * if the port address is inside the NULL page we alias this
+		 * range at +1MB and fix up the resource, otherwise just request
+		 */
+		if (!(rp->regs.start & 0xfffff000)) {
+			rp_res = request_iomem_region(dev_name(pcie->dev),
+					rp->regs.start + SZ_1M,
+					rp->regs.end + SZ_1M);
+			if (!rp_res)
+				return -ENOMEM;
+
+			map_io_sections(rp->regs.start,
+					(void *)rp_res->start, SZ_1M);
+
+			rp->regs.start = rp_res->start;
+			rp->regs.end = rp_res->end;
+		} else {
+			rp_res = request_iomem_region(dev_name(pcie->dev),
+					rp->regs.start, rp->regs.end);
+			if (!rp_res)
+				return -ENOMEM;
+		}
+
+		INIT_LIST_HEAD(&rp->list);
+		rp->index = index;
+		rp->lanes = value;
+		rp->pcie = pcie;
+
+		rp->base = (void __force __iomem *)rp->regs.start;
+		if (IS_ERR(rp->base))
+			return PTR_ERR(rp->base);
+
+		list_add_tail(&rp->list, &pcie->ports);
+	}
+
+	err = tegra_pcie_get_xbar_config(pcie, lanes, &pcie->xbar_config);
+	if (err < 0) {
+		dev_err(pcie->dev, "invalid lane configuration\n");
+		return err;
+	}
+
+	return 0;
+}
+
+/*
+ * FIXME: If there are no PCIe cards attached, then calling this function
+ * can result in the increase of the bootup time as there are big timeout
+ * loops.
+ */
+#define TEGRA_PCIE_LINKUP_TIMEOUT	200	/* up to 1.2 seconds */
+static bool tegra_pcie_port_check_link(struct tegra_pcie_port *port)
+{
+	unsigned int timeout;
+	unsigned int retries = 2;
+
+	do {
+		timeout = wait_on_timeout(50 * MSECOND,
+			readl(port->regs.start + RP_VEND_XP) & RP_VEND_XP_DL_UP);
+
+		if (timeout) {
+			dev_dbg(port->pcie->dev, "link %u down, retrying\n",
+				port->index);
+			goto retry;
+		}
+
+		timeout = wait_on_timeout(200 * MSECOND,
+			readl(port->regs.start + RP_LINK_CONTROL_STATUS) &
+			RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE);
+
+		if (!timeout)
+			return true;
+
+retry:
+		tegra_pcie_port_reset(port);
+	} while (--retries);
+
+	return false;
+}
+
+static int tegra_pcie_enable(struct tegra_pcie *pcie)
+{
+	struct tegra_pcie_port *port, *tmp;
+
+	list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
+		dev_dbg(pcie->dev, "probing port %u, using %u lanes\n",
+			 port->index, port->lanes);
+
+		tegra_pcie_port_enable(port);
+
+		if (tegra_pcie_port_check_link(port))
+			continue;
+
+		dev_dbg(pcie->dev, "link %u down, ignoring\n", port->index);
+
+		tegra_pcie_port_disable(port);
+		tegra_pcie_port_free(port);
+	}
+
+	pcie->pci.parent = pcie->dev;
+	pcie->pci.pci_ops = &tegra_pcie_ops;
+	pcie->pci.mem_resource = &pcie->mem;
+	pcie->pci.mem_pref_resource = &pcie->prefetch;
+	pcie->pci.io_resource = &pcie->io;
+
+	register_pci_controller(&pcie->pci);
+
+	return 0;
+}
+
+static const struct tegra_pcie_soc_data tegra20_pcie_data = {
+	.num_ports = 2,
+	.msi_base_shift = 0,
+	.pads_pll_ctl = PADS_PLL_CTL_TEGRA20,
+	.tx_ref_sel = PADS_PLL_CTL_TXCLKREF_DIV10,
+	.has_pex_clkreq_en = false,
+	.has_pex_bias_ctrl = false,
+	.has_intr_prsnt_sense = false,
+	.has_avdd_supply = false,
+	.has_cml_clk = false,
+};
+
+static const struct tegra_pcie_soc_data tegra30_pcie_data = {
+	.num_ports = 3,
+	.msi_base_shift = 8,
+	.pads_pll_ctl = PADS_PLL_CTL_TEGRA30,
+	.tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN,
+	.has_pex_clkreq_en = true,
+	.has_pex_bias_ctrl = true,
+	.has_intr_prsnt_sense = true,
+	.has_avdd_supply = true,
+	.has_cml_clk = true,
+};
+
+static __maybe_unused struct of_device_id tegra_pcie_of_match[] = {
+	{
+		.compatible = "nvidia,tegra30-pcie",
+		.data = (unsigned long)&tegra30_pcie_data
+	}, {
+		.compatible = "nvidia,tegra20-pcie",
+		.data = (unsigned long)&tegra20_pcie_data
+	}, {
+		/* sentinel */
+	},
+};
+
+static int tegra_pcie_probe(struct device_d *dev)
+{
+	struct tegra_pcie *pcie;
+	int err;
+
+	pcie = kzalloc(sizeof(*pcie), GFP_KERNEL);
+	if (!pcie)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&pcie->buses);
+	INIT_LIST_HEAD(&pcie->ports);
+	dev_get_drvdata(dev, (unsigned long *)&pcie->soc_data);
+	pcie->dev = dev;
+
+	err = tegra_pcie_parse_dt(pcie);
+	if (err < 0) {
+		printk("parse DT failed\n");
+		return err;
+	}
+
+	err = tegra_pcie_get_resources(pcie);
+	if (err < 0) {
+		dev_err(dev, "failed to request resources: %d\n", err);
+		return err;
+	}
+
+	err = tegra_pcie_enable_controller(pcie);
+	if (err)
+		goto put_resources;
+
+	/* setup the AFI address translations */
+	tegra_pcie_setup_translations(pcie);
+
+	err = tegra_pcie_enable(pcie);
+	if (err < 0) {
+		dev_err(dev, "failed to enable PCIe ports: %d\n", err);
+		goto put_resources;
+	}
+
+	return 0;
+
+put_resources:
+	tegra_pcie_put_resources(pcie);
+	return err;
+}
+
+static struct driver_d tegra_pcie_driver = {
+	.name = "tegra-pcie",
+	.of_compatible = DRV_OF_COMPAT(tegra_pcie_of_match),
+	.probe = tegra_pcie_probe,
+};
+device_platform_driver(tegra_pcie_driver);
-- 
1.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v2 17/19] ARM: tegra: advertise PCI support
  2014-10-04 17:40 [PATCH v2 00/19] PCI and Tegra series revamp Lucas Stach
                   ` (15 preceding siblings ...)
  2014-10-04 17:40 ` [PATCH v2 16/19] pci: add Tegra host controller driver Lucas Stach
@ 2014-10-04 17:40 ` Lucas Stach
  2014-10-04 17:40 ` [PATCH v2 18/19] net: add rtl8169 driver Lucas Stach
                   ` (2 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Lucas Stach @ 2014-10-04 17:40 UTC (permalink / raw)
  To: barebox

From: Lucas Stach <l.stach@pengutronix.de>

Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
---
 arch/arm/Kconfig                    | 1 +
 arch/arm/configs/tegra_v7_defconfig | 1 +
 2 files changed, 2 insertions(+)

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 3c9b81a..2a00e5e 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -198,6 +198,7 @@ config ARCH_TEGRA
 	bool "NVIDIA Tegra"
 	select CPU_V7
 	select HAS_DEBUG_LL
+	select HW_HAS_PCI
 	select COMMON_CLK
 	select COMMON_CLK_OF_PROVIDER
 	select CLKDEV_LOOKUP
diff --git a/arch/arm/configs/tegra_v7_defconfig b/arch/arm/configs/tegra_v7_defconfig
index 22f5765..8f01b54 100644
--- a/arch/arm/configs/tegra_v7_defconfig
+++ b/arch/arm/configs/tegra_v7_defconfig
@@ -37,6 +37,7 @@ CONFIG_DRIVER_SERIAL_NS16550=y
 CONFIG_MCI=y
 CONFIG_MCI_MMC_BOOT_PARTITIONS=y
 CONFIG_MCI_TEGRA=y
+CONFIG_PCI_TEGRA=y
 CONFIG_FS_EXT4=y
 CONFIG_FS_FAT=y
 CONFIG_FS_FAT_LFN=y
-- 
1.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v2 18/19] net: add rtl8169 driver
  2014-10-04 17:40 [PATCH v2 00/19] PCI and Tegra series revamp Lucas Stach
                   ` (16 preceding siblings ...)
  2014-10-04 17:40 ` [PATCH v2 17/19] ARM: tegra: advertise PCI support Lucas Stach
@ 2014-10-04 17:40 ` Lucas Stach
  2014-10-04 17:40 ` [PATCH v2 19/19] ARM: tegra: enable network related options in defconfig Lucas Stach
  2014-10-08  6:39 ` [PATCH v2 00/19] PCI and Tegra series revamp Sascha Hauer
  19 siblings, 0 replies; 21+ messages in thread
From: Lucas Stach @ 2014-10-04 17:40 UTC (permalink / raw)
  To: barebox

This adds the driver for RealTek 8169 and compatible
pci attached network chips.

Signed-off-by: Lucas Stach <dev@lynxeye.de>
---
 drivers/net/Kconfig   |   8 +
 drivers/net/Makefile  |   1 +
 drivers/net/rtl8169.c | 566 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 575 insertions(+)
 create mode 100644 drivers/net/rtl8169.c

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index c99fcc8..24b9844 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -156,6 +156,14 @@ config DRIVER_NET_RTL8139
 	  This is a driver for the Fast Ethernet PCI network cards based on
 	  the RTL 8139 chips.
 
+config DRIVER_NET_RTL8169
+	bool "RealTek RTL-8169 PCI Ethernet driver"
+	depends on PCI
+	select PHYLIB
+	help
+	  This is a driver for the Fast Ethernet PCI network cards based on
+	  the RTL 8169 chips.
+
 config DRIVER_NET_SMC911X
 	bool "smc911x ethernet driver"
 	select PHYLIB
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 1b85778..3e66b31 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_DRIVER_NET_MPC5200)	+= fec_mpc5200.o
 obj-$(CONFIG_DRIVER_NET_NETX)		+= netx_eth.o
 obj-$(CONFIG_DRIVER_NET_ORION)		+= orion-gbe.o
 obj-$(CONFIG_DRIVER_NET_RTL8139)	+= rtl8139.o
+obj-$(CONFIG_DRIVER_NET_RTL8169)	+= rtl8169.o
 obj-$(CONFIG_DRIVER_NET_SMC911X)	+= smc911x.o
 obj-$(CONFIG_DRIVER_NET_SMC91111)	+= smc91111.o
 obj-$(CONFIG_DRIVER_NET_TAP)		+= tap.o
diff --git a/drivers/net/rtl8169.c b/drivers/net/rtl8169.c
new file mode 100644
index 0000000..3ed2e56
--- /dev/null
+++ b/drivers/net/rtl8169.c
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2014 Lucas Stach <l.stach@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <asm/mmu.h>
+#include <common.h>
+#include <init.h>
+#include <net.h>
+#include <malloc.h>
+#include <linux/pci.h>
+
+#define NUM_TX_DESC	1
+#define NUM_RX_DESC	4
+#define PKT_BUF_SIZE	1536
+#define ETH_ZLEN	60
+
+struct rtl8169_chip_info {
+	const char *name;
+	u8 version;
+	u32 RxConfigMask;
+};
+
+#define BD_STAT_OWN	0x80000000
+#define BD_STAT_EOR	0x40000000
+#define BD_STAT_FS	0x20000000
+#define BD_STAT_LS	0x10000000
+#define BD_STAT_RX_RES	0x00200000
+struct bufdesc {
+	u32 status;
+	u32 vlan_tag;
+	u32 buf_addr;
+	u32 buf_Haddr;
+};
+
+struct rtl8169_priv {
+	struct eth_device	edev;
+	void __iomem		*base;
+	struct pci_dev		*pci_dev;
+	int			chipset;
+
+	struct bufdesc		*tx_desc;
+	void			*tx_buf;
+	unsigned int		cur_tx;
+
+	struct bufdesc		*rx_desc;
+	void			*rx_buf;
+	unsigned int		cur_rx;
+
+	struct mii_bus miibus;
+};
+
+#define MAC0 			0x00
+#define MAR0			0x08
+#define TxDescStartAddrLow	0x20
+#define TxDescStartAddrHigh	0x24
+#define TxHDescStartAddrLow	0x28
+#define TxHDescStartAddrHigh	0x2c
+#define FLASH			0x30
+#define ERSR			0x36
+#define ChipCmd			0x37
+#define  CmdReset		0x10
+#define  CmdRxEnb		0x08
+#define  CmdTxEnb		0x04
+#define  RxBufEmpty		0x01
+#define TxPoll			0x38
+#define IntrMask		0x3c
+#define IntrStatus		0x3e
+#define  SYSErr			0x8000
+#define  PCSTimeout		0x4000
+#define  SWInt			0x0100
+#define  TxDescUnavail		0x80
+#define  RxFIFOOver		0x40
+#define  RxUnderrun		0x20
+#define  RxOverflow		0x10
+#define  TxErr			0x08
+#define  TxOK			0x04
+#define  RxErr			0x02
+#define  RxOK			0x01
+#define TxConfig		0x40
+#define  TxInterFrameGapShift	24
+#define  TxDMAShift		8
+#define RxConfig		0x44
+#define  AcceptErr		0x20
+#define  AcceptRunt		0x10
+#define  AcceptBroadcast	0x08
+#define  AcceptMulticast	0x04
+#define  AcceptMyPhys		0x02
+#define  AcceptAllPhys		0x01
+#define  RxCfgFIFOShift		13
+#define  RxCfgDMAShift		8
+#define RxMissed		0x4c
+#define Cfg9346			0x50
+#define  Cfg9346_Lock		0x00
+#define  Cfg9346_Unlock		0xc0
+#define Config0			0x51
+#define Config1			0x52
+#define Config2			0x53
+#define Config3			0x54
+#define Config4			0x55
+#define Config5			0x56
+#define MultiIntr		0x5c
+#define PHYAR			0x60
+#define TBICSR			0x64
+#define TBI_ANAR		0x68
+#define TBI_LPAR		0x6a
+#define PHYstatus		0x6c
+#define RxMaxSize		0xda
+#define CPlusCmd		0xe0
+#define RxDescStartAddrLow	0xe4
+#define RxDescStartAddrHigh	0xe8
+#define EarlyTxThres		0xec
+#define FuncEvent		0xf0
+#define FuncEventMask		0xf4
+#define FuncPresetState		0xf8
+#define FuncForceEvent		0xfc
+
+/* write MMIO register */
+#define RTL_W8(priv, reg, val)	writeb(val, ((char *)(priv->base) + reg))
+#define RTL_W16(priv, reg, val)	writew(val, ((char *)(priv->base) + reg))
+#define RTL_W32(priv, reg, val)	writel(val, ((char *)(priv->base) + reg))
+
+/* read MMIO register */
+#define RTL_R8(priv, reg)	readb(((char *)(priv->base) + reg))
+#define RTL_R16(priv, reg)	readw(((char *)(priv->base) + reg))
+#define RTL_R32(priv, reg)	readl(((char *)(priv->base) + reg))
+
+static const u32 rtl8169_rx_config =
+		 (7 << RxCfgFIFOShift) | (6 << RxCfgDMAShift);
+
+static void rtl8169_chip_reset(struct rtl8169_priv *priv)
+{
+	int i;
+
+	/* Soft reset the chip. */
+	RTL_W8(priv, ChipCmd, CmdReset);
+
+	/* Check that the chip has finished the reset. */
+	for (i = 1000; i > 0; i--) {
+		if ((RTL_R8(priv, ChipCmd) & CmdReset) == 0)
+			break;
+		udelay(10);
+	}
+}
+
+static struct rtl8169_chip_info chip_info[] = {
+	{"RTL-8169",		0x00,	0xff7e1880},
+	{"RTL-8169",		0x04,	0xff7e1880},
+	{"RTL-8169",		0x00,	0xff7e1880},
+	{"RTL-8169s/8110s",	0x02,	0xff7e1880},
+	{"RTL-8169s/8110s",	0x04,	0xff7e1880},
+	{"RTL-8169sb/8110sb",	0x10,	0xff7e1880},
+	{"RTL-8169sc/8110sc",	0x18,	0xff7e1880},
+	{"RTL-8168b/8111sb",	0x30,	0xff7e1880},
+	{"RTL-8168b/8111sb",	0x38,	0xff7e1880},
+	{"RTL-8168d/8111d",	0x28,	0xff7e1880},
+	{"RTL-8168evl/8111evl",	0x2e,	0xff7e1880},
+	{"RTL-8101e",		0x34,	0xff7e1880},
+	{"RTL-8100e",		0x32,	0xff7e1880},
+};
+
+static void rtl8169_chip_identify(struct rtl8169_priv *priv)
+{
+	u32 val;
+	int i;
+
+	val = RTL_R32(priv, TxConfig);
+	val = ((val & 0x7c000000) + ((val & 0x00800000) << 2)) >> 24;
+
+	for (i = ARRAY_SIZE(chip_info) - 1; i >= 0; i--){
+		if (val == chip_info[i].version) {
+			priv->chipset = i;
+			dev_dbg(&priv->pci_dev->dev, "found %s chipset\n",
+				chip_info[i].name);
+			return;
+		}
+	}
+
+	dev_dbg(&priv->pci_dev->dev,
+		"no matching chip version found, assuming RTL-8169\n");
+	priv->chipset = 0;
+}
+
+static int rtl8169_init_dev(struct eth_device *edev)
+{
+	struct rtl8169_priv *priv = edev->priv;
+
+	rtl8169_chip_reset(priv);
+	rtl8169_chip_identify(priv);
+	pci_set_master(priv->pci_dev);
+
+	return 0;
+}
+
+static void __set_rx_mode(struct rtl8169_priv *priv)
+{
+	u32 mc_filter[2], val;
+
+	/* IFF_ALLMULTI */
+	/* Too many to filter perfectly -- accept all multicasts. */
+	mc_filter[1] = mc_filter[0] = 0xffffffff;
+
+	val = AcceptBroadcast | AcceptMulticast | AcceptMyPhys |
+	      rtl8169_rx_config | (RTL_R32(priv, RxConfig) &
+				  chip_info[priv->chipset].RxConfigMask);
+
+	RTL_W32(priv, RxConfig, val);
+	RTL_W32(priv, MAR0 + 0, mc_filter[0]);
+	RTL_W32(priv, MAR0 + 4, mc_filter[1]);
+}
+
+static void rtl8169_init_ring(struct rtl8169_priv *priv)
+{
+	int i;
+
+	priv->cur_rx = priv->cur_tx = 0;
+
+	priv->tx_desc = dma_alloc_coherent(NUM_TX_DESC *
+				sizeof(struct bufdesc));
+	priv->tx_buf = malloc(NUM_TX_DESC * PKT_BUF_SIZE);
+	priv->rx_desc = dma_alloc_coherent(NUM_RX_DESC *
+				sizeof(struct bufdesc));
+	priv->rx_buf = malloc(NUM_RX_DESC * PKT_BUF_SIZE);
+
+	memset(priv->tx_desc, 0, NUM_TX_DESC * sizeof(struct bufdesc));
+	memset(priv->rx_desc, 0, NUM_RX_DESC * sizeof(struct bufdesc));
+
+	for (i = 0; i < NUM_RX_DESC; i++) {
+		if (i == (NUM_RX_DESC - 1))
+			priv->rx_desc[i].status =
+				BD_STAT_OWN | BD_STAT_EOR | PKT_BUF_SIZE;
+		else
+			priv->rx_desc[i].status =
+				BD_STAT_OWN | PKT_BUF_SIZE;
+
+		priv->rx_desc[i].buf_addr =
+				virt_to_phys(priv->rx_buf + i * PKT_BUF_SIZE);
+	}
+
+	dma_flush_range((unsigned long)priv->rx_desc,
+			(unsigned long)priv->rx_desc +
+			NUM_RX_DESC * sizeof(struct bufdesc));
+}
+
+static void rtl8169_hw_start(struct rtl8169_priv *priv)
+{
+	u32 val;
+
+	RTL_W8(priv, Cfg9346, Cfg9346_Unlock);
+
+	/* RTL-8169sb/8110sb or previous version */
+	if (priv->chipset <= 5)
+		RTL_W8(priv, ChipCmd, CmdTxEnb | CmdRxEnb);
+
+	RTL_W8(priv, EarlyTxThres, 0x3f);
+
+	/* For gigabit rtl8169 */
+	RTL_W16(priv, RxMaxSize, 0x800);
+
+	/* Set Rx Config register */
+	val = rtl8169_rx_config | (RTL_R32(priv, RxConfig) &
+	      chip_info[priv->chipset].RxConfigMask);
+	RTL_W32(priv, RxConfig, val);
+
+	/* Set DMA burst size and Interframe Gap Time */
+	RTL_W32(priv, TxConfig, (6 << TxDMAShift) | (3 << TxInterFrameGapShift));
+
+	RTL_W32(priv, TxDescStartAddrLow, virt_to_phys(priv->tx_desc));
+	RTL_W32(priv, TxDescStartAddrHigh, 0);
+	RTL_W32(priv, RxDescStartAddrLow, virt_to_phys(priv->rx_desc));
+	RTL_W32(priv, RxDescStartAddrHigh, 0);
+
+	/* RTL-8169sc/8110sc or later version */
+	if (priv->chipset > 5)
+		RTL_W8(priv, ChipCmd, CmdTxEnb | CmdRxEnb);
+
+	RTL_W8(priv, Cfg9346, Cfg9346_Lock);
+	udelay(10);
+
+	RTL_W32(priv, RxMissed, 0);
+
+	__set_rx_mode(priv);
+
+	/* no early-rx interrupts */
+	RTL_W16(priv, MultiIntr, RTL_R16(priv, MultiIntr) & 0xf000);
+}
+
+static int rtl8169_eth_open(struct eth_device *edev)
+{
+	struct rtl8169_priv *priv = edev->priv;
+	int ret;
+
+	rtl8169_init_ring(priv);
+	rtl8169_hw_start(priv);
+
+	ret = phy_device_connect(edev, &priv->miibus, 0, NULL, 0,
+				 PHY_INTERFACE_MODE_NA);
+
+	return ret;
+}
+
+static int rtl8169_phy_write(struct mii_bus *bus, int phy_addr,
+	int reg, u16 val)
+{
+	struct rtl8169_priv *priv = bus->priv;
+	int i;
+
+	if (phy_addr != 0)
+		return -1;
+
+	RTL_W32(priv, PHYAR, 0x80000000 | (reg & 0xff) << 16 | val);
+	mdelay(1);
+
+	for (i = 2000; i > 0; i--) {
+		if (!(RTL_R32(priv, PHYAR) & 0x80000000)) {
+			return 0;
+		} else {
+			udelay(100);
+		}
+	}
+
+	return -1;
+}
+
+static int rtl8169_phy_read(struct mii_bus *bus, int phy_addr, int reg)
+{
+	struct rtl8169_priv *priv = bus->priv;
+	int i, val = 0xffff;
+
+	RTL_W32(priv, PHYAR, 0x0 | (reg & 0xff) << 16);
+	mdelay(10);
+
+	if (phy_addr != 0)
+		return val;
+
+	for (i = 2000; i > 0; i--) {
+		if (RTL_R32(priv, PHYAR) & 0x80000000) {
+			val = (int) (RTL_R32(priv, PHYAR) & 0xffff);
+			break;
+		} else {
+			udelay(100);
+		}
+	}
+	return val;
+}
+
+static int rtl8169_eth_send(struct eth_device *edev, void *packet,
+				int packet_length)
+{
+	struct rtl8169_priv *priv = edev->priv;
+	unsigned int entry;
+
+	entry = priv->cur_tx % NUM_TX_DESC;
+
+	if (packet_length < ETH_ZLEN)
+		memset(priv->tx_buf + entry * PKT_BUF_SIZE, 0, ETH_ZLEN);
+	memcpy(priv->tx_buf + entry * PKT_BUF_SIZE, packet, packet_length);
+	dma_flush_range((unsigned long)priv->tx_buf + entry * PKT_BUF_SIZE,
+			(unsigned long)priv->tx_buf + (entry + 1) * PKT_BUF_SIZE);
+
+	priv->tx_desc[entry].buf_Haddr = 0;
+	priv->tx_desc[entry].buf_addr =
+		virt_to_phys(priv->tx_buf + entry * PKT_BUF_SIZE);
+
+	if (entry != (NUM_TX_DESC - 1)) {
+		priv->tx_desc[entry].status =
+			BD_STAT_OWN | BD_STAT_FS | BD_STAT_LS |
+			((packet_length > ETH_ZLEN) ? packet_length : ETH_ZLEN);
+	} else {
+		priv->tx_desc[entry].status =
+			BD_STAT_OWN | BD_STAT_EOR | BD_STAT_FS | BD_STAT_LS |
+			((packet_length > ETH_ZLEN) ? packet_length : ETH_ZLEN);
+	}
+
+	dma_flush_range((unsigned long)&priv->tx_desc[entry],
+			(unsigned long)&priv->tx_desc[entry + 1]);
+
+	RTL_W8(priv, TxPoll, 0x40);
+	do {
+		dma_inv_range((unsigned long)&priv->tx_desc[entry],
+		              (unsigned long)&priv->tx_desc[entry + 1]);
+	} while (priv->tx_desc[entry].status & BD_STAT_OWN);
+
+	priv->cur_tx++;
+
+	return 0;
+}
+
+static int rtl8169_eth_rx(struct eth_device *edev)
+{
+	struct rtl8169_priv *priv = edev->priv;
+	unsigned int entry, pkt_size = 0;
+	u8 status;
+
+	entry = priv->cur_rx % NUM_RX_DESC;
+
+	dma_inv_range((unsigned long)&priv->rx_desc[entry],
+	              (unsigned long)&priv->rx_desc[entry + 1]);
+
+	if ((priv->rx_desc[entry].status & BD_STAT_OWN) == 0) {
+		if (!(priv->rx_desc[entry].status & BD_STAT_RX_RES)) {
+			pkt_size = (priv->rx_desc[entry].status & 0x1fff) - 4;
+
+			dma_inv_range((unsigned long)priv->rx_buf
+			               + entry * PKT_BUF_SIZE,
+			              (unsigned long)priv->rx_buf
+			               + entry * PKT_BUF_SIZE + pkt_size);
+
+			net_receive(edev, priv->rx_buf + entry * PKT_BUF_SIZE,
+			            pkt_size);
+
+			if (entry == NUM_RX_DESC - 1)
+				priv->rx_desc[entry].status = BD_STAT_OWN |
+					BD_STAT_EOR | PKT_BUF_SIZE;
+			else
+				priv->rx_desc[entry].status =
+					BD_STAT_OWN | PKT_BUF_SIZE;
+			priv->rx_desc[entry].buf_addr =
+				virt_to_phys(priv->rx_buf +
+				             entry * PKT_BUF_SIZE);
+
+			dma_flush_range((unsigned long)&priv->rx_desc[entry],
+			                (unsigned long)&priv->rx_desc[entry + 1]);
+		} else {
+			dev_err(&edev->dev, "rx error\n");
+		}
+
+		priv->cur_rx++;
+
+		return pkt_size;
+
+	} else {
+		status = RTL_R8(priv, IntrStatus);
+		RTL_W8(priv, IntrStatus, status & ~(TxErr | RxErr | SYSErr));
+		udelay(100);	/* wait */
+	}
+
+	return 0;
+}
+
+static int rtl8169_get_ethaddr(struct eth_device *edev, unsigned char *m)
+{
+	struct rtl8169_priv *priv = edev->priv;
+	int i;
+
+	for (i = 0; i < 6; i++) {
+		m[i] = RTL_R8(priv, MAC0 + i);
+	}
+
+	return 0;
+}
+
+static int rtl8169_set_ethaddr(struct eth_device *edev, unsigned char *mac_addr)
+{
+	struct rtl8169_priv *priv = edev->priv;
+	int i;
+
+	RTL_W8(priv, Cfg9346, Cfg9346_Unlock);
+
+	for (i = 0; i < 6; i++) {
+		RTL_W8(priv, (MAC0 + i), mac_addr[i]);
+		RTL_R8(priv, mac_addr[i]);
+	}
+
+	RTL_W8(priv, Cfg9346, Cfg9346_Lock);
+
+	return 0;
+}
+
+static void rtl8169_eth_halt(struct eth_device *edev)
+{
+	struct rtl8169_priv *priv = edev->priv;
+
+	/* Stop the chip's Tx and Rx DMA processes. */
+	RTL_W8(priv, ChipCmd, 0x00);
+
+	/* Disable interrupts by clearing the interrupt mask. */
+	RTL_W16(priv, IntrMask, 0x0000);
+	RTL_W32(priv, RxMissed, 0);
+
+	pci_clear_master(priv->pci_dev);
+}
+
+static int rtl8169_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	struct device_d *dev = &pdev->dev;
+	struct eth_device *edev;
+	struct rtl8169_priv *priv;
+	int ret;
+
+	/* enable pci device */
+	pci_enable_device(pdev);
+
+	priv = xzalloc(sizeof(struct rtl8169_priv));
+
+	edev = &priv->edev;
+	dev->type_data = edev;
+	edev->priv = priv;
+
+	priv->pci_dev = pdev;
+
+	priv->miibus.read = rtl8169_phy_read;
+	priv->miibus.write = rtl8169_phy_write;
+	priv->miibus.priv = priv;
+	priv->miibus.parent = &edev->dev;
+
+	priv->base = pci_iomap(pdev, pdev->device == 0x8168 ? 2 : 1);
+
+	dev_dbg(dev, "rtl%04x (rev %02x) (base=%p)\n",
+		 pdev->device, pdev->revision, priv->base);
+
+	edev->init = rtl8169_init_dev;
+	edev->open = rtl8169_eth_open;
+	edev->send = rtl8169_eth_send;
+	edev->recv = rtl8169_eth_rx;
+	edev->get_ethaddr = rtl8169_get_ethaddr;
+	edev->set_ethaddr = rtl8169_set_ethaddr;
+	edev->halt = rtl8169_eth_halt;
+	edev->parent = dev;
+	ret = eth_register(edev);
+	if (ret)
+		goto eth_err;
+
+	ret = mdiobus_register(&priv->miibus);
+	if (ret)
+		goto mdio_err;
+
+	return 0;
+
+mdio_err:
+	eth_unregister(edev);
+
+eth_err:
+	free(priv);
+
+	return ret;
+}
+static DEFINE_PCI_DEVICE_TABLE(rtl8169_pci_tbl) = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8167), },
+	{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8168), },
+	{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8169), },
+	{ /* sentinel */ }
+};
+
+static struct pci_driver rtl8169_eth_driver = {
+	.name = "rtl8169_eth",
+	.id_table = rtl8169_pci_tbl,
+	.probe = rtl8169_probe,
+};
+
+static int rtl8169_init(void)
+{
+	return pci_register_driver(&rtl8169_eth_driver);
+}
+device_initcall(rtl8169_init);
-- 
1.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v2 19/19] ARM: tegra: enable network related options in defconfig
  2014-10-04 17:40 [PATCH v2 00/19] PCI and Tegra series revamp Lucas Stach
                   ` (17 preceding siblings ...)
  2014-10-04 17:40 ` [PATCH v2 18/19] net: add rtl8169 driver Lucas Stach
@ 2014-10-04 17:40 ` Lucas Stach
  2014-10-08  6:39 ` [PATCH v2 00/19] PCI and Tegra series revamp Sascha Hauer
  19 siblings, 0 replies; 21+ messages in thread
From: Lucas Stach @ 2014-10-04 17:40 UTC (permalink / raw)
  To: barebox

Now that we have working network support on the Beaver board
it makes sense to enable some network options.

Signed-off-by: Lucas Stach <dev@lynxeye.de>
---
 arch/arm/configs/tegra_v7_defconfig | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/arch/arm/configs/tegra_v7_defconfig b/arch/arm/configs/tegra_v7_defconfig
index 8f01b54..ead69ed 100644
--- a/arch/arm/configs/tegra_v7_defconfig
+++ b/arch/arm/configs/tegra_v7_defconfig
@@ -27,17 +27,24 @@ CONFIG_CMD_RESET=y
 CONFIG_CMD_EXPORT=y
 CONFIG_CMD_LOADENV=y
 CONFIG_CMD_SAVEENV=y
+CONFIG_CMD_DHCP=y
+CONFIG_CMD_MIITOOL=y
+CONFIG_CMD_PING=y
 CONFIG_CMD_EDIT=y
 CONFIG_CMD_TIMEOUT=y
 CONFIG_CMD_CLK=y
 CONFIG_CMD_DETECT=y
 CONFIG_CMD_GPIO=y
 CONFIG_CMD_OFTREE=y
+CONFIG_NET=y
 CONFIG_DRIVER_SERIAL_NS16550=y
+CONFIG_DRIVER_NET_RTL8169=y
 CONFIG_MCI=y
 CONFIG_MCI_MMC_BOOT_PARTITIONS=y
 CONFIG_MCI_TEGRA=y
 CONFIG_PCI_TEGRA=y
 CONFIG_FS_EXT4=y
+CONFIG_FS_TFTP=y
+CONFIG_FS_NFS=y
 CONFIG_FS_FAT=y
 CONFIG_FS_FAT_LFN=y
-- 
1.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 00/19] PCI and Tegra series revamp
  2014-10-04 17:40 [PATCH v2 00/19] PCI and Tegra series revamp Lucas Stach
                   ` (18 preceding siblings ...)
  2014-10-04 17:40 ` [PATCH v2 19/19] ARM: tegra: enable network related options in defconfig Lucas Stach
@ 2014-10-08  6:39 ` Sascha Hauer
  19 siblings, 0 replies; 21+ messages in thread
From: Sascha Hauer @ 2014-10-08  6:39 UTC (permalink / raw)
  To: Lucas Stach; +Cc: barebox

On Sat, Oct 04, 2014 at 07:40:06PM +0200, Lucas Stach wrote:
> Ok, I got around to work a bit more on this. I now
> actually finished the rtl8169 network driver, so
> this series yields working PCI attached ethernet
> on Tegra30 Beaver.
> 
> For Jetson K1 some work is left to setup the PHYs, but
> that shouldn't be far out. 
> 
> While working on this I stumbled upon a few more issues,
> so I decided to just roll the fixes into this series
> and resend.
> 
> Lucas Stach (19):
>   MIPS: malta: fix pci IO resource assignment
>   pci: split out device init
>   pci: add resource enum
>   pci: properly populate prefetchable BARs
>   pci: setup bridges and traverse buses behind them
>   pci: defer device registration until after bridge setup
>   pci: prettyprint device names
>   pci: track parent<->child relationship
>   commands: lspci: go down into subordinate busses
>   clk: tegra: add PLLE setup functions
>   clk: tegra30: add PCIe clocks
>   i2c: tegra: move to fs initcall
>   ARM: tegra: beaver: enable PEX voltage rail
>   tegra: pmc: add powerdomain handling
>   of: import pci range parser from linux
>   pci: add Tegra host controller driver
>   ARM: tegra: advertise PCI support
>   net: add rtl8169 driver
>   ARM: tegra: enable network related options in defconfig

Applied, thanks

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2014-10-08  6:39 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-10-04 17:40 [PATCH v2 00/19] PCI and Tegra series revamp Lucas Stach
2014-10-04 17:40 ` [PATCH v2 01/19] MIPS: malta: fix pci IO resource assignment Lucas Stach
2014-10-04 17:40 ` [PATCH v2 02/19] pci: split out device init Lucas Stach
2014-10-04 17:40 ` [PATCH v2 03/19] pci: add resource enum Lucas Stach
2014-10-04 17:40 ` [PATCH v2 04/19] pci: properly populate prefetchable BARs Lucas Stach
2014-10-04 17:40 ` [PATCH v2 05/19] pci: setup bridges and traverse buses behind them Lucas Stach
2014-10-04 17:40 ` [PATCH v2 06/19] pci: defer device registration until after bridge setup Lucas Stach
2014-10-04 17:40 ` [PATCH v2 07/19] pci: prettyprint device names Lucas Stach
2014-10-04 17:40 ` [PATCH v2 08/19] pci: track parent<->child relationship Lucas Stach
2014-10-04 17:40 ` [PATCH v2 09/19] commands: lspci: go down into subordinate busses Lucas Stach
2014-10-04 17:40 ` [PATCH v2 10/19] clk: tegra: add PLLE setup functions Lucas Stach
2014-10-04 17:40 ` [PATCH v2 11/19] clk: tegra30: add PCIe clocks Lucas Stach
2014-10-04 17:40 ` [PATCH v2 12/19] i2c: tegra: move to fs initcall Lucas Stach
2014-10-04 17:40 ` [PATCH v2 13/19] ARM: tegra: beaver: enable PEX voltage rail Lucas Stach
2014-10-04 17:40 ` [PATCH v2 14/19] tegra: pmc: add powerdomain handling Lucas Stach
2014-10-04 17:40 ` [PATCH v2 15/19] of: import pci range parser from linux Lucas Stach
2014-10-04 17:40 ` [PATCH v2 16/19] pci: add Tegra host controller driver Lucas Stach
2014-10-04 17:40 ` [PATCH v2 17/19] ARM: tegra: advertise PCI support Lucas Stach
2014-10-04 17:40 ` [PATCH v2 18/19] net: add rtl8169 driver Lucas Stach
2014-10-04 17:40 ` [PATCH v2 19/19] ARM: tegra: enable network related options in defconfig Lucas Stach
2014-10-08  6:39 ` [PATCH v2 00/19] PCI and Tegra series revamp Sascha Hauer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox