Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[linux-drm-fsl-dcu.git] / drivers / xen / pci.c
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 <linux/acpi.h>
22 #include <xen/xen.h>
23 #include <xen/interface/physdev.h>
24 #include <xen/interface/xen.h>
25
26 #include <asm/xen/hypervisor.h>
27 #include <asm/xen/hypercall.h>
28 #include "../pci/pci.h"
29 #include <asm/pci_x86.h>
30
31 static bool __read_mostly pci_seg_supported = true;
32
33 static int xen_add_device(struct device *dev)
34 {
35         int r;
36         struct pci_dev *pci_dev = to_pci_dev(dev);
37 #ifdef CONFIG_PCI_IOV
38         struct pci_dev *physfn = pci_dev->physfn;
39 #endif
40
41         if (pci_seg_supported) {
42                 struct physdev_pci_device_add add = {
43                         .seg = pci_domain_nr(pci_dev->bus),
44                         .bus = pci_dev->bus->number,
45                         .devfn = pci_dev->devfn
46                 };
47 #ifdef CONFIG_ACPI
48                 acpi_handle handle;
49 #endif
50
51 #ifdef CONFIG_PCI_IOV
52                 if (pci_dev->is_virtfn) {
53                         add.flags = XEN_PCI_DEV_VIRTFN;
54                         add.physfn.bus = physfn->bus->number;
55                         add.physfn.devfn = physfn->devfn;
56                 } else
57 #endif
58                 if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn))
59                         add.flags = XEN_PCI_DEV_EXTFN;
60
61 #ifdef CONFIG_ACPI
62                 handle = ACPI_HANDLE(&pci_dev->dev);
63                 if (!handle && pci_dev->bus->bridge)
64                         handle = ACPI_HANDLE(pci_dev->bus->bridge);
65 #ifdef CONFIG_PCI_IOV
66                 if (!handle && pci_dev->is_virtfn)
67                         handle = ACPI_HANDLE(physfn->bus->bridge);
68 #endif
69                 if (handle) {
70                         acpi_status status;
71
72                         do {
73                                 unsigned long long pxm;
74
75                                 status = acpi_evaluate_integer(handle, "_PXM",
76                                                                NULL, &pxm);
77                                 if (ACPI_SUCCESS(status)) {
78                                         add.optarr[0] = pxm;
79                                         add.flags |= XEN_PCI_DEV_PXM;
80                                         break;
81                                 }
82                                 status = acpi_get_parent(handle, &handle);
83                         } while (ACPI_SUCCESS(status));
84                 }
85 #endif /* CONFIG_ACPI */
86
87                 r = HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_add, &add);
88                 if (r != -ENOSYS)
89                         return r;
90                 pci_seg_supported = false;
91         }
92
93         if (pci_domain_nr(pci_dev->bus))
94                 r = -ENOSYS;
95 #ifdef CONFIG_PCI_IOV
96         else if (pci_dev->is_virtfn) {
97                 struct physdev_manage_pci_ext manage_pci_ext = {
98                         .bus            = pci_dev->bus->number,
99                         .devfn          = pci_dev->devfn,
100                         .is_virtfn      = 1,
101                         .physfn.bus     = physfn->bus->number,
102                         .physfn.devfn   = physfn->devfn,
103                 };
104
105                 r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add_ext,
106                         &manage_pci_ext);
107         }
108 #endif
109         else if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn)) {
110                 struct physdev_manage_pci_ext manage_pci_ext = {
111                         .bus            = pci_dev->bus->number,
112                         .devfn          = pci_dev->devfn,
113                         .is_extfn       = 1,
114                 };
115
116                 r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add_ext,
117                         &manage_pci_ext);
118         } else {
119                 struct physdev_manage_pci manage_pci = {
120                         .bus    = pci_dev->bus->number,
121                         .devfn  = pci_dev->devfn,
122                 };
123
124                 r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add,
125                         &manage_pci);
126         }
127
128         return r;
129 }
130
131 static int xen_remove_device(struct device *dev)
132 {
133         int r;
134         struct pci_dev *pci_dev = to_pci_dev(dev);
135
136         if (pci_seg_supported) {
137                 struct physdev_pci_device device = {
138                         .seg = pci_domain_nr(pci_dev->bus),
139                         .bus = pci_dev->bus->number,
140                         .devfn = pci_dev->devfn
141                 };
142
143                 r = HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_remove,
144                                           &device);
145         } else if (pci_domain_nr(pci_dev->bus))
146                 r = -ENOSYS;
147         else {
148                 struct physdev_manage_pci manage_pci = {
149                         .bus = pci_dev->bus->number,
150                         .devfn = pci_dev->devfn
151                 };
152
153                 r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_remove,
154                                           &manage_pci);
155         }
156
157         return r;
158 }
159
160 static int xen_pci_notifier(struct notifier_block *nb,
161                             unsigned long action, void *data)
162 {
163         struct device *dev = data;
164         int r = 0;
165
166         switch (action) {
167         case BUS_NOTIFY_ADD_DEVICE:
168                 r = xen_add_device(dev);
169                 break;
170         case BUS_NOTIFY_DEL_DEVICE:
171                 r = xen_remove_device(dev);
172                 break;
173         default:
174                 return NOTIFY_DONE;
175         }
176         if (r)
177                 dev_err(dev, "Failed to %s - passthrough or MSI/MSI-X might fail!\n",
178                         action == BUS_NOTIFY_ADD_DEVICE ? "add" :
179                         (action == BUS_NOTIFY_DEL_DEVICE ? "delete" : "?"));
180         return NOTIFY_OK;
181 }
182
183 static struct notifier_block device_nb = {
184         .notifier_call = xen_pci_notifier,
185 };
186
187 static int __init register_xen_pci_notifier(void)
188 {
189         if (!xen_initial_domain())
190                 return 0;
191
192         return bus_register_notifier(&pci_bus_type, &device_nb);
193 }
194
195 arch_initcall(register_xen_pci_notifier);
196
197 #ifdef CONFIG_PCI_MMCONFIG
198 static int __init xen_mcfg_late(void)
199 {
200         struct pci_mmcfg_region *cfg;
201         int rc;
202
203         if (!xen_initial_domain())
204                 return 0;
205
206         if ((pci_probe & PCI_PROBE_MMCONF) == 0)
207                 return 0;
208
209         if (list_empty(&pci_mmcfg_list))
210                 return 0;
211
212         /* Check whether they are in the right area. */
213         list_for_each_entry(cfg, &pci_mmcfg_list, list) {
214                 struct physdev_pci_mmcfg_reserved r;
215
216                 r.address = cfg->address;
217                 r.segment = cfg->segment;
218                 r.start_bus = cfg->start_bus;
219                 r.end_bus = cfg->end_bus;
220                 r.flags = XEN_PCI_MMCFG_RESERVED;
221
222                 rc = HYPERVISOR_physdev_op(PHYSDEVOP_pci_mmcfg_reserved, &r);
223                 switch (rc) {
224                 case 0:
225                 case -ENOSYS:
226                         continue;
227
228                 default:
229                         pr_warn("Failed to report MMCONFIG reservation"
230                                 " state for %s to hypervisor"
231                                 " (%d)\n",
232                                 cfg->name, rc);
233                 }
234         }
235         return 0;
236 }
237 /*
238  * Needs to be done after acpi_init which are subsys_initcall.
239  */
240 subsys_initcall_sync(xen_mcfg_late);
241 #endif