ia64/xen-unstable

annotate unmodified_drivers/linux-2.6/platform-pci/platform-pci.c @ 13889:a68a3c6cac5f

Small cleanups to PV-on-HVM support code.
Signed-off-by: Keir Fraser <keir@xensource.com>
author Keir Fraser <keir@xensource.com>
date Thu Feb 08 17:38:30 2007 +0000 (2007-02-08)
parents 311b27546cf6
children e3dc8cea5bc0
rev   line source
kfraser@11185 1 /******************************************************************************
kfraser@11185 2 * evtchn-pci.c
kfraser@11185 3 * xen event channel fake PCI device driver
kfraser@11185 4 * Copyright (C) 2005, Intel Corporation.
kfraser@11185 5 *
kfraser@11185 6 * This program is free software; you can redistribute it and/or modify it
kfraser@11185 7 * under the terms and conditions of the GNU General Public License,
kfraser@11185 8 * version 2, as published by the Free Software Foundation.
kfraser@11185 9 *
kfraser@11185 10 * This program is distributed in the hope it will be useful, but WITHOUT
kfraser@11185 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
kfraser@11185 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
kfraser@11185 13 * more details.
kfraser@11185 14 *
kfraser@11185 15 * You should have received a copy of the GNU General Public License along with
kfraser@11185 16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
kfraser@11185 17 * Place - Suite 330, Boston, MA 02111-1307 USA.
kfraser@11185 18 *
kfraser@11185 19 */
kaf24@11443 20
kfraser@11185 21 #include <linux/module.h>
kfraser@11185 22 #include <linux/kernel.h>
kfraser@11185 23 #include <linux/sched.h>
kfraser@11185 24 #include <linux/errno.h>
kfraser@11185 25 #include <linux/pci.h>
kfraser@11185 26 #include <linux/init.h>
kfraser@11185 27 #include <linux/version.h>
kfraser@11185 28 #include <linux/interrupt.h>
kaf24@11443 29 #include <linux/vmalloc.h>
kaf24@11443 30 #include <linux/mm.h>
kfraser@11185 31 #include <asm/system.h>
kfraser@11185 32 #include <asm/io.h>
kfraser@11185 33 #include <asm/irq.h>
kfraser@11185 34 #include <asm/uaccess.h>
kfraser@11185 35 #include <asm/hypervisor.h>
ian@11974 36 #include <asm/pgtable.h>
kfraser@11185 37 #include <xen/interface/memory.h>
kfraser@11185 38 #include <xen/features.h>
awilliam@11805 39 #ifdef __ia64__
awilliam@11805 40 #include <asm/xen/xencomm.h>
awilliam@11805 41 #endif
kfraser@11185 42
kfraser@11185 43 #include "platform-pci.h"
kfraser@11185 44
ian@11983 45 #ifdef HAVE_XEN_PLATFORM_COMPAT_H
ian@11983 46 #include <xen/platform-compat.h>
ian@11983 47 #endif
ian@11983 48
kfraser@11185 49 #define DRV_NAME "xen-platform-pci"
kfraser@11185 50 #define DRV_VERSION "0.10"
kfraser@11185 51 #define DRV_RELDATE "03/03/2005"
kfraser@11185 52
kfraser@11249 53 char *hypercall_stubs;
kfraser@11249 54 EXPORT_SYMBOL(hypercall_stubs);
kfraser@11185 55
kfraser@11185 56 // Used to be xiaofeng.ling@intel.com
kfraser@11185 57 MODULE_AUTHOR("ssmith@xensource.com");
kfraser@11185 58 MODULE_DESCRIPTION("Xen platform PCI device");
kfraser@11185 59 MODULE_LICENSE("GPL");
kfraser@11185 60
kfraser@11185 61 unsigned long *phys_to_machine_mapping;
kfraser@11185 62 EXPORT_SYMBOL(phys_to_machine_mapping);
kfraser@11185 63
ian@13569 64 static int __devinit init_xen_info(void)
kfraser@11185 65 {
kfraser@11185 66 unsigned long shared_info_frame;
kfraser@11185 67 struct xen_add_to_physmap xatp;
kfraser@11185 68 extern void *shared_info_area;
kfraser@11185 69
awilliam@11805 70 #ifdef __ia64__
awilliam@11805 71 xencomm_init();
awilliam@11805 72 #endif
awilliam@11805 73
kfraser@11185 74 setup_xen_features();
kfraser@11185 75
kfraser@11185 76 shared_info_frame = alloc_xen_mmio(PAGE_SIZE) >> PAGE_SHIFT;
kfraser@11185 77 xatp.domid = DOMID_SELF;
kfraser@11185 78 xatp.idx = 0;
kfraser@11185 79 xatp.space = XENMAPSPACE_shared_info;
kfraser@11185 80 xatp.gpfn = shared_info_frame;
kfraser@11186 81 if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp))
kfraser@11186 82 BUG();
kfraser@11186 83
kfraser@11185 84 shared_info_area =
kfraser@11185 85 ioremap(shared_info_frame << PAGE_SHIFT, PAGE_SIZE);
kfraser@11186 86 if (shared_info_area == NULL)
kfraser@11185 87 panic("can't map shared info\n");
kfraser@11185 88
kfraser@11185 89 phys_to_machine_mapping = NULL;
kfraser@11185 90
kfraser@11186 91 gnttab_init();
kfraser@11186 92
kfraser@11185 93 return 0;
kfraser@11185 94 }
kfraser@11185 95
kfraser@11185 96 static void __devexit platform_pci_remove(struct pci_dev *pdev)
kfraser@11185 97 {
kfraser@11185 98 long ioaddr, iolen;
kfraser@11185 99 long mmio_addr, mmio_len;
kfraser@11185 100
kfraser@11185 101 ioaddr = pci_resource_start(pdev, 0);
kfraser@11185 102 iolen = pci_resource_len(pdev, 0);
kfraser@11185 103 mmio_addr = pci_resource_start(pdev, 1);
kfraser@11185 104 mmio_len = pci_resource_len(pdev, 1);
kfraser@11185 105
kfraser@11185 106 release_region(ioaddr, iolen);
kfraser@11185 107 release_mem_region(mmio_addr, mmio_len);
kfraser@11185 108
kfraser@11185 109 pci_set_drvdata(pdev, NULL);
kfraser@11185 110 free_irq(pdev->irq, pdev);
kfraser@11185 111 }
kfraser@11185 112
kfraser@11185 113 static unsigned long platform_mmio;
kfraser@11185 114 static unsigned long platform_mmio_alloc;
kfraser@11185 115 static unsigned long platform_mmiolen;
kfraser@11185 116
kfraser@11185 117 unsigned long alloc_xen_mmio(unsigned long len)
kfraser@11185 118 {
kfraser@11185 119 unsigned long addr;
kfraser@11185 120
keir@13889 121 addr = platform_mmio + platform_mmio_alloc;
keir@13889 122 platform_mmio_alloc += len;
keir@13889 123 BUG_ON(platform_mmio_alloc > platform_mmiolen);
keir@13889 124
kfraser@11185 125 return addr;
kfraser@11185 126 }
kfraser@11185 127
kaf24@11468 128 #ifndef __ia64__
kfraser@11185 129 /* Lifted from hvmloader.c */
kfraser@11249 130 static int get_hypercall_stubs(void)
kfraser@11185 131 {
kaf24@11443 132 uint32_t eax, ebx, ecx, edx, pages, msr, i;
kfraser@11185 133 char signature[13];
kfraser@11185 134
kfraser@11185 135 cpuid(0x40000000, &eax, &ebx, &ecx, &edx);
kfraser@11185 136 *(uint32_t*)(signature + 0) = ebx;
kfraser@11185 137 *(uint32_t*)(signature + 4) = ecx;
kfraser@11185 138 *(uint32_t*)(signature + 8) = edx;
kfraser@11185 139 signature[12] = 0;
kfraser@11185 140
kfraser@11249 141 if (strcmp("XenVMMXenVMM", signature) || (eax < 0x40000002)) {
kfraser@11185 142 printk(KERN_WARNING
kfraser@11249 143 "Detected Xen platform device but not Xen VMM?"
kfraser@11249 144 " (sig %s, eax %x)\n",
kfraser@11185 145 signature, eax);
kfraser@11185 146 return -EINVAL;
kfraser@11185 147 }
kfraser@11185 148
kfraser@11185 149 cpuid(0x40000001, &eax, &ebx, &ecx, &edx);
kfraser@11185 150
kfraser@11185 151 printk(KERN_INFO "Xen version %d.%d.\n", eax >> 16, eax & 0xffff);
kfraser@11185 152
kfraser@11249 153 cpuid(0x40000002, &pages, &msr, &ecx, &edx);
kfraser@11185 154
kaf24@11443 155 printk(KERN_INFO "Hypercall area is %u pages.\n", pages);
kfraser@11185 156
kaf24@11443 157 /* Use __vmalloc() because vmalloc_exec() is not an exported symbol. */
kaf24@11443 158 /* PAGE_KERNEL_EXEC also is not exported, hence we use PAGE_KERNEL. */
kaf24@11443 159 /* hypercall_stubs = vmalloc_exec(pages * PAGE_SIZE); */
kaf24@11443 160 hypercall_stubs = __vmalloc(pages * PAGE_SIZE,
kaf24@11443 161 GFP_KERNEL | __GFP_HIGHMEM,
kaf24@11443 162 __pgprot(__PAGE_KERNEL & ~_PAGE_NX));
kfraser@11249 163 if (hypercall_stubs == NULL)
kfraser@11185 164 return -ENOMEM;
kfraser@11249 165
kaf24@11443 166 for (i = 0; i < pages; i++) {
kaf24@11443 167 unsigned long pfn;
kaf24@11443 168 pfn = vmalloc_to_pfn((char *)hypercall_stubs + i*PAGE_SIZE);
kaf24@11443 169 wrmsrl(msr, ((u64)pfn << PAGE_SHIFT) + i);
kaf24@11443 170 }
kfraser@11185 171
kfraser@11185 172 return 0;
kfraser@11185 173 }
kaf24@11468 174 #else /* __ia64__ */
kaf24@11468 175 #define get_hypercall_stubs() (0)
kaf24@11468 176 #endif
kfraser@11185 177
kfraser@13374 178 static uint64_t get_callback_via(struct pci_dev *pdev)
kfraser@12545 179 {
awilliam@13842 180 u8 pin;
keir@13889 181 int irq;
keir@13889 182
kfraser@12545 183 #ifdef __ia64__
kfraser@12545 184 for (irq = 0; irq < 16; irq++) {
kfraser@12545 185 if (isa_irq_to_vector(irq) == pdev->irq)
awilliam@13842 186 return irq; /* ISA IRQ */
kfraser@12545 187 }
kfraser@12545 188 #else /* !__ia64__ */
keir@13889 189 irq = pdev->irq;
keir@13889 190 if (irq < 16)
keir@13889 191 return irq; /* ISA IRQ */
awilliam@13842 192 #endif
ian@13569 193
ian@13569 194 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
ian@13569 195 pin = pdev->pin;
ian@13569 196 #else
ian@13569 197 pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pin);
ian@13569 198 #endif
ian@13569 199
kfraser@13374 200 /* We don't know the GSI. Specify the PCI INTx line instead. */
kfraser@13374 201 return (((uint64_t)0x01 << 56) | /* PCI INTx identifier */
kfraser@13374 202 ((uint64_t)pci_domain_nr(pdev->bus) << 32) |
kfraser@13374 203 ((uint64_t)pdev->bus->number << 16) |
kfraser@13374 204 ((uint64_t)(pdev->devfn & 0xff) << 8) |
ian@13569 205 ((uint64_t)(pin - 1) & 3));
kfraser@12545 206 }
kfraser@12545 207
kaf24@13664 208 /* Invalidate foreign mappings (e.g., in qemu-based device model). */
kaf24@13664 209 static uint16_t invlmap_port;
kaf24@13664 210 void xen_invalidate_foreign_mappings(void)
kaf24@13664 211 {
kaf24@13664 212 outb(0, invlmap_port);
kaf24@13664 213 }
kaf24@13664 214 EXPORT_SYMBOL(xen_invalidate_foreign_mappings);
kaf24@13664 215
kfraser@11185 216 static int __devinit platform_pci_init(struct pci_dev *pdev,
kfraser@11185 217 const struct pci_device_id *ent)
kfraser@11185 218 {
kfraser@13374 219 int i, ret;
kfraser@11185 220 long ioaddr, iolen;
kfraser@11185 221 long mmio_addr, mmio_len;
kfraser@13374 222 uint64_t callback_via;
kfraser@11185 223
kfraser@11185 224 i = pci_enable_device(pdev);
kfraser@11185 225 if (i)
kfraser@11185 226 return i;
kfraser@11185 227
kfraser@11185 228 ioaddr = pci_resource_start(pdev, 0);
kfraser@11185 229 iolen = pci_resource_len(pdev, 0);
kfraser@11185 230
kfraser@11185 231 mmio_addr = pci_resource_start(pdev, 1);
kfraser@11185 232 mmio_len = pci_resource_len(pdev, 1);
kfraser@11185 233
kfraser@13374 234 callback_via = get_callback_via(pdev);
kfraser@12545 235
kfraser@13374 236 if (mmio_addr == 0 || ioaddr == 0 || callback_via == 0) {
kfraser@11185 237 printk(KERN_WARNING DRV_NAME ":no resources found\n");
kfraser@11185 238 return -ENOENT;
kfraser@11185 239 }
kfraser@11185 240
kaf24@13664 241 invlmap_port = ioaddr;
kaf24@13664 242
kfraser@11185 243 if (request_mem_region(mmio_addr, mmio_len, DRV_NAME) == NULL)
kfraser@11185 244 {
kfraser@11185 245 printk(KERN_ERR ":MEM I/O resource 0x%lx @ 0x%lx busy\n",
kfraser@11185 246 mmio_addr, mmio_len);
kfraser@11185 247 return -EBUSY;
kfraser@11185 248 }
kfraser@11185 249
kfraser@11185 250 if (request_region(ioaddr, iolen, DRV_NAME) == NULL)
kfraser@11185 251 {
kfraser@11185 252 printk(KERN_ERR DRV_NAME ":I/O resource 0x%lx @ 0x%lx busy\n",
kfraser@11185 253 iolen, ioaddr);
kfraser@11185 254 release_mem_region(mmio_addr, mmio_len);
kfraser@11185 255 return -EBUSY;
kfraser@11185 256 }
kfraser@11185 257
kfraser@11185 258 platform_mmio = mmio_addr;
kfraser@11185 259 platform_mmiolen = mmio_len;
kfraser@11185 260
kfraser@11249 261 ret = get_hypercall_stubs();
kfraser@11185 262 if (ret < 0)
kfraser@11185 263 goto out;
kfraser@11185 264
kfraser@11185 265 if ((ret = init_xen_info()))
kfraser@11185 266 goto out;
kfraser@11185 267
kfraser@13405 268 if ((ret = request_irq(pdev->irq, evtchn_interrupt,
kfraser@13405 269 SA_SHIRQ | SA_SAMPLE_RANDOM,
kfraser@13405 270 "xen-platform-pci", pdev)))
kfraser@11185 271 goto out;
kfraser@11185 272
kfraser@13374 273 if ((ret = set_callback_via(callback_via)))
kfraser@11185 274 goto out;
kfraser@11185 275
kfraser@11185 276 out:
kfraser@11185 277 if (ret) {
kfraser@11185 278 release_mem_region(mmio_addr, mmio_len);
kfraser@11185 279 release_region(ioaddr, iolen);
kfraser@11185 280 }
kfraser@11185 281
kfraser@11185 282 return ret;
kfraser@11185 283 }
kfraser@11185 284
ssmith@11555 285 #define XEN_PLATFORM_VENDOR_ID 0x5853
ssmith@11555 286 #define XEN_PLATFORM_DEVICE_ID 0x0001
kfraser@11185 287 static struct pci_device_id platform_pci_tbl[] __devinitdata = {
kfraser@11185 288 {XEN_PLATFORM_VENDOR_ID, XEN_PLATFORM_DEVICE_ID,
kfraser@11185 289 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
ssmith@11555 290 /* Continue to recognise the old ID for now */
ssmith@11555 291 {0xfffd, 0x0101, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
kfraser@11185 292 {0,}
kfraser@11185 293 };
kfraser@11185 294
kfraser@11185 295 MODULE_DEVICE_TABLE(pci, platform_pci_tbl);
kfraser@11185 296
kfraser@11185 297 static struct pci_driver platform_driver = {
kfraser@11185 298 name: DRV_NAME,
kfraser@11185 299 probe: platform_pci_init,
kfraser@11185 300 remove: __devexit_p(platform_pci_remove),
kfraser@11185 301 id_table: platform_pci_tbl,
kfraser@11185 302 };
kfraser@11185 303
kfraser@11185 304 static int pci_device_registered;
kfraser@11185 305
kfraser@11185 306 static int __init platform_pci_module_init(void)
kfraser@11185 307 {
kfraser@11185 308 int rc;
kfraser@11185 309
kfraser@11185 310 rc = pci_module_init(&platform_driver);
kfraser@11185 311 if (rc)
kfraser@11185 312 printk(KERN_INFO DRV_NAME ":No platform pci device model found\n");
kfraser@11185 313 else
kfraser@11185 314 pci_device_registered = 1;
kfraser@11185 315
kfraser@11185 316 return rc;
kfraser@11185 317 }
kfraser@11185 318
kfraser@11185 319 static void __exit platform_pci_module_cleanup(void)
kfraser@11185 320 {
kfraser@11185 321 printk(KERN_INFO DRV_NAME ":Do platform module cleanup\n");
kfraser@11185 322 /* disable hypervisor for callback irq */
kfraser@13374 323 set_callback_via(0);
kfraser@11185 324 if (pci_device_registered)
kfraser@11185 325 pci_unregister_driver(&platform_driver);
kfraser@11185 326 }
kfraser@11185 327
kfraser@11185 328 module_init(platform_pci_module_init);
kfraser@11185 329 module_exit(platform_pci_module_cleanup);