1
2
3
4
5
6
7
8
9
10
11
12#include <common.h>
13#include <dm.h>
14#include <regmap.h>
15#include <syscon.h>
16#include <asm/cpu.h>
17#include <asm/scu.h>
18#include <linux/bitops.h>
19#include <linux/delay.h>
20#include <linux/errno.h>
21#include <linux/io.h>
22#include <linux/kernel.h>
23
24
25struct ipc_regs {
26 u32 cmd;
27 u32 status;
28 u32 sptr;
29 u32 dptr;
30 u32 reserved[28];
31 u32 wbuf[4];
32 u32 rbuf[4];
33};
34
35struct scu {
36 struct ipc_regs *regs;
37};
38
39
40
41
42
43
44
45
46
47
48
49static void scu_ipc_send_command(struct ipc_regs *regs, u32 cmd)
50{
51 writel(cmd, ®s->cmd);
52}
53
54
55
56
57
58
59
60
61
62
63
64static int scu_ipc_check_status(struct ipc_regs *regs)
65{
66 int loop_count = 100000;
67 int status;
68
69 do {
70 status = readl(®s->status);
71 if (!(status & BIT(0)))
72 break;
73
74 udelay(1);
75 } while (--loop_count);
76 if (!loop_count)
77 return -ETIMEDOUT;
78
79 if (status & BIT(1)) {
80 printf("%s() status=0x%08x\n", __func__, status);
81 return -EIO;
82 }
83
84 return 0;
85}
86
87static int scu_ipc_cmd(struct ipc_regs *regs, u32 cmd, u32 sub,
88 u32 *in, int inlen, u32 *out, int outlen)
89{
90 int i, err;
91
92 for (i = 0; i < inlen; i++)
93 writel(*in++, ®s->wbuf[i]);
94
95 scu_ipc_send_command(regs, (inlen << 16) | (sub << 12) | cmd);
96 err = scu_ipc_check_status(regs);
97
98 if (!err) {
99 for (i = 0; i < outlen; i++)
100 *out++ = readl(®s->rbuf[i]);
101 }
102
103 return err;
104}
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121int scu_ipc_raw_command(u32 cmd, u32 sub, u32 *in, int inlen, u32 *out,
122 int outlen, u32 dptr, u32 sptr)
123{
124 int inbuflen = DIV_ROUND_UP(inlen, 4);
125 struct udevice *dev;
126 struct scu *scu;
127 int ret;
128
129 ret = syscon_get_by_driver_data(X86_SYSCON_SCU, &dev);
130 if (ret)
131 return ret;
132
133 scu = dev_get_priv(dev);
134
135
136 if (inbuflen > 4)
137 return -EINVAL;
138
139 writel(dptr, &scu->regs->dptr);
140 writel(sptr, &scu->regs->sptr);
141
142
143
144
145
146
147
148
149
150 u32 inbuf[4] = {0};
151
152 memcpy(inbuf, in, inlen);
153
154 return scu_ipc_cmd(scu->regs, cmd, sub, inbuf, inlen, out, outlen);
155}
156
157
158
159
160
161
162
163
164
165
166
167
168
169int scu_ipc_simple_command(u32 cmd, u32 sub)
170{
171 struct scu *scu;
172 struct udevice *dev;
173 int ret;
174
175 ret = syscon_get_by_driver_data(X86_SYSCON_SCU, &dev);
176 if (ret)
177 return ret;
178
179 scu = dev_get_priv(dev);
180
181 scu_ipc_send_command(scu->regs, sub << 12 | cmd);
182 return scu_ipc_check_status(scu->regs);
183}
184
185
186
187
188
189
190
191
192
193
194
195
196int scu_ipc_command(u32 cmd, u32 sub, u32 *in, int inlen, u32 *out, int outlen)
197{
198 struct scu *scu;
199 struct udevice *dev;
200 int ret;
201
202 ret = syscon_get_by_driver_data(X86_SYSCON_SCU, &dev);
203 if (ret)
204 return ret;
205
206 scu = dev_get_priv(dev);
207
208 return scu_ipc_cmd(scu->regs, cmd, sub, in, inlen, out, outlen);
209}
210
211static int scu_ipc_probe(struct udevice *dev)
212{
213 struct scu *scu = dev_get_priv(dev);
214
215 scu->regs = syscon_get_first_range(X86_SYSCON_SCU);
216
217 return 0;
218}
219
220static const struct udevice_id scu_ipc_match[] = {
221 { .compatible = "intel,scu-ipc", .data = X86_SYSCON_SCU },
222 { }
223};
224
225U_BOOT_DRIVER(scu_ipc) = {
226 .name = "scu_ipc",
227 .id = UCLASS_SYSCON,
228 .of_match = scu_ipc_match,
229 .probe = scu_ipc_probe,
230 .priv_auto = sizeof(struct scu),
231};
232