summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2018-08-13 16:56:04 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2018-08-13 16:56:04 +0200
commit64842a68070318c4c78e99bd69de2401e65de7a5 (patch)
tree316d943b62aed976d56d23296db6a98a88e3d914
parentc1ecf62939df7256f6fc77568cb7953d71746c55 (diff)
parentf345734707954466350b9d0f8e478ed06951a402 (diff)
downloadbarebox-64842a68070318c4c78e99bd69de2401e65de7a5.tar.gz
barebox-64842a68070318c4c78e99bd69de2401e65de7a5.tar.xz
Merge branch 'for-next/pci'
-rw-r--r--arch/arm/boards/gateworks-ventana/board.c29
-rw-r--r--arch/arm/lib32/barebox.lds.S12
-rw-r--r--drivers/pci/pci.c93
-rw-r--r--include/linux/pci.h54
4 files changed, 184 insertions, 4 deletions
diff --git a/arch/arm/boards/gateworks-ventana/board.c b/arch/arm/boards/gateworks-ventana/board.c
index 3ff142ee42..6f9e0343be 100644
--- a/arch/arm/boards/gateworks-ventana/board.c
+++ b/arch/arm/boards/gateworks-ventana/board.c
@@ -17,6 +17,7 @@
#include <i2c/i2c.h>
#include <init.h>
#include <linux/marvell_phy.h>
+#include <linux/pci.h>
#include <linux/phy.h>
#include <mach/bbu.h>
#include <mach/imx6.h>
@@ -111,3 +112,31 @@ static int gw54xx_coredevices_init(void)
return 0;
}
coredevice_initcall(gw54xx_coredevices_init);
+
+/*
+ * fixup for PLX PEX8909 bridge to configure GPIO1-7 as output High
+ * as they are used for slots1-7 PERST#
+ */
+static void ventana_pciesw_early_fixup(struct pci_dev *dev)
+{
+ u32 dw;
+
+ if (!of_machine_is_compatible("gw,ventana"))
+ return;
+
+ if (dev->devfn != 0)
+ return;
+
+ pci_read_config_dword(dev, 0x62c, &dw);
+ dw |= 0xaaa8; // GPIO1-7 outputs
+ pci_write_config_dword(dev, 0x62c, dw);
+
+ pci_read_config_dword(dev, 0x644, &dw);
+ dw |= 0xfe; // GPIO1-7 output high
+ pci_write_config_dword(dev, 0x644, dw);
+
+ mdelay(100);
+}
+DECLARE_PCI_FIXUP_EARLY(0x10b5, 0x8609, ventana_pciesw_early_fixup);
+DECLARE_PCI_FIXUP_EARLY(0x10b5, 0x8606, ventana_pciesw_early_fixup);
+DECLARE_PCI_FIXUP_EARLY(0x10b5, 0x8604, ventana_pciesw_early_fixup);
diff --git a/arch/arm/lib32/barebox.lds.S b/arch/arm/lib32/barebox.lds.S
index 594bf56837..7230e5f31f 100644
--- a/arch/arm/lib32/barebox.lds.S
+++ b/arch/arm/lib32/barebox.lds.S
@@ -106,6 +106,18 @@ SECTIONS
__usymtab : { BAREBOX_SYMS }
__usymtab_end = .;
+#ifdef CONFIG_PCI
+ __start_pci_fixups_early = .;
+ .pci_fixup_early : { KEEP(*(.pci_fixup_early)) }
+ __end_pci_fixups_early = .;
+ __start_pci_fixups_header = .;
+ .pci_fixup_header : { KEEP(*(.pci_fixup_header)) }
+ __end_pci_fixups_header = .;
+ __start_pci_fixups_enable = .;
+ .pci_fixup_enable : { KEEP(*(.pci_fixup_enable)) }
+ __end_pci_fixups_enable = .;
+#endif
+
.oftables : { BAREBOX_CLK_TABLE() }
.dtb : { BAREBOX_DTB() }
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index b2570eb151..d206c53848 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -253,6 +253,8 @@ static void setup_device(struct pci_dev *dev, int max_bar)
}
}
+ pci_fixup_device(pci_fixup_header, dev);
+
pci_write_config_byte(dev, PCI_COMMAND, cmd);
list_add_tail(&dev->bus_list, &dev->bus->devices);
}
@@ -331,6 +333,31 @@ static void postscan_setup_bridge(struct pci_dev *dev)
}
}
+static struct device_node *
+pci_of_match_device(struct device_d *parent, unsigned int devfn)
+{
+ struct device_node *np;
+ u32 reg;
+
+ if (!IS_ENABLED(CONFIG_OFTREE) || !parent->device_node)
+ return NULL;
+
+ for_each_child_of_node(parent->device_node, np) {
+ if (!of_property_read_u32_array(np, "reg", &reg, 1)) {
+ /*
+ * Only match device/function pair of the device
+ * address, other properties are defined by the
+ * PCI/OF node topology.
+ */
+ reg = (reg >> 8) & 0xffff;
+ if (reg == devfn)
+ return np;
+ }
+ }
+
+ return NULL;
+}
+
unsigned int pci_scan_bus(struct pci_bus *bus)
{
struct pci_dev *dev;
@@ -368,6 +395,11 @@ unsigned int pci_scan_bus(struct pci_bus *bus)
dev->vendor = l & 0xffff;
dev->device = (l >> 16) & 0xffff;
dev->dev.parent = bus->parent;
+ dev->dev.device_node = pci_of_match_device(bus->parent, devfn);
+ if (dev->dev.device_node)
+ pr_debug("found DT node %s for device %04x:%04x\n",
+ dev->dev.device_node->full_name,
+ dev->vendor, dev->device);
/* non-destructively determine if device can be a master: */
pci_read_config_byte(dev, PCI_COMMAND, &cmd);
@@ -382,6 +414,10 @@ unsigned int pci_scan_bus(struct pci_bus *bus)
class >>= 8;
dev->hdr_type = hdr_type;
+ pci_fixup_device(pci_fixup_early, dev);
+ /* the fixup may have changed the device class */
+ class = dev->class >> 8;
+
pr_debug("class = %08x, hdr_type = %08x\n", class, hdr_type);
pr_debug("%02x:%02x [%04x:%04x]\n", bus->number, dev->devfn,
dev->vendor, dev->device);
@@ -489,12 +525,61 @@ EXPORT_SYMBOL(pci_clear_master);
*/
int pci_enable_device(struct pci_dev *dev)
{
+ int ret;
u32 t;
pci_read_config_dword(dev, PCI_COMMAND, &t);
- return pci_write_config_dword(dev, PCI_COMMAND, t
- | PCI_COMMAND_IO
- | PCI_COMMAND_MEMORY
- );
+ ret = pci_write_config_dword(dev, PCI_COMMAND,
+ t | PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+ if (ret)
+ return ret;
+
+ pci_fixup_device(pci_fixup_enable, dev);
+
+ return 0;
}
EXPORT_SYMBOL(pci_enable_device);
+
+static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f,
+ struct pci_fixup *end)
+{
+ for (; f < end; f++)
+ if ((f->class == (u32) (dev->class >> f->class_shift) ||
+ f->class == (u32) PCI_ANY_ID) &&
+ (f->vendor == dev->vendor ||
+ f->vendor == (u16) PCI_ANY_ID) &&
+ (f->device == dev->device ||
+ f->device == (u16) PCI_ANY_ID)) {
+ f->hook(dev);
+ }
+}
+
+extern struct pci_fixup __start_pci_fixups_early[];
+extern struct pci_fixup __end_pci_fixups_early[];
+extern struct pci_fixup __start_pci_fixups_header[];
+extern struct pci_fixup __end_pci_fixups_header[];
+extern struct pci_fixup __start_pci_fixups_enable[];
+extern struct pci_fixup __end_pci_fixups_enable[];
+
+void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev)
+{
+ struct pci_fixup *start, *end;
+
+ switch (pass) {
+ case pci_fixup_early:
+ start = __start_pci_fixups_early;
+ end = __end_pci_fixups_early;
+ break;
+ case pci_fixup_header:
+ start = __start_pci_fixups_header;
+ end = __end_pci_fixups_header;
+ break;
+ case pci_fixup_enable:
+ start = __start_pci_fixups_enable;
+ end = __end_pci_fixups_enable;
+ break;
+ default:
+ unreachable();
+ }
+ pci_do_fixups(dev, start, end);
+}
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 152ba10a04..82f27f21b2 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -29,6 +29,7 @@
#include <linux/pci_ids.h>
+#define PCI_ANY_ID (~0)
/*
* The PCI interface treats multi-function devices as independent
@@ -299,4 +300,57 @@ int pci_enable_device(struct pci_dev *dev);
extern void __iomem *pci_iomap(struct pci_dev *dev, int bar);
+/*
+ * The world is not perfect and supplies us with broken PCI devices.
+ * For at least a part of these bugs we need a work-around, so both
+ * generic (drivers/pci/quirks.c) and per-architecture code can define
+ * fixup hooks to be called for particular buggy devices.
+ */
+
+struct pci_fixup {
+ u16 vendor; /* Or PCI_ANY_ID */
+ u16 device; /* Or PCI_ANY_ID */
+ u32 class; /* Or PCI_ANY_ID */
+ unsigned int class_shift; /* should be 0, 8, 16 */
+ void (*hook)(struct pci_dev *dev);
+};
+
+enum pci_fixup_pass {
+ pci_fixup_early, /* Before probing BARs */
+ pci_fixup_header, /* After reading configuration header */
+ pci_fixup_enable, /* pci_enable_device() time */
+};
+
+/* Anonymous variables would be nice... */
+#define DECLARE_PCI_FIXUP_SECTION(section, name, vendor, device, class, \
+ class_shift, hook) \
+ static const struct pci_fixup __PASTE(__pci_fixup_##name,__LINE__) __used \
+ __attribute__((__section__(#section), aligned((sizeof(void *))))) \
+ = { vendor, device, class, class_shift, hook };
+
+#define DECLARE_PCI_FIXUP_CLASS_EARLY(vendor, device, class, \
+ class_shift, hook) \
+ DECLARE_PCI_FIXUP_SECTION(.pci_fixup_early, \
+ hook, vendor, device, class, class_shift, hook)
+#define DECLARE_PCI_FIXUP_CLASS_HEADER(vendor, device, class, \
+ class_shift, hook) \
+ DECLARE_PCI_FIXUP_SECTION(.pci_fixup_header, \
+ hook, vendor, device, class, class_shift, hook)
+#define DECLARE_PCI_FIXUP_CLASS_ENABLE(vendor, device, class, \
+ class_shift, hook) \
+ DECLARE_PCI_FIXUP_SECTION(.pci_fixup_enable, \
+ hook, vendor, device, class, class_shift, hook)
+
+#define DECLARE_PCI_FIXUP_EARLY(vendor, device, hook) \
+ DECLARE_PCI_FIXUP_SECTION(.pci_fixup_early, \
+ hook, vendor, device, PCI_ANY_ID, 0, hook)
+#define DECLARE_PCI_FIXUP_HEADER(vendor, device, hook) \
+ DECLARE_PCI_FIXUP_SECTION(.pci_fixup_header, \
+ hook, vendor, device, PCI_ANY_ID, 0, hook)
+#define DECLARE_PCI_FIXUP_ENABLE(vendor, device, hook) \
+ DECLARE_PCI_FIXUP_SECTION(.pci_fixup_enable, \
+ hook, vendor, device, PCI_ANY_ID, 0, hook)
+
+void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev);
+
#endif /* LINUX_PCI_H */