cregit-Linux how code gets into the kernel

Release 4.16 drivers/misc/ocxl/pci.c

// SPDX-License-Identifier: GPL-2.0+
// Copyright 2017 IBM Corp.
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/idr.h>
#include <asm/pnv-ocxl.h>
#include "ocxl_internal.h"

/*
 * Any opencapi device which wants to use this 'generic' driver should
 * use the 0x062B device ID. Vendors should define the subsystem
 * vendor/device ID to help differentiate devices.
 */

static const struct pci_device_id ocxl_pci_tbl[] = {
	{ PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x062B), },
	{ }
};
MODULE_DEVICE_TABLE(pci, ocxl_pci_tbl);



static struct ocxl_fn *ocxl_fn_get(struct ocxl_fn *fn) { return (get_device(&fn->dev) == NULL) ? NULL : fn; }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat30100.00%1100.00%
Total30100.00%1100.00%


static void ocxl_fn_put(struct ocxl_fn *fn) { put_device(&fn->dev); }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat19100.00%1100.00%
Total19100.00%1100.00%


struct ocxl_afu *ocxl_afu_get(struct ocxl_afu *afu) { return (get_device(&afu->dev) == NULL) ? NULL : afu; }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat29100.00%1100.00%
Total29100.00%1100.00%


void ocxl_afu_put(struct ocxl_afu *afu) { put_device(&afu->dev); }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat18100.00%1100.00%
Total18100.00%1100.00%


static struct ocxl_afu *alloc_afu(struct ocxl_fn *fn) { struct ocxl_afu *afu; afu = kzalloc(sizeof(struct ocxl_afu), GFP_KERNEL); if (!afu) return NULL; mutex_init(&afu->contexts_lock); mutex_init(&afu->afu_control_lock); idr_init(&afu->contexts_idr); afu->fn = fn; ocxl_fn_get(fn); return afu; }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat77100.00%1100.00%
Total77100.00%1100.00%


static void free_afu(struct ocxl_afu *afu) { idr_destroy(&afu->contexts_idr); ocxl_fn_put(afu->fn); kfree(afu); }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat31100.00%1100.00%
Total31100.00%1100.00%


static void free_afu_dev(struct device *dev) { struct ocxl_afu *afu = to_ocxl_afu(dev); ocxl_unregister_afu(afu); free_afu(afu); }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat31100.00%1100.00%
Total31100.00%1100.00%


static int set_afu_device(struct ocxl_afu *afu, const char *location) { struct ocxl_fn *fn = afu->fn; int rc; afu->dev.parent = &fn->dev; afu->dev.release = free_afu_dev; rc = dev_set_name(&afu->dev, "%s.%s.%hhu", afu->config.name, location, afu->config.idx); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat76100.00%1100.00%
Total76100.00%1100.00%


static int assign_afu_actag(struct ocxl_afu *afu, struct pci_dev *dev) { struct ocxl_fn *fn = afu->fn; int actag_count, actag_offset; /* * if there were not enough actags for the function, each afu * reduces its count as well */ actag_count = afu->config.actag_supported * fn->actag_enabled / fn->actag_supported; actag_offset = ocxl_actag_afu_alloc(fn, actag_count); if (actag_offset < 0) { dev_err(&afu->dev, "Can't allocate %d actags for AFU: %d\n", actag_count, actag_offset); return actag_offset; } afu->actag_base = fn->actag_base + actag_offset; afu->actag_enabled = actag_count; ocxl_config_set_afu_actag(dev, afu->config.dvsec_afu_control_pos, afu->actag_base, afu->actag_enabled); dev_dbg(&afu->dev, "actag base=%d enabled=%d\n", afu->actag_base, afu->actag_enabled); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat137100.00%1100.00%
Total137100.00%1100.00%


static void reclaim_afu_actag(struct ocxl_afu *afu) { struct ocxl_fn *fn = afu->fn; int start_offset, size; start_offset = afu->actag_base - fn->actag_base; size = afu->actag_enabled; ocxl_actag_afu_free(afu->fn, start_offset, size); }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat52100.00%1100.00%
Total52100.00%1100.00%


static int assign_afu_pasid(struct ocxl_afu *afu, struct pci_dev *dev) { struct ocxl_fn *fn = afu->fn; int pasid_count, pasid_offset; /* * We only support the case where the function configuration * requested enough PASIDs to cover all AFUs. */ pasid_count = 1 << afu->config.pasid_supported_log; pasid_offset = ocxl_pasid_afu_alloc(fn, pasid_count); if (pasid_offset < 0) { dev_err(&afu->dev, "Can't allocate %d PASIDs for AFU: %d\n", pasid_count, pasid_offset); return pasid_offset; } afu->pasid_base = fn->pasid_base + pasid_offset; afu->pasid_count = 0; afu->pasid_max = pasid_count; ocxl_config_set_afu_pasid(dev, afu->config.dvsec_afu_control_pos, afu->pasid_base, afu->config.pasid_supported_log); dev_dbg(&afu->dev, "PASID base=%d, enabled=%d\n", afu->pasid_base, pasid_count); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat137100.00%1100.00%
Total137100.00%1100.00%


static void reclaim_afu_pasid(struct ocxl_afu *afu) { struct ocxl_fn *fn = afu->fn; int start_offset, size; start_offset = afu->pasid_base - fn->pasid_base; size = 1 << afu->config.pasid_supported_log; ocxl_pasid_afu_free(afu->fn, start_offset, size); }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat56100.00%1100.00%
Total56100.00%1100.00%


static int reserve_fn_bar(struct ocxl_fn *fn, int bar) { struct pci_dev *dev = to_pci_dev(fn->dev.parent); int rc, idx; if (bar != 0 && bar != 2 && bar != 4) return -EINVAL; idx = bar >> 1; if (fn->bar_used[idx]++ == 0) { rc = pci_request_region(dev, bar, "ocxl"); if (rc) return rc; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat92100.00%1100.00%
Total92100.00%1100.00%


static void release_fn_bar(struct ocxl_fn *fn, int bar) { struct pci_dev *dev = to_pci_dev(fn->dev.parent); int idx; if (bar != 0 && bar != 2 && bar != 4) return; idx = bar >> 1; if (--fn->bar_used[idx] == 0) pci_release_region(dev, bar); WARN_ON(fn->bar_used[idx] < 0); }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat83100.00%1100.00%
Total83100.00%1100.00%


static int map_mmio_areas(struct ocxl_afu *afu, struct pci_dev *dev) { int rc; rc = reserve_fn_bar(afu->fn, afu->config.global_mmio_bar); if (rc) return rc; rc = reserve_fn_bar(afu->fn, afu->config.pp_mmio_bar); if (rc) { release_fn_bar(afu->fn, afu->config.global_mmio_bar); return rc; } afu->global_mmio_start = pci_resource_start(dev, afu->config.global_mmio_bar) + afu->config.global_mmio_offset; afu->pp_mmio_start = pci_resource_start(dev, afu->config.pp_mmio_bar) + afu->config.pp_mmio_offset; afu->global_mmio_ptr = ioremap(afu->global_mmio_start, afu->config.global_mmio_size); if (!afu->global_mmio_ptr) { release_fn_bar(afu->fn, afu->config.pp_mmio_bar); release_fn_bar(afu->fn, afu->config.global_mmio_bar); dev_err(&dev->dev, "Error mapping global mmio area\n"); return -ENOMEM; } /* * Leave an empty page between the per-process mmio area and * the AFU interrupt mappings */ afu->irq_base_offset = afu->config.pp_mmio_stride + PAGE_SIZE; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat202100.00%1100.00%
Total202100.00%1100.00%


static void unmap_mmio_areas(struct ocxl_afu *afu) { if (afu->global_mmio_ptr) { iounmap(afu->global_mmio_ptr); afu->global_mmio_ptr = NULL; } afu->global_mmio_start = 0; afu->pp_mmio_start = 0; release_fn_bar(afu->fn, afu->config.pp_mmio_bar); release_fn_bar(afu->fn, afu->config.global_mmio_bar); }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat70100.00%1100.00%
Total70100.00%1100.00%


static int configure_afu(struct ocxl_afu *afu, u8 afu_idx, struct pci_dev *dev) { int rc; rc = ocxl_config_read_afu(dev, &afu->fn->config, &afu->config, afu_idx); if (rc) return rc; rc = set_afu_device(afu, dev_name(&dev->dev)); if (rc) return rc; rc = assign_afu_actag(afu, dev); if (rc) return rc; rc = assign_afu_pasid(afu, dev); if (rc) { reclaim_afu_actag(afu); return rc; } rc = map_mmio_areas(afu, dev); if (rc) { reclaim_afu_pasid(afu); reclaim_afu_actag(afu); return rc; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat142100.00%1100.00%
Total142100.00%1100.00%


static void deconfigure_afu(struct ocxl_afu *afu) { unmap_mmio_areas(afu); reclaim_afu_pasid(afu); reclaim_afu_actag(afu); }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat26100.00%1100.00%
Total26100.00%1100.00%


static int activate_afu(struct pci_dev *dev, struct ocxl_afu *afu) { int rc; ocxl_config_set_afu_state(dev, afu->config.dvsec_afu_control_pos, 1); /* * Char device creation is the last step, as processes can * call our driver immediately, so all our inits must be finished. */ rc = ocxl_create_cdev(afu); if (rc) return rc; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat50100.00%1100.00%
Total50100.00%1100.00%


static void deactivate_afu(struct ocxl_afu *afu) { struct pci_dev *dev = to_pci_dev(afu->fn->dev.parent); ocxl_destroy_cdev(afu); ocxl_config_set_afu_state(dev, afu->config.dvsec_afu_control_pos, 0); }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat45100.00%1100.00%
Total45100.00%1100.00%


static int init_afu(struct pci_dev *dev, struct ocxl_fn *fn, u8 afu_idx) { int rc; struct ocxl_afu *afu; afu = alloc_afu(fn); if (!afu) return -ENOMEM; rc = configure_afu(afu, afu_idx, dev); if (rc) { free_afu(afu); return rc; } rc = ocxl_register_afu(afu); if (rc) goto err; rc = ocxl_sysfs_add_afu(afu); if (rc) goto err; rc = activate_afu(dev, afu); if (rc) goto err_sys; list_add_tail(&afu->list, &fn->afu_list); return 0; err_sys: ocxl_sysfs_remove_afu(afu); err: deconfigure_afu(afu); device_unregister(&afu->dev); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat153100.00%1100.00%
Total153100.00%1100.00%


static void remove_afu(struct ocxl_afu *afu) { list_del(&afu->list); ocxl_context_detach_all(afu); deactivate_afu(afu); ocxl_sysfs_remove_afu(afu); deconfigure_afu(afu); device_unregister(&afu->dev); }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat47100.00%1100.00%
Total47100.00%1100.00%


static struct ocxl_fn *alloc_function(struct pci_dev *dev) { struct ocxl_fn *fn; fn = kzalloc(sizeof(struct ocxl_fn), GFP_KERNEL); if (!fn) return NULL; INIT_LIST_HEAD(&fn->afu_list); INIT_LIST_HEAD(&fn->pasid_list); INIT_LIST_HEAD(&fn->actag_list); return fn; }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat66100.00%1100.00%
Total66100.00%1100.00%


static void free_function(struct ocxl_fn *fn) { WARN_ON(!list_empty(&fn->afu_list)); WARN_ON(!list_empty(&fn->pasid_list)); kfree(fn); }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat40100.00%1100.00%
Total40100.00%1100.00%


static void free_function_dev(struct device *dev) { struct ocxl_fn *fn = to_ocxl_function(dev); free_function(fn); }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat26100.00%1100.00%
Total26100.00%1100.00%


static int set_function_device(struct ocxl_fn *fn, struct pci_dev *dev) { int rc; fn->dev.parent = &dev->dev; fn->dev.release = free_function_dev; rc = dev_set_name(&fn->dev, "ocxlfn.%s", dev_name(&dev->dev)); if (rc) return rc; pci_set_drvdata(dev, fn); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat75100.00%1100.00%
Total75100.00%1100.00%


static int assign_function_actag(struct ocxl_fn *fn) { struct pci_dev *dev = to_pci_dev(fn->dev.parent); u16 base, enabled, supported; int rc; rc = ocxl_config_get_actag_info(dev, &base, &enabled, &supported); if (rc) return rc; fn->actag_base = base; fn->actag_enabled = enabled; fn->actag_supported = supported; ocxl_config_set_actag(dev, fn->config.dvsec_function_pos, fn->actag_base, fn->actag_enabled); dev_dbg(&fn->dev, "actag range starting at %d, enabled %d\n", fn->actag_base, fn->actag_enabled); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat116100.00%1100.00%
Total116100.00%1100.00%


static int set_function_pasid(struct ocxl_fn *fn) { struct pci_dev *dev = to_pci_dev(fn->dev.parent); int rc, desired_count, max_count; /* A function may not require any PASID */ if (fn->config.max_pasid_log < 0) return 0; rc = ocxl_config_get_pasid_info(dev, &max_count); if (rc) return rc; desired_count = 1 << fn->config.max_pasid_log; if (desired_count > max_count) { dev_err(&fn->dev, "Function requires more PASIDs than is available (%d vs. %d)\n", desired_count, max_count); return -ENOSPC; } fn->pasid_base = 0; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat108100.00%1100.00%
Total108100.00%1100.00%


static int configure_function(struct ocxl_fn *fn, struct pci_dev *dev) { int rc; rc = pci_enable_device(dev); if (rc) { dev_err(&dev->dev, "pci_enable_device failed: %d\n", rc); return rc; } /* * Once it has been confirmed to work on our hardware, we * should reset the function, to force the adapter to restart * from scratch. * A function reset would also reset all its AFUs. * * Some hints for implementation: * * - there's not status bit to know when the reset is done. We * should try reading the config space to know when it's * done. * - probably something like: * Reset * wait 100ms * issue config read * allow device up to 1 sec to return success on config * read before declaring it broken * * Some shared logic on the card (CFG, TLX) won't be reset, so * there's no guarantee that it will be enough. */ rc = ocxl_config_read_function(dev, &fn->config); if (rc) return rc; rc = set_function_device(fn, dev); if (rc) return rc; rc = assign_function_actag(fn); if (rc) return rc; rc = set_function_pasid(fn); if (rc) return rc; rc = ocxl_link_setup(dev, 0, &fn->link); if (rc) return rc; rc = ocxl_config_set_TL(dev, fn->config.dvsec_tl_pos); if (rc) { ocxl_link_release(dev, fn->link); return rc; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat166100.00%1100.00%
Total166100.00%1100.00%


static void deconfigure_function(struct ocxl_fn *fn) { struct pci_dev *dev = to_pci_dev(fn->dev.parent); ocxl_link_release(dev, fn->link); pci_disable_device(dev); }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat39100.00%1100.00%
Total39100.00%1100.00%


static struct ocxl_fn *init_function(struct pci_dev *dev) { struct ocxl_fn *fn; int rc; fn = alloc_function(dev); if (!fn) return ERR_PTR(-ENOMEM); rc = configure_function(fn, dev); if (rc) { free_function(fn); return ERR_PTR(rc); } rc = device_register(&fn->dev); if (rc) { deconfigure_function(fn); device_unregister(&fn->dev); return ERR_PTR(rc); } return fn; }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat104100.00%1100.00%
Total104100.00%1100.00%


static void remove_function(struct ocxl_fn *fn) { deconfigure_function(fn); device_unregister(&fn->dev); }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat24100.00%1100.00%
Total24100.00%1100.00%


static int ocxl_probe(struct pci_dev *dev, const struct pci_device_id *id) { int rc, afu_count = 0; u8 afu; struct ocxl_fn *fn; if (!radix_enabled()) { dev_err(&dev->dev, "Unsupported memory model (hash)\n"); return -ENODEV; } fn = init_function(dev); if (IS_ERR(fn)) { dev_err(&dev->dev, "function init failed: %li\n", PTR_ERR(fn)); return PTR_ERR(fn); } for (afu = 0; afu <= fn->config.max_afu_index; afu++) { rc = ocxl_config_check_afu_index(dev, &fn->config, afu); if (rc > 0) { rc = init_afu(dev, fn, afu); if (rc) { dev_err(&dev->dev, "Can't initialize AFU index %d\n", afu); continue; } afu_count++; } } dev_info(&dev->dev, "%d AFU(s) configured\n", afu_count); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat180100.00%1100.00%
Total180100.00%1100.00%


static void ocxl_remove(struct pci_dev *dev) { struct ocxl_afu *afu, *tmp; struct ocxl_fn *fn = pci_get_drvdata(dev); list_for_each_entry_safe(afu, tmp, &fn->afu_list, list) { remove_afu(afu); } remove_function(fn); }

Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat51100.00%1100.00%
Total51100.00%1100.00%

struct pci_driver ocxl_pci_driver = { .name = "ocxl", .id_table = ocxl_pci_tbl, .probe = ocxl_probe, .remove = ocxl_remove, .shutdown = ocxl_remove, };

Overall Contributors

PersonTokensPropCommitsCommitProp
Frederic Barrat2674100.00%1100.00%
Total2674100.00%1100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.