02c402b1ed806c5eab68ee4018dc78c2793f194c
[pandora-kernel.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 <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                 return NOTIFY_DONE;
100         }
101         if (r)
102                 dev_err(dev, "Failed to %s - passthrough or MSI/MSI-X might fail!\n",
103                         action == BUS_NOTIFY_ADD_DEVICE ? "add" :
104                         (action == BUS_NOTIFY_DEL_DEVICE ? "delete" : "?"));
105         return NOTIFY_OK;
106 }
107
108 static struct notifier_block device_nb = {
109         .notifier_call = xen_pci_notifier,
110 };
111
112 static int __init register_xen_pci_notifier(void)
113 {
114         if (!xen_initial_domain())
115                 return 0;
116
117         return bus_register_notifier(&pci_bus_type, &device_nb);
118 }
119
120 arch_initcall(register_xen_pci_notifier);