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