diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2014-10-01 18:45:01 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2014-10-01 18:45:01 -0400 |
commit | b9c9c2558ba366d59dc242872da883a879b8ffc6 (patch) | |
tree | b2c15a99796161df92eb27ce80ec6b52cb7ca12f /drivers/pci | |
parent | 5d85142b9e5264a925e4d582c3b0d7297d9c2713 (diff) | |
parent | c8df6ac9452e8f47a6f660993c526d13e858a6f3 (diff) |
Merge branch 'pci/host-designware' into next
* pci/host-designware:
PCI: designware: Remove open-coded bitmap operations
PCI: designware: Setup and clear exactly one MSI at a time
Conflicts:
drivers/pci/host/pcie-designware.c
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/host/pcie-designware.c | 97 |
1 files changed, 14 insertions, 83 deletions
diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index f72744599d12..dfed00aa3ac0 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c | |||
@@ -196,30 +196,6 @@ void dw_pcie_msi_init(struct pcie_port *pp) | |||
196 | dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, 0); | 196 | dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, 0); |
197 | } | 197 | } |
198 | 198 | ||
199 | static int find_valid_pos0(struct pcie_port *pp, int msgvec, int pos, int *pos0) | ||
200 | { | ||
201 | int flag = 1; | ||
202 | |||
203 | do { | ||
204 | pos = find_next_zero_bit(pp->msi_irq_in_use, | ||
205 | MAX_MSI_IRQS, pos); | ||
206 | /*if you have reached to the end then get out from here.*/ | ||
207 | if (pos == MAX_MSI_IRQS) | ||
208 | return -ENOSPC; | ||
209 | /* | ||
210 | * Check if this position is at correct offset.nvec is always a | ||
211 | * power of two. pos0 must be nvec bit aligned. | ||
212 | */ | ||
213 | if (pos % msgvec) | ||
214 | pos += msgvec - (pos % msgvec); | ||
215 | else | ||
216 | flag = 0; | ||
217 | } while (flag); | ||
218 | |||
219 | *pos0 = pos; | ||
220 | return 0; | ||
221 | } | ||
222 | |||
223 | static void dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq) | 199 | static void dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq) |
224 | { | 200 | { |
225 | unsigned int res, bit, val; | 201 | unsigned int res, bit, val; |
@@ -238,13 +214,14 @@ static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base, | |||
238 | 214 | ||
239 | for (i = 0; i < nvec; i++) { | 215 | for (i = 0; i < nvec; i++) { |
240 | irq_set_msi_desc_off(irq_base, i, NULL); | 216 | irq_set_msi_desc_off(irq_base, i, NULL); |
241 | clear_bit(pos + i, pp->msi_irq_in_use); | ||
242 | /* Disable corresponding interrupt on MSI controller */ | 217 | /* Disable corresponding interrupt on MSI controller */ |
243 | if (pp->ops->msi_clear_irq) | 218 | if (pp->ops->msi_clear_irq) |
244 | pp->ops->msi_clear_irq(pp, pos + i); | 219 | pp->ops->msi_clear_irq(pp, pos + i); |
245 | else | 220 | else |
246 | dw_pcie_msi_clear_irq(pp, pos + i); | 221 | dw_pcie_msi_clear_irq(pp, pos + i); |
247 | } | 222 | } |
223 | |||
224 | bitmap_release_region(pp->msi_irq_in_use, pos, order_base_2(nvec)); | ||
248 | } | 225 | } |
249 | 226 | ||
250 | static void dw_pcie_msi_set_irq(struct pcie_port *pp, int irq) | 227 | static void dw_pcie_msi_set_irq(struct pcie_port *pp, int irq) |
@@ -260,26 +237,13 @@ static void dw_pcie_msi_set_irq(struct pcie_port *pp, int irq) | |||
260 | 237 | ||
261 | static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) | 238 | static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) |
262 | { | 239 | { |
263 | int irq, pos0, pos1, i; | 240 | int irq, pos0, i; |
264 | struct pcie_port *pp = sys_to_pcie(desc->dev->bus->sysdata); | 241 | struct pcie_port *pp = sys_to_pcie(desc->dev->bus->sysdata); |
265 | 242 | ||
266 | pos0 = find_first_zero_bit(pp->msi_irq_in_use, | 243 | pos0 = bitmap_find_free_region(pp->msi_irq_in_use, MAX_MSI_IRQS, |
267 | MAX_MSI_IRQS); | 244 | order_base_2(no_irqs)); |
268 | if (pos0 % no_irqs) { | 245 | if (pos0 < 0) |
269 | if (find_valid_pos0(pp, no_irqs, pos0, &pos0)) | 246 | goto no_valid_irq; |
270 | goto no_valid_irq; | ||
271 | } | ||
272 | if (no_irqs > 1) { | ||
273 | pos1 = find_next_bit(pp->msi_irq_in_use, | ||
274 | MAX_MSI_IRQS, pos0); | ||
275 | /* there must be nvec number of consecutive free bits */ | ||
276 | while ((pos1 - pos0) < no_irqs) { | ||
277 | if (find_valid_pos0(pp, no_irqs, pos1, &pos0)) | ||
278 | goto no_valid_irq; | ||
279 | pos1 = find_next_bit(pp->msi_irq_in_use, | ||
280 | MAX_MSI_IRQS, pos0); | ||
281 | } | ||
282 | } | ||
283 | 247 | ||
284 | irq = irq_find_mapping(pp->irq_domain, pos0); | 248 | irq = irq_find_mapping(pp->irq_domain, pos0); |
285 | if (!irq) | 249 | if (!irq) |
@@ -297,7 +261,6 @@ static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) | |||
297 | clear_irq_range(pp, irq, i, pos0); | 261 | clear_irq_range(pp, irq, i, pos0); |
298 | goto no_valid_irq; | 262 | goto no_valid_irq; |
299 | } | 263 | } |
300 | set_bit(pos0 + i, pp->msi_irq_in_use); | ||
301 | /*Enable corresponding interrupt in MSI interrupt controller */ | 264 | /*Enable corresponding interrupt in MSI interrupt controller */ |
302 | if (pp->ops->msi_set_irq) | 265 | if (pp->ops->msi_set_irq) |
303 | pp->ops->msi_set_irq(pp, pos0 + i); | 266 | pp->ops->msi_set_irq(pp, pos0 + i); |
@@ -313,53 +276,17 @@ no_valid_irq: | |||
313 | return -ENOSPC; | 276 | return -ENOSPC; |
314 | } | 277 | } |
315 | 278 | ||
316 | static void clear_irq(unsigned int irq) | ||
317 | { | ||
318 | unsigned int pos, nvec; | ||
319 | struct msi_desc *msi; | ||
320 | struct pcie_port *pp; | ||
321 | struct irq_data *data = irq_get_irq_data(irq); | ||
322 | |||
323 | /* get the port structure */ | ||
324 | msi = irq_data_get_msi(data); | ||
325 | pp = sys_to_pcie(msi->dev->bus->sysdata); | ||
326 | |||
327 | /* undo what was done in assign_irq */ | ||
328 | pos = data->hwirq; | ||
329 | nvec = 1 << msi->msi_attrib.multiple; | ||
330 | |||
331 | clear_irq_range(pp, irq, nvec, pos); | ||
332 | |||
333 | /* all irqs cleared; reset attributes */ | ||
334 | msi->irq = 0; | ||
335 | msi->msi_attrib.multiple = 0; | ||
336 | } | ||
337 | |||
338 | static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, | 279 | static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, |
339 | struct msi_desc *desc) | 280 | struct msi_desc *desc) |
340 | { | 281 | { |
341 | int irq, pos, msgvec; | 282 | int irq, pos; |
342 | u16 msg_ctr; | ||
343 | struct msi_msg msg; | 283 | struct msi_msg msg; |
344 | struct pcie_port *pp = sys_to_pcie(pdev->bus->sysdata); | 284 | struct pcie_port *pp = sys_to_pcie(pdev->bus->sysdata); |
345 | 285 | ||
346 | pci_read_config_word(pdev, pdev->msi_cap + PCI_MSI_FLAGS, &msg_ctr); | 286 | irq = assign_irq(1, desc, &pos); |
347 | msgvec = (msg_ctr & PCI_MSI_FLAGS_QSIZE) >> 4; | ||
348 | if (msgvec == 0) | ||
349 | msgvec = (msg_ctr & PCI_MSI_FLAGS_QMASK) >> 1; | ||
350 | if (msgvec > 5) | ||
351 | msgvec = 0; | ||
352 | |||
353 | irq = assign_irq((1 << msgvec), desc, &pos); | ||
354 | if (irq < 0) | 287 | if (irq < 0) |
355 | return irq; | 288 | return irq; |
356 | 289 | ||
357 | /* | ||
358 | * write_msi_msg() will update PCI_MSI_FLAGS so there is | ||
359 | * no need to explicitly call pci_write_config_word(). | ||
360 | */ | ||
361 | desc->msi_attrib.multiple = msgvec; | ||
362 | |||
363 | if (pp->ops->get_msi_addr) | 290 | if (pp->ops->get_msi_addr) |
364 | msg.address_lo = pp->ops->get_msi_addr(pp); | 291 | msg.address_lo = pp->ops->get_msi_addr(pp); |
365 | else | 292 | else |
@@ -378,7 +305,11 @@ static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, | |||
378 | 305 | ||
379 | static void dw_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) | 306 | static void dw_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) |
380 | { | 307 | { |
381 | clear_irq(irq); | 308 | struct irq_data *data = irq_get_irq_data(irq); |
309 | struct msi_desc *msi = irq_data_get_msi(data); | ||
310 | struct pcie_port *pp = sys_to_pcie(msi->dev->bus->sysdata); | ||
311 | |||
312 | clear_irq_range(pp, irq, 1, data->hwirq); | ||
382 | } | 313 | } |
383 | 314 | ||
384 | static struct msi_chip dw_pcie_msi_chip = { | 315 | static struct msi_chip dw_pcie_msi_chip = { |