Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9abb27c7 authored by Tomasz Nowicki's avatar Tomasz Nowicki Committed by Bjorn Helgaas
Browse files

PCI: thunder-pem: Add legacy firmware support for Cavium ThunderX host 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: default avatarRobert Richter <rrichter@cavium.com>
Signed-off-by: default avatarTomasz Nowicki <tn@semihalf.com>
Signed-off-by: default avatarVadim Lomovtsev <Vadim.Lomovtsev@caviumnetworks.com>
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
Acked-by: default avatarRobert Richter <rrichter@cavium.com>
CC: stable@vger.kernel.org	# v4.10+
parent 81caa91b
Loading
Loading
Loading
Loading
+54 −2
Original line number Diff line number Diff line
@@ -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>
@@ -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;
@@ -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);