xref: /openbmc/linux/drivers/misc/ocxl/core.c (revision 961d289b)
11ba21436SAlastair D'Silva // SPDX-License-Identifier: GPL-2.0+
21ba21436SAlastair D'Silva // Copyright 2019 IBM Corp.
31ba21436SAlastair D'Silva #include <linux/idr.h>
41ba21436SAlastair D'Silva #include "ocxl_internal.h"
51ba21436SAlastair D'Silva 
ocxl_fn_get(struct ocxl_fn * fn)61ba21436SAlastair D'Silva static struct ocxl_fn *ocxl_fn_get(struct ocxl_fn *fn)
71ba21436SAlastair D'Silva {
81ba21436SAlastair D'Silva 	return (get_device(&fn->dev) == NULL) ? NULL : fn;
91ba21436SAlastair D'Silva }
101ba21436SAlastair D'Silva 
ocxl_fn_put(struct ocxl_fn * fn)111ba21436SAlastair D'Silva static void ocxl_fn_put(struct ocxl_fn *fn)
121ba21436SAlastair D'Silva {
131ba21436SAlastair D'Silva 	put_device(&fn->dev);
141ba21436SAlastair D'Silva }
151ba21436SAlastair D'Silva 
alloc_afu(struct ocxl_fn * fn)161ba21436SAlastair D'Silva static struct ocxl_afu *alloc_afu(struct ocxl_fn *fn)
171ba21436SAlastair D'Silva {
181ba21436SAlastair D'Silva 	struct ocxl_afu *afu;
191ba21436SAlastair D'Silva 
201ba21436SAlastair D'Silva 	afu = kzalloc(sizeof(struct ocxl_afu), GFP_KERNEL);
211ba21436SAlastair D'Silva 	if (!afu)
221ba21436SAlastair D'Silva 		return NULL;
231ba21436SAlastair D'Silva 
2475ca758aSAlastair D'Silva 	kref_init(&afu->kref);
251ba21436SAlastair D'Silva 	mutex_init(&afu->contexts_lock);
261ba21436SAlastair D'Silva 	mutex_init(&afu->afu_control_lock);
271ba21436SAlastair D'Silva 	idr_init(&afu->contexts_idr);
281ba21436SAlastair D'Silva 	afu->fn = fn;
291ba21436SAlastair D'Silva 	ocxl_fn_get(fn);
301ba21436SAlastair D'Silva 	return afu;
311ba21436SAlastair D'Silva }
321ba21436SAlastair D'Silva 
free_afu(struct kref * kref)3375ca758aSAlastair D'Silva static void free_afu(struct kref *kref)
341ba21436SAlastair D'Silva {
3575ca758aSAlastair D'Silva 	struct ocxl_afu *afu = container_of(kref, struct ocxl_afu, kref);
3675ca758aSAlastair D'Silva 
371ba21436SAlastair D'Silva 	idr_destroy(&afu->contexts_idr);
381ba21436SAlastair D'Silva 	ocxl_fn_put(afu->fn);
391ba21436SAlastair D'Silva 	kfree(afu);
401ba21436SAlastair D'Silva }
411ba21436SAlastair D'Silva 
ocxl_afu_get(struct ocxl_afu * afu)4275ca758aSAlastair D'Silva void ocxl_afu_get(struct ocxl_afu *afu)
431ba21436SAlastair D'Silva {
4475ca758aSAlastair D'Silva 	kref_get(&afu->kref);
451ba21436SAlastair D'Silva }
4675ca758aSAlastair D'Silva EXPORT_SYMBOL_GPL(ocxl_afu_get);
471ba21436SAlastair D'Silva 
ocxl_afu_put(struct ocxl_afu * afu)4875ca758aSAlastair D'Silva void ocxl_afu_put(struct ocxl_afu *afu)
491ba21436SAlastair D'Silva {
5075ca758aSAlastair D'Silva 	kref_put(&afu->kref, free_afu);
511ba21436SAlastair D'Silva }
5275ca758aSAlastair D'Silva EXPORT_SYMBOL_GPL(ocxl_afu_put);
531ba21436SAlastair D'Silva 
assign_afu_actag(struct ocxl_afu * afu)542f7d3d14SAlastair D'Silva static int assign_afu_actag(struct ocxl_afu *afu)
551ba21436SAlastair D'Silva {
561ba21436SAlastair D'Silva 	struct ocxl_fn *fn = afu->fn;
571ba21436SAlastair D'Silva 	int actag_count, actag_offset;
582f7d3d14SAlastair D'Silva 	struct pci_dev *pci_dev = to_pci_dev(fn->dev.parent);
591ba21436SAlastair D'Silva 
601ba21436SAlastair D'Silva 	/*
611ba21436SAlastair D'Silva 	 * if there were not enough actags for the function, each afu
621ba21436SAlastair D'Silva 	 * reduces its count as well
631ba21436SAlastair D'Silva 	 */
641ba21436SAlastair D'Silva 	actag_count = afu->config.actag_supported *
651ba21436SAlastair D'Silva 		fn->actag_enabled / fn->actag_supported;
661ba21436SAlastair D'Silva 	actag_offset = ocxl_actag_afu_alloc(fn, actag_count);
671ba21436SAlastair D'Silva 	if (actag_offset < 0) {
682f7d3d14SAlastair D'Silva 		dev_err(&pci_dev->dev, "Can't allocate %d actags for AFU: %d\n",
691ba21436SAlastair D'Silva 			actag_count, actag_offset);
701ba21436SAlastair D'Silva 		return actag_offset;
711ba21436SAlastair D'Silva 	}
721ba21436SAlastair D'Silva 	afu->actag_base = fn->actag_base + actag_offset;
731ba21436SAlastair D'Silva 	afu->actag_enabled = actag_count;
741ba21436SAlastair D'Silva 
752f7d3d14SAlastair D'Silva 	ocxl_config_set_afu_actag(pci_dev, afu->config.dvsec_afu_control_pos,
761ba21436SAlastair D'Silva 				afu->actag_base, afu->actag_enabled);
772f7d3d14SAlastair D'Silva 	dev_dbg(&pci_dev->dev, "actag base=%d enabled=%d\n",
781ba21436SAlastair D'Silva 		afu->actag_base, afu->actag_enabled);
791ba21436SAlastair D'Silva 	return 0;
801ba21436SAlastair D'Silva }
811ba21436SAlastair D'Silva 
reclaim_afu_actag(struct ocxl_afu * afu)821ba21436SAlastair D'Silva static void reclaim_afu_actag(struct ocxl_afu *afu)
831ba21436SAlastair D'Silva {
841ba21436SAlastair D'Silva 	struct ocxl_fn *fn = afu->fn;
851ba21436SAlastair D'Silva 	int start_offset, size;
861ba21436SAlastair D'Silva 
871ba21436SAlastair D'Silva 	start_offset = afu->actag_base - fn->actag_base;
881ba21436SAlastair D'Silva 	size = afu->actag_enabled;
891ba21436SAlastair D'Silva 	ocxl_actag_afu_free(afu->fn, start_offset, size);
901ba21436SAlastair D'Silva }
911ba21436SAlastair D'Silva 
assign_afu_pasid(struct ocxl_afu * afu)922f7d3d14SAlastair D'Silva static int assign_afu_pasid(struct ocxl_afu *afu)
931ba21436SAlastair D'Silva {
941ba21436SAlastair D'Silva 	struct ocxl_fn *fn = afu->fn;
951ba21436SAlastair D'Silva 	int pasid_count, pasid_offset;
962f7d3d14SAlastair D'Silva 	struct pci_dev *pci_dev = to_pci_dev(fn->dev.parent);
971ba21436SAlastair D'Silva 
981ba21436SAlastair D'Silva 	/*
991ba21436SAlastair D'Silva 	 * We only support the case where the function configuration
1001ba21436SAlastair D'Silva 	 * requested enough PASIDs to cover all AFUs.
1011ba21436SAlastair D'Silva 	 */
1021ba21436SAlastair D'Silva 	pasid_count = 1 << afu->config.pasid_supported_log;
1031ba21436SAlastair D'Silva 	pasid_offset = ocxl_pasid_afu_alloc(fn, pasid_count);
1041ba21436SAlastair D'Silva 	if (pasid_offset < 0) {
1052f7d3d14SAlastair D'Silva 		dev_err(&pci_dev->dev, "Can't allocate %d PASIDs for AFU: %d\n",
1061ba21436SAlastair D'Silva 			pasid_count, pasid_offset);
1071ba21436SAlastair D'Silva 		return pasid_offset;
1081ba21436SAlastair D'Silva 	}
1091ba21436SAlastair D'Silva 	afu->pasid_base = fn->pasid_base + pasid_offset;
1101ba21436SAlastair D'Silva 	afu->pasid_count = 0;
1111ba21436SAlastair D'Silva 	afu->pasid_max = pasid_count;
1121ba21436SAlastair D'Silva 
1132f7d3d14SAlastair D'Silva 	ocxl_config_set_afu_pasid(pci_dev, afu->config.dvsec_afu_control_pos,
1141ba21436SAlastair D'Silva 				afu->pasid_base,
1151ba21436SAlastair D'Silva 				afu->config.pasid_supported_log);
1162f7d3d14SAlastair D'Silva 	dev_dbg(&pci_dev->dev, "PASID base=%d, enabled=%d\n",
1171ba21436SAlastair D'Silva 		afu->pasid_base, pasid_count);
1181ba21436SAlastair D'Silva 	return 0;
1191ba21436SAlastair D'Silva }
1201ba21436SAlastair D'Silva 
reclaim_afu_pasid(struct ocxl_afu * afu)1211ba21436SAlastair D'Silva static void reclaim_afu_pasid(struct ocxl_afu *afu)
1221ba21436SAlastair D'Silva {
1231ba21436SAlastair D'Silva 	struct ocxl_fn *fn = afu->fn;
1241ba21436SAlastair D'Silva 	int start_offset, size;
1251ba21436SAlastair D'Silva 
1261ba21436SAlastair D'Silva 	start_offset = afu->pasid_base - fn->pasid_base;
1271ba21436SAlastair D'Silva 	size = 1 << afu->config.pasid_supported_log;
1281ba21436SAlastair D'Silva 	ocxl_pasid_afu_free(afu->fn, start_offset, size);
1291ba21436SAlastair D'Silva }
1301ba21436SAlastair D'Silva 
reserve_fn_bar(struct ocxl_fn * fn,int bar)1311ba21436SAlastair D'Silva static int reserve_fn_bar(struct ocxl_fn *fn, int bar)
1321ba21436SAlastair D'Silva {
1331ba21436SAlastair D'Silva 	struct pci_dev *dev = to_pci_dev(fn->dev.parent);
1341ba21436SAlastair D'Silva 	int rc, idx;
1351ba21436SAlastair D'Silva 
1361ba21436SAlastair D'Silva 	if (bar != 0 && bar != 2 && bar != 4)
1371ba21436SAlastair D'Silva 		return -EINVAL;
1381ba21436SAlastair D'Silva 
1391ba21436SAlastair D'Silva 	idx = bar >> 1;
1401ba21436SAlastair D'Silva 	if (fn->bar_used[idx]++ == 0) {
1411ba21436SAlastair D'Silva 		rc = pci_request_region(dev, bar, "ocxl");
1421ba21436SAlastair D'Silva 		if (rc)
1431ba21436SAlastair D'Silva 			return rc;
1441ba21436SAlastair D'Silva 	}
1451ba21436SAlastair D'Silva 	return 0;
1461ba21436SAlastair D'Silva }
1471ba21436SAlastair D'Silva 
release_fn_bar(struct ocxl_fn * fn,int bar)1481ba21436SAlastair D'Silva static void release_fn_bar(struct ocxl_fn *fn, int bar)
1491ba21436SAlastair D'Silva {
1501ba21436SAlastair D'Silva 	struct pci_dev *dev = to_pci_dev(fn->dev.parent);
1511ba21436SAlastair D'Silva 	int idx;
1521ba21436SAlastair D'Silva 
1531ba21436SAlastair D'Silva 	if (bar != 0 && bar != 2 && bar != 4)
1541ba21436SAlastair D'Silva 		return;
1551ba21436SAlastair D'Silva 
1561ba21436SAlastair D'Silva 	idx = bar >> 1;
1571ba21436SAlastair D'Silva 	if (--fn->bar_used[idx] == 0)
1581ba21436SAlastair D'Silva 		pci_release_region(dev, bar);
1591ba21436SAlastair D'Silva 	WARN_ON(fn->bar_used[idx] < 0);
1601ba21436SAlastair D'Silva }
1611ba21436SAlastair D'Silva 
map_mmio_areas(struct ocxl_afu * afu)1622f7d3d14SAlastair D'Silva static int map_mmio_areas(struct ocxl_afu *afu)
1631ba21436SAlastair D'Silva {
1641ba21436SAlastair D'Silva 	int rc;
1652f7d3d14SAlastair D'Silva 	struct pci_dev *pci_dev = to_pci_dev(afu->fn->dev.parent);
1661ba21436SAlastair D'Silva 
1671ba21436SAlastair D'Silva 	rc = reserve_fn_bar(afu->fn, afu->config.global_mmio_bar);
1681ba21436SAlastair D'Silva 	if (rc)
1691ba21436SAlastair D'Silva 		return rc;
1701ba21436SAlastair D'Silva 
1711ba21436SAlastair D'Silva 	rc = reserve_fn_bar(afu->fn, afu->config.pp_mmio_bar);
1721ba21436SAlastair D'Silva 	if (rc) {
1731ba21436SAlastair D'Silva 		release_fn_bar(afu->fn, afu->config.global_mmio_bar);
1741ba21436SAlastair D'Silva 		return rc;
1751ba21436SAlastair D'Silva 	}
1761ba21436SAlastair D'Silva 
1771ba21436SAlastair D'Silva 	afu->global_mmio_start =
1782f7d3d14SAlastair D'Silva 		pci_resource_start(pci_dev, afu->config.global_mmio_bar) +
1791ba21436SAlastair D'Silva 		afu->config.global_mmio_offset;
1801ba21436SAlastair D'Silva 	afu->pp_mmio_start =
1812f7d3d14SAlastair D'Silva 		pci_resource_start(pci_dev, afu->config.pp_mmio_bar) +
1821ba21436SAlastair D'Silva 		afu->config.pp_mmio_offset;
1831ba21436SAlastair D'Silva 
1841ba21436SAlastair D'Silva 	afu->global_mmio_ptr = ioremap(afu->global_mmio_start,
1851ba21436SAlastair D'Silva 				afu->config.global_mmio_size);
1861ba21436SAlastair D'Silva 	if (!afu->global_mmio_ptr) {
1871ba21436SAlastair D'Silva 		release_fn_bar(afu->fn, afu->config.pp_mmio_bar);
1881ba21436SAlastair D'Silva 		release_fn_bar(afu->fn, afu->config.global_mmio_bar);
1892f7d3d14SAlastair D'Silva 		dev_err(&pci_dev->dev, "Error mapping global mmio area\n");
1901ba21436SAlastair D'Silva 		return -ENOMEM;
1911ba21436SAlastair D'Silva 	}
1921ba21436SAlastair D'Silva 
1931ba21436SAlastair D'Silva 	/*
1941ba21436SAlastair D'Silva 	 * Leave an empty page between the per-process mmio area and
1951ba21436SAlastair D'Silva 	 * the AFU interrupt mappings
1961ba21436SAlastair D'Silva 	 */
1971ba21436SAlastair D'Silva 	afu->irq_base_offset = afu->config.pp_mmio_stride + PAGE_SIZE;
1981ba21436SAlastair D'Silva 	return 0;
1991ba21436SAlastair D'Silva }
2001ba21436SAlastair D'Silva 
unmap_mmio_areas(struct ocxl_afu * afu)2011ba21436SAlastair D'Silva static void unmap_mmio_areas(struct ocxl_afu *afu)
2021ba21436SAlastair D'Silva {
2031ba21436SAlastair D'Silva 	if (afu->global_mmio_ptr) {
2041ba21436SAlastair D'Silva 		iounmap(afu->global_mmio_ptr);
2051ba21436SAlastair D'Silva 		afu->global_mmio_ptr = NULL;
2061ba21436SAlastair D'Silva 	}
2071ba21436SAlastair D'Silva 	afu->global_mmio_start = 0;
2081ba21436SAlastair D'Silva 	afu->pp_mmio_start = 0;
2091ba21436SAlastair D'Silva 	release_fn_bar(afu->fn, afu->config.pp_mmio_bar);
2101ba21436SAlastair D'Silva 	release_fn_bar(afu->fn, afu->config.global_mmio_bar);
2111ba21436SAlastair D'Silva }
2121ba21436SAlastair D'Silva 
configure_afu(struct ocxl_afu * afu,u8 afu_idx,struct pci_dev * dev)2131ba21436SAlastair D'Silva static int configure_afu(struct ocxl_afu *afu, u8 afu_idx, struct pci_dev *dev)
2141ba21436SAlastair D'Silva {
2151ba21436SAlastair D'Silva 	int rc;
2161ba21436SAlastair D'Silva 
2171ba21436SAlastair D'Silva 	rc = ocxl_config_read_afu(dev, &afu->fn->config, &afu->config, afu_idx);
2181ba21436SAlastair D'Silva 	if (rc)
2191ba21436SAlastair D'Silva 		return rc;
2201ba21436SAlastair D'Silva 
2212f7d3d14SAlastair D'Silva 	rc = assign_afu_actag(afu);
2221ba21436SAlastair D'Silva 	if (rc)
2231ba21436SAlastair D'Silva 		return rc;
2241ba21436SAlastair D'Silva 
2252f7d3d14SAlastair D'Silva 	rc = assign_afu_pasid(afu);
22675ca758aSAlastair D'Silva 	if (rc)
22775ca758aSAlastair D'Silva 		goto err_free_actag;
2281ba21436SAlastair D'Silva 
2292f7d3d14SAlastair D'Silva 	rc = map_mmio_areas(afu);
23075ca758aSAlastair D'Silva 	if (rc)
23175ca758aSAlastair D'Silva 		goto err_free_pasid;
23275ca758aSAlastair D'Silva 
23375ca758aSAlastair D'Silva 	return 0;
23475ca758aSAlastair D'Silva 
23575ca758aSAlastair D'Silva err_free_pasid:
2361ba21436SAlastair D'Silva 	reclaim_afu_pasid(afu);
23775ca758aSAlastair D'Silva err_free_actag:
2381ba21436SAlastair D'Silva 	reclaim_afu_actag(afu);
2391ba21436SAlastair D'Silva 	return rc;
2401ba21436SAlastair D'Silva }
2411ba21436SAlastair D'Silva 
deconfigure_afu(struct ocxl_afu * afu)2421ba21436SAlastair D'Silva static void deconfigure_afu(struct ocxl_afu *afu)
2431ba21436SAlastair D'Silva {
2441ba21436SAlastair D'Silva 	unmap_mmio_areas(afu);
2451ba21436SAlastair D'Silva 	reclaim_afu_pasid(afu);
2461ba21436SAlastair D'Silva 	reclaim_afu_actag(afu);
2471ba21436SAlastair D'Silva }
2481ba21436SAlastair D'Silva 
activate_afu(struct pci_dev * dev,struct ocxl_afu * afu)2491ba21436SAlastair D'Silva static int activate_afu(struct pci_dev *dev, struct ocxl_afu *afu)
2501ba21436SAlastair D'Silva {
2511ba21436SAlastair D'Silva 	ocxl_config_set_afu_state(dev, afu->config.dvsec_afu_control_pos, 1);
25275ca758aSAlastair D'Silva 
2531ba21436SAlastair D'Silva 	return 0;
2541ba21436SAlastair D'Silva }
2551ba21436SAlastair D'Silva 
deactivate_afu(struct ocxl_afu * afu)2561ba21436SAlastair D'Silva static void deactivate_afu(struct ocxl_afu *afu)
2571ba21436SAlastair D'Silva {
2581ba21436SAlastair D'Silva 	struct pci_dev *dev = to_pci_dev(afu->fn->dev.parent);
2591ba21436SAlastair D'Silva 
2601ba21436SAlastair D'Silva 	ocxl_config_set_afu_state(dev, afu->config.dvsec_afu_control_pos, 0);
2611ba21436SAlastair D'Silva }
2621ba21436SAlastair D'Silva 
init_afu(struct pci_dev * dev,struct ocxl_fn * fn,u8 afu_idx)26375ca758aSAlastair D'Silva static int init_afu(struct pci_dev *dev, struct ocxl_fn *fn, u8 afu_idx)
2641ba21436SAlastair D'Silva {
2651ba21436SAlastair D'Silva 	int rc;
2661ba21436SAlastair D'Silva 	struct ocxl_afu *afu;
2671ba21436SAlastair D'Silva 
2681ba21436SAlastair D'Silva 	afu = alloc_afu(fn);
2691ba21436SAlastair D'Silva 	if (!afu)
2701ba21436SAlastair D'Silva 		return -ENOMEM;
2711ba21436SAlastair D'Silva 
2721ba21436SAlastair D'Silva 	rc = configure_afu(afu, afu_idx, dev);
2731ba21436SAlastair D'Silva 	if (rc) {
27475ca758aSAlastair D'Silva 		ocxl_afu_put(afu);
2751ba21436SAlastair D'Silva 		return rc;
2761ba21436SAlastair D'Silva 	}
2771ba21436SAlastair D'Silva 
2781ba21436SAlastair D'Silva 	rc = activate_afu(dev, afu);
27975ca758aSAlastair D'Silva 	if (rc) {
2801ba21436SAlastair D'Silva 		deconfigure_afu(afu);
28175ca758aSAlastair D'Silva 		ocxl_afu_put(afu);
2821ba21436SAlastair D'Silva 		return rc;
2831ba21436SAlastair D'Silva 	}
2841ba21436SAlastair D'Silva 
28575ca758aSAlastair D'Silva 	list_add_tail(&afu->list, &fn->afu_list);
28675ca758aSAlastair D'Silva 
28775ca758aSAlastair D'Silva 	return 0;
28875ca758aSAlastair D'Silva }
28975ca758aSAlastair D'Silva 
remove_afu(struct ocxl_afu * afu)29075ca758aSAlastair D'Silva static void remove_afu(struct ocxl_afu *afu)
2911ba21436SAlastair D'Silva {
2921ba21436SAlastair D'Silva 	list_del(&afu->list);
2931ba21436SAlastair D'Silva 	ocxl_context_detach_all(afu);
2941ba21436SAlastair D'Silva 	deactivate_afu(afu);
2951ba21436SAlastair D'Silva 	deconfigure_afu(afu);
29675ca758aSAlastair D'Silva 	ocxl_afu_put(afu); // matches the implicit get in alloc_afu
2971ba21436SAlastair D'Silva }
2981ba21436SAlastair D'Silva 
alloc_function(void)2992f7d3d14SAlastair D'Silva static struct ocxl_fn *alloc_function(void)
3001ba21436SAlastair D'Silva {
3011ba21436SAlastair D'Silva 	struct ocxl_fn *fn;
3021ba21436SAlastair D'Silva 
3031ba21436SAlastair D'Silva 	fn = kzalloc(sizeof(struct ocxl_fn), GFP_KERNEL);
3041ba21436SAlastair D'Silva 	if (!fn)
3051ba21436SAlastair D'Silva 		return NULL;
3061ba21436SAlastair D'Silva 
3071ba21436SAlastair D'Silva 	INIT_LIST_HEAD(&fn->afu_list);
3081ba21436SAlastair D'Silva 	INIT_LIST_HEAD(&fn->pasid_list);
3091ba21436SAlastair D'Silva 	INIT_LIST_HEAD(&fn->actag_list);
3102f7d3d14SAlastair D'Silva 
3111ba21436SAlastair D'Silva 	return fn;
3121ba21436SAlastair D'Silva }
3131ba21436SAlastair D'Silva 
free_function(struct ocxl_fn * fn)3141ba21436SAlastair D'Silva static void free_function(struct ocxl_fn *fn)
3151ba21436SAlastair D'Silva {
3161ba21436SAlastair D'Silva 	WARN_ON(!list_empty(&fn->afu_list));
3171ba21436SAlastair D'Silva 	WARN_ON(!list_empty(&fn->pasid_list));
3181ba21436SAlastair D'Silva 	kfree(fn);
3191ba21436SAlastair D'Silva }
3201ba21436SAlastair D'Silva 
free_function_dev(struct device * dev)3211ba21436SAlastair D'Silva static void free_function_dev(struct device *dev)
3221ba21436SAlastair D'Silva {
32375ca758aSAlastair D'Silva 	struct ocxl_fn *fn = container_of(dev, struct ocxl_fn, dev);
3241ba21436SAlastair D'Silva 
3251ba21436SAlastair D'Silva 	free_function(fn);
3261ba21436SAlastair D'Silva }
3271ba21436SAlastair D'Silva 
set_function_device(struct ocxl_fn * fn,struct pci_dev * dev)3281ba21436SAlastair D'Silva static int set_function_device(struct ocxl_fn *fn, struct pci_dev *dev)
3291ba21436SAlastair D'Silva {
3301ba21436SAlastair D'Silva 	fn->dev.parent = &dev->dev;
3311ba21436SAlastair D'Silva 	fn->dev.release = free_function_dev;
332961d289bSQinglang Miao 	return dev_set_name(&fn->dev, "ocxlfn.%s", dev_name(&dev->dev));
3331ba21436SAlastair D'Silva }
3341ba21436SAlastair D'Silva 
assign_function_actag(struct ocxl_fn * fn)3351ba21436SAlastair D'Silva static int assign_function_actag(struct ocxl_fn *fn)
3361ba21436SAlastair D'Silva {
3371ba21436SAlastair D'Silva 	struct pci_dev *dev = to_pci_dev(fn->dev.parent);
3381ba21436SAlastair D'Silva 	u16 base, enabled, supported;
3391ba21436SAlastair D'Silva 	int rc;
3401ba21436SAlastair D'Silva 
3411ba21436SAlastair D'Silva 	rc = ocxl_config_get_actag_info(dev, &base, &enabled, &supported);
3421ba21436SAlastair D'Silva 	if (rc)
3431ba21436SAlastair D'Silva 		return rc;
3441ba21436SAlastair D'Silva 
3451ba21436SAlastair D'Silva 	fn->actag_base = base;
3461ba21436SAlastair D'Silva 	fn->actag_enabled = enabled;
3471ba21436SAlastair D'Silva 	fn->actag_supported = supported;
3481ba21436SAlastair D'Silva 
3491ba21436SAlastair D'Silva 	ocxl_config_set_actag(dev, fn->config.dvsec_function_pos,
3501ba21436SAlastair D'Silva 			fn->actag_base,	fn->actag_enabled);
3511ba21436SAlastair D'Silva 	dev_dbg(&fn->dev, "actag range starting at %d, enabled %d\n",
3521ba21436SAlastair D'Silva 		fn->actag_base, fn->actag_enabled);
3531ba21436SAlastair D'Silva 	return 0;
3541ba21436SAlastair D'Silva }
3551ba21436SAlastair D'Silva 
set_function_pasid(struct ocxl_fn * fn)3561ba21436SAlastair D'Silva static int set_function_pasid(struct ocxl_fn *fn)
3571ba21436SAlastair D'Silva {
3581ba21436SAlastair D'Silva 	struct pci_dev *dev = to_pci_dev(fn->dev.parent);
3591ba21436SAlastair D'Silva 	int rc, desired_count, max_count;
3601ba21436SAlastair D'Silva 
3611ba21436SAlastair D'Silva 	/* A function may not require any PASID */
3621ba21436SAlastair D'Silva 	if (fn->config.max_pasid_log < 0)
3631ba21436SAlastair D'Silva 		return 0;
3641ba21436SAlastair D'Silva 
3651ba21436SAlastair D'Silva 	rc = ocxl_config_get_pasid_info(dev, &max_count);
3661ba21436SAlastair D'Silva 	if (rc)
3671ba21436SAlastair D'Silva 		return rc;
3681ba21436SAlastair D'Silva 
3691ba21436SAlastair D'Silva 	desired_count = 1 << fn->config.max_pasid_log;
3701ba21436SAlastair D'Silva 
3711ba21436SAlastair D'Silva 	if (desired_count > max_count) {
3721ba21436SAlastair D'Silva 		dev_err(&fn->dev,
3731ba21436SAlastair D'Silva 			"Function requires more PASIDs than is available (%d vs. %d)\n",
3741ba21436SAlastair D'Silva 			desired_count, max_count);
3751ba21436SAlastair D'Silva 		return -ENOSPC;
3761ba21436SAlastair D'Silva 	}
3771ba21436SAlastair D'Silva 
3781ba21436SAlastair D'Silva 	fn->pasid_base = 0;
3791ba21436SAlastair D'Silva 	return 0;
3801ba21436SAlastair D'Silva }
3811ba21436SAlastair D'Silva 
configure_function(struct ocxl_fn * fn,struct pci_dev * dev)3821ba21436SAlastair D'Silva static int configure_function(struct ocxl_fn *fn, struct pci_dev *dev)
3831ba21436SAlastair D'Silva {
3841ba21436SAlastair D'Silva 	int rc;
3851ba21436SAlastair D'Silva 
3861ba21436SAlastair D'Silva 	rc = pci_enable_device(dev);
3871ba21436SAlastair D'Silva 	if (rc) {
3881ba21436SAlastair D'Silva 		dev_err(&dev->dev, "pci_enable_device failed: %d\n", rc);
3891ba21436SAlastair D'Silva 		return rc;
3901ba21436SAlastair D'Silva 	}
3911ba21436SAlastair D'Silva 
3921ba21436SAlastair D'Silva 	/*
3931ba21436SAlastair D'Silva 	 * Once it has been confirmed to work on our hardware, we
3941ba21436SAlastair D'Silva 	 * should reset the function, to force the adapter to restart
3951ba21436SAlastair D'Silva 	 * from scratch.
3961ba21436SAlastair D'Silva 	 * A function reset would also reset all its AFUs.
3971ba21436SAlastair D'Silva 	 *
3981ba21436SAlastair D'Silva 	 * Some hints for implementation:
3991ba21436SAlastair D'Silva 	 *
4001ba21436SAlastair D'Silva 	 * - there's not status bit to know when the reset is done. We
4011ba21436SAlastair D'Silva 	 *   should try reading the config space to know when it's
4021ba21436SAlastair D'Silva 	 *   done.
4031ba21436SAlastair D'Silva 	 * - probably something like:
4041ba21436SAlastair D'Silva 	 *	Reset
4051ba21436SAlastair D'Silva 	 *	wait 100ms
4061ba21436SAlastair D'Silva 	 *	issue config read
4071ba21436SAlastair D'Silva 	 *	allow device up to 1 sec to return success on config
4081ba21436SAlastair D'Silva 	 *	read before declaring it broken
4091ba21436SAlastair D'Silva 	 *
4101ba21436SAlastair D'Silva 	 * Some shared logic on the card (CFG, TLX) won't be reset, so
4111ba21436SAlastair D'Silva 	 * there's no guarantee that it will be enough.
4121ba21436SAlastair D'Silva 	 */
4131ba21436SAlastair D'Silva 	rc = ocxl_config_read_function(dev, &fn->config);
4141ba21436SAlastair D'Silva 	if (rc)
4151ba21436SAlastair D'Silva 		return rc;
4161ba21436SAlastair D'Silva 
4171ba21436SAlastair D'Silva 	rc = set_function_device(fn, dev);
4181ba21436SAlastair D'Silva 	if (rc)
4191ba21436SAlastair D'Silva 		return rc;
4201ba21436SAlastair D'Silva 
4211ba21436SAlastair D'Silva 	rc = assign_function_actag(fn);
4221ba21436SAlastair D'Silva 	if (rc)
4231ba21436SAlastair D'Silva 		return rc;
4241ba21436SAlastair D'Silva 
4251ba21436SAlastair D'Silva 	rc = set_function_pasid(fn);
4261ba21436SAlastair D'Silva 	if (rc)
4271ba21436SAlastair D'Silva 		return rc;
4281ba21436SAlastair D'Silva 
4291ba21436SAlastair D'Silva 	rc = ocxl_link_setup(dev, 0, &fn->link);
4301ba21436SAlastair D'Silva 	if (rc)
4311ba21436SAlastair D'Silva 		return rc;
4321ba21436SAlastair D'Silva 
4331ba21436SAlastair D'Silva 	rc = ocxl_config_set_TL(dev, fn->config.dvsec_tl_pos);
4341ba21436SAlastair D'Silva 	if (rc) {
4351ba21436SAlastair D'Silva 		ocxl_link_release(dev, fn->link);
4361ba21436SAlastair D'Silva 		return rc;
4371ba21436SAlastair D'Silva 	}
4381ba21436SAlastair D'Silva 	return 0;
4391ba21436SAlastair D'Silva }
4401ba21436SAlastair D'Silva 
deconfigure_function(struct ocxl_fn * fn)4411ba21436SAlastair D'Silva static void deconfigure_function(struct ocxl_fn *fn)
4421ba21436SAlastair D'Silva {
4431ba21436SAlastair D'Silva 	struct pci_dev *dev = to_pci_dev(fn->dev.parent);
4441ba21436SAlastair D'Silva 
4451ba21436SAlastair D'Silva 	ocxl_link_release(dev, fn->link);
4461ba21436SAlastair D'Silva 	pci_disable_device(dev);
4471ba21436SAlastair D'Silva }
4481ba21436SAlastair D'Silva 
init_function(struct pci_dev * dev)44975ca758aSAlastair D'Silva static struct ocxl_fn *init_function(struct pci_dev *dev)
4501ba21436SAlastair D'Silva {
4511ba21436SAlastair D'Silva 	struct ocxl_fn *fn;
4521ba21436SAlastair D'Silva 	int rc;
4531ba21436SAlastair D'Silva 
4542f7d3d14SAlastair D'Silva 	fn = alloc_function();
4551ba21436SAlastair D'Silva 	if (!fn)
4561ba21436SAlastair D'Silva 		return ERR_PTR(-ENOMEM);
4571ba21436SAlastair D'Silva 
4581ba21436SAlastair D'Silva 	rc = configure_function(fn, dev);
4591ba21436SAlastair D'Silva 	if (rc) {
4601ba21436SAlastair D'Silva 		free_function(fn);
4611ba21436SAlastair D'Silva 		return ERR_PTR(rc);
4621ba21436SAlastair D'Silva 	}
4631ba21436SAlastair D'Silva 
4641ba21436SAlastair D'Silva 	rc = device_register(&fn->dev);
4651ba21436SAlastair D'Silva 	if (rc) {
4661ba21436SAlastair D'Silva 		deconfigure_function(fn);
4671ba21436SAlastair D'Silva 		put_device(&fn->dev);
4681ba21436SAlastair D'Silva 		return ERR_PTR(rc);
4691ba21436SAlastair D'Silva 	}
4701ba21436SAlastair D'Silva 	return fn;
4711ba21436SAlastair D'Silva }
4721ba21436SAlastair D'Silva 
47375ca758aSAlastair D'Silva // Device detection & initialisation
47475ca758aSAlastair D'Silva 
ocxl_function_open(struct pci_dev * dev)47575ca758aSAlastair D'Silva struct ocxl_fn *ocxl_function_open(struct pci_dev *dev)
4761ba21436SAlastair D'Silva {
47775ca758aSAlastair D'Silva 	int rc, afu_count = 0;
47875ca758aSAlastair D'Silva 	u8 afu;
47975ca758aSAlastair D'Silva 	struct ocxl_fn *fn;
48075ca758aSAlastair D'Silva 
48175ca758aSAlastair D'Silva 	if (!radix_enabled()) {
48275ca758aSAlastair D'Silva 		dev_err(&dev->dev, "Unsupported memory model (hash)\n");
48375ca758aSAlastair D'Silva 		return ERR_PTR(-ENODEV);
48475ca758aSAlastair D'Silva 	}
48575ca758aSAlastair D'Silva 
48675ca758aSAlastair D'Silva 	fn = init_function(dev);
48775ca758aSAlastair D'Silva 	if (IS_ERR(fn)) {
48875ca758aSAlastair D'Silva 		dev_err(&dev->dev, "function init failed: %li\n",
48975ca758aSAlastair D'Silva 			PTR_ERR(fn));
49075ca758aSAlastair D'Silva 		return fn;
49175ca758aSAlastair D'Silva 	}
49275ca758aSAlastair D'Silva 
49375ca758aSAlastair D'Silva 	for (afu = 0; afu <= fn->config.max_afu_index; afu++) {
49475ca758aSAlastair D'Silva 		rc = ocxl_config_check_afu_index(dev, &fn->config, afu);
49575ca758aSAlastair D'Silva 		if (rc > 0) {
49675ca758aSAlastair D'Silva 			rc = init_afu(dev, fn, afu);
49775ca758aSAlastair D'Silva 			if (rc) {
49875ca758aSAlastair D'Silva 				dev_err(&dev->dev,
49975ca758aSAlastair D'Silva 					"Can't initialize AFU index %d\n", afu);
50075ca758aSAlastair D'Silva 				continue;
50175ca758aSAlastair D'Silva 			}
50275ca758aSAlastair D'Silva 			afu_count++;
50375ca758aSAlastair D'Silva 		}
50475ca758aSAlastair D'Silva 	}
50575ca758aSAlastair D'Silva 	dev_info(&dev->dev, "%d AFU(s) configured\n", afu_count);
50675ca758aSAlastair D'Silva 	return fn;
50775ca758aSAlastair D'Silva }
50875ca758aSAlastair D'Silva EXPORT_SYMBOL_GPL(ocxl_function_open);
50975ca758aSAlastair D'Silva 
ocxl_function_afu_list(struct ocxl_fn * fn)51075ca758aSAlastair D'Silva struct list_head *ocxl_function_afu_list(struct ocxl_fn *fn)
51175ca758aSAlastair D'Silva {
51275ca758aSAlastair D'Silva 	return &fn->afu_list;
51375ca758aSAlastair D'Silva }
51475ca758aSAlastair D'Silva EXPORT_SYMBOL_GPL(ocxl_function_afu_list);
51575ca758aSAlastair D'Silva 
ocxl_function_fetch_afu(struct ocxl_fn * fn,u8 afu_idx)51675ca758aSAlastair D'Silva struct ocxl_afu *ocxl_function_fetch_afu(struct ocxl_fn *fn, u8 afu_idx)
51775ca758aSAlastair D'Silva {
51875ca758aSAlastair D'Silva 	struct ocxl_afu *afu;
51975ca758aSAlastair D'Silva 
52075ca758aSAlastair D'Silva 	list_for_each_entry(afu, &fn->afu_list, list) {
52175ca758aSAlastair D'Silva 		if (afu->config.idx == afu_idx)
52275ca758aSAlastair D'Silva 			return afu;
52375ca758aSAlastair D'Silva 	}
52475ca758aSAlastair D'Silva 
52575ca758aSAlastair D'Silva 	return NULL;
52675ca758aSAlastair D'Silva }
52775ca758aSAlastair D'Silva EXPORT_SYMBOL_GPL(ocxl_function_fetch_afu);
52875ca758aSAlastair D'Silva 
ocxl_function_config(struct ocxl_fn * fn)52975ca758aSAlastair D'Silva const struct ocxl_fn_config *ocxl_function_config(struct ocxl_fn *fn)
53075ca758aSAlastair D'Silva {
53175ca758aSAlastair D'Silva 	return &fn->config;
53275ca758aSAlastair D'Silva }
53375ca758aSAlastair D'Silva EXPORT_SYMBOL_GPL(ocxl_function_config);
53475ca758aSAlastair D'Silva 
ocxl_function_close(struct ocxl_fn * fn)53575ca758aSAlastair D'Silva void ocxl_function_close(struct ocxl_fn *fn)
53675ca758aSAlastair D'Silva {
53775ca758aSAlastair D'Silva 	struct ocxl_afu *afu, *tmp;
53875ca758aSAlastair D'Silva 
53975ca758aSAlastair D'Silva 	list_for_each_entry_safe(afu, tmp, &fn->afu_list, list) {
54075ca758aSAlastair D'Silva 		remove_afu(afu);
54175ca758aSAlastair D'Silva 	}
54275ca758aSAlastair D'Silva 
5431ba21436SAlastair D'Silva 	deconfigure_function(fn);
5441ba21436SAlastair D'Silva 	device_unregister(&fn->dev);
5451ba21436SAlastair D'Silva }
54675ca758aSAlastair D'Silva EXPORT_SYMBOL_GPL(ocxl_function_close);
54775ca758aSAlastair D'Silva 
54875ca758aSAlastair D'Silva // AFU Metadata
54975ca758aSAlastair D'Silva 
ocxl_afu_config(struct ocxl_afu * afu)55075ca758aSAlastair D'Silva struct ocxl_afu_config *ocxl_afu_config(struct ocxl_afu *afu)
55175ca758aSAlastair D'Silva {
55275ca758aSAlastair D'Silva 	return &afu->config;
55375ca758aSAlastair D'Silva }
55475ca758aSAlastair D'Silva EXPORT_SYMBOL_GPL(ocxl_afu_config);
55575ca758aSAlastair D'Silva 
ocxl_afu_set_private(struct ocxl_afu * afu,void * private)55675ca758aSAlastair D'Silva void ocxl_afu_set_private(struct ocxl_afu *afu, void *private)
55775ca758aSAlastair D'Silva {
55875ca758aSAlastair D'Silva 	afu->private = private;
55975ca758aSAlastair D'Silva }
56075ca758aSAlastair D'Silva EXPORT_SYMBOL_GPL(ocxl_afu_set_private);
56175ca758aSAlastair D'Silva 
ocxl_afu_get_private(struct ocxl_afu * afu)56275ca758aSAlastair D'Silva void *ocxl_afu_get_private(struct ocxl_afu *afu)
56375ca758aSAlastair D'Silva {
56475ca758aSAlastair D'Silva 	if (afu)
56575ca758aSAlastair D'Silva 		return afu->private;
56675ca758aSAlastair D'Silva 
56775ca758aSAlastair D'Silva 	return NULL;
56875ca758aSAlastair D'Silva }
56975ca758aSAlastair D'Silva EXPORT_SYMBOL_GPL(ocxl_afu_get_private);
570