linux/arch/arm/mach-hisi/hotplug.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2013 Linaro Ltd.
   3 * Copyright (c) 2013 Hisilicon Limited.
   4 *
   5 * This program is free software; you can redistribute it and/or modify it
   6 * under the terms and conditions of the GNU General Public License,
   7 * version 2, as published by the Free Software Foundation.
   8 */
   9
  10#include <linux/cpu.h>
  11#include <linux/delay.h>
  12#include <linux/io.h>
  13#include <linux/of_address.h>
  14#include <linux/of_platform.h>
  15#include <asm/cacheflush.h>
  16#include <asm/smp_plat.h>
  17#include "core.h"
  18
  19/* Sysctrl registers in Hi3620 SoC */
  20#define SCISOEN                         0xc0
  21#define SCISODIS                        0xc4
  22#define SCPERPWREN                      0xd0
  23#define SCPERPWRDIS                     0xd4
  24#define SCCPUCOREEN                     0xf4
  25#define SCCPUCOREDIS                    0xf8
  26#define SCPERCTRL0                      0x200
  27#define SCCPURSTEN                      0x410
  28#define SCCPURSTDIS                     0x414
  29
  30/*
  31 * bit definition in SCISOEN/SCPERPWREN/...
  32 *
  33 * CPU2_ISO_CTRL        (1 << 5)
  34 * CPU3_ISO_CTRL        (1 << 6)
  35 * ...
  36 */
  37#define CPU2_ISO_CTRL                   (1 << 5)
  38
  39/*
  40 * bit definition in SCPERCTRL0
  41 *
  42 * CPU0_WFI_MASK_CFG    (1 << 28)
  43 * CPU1_WFI_MASK_CFG    (1 << 29)
  44 * ...
  45 */
  46#define CPU0_WFI_MASK_CFG               (1 << 28)
  47
  48/*
  49 * bit definition in SCCPURSTEN/...
  50 *
  51 * CPU0_SRST_REQ_EN     (1 << 0)
  52 * CPU1_SRST_REQ_EN     (1 << 1)
  53 * ...
  54 */
  55#define CPU0_HPM_SRST_REQ_EN            (1 << 22)
  56#define CPU0_DBG_SRST_REQ_EN            (1 << 12)
  57#define CPU0_NEON_SRST_REQ_EN           (1 << 4)
  58#define CPU0_SRST_REQ_EN                (1 << 0)
  59
  60#define HIX5HD2_PERI_CRG20              0x50
  61#define CRG20_CPU1_RESET                (1 << 17)
  62
  63#define HIX5HD2_PERI_PMC0               0x1000
  64#define PMC0_CPU1_WAIT_MTCOMS_ACK       (1 << 8)
  65#define PMC0_CPU1_PMC_ENABLE            (1 << 7)
  66#define PMC0_CPU1_POWERDOWN             (1 << 3)
  67
  68#define HIP01_PERI9                    0x50
  69#define PERI9_CPU1_RESET               (1 << 1)
  70
  71enum {
  72        HI3620_CTRL,
  73        ERROR_CTRL,
  74};
  75
  76static void __iomem *ctrl_base;
  77static int id;
  78
  79static void set_cpu_hi3620(int cpu, bool enable)
  80{
  81        u32 val = 0;
  82
  83        if (enable) {
  84                /* MTCMOS set */
  85                if ((cpu == 2) || (cpu == 3))
  86                        writel_relaxed(CPU2_ISO_CTRL << (cpu - 2),
  87                                       ctrl_base + SCPERPWREN);
  88                udelay(100);
  89
  90                /* Enable core */
  91                writel_relaxed(0x01 << cpu, ctrl_base + SCCPUCOREEN);
  92
  93                /* unreset */
  94                val = CPU0_DBG_SRST_REQ_EN | CPU0_NEON_SRST_REQ_EN
  95                        | CPU0_SRST_REQ_EN;
  96                writel_relaxed(val << cpu, ctrl_base + SCCPURSTDIS);
  97                /* reset */
  98                val |= CPU0_HPM_SRST_REQ_EN;
  99                writel_relaxed(val << cpu, ctrl_base + SCCPURSTEN);
 100
 101                /* ISO disable */
 102                if ((cpu == 2) || (cpu == 3))
 103                        writel_relaxed(CPU2_ISO_CTRL << (cpu - 2),
 104                                       ctrl_base + SCISODIS);
 105                udelay(1);
 106
 107                /* WFI Mask */
 108                val = readl_relaxed(ctrl_base + SCPERCTRL0);
 109                val &= ~(CPU0_WFI_MASK_CFG << cpu);
 110                writel_relaxed(val, ctrl_base + SCPERCTRL0);
 111
 112                /* Unreset */
 113                val = CPU0_DBG_SRST_REQ_EN | CPU0_NEON_SRST_REQ_EN
 114                        | CPU0_SRST_REQ_EN | CPU0_HPM_SRST_REQ_EN;
 115                writel_relaxed(val << cpu, ctrl_base + SCCPURSTDIS);
 116        } else {
 117                /* wfi mask */
 118                val = readl_relaxed(ctrl_base + SCPERCTRL0);
 119                val |= (CPU0_WFI_MASK_CFG << cpu);
 120                writel_relaxed(val, ctrl_base + SCPERCTRL0);
 121
 122                /* disable core*/
 123                writel_relaxed(0x01 << cpu, ctrl_base + SCCPUCOREDIS);
 124
 125                if ((cpu == 2) || (cpu == 3)) {
 126                        /* iso enable */
 127                        writel_relaxed(CPU2_ISO_CTRL << (cpu - 2),
 128                                       ctrl_base + SCISOEN);
 129                        udelay(1);
 130                }
 131
 132                /* reset */
 133                val = CPU0_DBG_SRST_REQ_EN | CPU0_NEON_SRST_REQ_EN
 134                        | CPU0_SRST_REQ_EN | CPU0_HPM_SRST_REQ_EN;
 135                writel_relaxed(val << cpu, ctrl_base + SCCPURSTEN);
 136
 137                if ((cpu == 2) || (cpu == 3)) {
 138                        /* MTCMOS unset */
 139                        writel_relaxed(CPU2_ISO_CTRL << (cpu - 2),
 140                                       ctrl_base + SCPERPWRDIS);
 141                        udelay(100);
 142                }
 143        }
 144}
 145
 146static int hi3xxx_hotplug_init(void)
 147{
 148        struct device_node *node;
 149
 150        node = of_find_compatible_node(NULL, NULL, "hisilicon,sysctrl");
 151        if (node) {
 152                ctrl_base = of_iomap(node, 0);
 153                id = HI3620_CTRL;
 154                return 0;
 155        }
 156        id = ERROR_CTRL;
 157        return -ENOENT;
 158}
 159
 160void hi3xxx_set_cpu(int cpu, bool enable)
 161{
 162        if (!ctrl_base) {
 163                if (hi3xxx_hotplug_init() < 0)
 164                        return;
 165        }
 166
 167        if (id == HI3620_CTRL)
 168                set_cpu_hi3620(cpu, enable);
 169}
 170
 171static bool hix5hd2_hotplug_init(void)
 172{
 173        struct device_node *np;
 174
 175        np = of_find_compatible_node(NULL, NULL, "hisilicon,cpuctrl");
 176        if (np) {
 177                ctrl_base = of_iomap(np, 0);
 178                return true;
 179        }
 180        return false;
 181}
 182
 183void hix5hd2_set_cpu(int cpu, bool enable)
 184{
 185        u32 val = 0;
 186
 187        if (!ctrl_base)
 188                if (!hix5hd2_hotplug_init())
 189                        BUG();
 190
 191        if (enable) {
 192                /* power on cpu1 */
 193                val = readl_relaxed(ctrl_base + HIX5HD2_PERI_PMC0);
 194                val &= ~(PMC0_CPU1_WAIT_MTCOMS_ACK | PMC0_CPU1_POWERDOWN);
 195                val |= PMC0_CPU1_PMC_ENABLE;
 196                writel_relaxed(val, ctrl_base + HIX5HD2_PERI_PMC0);
 197                /* unreset */
 198                val = readl_relaxed(ctrl_base + HIX5HD2_PERI_CRG20);
 199                val &= ~CRG20_CPU1_RESET;
 200                writel_relaxed(val, ctrl_base + HIX5HD2_PERI_CRG20);
 201        } else {
 202                /* power down cpu1 */
 203                val = readl_relaxed(ctrl_base + HIX5HD2_PERI_PMC0);
 204                val |= PMC0_CPU1_PMC_ENABLE | PMC0_CPU1_POWERDOWN;
 205                val &= ~PMC0_CPU1_WAIT_MTCOMS_ACK;
 206                writel_relaxed(val, ctrl_base + HIX5HD2_PERI_PMC0);
 207
 208                /* reset */
 209                val = readl_relaxed(ctrl_base + HIX5HD2_PERI_CRG20);
 210                val |= CRG20_CPU1_RESET;
 211                writel_relaxed(val, ctrl_base + HIX5HD2_PERI_CRG20);
 212        }
 213}
 214
 215void hip01_set_cpu(int cpu, bool enable)
 216{
 217        unsigned int temp;
 218        struct device_node *np;
 219
 220        if (!ctrl_base) {
 221                np = of_find_compatible_node(NULL, NULL, "hisilicon,hip01-sysctrl");
 222                if (np)
 223                        ctrl_base = of_iomap(np, 0);
 224                else
 225                        BUG();
 226        }
 227
 228        if (enable) {
 229                /* reset on CPU1  */
 230                temp = readl_relaxed(ctrl_base + HIP01_PERI9);
 231                temp |= PERI9_CPU1_RESET;
 232                writel_relaxed(temp, ctrl_base + HIP01_PERI9);
 233
 234                udelay(50);
 235
 236                /* unreset on CPU1 */
 237                temp = readl_relaxed(ctrl_base + HIP01_PERI9);
 238                temp &= ~PERI9_CPU1_RESET;
 239                writel_relaxed(temp, ctrl_base + HIP01_PERI9);
 240        }
 241}
 242
 243static inline void cpu_enter_lowpower(void)
 244{
 245        unsigned int v;
 246
 247        flush_cache_all();
 248
 249        /*
 250         * Turn off coherency and L1 D-cache
 251         */
 252        asm volatile(
 253        "       mrc     p15, 0, %0, c1, c0, 1\n"
 254        "       bic     %0, %0, #0x40\n"
 255        "       mcr     p15, 0, %0, c1, c0, 1\n"
 256        "       mrc     p15, 0, %0, c1, c0, 0\n"
 257        "       bic     %0, %0, #0x04\n"
 258        "       mcr     p15, 0, %0, c1, c0, 0\n"
 259          : "=&r" (v)
 260          : "r" (0)
 261          : "cc");
 262}
 263
 264#ifdef CONFIG_HOTPLUG_CPU
 265void hi3xxx_cpu_die(unsigned int cpu)
 266{
 267        cpu_enter_lowpower();
 268        hi3xxx_set_cpu_jump(cpu, phys_to_virt(0));
 269        cpu_do_idle();
 270
 271        /* We should have never returned from idle */
 272        panic("cpu %d unexpectedly exit from shutdown\n", cpu);
 273}
 274
 275int hi3xxx_cpu_kill(unsigned int cpu)
 276{
 277        unsigned long timeout = jiffies + msecs_to_jiffies(50);
 278
 279        while (hi3xxx_get_cpu_jump(cpu))
 280                if (time_after(jiffies, timeout))
 281                        return 0;
 282        hi3xxx_set_cpu(cpu, false);
 283        return 1;
 284}
 285
 286void hix5hd2_cpu_die(unsigned int cpu)
 287{
 288        flush_cache_all();
 289        hix5hd2_set_cpu(cpu, false);
 290}
 291#endif
 292