Skip to content

Commit

Permalink
PCI: thunder-pem: Add legacy firmware support for Cavium ThunderX hos…
Browse files Browse the repository at this point in the history
…t controller

During early days of PCI quirks support, ThunderX firmware did not provide
PNP0c02 node with PCI configuration space and PEM-specific register ranges.
This means that for legacy FW we are not reserving these resources and
cannot gather PEM-specific resources for further PEM initialization.

To support already deployed legacy FW, calculate PEM-specific ranges and
provide resources reservation as fallback scenario into PEM driver when we
could not gather PEM reg base from ACPI tables.

Tested-by: Robert Richter <rrichter@cavium.com>
Signed-off-by: Tomasz Nowicki <tn@semihalf.com>
Signed-off-by: Vadim Lomovtsev <Vadim.Lomovtsev@caviumnetworks.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Robert Richter <rrichter@cavium.com>
CC: stable@vger.kernel.org	# v4.10+
  • Loading branch information
Tomasz Nowicki authored and Bjorn Helgaas committed Mar 23, 2017
1 parent 81caa91 commit 9abb27c
Showing 1 changed file with 54 additions and 2 deletions.
56 changes: 54 additions & 2 deletions drivers/pci/host/pci-thunder-pem.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -334,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;
Expand All @@ -347,9 +392,16 @@ static int thunder_pem_acpi_init(struct pci_config_window *cfg)
return -ENOMEM;

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);
Expand Down

0 comments on commit 9abb27c

Please sign in to comment.