1 /*
2  * Copyright 2015 Red Hat Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * Authors: Ben Skeggs <bskeggs@redhat.com>
23  */
24 #include <core/pci.h>
25 #include "priv.h"
26 
27 static struct nvkm_device_pci *
28 nvkm_device_pci(struct nvkm_device *device)
29 {
30 	return container_of(device, struct nvkm_device_pci, device);
31 }
32 
33 static void
34 nvkm_device_pci_fini(struct nvkm_device *device, bool suspend)
35 {
36 	struct nvkm_device_pci *pdev = nvkm_device_pci(device);
37 	if (suspend) {
38 		pci_disable_device(pdev->pdev);
39 		pdev->suspend = true;
40 	}
41 }
42 
43 static int
44 nvkm_device_pci_preinit(struct nvkm_device *device)
45 {
46 	struct nvkm_device_pci *pdev = nvkm_device_pci(device);
47 	if (pdev->suspend) {
48 		int ret = pci_enable_device(pdev->pdev);
49 		if (ret)
50 			return ret;
51 		pci_set_master(pdev->pdev);
52 		pdev->suspend = false;
53 	}
54 	return 0;
55 }
56 
57 static void *
58 nvkm_device_pci_dtor(struct nvkm_device *device)
59 {
60 	struct nvkm_device_pci *pdev = nvkm_device_pci(device);
61 	pci_disable_device(pdev->pdev);
62 	return pdev;
63 }
64 
65 static const struct nvkm_device_func
66 nvkm_device_pci_func = {
67 	.pci = nvkm_device_pci,
68 	.dtor = nvkm_device_pci_dtor,
69 	.preinit = nvkm_device_pci_preinit,
70 	.fini = nvkm_device_pci_fini,
71 };
72 
73 int
74 nvkm_device_pci_new(struct pci_dev *pci_dev, const char *cfg, const char *dbg,
75 		    bool detect, bool mmio, u64 subdev_mask,
76 		    struct nvkm_device **pdevice)
77 {
78 	struct nvkm_device_pci *pdev;
79 	int ret;
80 
81 	ret = pci_enable_device(pci_dev);
82 	if (ret)
83 		return ret;
84 
85 	if (!(pdev = kzalloc(sizeof(*pdev), GFP_KERNEL))) {
86 		pci_disable_device(pci_dev);
87 		return -ENOMEM;
88 	}
89 	*pdevice = &pdev->device;
90 	pdev->pdev = pci_dev;
91 
92 	return nvkm_device_ctor(&nvkm_device_pci_func, NULL,
93 				pci_dev, NVKM_BUS_PCI,
94 				(u64)pci_domain_nr(pci_dev->bus) << 32 |
95 				     pci_dev->bus->number << 16 |
96 				     PCI_SLOT(pci_dev->devfn) << 8 |
97 				     PCI_FUNC(pci_dev->devfn), NULL,
98 				cfg, dbg, detect, mmio, subdev_mask,
99 				&pdev->device);
100 }
101