fiq_glue_setup.c 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. /*
  2. * Copyright (C) 2010 Google, Inc.
  3. *
  4. * This software is licensed under the terms of the GNU General Public
  5. * License version 2, as published by the Free Software Foundation, and
  6. * may be copied, distributed, and modified under those terms.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. */
  13. #include <linux/kernel.h>
  14. #include <linux/percpu.h>
  15. #include <linux/slab.h>
  16. #include <asm/fiq.h>
  17. #include <asm/fiq_glue.h>
  18. extern unsigned char fiq_glue, fiq_glue_end;
  19. extern void fiq_glue_setup(void *func, void *data, void *sp,
  20. fiq_return_handler_t fiq_return_handler);
  21. static struct fiq_handler fiq_debbuger_fiq_handler = {
  22. .name = "fiq_glue",
  23. };
  24. DEFINE_PER_CPU(void *, fiq_stack);
  25. static struct fiq_glue_handler *current_handler;
  26. static fiq_return_handler_t fiq_return_handler;
  27. static DEFINE_MUTEX(fiq_glue_lock);
  28. static void fiq_glue_setup_helper(void *info)
  29. {
  30. struct fiq_glue_handler *handler = info;
  31. fiq_glue_setup(handler->fiq, handler,
  32. __get_cpu_var(fiq_stack) + THREAD_START_SP,
  33. fiq_return_handler);
  34. }
  35. int fiq_glue_register_handler(struct fiq_glue_handler *handler)
  36. {
  37. int ret;
  38. int cpu;
  39. if (!handler || !handler->fiq)
  40. return -EINVAL;
  41. mutex_lock(&fiq_glue_lock);
  42. if (fiq_stack) {
  43. ret = -EBUSY;
  44. goto err_busy;
  45. }
  46. for_each_possible_cpu(cpu) {
  47. void *stack;
  48. stack = (void *)__get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER);
  49. if (WARN_ON(!stack)) {
  50. ret = -ENOMEM;
  51. goto err_alloc_fiq_stack;
  52. }
  53. per_cpu(fiq_stack, cpu) = stack;
  54. }
  55. ret = claim_fiq(&fiq_debbuger_fiq_handler);
  56. if (WARN_ON(ret))
  57. goto err_claim_fiq;
  58. current_handler = handler;
  59. on_each_cpu(fiq_glue_setup_helper, handler, true);
  60. set_fiq_handler(&fiq_glue, &fiq_glue_end - &fiq_glue);
  61. mutex_unlock(&fiq_glue_lock);
  62. return 0;
  63. err_claim_fiq:
  64. err_alloc_fiq_stack:
  65. for_each_possible_cpu(cpu) {
  66. __free_pages(per_cpu(fiq_stack, cpu), THREAD_SIZE_ORDER);
  67. per_cpu(fiq_stack, cpu) = NULL;
  68. }
  69. err_busy:
  70. mutex_unlock(&fiq_glue_lock);
  71. return ret;
  72. }
  73. static void fiq_glue_update_return_handler(void (*fiq_return)(void))
  74. {
  75. fiq_return_handler = fiq_return;
  76. if (current_handler)
  77. on_each_cpu(fiq_glue_setup_helper, current_handler, true);
  78. }
  79. int fiq_glue_set_return_handler(void (*fiq_return)(void))
  80. {
  81. int ret;
  82. mutex_lock(&fiq_glue_lock);
  83. if (fiq_return_handler) {
  84. ret = -EBUSY;
  85. goto err_busy;
  86. }
  87. fiq_glue_update_return_handler(fiq_return);
  88. ret = 0;
  89. err_busy:
  90. mutex_unlock(&fiq_glue_lock);
  91. return ret;
  92. }
  93. EXPORT_SYMBOL(fiq_glue_set_return_handler);
  94. int fiq_glue_clear_return_handler(void (*fiq_return)(void))
  95. {
  96. int ret;
  97. mutex_lock(&fiq_glue_lock);
  98. if (WARN_ON(fiq_return_handler != fiq_return)) {
  99. ret = -EINVAL;
  100. goto err_inval;
  101. }
  102. fiq_glue_update_return_handler(NULL);
  103. ret = 0;
  104. err_inval:
  105. mutex_unlock(&fiq_glue_lock);
  106. return ret;
  107. }
  108. EXPORT_SYMBOL(fiq_glue_clear_return_handler);
  109. /**
  110. * fiq_glue_resume - Restore fiqs after suspend or low power idle states
  111. *
  112. * This must be called before calling local_fiq_enable after returning from a
  113. * power state where the fiq mode registers were lost. If a driver provided
  114. * a resume hook when it registered the handler it will be called.
  115. */
  116. void fiq_glue_resume(void)
  117. {
  118. if (!current_handler)
  119. return;
  120. fiq_glue_setup(current_handler->fiq, current_handler,
  121. __get_cpu_var(fiq_stack) + THREAD_START_SP,
  122. fiq_return_handler);
  123. if (current_handler->resume)
  124. current_handler->resume(current_handler);
  125. }