Commit d5ebde6e authored by Bjorn Helgaas's avatar Bjorn Helgaas Committed by Andi Kleen

PNP: support optional IRQ resources

This patch adds an IORESOURCE_IRQ_OPTIONAL flag for use when
assigning resources to a device.  If the flag is set and we are
unable to assign an IRQ to the device, we can leave the IRQ
disabled but allow the overall resource allocation to succeed.

Some devices request an IRQ, but can run without an IRQ
(possibly with degraded performance).  This flag lets us run
the device without the IRQ instead of just leaving the
device disabled.

This is a reimplementation of this previous change by Rene
Herman <rene.herman@gmail.com>:
    http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=3b73a223661ed137c5d3d2635f954382e94f5a43

I reimplemented this for two reasons:
    - to prepare for converting all resource options into a single linked
      list, as opposed to the per-resource-type lists we have now, and
    - to preserve the order and number of resource options.

In PNPBIOS and ACPI, we configure a device by giving firmware a
list of resource assignments.  It is important that this list
has exactly the same number of resources, in the same order,
as the "template" list we got from the firmware in the first
place.

The problem of a sound card MPU401 being left disabled for want of
an IRQ was reported by Uwe Bugla <uwe.bugla@gmx.de>.
Signed-off-by: default avatarBjorn Helgaas <bjorn.helgaas@hp.com>
Signed-off-by: default avatarAndi Kleen <ak@linux.intel.com>
Acked-by: default avatarRene Herman <rene.herman@gmail.com>
Signed-off-by: default avatarLen Brown <len.brown@intel.com>
parent 2d29a7a7
...@@ -90,6 +90,8 @@ static void pnp_print_irq(pnp_info_buffer_t * buffer, char *space, ...@@ -90,6 +90,8 @@ static void pnp_print_irq(pnp_info_buffer_t * buffer, char *space,
pnp_printf(buffer, " High-Level"); pnp_printf(buffer, " High-Level");
if (irq->flags & IORESOURCE_IRQ_LOWLEVEL) if (irq->flags & IORESOURCE_IRQ_LOWLEVEL)
pnp_printf(buffer, " Low-Level"); pnp_printf(buffer, " Low-Level");
if (irq->flags & IORESOURCE_IRQ_OPTIONAL)
pnp_printf(buffer, " (optional)");
pnp_printf(buffer, "\n"); pnp_printf(buffer, "\n");
} }
......
...@@ -153,6 +153,15 @@ static int pnp_assign_irq(struct pnp_dev *dev, struct pnp_irq *rule, int idx) ...@@ -153,6 +153,15 @@ static int pnp_assign_irq(struct pnp_dev *dev, struct pnp_irq *rule, int idx)
goto __add; goto __add;
} }
} }
if (rule->flags & IORESOURCE_IRQ_OPTIONAL) {
res->start = -1;
res->end = -1;
res->flags |= IORESOURCE_DISABLED;
dev_dbg(&dev->dev, " irq %d disabled (optional)\n", idx);
goto __add;
}
dev_dbg(&dev->dev, " couldn't assign irq %d\n", idx); dev_dbg(&dev->dev, " couldn't assign irq %d\n", idx);
return -EBUSY; return -EBUSY;
......
...@@ -121,34 +121,46 @@ static struct pnp_option *quirk_isapnp_mpu_options(struct pnp_dev *dev) ...@@ -121,34 +121,46 @@ static struct pnp_option *quirk_isapnp_mpu_options(struct pnp_dev *dev)
struct pnp_option *res; struct pnp_option *res;
/* /*
* Build a functional IRQ-less variant of each MPU option. * Build a functional IRQ-optional variant of each MPU option.
*/ */
for (res = dev->dependent; res; res = res->next) { for (res = dev->dependent; res; res = res->next) {
struct pnp_option *curr; struct pnp_option *curr;
struct pnp_port *port; struct pnp_port *port;
struct pnp_port *copy; struct pnp_port *copy_port;
struct pnp_irq *irq;
struct pnp_irq *copy_irq;
port = res->port; port = res->port;
if (!port || !res->irq) irq = res->irq;
if (!port || !irq)
continue; continue;
copy = pnp_alloc(sizeof *copy); copy_port = pnp_alloc(sizeof *copy_port);
if (!copy) if (!copy_port)
break; break;
copy->min = port->min; copy_irq = pnp_alloc(sizeof *copy_irq);
copy->max = port->max; if (!copy_irq) {
copy->align = port->align; kfree(copy_port);
copy->size = port->size; break;
copy->flags = port->flags; }
*copy_port = *port;
copy_port->next = NULL;
*copy_irq = *irq;
copy_irq->flags |= IORESOURCE_IRQ_OPTIONAL;
copy_irq->next = NULL;
curr = pnp_build_option(PNP_RES_PRIORITY_FUNCTIONAL); curr = pnp_build_option(PNP_RES_PRIORITY_FUNCTIONAL);
if (!curr) { if (!curr) {
kfree(copy); kfree(copy_port);
kfree(copy_irq);
break; break;
} }
curr->port = copy; curr->port = copy_port;
curr->irq = copy_irq;
if (prev) if (prev)
prev->next = curr; prev->next = curr;
...@@ -157,7 +169,7 @@ static struct pnp_option *quirk_isapnp_mpu_options(struct pnp_dev *dev) ...@@ -157,7 +169,7 @@ static struct pnp_option *quirk_isapnp_mpu_options(struct pnp_dev *dev)
prev = curr; prev = curr;
} }
if (head) if (head)
dev_info(&dev->dev, "adding IRQ-less MPU options\n"); dev_info(&dev->dev, "adding IRQ-optional MPU options\n");
return head; return head;
} }
...@@ -167,10 +179,6 @@ static void quirk_ad1815_mpu_resources(struct pnp_dev *dev) ...@@ -167,10 +179,6 @@ static void quirk_ad1815_mpu_resources(struct pnp_dev *dev)
struct pnp_option *res; struct pnp_option *res;
struct pnp_irq *irq; struct pnp_irq *irq;
/*
* Distribute the independent IRQ over the dependent options
*/
res = dev->independent; res = dev->independent;
if (!res) if (!res)
return; return;
...@@ -179,33 +187,8 @@ static void quirk_ad1815_mpu_resources(struct pnp_dev *dev) ...@@ -179,33 +187,8 @@ static void quirk_ad1815_mpu_resources(struct pnp_dev *dev)
if (!irq || irq->next) if (!irq || irq->next)
return; return;
res = dev->dependent; irq->flags |= IORESOURCE_IRQ_OPTIONAL;
if (!res) dev_info(&dev->dev, "made independent IRQ optional\n");
return;
while (1) {
struct pnp_irq *copy;
copy = pnp_alloc(sizeof *copy);
if (!copy)
break;
bitmap_copy(copy->map.bits, irq->map.bits, PNP_IRQ_NR);
copy->flags = irq->flags;
copy->next = res->irq; /* Yes, this is NULL */
res->irq = copy;
if (!res->next)
break;
res = res->next;
}
kfree(irq);
res->next = quirk_isapnp_mpu_options(dev);
res = dev->independent;
res->irq = NULL;
} }
static void quirk_isapnp_mpu_resources(struct pnp_dev *dev) static void quirk_isapnp_mpu_resources(struct pnp_dev *dev)
......
...@@ -59,6 +59,7 @@ struct resource_list { ...@@ -59,6 +59,7 @@ struct resource_list {
#define IORESOURCE_IRQ_HIGHLEVEL (1<<2) #define IORESOURCE_IRQ_HIGHLEVEL (1<<2)
#define IORESOURCE_IRQ_LOWLEVEL (1<<3) #define IORESOURCE_IRQ_LOWLEVEL (1<<3)
#define IORESOURCE_IRQ_SHAREABLE (1<<4) #define IORESOURCE_IRQ_SHAREABLE (1<<4)
#define IORESOURCE_IRQ_OPTIONAL (1<<5)
/* PnP DMA specific bits (IORESOURCE_BITS) */ /* PnP DMA specific bits (IORESOURCE_BITS) */
#define IORESOURCE_DMA_TYPE_MASK (3<<0) #define IORESOURCE_DMA_TYPE_MASK (3<<0)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment