if (vendor == PCI_VENDOR_ID_IBM &&
id == OCXL_DVSEC_AFU_CTRL_ID) {
pci_read_config_byte(dev,
vsec + OCXL_DVSEC_AFU_CTRL_AFU_IDX,
&idx); if (idx == afu_idx) return vsec;
}
} return 0;
}
/** * get_function_0() - Find a related PCI device (function 0) * @dev: PCI device to match * * Returns a pointer to the related device, or null if not found
*/ staticstruct pci_dev *get_function_0(struct pci_dev *dev)
{ unsignedint devfn = PCI_DEVFN(PCI_SLOT(dev->devfn), 0);
/* * vendor specific DVSEC, for IBM images only. Some older * images may not have it * * It's only used on function 0 to specify the version of some * logic blocks and to give access to special registers to * enable host-based flashing.
*/ if (PCI_FUNC(dev->devfn) != 0) return 0;
pos = find_dvsec(dev, OCXL_DVSEC_VENDOR_ID); if (!pos) return 0;
dev_dbg(&dev->dev, "Vendor specific DVSEC:\n");
dev_dbg(&dev->dev, " CFG version = 0x%x\n", cfg);
dev_dbg(&dev->dev, " TLX version = 0x%x\n", tlx);
dev_dbg(&dev->dev, " DLX version = 0x%x\n", dlx);
dev_dbg(&dev->dev, " ResetReload = 0x%x\n", reset_reload); return 0;
}
/** * get_dvsec_vendor0() - Find a related PCI device (function 0) * @dev: PCI device to match * @dev0: The PCI device (function 0) found * @out_pos: The position of PCI device (function 0) * * Returns 0 on success, negative on failure. * * NOTE: If it's successful, the reference of dev0 is increased, * so after using it, the callers must call pci_dev_put() to give * up the reference.
*/ staticint get_dvsec_vendor0(struct pci_dev *dev, struct pci_dev **dev0, int *out_pos)
{ int pos;
if (PCI_FUNC(dev->devfn) != 0) {
dev = get_function_0(dev); if (!dev) return -1;
} else {
dev = pci_dev_get(dev);
}
pos = find_dvsec(dev, OCXL_DVSEC_VENDOR_ID); if (!pos) {
pci_dev_put(dev); return -1;
}
*dev0 = dev;
*out_pos = pos; return 0;
}
int ocxl_config_get_reset_reload(struct pci_dev *dev, int *val)
{ struct pci_dev *dev0;
u32 reset_reload; int pos;
if (get_dvsec_vendor0(dev, &dev0, &pos)) return -1;
staticint validate_function(struct pci_dev *dev, struct ocxl_fn_config *fn)
{ if (fn->max_pasid_log == -1 && fn->max_afu_index >= 0) {
dev_err(&dev->dev, "AFUs are defined but no PASIDs are requested\n"); return -EINVAL;
}
if (fn->max_afu_index > OCXL_MAX_AFU_PER_FUNCTION) {
dev_err(&dev->dev, "Max AFU index out of architectural limit (%d vs %d)\n",
fn->max_afu_index, OCXL_MAX_AFU_PER_FUNCTION); return -EINVAL;
} return 0;
}
int ocxl_config_read_function(struct pci_dev *dev, struct ocxl_fn_config *fn)
{ int rc;
/** * read_template_version() - Read the template version from the AFU * @dev: the device for the AFU * @fn: the AFU offsets * @len: outputs the template length * @version: outputs the major<<8,minor version * * Returns 0 on success, negative on failure
*/ staticint read_template_version(struct pci_dev *dev, struct ocxl_fn_config *fn,
u16 *len, u16 *version)
{
u32 val32;
u8 major, minor; int rc;
rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_VERSION, &val32); if (rc) return rc;
staticbool char_allowed(int c)
{ /* * Permitted Characters : Alphanumeric, hyphen, underscore, comma
*/ if ((c >= 0x30 && c <= 0x39) /* digits */ ||
(c >= 0x41 && c <= 0x5A) /* upper case */ ||
(c >= 0x61 && c <= 0x7A) /* lower case */ ||
c == 0 /* NULL */ ||
c == 0x2D /* - */ ||
c == 0x5F /* _ */ ||
c == 0x2C /* , */) returntrue; returnfalse;
}
staticint validate_afu(struct pci_dev *dev, struct ocxl_afu_config *afu)
{ int i;
if (!afu->name[0]) {
dev_err(&dev->dev, "Empty AFU name\n"); return -EINVAL;
} for (i = 0; i < OCXL_TEMPL_NAME_LEN; i++) { if (!char_allowed(afu->name[i])) {
dev_err(&dev->dev, "Invalid character in AFU name\n"); return -EINVAL;
}
}
if (afu->global_mmio_bar != 0 &&
afu->global_mmio_bar != 2 &&
afu->global_mmio_bar != 4) {
dev_err(&dev->dev, "Invalid global MMIO bar number\n"); return -EINVAL;
} if (afu->pp_mmio_bar != 0 &&
afu->pp_mmio_bar != 2 &&
afu->pp_mmio_bar != 4) {
dev_err(&dev->dev, "Invalid per-process MMIO bar number\n"); return -EINVAL;
} return 0;
}
/** * read_afu_lpc_memory_info() - Populate AFU metadata regarding LPC memory * @dev: the device for the AFU * @fn: the AFU offsets * @afu: the AFU struct to populate the LPC metadata into * * Returns 0 on success, negative on failure
*/ staticint read_afu_lpc_memory_info(struct pci_dev *dev, struct ocxl_fn_config *fn, struct ocxl_afu_config *afu)
{ int rc;
u32 val32;
u16 templ_version;
u16 templ_len;
u64 total_mem_size = 0;
u64 lpc_mem_size = 0;
afu->lpc_mem_offset = 0;
afu->lpc_mem_size = 0;
afu->special_purpose_mem_offset = 0;
afu->special_purpose_mem_size = 0; /* * For AFUs following template v1.0, the LPC memory covers the * total memory. Its size is a power of 2. * * For AFUs with template >= v1.01, the total memory size is * still a power of 2, but it is split in 2 parts: * - the LPC memory, whose size can now be anything * - the remainder memory is a special purpose memory, whose * definition is AFU-dependent. It is not accessible through * the usual commands for LPC memory
*/
rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_ALL_MEM_SZ, &val32); if (rc) return rc;
val32 = EXTRACT_BITS(val32, 0, 7); if (!val32) return 0; /* No LPC memory */
/* * The configuration space spec allows for a memory size of up * to 2^255 bytes. * * Current generation hardware uses 56-bit physical addresses, * but we won't be able to get near close to that, as we won't * have a hole big enough in the memory map. Let it pass in * the driver for now. We'll get an error from the firmware * when trying to configure something too big.
*/
total_mem_size = 1ull << val32;
rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_LPC_MEM_START, &val32); if (rc) return rc;
int ocxl_config_read_afu(struct pci_dev *dev, struct ocxl_fn_config *fn, struct ocxl_afu_config *afu, u8 afu_idx)
{ int rc;
u32 val32;
/* * First, we need to write the AFU idx for the AFU we want to * access.
*/
WARN_ON((afu_idx & OCXL_DVSEC_AFU_IDX_MASK) != afu_idx);
afu->idx = afu_idx;
pci_write_config_byte(dev,
fn->dvsec_afu_info_pos + OCXL_DVSEC_AFU_INFO_AFU_IDX,
afu->idx);
rc = read_afu_name(dev, fn, afu); if (rc) return rc;
int ocxl_config_get_actag_info(struct pci_dev *dev, u16 *base, u16 *enabled,
u16 *supported)
{ int rc;
/* * This is really a simple wrapper for the kernel API, to * avoid an external driver using ocxl as a library to call * platform-dependent code
*/
rc = pnv_ocxl_get_actag(dev, base, enabled, supported); if (rc) {
dev_err(&dev->dev, "Can't get actag for device: %d\n", rc); return rc;
} return 0;
}
EXPORT_SYMBOL_GPL(ocxl_config_get_actag_info);
void ocxl_config_set_afu_actag(struct pci_dev *dev, int pos, int actag_base, int actag_count)
{
u16 val;
val = actag_count & OCXL_DVSEC_ACTAG_MASK;
pci_write_config_byte(dev, pos + OCXL_DVSEC_AFU_CTRL_ACTAG_EN, val);
void ocxl_config_set_afu_state(struct pci_dev *dev, int pos, int enable)
{
u8 val;
pci_read_config_byte(dev, pos + OCXL_DVSEC_AFU_CTRL_ENABLE, &val); if (enable)
val |= 1; else
val &= 0xFE;
pci_write_config_byte(dev, pos + OCXL_DVSEC_AFU_CTRL_ENABLE, val);
}
EXPORT_SYMBOL_GPL(ocxl_config_set_afu_state);
int ocxl_config_set_TL(struct pci_dev *dev, int tl_dvsec)
{
u32 val;
__be32 *be32ptr;
u8 timers; int i, rc; long recv_cap; char *recv_rate;
/* * Skip on function != 0, as the TL can only be defined on 0
*/ if (PCI_FUNC(dev->devfn) != 0) return 0;
recv_rate = kzalloc(PNV_OCXL_TL_RATE_BUF_SIZE, GFP_KERNEL); if (!recv_rate) return -ENOMEM; /* * The spec defines 64 templates for messages in the * Transaction Layer (TL). * * The host and device each support a subset, so we need to * configure the transmitters on each side to send only * templates the receiver understands, at a rate the receiver * can process. Per the spec, template 0 must be supported by * everybody. That's the template which has been used by the * host and device so far. * * The sending rate limit must be set before the template is * enabled.
*/
for (i = 0; i < PNV_OCXL_TL_RATE_BUF_SIZE; i += 4) {
be32ptr = (__be32 *) &recv_rate[i];
pci_write_config_dword(dev,
tl_dvsec + OCXL_DVSEC_TL_SEND_RATE + i,
be32_to_cpu(*be32ptr));
}
val = recv_cap >> 32;
pci_write_config_dword(dev, tl_dvsec + OCXL_DVSEC_TL_SEND_CAP, val);
val = recv_cap & GENMASK(31, 0);
pci_write_config_dword(dev, tl_dvsec + OCXL_DVSEC_TL_SEND_CAP + 4, val);
/* * Host -> device
*/ for (i = 0; i < PNV_OCXL_TL_RATE_BUF_SIZE; i += 4) {
pci_read_config_dword(dev,
tl_dvsec + OCXL_DVSEC_TL_RECV_RATE + i,
&val);
be32ptr = (__be32 *) &recv_rate[i];
*be32ptr = cpu_to_be32(val);
}
pci_read_config_dword(dev, tl_dvsec + OCXL_DVSEC_TL_RECV_CAP, &val);
recv_cap = (long) val << 32;
pci_read_config_dword(dev, tl_dvsec + OCXL_DVSEC_TL_RECV_CAP + 4, &val);
recv_cap |= val;
rc = pnv_ocxl_set_tl_conf(dev, recv_cap, __pa(recv_rate),
PNV_OCXL_TL_RATE_BUF_SIZE); if (rc) goto out;
/* * Opencapi commands needing to be retried are classified per * the TL in 2 groups: short and long commands. * * The short back off timer it not used for now. It will be * for opencapi 4.0. * * The long back off timer is typically used when an AFU hits * a page fault but the NPU is already processing one. So the * AFU needs to wait before it can resubmit. Having a value * too low doesn't break anything, but can generate extra * traffic on the link. * We set it to 1.6 us for now. It's shorter than, but in the * same order of magnitude as the time spent to process a page * fault.
*/
timers = 0x2 << 4; /* long timer = 1.6 us */
pci_write_config_byte(dev, tl_dvsec + OCXL_DVSEC_TL_BACKOFF_TIMERS,
timers);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.