Branch data Line data Source code
1 : : /*
2 : : * arch/arm/mach-vexpress/tc2_pm_psci.c - TC2 PSCI support
3 : : *
4 : : * Created by: Achin Gupta, December 2012
5 : : * Copyright: (C) 2012 ARM Limited
6 : : *
7 : : * Some portions of this file were originally written by Nicolas Pitre
8 : : * Copyright: (C) 2012 Linaro 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/init.h>
16 : : #include <linux/kernel.h>
17 : : #include <linux/of.h>
18 : : #include <linux/spinlock.h>
19 : : #include <linux/errno.h>
20 : :
21 : : #include <asm/mcpm.h>
22 : : #include <asm/proc-fns.h>
23 : : #include <asm/cacheflush.h>
24 : : #include <asm/psci.h>
25 : : #include <asm/atomic.h>
26 : : #include <asm/cputype.h>
27 : : #include <asm/cp15.h>
28 : :
29 : : #include <mach/motherboard.h>
30 : :
31 : : #include <linux/vexpress.h>
32 : :
33 : : /*
34 : : * Platform specific state id understood by the firmware and used to
35 : : * program the power controller
36 : : */
37 : : #define PSCI_POWER_STATE_ID 0
38 : :
39 : : #define TC2_CLUSTERS 2
40 : : #define TC2_MAX_CPUS_PER_CLUSTER 3
41 : :
42 : : static atomic_t tc2_pm_use_count[TC2_MAX_CPUS_PER_CLUSTER][TC2_CLUSTERS];
43 : :
44 : 0 : static int tc2_pm_psci_power_up(unsigned int cpu, unsigned int cluster)
45 : : {
46 : 0 : unsigned int mpidr = (cluster << 8) | cpu;
47 : : int ret = 0;
48 : :
49 [ # # ]: 0 : BUG_ON(!psci_ops.cpu_on);
50 : :
51 [ # # # ]: 0 : switch (atomic_inc_return(&tc2_pm_use_count[cpu][cluster])) {
52 : : case 1:
53 : : /*
54 : : * This is a request to power up a cpu that linux thinks has
55 : : * been powered down. Retries are needed if the firmware has
56 : : * seen the power down request as yet.
57 : : */
58 : : do
59 : 0 : ret = psci_ops.cpu_on(mpidr,
60 : : virt_to_phys(mcpm_entry_point));
61 [ # # ]: 0 : while (ret == -EAGAIN);
62 : :
63 : : return ret;
64 : : case 2:
65 : : /* This power up request has overtaken a power down request */
66 : : return ret;
67 : : default:
68 : : /* Any other value is a bug */
69 : 0 : BUG();
70 : : }
71 : : }
72 : :
73 : 0 : static void tc2_pm_psci_power_down(void)
74 : : {
75 : : struct psci_power_state power_state;
76 : : unsigned int mpidr, cpu, cluster;
77 : :
78 : : mpidr = read_cpuid_mpidr();
79 : 0 : cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
80 : 0 : cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
81 : :
82 [ # # ]: 0 : BUG_ON(!psci_ops.cpu_off);
83 : :
84 [ # # # ]: 0 : switch (atomic_dec_return(&tc2_pm_use_count[cpu][cluster])) {
85 : : case 1:
86 : : /*
87 : : * Overtaken by a power up. Flush caches, exit coherency,
88 : : * return & fake a reset
89 : : */
90 : 0 : set_cr(get_cr() & ~CR_C);
91 : :
92 : 0 : flush_cache_louis();
93 : :
94 : 0 : asm volatile ("clrex");
95 : 0 : set_auxcr(get_auxcr() & ~(1 << 6));
96 : :
97 : 0 : return;
98 : : case 0:
99 : : /* A normal request to possibly power down the cluster */
100 : 0 : power_state.id = PSCI_POWER_STATE_ID;
101 : 0 : power_state.type = PSCI_POWER_STATE_TYPE_POWER_DOWN;
102 : 0 : power_state.affinity_level = PSCI_POWER_STATE_AFFINITY_LEVEL1;
103 : :
104 : 0 : psci_ops.cpu_off(power_state);
105 : :
106 : : /* On success this function never returns */
107 : : default:
108 : : /* Any other value is a bug */
109 : 0 : BUG();
110 : : }
111 : : }
112 : :
113 : 0 : static void tc2_pm_psci_suspend(u64 unused)
114 : : {
115 : : struct psci_power_state power_state;
116 : :
117 [ # # ]: 0 : BUG_ON(!psci_ops.cpu_suspend);
118 : :
119 : : /* On TC2 always attempt to power down the cluster */
120 : 0 : power_state.id = PSCI_POWER_STATE_ID;
121 : 0 : power_state.type = PSCI_POWER_STATE_TYPE_POWER_DOWN;
122 : 0 : power_state.affinity_level = PSCI_POWER_STATE_AFFINITY_LEVEL1;
123 : :
124 : 0 : psci_ops.cpu_suspend(power_state, virt_to_phys(mcpm_entry_point));
125 : :
126 : : /* On success this function never returns */
127 : 0 : BUG();
128 : : }
129 : :
130 : : static const struct mcpm_platform_ops tc2_pm_power_ops = {
131 : : .power_up = tc2_pm_psci_power_up,
132 : : .power_down = tc2_pm_psci_power_down,
133 : : .suspend = tc2_pm_psci_suspend,
134 : : };
135 : :
136 : 0 : static void __init tc2_pm_usage_count_init(void)
137 : : {
138 : : unsigned int mpidr, cpu, cluster;
139 : :
140 : : mpidr = read_cpuid_mpidr();
141 : 0 : cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
142 : 0 : cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
143 : :
144 : : pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
145 [ # # ]: 0 : BUG_ON(cluster >= TC2_CLUSTERS || cpu >= TC2_MAX_CPUS_PER_CLUSTER);
146 : :
147 : 0 : atomic_set(&tc2_pm_use_count[cpu][cluster], 1);
148 : 0 : }
149 : :
150 : 0 : static int __init tc2_pm_psci_init(void)
151 : : {
152 : : int ret;
153 : :
154 : 0 : ret = psci_probe();
155 [ # # ]: 0 : if (ret) {
156 : : pr_debug("psci not found. Aborting psci init\n");
157 : : return -ENODEV;
158 : : }
159 : :
160 [ # # ]: 0 : if (!of_machine_is_compatible("arm,vexpress,v2p-ca15_a7"))
161 : : return -ENODEV;
162 : :
163 : 0 : tc2_pm_usage_count_init();
164 : :
165 : 0 : ret = mcpm_platform_register(&tc2_pm_power_ops);
166 [ # # ]: 0 : if (!ret)
167 : 0 : ret = mcpm_sync_init(NULL);
168 [ # # ]: 0 : if (!ret)
169 : 0 : pr_info("TC2 power management using PSCI initialized\n");
170 : 0 : return ret;
171 : : }
172 : :
173 : : early_initcall(tc2_pm_psci_init);
|