* [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 |= (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