cpucfg-emul.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include <linux/smp.h>
  3. #include <linux/types.h>
  4. #include <asm/cpu.h>
  5. #include <asm/cpu-info.h>
  6. #include <asm/elf.h>
  7. #include <loongson_regs.h>
  8. #include <cpucfg-emul.h>
  9. static bool is_loongson(struct cpuinfo_mips *c)
  10. {
  11. switch (c->processor_id & PRID_COMP_MASK) {
  12. case PRID_COMP_LEGACY:
  13. return ((c->processor_id & PRID_IMP_MASK) ==
  14. PRID_IMP_LOONGSON_64C);
  15. case PRID_COMP_LOONGSON:
  16. return true;
  17. default:
  18. return false;
  19. }
  20. }
  21. static u32 get_loongson_fprev(struct cpuinfo_mips *c)
  22. {
  23. return c->fpu_id & LOONGSON_FPREV_MASK;
  24. }
  25. static bool cpu_has_uca(void)
  26. {
  27. u32 diag = read_c0_diag();
  28. u32 new_diag;
  29. if (diag & LOONGSON_DIAG_UCAC)
  30. /* UCA is already enabled. */
  31. return true;
  32. /* See if UCAC bit can be flipped on. This should be safe. */
  33. new_diag = diag | LOONGSON_DIAG_UCAC;
  34. write_c0_diag(new_diag);
  35. new_diag = read_c0_diag();
  36. write_c0_diag(diag);
  37. return (new_diag & LOONGSON_DIAG_UCAC) != 0;
  38. }
  39. static void probe_uca(struct cpuinfo_mips *c)
  40. {
  41. if (cpu_has_uca())
  42. c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_LSUCA;
  43. }
  44. static void decode_loongson_config6(struct cpuinfo_mips *c)
  45. {
  46. u32 config6 = read_c0_config6();
  47. if (config6 & LOONGSON_CONF6_SFBEN)
  48. c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_SFBP;
  49. if (config6 & LOONGSON_CONF6_LLEXC)
  50. c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_LLEXC;
  51. if (config6 & LOONGSON_CONF6_SCRAND)
  52. c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_SCRAND;
  53. }
  54. static void patch_cpucfg_sel1(struct cpuinfo_mips *c)
  55. {
  56. u64 ases = c->ases;
  57. u64 options = c->options;
  58. u32 data = c->loongson3_cpucfg_data[0];
  59. if (options & MIPS_CPU_FPU) {
  60. data |= LOONGSON_CFG1_FP;
  61. data |= get_loongson_fprev(c) << LOONGSON_CFG1_FPREV_OFFSET;
  62. }
  63. if (ases & MIPS_ASE_LOONGSON_MMI)
  64. data |= LOONGSON_CFG1_MMI;
  65. if (ases & MIPS_ASE_MSA)
  66. data |= LOONGSON_CFG1_MSA1;
  67. c->loongson3_cpucfg_data[0] = data;
  68. }
  69. static void patch_cpucfg_sel2(struct cpuinfo_mips *c)
  70. {
  71. u64 ases = c->ases;
  72. u64 options = c->options;
  73. u32 data = c->loongson3_cpucfg_data[1];
  74. if (ases & MIPS_ASE_LOONGSON_EXT)
  75. data |= LOONGSON_CFG2_LEXT1;
  76. if (ases & MIPS_ASE_LOONGSON_EXT2)
  77. data |= LOONGSON_CFG2_LEXT2;
  78. if (options & MIPS_CPU_LDPTE)
  79. data |= LOONGSON_CFG2_LSPW;
  80. if (ases & MIPS_ASE_VZ)
  81. data |= LOONGSON_CFG2_LVZP;
  82. else
  83. data &= ~LOONGSON_CFG2_LVZREV;
  84. c->loongson3_cpucfg_data[1] = data;
  85. }
  86. static void patch_cpucfg_sel3(struct cpuinfo_mips *c)
  87. {
  88. u64 ases = c->ases;
  89. u32 data = c->loongson3_cpucfg_data[2];
  90. if (ases & MIPS_ASE_LOONGSON_CAM) {
  91. data |= LOONGSON_CFG3_LCAMP;
  92. } else {
  93. data &= ~LOONGSON_CFG3_LCAMREV;
  94. data &= ~LOONGSON_CFG3_LCAMNUM;
  95. data &= ~LOONGSON_CFG3_LCAMKW;
  96. data &= ~LOONGSON_CFG3_LCAMVW;
  97. }
  98. c->loongson3_cpucfg_data[2] = data;
  99. }
  100. void loongson3_cpucfg_synthesize_data(struct cpuinfo_mips *c)
  101. {
  102. /* Only engage the logic on Loongson processors. */
  103. if (!is_loongson(c))
  104. return;
  105. /* CPUs with CPUCFG support don't need to synthesize anything. */
  106. if (cpu_has_cfg())
  107. goto have_cpucfg_now;
  108. c->loongson3_cpucfg_data[0] = 0;
  109. c->loongson3_cpucfg_data[1] = 0;
  110. c->loongson3_cpucfg_data[2] = 0;
  111. /* Add CPUCFG features non-discoverable otherwise. */
  112. switch (c->processor_id & (PRID_IMP_MASK | PRID_REV_MASK)) {
  113. case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_0:
  114. case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_1:
  115. case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_2:
  116. case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_3:
  117. decode_loongson_config6(c);
  118. probe_uca(c);
  119. c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_LSLDR0 |
  120. LOONGSON_CFG1_LSSYNCI | LOONGSON_CFG1_LLSYNC |
  121. LOONGSON_CFG1_TGTSYNC);
  122. c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 |
  123. LOONGSON_CFG2_LBT2 | LOONGSON_CFG2_LPMP |
  124. LOONGSON_CFG2_LPM_REV2);
  125. c->loongson3_cpucfg_data[2] = 0;
  126. break;
  127. case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R1:
  128. c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_LSLDR0 |
  129. LOONGSON_CFG1_LSSYNCI | LOONGSON_CFG1_LSUCA |
  130. LOONGSON_CFG1_LLSYNC | LOONGSON_CFG1_TGTSYNC);
  131. c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 |
  132. LOONGSON_CFG2_LPMP | LOONGSON_CFG2_LPM_REV1);
  133. c->loongson3_cpucfg_data[2] |= (
  134. LOONGSON_CFG3_LCAM_REV1 |
  135. LOONGSON_CFG3_LCAMNUM_REV1 |
  136. LOONGSON_CFG3_LCAMKW_REV1 |
  137. LOONGSON_CFG3_LCAMVW_REV1);
  138. break;
  139. case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3B_R1:
  140. case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3B_R2:
  141. c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_LSLDR0 |
  142. LOONGSON_CFG1_LSSYNCI | LOONGSON_CFG1_LSUCA |
  143. LOONGSON_CFG1_LLSYNC | LOONGSON_CFG1_TGTSYNC);
  144. c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 |
  145. LOONGSON_CFG2_LPMP | LOONGSON_CFG2_LPM_REV1);
  146. c->loongson3_cpucfg_data[2] |= (
  147. LOONGSON_CFG3_LCAM_REV1 |
  148. LOONGSON_CFG3_LCAMNUM_REV1 |
  149. LOONGSON_CFG3_LCAMKW_REV1 |
  150. LOONGSON_CFG3_LCAMVW_REV1);
  151. break;
  152. case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R2_0:
  153. case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R2_1:
  154. case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R3_0:
  155. case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R3_1:
  156. decode_loongson_config6(c);
  157. probe_uca(c);
  158. c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_CNT64 |
  159. LOONGSON_CFG1_LSLDR0 | LOONGSON_CFG1_LSPREF |
  160. LOONGSON_CFG1_LSPREFX | LOONGSON_CFG1_LSSYNCI |
  161. LOONGSON_CFG1_LLSYNC | LOONGSON_CFG1_TGTSYNC);
  162. c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 |
  163. LOONGSON_CFG2_LBT2 | LOONGSON_CFG2_LBTMMU |
  164. LOONGSON_CFG2_LPMP | LOONGSON_CFG2_LPM_REV1 |
  165. LOONGSON_CFG2_LVZ_REV1);
  166. c->loongson3_cpucfg_data[2] |= (LOONGSON_CFG3_LCAM_REV1 |
  167. LOONGSON_CFG3_LCAMNUM_REV1 |
  168. LOONGSON_CFG3_LCAMKW_REV1 |
  169. LOONGSON_CFG3_LCAMVW_REV1);
  170. break;
  171. default:
  172. /* It is possible that some future Loongson cores still do
  173. * not have CPUCFG, so do not emulate anything for these
  174. * cores.
  175. */
  176. return;
  177. }
  178. /* This feature is set by firmware, but all known Loongson-64 systems
  179. * are configured this way.
  180. */
  181. c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_CDMAP;
  182. /* Patch in dynamically probed bits. */
  183. patch_cpucfg_sel1(c);
  184. patch_cpucfg_sel2(c);
  185. patch_cpucfg_sel3(c);
  186. have_cpucfg_now:
  187. /* We have usable CPUCFG now, emulated or not.
  188. * Announce CPUCFG availability to userspace via hwcap.
  189. */
  190. elf_hwcap |= HWCAP_LOONGSON_CPUCFG;
  191. }