source: trunk/packages/xen-3.1/xen-3.1/linux-2.6-xen-sparse/drivers/xen/pcifront/pci_op.c @ 34

Last change on this file since 34 was 34, checked in by hartmans, 18 years ago

Add xen and xen-common

File size: 6.3 KB
Line 
1/*
2 * PCI Frontend Operations - Communicates with frontend
3 *
4 *   Author: Ryan Wilson <hap9@epoch.ncsc.mil>
5 */
6#include <linux/module.h>
7#include <linux/version.h>
8#include <linux/init.h>
9#include <linux/pci.h>
10#include <linux/spinlock.h>
11#include <linux/time.h>
12#include <xen/evtchn.h>
13#include "pcifront.h"
14
15static int verbose_request = 0;
16module_param(verbose_request, int, 0644);
17
18static int errno_to_pcibios_err(int errno)
19{
20        switch (errno) {
21        case XEN_PCI_ERR_success:
22                return PCIBIOS_SUCCESSFUL;
23
24        case XEN_PCI_ERR_dev_not_found:
25                return PCIBIOS_DEVICE_NOT_FOUND;
26
27        case XEN_PCI_ERR_invalid_offset:
28        case XEN_PCI_ERR_op_failed:
29                return PCIBIOS_BAD_REGISTER_NUMBER;
30
31        case XEN_PCI_ERR_not_implemented:
32                return PCIBIOS_FUNC_NOT_SUPPORTED;
33
34        case XEN_PCI_ERR_access_denied:
35                return PCIBIOS_SET_FAILED;
36        }
37        return errno;
38}
39
40static int do_pci_op(struct pcifront_device *pdev, struct xen_pci_op *op)
41{
42        int err = 0;
43        struct xen_pci_op *active_op = &pdev->sh_info->op;
44        unsigned long irq_flags;
45        evtchn_port_t port = pdev->evtchn;
46        s64 ns, ns_timeout;
47        struct timeval tv;
48
49        spin_lock_irqsave(&pdev->sh_info_lock, irq_flags);
50
51        memcpy(active_op, op, sizeof(struct xen_pci_op));
52
53        /* Go */
54        wmb();
55        set_bit(_XEN_PCIF_active, (unsigned long *)&pdev->sh_info->flags);
56        notify_remote_via_evtchn(port);
57
58        /*
59         * We set a poll timeout of 3 seconds but give up on return after
60         * 2 seconds. It is better to time out too late rather than too early
61         * (in the latter case we end up continually re-executing poll() with a
62         * timeout in the past). 1s difference gives plenty of slack for error.
63         */
64        do_gettimeofday(&tv);
65        ns_timeout = timeval_to_ns(&tv) + 2 * (s64)NSEC_PER_SEC;
66
67        clear_evtchn(port);
68
69        while (test_bit(_XEN_PCIF_active,
70                        (unsigned long *)&pdev->sh_info->flags)) {
71                if (HYPERVISOR_poll(&port, 1, jiffies + 3*HZ))
72                        BUG();
73                clear_evtchn(port);
74                do_gettimeofday(&tv);
75                ns = timeval_to_ns(&tv);
76                if (ns > ns_timeout) {
77                        dev_err(&pdev->xdev->dev,
78                                "pciback not responding!!!\n");
79                        clear_bit(_XEN_PCIF_active,
80                                  (unsigned long *)&pdev->sh_info->flags);
81                        err = XEN_PCI_ERR_dev_not_found;
82                        goto out;
83                }
84        }
85
86        memcpy(op, active_op, sizeof(struct xen_pci_op));
87
88        err = op->err;
89      out:
90        spin_unlock_irqrestore(&pdev->sh_info_lock, irq_flags);
91        return err;
92}
93
94/* Access to this function is spinlocked in drivers/pci/access.c */
95static int pcifront_bus_read(struct pci_bus *bus, unsigned int devfn,
96                             int where, int size, u32 * val)
97{
98        int err = 0;
99        struct xen_pci_op op = {
100                .cmd    = XEN_PCI_OP_conf_read,
101                .domain = pci_domain_nr(bus),
102                .bus    = bus->number,
103                .devfn  = devfn,
104                .offset = where,
105                .size   = size,
106        };
107        struct pcifront_sd *sd = bus->sysdata;
108        struct pcifront_device *pdev = pcifront_get_pdev(sd);
109
110        if (verbose_request)
111                dev_info(&pdev->xdev->dev,
112                         "read dev=%04x:%02x:%02x.%01x - offset %x size %d\n",
113                         pci_domain_nr(bus), bus->number, PCI_SLOT(devfn),
114                         PCI_FUNC(devfn), where, size);
115
116        err = do_pci_op(pdev, &op);
117
118        if (likely(!err)) {
119                if (verbose_request)
120                        dev_info(&pdev->xdev->dev, "read got back value %x\n",
121                                 op.value);
122
123                *val = op.value;
124        } else if (err == -ENODEV) {
125                /* No device here, pretend that it just returned 0 */
126                err = 0;
127                *val = 0;
128        }
129
130        return errno_to_pcibios_err(err);
131}
132
133/* Access to this function is spinlocked in drivers/pci/access.c */
134static int pcifront_bus_write(struct pci_bus *bus, unsigned int devfn,
135                              int where, int size, u32 val)
136{
137        struct xen_pci_op op = {
138                .cmd    = XEN_PCI_OP_conf_write,
139                .domain = pci_domain_nr(bus),
140                .bus    = bus->number,
141                .devfn  = devfn,
142                .offset = where,
143                .size   = size,
144                .value  = val,
145        };
146        struct pcifront_sd *sd = bus->sysdata;
147        struct pcifront_device *pdev = pcifront_get_pdev(sd);
148
149        if (verbose_request)
150                dev_info(&pdev->xdev->dev,
151                         "write dev=%04x:%02x:%02x.%01x - "
152                         "offset %x size %d val %x\n",
153                         pci_domain_nr(bus), bus->number,
154                         PCI_SLOT(devfn), PCI_FUNC(devfn), where, size, val);
155
156        return errno_to_pcibios_err(do_pci_op(pdev, &op));
157}
158
159struct pci_ops pcifront_bus_ops = {
160        .read = pcifront_bus_read,
161        .write = pcifront_bus_write,
162};
163
164/* Claim resources for the PCI frontend as-is, backend won't allow changes */
165static void pcifront_claim_resource(struct pci_dev *dev, void *data)
166{
167        struct pcifront_device *pdev = data;
168        int i;
169        struct resource *r;
170
171        for (i = 0; i < PCI_NUM_RESOURCES; i++) {
172                r = &dev->resource[i];
173
174                if (!r->parent && r->start && r->flags) {
175                        dev_dbg(&pdev->xdev->dev, "claiming resource %s/%d\n",
176                                pci_name(dev), i);
177                        pci_claim_resource(dev, i);
178                }
179        }
180}
181
182int pcifront_scan_root(struct pcifront_device *pdev,
183                       unsigned int domain, unsigned int bus)
184{
185        struct pci_bus *b;
186        struct pcifront_sd *sd = NULL;
187        struct pci_bus_entry *bus_entry = NULL;
188        int err = 0;
189
190#ifndef CONFIG_PCI_DOMAINS
191        if (domain != 0) {
192                dev_err(&pdev->xdev->dev,
193                        "PCI Root in non-zero PCI Domain! domain=%d\n", domain);
194                dev_err(&pdev->xdev->dev,
195                        "Please compile with CONFIG_PCI_DOMAINS\n");
196                err = -EINVAL;
197                goto err_out;
198        }
199#endif
200
201        dev_info(&pdev->xdev->dev, "Creating PCI Frontend Bus %04x:%02x\n",
202                 domain, bus);
203
204        bus_entry = kmalloc(sizeof(*bus_entry), GFP_KERNEL);
205        sd = kmalloc(sizeof(*sd), GFP_KERNEL);
206        if (!bus_entry || !sd) {
207                err = -ENOMEM;
208                goto err_out;
209        }
210        pcifront_init_sd(sd, domain, pdev);
211
212        b = pci_scan_bus_parented(&pdev->xdev->dev, bus,
213                                  &pcifront_bus_ops, sd);
214        if (!b) {
215                dev_err(&pdev->xdev->dev,
216                        "Error creating PCI Frontend Bus!\n");
217                err = -ENOMEM;
218                goto err_out;
219        }
220        bus_entry->bus = b;
221
222        list_add(&bus_entry->list, &pdev->root_buses);
223
224        /* Claim resources before going "live" with our devices */
225        pci_walk_bus(b, pcifront_claim_resource, pdev);
226
227        pci_bus_add_devices(b);
228
229        return 0;
230
231      err_out:
232        kfree(bus_entry);
233        kfree(sd);
234
235        return err;
236}
237
238static void free_root_bus_devs(struct pci_bus *bus)
239{
240        struct pci_dev *dev;
241
242        while (!list_empty(&bus->devices)) {
243                dev = container_of(bus->devices.next, struct pci_dev,
244                                   bus_list);
245                dev_dbg(&dev->dev, "removing device\n");
246                pci_remove_bus_device(dev);
247        }
248}
249
250void pcifront_free_roots(struct pcifront_device *pdev)
251{
252        struct pci_bus_entry *bus_entry, *t;
253
254        dev_dbg(&pdev->xdev->dev, "cleaning up root buses\n");
255
256        list_for_each_entry_safe(bus_entry, t, &pdev->root_buses, list) {
257                list_del(&bus_entry->list);
258
259                free_root_bus_devs(bus_entry->bus);
260
261                kfree(bus_entry->bus->sysdata);
262
263                device_unregister(bus_entry->bus->bridge);
264                pci_remove_bus(bus_entry->bus);
265
266                kfree(bus_entry);
267        }
268}
Note: See TracBrowser for help on using the repository browser.