diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2013-12-18 16:03:57 -0500 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2013-12-18 16:03:57 -0500 |
commit | c354e811a911e680f5c53baa5cdb5d36e660237b (patch) | |
tree | cd6de22440149b4ec805bb71c41c8ae4d8cf04fc | |
parent | 330ebfe36e3dc21f656dfa59d69895e21bf6e9da (diff) | |
parent | 0b8cfb6aa3aabc96177b1e68ef13d2eb5c686606 (diff) |
Merge branch 'pci/host-designware' into next
* pci/host-designware:
PCI: designware: Use typical "for" loop idiom
PCI: designware: Remove redundant call to pci_write_config_word()
PCI: designware: Fix crash in dw_msi_teardown_irq()
-rw-r--r-- | drivers/pci/host/pcie-designware.c | 59 |
1 files changed, 40 insertions, 19 deletions
diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index e33b68be0391..1c92833a4ed3 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c | |||
@@ -209,6 +209,23 @@ static int find_valid_pos0(struct pcie_port *pp, int msgvec, int pos, int *pos0) | |||
209 | return 0; | 209 | return 0; |
210 | } | 210 | } |
211 | 211 | ||
212 | static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base, | ||
213 | unsigned int nvec, unsigned int pos) | ||
214 | { | ||
215 | unsigned int i, res, bit, val; | ||
216 | |||
217 | for (i = 0; i < nvec; i++) { | ||
218 | irq_set_msi_desc_off(irq_base, i, NULL); | ||
219 | clear_bit(pos + i, pp->msi_irq_in_use); | ||
220 | /* Disable corresponding interrupt on MSI interrupt controller */ | ||
221 | res = ((pos + i) / 32) * 12; | ||
222 | bit = (pos + i) % 32; | ||
223 | dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); | ||
224 | val &= ~(1 << bit); | ||
225 | dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); | ||
226 | } | ||
227 | } | ||
228 | |||
212 | static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) | 229 | static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) |
213 | { | 230 | { |
214 | int res, bit, irq, pos0, pos1, i; | 231 | int res, bit, irq, pos0, pos1, i; |
@@ -242,18 +259,25 @@ static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) | |||
242 | if (!irq) | 259 | if (!irq) |
243 | goto no_valid_irq; | 260 | goto no_valid_irq; |
244 | 261 | ||
245 | i = 0; | 262 | /* |
246 | while (i < no_irqs) { | 263 | * irq_create_mapping (called from dw_pcie_host_init) pre-allocates |
264 | * descs so there is no need to allocate descs here. We can therefore | ||
265 | * assume that if irq_find_mapping above returns non-zero, then the | ||
266 | * descs are also successfully allocated. | ||
267 | */ | ||
268 | |||
269 | for (i = 0; i < no_irqs; i++) { | ||
270 | if (irq_set_msi_desc_off(irq, i, desc) != 0) { | ||
271 | clear_irq_range(pp, irq, i, pos0); | ||
272 | goto no_valid_irq; | ||
273 | } | ||
247 | set_bit(pos0 + i, pp->msi_irq_in_use); | 274 | set_bit(pos0 + i, pp->msi_irq_in_use); |
248 | irq_alloc_descs((irq + i), (irq + i), 1, 0); | ||
249 | irq_set_msi_desc(irq + i, desc); | ||
250 | /*Enable corresponding interrupt in MSI interrupt controller */ | 275 | /*Enable corresponding interrupt in MSI interrupt controller */ |
251 | res = ((pos0 + i) / 32) * 12; | 276 | res = ((pos0 + i) / 32) * 12; |
252 | bit = (pos0 + i) % 32; | 277 | bit = (pos0 + i) % 32; |
253 | dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); | 278 | dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); |
254 | val |= 1 << bit; | 279 | val |= 1 << bit; |
255 | dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); | 280 | dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); |
256 | i++; | ||
257 | } | 281 | } |
258 | 282 | ||
259 | *pos = pos0; | 283 | *pos = pos0; |
@@ -266,7 +290,7 @@ no_valid_irq: | |||
266 | 290 | ||
267 | static void clear_irq(unsigned int irq) | 291 | static void clear_irq(unsigned int irq) |
268 | { | 292 | { |
269 | int res, bit, val, pos; | 293 | unsigned int pos, nvec; |
270 | struct irq_desc *desc; | 294 | struct irq_desc *desc; |
271 | struct msi_desc *msi; | 295 | struct msi_desc *msi; |
272 | struct pcie_port *pp; | 296 | struct pcie_port *pp; |
@@ -281,18 +305,15 @@ static void clear_irq(unsigned int irq) | |||
281 | return; | 305 | return; |
282 | } | 306 | } |
283 | 307 | ||
308 | /* undo what was done in assign_irq */ | ||
284 | pos = data->hwirq; | 309 | pos = data->hwirq; |
310 | nvec = 1 << msi->msi_attrib.multiple; | ||
285 | 311 | ||
286 | irq_free_desc(irq); | 312 | clear_irq_range(pp, irq, nvec, pos); |
287 | 313 | ||
288 | clear_bit(pos, pp->msi_irq_in_use); | 314 | /* all irqs cleared; reset attributes */ |
289 | 315 | msi->irq = 0; | |
290 | /* Disable corresponding interrupt on MSI interrupt controller */ | 316 | msi->msi_attrib.multiple = 0; |
291 | res = (pos / 32) * 12; | ||
292 | bit = pos % 32; | ||
293 | dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); | ||
294 | val &= ~(1 << bit); | ||
295 | dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); | ||
296 | } | 317 | } |
297 | 318 | ||
298 | static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, | 319 | static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, |
@@ -320,10 +341,10 @@ static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, | |||
320 | if (irq < 0) | 341 | if (irq < 0) |
321 | return irq; | 342 | return irq; |
322 | 343 | ||
323 | msg_ctr &= ~PCI_MSI_FLAGS_QSIZE; | 344 | /* |
324 | msg_ctr |= msgvec << 4; | 345 | * write_msi_msg() will update PCI_MSI_FLAGS so there is |
325 | pci_write_config_word(pdev, desc->msi_attrib.pos + PCI_MSI_FLAGS, | 346 | * no need to explicitly call pci_write_config_word(). |
326 | msg_ctr); | 347 | */ |
327 | desc->msi_attrib.multiple = msgvec; | 348 | desc->msi_attrib.multiple = msgvec; |
328 | 349 | ||
329 | msg.address_lo = virt_to_phys((void *)pp->msi_data); | 350 | msg.address_lo = virt_to_phys((void *)pp->msi_data); |