[PATCH] pci: quirk for pci bridge behind hypertransport link with 32bits addressing.

From: JÃrÃme Glisse
Date: Sun Apr 27 2014 - 15:48:57 EST


It seems some PCI or PCIE bridge are behind HyperTransport that do not
support 64bits addressing but only 32bit (even though the HyperTransport
specification state that it should 40bit at least). This patch add a
quirk that walk the parent chain of a device before setting the dma mask
so that any dma allocation/mapping will fit inside the limit of any
HyperTransport link that might be in front of the PCI/PCIE root complex.

Signed-off-by: Jéme Glisse <jglisse@xxxxxxxxxx>
---
drivers/pci/quirks.c | 36 ++++++++++++++++++++++++++++++++++++
include/asm-generic/pci-dma-compat.h | 26 ++++++++++++++++++++++++++
2 files changed, 62 insertions(+)

diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index e729206..373ae05 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -2447,6 +2447,42 @@ out:
pci_dev_put(host_bridge);
}

+#define PCI_HT_LCTR_64B 0x8000 /* 64-bit Addressing Enable */
+
+u64 pci_ht_quirk_dma_32bit_only(struct pci_dev *dev, u64 mask)
+{
+ struct pci_bus *bus = dev->bus;
+
+ do {
+ struct pci_dev *bridge = bus->self;
+ int pos;
+
+ pos = pci_find_ht_capability(bridge, HT_CAPTYPE_SLAVE);
+ if (pos) {
+ int ctrl_off;
+ u16 flags, ctrl;
+
+ /* See hypertransport specification about master host
+ * (section 7.5.3.2 in HTC200393).
+ */
+ pci_read_config_word(dev, pos + PCI_CAP_FLAGS, &flags);
+ ctrl_off = ((flags >> 10) & 1) ?
+ PCI_HT_CAP_SLAVE_CTRL0 : PCI_HT_CAP_SLAVE_CTRL1;
+ pci_read_config_word(dev, pos + ctrl_off, &ctrl);
+ if (!(ctrl & PCI_HT_LCTR_64B)) {
+ /* So 32bits only. Maybe there is one more bug
+ * as HyperTransport specification says that it
+ * should be 40bits.
+ */
+ return 0xffffffff;
+ }
+ }
+ bus = bus->parent;
+ } while (bus);
+ return mask;
+}
+EXPORT_SYMBOL(pci_ht_quirk_dma_32bit_only);
+
static void ht_disable_msi_mapping(struct pci_dev *dev)
{
int pos, ttl = 48;
diff --git a/include/asm-generic/pci-dma-compat.h b/include/asm-generic/pci-dma-compat.h
index 1437b7d..bae17bb 100644
--- a/include/asm-generic/pci-dma-compat.h
+++ b/include/asm-generic/pci-dma-compat.h
@@ -102,13 +102,39 @@ pci_dma_mapping_error(struct pci_dev *pdev, dma_addr_t dma_addr)
}

#ifdef CONFIG_PCI
+
+#ifdef CONFIG_PCI_QUIRKS
+u64 pci_ht_quirk_dma_32bit_only(struct pci_dev *dev, u64 mask);
+#else
+static inline u64 pci_ht_quirk_dma_32bit_only(struct pci_dev *dev, u64 mask)
+{
+ return mask;
+}
+#endif
+
static inline int pci_set_dma_mask(struct pci_dev *dev, u64 mask)
{
+ /* We are living in a monstruous world in which you can have the pci
+ * root complex behind an hypertransport link which can not address
+ * anything above 32bit (well hypertransport specification says 40bits
+ * but hardware such as SIS761 only support 32bits).
+ *
+ * So if a device set a mask bigger than 32bit walk the chain of its
+ * parent to see if any is behind a transportlink and if so check that
+ * the transport link support 64bits.
+ */
+ if (mask & (1ULL << 32ULL)) {
+ mask = pci_ht_quirk_dma_32bit_only(dev, mask);
+ }
return dma_set_mask(&dev->dev, mask);
}

static inline int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask)
{
+ /* See comment in pci_set_dma_mask */
+ if (mask & (1ULL << 32ULL)) {
+ mask = pci_ht_quirk_dma_32bit_only(dev, mask);
+ }
return dma_set_coherent_mask(&dev->dev, mask);
}
#endif
--
1.9.0
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/