162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2014 IBM Corp.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/pci.h>
762306a36Sopenharmony_ci#include <misc/cxl.h>
862306a36Sopenharmony_ci#include "cxl.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_cistatic int cxl_pci_probe_mode(struct pci_bus *bus)
1162306a36Sopenharmony_ci{
1262306a36Sopenharmony_ci	return PCI_PROBE_NORMAL;
1362306a36Sopenharmony_ci}
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistatic int cxl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	return -ENODEV;
1862306a36Sopenharmony_ci}
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic void cxl_teardown_msi_irqs(struct pci_dev *pdev)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	/*
2362306a36Sopenharmony_ci	 * MSI should never be set but need still need to provide this call
2462306a36Sopenharmony_ci	 * back.
2562306a36Sopenharmony_ci	 */
2662306a36Sopenharmony_ci}
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic bool cxl_pci_enable_device_hook(struct pci_dev *dev)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	struct pci_controller *phb;
3162306a36Sopenharmony_ci	struct cxl_afu *afu;
3262306a36Sopenharmony_ci	struct cxl_context *ctx;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	phb = pci_bus_to_host(dev->bus);
3562306a36Sopenharmony_ci	afu = (struct cxl_afu *)phb->private_data;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	if (!cxl_ops->link_ok(afu->adapter, afu)) {
3862306a36Sopenharmony_ci		dev_warn(&dev->dev, "%s: Device link is down, refusing to enable AFU\n", __func__);
3962306a36Sopenharmony_ci		return false;
4062306a36Sopenharmony_ci	}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	dev->dev.archdata.dma_offset = PAGE_OFFSET;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	/*
4562306a36Sopenharmony_ci	 * Allocate a context to do cxl things too.  If we eventually do real
4662306a36Sopenharmony_ci	 * DMA ops, we'll need a default context to attach them to
4762306a36Sopenharmony_ci	 */
4862306a36Sopenharmony_ci	ctx = cxl_dev_context_init(dev);
4962306a36Sopenharmony_ci	if (IS_ERR(ctx))
5062306a36Sopenharmony_ci		return false;
5162306a36Sopenharmony_ci	dev->dev.archdata.cxl_ctx = ctx;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	return (cxl_ops->afu_check_and_enable(afu) == 0);
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic void cxl_pci_disable_device(struct pci_dev *dev)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	struct cxl_context *ctx = cxl_get_context(dev);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	if (ctx) {
6162306a36Sopenharmony_ci		if (ctx->status == STARTED) {
6262306a36Sopenharmony_ci			dev_err(&dev->dev, "Default context started\n");
6362306a36Sopenharmony_ci			return;
6462306a36Sopenharmony_ci		}
6562306a36Sopenharmony_ci		dev->dev.archdata.cxl_ctx = NULL;
6662306a36Sopenharmony_ci		cxl_release_context(ctx);
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic void cxl_pci_reset_secondary_bus(struct pci_dev *dev)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	/* Should we do an AFU reset here ? */
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic int cxl_pcie_cfg_record(u8 bus, u8 devfn)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	return (bus << 8) + devfn;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic inline struct cxl_afu *pci_bus_to_afu(struct pci_bus *bus)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	struct pci_controller *phb = bus ? pci_bus_to_host(bus) : NULL;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	return phb ? phb->private_data : NULL;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic void cxl_afu_configured_put(struct cxl_afu *afu)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	atomic_dec_if_positive(&afu->configured_state);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic bool cxl_afu_configured_get(struct cxl_afu *afu)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	return atomic_inc_unless_negative(&afu->configured_state);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic inline int cxl_pcie_config_info(struct pci_bus *bus, unsigned int devfn,
9862306a36Sopenharmony_ci				       struct cxl_afu *afu, int *_record)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	int record;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	record = cxl_pcie_cfg_record(bus->number, devfn);
10362306a36Sopenharmony_ci	if (record > afu->crs_num)
10462306a36Sopenharmony_ci		return PCIBIOS_DEVICE_NOT_FOUND;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	*_record = record;
10762306a36Sopenharmony_ci	return 0;
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic int cxl_pcie_read_config(struct pci_bus *bus, unsigned int devfn,
11162306a36Sopenharmony_ci				int offset, int len, u32 *val)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	int rc, record;
11462306a36Sopenharmony_ci	struct cxl_afu *afu;
11562306a36Sopenharmony_ci	u8 val8;
11662306a36Sopenharmony_ci	u16 val16;
11762306a36Sopenharmony_ci	u32 val32;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	afu = pci_bus_to_afu(bus);
12062306a36Sopenharmony_ci	/* Grab a reader lock on afu. */
12162306a36Sopenharmony_ci	if (afu == NULL || !cxl_afu_configured_get(afu))
12262306a36Sopenharmony_ci		return PCIBIOS_DEVICE_NOT_FOUND;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	rc = cxl_pcie_config_info(bus, devfn, afu, &record);
12562306a36Sopenharmony_ci	if (rc)
12662306a36Sopenharmony_ci		goto out;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	switch (len) {
12962306a36Sopenharmony_ci	case 1:
13062306a36Sopenharmony_ci		rc = cxl_ops->afu_cr_read8(afu, record, offset,	&val8);
13162306a36Sopenharmony_ci		*val = val8;
13262306a36Sopenharmony_ci		break;
13362306a36Sopenharmony_ci	case 2:
13462306a36Sopenharmony_ci		rc = cxl_ops->afu_cr_read16(afu, record, offset, &val16);
13562306a36Sopenharmony_ci		*val = val16;
13662306a36Sopenharmony_ci		break;
13762306a36Sopenharmony_ci	case 4:
13862306a36Sopenharmony_ci		rc = cxl_ops->afu_cr_read32(afu, record, offset, &val32);
13962306a36Sopenharmony_ci		*val = val32;
14062306a36Sopenharmony_ci		break;
14162306a36Sopenharmony_ci	default:
14262306a36Sopenharmony_ci		WARN_ON(1);
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ciout:
14662306a36Sopenharmony_ci	cxl_afu_configured_put(afu);
14762306a36Sopenharmony_ci	return rc ? PCIBIOS_DEVICE_NOT_FOUND : 0;
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic int cxl_pcie_write_config(struct pci_bus *bus, unsigned int devfn,
15162306a36Sopenharmony_ci				 int offset, int len, u32 val)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	int rc, record;
15462306a36Sopenharmony_ci	struct cxl_afu *afu;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	afu = pci_bus_to_afu(bus);
15762306a36Sopenharmony_ci	/* Grab a reader lock on afu. */
15862306a36Sopenharmony_ci	if (afu == NULL || !cxl_afu_configured_get(afu))
15962306a36Sopenharmony_ci		return PCIBIOS_DEVICE_NOT_FOUND;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	rc = cxl_pcie_config_info(bus, devfn, afu, &record);
16262306a36Sopenharmony_ci	if (rc)
16362306a36Sopenharmony_ci		goto out;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	switch (len) {
16662306a36Sopenharmony_ci	case 1:
16762306a36Sopenharmony_ci		rc = cxl_ops->afu_cr_write8(afu, record, offset, val & 0xff);
16862306a36Sopenharmony_ci		break;
16962306a36Sopenharmony_ci	case 2:
17062306a36Sopenharmony_ci		rc = cxl_ops->afu_cr_write16(afu, record, offset, val & 0xffff);
17162306a36Sopenharmony_ci		break;
17262306a36Sopenharmony_ci	case 4:
17362306a36Sopenharmony_ci		rc = cxl_ops->afu_cr_write32(afu, record, offset, val);
17462306a36Sopenharmony_ci		break;
17562306a36Sopenharmony_ci	default:
17662306a36Sopenharmony_ci		WARN_ON(1);
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ciout:
18062306a36Sopenharmony_ci	cxl_afu_configured_put(afu);
18162306a36Sopenharmony_ci	return rc ? PCIBIOS_SET_FAILED : 0;
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic struct pci_ops cxl_pcie_pci_ops =
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	.read = cxl_pcie_read_config,
18762306a36Sopenharmony_ci	.write = cxl_pcie_write_config,
18862306a36Sopenharmony_ci};
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic struct pci_controller_ops cxl_pci_controller_ops =
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	.probe_mode = cxl_pci_probe_mode,
19462306a36Sopenharmony_ci	.enable_device_hook = cxl_pci_enable_device_hook,
19562306a36Sopenharmony_ci	.disable_device = cxl_pci_disable_device,
19662306a36Sopenharmony_ci	.release_device = cxl_pci_disable_device,
19762306a36Sopenharmony_ci	.reset_secondary_bus = cxl_pci_reset_secondary_bus,
19862306a36Sopenharmony_ci	.setup_msi_irqs = cxl_setup_msi_irqs,
19962306a36Sopenharmony_ci	.teardown_msi_irqs = cxl_teardown_msi_irqs,
20062306a36Sopenharmony_ci};
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ciint cxl_pci_vphb_add(struct cxl_afu *afu)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	struct pci_controller *phb;
20562306a36Sopenharmony_ci	struct device_node *vphb_dn;
20662306a36Sopenharmony_ci	struct device *parent;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	/*
20962306a36Sopenharmony_ci	 * If there are no AFU configuration records we won't have anything to
21062306a36Sopenharmony_ci	 * expose under the vPHB, so skip creating one, returning success since
21162306a36Sopenharmony_ci	 * this is still a valid case. This will also opt us out of EEH
21262306a36Sopenharmony_ci	 * handling since we won't have anything special to do if there are no
21362306a36Sopenharmony_ci	 * kernel drivers attached to the vPHB, and EEH handling is not yet
21462306a36Sopenharmony_ci	 * supported in the peer model.
21562306a36Sopenharmony_ci	 */
21662306a36Sopenharmony_ci	if (!afu->crs_num)
21762306a36Sopenharmony_ci		return 0;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	/* The parent device is the adapter. Reuse the device node of
22062306a36Sopenharmony_ci	 * the adapter.
22162306a36Sopenharmony_ci	 * We don't seem to care what device node is used for the vPHB,
22262306a36Sopenharmony_ci	 * but tools such as lsvpd walk up the device parents looking
22362306a36Sopenharmony_ci	 * for a valid location code, so we might as well show devices
22462306a36Sopenharmony_ci	 * attached to the adapter as being located on that adapter.
22562306a36Sopenharmony_ci	 */
22662306a36Sopenharmony_ci	parent = afu->adapter->dev.parent;
22762306a36Sopenharmony_ci	vphb_dn = parent->of_node;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	/* Alloc and setup PHB data structure */
23062306a36Sopenharmony_ci	phb = pcibios_alloc_controller(vphb_dn);
23162306a36Sopenharmony_ci	if (!phb)
23262306a36Sopenharmony_ci		return -ENODEV;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	/* Setup parent in sysfs */
23562306a36Sopenharmony_ci	phb->parent = parent;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	/* Setup the PHB using arch provided callback */
23862306a36Sopenharmony_ci	phb->ops = &cxl_pcie_pci_ops;
23962306a36Sopenharmony_ci	phb->cfg_addr = NULL;
24062306a36Sopenharmony_ci	phb->cfg_data = NULL;
24162306a36Sopenharmony_ci	phb->private_data = afu;
24262306a36Sopenharmony_ci	phb->controller_ops = cxl_pci_controller_ops;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/* Scan the bus */
24562306a36Sopenharmony_ci	pcibios_scan_phb(phb);
24662306a36Sopenharmony_ci	if (phb->bus == NULL)
24762306a36Sopenharmony_ci		return -ENXIO;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	/* Set release hook on root bus */
25062306a36Sopenharmony_ci	pci_set_host_bridge_release(to_pci_host_bridge(phb->bus->bridge),
25162306a36Sopenharmony_ci				    pcibios_free_controller_deferred,
25262306a36Sopenharmony_ci				    (void *) phb);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	/* Claim resources. This might need some rework as well depending
25562306a36Sopenharmony_ci	 * whether we are doing probe-only or not, like assigning unassigned
25662306a36Sopenharmony_ci	 * resources etc...
25762306a36Sopenharmony_ci	 */
25862306a36Sopenharmony_ci	pcibios_claim_one_bus(phb->bus);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	/* Add probed PCI devices to the device model */
26162306a36Sopenharmony_ci	pci_bus_add_devices(phb->bus);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	afu->phb = phb;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	return 0;
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_civoid cxl_pci_vphb_remove(struct cxl_afu *afu)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	struct pci_controller *phb;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	/* If there is no configuration record we won't have one of these */
27362306a36Sopenharmony_ci	if (!afu || !afu->phb)
27462306a36Sopenharmony_ci		return;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	phb = afu->phb;
27762306a36Sopenharmony_ci	afu->phb = NULL;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	pci_remove_root_bus(phb->bus);
28062306a36Sopenharmony_ci	/*
28162306a36Sopenharmony_ci	 * We don't free phb here - that's handled by
28262306a36Sopenharmony_ci	 * pcibios_free_controller_deferred()
28362306a36Sopenharmony_ci	 */
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cibool cxl_pci_is_vphb_device(struct pci_dev *dev)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	struct pci_controller *phb;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	phb = pci_bus_to_host(dev->bus);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	return (phb->ops == &cxl_pcie_pci_ops);
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistruct cxl_afu *cxl_pci_to_afu(struct pci_dev *dev)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	struct pci_controller *phb;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	phb = pci_bus_to_host(dev->bus);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	return (struct cxl_afu *)phb->private_data;
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_pci_to_afu);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ciunsigned int cxl_pci_to_cfg_record(struct pci_dev *dev)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	return cxl_pcie_cfg_record(dev->bus->number, dev->devfn);
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cxl_pci_to_cfg_record);
310