linux/arch/arm/mach-mvebu/platsmp.c
<<
>>
Prefs
   1/*
   2 * Symmetric Multi Processing (SMP) support for Armada XP
   3 *
   4 * Copyright (C) 2012 Marvell
   5 *
   6 * Lior Amsalem <alior@marvell.com>
   7 * Yehuda Yitschak <yehuday@marvell.com>
   8 * Gregory CLEMENT <gregory.clement@free-electrons.com>
   9 * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
  10 *
  11 * This file is licensed under the terms of the GNU General Public
  12 * License version 2.  This program is licensed "as is" without any
  13 * warranty of any kind, whether express or implied.
  14 *
  15 * The Armada XP SoC has 4 ARMv7 PJ4B CPUs running in full HW coherency
  16 * This file implements the routines for preparing the SMP infrastructure
  17 * and waking up the secondary CPUs
  18 */
  19
  20#include <linux/init.h>
  21#include <linux/smp.h>
  22#include <linux/clk.h>
  23#include <linux/of.h>
  24#include <linux/of_address.h>
  25#include <linux/mbus.h>
  26#include <asm/cacheflush.h>
  27#include <asm/smp_plat.h>
  28#include "common.h"
  29#include "armada-370-xp.h"
  30#include "pmsu.h"
  31#include "coherency.h"
  32
  33#define ARMADA_XP_MAX_CPUS 4
  34
  35#define AXP_BOOTROM_BASE 0xfff00000
  36#define AXP_BOOTROM_SIZE 0x100000
  37
  38static struct clk *boot_cpu_clk;
  39
  40static struct clk *get_cpu_clk(int cpu)
  41{
  42        struct clk *cpu_clk;
  43        struct device_node *np = of_get_cpu_node(cpu, NULL);
  44
  45        if (WARN(!np, "missing cpu node\n"))
  46                return NULL;
  47        cpu_clk = of_clk_get(np, 0);
  48        if (WARN_ON(IS_ERR(cpu_clk)))
  49                return NULL;
  50        return cpu_clk;
  51}
  52
  53static int armada_xp_boot_secondary(unsigned int cpu, struct task_struct *idle)
  54{
  55        int ret, hw_cpu;
  56
  57        pr_info("Booting CPU %d\n", cpu);
  58
  59        hw_cpu = cpu_logical_map(cpu);
  60        mvebu_pmsu_set_cpu_boot_addr(hw_cpu, armada_xp_secondary_startup);
  61
  62        /*
  63         * This is needed to wake up CPUs in the offline state after
  64         * using CPU hotplug.
  65         */
  66        arch_send_wakeup_ipi_mask(cpumask_of(cpu));
  67
  68        /*
  69         * This is needed to take secondary CPUs out of reset on the
  70         * initial boot.
  71         */
  72        ret = mvebu_cpu_reset_deassert(hw_cpu);
  73        if (ret) {
  74                pr_warn("unable to boot CPU: %d\n", ret);
  75                return ret;
  76        }
  77
  78        return 0;
  79}
  80
  81/*
  82 * When a CPU is brought back online, either through CPU hotplug, or
  83 * because of the boot of a kexec'ed kernel, the PMSU configuration
  84 * for this CPU might be in the deep idle state, preventing this CPU
  85 * from receiving interrupts. Here, we therefore take out the current
  86 * CPU from this state, which was entered by armada_xp_cpu_die()
  87 * below.
  88 */
  89static void armada_xp_secondary_init(unsigned int cpu)
  90{
  91        mvebu_v7_pmsu_idle_exit();
  92}
  93
  94static void __init armada_xp_smp_init_cpus(void)
  95{
  96        unsigned int ncores = num_possible_cpus();
  97
  98        if (ncores == 0 || ncores > ARMADA_XP_MAX_CPUS)
  99                panic("Invalid number of CPUs in DT\n");
 100}
 101
 102static int armada_xp_sync_secondary_clk(unsigned int cpu)
 103{
 104        struct clk *cpu_clk = get_cpu_clk(cpu);
 105
 106        if (!cpu_clk || !boot_cpu_clk)
 107                return 0;
 108
 109        clk_prepare_enable(cpu_clk);
 110        clk_set_rate(cpu_clk, clk_get_rate(boot_cpu_clk));
 111
 112        return 0;
 113}
 114
 115static void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus)
 116{
 117        struct device_node *node;
 118        struct resource res;
 119        int err;
 120
 121        flush_cache_all();
 122        set_cpu_coherent();
 123
 124        boot_cpu_clk = get_cpu_clk(smp_processor_id());
 125        if (boot_cpu_clk) {
 126                clk_prepare_enable(boot_cpu_clk);
 127                cpuhp_setup_state_nocalls(CPUHP_AP_ARM_MVEBU_SYNC_CLOCKS,
 128                                          "arm/mvebu/sync_clocks:online",
 129                                          armada_xp_sync_secondary_clk, NULL);
 130        }
 131
 132        /*
 133         * In order to boot the secondary CPUs we need to ensure
 134         * the bootROM is mapped at the correct address.
 135         */
 136        node = of_find_compatible_node(NULL, NULL, "marvell,bootrom");
 137        if (!node)
 138                panic("Cannot find 'marvell,bootrom' compatible node");
 139
 140        err = of_address_to_resource(node, 0, &res);
 141        of_node_put(node);
 142        if (err < 0)
 143                panic("Cannot get 'bootrom' node address");
 144
 145        if (res.start != AXP_BOOTROM_BASE ||
 146            resource_size(&res) != AXP_BOOTROM_SIZE)
 147                panic("The address for the BootROM is incorrect");
 148}
 149
 150#ifdef CONFIG_HOTPLUG_CPU
 151static void armada_xp_cpu_die(unsigned int cpu)
 152{
 153        /*
 154         * CPU hotplug is implemented by putting offline CPUs into the
 155         * deep idle sleep state.
 156         */
 157        armada_370_xp_pmsu_idle_enter(true);
 158}
 159
 160/*
 161 * We need a dummy function, so that platform_can_cpu_hotplug() knows
 162 * we support CPU hotplug. However, the function does not need to do
 163 * anything, because CPUs going offline can enter the deep idle state
 164 * by themselves, without any help from a still alive CPU.
 165 */
 166static int armada_xp_cpu_kill(unsigned int cpu)
 167{
 168        return 1;
 169}
 170#endif
 171
 172const struct smp_operations armada_xp_smp_ops __initconst = {
 173        .smp_init_cpus          = armada_xp_smp_init_cpus,
 174        .smp_prepare_cpus       = armada_xp_smp_prepare_cpus,
 175        .smp_boot_secondary     = armada_xp_boot_secondary,
 176        .smp_secondary_init     = armada_xp_secondary_init,
 177#ifdef CONFIG_HOTPLUG_CPU
 178        .cpu_die                = armada_xp_cpu_die,
 179        .cpu_kill               = armada_xp_cpu_kill,
 180#endif
 181};
 182
 183CPU_METHOD_OF_DECLARE(armada_xp_smp, "marvell,armada-xp-smp",
 184                      &armada_xp_smp_ops);
 185
 186#define MV98DX3236_CPU_RESUME_CTRL_REG 0x08
 187#define MV98DX3236_CPU_RESUME_ADDR_REG 0x04
 188
 189static const struct of_device_id of_mv98dx3236_resume_table[] = {
 190        {
 191                .compatible = "marvell,98dx3336-resume-ctrl",
 192        },
 193        { /* end of list */ },
 194};
 195
 196static int mv98dx3236_resume_set_cpu_boot_addr(int hw_cpu, void *boot_addr)
 197{
 198        struct device_node *np;
 199        void __iomem *base;
 200        WARN_ON(hw_cpu != 1);
 201
 202        np = of_find_matching_node(NULL, of_mv98dx3236_resume_table);
 203        if (!np)
 204                return -ENODEV;
 205
 206        base = of_io_request_and_map(np, 0, of_node_full_name(np));
 207        of_node_put(np);
 208        if (IS_ERR(base))
 209                return PTR_ERR(base);
 210
 211        writel(0, base + MV98DX3236_CPU_RESUME_CTRL_REG);
 212        writel(__pa_symbol(boot_addr), base + MV98DX3236_CPU_RESUME_ADDR_REG);
 213
 214        iounmap(base);
 215
 216        return 0;
 217}
 218
 219static int mv98dx3236_boot_secondary(unsigned int cpu, struct task_struct *idle)
 220{
 221        int ret, hw_cpu;
 222
 223        hw_cpu = cpu_logical_map(cpu);
 224        mv98dx3236_resume_set_cpu_boot_addr(hw_cpu,
 225                                            armada_xp_secondary_startup);
 226
 227        /*
 228         * This is needed to wake up CPUs in the offline state after
 229         * using CPU hotplug.
 230         */
 231        arch_send_wakeup_ipi_mask(cpumask_of(cpu));
 232
 233        /*
 234         * This is needed to take secondary CPUs out of reset on the
 235         * initial boot.
 236         */
 237        ret = mvebu_cpu_reset_deassert(hw_cpu);
 238        if (ret) {
 239                pr_warn("unable to boot CPU: %d\n", ret);
 240                return ret;
 241        }
 242
 243        return 0;
 244}
 245
 246static const struct smp_operations mv98dx3236_smp_ops __initconst = {
 247        .smp_init_cpus          = armada_xp_smp_init_cpus,
 248        .smp_prepare_cpus       = armada_xp_smp_prepare_cpus,
 249        .smp_boot_secondary     = mv98dx3236_boot_secondary,
 250        .smp_secondary_init     = armada_xp_secondary_init,
 251#ifdef CONFIG_HOTPLUG_CPU
 252        .cpu_die                = armada_xp_cpu_die,
 253        .cpu_kill               = armada_xp_cpu_kill,
 254#endif
 255};
 256
 257CPU_METHOD_OF_DECLARE(mv98dx3236_smp, "marvell,98dx3236-smp",
 258                      &mv98dx3236_smp_ops);
 259
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.