| 1 | |
| 2 | |
| 3 | * platform-pci.c
|
| 4 | *
|
| 5 | * Xen platform PCI device driver
|
| 6 | *
|
| 7 | * Authors: [email protected] and [email protected]
|
| 8 | *
|
| 9 | * Copyright (c) 2005, Intel Corporation.
|
| 10 | * Copyright (c) 2007, XenSource Inc.
|
| 11 | * Copyright (c) 2010, Citrix
|
| 12 |
|
| 13 |
|
| 14 |
|
| 15 | #include <linux/interrupt.h>
|
| 16 | #include <linux/io.h>
|
| 17 | #include <linux/init.h>
|
| 18 | #include <linux/pci.h>
|
| 19 |
|
| 20 | #include <xen/platform_pci.h>
|
| 21 | #include <xen/grant_table.h>
|
| 22 | #include <xen/xenbus.h>
|
| 23 | #include <xen/events.h>
|
| 24 | #include <xen/hvm.h>
|
| 25 | #include <xen/xen-ops.h>
|
| 26 |
|
| 27 | #define DRV_NAME "xen-platform-pci"
|
| 28 |
|
| 29 | #define PCI_DEVICE_ID_XEN_PLATFORM_XS61 0x0002
|
| 30 |
|
| 31 | static unsigned long platform_mmio;
|
| 32 | static unsigned long platform_mmio_alloc;
|
| 33 | static unsigned long platform_mmiolen;
|
| 34 | static uint64_t callback_via;
|
| 35 |
|
| 36 | static unsigned long alloc_xen_mmio(unsigned long len)
|
| 37 | {
|
| 38 | unsigned long addr;
|
| 39 |
|
| 40 | addr = platform_mmio + platform_mmio_alloc;
|
| 41 | platform_mmio_alloc += len;
|
| 42 | BUG_ON(platform_mmio_alloc > platform_mmiolen);
|
| 43 |
|
| 44 | return addr;
|
| 45 | }
|
| 46 |
|
| 47 | static uint64_t get_callback_via(struct pci_dev *pdev)
|
| 48 | {
|
| 49 | u8 pin;
|
| 50 | int irq;
|
| 51 |
|
| 52 | irq = pdev->irq;
|
| 53 | if (irq < 16)
|
| 54 | return irq;
|
| 55 |
|
| 56 | pin = pdev->pin;
|
| 57 |
|
| 58 |
|
| 59 | return ((uint64_t)HVM_PARAM_CALLBACK_TYPE_PCI_INTX <<
|
| 60 | HVM_CALLBACK_VIA_TYPE_SHIFT) |
|
| 61 | ((uint64_t)pci_domain_nr(pdev->bus) << 32) |
|
| 62 | ((uint64_t)pdev->bus->number << 16) |
|
| 63 | ((uint64_t)(pdev->devfn & 0xff) << 8) |
|
| 64 | ((uint64_t)(pin - 1) & 3);
|
| 65 | }
|
| 66 |
|
| 67 | static irqreturn_t do_hvm_evtchn_intr(int irq, void *dev_id)
|
| 68 | {
|
| 69 | return xen_evtchn_do_upcall();
|
| 70 | }
|
| 71 |
|
| 72 | static int xen_allocate_irq(struct pci_dev *pdev)
|
| 73 | {
|
| 74 | return request_irq(pdev->irq, do_hvm_evtchn_intr,
|
| 75 | IRQF_NOBALANCING | IRQF_SHARED,
|
| 76 | "xen-platform-pci", pdev);
|
| 77 | }
|
| 78 |
|
| 79 | static int platform_pci_resume(struct device *dev)
|
| 80 | {
|
| 81 | int err;
|
| 82 |
|
| 83 | if (xen_have_vector_callback)
|
| 84 | return 0;
|
| 85 |
|
| 86 | err = xen_set_callback_via(callback_via);
|
| 87 | if (err) {
|
| 88 | dev_err(dev, "platform_pci_resume failure!\n");
|
| 89 | return err;
|
| 90 | }
|
| 91 | return 0;
|
| 92 | }
|
| 93 |
|
| 94 | static int platform_pci_probe(struct pci_dev *pdev,
|
| 95 | const struct pci_device_id *ent)
|
| 96 | {
|
| 97 | int i, ret;
|
| 98 | long ioaddr;
|
| 99 | long mmio_addr, mmio_len;
|
| 100 | unsigned int max_nr_gframes;
|
| 101 | unsigned long grant_frames;
|
| 102 |
|
| 103 | if (!xen_domain())
|
| 104 | return -ENODEV;
|
| 105 |
|
| 106 | i = pci_enable_device(pdev);
|
| 107 | if (i)
|
| 108 | return i;
|
| 109 |
|
| 110 | ioaddr = pci_resource_start(pdev, 0);
|
| 111 |
|
| 112 | mmio_addr = pci_resource_start(pdev, 1);
|
| 113 | mmio_len = pci_resource_len(pdev, 1);
|
| 114 |
|
| 115 | if (mmio_addr == 0 || ioaddr == 0) {
|
| 116 | dev_err(&pdev->dev, "no resources found\n");
|
| 117 | ret = -ENOENT;
|
| 118 | goto pci_out;
|
| 119 | }
|
| 120 |
|
| 121 | ret = pci_request_region(pdev, 1, DRV_NAME);
|
| 122 | if (ret < 0)
|
| 123 | goto pci_out;
|
| 124 |
|
| 125 | ret = pci_request_region(pdev, 0, DRV_NAME);
|
| 126 | if (ret < 0)
|
| 127 | goto mem_out;
|
| 128 |
|
| 129 | platform_mmio = mmio_addr;
|
| 130 | platform_mmiolen = mmio_len;
|
| 131 | if (!xen_have_vector_callback) {
|
| 132 | ret = xen_allocate_irq(pdev);
|
| 133 | if (ret) {
|
| 134 | dev_warn(&pdev->dev, "request_irq failed err=%d\n", ret);
|
| 135 | goto out;
|
| 136 | }
|
| 137 | |
| 138 | * It doesn't strictly *have* to run on CPU0 but it sure
|
| 139 | * as hell better process the event channel ports delivered
|
| 140 | * to CPU0.
|
| 141 |
|
| 142 | irq_set_affinity(pdev->irq, cpumask_of(0));
|
| 143 |
|
| 144 | callback_via = get_callback_via(pdev);
|
| 145 | ret = xen_set_callback_via(callback_via);
|
| 146 | if (ret) {
|
| 147 | dev_warn(&pdev->dev, "Unable to set the evtchn callback "
|
| 148 | "err=%d\n", ret);
|
| 149 | goto irq_out;
|
| 150 | }
|
| 151 | }
|
| 152 |
|
| 153 | max_nr_gframes = gnttab_max_grant_frames();
|
| 154 | grant_frames = alloc_xen_mmio(PAGE_SIZE * max_nr_gframes);
|
| 155 | ret = gnttab_setup_auto_xlat_frames(grant_frames);
|
| 156 | if (ret)
|
| 157 | goto irq_out;
|
| 158 | ret = gnttab_init();
|
| 159 | if (ret)
|
| 160 | goto grant_out;
|
| 161 | return 0;
|
| 162 | grant_out:
|
| 163 | gnttab_free_auto_xlat_frames();
|
| 164 | irq_out:
|
| 165 | if (!xen_have_vector_callback)
|
| 166 | free_irq(pdev->irq, pdev);
|
| 167 | out:
|
| 168 | pci_release_region(pdev, 0);
|
| 169 | mem_out:
|
| 170 | pci_release_region(pdev, 1);
|
| 171 | pci_out:
|
| 172 | pci_disable_device(pdev);
|
| 173 | return ret;
|
| 174 | }
|
| 175 |
|
| 176 | static const struct pci_device_id platform_pci_tbl[] = {
|
| 177 | { PCI_VDEVICE(XEN, PCI_DEVICE_ID_XEN_PLATFORM) },
|
| 178 | { PCI_VDEVICE(XEN, PCI_DEVICE_ID_XEN_PLATFORM_XS61) },
|
| 179 | { }
|
| 180 | };
|
| 181 |
|
| 182 | static const struct dev_pm_ops platform_pm_ops = {
|
| 183 | .resume_noirq = platform_pci_resume,
|
| 184 | };
|
| 185 |
|
| 186 | static struct pci_driver platform_driver = {
|
| 187 | .name = DRV_NAME,
|
| 188 | .probe = platform_pci_probe,
|
| 189 | .id_table = platform_pci_tbl,
|
| 190 | .driver = {
|
| 191 | .pm = &platform_pm_ops,
|
| 192 | },
|
| 193 | };
|
| 194 |
|
| 195 | builtin_pci_driver(platform_driver);
|