dbell.c 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. /*
  2. * Author: Kumar Gala <galak@kernel.crashing.org>
  3. *
  4. * Copyright 2009 Freescale Semiconductor Inc.
  5. *
  6. * This program is free software; you can redistribute it and/or modify it
  7. * under the terms of the GNU General Public License as published by the
  8. * Free Software Foundation; either version 2 of the License, or (at your
  9. * option) any later version.
  10. */
  11. #include <linux/stddef.h>
  12. #include <linux/kernel.h>
  13. #include <linux/smp.h>
  14. #include <linux/threads.h>
  15. #include <linux/percpu.h>
  16. #include <asm/dbell.h>
  17. #include <asm/irq_regs.h>
  18. #ifdef CONFIG_SMP
  19. struct doorbell_cpu_info {
  20. unsigned long messages; /* current messages bits */
  21. unsigned int tag; /* tag value */
  22. };
  23. static DEFINE_PER_CPU(struct doorbell_cpu_info, doorbell_cpu_info);
  24. void doorbell_setup_this_cpu(void)
  25. {
  26. struct doorbell_cpu_info *info = &__get_cpu_var(doorbell_cpu_info);
  27. info->messages = 0;
  28. info->tag = mfspr(SPRN_PIR) & 0x3fff;
  29. }
  30. void doorbell_message_pass(int target, int msg)
  31. {
  32. struct doorbell_cpu_info *info;
  33. int i;
  34. if (target < NR_CPUS) {
  35. info = &per_cpu(doorbell_cpu_info, target);
  36. set_bit(msg, &info->messages);
  37. ppc_msgsnd(PPC_DBELL, 0, info->tag);
  38. }
  39. else if (target == MSG_ALL_BUT_SELF) {
  40. for_each_online_cpu(i) {
  41. if (i == smp_processor_id())
  42. continue;
  43. info = &per_cpu(doorbell_cpu_info, i);
  44. set_bit(msg, &info->messages);
  45. ppc_msgsnd(PPC_DBELL, 0, info->tag);
  46. }
  47. }
  48. else { /* target == MSG_ALL */
  49. for_each_online_cpu(i) {
  50. info = &per_cpu(doorbell_cpu_info, i);
  51. set_bit(msg, &info->messages);
  52. }
  53. ppc_msgsnd(PPC_DBELL, PPC_DBELL_MSG_BRDCAST, 0);
  54. }
  55. }
  56. void doorbell_exception(struct pt_regs *regs)
  57. {
  58. struct pt_regs *old_regs = set_irq_regs(regs);
  59. struct doorbell_cpu_info *info = &__get_cpu_var(doorbell_cpu_info);
  60. int msg;
  61. /* Warning: regs can be NULL when called from irq enable */
  62. if (!info->messages || (num_online_cpus() < 2))
  63. goto out;
  64. for (msg = 0; msg < 4; msg++)
  65. if (test_and_clear_bit(msg, &info->messages))
  66. smp_message_recv(msg);
  67. out:
  68. set_irq_regs(old_regs);
  69. }
  70. void doorbell_check_self(void)
  71. {
  72. struct doorbell_cpu_info *info = &__get_cpu_var(doorbell_cpu_info);
  73. if (!info->messages)
  74. return;
  75. ppc_msgsnd(PPC_DBELL, 0, info->tag);
  76. }
  77. #else /* CONFIG_SMP */
  78. void doorbell_exception(struct pt_regs *regs)
  79. {
  80. printk(KERN_WARNING "Received doorbell on non-smp system\n");
  81. }
  82. #endif /* CONFIG_SMP */