LCOV - code coverage report
Current view: top level - arch/arm/mach-vexpress - tc2_pm.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 52 108 48.1 %
Date: 2014-02-18 Functions: 5 9 55.6 %
Branches: 37 81 45.7 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * arch/arm/mach-vexpress/tc2_pm.c - TC2 power management support
       3                 :            :  *
       4                 :            :  * Created by:  Nicolas Pitre, October 2012
       5                 :            :  * Copyright:   (C) 2012-2013  Linaro Limited
       6                 :            :  *
       7                 :            :  * Some portions of this file were originally written by Achin Gupta
       8                 :            :  * Copyright:   (C) 2012  ARM Limited
       9                 :            :  *
      10                 :            :  * This program is free software; you can redistribute it and/or modify
      11                 :            :  * it under the terms of the GNU General Public License version 2 as
      12                 :            :  * published by the Free Software Foundation.
      13                 :            :  */
      14                 :            : 
      15                 :            : #include <linux/delay.h>
      16                 :            : #include <linux/init.h>
      17                 :            : #include <linux/io.h>
      18                 :            : #include <linux/kernel.h>
      19                 :            : #include <linux/of_address.h>
      20                 :            : #include <linux/of_irq.h>
      21                 :            : #include <linux/spinlock.h>
      22                 :            : #include <linux/errno.h>
      23                 :            : #include <linux/irqchip/arm-gic.h>
      24                 :            : 
      25                 :            : #include <asm/mcpm.h>
      26                 :            : #include <asm/proc-fns.h>
      27                 :            : #include <asm/cacheflush.h>
      28                 :            : #include <asm/cputype.h>
      29                 :            : #include <asm/cp15.h>
      30                 :            : #include <asm/psci.h>
      31                 :            : 
      32                 :            : #include <linux/arm-cci.h>
      33                 :            : 
      34                 :            : #include "spc.h"
      35                 :            : 
      36                 :            : /* SCC conf registers */
      37                 :            : #define RESET_CTRL              0x018
      38                 :            : #define RESET_A15_NCORERESET(cpu)       (1 << (2 + (cpu)))
      39                 :            : #define RESET_A7_NCORERESET(cpu)        (1 << (16 + (cpu)))
      40                 :            : 
      41                 :            : #define A15_CONF                0x400
      42                 :            : #define A7_CONF                 0x500
      43                 :            : #define SYS_INFO                0x700
      44                 :            : #define SPC_BASE                0xb00
      45                 :            : 
      46                 :            : static void __iomem *scc;
      47                 :            : 
      48                 :            : /*
      49                 :            :  * We can't use regular spinlocks. In the switcher case, it is possible
      50                 :            :  * for an outbound CPU to call power_down() after its inbound counterpart
      51                 :            :  * is already live using the same logical CPU number which trips lockdep
      52                 :            :  * debugging.
      53                 :            :  */
      54                 :            : static arch_spinlock_t tc2_pm_lock = __ARCH_SPIN_LOCK_UNLOCKED;
      55                 :            : 
      56                 :            : #define TC2_CLUSTERS                    2
      57                 :            : #define TC2_MAX_CPUS_PER_CLUSTER        3
      58                 :            : 
      59                 :            : static unsigned int tc2_nr_cpus[TC2_CLUSTERS];
      60                 :            : 
      61                 :            : /* Keep per-cpu usage count to cope with unordered up/down requests */
      62                 :            : static int tc2_pm_use_count[TC2_MAX_CPUS_PER_CLUSTER][TC2_CLUSTERS];
      63                 :            : 
      64                 :            : #define tc2_cluster_unused(cluster) \
      65                 :            :         (!tc2_pm_use_count[0][cluster] && \
      66                 :            :          !tc2_pm_use_count[1][cluster] && \
      67                 :            :          !tc2_pm_use_count[2][cluster])
      68                 :            : 
      69                 :          0 : static int tc2_pm_power_up(unsigned int cpu, unsigned int cluster)
      70                 :            : {
      71                 :            :         pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
      72    [ +  + ][ + ]:       4877 :         if (cluster >= TC2_CLUSTERS || cpu >= tc2_nr_cpus[cluster])
      73                 :            :                 return -EINVAL;
      74                 :            : 
      75                 :            :         /*
      76                 :            :          * Since this is called with IRQs enabled, and no arch_spin_lock_irq
      77                 :            :          * variant exists, we need to disable IRQs manually here.
      78                 :            :          */
      79                 :            :         local_irq_disable();
      80                 :            :         arch_spin_lock(&tc2_pm_lock);
      81                 :            : 
      82 [ +  + ][ +  + ]:       4878 :         if (tc2_cluster_unused(cluster))
                 [ +  - ]
      83                 :       3678 :                 ve_spc_powerdown(cluster, false);
      84                 :            : 
      85                 :       4878 :         tc2_pm_use_count[cpu][cluster]++;
      86         [ +  - ]:       4878 :         if (tc2_pm_use_count[cpu][cluster] == 1) {
      87                 :       4878 :                 ve_spc_set_resume_addr(cluster, cpu,
      88                 :            :                                        virt_to_phys(mcpm_entry_point));
      89                 :       4878 :                 ve_spc_cpu_wakeup_irq(cluster, cpu, true);
      90         [ #  # ]:          0 :         } else if (tc2_pm_use_count[cpu][cluster] != 2) {
      91                 :            :                 /*
      92                 :            :                  * The only possible values are:
      93                 :            :                  * 0 = CPU down
      94                 :            :                  * 1 = CPU (still) up
      95                 :            :                  * 2 = CPU requested to be up before it had a chance
      96                 :            :                  *     to actually make itself down.
      97                 :            :                  * Any other value is a bug.
      98                 :            :                  */
      99                 :          0 :                 BUG();
     100                 :            :         }
     101                 :            : 
     102                 :            :         arch_spin_unlock(&tc2_pm_lock);
     103                 :            :         local_irq_enable();
     104                 :            : 
     105                 :       4878 :         return 0;
     106                 :            : }
     107                 :            : 
     108                 :          0 : static void tc2_pm_down(u64 residency)
     109                 :            : {
     110                 :            :         unsigned int mpidr, cpu, cluster;
     111                 :            :         bool last_man = false, skip_wfi = false;
     112                 :            : 
     113                 :            :         mpidr = read_cpuid_mpidr();
     114                 :    1200195 :         cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
     115                 :    1200195 :         cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
     116                 :            : 
     117                 :            :         pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
     118         [ -  + ]:    1200195 :         BUG_ON(cluster >= TC2_CLUSTERS || cpu >= TC2_MAX_CPUS_PER_CLUSTER);
     119                 :            : 
     120                 :    1200195 :         __mcpm_cpu_going_down(cpu, cluster);
     121                 :            : 
     122                 :            :         arch_spin_lock(&tc2_pm_lock);
     123         [ -  + ]:    1200197 :         BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP);
     124                 :    1200197 :         tc2_pm_use_count[cpu][cluster]--;
     125         [ +  - ]:    1200197 :         if (tc2_pm_use_count[cpu][cluster] == 0) {
     126                 :    1200197 :                 ve_spc_cpu_wakeup_irq(cluster, cpu, true);
     127 [ +  + ][ +  + ]:    1200197 :                 if (tc2_cluster_unused(cluster)) {
                 [ +  - ]
     128                 :     767476 :                         ve_spc_powerdown(cluster, true);
     129                 :     767476 :                         ve_spc_global_wakeup_irq(true);
     130                 :            :                         last_man = true;
     131                 :            :                 }
     132         [ #  # ]:          0 :         } else if (tc2_pm_use_count[cpu][cluster] == 1) {
     133                 :            :                 /*
     134                 :            :                  * A power_up request went ahead of us.
     135                 :            :                  * Even if we do not want to shut this CPU down,
     136                 :            :                  * the caller expects a certain state as if the WFI
     137                 :            :                  * was aborted.  So let's continue with cache cleaning.
     138                 :            :                  */
     139                 :            :                 skip_wfi = true;
     140                 :            :         } else
     141                 :          0 :                 BUG();
     142                 :            : 
     143                 :            :         /*
     144                 :            :          * If the CPU is committed to power down, make sure
     145                 :            :          * the power controller will be in charge of waking it
     146                 :            :          * up upon IRQ, ie IRQ lines are cut from GIC CPU IF
     147                 :            :          * to the CPU by disabling the GIC CPU IF to prevent wfi
     148                 :            :          * from completing execution behind power controller back
     149                 :            :          */
     150         [ +  - ]:    2400392 :         if (!skip_wfi)
     151                 :    1200197 :                 gic_cpu_if_down();
     152                 :            : 
     153 [ +  + ][ +  + ]:    1200197 :         if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) {
     154                 :            :                 arch_spin_unlock(&tc2_pm_lock);
     155                 :            : 
     156         [ +  + ]:     764228 :                 if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A15) {
     157                 :            :                         /*
     158                 :            :                          * On the Cortex-A15 we need to disable
     159                 :            :                          * L2 prefetching before flushing the cache.
     160                 :            :                          */
     161                 :     152996 :                         asm volatile(
     162                 :            :                         "mcr       p15, 1, %0, c15, c0, 3 \n\t"
     163                 :            :                         "isb       \n\t"
     164                 :            :                         "dsb       "
     165                 :            :                         : : "r" (0x400) );
     166                 :            :                 }
     167                 :            : 
     168                 :     764228 :                 v7_exit_coherency_flush(all);
     169                 :            : 
     170                 :     764226 :                 cci_disable_port_by_cpu(mpidr);
     171                 :            : 
     172                 :     764224 :                 __mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN);
     173                 :            :         } else {
     174                 :            :                 /*
     175                 :            :                  * If last man then undo any setup done previously.
     176                 :            :                  */
     177         [ +  + ]:     435969 :                 if (last_man) {
     178                 :       3248 :                         ve_spc_powerdown(cluster, false);
     179                 :       3248 :                         ve_spc_global_wakeup_irq(false);
     180                 :            :                 }
     181                 :            : 
     182                 :            :                 arch_spin_unlock(&tc2_pm_lock);
     183                 :            : 
     184                 :     435969 :                 v7_exit_coherency_flush(louis);
     185                 :            :         }
     186                 :            : 
     187                 :    1200193 :         __mcpm_cpu_down(cpu, cluster);
     188                 :            : 
     189                 :            :         /* Now we are prepared for power-down, do it: */
     190         [ +  + ]:    1200193 :         if (!skip_wfi)
     191                 :    1200191 :                 wfi();
     192                 :            : 
     193                 :            :         /* Not dead at this point?  Let our caller cope. */
     194                 :          2 : }
     195                 :            : 
     196                 :          0 : static void tc2_pm_power_down(void)
     197                 :            : {
     198                 :       4878 :         tc2_pm_down(0);
     199                 :          0 : }
     200                 :            : 
     201                 :            : static int tc2_core_in_reset(unsigned int cpu, unsigned int cluster)
     202                 :            : {
     203         [ #  # ]:          0 :         u32 mask = cluster ?
     204                 :          0 :                   RESET_A7_NCORERESET(cpu)
     205                 :          0 :                 : RESET_A15_NCORERESET(cpu);
     206                 :            : 
     207                 :          0 :         return !(readl_relaxed(scc + RESET_CTRL) & mask);
     208                 :            : }
     209                 :            : 
     210                 :            : #define POLL_MSEC 10
     211                 :            : #define TIMEOUT_MSEC 1000
     212                 :            : 
     213                 :          0 : static int tc2_pm_power_down_finish(unsigned int cpu, unsigned int cluster)
     214                 :            : {
     215                 :            :         unsigned tries;
     216                 :            : 
     217                 :            :         pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
     218         [ #  # ]:          0 :         BUG_ON(cluster >= TC2_CLUSTERS || cpu >= TC2_MAX_CPUS_PER_CLUSTER);
     219                 :            : 
     220         [ #  # ]:          0 :         for (tries = 0; tries < TIMEOUT_MSEC / POLL_MSEC; ++tries) {
     221                 :            :                 /*
     222                 :            :                  * Only examine the hardware state if the target CPU has
     223                 :            :                  * caught up at least as far as tc2_pm_down():
     224                 :            :                  */
     225         [ #  # ]:          0 :                 if (ACCESS_ONCE(tc2_pm_use_count[cpu][cluster]) == 0) {
     226                 :          0 :                         pr_debug("%s(cpu=%u, cluster=%u): RESET_CTRL = 0x%08X\n",
     227                 :            :                                  __func__, cpu, cluster,
     228                 :            :                                  readl_relaxed(scc + RESET_CTRL));
     229                 :            : 
     230                 :            :                         /*
     231                 :            :                          * We need the CPU to reach WFI, but the power
     232                 :            :                          * controller may put the cluster in reset and
     233                 :            :                          * power it off as soon as that happens, before
     234                 :            :                          * we have a chance to see STANDBYWFI.
     235                 :            :                          *
     236                 :            :                          * So we need to check for both conditions:
     237                 :            :                          */
     238   [ #  #  #  # ]:          0 :                         if (tc2_core_in_reset(cpu, cluster) ||
     239                 :          0 :                             ve_spc_cpu_in_wfi(cpu, cluster))
     240                 :            :                                 return 0; /* success: the CPU is halted */
     241                 :            :                 }
     242                 :            : 
     243                 :            :                 /* Otherwise, wait and retry: */
     244                 :          0 :                 msleep(POLL_MSEC);
     245                 :            :         }
     246                 :            : 
     247                 :            :         return -ETIMEDOUT; /* timeout */
     248                 :            : }
     249                 :            : 
     250                 :          0 : static void tc2_pm_suspend(u64 residency)
     251                 :            : {
     252                 :            :         unsigned int mpidr, cpu, cluster;
     253                 :            : 
     254                 :            :         mpidr = read_cpuid_mpidr();
     255                 :    1195312 :         cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
     256                 :    1195312 :         cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
     257                 :    1195312 :         ve_spc_set_resume_addr(cluster, cpu, virt_to_phys(mcpm_entry_point));
     258                 :    1195300 :         tc2_pm_down(residency);
     259                 :          0 : }
     260                 :            : 
     261                 :          0 : static void tc2_pm_powered_up(void)
     262                 :            : {
     263                 :            :         unsigned int mpidr, cpu, cluster;
     264                 :            :         unsigned long flags;
     265                 :            : 
     266                 :            :         mpidr = read_cpuid_mpidr();
     267                 :    1196993 :         cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
     268                 :    1196993 :         cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
     269                 :            : 
     270                 :            :         pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
     271         [ -  + ]:    1196993 :         BUG_ON(cluster >= TC2_CLUSTERS || cpu >= TC2_MAX_CPUS_PER_CLUSTER);
     272                 :            : 
     273                 :            :         local_irq_save(flags);
     274                 :            :         arch_spin_lock(&tc2_pm_lock);
     275                 :            : 
     276 [ +  + ][ +  + ]:    1200196 :         if (tc2_cluster_unused(cluster)) {
                 [ +  - ]
     277                 :     763797 :                 ve_spc_powerdown(cluster, false);
     278                 :     763797 :                 ve_spc_global_wakeup_irq(false);
     279                 :            :         }
     280                 :            : 
     281         [ +  + ]:    2397189 :         if (!tc2_pm_use_count[cpu][cluster])
     282                 :    1195318 :                 tc2_pm_use_count[cpu][cluster] = 1;
     283                 :            : 
     284                 :    1200196 :         ve_spc_cpu_wakeup_irq(cluster, cpu, false);
     285                 :    1200196 :         ve_spc_set_resume_addr(cluster, cpu, 0);
     286                 :            : 
     287                 :            :         arch_spin_unlock(&tc2_pm_lock);
     288         [ +  - ]:    1200196 :         local_irq_restore(flags);
     289                 :    1200196 : }
     290                 :            : 
     291                 :            : static const struct mcpm_platform_ops tc2_pm_power_ops = {
     292                 :            :         .power_up               = tc2_pm_power_up,
     293                 :            :         .power_down             = tc2_pm_power_down,
     294                 :            :         .power_down_finish      = tc2_pm_power_down_finish,
     295                 :            :         .suspend                = tc2_pm_suspend,
     296                 :            :         .powered_up             = tc2_pm_powered_up,
     297                 :            : };
     298                 :            : 
     299                 :          0 : static bool __init tc2_pm_usage_count_init(void)
     300                 :            : {
     301                 :            :         unsigned int mpidr, cpu, cluster;
     302                 :            : 
     303                 :            :         mpidr = read_cpuid_mpidr();
     304                 :          0 :         cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
     305                 :          0 :         cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
     306                 :            : 
     307                 :            :         pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
     308 [ #  # ][ #  # ]:          0 :         if (cluster >= TC2_CLUSTERS || cpu >= tc2_nr_cpus[cluster]) {
     309                 :          0 :                 pr_err("%s: boot CPU is out of bound!\n", __func__);
     310                 :          0 :                 return false;
     311                 :            :         }
     312                 :          0 :         tc2_pm_use_count[cpu][cluster] = 1;
     313                 :          0 :         return true;
     314                 :            : }
     315                 :            : 
     316                 :            : /*
     317                 :            :  * Enable cluster-level coherency, in preparation for turning on the MMU.
     318                 :            :  */
     319                 :          0 : static void __naked tc2_pm_power_up_setup(unsigned int affinity_level)
     320                 :            : {
     321                 :          0 :         asm volatile (" \n"
     322                 :            : "  cmp     r0, #1 \n"
     323                 :            : "  bxne    lr \n"
     324                 :            : "  b       cci_enable_port_for_self ");
     325                 :          0 : }
     326                 :            : 
     327                 :          0 : static int __init tc2_pm_init(void)
     328                 :            : {
     329                 :            :         int ret, irq;
     330                 :            :         u32 a15_cluster_id, a7_cluster_id, sys_info;
     331                 :            :         struct device_node *np;
     332                 :            : 
     333                 :          0 :         ret = psci_probe();
     334         [ #  # ]:          0 :         if (!ret) {
     335                 :            :                 pr_debug("psci found. Aborting native init\n");
     336                 :            :                 return -ENODEV;
     337                 :            :         }
     338                 :            : 
     339                 :            :         /*
     340                 :            :          * The power management-related features are hidden behind
     341                 :            :          * SCC registers. We need to extract runtime information like
     342                 :            :          * cluster ids and number of CPUs really available in clusters.
     343                 :            :          */
     344                 :          0 :         np = of_find_compatible_node(NULL, NULL,
     345                 :            :                         "arm,vexpress-scc,v2p-ca15_a7");
     346                 :          0 :         scc = of_iomap(np, 0);
     347         [ #  # ]:          0 :         if (!scc)
     348                 :            :                 return -ENODEV;
     349                 :            : 
     350                 :          0 :         a15_cluster_id = readl_relaxed(scc + A15_CONF) & 0xf;
     351                 :          0 :         a7_cluster_id = readl_relaxed(scc + A7_CONF) & 0xf;
     352         [ #  # ]:          0 :         if (a15_cluster_id >= TC2_CLUSTERS || a7_cluster_id >= TC2_CLUSTERS)
     353                 :            :                 return -EINVAL;
     354                 :            : 
     355                 :          0 :         sys_info = readl_relaxed(scc + SYS_INFO);
     356                 :          0 :         tc2_nr_cpus[a15_cluster_id] = (sys_info >> 16) & 0xf;
     357                 :          0 :         tc2_nr_cpus[a7_cluster_id] = (sys_info >> 20) & 0xf;
     358                 :            : 
     359                 :          0 :         irq = irq_of_parse_and_map(np, 0);
     360                 :            : 
     361                 :            :         /*
     362                 :            :          * A subset of the SCC registers is also used to communicate
     363                 :            :          * with the SPC (power controller). We need to be able to
     364                 :            :          * drive it very early in the boot process to power up
     365                 :            :          * processors, so we initialize the SPC driver here.
     366                 :            :          */
     367                 :          0 :         ret = ve_spc_init(scc + SPC_BASE, a15_cluster_id, irq);
     368         [ #  # ]:          0 :         if (ret)
     369                 :            :                 return ret;
     370                 :            : 
     371         [ #  # ]:          0 :         if (!cci_probed())
     372                 :            :                 return -ENODEV;
     373                 :            : 
     374         [ #  # ]:          0 :         if (!tc2_pm_usage_count_init())
     375                 :            :                 return -EINVAL;
     376                 :            : 
     377                 :          0 :         ret = mcpm_platform_register(&tc2_pm_power_ops);
     378         [ #  # ]:          0 :         if (!ret) {
     379                 :          0 :                 mcpm_sync_init(tc2_pm_power_up_setup);
     380                 :          0 :                 pr_info("TC2 power management initialized\n");
     381                 :            :         }
     382                 :          0 :         return ret;
     383                 :            : }
     384                 :            : 
     385                 :            : early_initcall(tc2_pm_init);

Generated by: LCOV version 1.9