diff options
Diffstat (limited to 'drivers/hv/vmbus_drv.c')
-rw-r--r-- | drivers/hv/vmbus_drv.c | 108 |
1 files changed, 44 insertions, 64 deletions
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 077bb1bdac34..8e53a3c2607e 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -25,7 +25,6 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/device.h> -#include <linux/irq.h> #include <linux/interrupt.h> #include <linux/sysctl.h> #include <linux/slab.h> @@ -44,6 +43,12 @@ static struct tasklet_struct msg_dpc; static struct completion probe_event; static int irq; +struct resource hyperv_mmio = { + .name = "hyperv mmio", + .flags = IORESOURCE_MEM, +}; +EXPORT_SYMBOL_GPL(hyperv_mmio); + static int vmbus_exists(void) { if (hv_acpi_dev == NULL) @@ -558,9 +563,6 @@ static struct bus_type hv_bus = { .dev_groups = vmbus_groups, }; -static const char *driver_name = "hyperv"; - - struct onmessage_work_context { struct work_struct work; struct hv_message msg; @@ -619,7 +621,7 @@ static void vmbus_on_msg_dpc(unsigned long data) } } -static irqreturn_t vmbus_isr(int irq, void *dev_id) +static void vmbus_isr(void) { int cpu = smp_processor_id(); void *page_addr; @@ -629,7 +631,7 @@ static irqreturn_t vmbus_isr(int irq, void *dev_id) page_addr = hv_context.synic_event_page[cpu]; if (page_addr == NULL) - return IRQ_NONE; + return; event = (union hv_synic_event_flags *)page_addr + VMBUS_MESSAGE_SINT; @@ -665,28 +667,8 @@ static irqreturn_t vmbus_isr(int irq, void *dev_id) msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; /* Check if there are actual msgs to be processed */ - if (msg->header.message_type != HVMSG_NONE) { - handled = true; + if (msg->header.message_type != HVMSG_NONE) tasklet_schedule(&msg_dpc); - } - - if (handled) - return IRQ_HANDLED; - else - return IRQ_NONE; -} - -/* - * vmbus interrupt flow handler: - * vmbus interrupts can concurrently occur on multiple CPUs and - * can be handled concurrently. - */ - -static void vmbus_flow_handler(unsigned int irq, struct irq_desc *desc) -{ - kstat_incr_irqs_this_cpu(irq, desc); - - desc->action->handler(irq, desc->action->dev_id); } /* @@ -715,25 +697,7 @@ static int vmbus_bus_init(int irq) if (ret) goto err_cleanup; - ret = request_irq(irq, vmbus_isr, 0, driver_name, hv_acpi_dev); - - if (ret != 0) { - pr_err("Unable to request IRQ %d\n", - irq); - goto err_unregister; - } - - /* - * Vmbus interrupts can be handled concurrently on - * different CPUs. Establish an appropriate interrupt flow - * handler that can support this model. - */ - irq_set_handler(irq, vmbus_flow_handler); - - /* - * Register our interrupt handler. - */ - hv_register_vmbus_handler(irq, vmbus_isr); + hv_setup_vmbus_irq(vmbus_isr); ret = hv_synic_alloc(); if (ret) @@ -753,9 +717,8 @@ static int vmbus_bus_init(int irq) err_alloc: hv_synic_free(); - free_irq(irq, hv_acpi_dev); + hv_remove_vmbus_irq(); -err_unregister: bus_unregister(&hv_bus); err_cleanup: @@ -886,18 +849,21 @@ void vmbus_device_unregister(struct hv_device *device_obj) /* - * VMBUS is an acpi enumerated device. Get the the IRQ information - * from DSDT. + * VMBUS is an acpi enumerated device. Get the the information we + * need from DSDT. */ -static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *irq) +static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx) { + switch (res->type) { + case ACPI_RESOURCE_TYPE_IRQ: + irq = res->data.irq.interrupts[0]; + break; - if (res->type == ACPI_RESOURCE_TYPE_IRQ) { - struct acpi_resource_irq *irqp; - irqp = &res->data.irq; - - *((unsigned int *)irq) = irqp->interrupts[0]; + case ACPI_RESOURCE_TYPE_ADDRESS64: + hyperv_mmio.start = res->data.address64.minimum; + hyperv_mmio.end = res->data.address64.maximum; + break; } return AE_OK; @@ -906,18 +872,34 @@ static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *irq) static int vmbus_acpi_add(struct acpi_device *device) { acpi_status result; + int ret_val = -ENODEV; hv_acpi_dev = device; result = acpi_walk_resources(device->handle, METHOD_NAME__CRS, - vmbus_walk_resources, &irq); + vmbus_walk_resources, NULL); - if (ACPI_FAILURE(result)) { - complete(&probe_event); - return -ENODEV; + if (ACPI_FAILURE(result)) + goto acpi_walk_err; + /* + * The parent of the vmbus acpi device (Gen2 firmware) is the VMOD that + * has the mmio ranges. Get that. + */ + if (device->parent) { + result = acpi_walk_resources(device->parent->handle, + METHOD_NAME__CRS, + vmbus_walk_resources, NULL); + + if (ACPI_FAILURE(result)) + goto acpi_walk_err; + if (hyperv_mmio.start && hyperv_mmio.end) + request_resource(&iomem_resource, &hyperv_mmio); } + ret_val = 0; + +acpi_walk_err: complete(&probe_event); - return 0; + return ret_val; } static const struct acpi_device_id vmbus_acpi_device_ids[] = { @@ -947,7 +929,6 @@ static int __init hv_acpi_init(void) /* * Get irq resources first. */ - ret = acpi_bus_register_driver(&vmbus_acpi_driver); if (ret) @@ -978,8 +959,7 @@ cleanup: static void __exit vmbus_exit(void) { - - free_irq(irq, hv_acpi_dev); + hv_remove_vmbus_irq(); vmbus_free_channels(); bus_unregister(&hv_bus); hv_cleanup(); |