blob: eefb029197ca004f81167e6098e52da338886001 [file] [log] [blame]
Lorenzo Pieralisi63819cb2013-07-16 17:05:43 +01001/*
2 * Versatile Express Serial Power Controller (SPC) support
3 *
4 * Copyright (C) 2013 ARM Ltd.
5 *
6 * Authors: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
7 * Achin Gupta <achin.gupta@arm.com>
8 * Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
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 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
15 * kind, whether express or implied; without even the implied warranty
16 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 */
19
20#include <linux/err.h>
21#include <linux/io.h>
22#include <linux/slab.h>
23
24#include <asm/cacheflush.h>
25
26#define SPCLOG "vexpress-spc: "
27
28/* SPC wake-up IRQs status and mask */
29#define WAKE_INT_MASK 0x24
30#define WAKE_INT_RAW 0x28
31#define WAKE_INT_STAT 0x2c
32/* SPC power down registers */
33#define A15_PWRDN_EN 0x30
34#define A7_PWRDN_EN 0x34
35/* SPC per-CPU mailboxes */
36#define A15_BX_ADDR0 0x68
37#define A7_BX_ADDR0 0x78
38
39/* wake-up interrupt masks */
40#define GBL_WAKEUP_INT_MSK (0x3 << 10)
41
42/* TC2 static dual-cluster configuration */
43#define MAX_CLUSTERS 2
44
45struct ve_spc_drvdata {
46 void __iomem *baseaddr;
47 /*
48 * A15s cluster identifier
49 * It corresponds to A15 processors MPIDR[15:8] bitfield
50 */
51 u32 a15_clusid;
52};
53
54static struct ve_spc_drvdata *info;
55
56static inline bool cluster_is_a15(u32 cluster)
57{
58 return cluster == info->a15_clusid;
59}
60
61/**
62 * ve_spc_global_wakeup_irq()
63 *
64 * Function to set/clear global wakeup IRQs. Not protected by locking since
65 * it might be used in code paths where normal cacheable locks are not
66 * working. Locking must be provided by the caller to ensure atomicity.
67 *
68 * @set: if true, global wake-up IRQs are set, if false they are cleared
69 */
70void ve_spc_global_wakeup_irq(bool set)
71{
72 u32 reg;
73
74 reg = readl_relaxed(info->baseaddr + WAKE_INT_MASK);
75
76 if (set)
77 reg |= GBL_WAKEUP_INT_MSK;
78 else
79 reg &= ~GBL_WAKEUP_INT_MSK;
80
81 writel_relaxed(reg, info->baseaddr + WAKE_INT_MASK);
82}
83
84/**
85 * ve_spc_cpu_wakeup_irq()
86 *
87 * Function to set/clear per-CPU wake-up IRQs. Not protected by locking since
88 * it might be used in code paths where normal cacheable locks are not
89 * working. Locking must be provided by the caller to ensure atomicity.
90 *
91 * @cluster: mpidr[15:8] bitfield describing cluster affinity level
92 * @cpu: mpidr[7:0] bitfield describing cpu affinity level
93 * @set: if true, wake-up IRQs are set, if false they are cleared
94 */
95void ve_spc_cpu_wakeup_irq(u32 cluster, u32 cpu, bool set)
96{
97 u32 mask, reg;
98
99 if (cluster >= MAX_CLUSTERS)
100 return;
101
102 mask = 1 << cpu;
103
104 if (!cluster_is_a15(cluster))
105 mask <<= 4;
106
107 reg = readl_relaxed(info->baseaddr + WAKE_INT_MASK);
108
109 if (set)
110 reg |= mask;
111 else
112 reg &= ~mask;
113
114 writel_relaxed(reg, info->baseaddr + WAKE_INT_MASK);
115}
116
117/**
118 * ve_spc_set_resume_addr() - set the jump address used for warm boot
119 *
120 * @cluster: mpidr[15:8] bitfield describing cluster affinity level
121 * @cpu: mpidr[7:0] bitfield describing cpu affinity level
122 * @addr: physical resume address
123 */
124void ve_spc_set_resume_addr(u32 cluster, u32 cpu, u32 addr)
125{
126 void __iomem *baseaddr;
127
128 if (cluster >= MAX_CLUSTERS)
129 return;
130
131 if (cluster_is_a15(cluster))
132 baseaddr = info->baseaddr + A15_BX_ADDR0 + (cpu << 2);
133 else
134 baseaddr = info->baseaddr + A7_BX_ADDR0 + (cpu << 2);
135
136 writel_relaxed(addr, baseaddr);
137}
138
139/**
140 * ve_spc_powerdown()
141 *
142 * Function to enable/disable cluster powerdown. Not protected by locking
143 * since it might be used in code paths where normal cacheable locks are not
144 * working. Locking must be provided by the caller to ensure atomicity.
145 *
146 * @cluster: mpidr[15:8] bitfield describing cluster affinity level
147 * @enable: if true enables powerdown, if false disables it
148 */
149void ve_spc_powerdown(u32 cluster, bool enable)
150{
151 u32 pwdrn_reg;
152
153 if (cluster >= MAX_CLUSTERS)
154 return;
155
156 pwdrn_reg = cluster_is_a15(cluster) ? A15_PWRDN_EN : A7_PWRDN_EN;
157 writel_relaxed(enable, info->baseaddr + pwdrn_reg);
158}
159
160int __init ve_spc_init(void __iomem *baseaddr, u32 a15_clusid)
161{
162 info = kzalloc(sizeof(*info), GFP_KERNEL);
163 if (!info) {
164 pr_err(SPCLOG "unable to allocate mem\n");
165 return -ENOMEM;
166 }
167
168 info->baseaddr = baseaddr;
169 info->a15_clusid = a15_clusid;
170
171 /*
172 * Multi-cluster systems may need this data when non-coherent, during
173 * cluster power-up/power-down. Make sure driver info reaches main
174 * memory.
175 */
176 sync_cache_w(info);
177 sync_cache_w(&info);
178
179 return 0;
180}