linux/arch/arm/mach-vexpress/dcscb.c
<<
>>
Prefs
   1/*
   2 * arch/arm/mach-vexpress/dcscb.c - Dual Cluster System Configuration Block
   3 *
   4 * Created by:  Nicolas Pitre, May 2012
   5 * Copyright:   (C) 2012-2013  Linaro Limited
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 */
  11
  12#include <linux/init.h>
  13#include <linux/kernel.h>
  14#include <linux/io.h>
  15#include <linux/errno.h>
  16#include <linux/of_address.h>
  17#include <linux/vexpress.h>
  18#include <linux/arm-cci.h>
  19
  20#include <asm/mcpm.h>
  21#include <asm/proc-fns.h>
  22#include <asm/cacheflush.h>
  23#include <asm/cputype.h>
  24#include <asm/cp15.h>
  25
  26
  27#define RST_HOLD0       0x0
  28#define RST_HOLD1       0x4
  29#define SYS_SWRESET     0x8
  30#define RST_STAT0       0xc
  31#define RST_STAT1       0x10
  32#define EAG_CFG_R       0x20
  33#define EAG_CFG_W       0x24
  34#define KFC_CFG_R       0x28
  35#define KFC_CFG_W       0x2c
  36#define DCS_CFG_R       0x30
  37
  38static void __iomem *dcscb_base;
  39static int dcscb_allcpus_mask[2];
  40
  41static int dcscb_cpu_powerup(unsigned int cpu, unsigned int cluster)
  42{
  43        unsigned int rst_hold, cpumask = (1 << cpu);
  44
  45        pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
  46        if (cluster >= 2 || !(cpumask & dcscb_allcpus_mask[cluster]))
  47                return -EINVAL;
  48
  49        rst_hold = readl_relaxed(dcscb_base + RST_HOLD0 + cluster * 4);
  50        rst_hold &= ~(cpumask | (cpumask << 4));
  51        writel_relaxed(rst_hold, dcscb_base + RST_HOLD0 + cluster * 4);
  52        return 0;
  53}
  54
  55static int dcscb_cluster_powerup(unsigned int cluster)
  56{
  57        unsigned int rst_hold;
  58
  59        pr_debug("%s: cluster %u\n", __func__, cluster);
  60        if (cluster >= 2)
  61                return -EINVAL;
  62
  63        /* remove cluster reset and add individual CPU's reset */
  64        rst_hold = readl_relaxed(dcscb_base + RST_HOLD0 + cluster * 4);
  65        rst_hold &= ~(1 << 8);
  66        rst_hold |= dcscb_allcpus_mask[cluster];
  67        writel_relaxed(rst_hold, dcscb_base + RST_HOLD0 + cluster * 4);
  68        return 0;
  69}
  70
  71static void dcscb_cpu_powerdown_prepare(unsigned int cpu, unsigned int cluster)
  72{
  73        unsigned int rst_hold;
  74
  75        pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
  76        BUG_ON(cluster >= 2 || !((1 << cpu) & dcscb_allcpus_mask[cluster]));
  77
  78        rst_hold = readl_relaxed(dcscb_base + RST_HOLD0 + cluster * 4);
  79        rst_hold |= (1 << cpu);
  80        writel_relaxed(rst_hold, dcscb_base + RST_HOLD0 + cluster * 4);
  81}
  82
  83static void dcscb_cluster_powerdown_prepare(unsigned int cluster)
  84{
  85        unsigned int rst_hold;
  86
  87        pr_debug("%s: cluster %u\n", __func__, cluster);
  88        BUG_ON(cluster >= 2);
  89
  90        rst_hold = readl_relaxed(dcscb_base + RST_HOLD0 + cluster * 4);
  91        rst_hold |= (1 << 8);
  92        writel_relaxed(rst_hold, dcscb_base + RST_HOLD0 + cluster * 4);
  93}
  94
  95static void dcscb_cpu_cache_disable(void)
  96{
  97        /* Disable and flush the local CPU cache. */
  98        v7_exit_coherency_flush(louis);
  99}
 100
 101static void dcscb_cluster_cache_disable(void)
 102{
 103        /* Flush all cache levels for this cluster. */
 104        v7_exit_coherency_flush(all);
 105
 106        /*
 107         * A full outer cache flush could be needed at this point
 108         * on platforms with such a cache, depending on where the
 109         * outer cache sits. In some cases the notion of a "last
 110         * cluster standing" would need to be implemented if the
 111         * outer cache is shared across clusters. In any case, when
 112         * the outer cache needs flushing, there is no concurrent
 113         * access to the cache controller to worry about and no
 114         * special locking besides what is already provided by the
 115         * MCPM state machinery is needed.
 116         */
 117
 118        /*
 119         * Disable cluster-level coherency by masking
 120         * incoming snoops and DVM messages:
 121         */
 122        cci_disable_port_by_cpu(read_cpuid_mpidr());
 123}
 124
 125static const struct mcpm_platform_ops dcscb_power_ops = {
 126        .cpu_powerup            = dcscb_cpu_powerup,
 127        .cluster_powerup        = dcscb_cluster_powerup,
 128        .cpu_powerdown_prepare  = dcscb_cpu_powerdown_prepare,
 129        .cluster_powerdown_prepare = dcscb_cluster_powerdown_prepare,
 130        .cpu_cache_disable      = dcscb_cpu_cache_disable,
 131        .cluster_cache_disable  = dcscb_cluster_cache_disable,
 132};
 133
 134extern void dcscb_power_up_setup(unsigned int affinity_level);
 135
 136static int __init dcscb_init(void)
 137{
 138        struct device_node *node;
 139        unsigned int cfg;
 140        int ret;
 141
 142        if (!cci_probed())
 143                return -ENODEV;
 144
 145        node = of_find_compatible_node(NULL, NULL, "arm,rtsm,dcscb");
 146        if (!node)
 147                return -ENODEV;
 148        dcscb_base = of_iomap(node, 0);
 149        if (!dcscb_base)
 150                return -EADDRNOTAVAIL;
 151        cfg = readl_relaxed(dcscb_base + DCS_CFG_R);
 152        dcscb_allcpus_mask[0] = (1 << (((cfg >> 16) >> (0 << 2)) & 0xf)) - 1;
 153        dcscb_allcpus_mask[1] = (1 << (((cfg >> 16) >> (1 << 2)) & 0xf)) - 1;
 154
 155        ret = mcpm_platform_register(&dcscb_power_ops);
 156        if (!ret)
 157                ret = mcpm_sync_init(dcscb_power_up_setup);
 158        if (ret) {
 159                iounmap(dcscb_base);
 160                return ret;
 161        }
 162
 163        pr_info("VExpress DCSCB support installed\n");
 164
 165        /*
 166         * Future entries into the kernel can now go
 167         * through the cluster entry vectors.
 168         */
 169        vexpress_flags_set(__pa_symbol(mcpm_entry_point));
 170
 171        return 0;
 172}
 173
 174early_initcall(dcscb_init);
 175