aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjorn Helgaas <bjorn.helgaas@hp.com>2008-06-27 18:57:14 -0400
committerAndi Kleen <andi@basil.nowhere.org>2008-07-16 17:27:07 -0400
commitd5ebde6ef5c2d51828f975a81d7d0e58bccfd833 (patch)
tree04563a7350dfc83784c77c16d0c3d49120dc05ad
parent2d29a7a794c5bae982955cd5dd0a76e766e57f39 (diff)
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: Bjorn Helgaas <bjorn.helgaas@hp.com> Signed-off-by: Andi Kleen <ak@linux.intel.com> Acked-by: Rene Herman <rene.herman@gmail.com> Signed-off-by: Len Brown <len.brown@intel.com>
-rw-r--r--drivers/pnp/interface.c2
-rw-r--r--drivers/pnp/manager.c9
-rw-r--r--drivers/pnp/quirks.c71
-rw-r--r--include/linux/ioport.h1
4 files changed, 39 insertions, 44 deletions
diff --git a/drivers/pnp/interface.c b/drivers/pnp/interface.c
index b09f67de13d0..7a9fb5544b80 100644
--- a/drivers/pnp/interface.c
+++ b/drivers/pnp/interface.c
@@ -90,6 +90,8 @@ static void pnp_print_irq(pnp_info_buffer_t * buffer, char *space,
90 pnp_printf(buffer, " High-Level"); 90 pnp_printf(buffer, " High-Level");
91 if (irq->flags & IORESOURCE_IRQ_LOWLEVEL) 91 if (irq->flags & IORESOURCE_IRQ_LOWLEVEL)
92 pnp_printf(buffer, " Low-Level"); 92 pnp_printf(buffer, " Low-Level");
93 if (irq->flags & IORESOURCE_IRQ_OPTIONAL)
94 pnp_printf(buffer, " (optional)");
93 pnp_printf(buffer, "\n"); 95 pnp_printf(buffer, "\n");
94} 96}
95 97
diff --git a/drivers/pnp/manager.c b/drivers/pnp/manager.c
index 7ea9e1e28003..a20accb5ef8f 100644
--- a/drivers/pnp/manager.c
+++ b/drivers/pnp/manager.c
@@ -153,6 +153,15 @@ static int pnp_assign_irq(struct pnp_dev *dev, struct pnp_irq *rule, int idx)
153 goto __add; 153 goto __add;
154 } 154 }
155 } 155 }
156
157 if (rule->flags & IORESOURCE_IRQ_OPTIONAL) {
158 res->start = -1;
159 res->end = -1;
160 res->flags |= IORESOURCE_DISABLED;
161 dev_dbg(&dev->dev, " irq %d disabled (optional)\n", idx);
162 goto __add;
163 }
164
156 dev_dbg(&dev->dev, " couldn't assign irq %d\n", idx); 165 dev_dbg(&dev->dev, " couldn't assign irq %d\n", idx);
157 return -EBUSY; 166 return -EBUSY;
158 167
diff --git a/drivers/pnp/quirks.c b/drivers/pnp/quirks.c
index 48e60171b3ba..e8515ce0d296 100644
--- a/drivers/pnp/quirks.c
+++ b/drivers/pnp/quirks.c
@@ -121,34 +121,46 @@ static struct pnp_option *quirk_isapnp_mpu_options(struct pnp_dev *dev)
121 struct pnp_option *res; 121 struct pnp_option *res;
122 122
123 /* 123 /*
124 * Build a functional IRQ-less variant of each MPU option. 124 * Build a functional IRQ-optional variant of each MPU option.
125 */ 125 */
126 126
127 for (res = dev->dependent; res; res = res->next) { 127 for (res = dev->dependent; res; res = res->next) {
128 struct pnp_option *curr; 128 struct pnp_option *curr;
129 struct pnp_port *port; 129 struct pnp_port *port;
130 struct pnp_port *copy; 130 struct pnp_port *copy_port;
131 struct pnp_irq *irq;
132 struct pnp_irq *copy_irq;
131 133
132 port = res->port; 134 port = res->port;
133 if (!port || !res->irq) 135 irq = res->irq;
136 if (!port || !irq)
134 continue; 137 continue;
135 138
136 copy = pnp_alloc(sizeof *copy); 139 copy_port = pnp_alloc(sizeof *copy_port);
137 if (!copy) 140 if (!copy_port)
141 break;
142
143 copy_irq = pnp_alloc(sizeof *copy_irq);
144 if (!copy_irq) {
145 kfree(copy_port);
138 break; 146 break;
147 }
148
149 *copy_port = *port;
150 copy_port->next = NULL;
139 151
140 copy->min = port->min; 152 *copy_irq = *irq;
141 copy->max = port->max; 153 copy_irq->flags |= IORESOURCE_IRQ_OPTIONAL;
142 copy->align = port->align; 154 copy_irq->next = NULL;
143 copy->size = port->size;
144 copy->flags = port->flags;
145 155
146 curr = pnp_build_option(PNP_RES_PRIORITY_FUNCTIONAL); 156 curr = pnp_build_option(PNP_RES_PRIORITY_FUNCTIONAL);
147 if (!curr) { 157 if (!curr) {
148 kfree(copy); 158 kfree(copy_port);
159 kfree(copy_irq);
149 break; 160 break;
150 } 161 }
151 curr->port = copy; 162 curr->port = copy_port;
163 curr->irq = copy_irq;
152 164
153 if (prev) 165 if (prev)
154 prev->next = curr; 166 prev->next = curr;
@@ -157,7 +169,7 @@ static struct pnp_option *quirk_isapnp_mpu_options(struct pnp_dev *dev)
157 prev = curr; 169 prev = curr;
158 } 170 }
159 if (head) 171 if (head)
160 dev_info(&dev->dev, "adding IRQ-less MPU options\n"); 172 dev_info(&dev->dev, "adding IRQ-optional MPU options\n");
161 173
162 return head; 174 return head;
163} 175}
@@ -167,10 +179,6 @@ static void quirk_ad1815_mpu_resources(struct pnp_dev *dev)
167 struct pnp_option *res; 179 struct pnp_option *res;
168 struct pnp_irq *irq; 180 struct pnp_irq *irq;
169 181
170 /*
171 * Distribute the independent IRQ over the dependent options
172 */
173
174 res = dev->independent; 182 res = dev->independent;
175 if (!res) 183 if (!res)
176 return; 184 return;
@@ -179,33 +187,8 @@ static void quirk_ad1815_mpu_resources(struct pnp_dev *dev)
179 if (!irq || irq->next) 187 if (!irq || irq->next)
180 return; 188 return;
181 189
182 res = dev->dependent; 190 irq->flags |= IORESOURCE_IRQ_OPTIONAL;
183 if (!res) 191 dev_info(&dev->dev, "made independent IRQ optional\n");
184 return;
185
186 while (1) {
187 struct pnp_irq *copy;
188
189 copy = pnp_alloc(sizeof *copy);
190 if (!copy)
191 break;
192
193 bitmap_copy(copy->map.bits, irq->map.bits, PNP_IRQ_NR);
194 copy->flags = irq->flags;
195
196 copy->next = res->irq; /* Yes, this is NULL */
197 res->irq = copy;
198
199 if (!res->next)
200 break;
201 res = res->next;
202 }
203 kfree(irq);
204
205 res->next = quirk_isapnp_mpu_options(dev);
206
207 res = dev->independent;
208 res->irq = NULL;
209} 192}
210 193
211static void quirk_isapnp_mpu_resources(struct pnp_dev *dev) 194static void quirk_isapnp_mpu_resources(struct pnp_dev *dev)
diff --git a/include/linux/ioport.h b/include/linux/ioport.h
index 39db059ffb8b..2cd07cc29687 100644
--- a/include/linux/ioport.h
+++ b/include/linux/ioport.h
@@ -59,6 +59,7 @@ struct resource_list {
59#define IORESOURCE_IRQ_HIGHLEVEL (1<<2) 59#define IORESOURCE_IRQ_HIGHLEVEL (1<<2)
60#define IORESOURCE_IRQ_LOWLEVEL (1<<3) 60#define IORESOURCE_IRQ_LOWLEVEL (1<<3)
61#define IORESOURCE_IRQ_SHAREABLE (1<<4) 61#define IORESOURCE_IRQ_SHAREABLE (1<<4)
62#define IORESOURCE_IRQ_OPTIONAL (1<<5)
62 63
63/* PnP DMA specific bits (IORESOURCE_BITS) */ 64/* PnP DMA specific bits (IORESOURCE_BITS) */
64#define IORESOURCE_DMA_TYPE_MASK (3<<0) 65#define IORESOURCE_DMA_TYPE_MASK (3<<0)