fiq_glue_setup.c 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  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. static struct fiq_handler fiq_debbuger_fiq_handler = {
  21. .name = "fiq_glue",
  22. };
  23. DEFINE_PER_CPU(void *, fiq_stack);
  24. static struct fiq_glue_handler *current_handler;
  25. static DEFINE_MUTEX(fiq_glue_lock);
  26. static void fiq_glue_setup_helper(void *info)
  27. {
  28. struct fiq_glue_handler *handler = info;
  29. fiq_glue_setup(handler->fiq, handler,
  30. __get_cpu_var(fiq_stack) + THREAD_START_SP);
  31. }
  32. int fiq_glue_register_handler(struct fiq_glue_handler *handler)
  33. {
  34. int ret;
  35. int cpu;
  36. if (!handler || !handler->fiq)
  37. return -EINVAL;
  38. mutex_lock(&fiq_glue_lock);
  39. if (fiq_stack) {
  40. ret = -EBUSY;
  41. goto err_busy;
  42. }
  43. for_each_possible_cpu(cpu) {
  44. void *stack;
  45. stack = (void *)__get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER);
  46. if (WARN_ON(!stack)) {
  47. ret = -ENOMEM;
  48. goto err_alloc_fiq_stack;
  49. }
  50. per_cpu(fiq_stack, cpu) = stack;
  51. }
  52. ret = claim_fiq(&fiq_debbuger_fiq_handler);
  53. if (WARN_ON(ret))
  54. goto err_claim_fiq;
  55. current_handler = handler;
  56. on_each_cpu(fiq_glue_setup_helper, handler, true);
  57. set_fiq_handler(&fiq_glue, &fiq_glue_end - &fiq_glue);
  58. mutex_unlock(&fiq_glue_lock);
  59. return 0;
  60. err_claim_fiq:
  61. err_alloc_fiq_stack:
  62. for_each_possible_cpu(cpu) {
  63. __free_pages(per_cpu(fiq_stack, cpu), THREAD_SIZE_ORDER);
  64. per_cpu(fiq_stack, cpu) = NULL;
  65. }
  66. err_busy:
  67. mutex_unlock(&fiq_glue_lock);
  68. return ret;
  69. }
  70. /**
  71. * fiq_glue_resume - Restore fiqs after suspend or low power idle states
  72. *
  73. * This must be called before calling local_fiq_enable after returning from a
  74. * power state where the fiq mode registers were lost. If a driver provided
  75. * a resume hook when it registered the handler it will be called.
  76. */
  77. void fiq_glue_resume(void)
  78. {
  79. if (!current_handler)
  80. return;
  81. fiq_glue_setup(current_handler->fiq, current_handler,
  82. __get_cpu_var(fiq_stack) + THREAD_START_SP);
  83. if (current_handler->resume)
  84. current_handler->resume(current_handler);
  85. }