platsmp.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. /*
  2. * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. */
  14. #define pr_fmt(fmt) "uniphier: " fmt
  15. #include <linux/init.h>
  16. #include <linux/io.h>
  17. #include <linux/ioport.h>
  18. #include <linux/of.h>
  19. #include <linux/of_address.h>
  20. #include <linux/sizes.h>
  21. #include <asm/cacheflush.h>
  22. #include <asm/hardware/cache-uniphier.h>
  23. #include <asm/pgtable.h>
  24. #include <asm/smp.h>
  25. #include <asm/smp_scu.h>
  26. /*
  27. * The secondary CPUs check this register from the boot ROM for the jump
  28. * destination. After that, it can be reused as a scratch register.
  29. */
  30. #define UNIPHIER_SBC_ROM_BOOT_RSV2 0x1208
  31. static void __iomem *uniphier_smp_rom_boot_rsv2;
  32. static unsigned int uniphier_smp_max_cpus;
  33. extern char uniphier_smp_trampoline;
  34. extern char uniphier_smp_trampoline_jump;
  35. extern char uniphier_smp_trampoline_poll_addr;
  36. extern char uniphier_smp_trampoline_end;
  37. /*
  38. * Copy trampoline code to the tail of the 1st section of the page table used
  39. * in the boot ROM. This area is directly accessible by the secondary CPUs
  40. * for all the UniPhier SoCs.
  41. */
  42. static const phys_addr_t uniphier_smp_trampoline_dest_end = SECTION_SIZE;
  43. static phys_addr_t uniphier_smp_trampoline_dest;
  44. static int __init uniphier_smp_copy_trampoline(phys_addr_t poll_addr)
  45. {
  46. size_t trmp_size;
  47. static void __iomem *trmp_base;
  48. if (!uniphier_cache_l2_is_enabled()) {
  49. pr_warn("outer cache is needed for SMP, but not enabled\n");
  50. return -ENODEV;
  51. }
  52. uniphier_cache_l2_set_locked_ways(1);
  53. outer_flush_all();
  54. trmp_size = &uniphier_smp_trampoline_end - &uniphier_smp_trampoline;
  55. uniphier_smp_trampoline_dest = uniphier_smp_trampoline_dest_end -
  56. trmp_size;
  57. uniphier_cache_l2_touch_range(uniphier_smp_trampoline_dest,
  58. uniphier_smp_trampoline_dest_end);
  59. trmp_base = ioremap_cache(uniphier_smp_trampoline_dest, trmp_size);
  60. if (!trmp_base) {
  61. pr_err("failed to map trampoline destination area\n");
  62. return -ENOMEM;
  63. }
  64. memcpy(trmp_base, &uniphier_smp_trampoline, trmp_size);
  65. writel(virt_to_phys(secondary_startup),
  66. trmp_base + (&uniphier_smp_trampoline_jump -
  67. &uniphier_smp_trampoline));
  68. writel(poll_addr, trmp_base + (&uniphier_smp_trampoline_poll_addr -
  69. &uniphier_smp_trampoline));
  70. flush_cache_all(); /* flush out trampoline code to outer cache */
  71. iounmap(trmp_base);
  72. return 0;
  73. }
  74. static int __init uniphier_smp_prepare_trampoline(unsigned int max_cpus)
  75. {
  76. struct device_node *np;
  77. struct resource res;
  78. phys_addr_t rom_rsv2_phys;
  79. int ret;
  80. np = of_find_compatible_node(NULL, NULL,
  81. "socionext,uniphier-system-bus-controller");
  82. ret = of_address_to_resource(np, 1, &res);
  83. if (ret) {
  84. pr_err("failed to get resource of system-bus-controller\n");
  85. return ret;
  86. }
  87. rom_rsv2_phys = res.start + UNIPHIER_SBC_ROM_BOOT_RSV2;
  88. ret = uniphier_smp_copy_trampoline(rom_rsv2_phys);
  89. if (ret)
  90. return ret;
  91. uniphier_smp_rom_boot_rsv2 = ioremap(rom_rsv2_phys, sizeof(SZ_4));
  92. if (!uniphier_smp_rom_boot_rsv2) {
  93. pr_err("failed to map ROM_BOOT_RSV2 register\n");
  94. return -ENOMEM;
  95. }
  96. writel(uniphier_smp_trampoline_dest, uniphier_smp_rom_boot_rsv2);
  97. asm("sev"); /* Bring up all secondary CPUs to the trampoline code */
  98. uniphier_smp_max_cpus = max_cpus; /* save for later use */
  99. return 0;
  100. }
  101. static void __init uniphier_smp_unprepare_trampoline(void)
  102. {
  103. iounmap(uniphier_smp_rom_boot_rsv2);
  104. if (uniphier_smp_trampoline_dest)
  105. outer_inv_range(uniphier_smp_trampoline_dest,
  106. uniphier_smp_trampoline_dest_end);
  107. uniphier_cache_l2_set_locked_ways(0);
  108. }
  109. static int __init uniphier_smp_enable_scu(void)
  110. {
  111. unsigned long scu_base_phys = 0;
  112. void __iomem *scu_base;
  113. if (scu_a9_has_base())
  114. scu_base_phys = scu_a9_get_base();
  115. if (!scu_base_phys) {
  116. pr_err("failed to get scu base\n");
  117. return -ENODEV;
  118. }
  119. scu_base = ioremap(scu_base_phys, SZ_128);
  120. if (!scu_base) {
  121. pr_err("failed to map scu base\n");
  122. return -ENOMEM;
  123. }
  124. scu_enable(scu_base);
  125. iounmap(scu_base);
  126. return 0;
  127. }
  128. static void __init uniphier_smp_prepare_cpus(unsigned int max_cpus)
  129. {
  130. static cpumask_t only_cpu_0 = { CPU_BITS_CPU0 };
  131. int ret;
  132. ret = uniphier_smp_prepare_trampoline(max_cpus);
  133. if (ret)
  134. goto err;
  135. ret = uniphier_smp_enable_scu();
  136. if (ret)
  137. goto err;
  138. return;
  139. err:
  140. pr_warn("disabling SMP\n");
  141. init_cpu_present(&only_cpu_0);
  142. uniphier_smp_unprepare_trampoline();
  143. }
  144. static int __init uniphier_smp_boot_secondary(unsigned int cpu,
  145. struct task_struct *idle)
  146. {
  147. if (WARN_ON_ONCE(!uniphier_smp_rom_boot_rsv2))
  148. return -EFAULT;
  149. writel(cpu, uniphier_smp_rom_boot_rsv2);
  150. readl(uniphier_smp_rom_boot_rsv2); /* relax */
  151. asm("sev"); /* wake up secondary CPUs sleeping in the trampoline */
  152. if (cpu == uniphier_smp_max_cpus - 1) {
  153. /* clean up resources if this is the last CPU */
  154. uniphier_smp_unprepare_trampoline();
  155. }
  156. return 0;
  157. }
  158. static const struct smp_operations uniphier_smp_ops __initconst = {
  159. .smp_prepare_cpus = uniphier_smp_prepare_cpus,
  160. .smp_boot_secondary = uniphier_smp_boot_secondary,
  161. };
  162. CPU_METHOD_OF_DECLARE(uniphier_smp, "socionext,uniphier-smp",
  163. &uniphier_smp_ops);