155435ea7SShannon Nelson // SPDX-License-Identifier: GPL-2.0
255435ea7SShannon Nelson /* Copyright(c) 2023 Advanced Micro Devices, Inc */
355435ea7SShannon Nelson 
455435ea7SShannon Nelson #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
555435ea7SShannon Nelson 
655435ea7SShannon Nelson #include <linux/pci.h>
755435ea7SShannon Nelson 
855435ea7SShannon Nelson #include <linux/pds/pds_common.h>
955435ea7SShannon Nelson 
1055435ea7SShannon Nelson #include "core.h"
1155435ea7SShannon Nelson 
1255435ea7SShannon Nelson MODULE_DESCRIPTION(PDSC_DRV_DESCRIPTION);
1355435ea7SShannon Nelson MODULE_AUTHOR("Advanced Micro Devices, Inc");
1455435ea7SShannon Nelson MODULE_LICENSE("GPL");
1555435ea7SShannon Nelson 
1655435ea7SShannon Nelson /* Supported devices */
1755435ea7SShannon Nelson static const struct pci_device_id pdsc_id_table[] = {
1855435ea7SShannon Nelson 	{ PCI_VDEVICE(PENSANDO, PCI_DEVICE_ID_PENSANDO_CORE_PF) },
19f53d9311SShannon Nelson 	{ PCI_VDEVICE(PENSANDO, PCI_DEVICE_ID_PENSANDO_VDPA_VF) },
2055435ea7SShannon Nelson 	{ 0, }	/* end of table */
2155435ea7SShannon Nelson };
2255435ea7SShannon Nelson MODULE_DEVICE_TABLE(pci, pdsc_id_table);
2355435ea7SShannon Nelson 
24c2dbb090SShannon Nelson static void pdsc_wdtimer_cb(struct timer_list *t)
25c2dbb090SShannon Nelson {
26c2dbb090SShannon Nelson 	struct pdsc *pdsc = from_timer(pdsc, t, wdtimer);
27c2dbb090SShannon Nelson 
28c2dbb090SShannon Nelson 	dev_dbg(pdsc->dev, "%s: jiffies %ld\n", __func__, jiffies);
29c2dbb090SShannon Nelson 	mod_timer(&pdsc->wdtimer,
30c2dbb090SShannon Nelson 		  round_jiffies(jiffies + pdsc->wdtimer_period));
31c2dbb090SShannon Nelson 
32c2dbb090SShannon Nelson 	queue_work(pdsc->wq, &pdsc->health_work);
33c2dbb090SShannon Nelson }
34c2dbb090SShannon Nelson 
3555435ea7SShannon Nelson static void pdsc_unmap_bars(struct pdsc *pdsc)
3655435ea7SShannon Nelson {
3755435ea7SShannon Nelson 	struct pdsc_dev_bar *bars = pdsc->bars;
3855435ea7SShannon Nelson 	unsigned int i;
3955435ea7SShannon Nelson 
4055435ea7SShannon Nelson 	for (i = 0; i < PDS_CORE_BARS_MAX; i++) {
4155435ea7SShannon Nelson 		if (bars[i].vaddr)
4255435ea7SShannon Nelson 			pci_iounmap(pdsc->pdev, bars[i].vaddr);
4355435ea7SShannon Nelson 	}
4455435ea7SShannon Nelson }
4555435ea7SShannon Nelson 
4655435ea7SShannon Nelson static int pdsc_map_bars(struct pdsc *pdsc)
4755435ea7SShannon Nelson {
4855435ea7SShannon Nelson 	struct pdsc_dev_bar *bar = pdsc->bars;
4955435ea7SShannon Nelson 	struct pci_dev *pdev = pdsc->pdev;
5055435ea7SShannon Nelson 	struct device *dev = pdsc->dev;
5155435ea7SShannon Nelson 	struct pdsc_dev_bar *bars;
5255435ea7SShannon Nelson 	unsigned int i, j;
5355435ea7SShannon Nelson 	int num_bars = 0;
5455435ea7SShannon Nelson 	int err;
5555435ea7SShannon Nelson 	u32 sig;
5655435ea7SShannon Nelson 
5755435ea7SShannon Nelson 	bars = pdsc->bars;
5855435ea7SShannon Nelson 
5955435ea7SShannon Nelson 	/* Since the PCI interface in the hardware is configurable,
6055435ea7SShannon Nelson 	 * we need to poke into all the bars to find the set we're
6155435ea7SShannon Nelson 	 * expecting.
6255435ea7SShannon Nelson 	 */
6355435ea7SShannon Nelson 	for (i = 0, j = 0; i < PDS_CORE_BARS_MAX; i++) {
6455435ea7SShannon Nelson 		if (!(pci_resource_flags(pdev, i) & IORESOURCE_MEM))
6555435ea7SShannon Nelson 			continue;
6655435ea7SShannon Nelson 
6755435ea7SShannon Nelson 		bars[j].len = pci_resource_len(pdev, i);
6855435ea7SShannon Nelson 		bars[j].bus_addr = pci_resource_start(pdev, i);
6955435ea7SShannon Nelson 		bars[j].res_index = i;
7055435ea7SShannon Nelson 
7155435ea7SShannon Nelson 		/* only map the whole bar 0 */
7255435ea7SShannon Nelson 		if (j > 0) {
7355435ea7SShannon Nelson 			bars[j].vaddr = NULL;
7455435ea7SShannon Nelson 		} else {
7555435ea7SShannon Nelson 			bars[j].vaddr = pci_iomap(pdev, i, bars[j].len);
7655435ea7SShannon Nelson 			if (!bars[j].vaddr) {
7755435ea7SShannon Nelson 				dev_err(dev, "Cannot map BAR %d, aborting\n", i);
7855435ea7SShannon Nelson 				return -ENODEV;
7955435ea7SShannon Nelson 			}
8055435ea7SShannon Nelson 		}
8155435ea7SShannon Nelson 
8255435ea7SShannon Nelson 		j++;
8355435ea7SShannon Nelson 	}
8455435ea7SShannon Nelson 	num_bars = j;
8555435ea7SShannon Nelson 
8655435ea7SShannon Nelson 	/* BAR0: dev_cmd and interrupts */
8755435ea7SShannon Nelson 	if (num_bars < 1) {
8855435ea7SShannon Nelson 		dev_err(dev, "No bars found\n");
8955435ea7SShannon Nelson 		err = -EFAULT;
9055435ea7SShannon Nelson 		goto err_out;
9155435ea7SShannon Nelson 	}
9255435ea7SShannon Nelson 
9355435ea7SShannon Nelson 	if (bar->len < PDS_CORE_BAR0_SIZE) {
9455435ea7SShannon Nelson 		dev_err(dev, "Resource bar size %lu too small\n", bar->len);
9555435ea7SShannon Nelson 		err = -EFAULT;
9655435ea7SShannon Nelson 		goto err_out;
9755435ea7SShannon Nelson 	}
9855435ea7SShannon Nelson 
9955435ea7SShannon Nelson 	pdsc->info_regs = bar->vaddr + PDS_CORE_BAR0_DEV_INFO_REGS_OFFSET;
10055435ea7SShannon Nelson 	pdsc->cmd_regs = bar->vaddr + PDS_CORE_BAR0_DEV_CMD_REGS_OFFSET;
10155435ea7SShannon Nelson 	pdsc->intr_status = bar->vaddr + PDS_CORE_BAR0_INTR_STATUS_OFFSET;
10255435ea7SShannon Nelson 	pdsc->intr_ctrl = bar->vaddr + PDS_CORE_BAR0_INTR_CTRL_OFFSET;
10355435ea7SShannon Nelson 
10455435ea7SShannon Nelson 	sig = ioread32(&pdsc->info_regs->signature);
10555435ea7SShannon Nelson 	if (sig != PDS_CORE_DEV_INFO_SIGNATURE) {
10655435ea7SShannon Nelson 		dev_err(dev, "Incompatible firmware signature %x", sig);
10755435ea7SShannon Nelson 		err = -EFAULT;
10855435ea7SShannon Nelson 		goto err_out;
10955435ea7SShannon Nelson 	}
11055435ea7SShannon Nelson 
11155435ea7SShannon Nelson 	/* BAR1: doorbells */
11255435ea7SShannon Nelson 	bar++;
11355435ea7SShannon Nelson 	if (num_bars < 2) {
11455435ea7SShannon Nelson 		dev_err(dev, "Doorbell bar missing\n");
11555435ea7SShannon Nelson 		err = -EFAULT;
11655435ea7SShannon Nelson 		goto err_out;
11755435ea7SShannon Nelson 	}
11855435ea7SShannon Nelson 
11955435ea7SShannon Nelson 	pdsc->db_pages = bar->vaddr;
12055435ea7SShannon Nelson 	pdsc->phy_db_pages = bar->bus_addr;
12155435ea7SShannon Nelson 
12255435ea7SShannon Nelson 	return 0;
12355435ea7SShannon Nelson 
12455435ea7SShannon Nelson err_out:
12555435ea7SShannon Nelson 	pdsc_unmap_bars(pdsc);
12655435ea7SShannon Nelson 	return err;
12755435ea7SShannon Nelson }
12855435ea7SShannon Nelson 
12945d76f49SShannon Nelson void __iomem *pdsc_map_dbpage(struct pdsc *pdsc, int page_num)
13045d76f49SShannon Nelson {
13145d76f49SShannon Nelson 	return pci_iomap_range(pdsc->pdev,
13245d76f49SShannon Nelson 			       pdsc->bars[PDS_CORE_PCI_BAR_DBELL].res_index,
13345d76f49SShannon Nelson 			       (u64)page_num << PAGE_SHIFT, PAGE_SIZE);
13445d76f49SShannon Nelson }
13545d76f49SShannon Nelson 
136f53d9311SShannon Nelson static int pdsc_sriov_configure(struct pci_dev *pdev, int num_vfs)
137f53d9311SShannon Nelson {
138f53d9311SShannon Nelson 	struct pdsc *pdsc = pci_get_drvdata(pdev);
139f53d9311SShannon Nelson 	struct device *dev = pdsc->dev;
140f53d9311SShannon Nelson 	int ret = 0;
141f53d9311SShannon Nelson 
142f53d9311SShannon Nelson 	if (num_vfs > 0) {
143f53d9311SShannon Nelson 		pdsc->vfs = kcalloc(num_vfs, sizeof(struct pdsc_vf),
144f53d9311SShannon Nelson 				    GFP_KERNEL);
145f53d9311SShannon Nelson 		if (!pdsc->vfs)
146f53d9311SShannon Nelson 			return -ENOMEM;
147f53d9311SShannon Nelson 		pdsc->num_vfs = num_vfs;
148f53d9311SShannon Nelson 
149f53d9311SShannon Nelson 		ret = pci_enable_sriov(pdev, num_vfs);
150f53d9311SShannon Nelson 		if (ret) {
151f53d9311SShannon Nelson 			dev_err(dev, "Cannot enable SRIOV: %pe\n",
152f53d9311SShannon Nelson 				ERR_PTR(ret));
153f53d9311SShannon Nelson 			goto no_vfs;
154f53d9311SShannon Nelson 		}
155f53d9311SShannon Nelson 
156f53d9311SShannon Nelson 		return num_vfs;
157f53d9311SShannon Nelson 	}
158f53d9311SShannon Nelson 
159f53d9311SShannon Nelson no_vfs:
160f53d9311SShannon Nelson 	pci_disable_sriov(pdev);
161f53d9311SShannon Nelson 
162f53d9311SShannon Nelson 	kfree(pdsc->vfs);
163f53d9311SShannon Nelson 	pdsc->vfs = NULL;
164f53d9311SShannon Nelson 	pdsc->num_vfs = 0;
165f53d9311SShannon Nelson 
166f53d9311SShannon Nelson 	return ret;
167f53d9311SShannon Nelson }
168f53d9311SShannon Nelson 
16955435ea7SShannon Nelson static int pdsc_init_vf(struct pdsc *vf)
17055435ea7SShannon Nelson {
171f53d9311SShannon Nelson 	struct devlink *dl;
172*4569cce4SShannon Nelson 	struct pdsc *pf;
173*4569cce4SShannon Nelson 	int err;
174*4569cce4SShannon Nelson 
175*4569cce4SShannon Nelson 	pf = pdsc_get_pf_struct(vf->pdev);
176*4569cce4SShannon Nelson 	if (IS_ERR_OR_NULL(pf))
177*4569cce4SShannon Nelson 		return PTR_ERR(pf) ?: -1;
178f53d9311SShannon Nelson 
179f53d9311SShannon Nelson 	vf->vf_id = pci_iov_vf_id(vf->pdev);
180f53d9311SShannon Nelson 
181f53d9311SShannon Nelson 	dl = priv_to_devlink(vf);
182f53d9311SShannon Nelson 	devl_lock(dl);
183f53d9311SShannon Nelson 	devl_register(dl);
184f53d9311SShannon Nelson 	devl_unlock(dl);
185f53d9311SShannon Nelson 
186*4569cce4SShannon Nelson 	pf->vfs[vf->vf_id].vf = vf;
187*4569cce4SShannon Nelson 	err = pdsc_auxbus_dev_add(vf, pf);
188*4569cce4SShannon Nelson 	if (err) {
189*4569cce4SShannon Nelson 		devl_lock(dl);
190*4569cce4SShannon Nelson 		devl_unregister(dl);
191*4569cce4SShannon Nelson 		devl_unlock(dl);
192*4569cce4SShannon Nelson 	}
193*4569cce4SShannon Nelson 
194*4569cce4SShannon Nelson 	return err;
19555435ea7SShannon Nelson }
19655435ea7SShannon Nelson 
19725b450c0SShannon Nelson static const struct devlink_health_reporter_ops pdsc_fw_reporter_ops = {
19825b450c0SShannon Nelson 	.name = "fw",
19925b450c0SShannon Nelson 	.diagnose = pdsc_fw_reporter_diagnose,
20025b450c0SShannon Nelson };
20125b450c0SShannon Nelson 
202c2dbb090SShannon Nelson #define PDSC_WQ_NAME_LEN 24
203c2dbb090SShannon Nelson 
20455435ea7SShannon Nelson static int pdsc_init_pf(struct pdsc *pdsc)
20555435ea7SShannon Nelson {
20625b450c0SShannon Nelson 	struct devlink_health_reporter *hr;
207c2dbb090SShannon Nelson 	char wq_name[PDSC_WQ_NAME_LEN];
20855435ea7SShannon Nelson 	struct devlink *dl;
20955435ea7SShannon Nelson 	int err;
21055435ea7SShannon Nelson 
21155435ea7SShannon Nelson 	pcie_print_link_status(pdsc->pdev);
21255435ea7SShannon Nelson 
21355435ea7SShannon Nelson 	err = pci_request_regions(pdsc->pdev, PDS_CORE_DRV_NAME);
21455435ea7SShannon Nelson 	if (err) {
21555435ea7SShannon Nelson 		dev_err(pdsc->dev, "Cannot request PCI regions: %pe\n",
21655435ea7SShannon Nelson 			ERR_PTR(err));
21755435ea7SShannon Nelson 		return err;
21855435ea7SShannon Nelson 	}
21955435ea7SShannon Nelson 
22055435ea7SShannon Nelson 	err = pdsc_map_bars(pdsc);
22155435ea7SShannon Nelson 	if (err)
22255435ea7SShannon Nelson 		goto err_out_release_regions;
22355435ea7SShannon Nelson 
224c2dbb090SShannon Nelson 	/* General workqueue and timer, but don't start timer yet */
225c2dbb090SShannon Nelson 	snprintf(wq_name, sizeof(wq_name), "%s.%d", PDS_CORE_DRV_NAME, pdsc->uid);
226c2dbb090SShannon Nelson 	pdsc->wq = create_singlethread_workqueue(wq_name);
227c2dbb090SShannon Nelson 	INIT_WORK(&pdsc->health_work, pdsc_health_thread);
228c2dbb090SShannon Nelson 	timer_setup(&pdsc->wdtimer, pdsc_wdtimer_cb, 0);
229c2dbb090SShannon Nelson 	pdsc->wdtimer_period = PDSC_WATCHDOG_SECS * HZ;
230c2dbb090SShannon Nelson 
231523847dfSShannon Nelson 	mutex_init(&pdsc->devcmd_lock);
232523847dfSShannon Nelson 	mutex_init(&pdsc->config_lock);
23345d76f49SShannon Nelson 	spin_lock_init(&pdsc->adminq_lock);
234523847dfSShannon Nelson 
235523847dfSShannon Nelson 	mutex_lock(&pdsc->config_lock);
236523847dfSShannon Nelson 	set_bit(PDSC_S_FW_DEAD, &pdsc->state);
237523847dfSShannon Nelson 
238523847dfSShannon Nelson 	err = pdsc_setup(pdsc, PDSC_SETUP_INIT);
239523847dfSShannon Nelson 	if (err)
240523847dfSShannon Nelson 		goto err_out_unmap_bars;
24145d76f49SShannon Nelson 	err = pdsc_start(pdsc);
24245d76f49SShannon Nelson 	if (err)
24345d76f49SShannon Nelson 		goto err_out_teardown;
244523847dfSShannon Nelson 
245523847dfSShannon Nelson 	mutex_unlock(&pdsc->config_lock);
246523847dfSShannon Nelson 
24755435ea7SShannon Nelson 	dl = priv_to_devlink(pdsc);
24855435ea7SShannon Nelson 	devl_lock(dl);
24925b450c0SShannon Nelson 
25025b450c0SShannon Nelson 	hr = devl_health_reporter_create(dl, &pdsc_fw_reporter_ops, 0, pdsc);
25125b450c0SShannon Nelson 	if (IS_ERR(hr)) {
25225b450c0SShannon Nelson 		dev_warn(pdsc->dev, "Failed to create fw reporter: %pe\n", hr);
25325b450c0SShannon Nelson 		err = PTR_ERR(hr);
25425b450c0SShannon Nelson 		devl_unlock(dl);
25545d76f49SShannon Nelson 		goto err_out_stop;
25625b450c0SShannon Nelson 	}
25725b450c0SShannon Nelson 	pdsc->fw_reporter = hr;
25825b450c0SShannon Nelson 
25955435ea7SShannon Nelson 	devl_register(dl);
26055435ea7SShannon Nelson 	devl_unlock(dl);
26155435ea7SShannon Nelson 
262c2dbb090SShannon Nelson 	/* Lastly, start the health check timer */
263c2dbb090SShannon Nelson 	mod_timer(&pdsc->wdtimer, round_jiffies(jiffies + pdsc->wdtimer_period));
264c2dbb090SShannon Nelson 
26555435ea7SShannon Nelson 	return 0;
26655435ea7SShannon Nelson 
26745d76f49SShannon Nelson err_out_stop:
26845d76f49SShannon Nelson 	pdsc_stop(pdsc);
26925b450c0SShannon Nelson err_out_teardown:
27025b450c0SShannon Nelson 	pdsc_teardown(pdsc, PDSC_TEARDOWN_REMOVING);
271523847dfSShannon Nelson err_out_unmap_bars:
272523847dfSShannon Nelson 	mutex_unlock(&pdsc->config_lock);
273c2dbb090SShannon Nelson 	del_timer_sync(&pdsc->wdtimer);
274c2dbb090SShannon Nelson 	if (pdsc->wq)
275c2dbb090SShannon Nelson 		destroy_workqueue(pdsc->wq);
276523847dfSShannon Nelson 	mutex_destroy(&pdsc->config_lock);
277523847dfSShannon Nelson 	mutex_destroy(&pdsc->devcmd_lock);
278523847dfSShannon Nelson 	pci_free_irq_vectors(pdsc->pdev);
279523847dfSShannon Nelson 	pdsc_unmap_bars(pdsc);
28055435ea7SShannon Nelson err_out_release_regions:
28155435ea7SShannon Nelson 	pci_release_regions(pdsc->pdev);
28255435ea7SShannon Nelson 
28355435ea7SShannon Nelson 	return err;
28455435ea7SShannon Nelson }
28555435ea7SShannon Nelson 
28655435ea7SShannon Nelson static const struct devlink_ops pdsc_dl_ops = {
28745d76f49SShannon Nelson 	.info_get	= pdsc_dl_info_get,
28849ce92fbSShannon Nelson 	.flash_update	= pdsc_dl_flash_update,
28955435ea7SShannon Nelson };
29055435ea7SShannon Nelson 
29155435ea7SShannon Nelson static const struct devlink_ops pdsc_dl_vf_ops = {
29255435ea7SShannon Nelson };
29355435ea7SShannon Nelson 
29455435ea7SShannon Nelson static DEFINE_IDA(pdsc_ida);
29555435ea7SShannon Nelson 
29655435ea7SShannon Nelson static int pdsc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
29755435ea7SShannon Nelson {
29855435ea7SShannon Nelson 	struct device *dev = &pdev->dev;
29955435ea7SShannon Nelson 	const struct devlink_ops *ops;
30055435ea7SShannon Nelson 	struct devlink *dl;
30155435ea7SShannon Nelson 	struct pdsc *pdsc;
30255435ea7SShannon Nelson 	bool is_pf;
30355435ea7SShannon Nelson 	int err;
30455435ea7SShannon Nelson 
30555435ea7SShannon Nelson 	is_pf = !pdev->is_virtfn;
30655435ea7SShannon Nelson 	ops = is_pf ? &pdsc_dl_ops : &pdsc_dl_vf_ops;
30755435ea7SShannon Nelson 	dl = devlink_alloc(ops, sizeof(struct pdsc), dev);
30855435ea7SShannon Nelson 	if (!dl)
30955435ea7SShannon Nelson 		return -ENOMEM;
31055435ea7SShannon Nelson 	pdsc = devlink_priv(dl);
31155435ea7SShannon Nelson 
31255435ea7SShannon Nelson 	pdsc->pdev = pdev;
31355435ea7SShannon Nelson 	pdsc->dev = &pdev->dev;
31455435ea7SShannon Nelson 	set_bit(PDSC_S_INITING_DRIVER, &pdsc->state);
31555435ea7SShannon Nelson 	pci_set_drvdata(pdev, pdsc);
31655435ea7SShannon Nelson 	pdsc_debugfs_add_dev(pdsc);
31755435ea7SShannon Nelson 
31855435ea7SShannon Nelson 	err = ida_alloc(&pdsc_ida, GFP_KERNEL);
31955435ea7SShannon Nelson 	if (err < 0) {
32055435ea7SShannon Nelson 		dev_err(pdsc->dev, "%s: id alloc failed: %pe\n",
32155435ea7SShannon Nelson 			__func__, ERR_PTR(err));
32255435ea7SShannon Nelson 		goto err_out_free_devlink;
32355435ea7SShannon Nelson 	}
32455435ea7SShannon Nelson 	pdsc->uid = err;
32555435ea7SShannon Nelson 
32655435ea7SShannon Nelson 	/* Query system for DMA addressing limitation for the device. */
32755435ea7SShannon Nelson 	err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(PDS_CORE_ADDR_LEN));
32855435ea7SShannon Nelson 	if (err) {
32955435ea7SShannon Nelson 		dev_err(dev, "Unable to obtain 64-bit DMA for consistent allocations, aborting: %pe\n",
33055435ea7SShannon Nelson 			ERR_PTR(err));
33155435ea7SShannon Nelson 		goto err_out_free_ida;
33255435ea7SShannon Nelson 	}
33355435ea7SShannon Nelson 
33455435ea7SShannon Nelson 	err = pci_enable_device(pdev);
33555435ea7SShannon Nelson 	if (err) {
33655435ea7SShannon Nelson 		dev_err(dev, "Cannot enable PCI device: %pe\n", ERR_PTR(err));
33755435ea7SShannon Nelson 		goto err_out_free_ida;
33855435ea7SShannon Nelson 	}
33955435ea7SShannon Nelson 	pci_set_master(pdev);
34055435ea7SShannon Nelson 
34155435ea7SShannon Nelson 	if (is_pf)
34255435ea7SShannon Nelson 		err = pdsc_init_pf(pdsc);
34355435ea7SShannon Nelson 	else
34455435ea7SShannon Nelson 		err = pdsc_init_vf(pdsc);
34555435ea7SShannon Nelson 	if (err) {
34655435ea7SShannon Nelson 		dev_err(dev, "Cannot init device: %pe\n", ERR_PTR(err));
34755435ea7SShannon Nelson 		goto err_out_clear_master;
34855435ea7SShannon Nelson 	}
34955435ea7SShannon Nelson 
35055435ea7SShannon Nelson 	clear_bit(PDSC_S_INITING_DRIVER, &pdsc->state);
35155435ea7SShannon Nelson 	return 0;
35255435ea7SShannon Nelson 
35355435ea7SShannon Nelson err_out_clear_master:
35455435ea7SShannon Nelson 	pci_clear_master(pdev);
35555435ea7SShannon Nelson 	pci_disable_device(pdev);
35655435ea7SShannon Nelson err_out_free_ida:
35755435ea7SShannon Nelson 	ida_free(&pdsc_ida, pdsc->uid);
35855435ea7SShannon Nelson err_out_free_devlink:
35955435ea7SShannon Nelson 	pdsc_debugfs_del_dev(pdsc);
36055435ea7SShannon Nelson 	devlink_free(dl);
36155435ea7SShannon Nelson 
36255435ea7SShannon Nelson 	return err;
36355435ea7SShannon Nelson }
36455435ea7SShannon Nelson 
36555435ea7SShannon Nelson static void pdsc_remove(struct pci_dev *pdev)
36655435ea7SShannon Nelson {
36755435ea7SShannon Nelson 	struct pdsc *pdsc = pci_get_drvdata(pdev);
36855435ea7SShannon Nelson 	struct devlink *dl;
36955435ea7SShannon Nelson 
37055435ea7SShannon Nelson 	/* Unhook the registrations first to be sure there
37155435ea7SShannon Nelson 	 * are no requests while we're stopping.
37255435ea7SShannon Nelson 	 */
37355435ea7SShannon Nelson 	dl = priv_to_devlink(pdsc);
37455435ea7SShannon Nelson 	devl_lock(dl);
37555435ea7SShannon Nelson 	devl_unregister(dl);
37625b450c0SShannon Nelson 	if (pdsc->fw_reporter) {
37725b450c0SShannon Nelson 		devl_health_reporter_destroy(pdsc->fw_reporter);
37825b450c0SShannon Nelson 		pdsc->fw_reporter = NULL;
37925b450c0SShannon Nelson 	}
38055435ea7SShannon Nelson 	devl_unlock(dl);
38155435ea7SShannon Nelson 
382*4569cce4SShannon Nelson 	if (pdev->is_virtfn) {
383*4569cce4SShannon Nelson 		struct pdsc *pf;
384*4569cce4SShannon Nelson 
385*4569cce4SShannon Nelson 		pf = pdsc_get_pf_struct(pdsc->pdev);
386*4569cce4SShannon Nelson 		if (!IS_ERR(pf)) {
387*4569cce4SShannon Nelson 			pdsc_auxbus_dev_del(pdsc, pf);
388*4569cce4SShannon Nelson 			pf->vfs[pdsc->vf_id].vf = NULL;
389*4569cce4SShannon Nelson 		}
390*4569cce4SShannon Nelson 	} else {
391*4569cce4SShannon Nelson 		/* Remove the VFs and their aux_bus connections before other
392*4569cce4SShannon Nelson 		 * cleanup so that the clients can use the AdminQ to cleanly
393*4569cce4SShannon Nelson 		 * shut themselves down.
394*4569cce4SShannon Nelson 		 */
395f53d9311SShannon Nelson 		pdsc_sriov_configure(pdev, 0);
396f53d9311SShannon Nelson 
397c2dbb090SShannon Nelson 		del_timer_sync(&pdsc->wdtimer);
398c2dbb090SShannon Nelson 		if (pdsc->wq)
399c2dbb090SShannon Nelson 			destroy_workqueue(pdsc->wq);
400c2dbb090SShannon Nelson 
401523847dfSShannon Nelson 		mutex_lock(&pdsc->config_lock);
402523847dfSShannon Nelson 		set_bit(PDSC_S_STOPPING_DRIVER, &pdsc->state);
403523847dfSShannon Nelson 
40445d76f49SShannon Nelson 		pdsc_stop(pdsc);
405523847dfSShannon Nelson 		pdsc_teardown(pdsc, PDSC_TEARDOWN_REMOVING);
406523847dfSShannon Nelson 		mutex_unlock(&pdsc->config_lock);
407523847dfSShannon Nelson 		mutex_destroy(&pdsc->config_lock);
408523847dfSShannon Nelson 		mutex_destroy(&pdsc->devcmd_lock);
409523847dfSShannon Nelson 
410523847dfSShannon Nelson 		pci_free_irq_vectors(pdev);
41155435ea7SShannon Nelson 		pdsc_unmap_bars(pdsc);
41255435ea7SShannon Nelson 		pci_release_regions(pdev);
413523847dfSShannon Nelson 	}
41455435ea7SShannon Nelson 
41555435ea7SShannon Nelson 	pci_clear_master(pdev);
41655435ea7SShannon Nelson 	pci_disable_device(pdev);
41755435ea7SShannon Nelson 
41855435ea7SShannon Nelson 	ida_free(&pdsc_ida, pdsc->uid);
41955435ea7SShannon Nelson 	pdsc_debugfs_del_dev(pdsc);
42055435ea7SShannon Nelson 	devlink_free(dl);
42155435ea7SShannon Nelson }
42255435ea7SShannon Nelson 
42355435ea7SShannon Nelson static struct pci_driver pdsc_driver = {
42455435ea7SShannon Nelson 	.name = PDS_CORE_DRV_NAME,
42555435ea7SShannon Nelson 	.id_table = pdsc_id_table,
42655435ea7SShannon Nelson 	.probe = pdsc_probe,
42755435ea7SShannon Nelson 	.remove = pdsc_remove,
428f53d9311SShannon Nelson 	.sriov_configure = pdsc_sriov_configure,
42955435ea7SShannon Nelson };
43055435ea7SShannon Nelson 
431*4569cce4SShannon Nelson void *pdsc_get_pf_struct(struct pci_dev *vf_pdev)
432*4569cce4SShannon Nelson {
433*4569cce4SShannon Nelson 	return pci_iov_get_pf_drvdata(vf_pdev, &pdsc_driver);
434*4569cce4SShannon Nelson }
435*4569cce4SShannon Nelson EXPORT_SYMBOL_GPL(pdsc_get_pf_struct);
436*4569cce4SShannon Nelson 
43755435ea7SShannon Nelson static int __init pdsc_init_module(void)
43855435ea7SShannon Nelson {
43955435ea7SShannon Nelson 	if (strcmp(KBUILD_MODNAME, PDS_CORE_DRV_NAME))
44055435ea7SShannon Nelson 		return -EINVAL;
44155435ea7SShannon Nelson 
44255435ea7SShannon Nelson 	pdsc_debugfs_create();
44355435ea7SShannon Nelson 	return pci_register_driver(&pdsc_driver);
44455435ea7SShannon Nelson }
44555435ea7SShannon Nelson 
44655435ea7SShannon Nelson static void __exit pdsc_cleanup_module(void)
44755435ea7SShannon Nelson {
44855435ea7SShannon Nelson 	pci_unregister_driver(&pdsc_driver);
44955435ea7SShannon Nelson 	pdsc_debugfs_destroy();
45055435ea7SShannon Nelson }
45155435ea7SShannon Nelson 
45255435ea7SShannon Nelson module_init(pdsc_init_module);
45355435ea7SShannon Nelson module_exit(pdsc_cleanup_module);
454