modem-irq-db5500.c 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. /*
  2. * Copyright (C) ST-Ericsson SA 2010
  3. * Author: Stefan Nilsson <stefan.xk.nilsson@stericsson.com> for ST-Ericsson.
  4. * Author: Martin Persson <martin.persson@stericsson.com> for ST-Ericsson.
  5. * License terms: GNU General Public License (GPL), version 2.
  6. */
  7. #include <linux/module.h>
  8. #include <linux/kernel.h>
  9. #include <linux/irq.h>
  10. #include <linux/interrupt.h>
  11. #include <linux/io.h>
  12. #include <linux/slab.h>
  13. #include <mach/id.h>
  14. #define MODEM_INTCON_BASE_ADDR 0xBFFD3000
  15. #define MODEM_INTCON_SIZE 0xFFF
  16. #define DEST_IRQ41_OFFSET 0x2A4
  17. #define DEST_IRQ43_OFFSET 0x2AC
  18. #define DEST_IRQ45_OFFSET 0x2B4
  19. #define PRIO_IRQ41_OFFSET 0x6A4
  20. #define PRIO_IRQ43_OFFSET 0x6AC
  21. #define PRIO_IRQ45_OFFSET 0x6B4
  22. #define ALLOW_IRQ_OFFSET 0x104
  23. #define MODEM_INTCON_CPU_NBR 0x1
  24. #define MODEM_INTCON_PRIO_HIGH 0x0
  25. #define MODEM_INTCON_ALLOW_IRQ41 0x0200
  26. #define MODEM_INTCON_ALLOW_IRQ43 0x0800
  27. #define MODEM_INTCON_ALLOW_IRQ45 0x2000
  28. #define MODEM_IRQ_REG_OFFSET 0x4
  29. struct modem_irq {
  30. void __iomem *modem_intcon_base;
  31. };
  32. static void setup_modem_intcon(void __iomem *modem_intcon_base)
  33. {
  34. /* IC_DESTINATION_BASE_ARRAY - Which CPU to receive the IRQ */
  35. writel(MODEM_INTCON_CPU_NBR, modem_intcon_base + DEST_IRQ41_OFFSET);
  36. writel(MODEM_INTCON_CPU_NBR, modem_intcon_base + DEST_IRQ43_OFFSET);
  37. writel(MODEM_INTCON_CPU_NBR, modem_intcon_base + DEST_IRQ45_OFFSET);
  38. /* IC_PRIORITY_BASE_ARRAY - IRQ priority in modem IRQ controller */
  39. writel(MODEM_INTCON_PRIO_HIGH, modem_intcon_base + PRIO_IRQ41_OFFSET);
  40. writel(MODEM_INTCON_PRIO_HIGH, modem_intcon_base + PRIO_IRQ43_OFFSET);
  41. writel(MODEM_INTCON_PRIO_HIGH, modem_intcon_base + PRIO_IRQ45_OFFSET);
  42. /* IC_ALLOW_ARRAY - IRQ enable */
  43. writel(MODEM_INTCON_ALLOW_IRQ41 |
  44. MODEM_INTCON_ALLOW_IRQ43 |
  45. MODEM_INTCON_ALLOW_IRQ45,
  46. modem_intcon_base + ALLOW_IRQ_OFFSET);
  47. }
  48. static irqreturn_t modem_cpu_irq_handler(int irq, void *data)
  49. {
  50. int real_irq;
  51. int virt_irq;
  52. struct modem_irq *mi = (struct modem_irq *)data;
  53. /* Read modem side IRQ number from modem IRQ controller */
  54. real_irq = readl(mi->modem_intcon_base + MODEM_IRQ_REG_OFFSET) & 0xFF;
  55. virt_irq = IRQ_MODEM_EVENTS_BASE + real_irq;
  56. pr_debug("modem_irq: Worker read addr 0x%X and got value 0x%X "
  57. "which will be 0x%X (%d) which translates to "
  58. "virtual IRQ 0x%X (%d)!\n",
  59. (u32)mi->modem_intcon_base + MODEM_IRQ_REG_OFFSET,
  60. real_irq,
  61. real_irq & 0xFF,
  62. real_irq & 0xFF,
  63. virt_irq,
  64. virt_irq);
  65. if (virt_irq != 0)
  66. generic_handle_irq(virt_irq);
  67. pr_debug("modem_irq: Done handling virtual IRQ %d!\n", virt_irq);
  68. return IRQ_HANDLED;
  69. }
  70. static void create_virtual_irq(int irq, struct irq_chip *modem_irq_chip)
  71. {
  72. irq_set_chip_and_handler(irq, modem_irq_chip, handle_simple_irq);
  73. set_irq_flags(irq, IRQF_VALID);
  74. pr_debug("modem_irq: Created virtual IRQ %d\n", irq);
  75. }
  76. static int modem_irq_init(void)
  77. {
  78. int err;
  79. static struct irq_chip modem_irq_chip;
  80. struct modem_irq *mi;
  81. if (!cpu_is_u5500())
  82. return -ENODEV;
  83. pr_info("modem_irq: Set up IRQ handler for incoming modem IRQ %d\n",
  84. IRQ_DB5500_MODEM);
  85. mi = kmalloc(sizeof(struct modem_irq), GFP_KERNEL);
  86. if (!mi) {
  87. pr_err("modem_irq: Could not allocate device\n");
  88. return -ENOMEM;
  89. }
  90. mi->modem_intcon_base =
  91. ioremap(MODEM_INTCON_BASE_ADDR, MODEM_INTCON_SIZE);
  92. pr_debug("modem_irq: ioremapped modem_intcon_base from "
  93. "phy 0x%x to virt 0x%x\n", MODEM_INTCON_BASE_ADDR,
  94. (u32)mi->modem_intcon_base);
  95. setup_modem_intcon(mi->modem_intcon_base);
  96. modem_irq_chip = dummy_irq_chip;
  97. modem_irq_chip.name = "modem_irq";
  98. /* Create the virtual IRQ:s needed */
  99. create_virtual_irq(MBOX_PAIR0_VIRT_IRQ, &modem_irq_chip);
  100. create_virtual_irq(MBOX_PAIR1_VIRT_IRQ, &modem_irq_chip);
  101. create_virtual_irq(MBOX_PAIR2_VIRT_IRQ, &modem_irq_chip);
  102. err = request_threaded_irq(IRQ_DB5500_MODEM, NULL,
  103. modem_cpu_irq_handler, IRQF_ONESHOT,
  104. "modem_irq", mi);
  105. if (err)
  106. pr_err("modem_irq: Could not register IRQ %d\n",
  107. IRQ_DB5500_MODEM);
  108. return 0;
  109. }
  110. arch_initcall(modem_irq_init);