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