xref: /openbmc/linux/drivers/xen/pci.c (revision 7fe2f639)
1 /*
2  * Copyright (c) 2009, Intel Corporation.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms and conditions of the GNU General Public License,
6  * version 2, as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11  * more details.
12  *
13  * You should have received a copy of the GNU General Public License along with
14  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
15  * Place - Suite 330, Boston, MA 02111-1307 USA.
16  *
17  * Author: Weidong Han <weidong.han@intel.com>
18  */
19 
20 #include <linux/pci.h>
21 #include <xen/xen.h>
22 #include <xen/interface/physdev.h>
23 #include <xen/interface/xen.h>
24 
25 #include <asm/xen/hypervisor.h>
26 #include <asm/xen/hypercall.h>
27 #include "../pci/pci.h"
28 
29 static int xen_add_device(struct device *dev)
30 {
31 	int r;
32 	struct pci_dev *pci_dev = to_pci_dev(dev);
33 
34 #ifdef CONFIG_PCI_IOV
35 	if (pci_dev->is_virtfn) {
36 		struct physdev_manage_pci_ext manage_pci_ext = {
37 			.bus		= pci_dev->bus->number,
38 			.devfn		= pci_dev->devfn,
39 			.is_virtfn 	= 1,
40 			.physfn.bus	= pci_dev->physfn->bus->number,
41 			.physfn.devfn	= pci_dev->physfn->devfn,
42 		};
43 
44 		r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add_ext,
45 			&manage_pci_ext);
46 	} else
47 #endif
48 	if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn)) {
49 		struct physdev_manage_pci_ext manage_pci_ext = {
50 			.bus		= pci_dev->bus->number,
51 			.devfn		= pci_dev->devfn,
52 			.is_extfn	= 1,
53 		};
54 
55 		r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add_ext,
56 			&manage_pci_ext);
57 	} else {
58 		struct physdev_manage_pci manage_pci = {
59 			.bus 	= pci_dev->bus->number,
60 			.devfn	= pci_dev->devfn,
61 		};
62 
63 		r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add,
64 			&manage_pci);
65 	}
66 
67 	return r;
68 }
69 
70 static int xen_remove_device(struct device *dev)
71 {
72 	int r;
73 	struct pci_dev *pci_dev = to_pci_dev(dev);
74 	struct physdev_manage_pci manage_pci;
75 
76 	manage_pci.bus = pci_dev->bus->number;
77 	manage_pci.devfn = pci_dev->devfn;
78 
79 	r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_remove,
80 		&manage_pci);
81 
82 	return r;
83 }
84 
85 static int xen_pci_notifier(struct notifier_block *nb,
86 			    unsigned long action, void *data)
87 {
88 	struct device *dev = data;
89 	int r = 0;
90 
91 	switch (action) {
92 	case BUS_NOTIFY_ADD_DEVICE:
93 		r = xen_add_device(dev);
94 		break;
95 	case BUS_NOTIFY_DEL_DEVICE:
96 		r = xen_remove_device(dev);
97 		break;
98 	default:
99 		break;
100 	}
101 
102 	return r;
103 }
104 
105 struct notifier_block device_nb = {
106 	.notifier_call = xen_pci_notifier,
107 };
108 
109 static int __init register_xen_pci_notifier(void)
110 {
111 	if (!xen_initial_domain())
112 		return 0;
113 
114 	return bus_register_notifier(&pci_bus_type, &device_nb);
115 }
116 
117 arch_initcall(register_xen_pci_notifier);
118