msi.c 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. /*
  2. * Copyright 2011 Michael Ellerman, IBM Corp.
  3. *
  4. * This program is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU General Public License
  6. * as published by the Free Software Foundation; either version
  7. * 2 of the License, or (at your option) any later version.
  8. */
  9. #include <linux/kernel.h>
  10. #include <linux/pci.h>
  11. #include <linux/msi.h>
  12. #include <linux/irq.h>
  13. #include <linux/interrupt.h>
  14. #include "msi.h"
  15. #include "ics.h"
  16. #include "wsp_pci.h"
  17. /* Magic addresses for 32 & 64-bit MSIs with hardcoded MVE 0 */
  18. #define MSI_ADDR_32 0xFFFF0000ul
  19. #define MSI_ADDR_64 0x1000000000000000ul
  20. int wsp_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
  21. {
  22. struct pci_controller *phb;
  23. struct msi_desc *entry;
  24. struct msi_msg msg;
  25. unsigned int virq;
  26. int hwirq;
  27. phb = pci_bus_to_host(dev->bus);
  28. if (!phb)
  29. return -ENOENT;
  30. entry = list_first_entry(&dev->msi_list, struct msi_desc, list);
  31. if (entry->msi_attrib.is_64) {
  32. msg.address_lo = 0;
  33. msg.address_hi = MSI_ADDR_64 >> 32;
  34. } else {
  35. msg.address_lo = MSI_ADDR_32;
  36. msg.address_hi = 0;
  37. }
  38. list_for_each_entry(entry, &dev->msi_list, list) {
  39. hwirq = wsp_ics_alloc_irq(phb->dn, 1);
  40. if (hwirq < 0) {
  41. dev_warn(&dev->dev, "wsp_msi: hwirq alloc failed!\n");
  42. return hwirq;
  43. }
  44. virq = irq_create_mapping(NULL, hwirq);
  45. if (virq == NO_IRQ) {
  46. dev_warn(&dev->dev, "wsp_msi: virq alloc failed!\n");
  47. return -1;
  48. }
  49. dev_dbg(&dev->dev, "wsp_msi: allocated irq %#x/%#x\n",
  50. hwirq, virq);
  51. wsp_ics_set_msi_chip(virq);
  52. irq_set_msi_desc(virq, entry);
  53. msg.data = hwirq & XIVE_ADDR_MASK;
  54. write_msi_msg(virq, &msg);
  55. }
  56. return 0;
  57. }
  58. void wsp_teardown_msi_irqs(struct pci_dev *dev)
  59. {
  60. struct pci_controller *phb;
  61. struct msi_desc *entry;
  62. int hwirq;
  63. phb = pci_bus_to_host(dev->bus);
  64. dev_dbg(&dev->dev, "wsp_msi: tearing down msi irqs\n");
  65. list_for_each_entry(entry, &dev->msi_list, list) {
  66. if (entry->irq == NO_IRQ)
  67. continue;
  68. irq_set_msi_desc(entry->irq, NULL);
  69. wsp_ics_set_std_chip(entry->irq);
  70. hwirq = virq_to_hw(entry->irq);
  71. /* In this order to avoid racing with irq_create_mapping() */
  72. irq_dispose_mapping(entry->irq);
  73. wsp_ics_free_irq(phb->dn, hwirq);
  74. }
  75. }
  76. void wsp_setup_phb_msi(struct pci_controller *phb)
  77. {
  78. /* Create a single MVE at offset 0 that matches everything */
  79. out_be64(phb->cfg_data + PCIE_REG_IODA_ADDR, PCIE_REG_IODA_AD_TBL_MVT);
  80. out_be64(phb->cfg_data + PCIE_REG_IODA_DATA0, 1ull << 63);
  81. ppc_md.setup_msi_irqs = wsp_setup_msi_irqs;
  82. ppc_md.teardown_msi_irqs = wsp_teardown_msi_irqs;
  83. }