summaryrefslogtreecommitdiffstats
path: root/drivers/pci/host/pci-thunder-pem.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/host/pci-thunder-pem.c')
-rw-r--r--drivers/pci/host/pci-thunder-pem.c83
1 files changed, 75 insertions, 8 deletions
diff --git a/drivers/pci/host/pci-thunder-pem.c b/drivers/pci/host/pci-thunder-pem.c
index af722eb0ca75..b89c373555c5 100644
--- a/drivers/pci/host/pci-thunder-pem.c
+++ b/drivers/pci/host/pci-thunder-pem.c
@@ -14,6 +14,7 @@
* Copyright (C) 2015 - 2016 Cavium, Inc.
*/
+#include <linux/bitfield.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of_address.h>
@@ -36,7 +37,7 @@ struct thunder_pem_pci {
static int thunder_pem_bridge_read(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *val)
{
- u64 read_val;
+ u64 read_val, tmp_val;
struct pci_config_window *cfg = bus->sysdata;
struct thunder_pem_pci *pem_pci = (struct thunder_pem_pci *)cfg->priv;
@@ -65,13 +66,28 @@ static int thunder_pem_bridge_read(struct pci_bus *bus, unsigned int devfn,
read_val |= 0x00007000; /* Skip MSI CAP */
break;
case 0x70: /* Express Cap */
- /* PME interrupt on vector 2*/
- read_val |= (2u << 25);
+ /*
+ * Change PME interrupt to vector 2 on T88 where it
+ * reads as 0, else leave it alone.
+ */
+ if (!(read_val & (0x1f << 25)))
+ read_val |= (2u << 25);
break;
case 0xb0: /* MSI-X Cap */
- /* TableSize=4, Next Cap is EA */
+ /* TableSize=2 or 4, Next Cap is EA */
read_val &= 0xc00000ff;
- read_val |= 0x0003bc00;
+ /*
+ * If Express Cap(0x70) raw PME vector reads as 0 we are on
+ * T88 and TableSize is reported as 4, else TableSize
+ * is 2.
+ */
+ writeq(0x70, pem_pci->pem_reg_base + PEM_CFG_RD);
+ tmp_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD);
+ tmp_val >>= 32;
+ if (!(tmp_val & (0x1f << 25)))
+ read_val |= 0x0003bc00;
+ else
+ read_val |= 0x0001bc00;
break;
case 0xb4:
/* Table offset=0, BIR=0 */
@@ -319,6 +335,50 @@ static int thunder_pem_init(struct device *dev, struct pci_config_window *cfg,
#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
+#define PEM_RES_BASE 0x87e0c0000000UL
+#define PEM_NODE_MASK GENMASK(45, 44)
+#define PEM_INDX_MASK GENMASK(26, 24)
+#define PEM_MIN_DOM_IN_NODE 4
+#define PEM_MAX_DOM_IN_NODE 10
+
+static void thunder_pem_reserve_range(struct device *dev, int seg,
+ struct resource *r)
+{
+ resource_size_t start = r->start, end = r->end;
+ struct resource *res;
+ const char *regionid;
+
+ regionid = kasprintf(GFP_KERNEL, "PEM RC:%d", seg);
+ if (!regionid)
+ return;
+
+ res = request_mem_region(start, end - start + 1, regionid);
+ if (res)
+ res->flags &= ~IORESOURCE_BUSY;
+ else
+ kfree(regionid);
+
+ dev_info(dev, "%pR %s reserved\n", r,
+ res ? "has been" : "could not be");
+}
+
+static void thunder_pem_legacy_fw(struct acpi_pci_root *root,
+ struct resource *res_pem)
+{
+ int node = acpi_get_node(root->device->handle);
+ int index;
+
+ if (node == NUMA_NO_NODE)
+ node = 0;
+
+ index = root->segment - PEM_MIN_DOM_IN_NODE;
+ index -= node * PEM_MAX_DOM_IN_NODE;
+ res_pem->start = PEM_RES_BASE | FIELD_PREP(PEM_NODE_MASK, node) |
+ FIELD_PREP(PEM_INDX_MASK, index);
+ res_pem->end = res_pem->start + SZ_16M - 1;
+ res_pem->flags = IORESOURCE_MEM;
+}
+
static int thunder_pem_acpi_init(struct pci_config_window *cfg)
{
struct device *dev = cfg->parent;
@@ -331,10 +391,17 @@ static int thunder_pem_acpi_init(struct pci_config_window *cfg)
if (!res_pem)
return -ENOMEM;
- ret = acpi_get_rc_resources(dev, "THRX0002", root->segment, res_pem);
+ ret = acpi_get_rc_resources(dev, "CAVA02B", root->segment, res_pem);
+
+ /*
+ * If we fail to gather resources it means that we run with old
+ * FW where we need to calculate PEM-specific resources manually.
+ */
if (ret) {
- dev_err(dev, "can't get rc base address\n");
- return ret;
+ thunder_pem_legacy_fw(root, res_pem);
+ /* Reserve PEM-specific resources and PCI configuration space */
+ thunder_pem_reserve_range(dev, root->segment, res_pem);
+ thunder_pem_reserve_range(dev, root->segment, &cfg->res);
}
return thunder_pem_init(dev, cfg, res_pem);