pj4-cp0.c 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. /*
  2. * linux/arch/arm/kernel/pj4-cp0.c
  3. *
  4. * PJ4 iWMMXt coprocessor context switching and handling
  5. *
  6. * Copyright (c) 2010 Marvell International Inc.
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License version 2 as
  10. * published by the Free Software Foundation.
  11. */
  12. #include <linux/types.h>
  13. #include <linux/kernel.h>
  14. #include <linux/signal.h>
  15. #include <linux/sched.h>
  16. #include <linux/init.h>
  17. #include <linux/io.h>
  18. #include <asm/thread_notify.h>
  19. #include <asm/cputype.h>
  20. static int iwmmxt_do(struct notifier_block *self, unsigned long cmd, void *t)
  21. {
  22. struct thread_info *thread = t;
  23. switch (cmd) {
  24. case THREAD_NOTIFY_FLUSH:
  25. /*
  26. * flush_thread() zeroes thread->fpstate, so no need
  27. * to do anything here.
  28. *
  29. * FALLTHROUGH: Ensure we don't try to overwrite our newly
  30. * initialised state information on the first fault.
  31. */
  32. case THREAD_NOTIFY_EXIT:
  33. iwmmxt_task_release(thread);
  34. break;
  35. case THREAD_NOTIFY_SWITCH:
  36. iwmmxt_task_switch(thread);
  37. break;
  38. }
  39. return NOTIFY_DONE;
  40. }
  41. static struct notifier_block __maybe_unused iwmmxt_notifier_block = {
  42. .notifier_call = iwmmxt_do,
  43. };
  44. static u32 __init pj4_cp_access_read(void)
  45. {
  46. u32 value;
  47. __asm__ __volatile__ (
  48. "mrc p15, 0, %0, c1, c0, 2\n\t"
  49. : "=r" (value));
  50. return value;
  51. }
  52. static void __init pj4_cp_access_write(u32 value)
  53. {
  54. u32 temp;
  55. __asm__ __volatile__ (
  56. "mcr p15, 0, %1, c1, c0, 2\n\t"
  57. #ifdef CONFIG_THUMB2_KERNEL
  58. "isb\n\t"
  59. #else
  60. "mrc p15, 0, %0, c1, c0, 2\n\t"
  61. "mov %0, %0\n\t"
  62. "sub pc, pc, #4\n\t"
  63. #endif
  64. : "=r" (temp) : "r" (value));
  65. }
  66. static int __init pj4_get_iwmmxt_version(void)
  67. {
  68. u32 cp_access, wcid;
  69. cp_access = pj4_cp_access_read();
  70. pj4_cp_access_write(cp_access | 0xf);
  71. /* check if coprocessor 0 and 1 are available */
  72. if ((pj4_cp_access_read() & 0xf) != 0xf) {
  73. pj4_cp_access_write(cp_access);
  74. return -ENODEV;
  75. }
  76. /* read iWMMXt coprocessor id register p1, c0 */
  77. __asm__ __volatile__ ("mrc p1, 0, %0, c0, c0, 0\n" : "=r" (wcid));
  78. pj4_cp_access_write(cp_access);
  79. /* iWMMXt v1 */
  80. if ((wcid & 0xffffff00) == 0x56051000)
  81. return 1;
  82. /* iWMMXt v2 */
  83. if ((wcid & 0xffffff00) == 0x56052000)
  84. return 2;
  85. return -EINVAL;
  86. }
  87. /*
  88. * Disable CP0/CP1 on boot, and let call_fpe() and the iWMMXt lazy
  89. * switch code handle iWMMXt context switching.
  90. */
  91. static int __init pj4_cp0_init(void)
  92. {
  93. u32 __maybe_unused cp_access;
  94. int vers;
  95. if (!cpu_is_pj4())
  96. return 0;
  97. vers = pj4_get_iwmmxt_version();
  98. if (vers < 0)
  99. return 0;
  100. #ifndef CONFIG_IWMMXT
  101. pr_info("PJ4 iWMMXt coprocessor detected, but kernel support is missing.\n");
  102. #else
  103. cp_access = pj4_cp_access_read() & ~0xf;
  104. pj4_cp_access_write(cp_access);
  105. pr_info("PJ4 iWMMXt v%d coprocessor enabled.\n", vers);
  106. elf_hwcap |= HWCAP_IWMMXT;
  107. thread_register_notifier(&iwmmxt_notifier_block);
  108. #endif
  109. return 0;
  110. }
  111. late_initcall(pj4_cp0_init);