intr_hw.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Tegra host1x Interrupt Management
  4. *
  5. * Copyright (C) 2010 Google, Inc.
  6. * Copyright (c) 2010-2013, NVIDIA Corporation.
  7. */
  8. #include <linux/interrupt.h>
  9. #include <linux/irq.h>
  10. #include <linux/io.h>
  11. #include "../intr.h"
  12. #include "../dev.h"
  13. /*
  14. * Sync point threshold interrupt service function
  15. * Handles sync point threshold triggers, in interrupt context
  16. */
  17. static void host1x_intr_syncpt_handle(struct host1x_syncpt *syncpt)
  18. {
  19. unsigned int id = syncpt->id;
  20. struct host1x *host = syncpt->host;
  21. host1x_sync_writel(host, BIT(id % 32),
  22. HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(id / 32));
  23. host1x_sync_writel(host, BIT(id % 32),
  24. HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(id / 32));
  25. schedule_work(&syncpt->intr.work);
  26. }
  27. static irqreturn_t syncpt_thresh_isr(int irq, void *dev_id)
  28. {
  29. struct host1x *host = dev_id;
  30. unsigned long reg;
  31. unsigned int i, id;
  32. for (i = 0; i < DIV_ROUND_UP(host->info->nb_pts, 32); i++) {
  33. reg = host1x_sync_readl(host,
  34. HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(i));
  35. for_each_set_bit(id, &reg, 32) {
  36. struct host1x_syncpt *syncpt =
  37. host->syncpt + (i * 32 + id);
  38. host1x_intr_syncpt_handle(syncpt);
  39. }
  40. }
  41. return IRQ_HANDLED;
  42. }
  43. static void _host1x_intr_disable_all_syncpt_intrs(struct host1x *host)
  44. {
  45. unsigned int i;
  46. for (i = 0; i < DIV_ROUND_UP(host->info->nb_pts, 32); ++i) {
  47. host1x_sync_writel(host, 0xffffffffu,
  48. HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(i));
  49. host1x_sync_writel(host, 0xffffffffu,
  50. HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(i));
  51. }
  52. }
  53. static void intr_hw_init(struct host1x *host, u32 cpm)
  54. {
  55. #if HOST1X_HW < 6
  56. /* disable the ip_busy_timeout. this prevents write drops */
  57. host1x_sync_writel(host, 0, HOST1X_SYNC_IP_BUSY_TIMEOUT);
  58. /*
  59. * increase the auto-ack timout to the maximum value. 2d will hang
  60. * otherwise on Tegra2.
  61. */
  62. host1x_sync_writel(host, 0xff, HOST1X_SYNC_CTXSW_TIMEOUT_CFG);
  63. /* update host clocks per usec */
  64. host1x_sync_writel(host, cpm, HOST1X_SYNC_USEC_CLK);
  65. #endif
  66. #if HOST1X_HW >= 8
  67. u32 id;
  68. /*
  69. * Program threshold interrupt destination among 8 lines per VM,
  70. * per syncpoint. For now, just direct all to the first interrupt
  71. * line.
  72. */
  73. for (id = 0; id < host->info->nb_pts; id++)
  74. host1x_sync_writel(host, 0, HOST1X_SYNC_SYNCPT_INTR_DEST(id));
  75. #endif
  76. }
  77. static int
  78. _host1x_intr_init_host_sync(struct host1x *host, u32 cpm,
  79. void (*syncpt_thresh_work)(struct work_struct *))
  80. {
  81. unsigned int i;
  82. int err;
  83. host1x_hw_intr_disable_all_syncpt_intrs(host);
  84. for (i = 0; i < host->info->nb_pts; i++)
  85. INIT_WORK(&host->syncpt[i].intr.work, syncpt_thresh_work);
  86. err = devm_request_irq(host->dev, host->intr_syncpt_irq,
  87. syncpt_thresh_isr, IRQF_SHARED,
  88. "host1x_syncpt", host);
  89. if (err < 0) {
  90. WARN_ON(1);
  91. return err;
  92. }
  93. intr_hw_init(host, cpm);
  94. return 0;
  95. }
  96. static void _host1x_intr_set_syncpt_threshold(struct host1x *host,
  97. unsigned int id,
  98. u32 thresh)
  99. {
  100. host1x_sync_writel(host, thresh, HOST1X_SYNC_SYNCPT_INT_THRESH(id));
  101. }
  102. static void _host1x_intr_enable_syncpt_intr(struct host1x *host,
  103. unsigned int id)
  104. {
  105. host1x_sync_writel(host, BIT(id % 32),
  106. HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0(id / 32));
  107. }
  108. static void _host1x_intr_disable_syncpt_intr(struct host1x *host,
  109. unsigned int id)
  110. {
  111. host1x_sync_writel(host, BIT(id % 32),
  112. HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(id / 32));
  113. host1x_sync_writel(host, BIT(id % 32),
  114. HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(id / 32));
  115. }
  116. static int _host1x_free_syncpt_irq(struct host1x *host)
  117. {
  118. unsigned int i;
  119. devm_free_irq(host->dev, host->intr_syncpt_irq, host);
  120. for (i = 0; i < host->info->nb_pts; i++)
  121. cancel_work_sync(&host->syncpt[i].intr.work);
  122. return 0;
  123. }
  124. static const struct host1x_intr_ops host1x_intr_ops = {
  125. .init_host_sync = _host1x_intr_init_host_sync,
  126. .set_syncpt_threshold = _host1x_intr_set_syncpt_threshold,
  127. .enable_syncpt_intr = _host1x_intr_enable_syncpt_intr,
  128. .disable_syncpt_intr = _host1x_intr_disable_syncpt_intr,
  129. .disable_all_syncpt_intrs = _host1x_intr_disable_all_syncpt_intrs,
  130. .free_syncpt_irq = _host1x_free_syncpt_irq,
  131. };