LCOV - code coverage report
Current view: top level - arch/arm/mach-vexpress - dcscb.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 69 0.0 %
Date: 2014-02-18 Functions: 0 4 0.0 %
Branches: 0 38 0.0 %

           Branch data     Line data    Source code
       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/spinlock.h>
      16                 :            : #include <linux/errno.h>
      17                 :            : #include <linux/of_address.h>
      18                 :            : #include <linux/vexpress.h>
      19                 :            : #include <linux/arm-cci.h>
      20                 :            : 
      21                 :            : #include <asm/mcpm.h>
      22                 :            : #include <asm/proc-fns.h>
      23                 :            : #include <asm/cacheflush.h>
      24                 :            : #include <asm/cputype.h>
      25                 :            : #include <asm/cp15.h>
      26                 :            : #include <asm/psci.h>
      27                 :            : 
      28                 :            : 
      29                 :            : #define RST_HOLD0       0x0
      30                 :            : #define RST_HOLD1       0x4
      31                 :            : #define SYS_SWRESET     0x8
      32                 :            : #define RST_STAT0       0xc
      33                 :            : #define RST_STAT1       0x10
      34                 :            : #define EAG_CFG_R       0x20
      35                 :            : #define EAG_CFG_W       0x24
      36                 :            : #define KFC_CFG_R       0x28
      37                 :            : #define KFC_CFG_W       0x2c
      38                 :            : #define DCS_CFG_R       0x30
      39                 :            : 
      40                 :            : /*
      41                 :            :  * We can't use regular spinlocks. In the switcher case, it is possible
      42                 :            :  * for an outbound CPU to call power_down() while its inbound counterpart
      43                 :            :  * is already live using the same logical CPU number which trips lockdep
      44                 :            :  * debugging.
      45                 :            :  */
      46                 :            : static arch_spinlock_t dcscb_lock = __ARCH_SPIN_LOCK_UNLOCKED;
      47                 :            : 
      48                 :            : static void __iomem *dcscb_base;
      49                 :            : static int dcscb_use_count[4][2];
      50                 :            : static int dcscb_allcpus_mask[2];
      51                 :            : 
      52                 :          0 : static int dcscb_power_up(unsigned int cpu, unsigned int cluster)
      53                 :            : {
      54                 :          0 :         unsigned int rst_hold, cpumask = (1 << cpu);
      55                 :          0 :         unsigned int all_mask = dcscb_allcpus_mask[cluster];
      56                 :            : 
      57                 :            :         pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
      58         [ #  # ]:          0 :         if (cpu >= 4 || cluster >= 2)
      59                 :            :                 return -EINVAL;
      60                 :            : 
      61                 :            :         /*
      62                 :            :          * Since this is called with IRQs enabled, and no arch_spin_lock_irq
      63                 :            :          * variant exists, we need to disable IRQs manually here.
      64                 :            :          */
      65                 :            :         local_irq_disable();
      66                 :            :         arch_spin_lock(&dcscb_lock);
      67                 :            : 
      68                 :          0 :         dcscb_use_count[cpu][cluster]++;
      69         [ #  # ]:          0 :         if (dcscb_use_count[cpu][cluster] == 1) {
      70                 :          0 :                 rst_hold = readl_relaxed(dcscb_base + RST_HOLD0 + cluster * 4);
      71         [ #  # ]:          0 :                 if (rst_hold & (1 << 8)) {
      72                 :            :                         /* remove cluster reset and add individual CPU's reset */
      73                 :          0 :                         rst_hold &= ~(1 << 8);
      74                 :          0 :                         rst_hold |= all_mask;
      75                 :            :                 }
      76                 :          0 :                 rst_hold &= ~(cpumask | (cpumask << 4));
      77                 :          0 :                 writel_relaxed(rst_hold, dcscb_base + RST_HOLD0 + cluster * 4);
      78         [ #  # ]:          0 :         } else if (dcscb_use_count[cpu][cluster] != 2) {
      79                 :            :                 /*
      80                 :            :                  * The only possible values are:
      81                 :            :                  * 0 = CPU down
      82                 :            :                  * 1 = CPU (still) up
      83                 :            :                  * 2 = CPU requested to be up before it had a chance
      84                 :            :                  *     to actually make itself down.
      85                 :            :                  * Any other value is a bug.
      86                 :            :                  */
      87                 :          0 :                 BUG();
      88                 :            :         }
      89                 :            : 
      90                 :            :         arch_spin_unlock(&dcscb_lock);
      91                 :            :         local_irq_enable();
      92                 :            : 
      93                 :          0 :         return 0;
      94                 :            : }
      95                 :            : 
      96                 :          0 : static void dcscb_power_down(void)
      97                 :            : {
      98                 :            :         unsigned int mpidr, cpu, cluster, rst_hold, cpumask, all_mask;
      99                 :            :         bool last_man = false, skip_wfi = false;
     100                 :            : 
     101                 :            :         mpidr = read_cpuid_mpidr();
     102                 :          0 :         cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
     103                 :          0 :         cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
     104                 :          0 :         cpumask = (1 << cpu);
     105                 :          0 :         all_mask = dcscb_allcpus_mask[cluster];
     106                 :            : 
     107                 :            :         pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
     108         [ #  # ]:          0 :         BUG_ON(cpu >= 4 || cluster >= 2);
     109                 :            : 
     110                 :          0 :         __mcpm_cpu_going_down(cpu, cluster);
     111                 :            : 
     112                 :            :         arch_spin_lock(&dcscb_lock);
     113         [ #  # ]:          0 :         BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP);
     114                 :          0 :         dcscb_use_count[cpu][cluster]--;
     115         [ #  # ]:          0 :         if (dcscb_use_count[cpu][cluster] == 0) {
     116                 :          0 :                 rst_hold = readl_relaxed(dcscb_base + RST_HOLD0 + cluster * 4);
     117                 :          0 :                 rst_hold |= cpumask;
     118         [ #  # ]:          0 :                 if (((rst_hold | (rst_hold >> 4)) & all_mask) == all_mask) {
     119                 :          0 :                         rst_hold |= (1 << 8);
     120                 :            :                         last_man = true;
     121                 :            :                 }
     122                 :          0 :                 writel_relaxed(rst_hold, dcscb_base + RST_HOLD0 + cluster * 4);
     123         [ #  # ]:          0 :         } else if (dcscb_use_count[cpu][cluster] == 1) {
     124                 :            :                 /*
     125                 :            :                  * A power_up request went ahead of us.
     126                 :            :                  * Even if we do not want to shut this CPU down,
     127                 :            :                  * the caller expects a certain state as if the WFI
     128                 :            :                  * was aborted.  So let's continue with cache cleaning.
     129                 :            :                  */
     130                 :            :                 skip_wfi = true;
     131                 :            :         } else
     132                 :          0 :                 BUG();
     133                 :            : 
     134 [ #  # ][ #  # ]:          0 :         if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) {
     135                 :            :                 arch_spin_unlock(&dcscb_lock);
     136                 :            : 
     137                 :            :                 /* Flush all cache levels for this cluster. */
     138                 :          0 :                 v7_exit_coherency_flush(all);
     139                 :            : 
     140                 :            :                 /*
     141                 :            :                  * This is a harmless no-op.  On platforms with a real
     142                 :            :                  * outer cache this might either be needed or not,
     143                 :            :                  * depending on where the outer cache sits.
     144                 :            :                  */
     145                 :            :                 outer_flush_all();
     146                 :            : 
     147                 :            :                 /*
     148                 :            :                  * Disable cluster-level coherency by masking
     149                 :            :                  * incoming snoops and DVM messages:
     150                 :            :                  */
     151                 :          0 :                 cci_disable_port_by_cpu(mpidr);
     152                 :            : 
     153                 :          0 :                 __mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN);
     154                 :            :         } else {
     155                 :            :                 arch_spin_unlock(&dcscb_lock);
     156                 :            : 
     157                 :            :                 /* Disable and flush the local CPU cache. */
     158                 :          0 :                 v7_exit_coherency_flush(louis);
     159                 :            :         }
     160                 :            : 
     161                 :          0 :         __mcpm_cpu_down(cpu, cluster);
     162                 :            : 
     163                 :            :         /* Now we are prepared for power-down, do it: */
     164                 :          0 :         dsb();
     165         [ #  # ]:          0 :         if (!skip_wfi)
     166                 :          0 :                 wfi();
     167                 :            : 
     168                 :            :         /* Not dead at this point?  Let our caller cope. */
     169                 :          0 : }
     170                 :            : 
     171                 :            : static const struct mcpm_platform_ops dcscb_power_ops = {
     172                 :            :         .power_up       = dcscb_power_up,
     173                 :            :         .power_down     = dcscb_power_down,
     174                 :            : };
     175                 :            : 
     176                 :          0 : static void __init dcscb_usage_count_init(void)
     177                 :            : {
     178                 :            :         unsigned int mpidr, cpu, cluster;
     179                 :            : 
     180                 :            :         mpidr = read_cpuid_mpidr();
     181                 :          0 :         cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
     182                 :          0 :         cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
     183                 :            : 
     184                 :            :         pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
     185         [ #  # ]:          0 :         BUG_ON(cpu >= 4 || cluster >= 2);
     186                 :          0 :         dcscb_use_count[cpu][cluster] = 1;
     187                 :          0 : }
     188                 :            : 
     189                 :            : extern void dcscb_power_up_setup(unsigned int affinity_level);
     190                 :            : 
     191                 :          0 : static int __init dcscb_init(void)
     192                 :            : {
     193                 :            :         struct device_node *node;
     194                 :            :         unsigned int cfg;
     195                 :            :         int ret;
     196                 :            : 
     197                 :          0 :         ret = psci_probe();
     198         [ #  # ]:          0 :         if (!ret) {
     199                 :            :                 pr_debug("psci found. Aborting native init\n");
     200                 :            :                 return -ENODEV;
     201                 :            :         }
     202                 :            : 
     203         [ #  # ]:          0 :         if (!cci_probed())
     204                 :            :                 return -ENODEV;
     205                 :            : 
     206                 :          0 :         node = of_find_compatible_node(NULL, NULL, "arm,rtsm,dcscb");
     207         [ #  # ]:          0 :         if (!node)
     208                 :            :                 return -ENODEV;
     209                 :          0 :         dcscb_base = of_iomap(node, 0);
     210         [ #  # ]:          0 :         if (!dcscb_base)
     211                 :            :                 return -EADDRNOTAVAIL;
     212                 :          0 :         cfg = readl_relaxed(dcscb_base + DCS_CFG_R);
     213                 :          0 :         dcscb_allcpus_mask[0] = (1 << (((cfg >> 16) >> (0 << 2)) & 0xf)) - 1;
     214                 :          0 :         dcscb_allcpus_mask[1] = (1 << (((cfg >> 16) >> (1 << 2)) & 0xf)) - 1;
     215                 :          0 :         dcscb_usage_count_init();
     216                 :            : 
     217                 :          0 :         ret = mcpm_platform_register(&dcscb_power_ops);
     218         [ #  # ]:          0 :         if (!ret)
     219                 :          0 :                 ret = mcpm_sync_init(dcscb_power_up_setup);
     220         [ #  # ]:          0 :         if (ret) {
     221                 :          0 :                 iounmap(dcscb_base);
     222                 :          0 :                 return ret;
     223                 :            :         }
     224                 :            : 
     225                 :          0 :         pr_info("VExpress DCSCB support installed\n");
     226                 :            : 
     227                 :            :         /*
     228                 :            :          * Future entries into the kernel can now go
     229                 :            :          * through the cluster entry vectors.
     230                 :            :          */
     231                 :          0 :         vexpress_flags_set(virt_to_phys(mcpm_entry_point));
     232                 :            : 
     233                 :          0 :         return 0;
     234                 :            : }
     235                 :            : 
     236                 :            : early_initcall(dcscb_init);

Generated by: LCOV version 1.9