123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- /*
- * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #define pr_fmt(fmt) "uniphier: " fmt
- #include <linux/init.h>
- #include <linux/io.h>
- #include <linux/ioport.h>
- #include <linux/of.h>
- #include <linux/of_address.h>
- #include <linux/sizes.h>
- #include <asm/cacheflush.h>
- #include <asm/hardware/cache-uniphier.h>
- #include <asm/pgtable.h>
- #include <asm/smp.h>
- #include <asm/smp_scu.h>
- /*
- * The secondary CPUs check this register from the boot ROM for the jump
- * destination. After that, it can be reused as a scratch register.
- */
- #define UNIPHIER_SBC_ROM_BOOT_RSV2 0x1208
- static void __iomem *uniphier_smp_rom_boot_rsv2;
- static unsigned int uniphier_smp_max_cpus;
- extern char uniphier_smp_trampoline;
- extern char uniphier_smp_trampoline_jump;
- extern char uniphier_smp_trampoline_poll_addr;
- extern char uniphier_smp_trampoline_end;
- /*
- * Copy trampoline code to the tail of the 1st section of the page table used
- * in the boot ROM. This area is directly accessible by the secondary CPUs
- * for all the UniPhier SoCs.
- */
- static const phys_addr_t uniphier_smp_trampoline_dest_end = SECTION_SIZE;
- static phys_addr_t uniphier_smp_trampoline_dest;
- static int __init uniphier_smp_copy_trampoline(phys_addr_t poll_addr)
- {
- size_t trmp_size;
- static void __iomem *trmp_base;
- if (!uniphier_cache_l2_is_enabled()) {
- pr_warn("outer cache is needed for SMP, but not enabled\n");
- return -ENODEV;
- }
- uniphier_cache_l2_set_locked_ways(1);
- outer_flush_all();
- trmp_size = &uniphier_smp_trampoline_end - &uniphier_smp_trampoline;
- uniphier_smp_trampoline_dest = uniphier_smp_trampoline_dest_end -
- trmp_size;
- uniphier_cache_l2_touch_range(uniphier_smp_trampoline_dest,
- uniphier_smp_trampoline_dest_end);
- trmp_base = ioremap_cache(uniphier_smp_trampoline_dest, trmp_size);
- if (!trmp_base) {
- pr_err("failed to map trampoline destination area\n");
- return -ENOMEM;
- }
- memcpy(trmp_base, &uniphier_smp_trampoline, trmp_size);
- writel(virt_to_phys(secondary_startup),
- trmp_base + (&uniphier_smp_trampoline_jump -
- &uniphier_smp_trampoline));
- writel(poll_addr, trmp_base + (&uniphier_smp_trampoline_poll_addr -
- &uniphier_smp_trampoline));
- flush_cache_all(); /* flush out trampoline code to outer cache */
- iounmap(trmp_base);
- return 0;
- }
- static int __init uniphier_smp_prepare_trampoline(unsigned int max_cpus)
- {
- struct device_node *np;
- struct resource res;
- phys_addr_t rom_rsv2_phys;
- int ret;
- np = of_find_compatible_node(NULL, NULL,
- "socionext,uniphier-system-bus-controller");
- ret = of_address_to_resource(np, 1, &res);
- if (ret) {
- pr_err("failed to get resource of system-bus-controller\n");
- return ret;
- }
- rom_rsv2_phys = res.start + UNIPHIER_SBC_ROM_BOOT_RSV2;
- ret = uniphier_smp_copy_trampoline(rom_rsv2_phys);
- if (ret)
- return ret;
- uniphier_smp_rom_boot_rsv2 = ioremap(rom_rsv2_phys, sizeof(SZ_4));
- if (!uniphier_smp_rom_boot_rsv2) {
- pr_err("failed to map ROM_BOOT_RSV2 register\n");
- return -ENOMEM;
- }
- writel(uniphier_smp_trampoline_dest, uniphier_smp_rom_boot_rsv2);
- asm("sev"); /* Bring up all secondary CPUs to the trampoline code */
- uniphier_smp_max_cpus = max_cpus; /* save for later use */
- return 0;
- }
- static void __init uniphier_smp_unprepare_trampoline(void)
- {
- iounmap(uniphier_smp_rom_boot_rsv2);
- if (uniphier_smp_trampoline_dest)
- outer_inv_range(uniphier_smp_trampoline_dest,
- uniphier_smp_trampoline_dest_end);
- uniphier_cache_l2_set_locked_ways(0);
- }
- static int __init uniphier_smp_enable_scu(void)
- {
- unsigned long scu_base_phys = 0;
- void __iomem *scu_base;
- if (scu_a9_has_base())
- scu_base_phys = scu_a9_get_base();
- if (!scu_base_phys) {
- pr_err("failed to get scu base\n");
- return -ENODEV;
- }
- scu_base = ioremap(scu_base_phys, SZ_128);
- if (!scu_base) {
- pr_err("failed to map scu base\n");
- return -ENOMEM;
- }
- scu_enable(scu_base);
- iounmap(scu_base);
- return 0;
- }
- static void __init uniphier_smp_prepare_cpus(unsigned int max_cpus)
- {
- static cpumask_t only_cpu_0 = { CPU_BITS_CPU0 };
- int ret;
- ret = uniphier_smp_prepare_trampoline(max_cpus);
- if (ret)
- goto err;
- ret = uniphier_smp_enable_scu();
- if (ret)
- goto err;
- return;
- err:
- pr_warn("disabling SMP\n");
- init_cpu_present(&only_cpu_0);
- uniphier_smp_unprepare_trampoline();
- }
- static int __init uniphier_smp_boot_secondary(unsigned int cpu,
- struct task_struct *idle)
- {
- if (WARN_ON_ONCE(!uniphier_smp_rom_boot_rsv2))
- return -EFAULT;
- writel(cpu, uniphier_smp_rom_boot_rsv2);
- readl(uniphier_smp_rom_boot_rsv2); /* relax */
- asm("sev"); /* wake up secondary CPUs sleeping in the trampoline */
- if (cpu == uniphier_smp_max_cpus - 1) {
- /* clean up resources if this is the last CPU */
- uniphier_smp_unprepare_trampoline();
- }
- return 0;
- }
- static const struct smp_operations uniphier_smp_ops __initconst = {
- .smp_prepare_cpus = uniphier_smp_prepare_cpus,
- .smp_boot_secondary = uniphier_smp_boot_secondary,
- };
- CPU_METHOD_OF_DECLARE(uniphier_smp, "socionext,uniphier-smp",
- &uniphier_smp_ops);
|