bcm63xx_smp.c 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. /*
  2. * Broadcom BCM63138 DSL SoCs SMP support code
  3. *
  4. * Copyright (C) 2015, Broadcom Corporation
  5. *
  6. * Licensed under the terms of the GPLv2
  7. */
  8. #include <linux/delay.h>
  9. #include <linux/init.h>
  10. #include <linux/smp.h>
  11. #include <linux/io.h>
  12. #include <linux/of.h>
  13. #include <linux/of_address.h>
  14. #include <asm/cacheflush.h>
  15. #include <asm/smp_scu.h>
  16. #include <asm/smp_plat.h>
  17. #include <asm/vfp.h>
  18. #include "bcm63xx_smp.h"
  19. /* Size of mapped Cortex A9 SCU address space */
  20. #define CORTEX_A9_SCU_SIZE 0x58
  21. /*
  22. * Enable the Cortex A9 Snoop Control Unit
  23. *
  24. * By the time this is called we already know there are multiple
  25. * cores present. We assume we're running on a Cortex A9 processor,
  26. * so any trouble getting the base address register or getting the
  27. * SCU base is a problem.
  28. *
  29. * Return 0 if successful or an error code otherwise.
  30. */
  31. static int __init scu_a9_enable(void)
  32. {
  33. unsigned long config_base;
  34. void __iomem *scu_base;
  35. unsigned int i, ncores;
  36. if (!scu_a9_has_base()) {
  37. pr_err("no configuration base address register!\n");
  38. return -ENXIO;
  39. }
  40. /* Config base address register value is zero for uniprocessor */
  41. config_base = scu_a9_get_base();
  42. if (!config_base) {
  43. pr_err("hardware reports only one core\n");
  44. return -ENOENT;
  45. }
  46. scu_base = ioremap((phys_addr_t)config_base, CORTEX_A9_SCU_SIZE);
  47. if (!scu_base) {
  48. pr_err("failed to remap config base (%lu/%u) for SCU\n",
  49. config_base, CORTEX_A9_SCU_SIZE);
  50. return -ENOMEM;
  51. }
  52. scu_enable(scu_base);
  53. ncores = scu_base ? scu_get_core_count(scu_base) : 1;
  54. if (ncores > nr_cpu_ids) {
  55. pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
  56. ncores, nr_cpu_ids);
  57. ncores = nr_cpu_ids;
  58. }
  59. /* The BCM63138 SoC has two Cortex-A9 CPUs, CPU0 features a complete
  60. * and fully functional VFP unit that can be used, but CPU1 does not.
  61. * Since we will not be able to trap kernel-mode NEON to force
  62. * migration to CPU0, just do not advertise VFP support at all.
  63. *
  64. * This will make vfp_init bail out and do not attempt to use VFP at
  65. * all, for kernel-mode NEON, we do not want to introduce any
  66. * conditionals in hot-paths, so we just restrict the system to UP.
  67. */
  68. #ifdef CONFIG_VFP
  69. if (ncores > 1) {
  70. pr_warn("SMP: secondary CPUs lack VFP unit, disabling VFP\n");
  71. vfp_disable();
  72. #ifdef CONFIG_KERNEL_MODE_NEON
  73. WARN(1, "SMP: kernel-mode NEON enabled, restricting to UP\n");
  74. ncores = 1;
  75. #endif
  76. }
  77. #endif
  78. for (i = 0; i < ncores; i++)
  79. set_cpu_possible(i, true);
  80. iounmap(scu_base); /* That's the last we'll need of this */
  81. return 0;
  82. }
  83. static const struct of_device_id bcm63138_bootlut_ids[] = {
  84. { .compatible = "brcm,bcm63138-bootlut", },
  85. { /* sentinel */ },
  86. };
  87. #define BOOTLUT_RESET_VECT 0x20
  88. static int bcm63138_smp_boot_secondary(unsigned int cpu,
  89. struct task_struct *idle)
  90. {
  91. void __iomem *bootlut_base;
  92. struct device_node *dn;
  93. int ret = 0;
  94. u32 val;
  95. dn = of_find_matching_node(NULL, bcm63138_bootlut_ids);
  96. if (!dn) {
  97. pr_err("SMP: unable to find bcm63138 boot LUT node\n");
  98. return -ENODEV;
  99. }
  100. bootlut_base = of_iomap(dn, 0);
  101. of_node_put(dn);
  102. if (!bootlut_base) {
  103. pr_err("SMP: unable to remap boot LUT base register\n");
  104. return -ENOMEM;
  105. }
  106. /* Locate the secondary CPU node */
  107. dn = of_get_cpu_node(cpu, NULL);
  108. if (!dn) {
  109. pr_err("SMP: failed to locate secondary CPU%d node\n", cpu);
  110. ret = -ENODEV;
  111. goto out;
  112. }
  113. /* Write the secondary init routine to the BootLUT reset vector */
  114. val = __pa_symbol(secondary_startup);
  115. writel_relaxed(val, bootlut_base + BOOTLUT_RESET_VECT);
  116. /* Power up the core, will jump straight to its reset vector when we
  117. * return
  118. */
  119. ret = bcm63xx_pmb_power_on_cpu(dn);
  120. if (ret)
  121. goto out;
  122. out:
  123. iounmap(bootlut_base);
  124. return ret;
  125. }
  126. static void __init bcm63138_smp_prepare_cpus(unsigned int max_cpus)
  127. {
  128. int ret;
  129. ret = scu_a9_enable();
  130. if (ret) {
  131. pr_warn("SMP: Cortex-A9 SCU setup failed\n");
  132. return;
  133. }
  134. }
  135. static const struct smp_operations bcm63138_smp_ops __initconst = {
  136. .smp_prepare_cpus = bcm63138_smp_prepare_cpus,
  137. .smp_boot_secondary = bcm63138_smp_boot_secondary,
  138. };
  139. CPU_METHOD_OF_DECLARE(bcm63138_smp, "brcm,bcm63138", &bcm63138_smp_ops);