linux/arch/arm/mach-hisi/platmcpm.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2013-2014 Linaro Ltd.
   3 * Copyright (c) 2013-2014 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#include <linux/delay.h>
  10#include <linux/io.h>
  11#include <linux/memblock.h>
  12#include <linux/of_address.h>
  13
  14#include <asm/cputype.h>
  15#include <asm/cp15.h>
  16#include <asm/mcpm.h>
  17
  18#include "core.h"
  19
  20/* bits definition in SC_CPU_RESET_REQ[x]/SC_CPU_RESET_DREQ[x]
  21 * 1 -- unreset; 0 -- reset
  22 */
  23#define CORE_RESET_BIT(x)               (1 << x)
  24#define NEON_RESET_BIT(x)               (1 << (x + 4))
  25#define CORE_DEBUG_RESET_BIT(x)         (1 << (x + 9))
  26#define CLUSTER_L2_RESET_BIT            (1 << 8)
  27#define CLUSTER_DEBUG_RESET_BIT         (1 << 13)
  28
  29/*
  30 * bits definition in SC_CPU_RESET_STATUS[x]
  31 * 1 -- reset status; 0 -- unreset status
  32 */
  33#define CORE_RESET_STATUS(x)            (1 << x)
  34#define NEON_RESET_STATUS(x)            (1 << (x + 4))
  35#define CORE_DEBUG_RESET_STATUS(x)      (1 << (x + 9))
  36#define CLUSTER_L2_RESET_STATUS         (1 << 8)
  37#define CLUSTER_DEBUG_RESET_STATUS      (1 << 13)
  38#define CORE_WFI_STATUS(x)              (1 << (x + 16))
  39#define CORE_WFE_STATUS(x)              (1 << (x + 20))
  40#define CORE_DEBUG_ACK(x)               (1 << (x + 24))
  41
  42#define SC_CPU_RESET_REQ(x)             (0x520 + (x << 3))      /* reset */
  43#define SC_CPU_RESET_DREQ(x)            (0x524 + (x << 3))      /* unreset */
  44#define SC_CPU_RESET_STATUS(x)          (0x1520 + (x << 3))
  45
  46#define FAB_SF_MODE                     0x0c
  47#define FAB_SF_INVLD                    0x10
  48
  49/* bits definition in FB_SF_INVLD */
  50#define FB_SF_INVLD_START               (1 << 8)
  51
  52#define HIP04_MAX_CLUSTERS              4
  53#define HIP04_MAX_CPUS_PER_CLUSTER      4
  54
  55#define POLL_MSEC       10
  56#define TIMEOUT_MSEC    1000
  57
  58static void __iomem *sysctrl, *fabric;
  59static int hip04_cpu_table[HIP04_MAX_CLUSTERS][HIP04_MAX_CPUS_PER_CLUSTER];
  60static DEFINE_SPINLOCK(boot_lock);
  61static u32 fabric_phys_addr;
  62/*
  63 * [0]: bootwrapper physical address
  64 * [1]: bootwrapper size
  65 * [2]: relocation address
  66 * [3]: relocation size
  67 */
  68static u32 hip04_boot_method[4];
  69
  70static bool hip04_cluster_is_down(unsigned int cluster)
  71{
  72        int i;
  73
  74        for (i = 0; i < HIP04_MAX_CPUS_PER_CLUSTER; i++)
  75                if (hip04_cpu_table[cluster][i])
  76                        return false;
  77        return true;
  78}
  79
  80static void hip04_set_snoop_filter(unsigned int cluster, unsigned int on)
  81{
  82        unsigned long data;
  83
  84        if (!fabric)
  85                BUG();
  86        data = readl_relaxed(fabric + FAB_SF_MODE);
  87        if (on)
  88                data |= 1 << cluster;
  89        else
  90                data &= ~(1 << cluster);
  91        writel_relaxed(data, fabric + FAB_SF_MODE);
  92        do {
  93                cpu_relax();
  94        } while (data != readl_relaxed(fabric + FAB_SF_MODE));
  95}
  96
  97static int hip04_mcpm_power_up(unsigned int cpu, unsigned int cluster)
  98{
  99        unsigned long data;
 100        void __iomem *sys_dreq, *sys_status;
 101
 102        if (!sysctrl)
 103                return -ENODEV;
 104        if (cluster >= HIP04_MAX_CLUSTERS || cpu >= HIP04_MAX_CPUS_PER_CLUSTER)
 105                return -EINVAL;
 106
 107        spin_lock_irq(&boot_lock);
 108
 109        if (hip04_cpu_table[cluster][cpu])
 110                goto out;
 111
 112        sys_dreq = sysctrl + SC_CPU_RESET_DREQ(cluster);
 113        sys_status = sysctrl + SC_CPU_RESET_STATUS(cluster);
 114        if (hip04_cluster_is_down(cluster)) {
 115                data = CLUSTER_DEBUG_RESET_BIT;
 116                writel_relaxed(data, sys_dreq);
 117                do {
 118                        cpu_relax();
 119                        data = readl_relaxed(sys_status);
 120                } while (data & CLUSTER_DEBUG_RESET_STATUS);
 121        }
 122
 123        data = CORE_RESET_BIT(cpu) | NEON_RESET_BIT(cpu) | \
 124               CORE_DEBUG_RESET_BIT(cpu);
 125        writel_relaxed(data, sys_dreq);
 126        do {
 127                cpu_relax();
 128        } while (data == readl_relaxed(sys_status));
 129        /*
 130         * We may fail to power up core again without this delay.
 131         * It's not mentioned in document. It's found by test.
 132         */
 133        udelay(20);
 134out:
 135        hip04_cpu_table[cluster][cpu]++;
 136        spin_unlock_irq(&boot_lock);
 137
 138        return 0;
 139}
 140
 141static void hip04_mcpm_power_down(void)
 142{
 143        unsigned int mpidr, cpu, cluster;
 144        bool skip_wfi = false, last_man = false;
 145
 146        mpidr = read_cpuid_mpidr();
 147        cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
 148        cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
 149
 150        __mcpm_cpu_going_down(cpu, cluster);
 151
 152        spin_lock(&boot_lock);
 153        BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP);
 154        hip04_cpu_table[cluster][cpu]--;
 155        if (hip04_cpu_table[cluster][cpu] == 1) {
 156                /* A power_up request went ahead of us. */
 157                skip_wfi = true;
 158        } else if (hip04_cpu_table[cluster][cpu] > 1) {
 159                pr_err("Cluster %d CPU%d boots multiple times\n", cluster, cpu);
 160                BUG();
 161        }
 162
 163        last_man = hip04_cluster_is_down(cluster);
 164        if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) {
 165                spin_unlock(&boot_lock);
 166                /* Since it's Cortex A15, disable L2 prefetching. */
 167                asm volatile(
 168                "mcr    p15, 1, %0, c15, c0, 3 \n\t"
 169                "isb    \n\t"
 170                "dsb    "
 171                : : "r" (0x400) );
 172                v7_exit_coherency_flush(all);
 173                hip04_set_snoop_filter(cluster, 0);
 174                __mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN);
 175        } else {
 176                spin_unlock(&boot_lock);
 177                v7_exit_coherency_flush(louis);
 178        }
 179
 180        __mcpm_cpu_down(cpu, cluster);
 181
 182        if (!skip_wfi)
 183                wfi();
 184}
 185
 186static int hip04_mcpm_wait_for_powerdown(unsigned int cpu, unsigned int cluster)
 187{
 188        unsigned int data, tries, count;
 189        int ret = -ETIMEDOUT;
 190
 191        BUG_ON(cluster >= HIP04_MAX_CLUSTERS ||
 192               cpu >= HIP04_MAX_CPUS_PER_CLUSTER);
 193
 194        count = TIMEOUT_MSEC / POLL_MSEC;
 195        spin_lock_irq(&boot_lock);
 196        for (tries = 0; tries < count; tries++) {
 197                if (hip04_cpu_table[cluster][cpu]) {
 198                        ret = -EBUSY;
 199                        goto err;
 200                }
 201                cpu_relax();
 202                data = readl_relaxed(sysctrl + SC_CPU_RESET_STATUS(cluster));
 203                if (data & CORE_WFI_STATUS(cpu))
 204                        break;
 205                spin_unlock_irq(&boot_lock);
 206                /* Wait for clean L2 when the whole cluster is down. */
 207                msleep(POLL_MSEC);
 208                spin_lock_irq(&boot_lock);
 209        }
 210        if (tries >= count)
 211                goto err;
 212        data = CORE_RESET_BIT(cpu) | NEON_RESET_BIT(cpu) | \
 213               CORE_DEBUG_RESET_BIT(cpu);
 214        writel_relaxed(data, sysctrl + SC_CPU_RESET_REQ(cluster));
 215        for (tries = 0; tries < count; tries++) {
 216                cpu_relax();
 217                data = readl_relaxed(sysctrl + SC_CPU_RESET_STATUS(cluster));
 218                if (data & CORE_RESET_STATUS(cpu))
 219                        break;
 220        }
 221        if (tries >= count)
 222                goto err;
 223        spin_unlock_irq(&boot_lock);
 224        return 0;
 225err:
 226        spin_unlock_irq(&boot_lock);
 227        return ret;
 228}
 229
 230static void hip04_mcpm_powered_up(void)
 231{
 232        unsigned int mpidr, cpu, cluster;
 233
 234        mpidr = read_cpuid_mpidr();
 235        cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
 236        cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
 237
 238        spin_lock(&boot_lock);
 239        if (!hip04_cpu_table[cluster][cpu])
 240                hip04_cpu_table[cluster][cpu] = 1;
 241        spin_unlock(&boot_lock);
 242}
 243
 244static void __naked hip04_mcpm_power_up_setup(unsigned int affinity_level)
 245{
 246        asm volatile ("                 \n"
 247"       cmp     r0, #0                  \n"
 248"       bxeq    lr                      \n"
 249        /* calculate fabric phys address */
 250"       adr     r2, 2f                  \n"
 251"       ldmia   r2, {r1, r3}            \n"
 252"       sub     r0, r2, r1              \n"
 253"       ldr     r2, [r0, r3]            \n"
 254        /* get cluster id from MPIDR */
 255"       mrc     p15, 0, r0, c0, c0, 5   \n"
 256"       ubfx    r1, r0, #8, #8          \n"
 257        /* 1 << cluster id */
 258"       mov     r0, #1                  \n"
 259"       mov     r3, r0, lsl r1          \n"
 260"       ldr     r0, [r2, #"__stringify(FAB_SF_MODE)"]   \n"
 261"       tst     r0, r3                  \n"
 262"       bxne    lr                      \n"
 263"       orr     r1, r0, r3              \n"
 264"       str     r1, [r2, #"__stringify(FAB_SF_MODE)"]   \n"
 265"1:     ldr     r0, [r2, #"__stringify(FAB_SF_MODE)"]   \n"
 266"       tst     r0, r3                  \n"
 267"       beq     1b                      \n"
 268"       bx      lr                      \n"
 269
 270"       .align  2                       \n"
 271"2:     .word   .                       \n"
 272"       .word   fabric_phys_addr        \n"
 273        );
 274}
 275
 276static const struct mcpm_platform_ops hip04_mcpm_ops = {
 277        .power_up               = hip04_mcpm_power_up,
 278        .power_down             = hip04_mcpm_power_down,
 279        .wait_for_powerdown     = hip04_mcpm_wait_for_powerdown,
 280        .powered_up             = hip04_mcpm_powered_up,
 281};
 282
 283static bool __init hip04_cpu_table_init(void)
 284{
 285        unsigned int mpidr, cpu, cluster;
 286
 287        mpidr = read_cpuid_mpidr();
 288        cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
 289        cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
 290
 291        if (cluster >= HIP04_MAX_CLUSTERS ||
 292            cpu >= HIP04_MAX_CPUS_PER_CLUSTER) {
 293                pr_err("%s: boot CPU is out of bound!\n", __func__);
 294                return false;
 295        }
 296        hip04_set_snoop_filter(cluster, 1);
 297        hip04_cpu_table[cluster][cpu] = 1;
 298        return true;
 299}
 300
 301static int __init hip04_mcpm_init(void)
 302{
 303        struct device_node *np, *np_sctl, *np_fab;
 304        struct resource fab_res;
 305        void __iomem *relocation;
 306        int ret = -ENODEV;
 307
 308        np = of_find_compatible_node(NULL, NULL, "hisilicon,hip04-bootwrapper");
 309        if (!np)
 310                goto err;
 311        ret = of_property_read_u32_array(np, "boot-method",
 312                                         &hip04_boot_method[0], 4);
 313        if (ret)
 314                goto err;
 315        np_sctl = of_find_compatible_node(NULL, NULL, "hisilicon,sysctrl");
 316        if (!np_sctl)
 317                goto err;
 318        np_fab = of_find_compatible_node(NULL, NULL, "hisilicon,hip04-fabric");
 319        if (!np_fab)
 320                goto err;
 321
 322        ret = memblock_reserve(hip04_boot_method[0], hip04_boot_method[1]);
 323        if (ret)
 324                goto err;
 325
 326        relocation = ioremap(hip04_boot_method[2], hip04_boot_method[3]);
 327        if (!relocation) {
 328                pr_err("failed to map relocation space\n");
 329                ret = -ENOMEM;
 330                goto err_reloc;
 331        }
 332        sysctrl = of_iomap(np_sctl, 0);
 333        if (!sysctrl) {
 334                pr_err("failed to get sysctrl base\n");
 335                ret = -ENOMEM;
 336                goto err_sysctrl;
 337        }
 338        ret = of_address_to_resource(np_fab, 0, &fab_res);
 339        if (ret) {
 340                pr_err("failed to get fabric base phys\n");
 341                goto err_fabric;
 342        }
 343        fabric_phys_addr = fab_res.start;
 344        sync_cache_w(&fabric_phys_addr);
 345        fabric = of_iomap(np_fab, 0);
 346        if (!fabric) {
 347                pr_err("failed to get fabric base\n");
 348                ret = -ENOMEM;
 349                goto err_fabric;
 350        }
 351
 352        if (!hip04_cpu_table_init()) {
 353                ret = -EINVAL;
 354                goto err_table;
 355        }
 356        ret = mcpm_platform_register(&hip04_mcpm_ops);
 357        if (ret) {
 358                goto err_table;
 359        }
 360
 361        /*
 362         * Fill the instruction address that is used after secondary core
 363         * out of reset.
 364         */
 365        writel_relaxed(hip04_boot_method[0], relocation);
 366        writel_relaxed(0xa5a5a5a5, relocation + 4);     /* magic number */
 367        writel_relaxed(virt_to_phys(mcpm_entry_point), relocation + 8);
 368        writel_relaxed(0, relocation + 12);
 369        iounmap(relocation);
 370
 371        mcpm_sync_init(hip04_mcpm_power_up_setup);
 372        mcpm_smp_set_ops();
 373        pr_info("HiP04 MCPM initialized\n");
 374        return ret;
 375err_table:
 376        iounmap(fabric);
 377err_fabric:
 378        iounmap(sysctrl);
 379err_sysctrl:
 380        iounmap(relocation);
 381err_reloc:
 382        memblock_free(hip04_boot_method[0], hip04_boot_method[1]);
 383err:
 384        return ret;
 385}
 386early_initcall(hip04_mcpm_init);
 387