linux/arch/arm/mach-bcm/platsmp-brcmstb.c
<<
>>
Prefs
   1/*
   2 * Broadcom STB CPU SMP and hotplug support for ARM
   3 *
   4 * Copyright (C) 2013-2014 Broadcom Corporation
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License as
   8 * published by the Free Software Foundation version 2.
   9 *
  10 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
  11 * kind, whether express or implied; without even the implied warranty
  12 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 * GNU General Public License for more details.
  14 */
  15
  16#include <linux/delay.h>
  17#include <linux/errno.h>
  18#include <linux/init.h>
  19#include <linux/io.h>
  20#include <linux/jiffies.h>
  21#include <linux/of_address.h>
  22#include <linux/of_platform.h>
  23#include <linux/printk.h>
  24#include <linux/regmap.h>
  25#include <linux/smp.h>
  26#include <linux/mfd/syscon.h>
  27
  28#include <asm/cacheflush.h>
  29#include <asm/cp15.h>
  30#include <asm/mach-types.h>
  31#include <asm/smp_plat.h>
  32
  33enum {
  34        ZONE_MAN_CLKEN_MASK             = BIT(0),
  35        ZONE_MAN_RESET_CNTL_MASK        = BIT(1),
  36        ZONE_MAN_MEM_PWR_MASK           = BIT(4),
  37        ZONE_RESERVED_1_MASK            = BIT(5),
  38        ZONE_MAN_ISO_CNTL_MASK          = BIT(6),
  39        ZONE_MANUAL_CONTROL_MASK        = BIT(7),
  40        ZONE_PWR_DN_REQ_MASK            = BIT(9),
  41        ZONE_PWR_UP_REQ_MASK            = BIT(10),
  42        ZONE_BLK_RST_ASSERT_MASK        = BIT(12),
  43        ZONE_PWR_OFF_STATE_MASK         = BIT(25),
  44        ZONE_PWR_ON_STATE_MASK          = BIT(26),
  45        ZONE_DPG_PWR_STATE_MASK         = BIT(28),
  46        ZONE_MEM_PWR_STATE_MASK         = BIT(29),
  47        ZONE_RESET_STATE_MASK           = BIT(31),
  48        CPU0_PWR_ZONE_CTRL_REG          = 1,
  49        CPU_RESET_CONFIG_REG            = 2,
  50};
  51
  52static void __iomem *cpubiuctrl_block;
  53static void __iomem *hif_cont_block;
  54static u32 cpu0_pwr_zone_ctrl_reg;
  55static u32 cpu_rst_cfg_reg;
  56static u32 hif_cont_reg;
  57
  58#ifdef CONFIG_HOTPLUG_CPU
  59/*
  60 * We must quiesce a dying CPU before it can be killed by the boot CPU. Because
  61 * one or more cache may be disabled, we must flush to ensure coherency. We
  62 * cannot use traditionl completion structures or spinlocks as they rely on
  63 * coherency.
  64 */
  65static DEFINE_PER_CPU_ALIGNED(int, per_cpu_sw_state);
  66
  67static int per_cpu_sw_state_rd(u32 cpu)
  68{
  69        sync_cache_r(SHIFT_PERCPU_PTR(&per_cpu_sw_state, per_cpu_offset(cpu)));
  70        return per_cpu(per_cpu_sw_state, cpu);
  71}
  72
  73static void per_cpu_sw_state_wr(u32 cpu, int val)
  74{
  75        dmb();
  76        per_cpu(per_cpu_sw_state, cpu) = val;
  77        sync_cache_w(SHIFT_PERCPU_PTR(&per_cpu_sw_state, per_cpu_offset(cpu)));
  78}
  79#else
  80static inline void per_cpu_sw_state_wr(u32 cpu, int val) { }
  81#endif
  82
  83static void __iomem *pwr_ctrl_get_base(u32 cpu)
  84{
  85        void __iomem *base = cpubiuctrl_block + cpu0_pwr_zone_ctrl_reg;
  86        base += (cpu_logical_map(cpu) * 4);
  87        return base;
  88}
  89
  90static u32 pwr_ctrl_rd(u32 cpu)
  91{
  92        void __iomem *base = pwr_ctrl_get_base(cpu);
  93        return readl_relaxed(base);
  94}
  95
  96static void pwr_ctrl_set(unsigned int cpu, u32 val, u32 mask)
  97{
  98        void __iomem *base = pwr_ctrl_get_base(cpu);
  99        writel((readl(base) & mask) | val, base);
 100}
 101
 102static void pwr_ctrl_clr(unsigned int cpu, u32 val, u32 mask)
 103{
 104        void __iomem *base = pwr_ctrl_get_base(cpu);
 105        writel((readl(base) & mask) & ~val, base);
 106}
 107
 108#define POLL_TMOUT_MS 500
 109static int pwr_ctrl_wait_tmout(unsigned int cpu, u32 set, u32 mask)
 110{
 111        const unsigned long timeo = jiffies + msecs_to_jiffies(POLL_TMOUT_MS);
 112        u32 tmp;
 113
 114        do {
 115                tmp = pwr_ctrl_rd(cpu) & mask;
 116                if (!set == !tmp)
 117                        return 0;
 118        } while (time_before(jiffies, timeo));
 119
 120        tmp = pwr_ctrl_rd(cpu) & mask;
 121        if (!set == !tmp)
 122                return 0;
 123
 124        return -ETIMEDOUT;
 125}
 126
 127static void cpu_rst_cfg_set(u32 cpu, int set)
 128{
 129        u32 val;
 130        val = readl_relaxed(cpubiuctrl_block + cpu_rst_cfg_reg);
 131        if (set)
 132                val |= BIT(cpu_logical_map(cpu));
 133        else
 134                val &= ~BIT(cpu_logical_map(cpu));
 135        writel_relaxed(val, cpubiuctrl_block + cpu_rst_cfg_reg);
 136}
 137
 138static void cpu_set_boot_addr(u32 cpu, unsigned long boot_addr)
 139{
 140        const int reg_ofs = cpu_logical_map(cpu) * 8;
 141        writel_relaxed(0, hif_cont_block + hif_cont_reg + reg_ofs);
 142        writel_relaxed(boot_addr, hif_cont_block + hif_cont_reg + 4 + reg_ofs);
 143}
 144
 145static void brcmstb_cpu_boot(u32 cpu)
 146{
 147        /* Mark this CPU as "up" */
 148        per_cpu_sw_state_wr(cpu, 1);
 149
 150        /*
 151         * Set the reset vector to point to the secondary_startup
 152         * routine
 153         */
 154        cpu_set_boot_addr(cpu, __pa_symbol(secondary_startup));
 155
 156        /* Unhalt the cpu */
 157        cpu_rst_cfg_set(cpu, 0);
 158}
 159
 160static void brcmstb_cpu_power_on(u32 cpu)
 161{
 162        /*
 163         * The secondary cores power was cut, so we must go through
 164         * power-on initialization.
 165         */
 166        pwr_ctrl_set(cpu, ZONE_MAN_ISO_CNTL_MASK, 0xffffff00);
 167        pwr_ctrl_set(cpu, ZONE_MANUAL_CONTROL_MASK, -1);
 168        pwr_ctrl_set(cpu, ZONE_RESERVED_1_MASK, -1);
 169
 170        pwr_ctrl_set(cpu, ZONE_MAN_MEM_PWR_MASK, -1);
 171
 172        if (pwr_ctrl_wait_tmout(cpu, 1, ZONE_MEM_PWR_STATE_MASK))
 173                panic("ZONE_MEM_PWR_STATE_MASK set timeout");
 174
 175        pwr_ctrl_set(cpu, ZONE_MAN_CLKEN_MASK, -1);
 176
 177        if (pwr_ctrl_wait_tmout(cpu, 1, ZONE_DPG_PWR_STATE_MASK))
 178                panic("ZONE_DPG_PWR_STATE_MASK set timeout");
 179
 180        pwr_ctrl_clr(cpu, ZONE_MAN_ISO_CNTL_MASK, -1);
 181        pwr_ctrl_set(cpu, ZONE_MAN_RESET_CNTL_MASK, -1);
 182}
 183
 184static int brcmstb_cpu_get_power_state(u32 cpu)
 185{
 186        int tmp = pwr_ctrl_rd(cpu);
 187        return (tmp & ZONE_RESET_STATE_MASK) ? 0 : 1;
 188}
 189
 190#ifdef CONFIG_HOTPLUG_CPU
 191
 192static void brcmstb_cpu_die(u32 cpu)
 193{
 194        v7_exit_coherency_flush(all);
 195
 196        per_cpu_sw_state_wr(cpu, 0);
 197
 198        /* Sit and wait to die */
 199        wfi();
 200
 201        /* We should never get here... */
 202        while (1)
 203                ;
 204}
 205
 206static int brcmstb_cpu_kill(u32 cpu)
 207{
 208        /*
 209         * Ordinarily, the hardware forbids power-down of CPU0 (which is good
 210         * because it is the boot CPU), but this is not true when using BPCM
 211         * manual mode.  Consequently, we must avoid turning off CPU0 here to
 212         * ensure that TI2C master reset will work.
 213         */
 214        if (cpu == 0) {
 215                pr_warn("SMP: refusing to power off CPU0\n");
 216                return 1;
 217        }
 218
 219        while (per_cpu_sw_state_rd(cpu))
 220                ;
 221
 222        pwr_ctrl_set(cpu, ZONE_MANUAL_CONTROL_MASK, -1);
 223        pwr_ctrl_clr(cpu, ZONE_MAN_RESET_CNTL_MASK, -1);
 224        pwr_ctrl_clr(cpu, ZONE_MAN_CLKEN_MASK, -1);
 225        pwr_ctrl_set(cpu, ZONE_MAN_ISO_CNTL_MASK, -1);
 226        pwr_ctrl_clr(cpu, ZONE_MAN_MEM_PWR_MASK, -1);
 227
 228        if (pwr_ctrl_wait_tmout(cpu, 0, ZONE_MEM_PWR_STATE_MASK))
 229                panic("ZONE_MEM_PWR_STATE_MASK clear timeout");
 230
 231        pwr_ctrl_clr(cpu, ZONE_RESERVED_1_MASK, -1);
 232
 233        if (pwr_ctrl_wait_tmout(cpu, 0, ZONE_DPG_PWR_STATE_MASK))
 234                panic("ZONE_DPG_PWR_STATE_MASK clear timeout");
 235
 236        /* Flush pipeline before resetting CPU */
 237        mb();
 238
 239        /* Assert reset on the CPU */
 240        cpu_rst_cfg_set(cpu, 1);
 241
 242        return 1;
 243}
 244
 245#endif /* CONFIG_HOTPLUG_CPU */
 246
 247static int __init setup_hifcpubiuctrl_regs(struct device_node *np)
 248{
 249        int rc = 0;
 250        char *name;
 251        struct device_node *syscon_np = NULL;
 252
 253        name = "syscon-cpu";
 254
 255        syscon_np = of_parse_phandle(np, name, 0);
 256        if (!syscon_np) {
 257                pr_err("can't find phandle %s\n", name);
 258                rc = -EINVAL;
 259                goto cleanup;
 260        }
 261
 262        cpubiuctrl_block = of_iomap(syscon_np, 0);
 263        if (!cpubiuctrl_block) {
 264                pr_err("iomap failed for cpubiuctrl_block\n");
 265                rc = -EINVAL;
 266                goto cleanup;
 267        }
 268
 269        rc = of_property_read_u32_index(np, name, CPU0_PWR_ZONE_CTRL_REG,
 270                                        &cpu0_pwr_zone_ctrl_reg);
 271        if (rc) {
 272                pr_err("failed to read 1st entry from %s property (%d)\n", name,
 273                        rc);
 274                rc = -EINVAL;
 275                goto cleanup;
 276        }
 277
 278        rc = of_property_read_u32_index(np, name, CPU_RESET_CONFIG_REG,
 279                                        &cpu_rst_cfg_reg);
 280        if (rc) {
 281                pr_err("failed to read 2nd entry from %s property (%d)\n", name,
 282                        rc);
 283                rc = -EINVAL;
 284                goto cleanup;
 285        }
 286
 287cleanup:
 288        of_node_put(syscon_np);
 289        return rc;
 290}
 291
 292static int __init setup_hifcont_regs(struct device_node *np)
 293{
 294        int rc = 0;
 295        char *name;
 296        struct device_node *syscon_np = NULL;
 297
 298        name = "syscon-cont";
 299
 300        syscon_np = of_parse_phandle(np, name, 0);
 301        if (!syscon_np) {
 302                pr_err("can't find phandle %s\n", name);
 303                rc = -EINVAL;
 304                goto cleanup;
 305        }
 306
 307        hif_cont_block = of_iomap(syscon_np, 0);
 308        if (!hif_cont_block) {
 309                pr_err("iomap failed for hif_cont_block\n");
 310                rc = -EINVAL;
 311                goto cleanup;
 312        }
 313
 314        /* Offset is at top of hif_cont_block */
 315        hif_cont_reg = 0;
 316
 317cleanup:
 318        of_node_put(syscon_np);
 319        return rc;
 320}
 321
 322static void __init brcmstb_cpu_ctrl_setup(unsigned int max_cpus)
 323{
 324        int rc;
 325        struct device_node *np;
 326        char *name;
 327
 328        name = "brcm,brcmstb-smpboot";
 329        np = of_find_compatible_node(NULL, NULL, name);
 330        if (!np) {
 331                pr_err("can't find compatible node %s\n", name);
 332                return;
 333        }
 334
 335        rc = setup_hifcpubiuctrl_regs(np);
 336        if (rc)
 337                goto out_put_node;
 338
 339        rc = setup_hifcont_regs(np);
 340        if (rc)
 341                goto out_put_node;
 342
 343out_put_node:
 344        of_node_put(np);
 345}
 346
 347static int brcmstb_boot_secondary(unsigned int cpu, struct task_struct *idle)
 348{
 349        /* Missing the brcm,brcmstb-smpboot DT node? */
 350        if (!cpubiuctrl_block || !hif_cont_block)
 351                return -ENODEV;
 352
 353        /* Bring up power to the core if necessary */
 354        if (brcmstb_cpu_get_power_state(cpu) == 0)
 355                brcmstb_cpu_power_on(cpu);
 356
 357        brcmstb_cpu_boot(cpu);
 358
 359        return 0;
 360}
 361
 362static const struct smp_operations brcmstb_smp_ops __initconst = {
 363        .smp_prepare_cpus       = brcmstb_cpu_ctrl_setup,
 364        .smp_boot_secondary     = brcmstb_boot_secondary,
 365#ifdef CONFIG_HOTPLUG_CPU
 366        .cpu_kill               = brcmstb_cpu_kill,
 367        .cpu_die                = brcmstb_cpu_die,
 368#endif
 369};
 370
 371CPU_METHOD_OF_DECLARE(brcmstb_smp, "brcm,brahma-b15", &brcmstb_smp_ops);
 372