Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/x86/platform/olpc/olpc.c
26489 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* Support for the OLPC DCON and OLPC EC access
4
*
5
* Copyright © 2006 Advanced Micro Devices, Inc.
6
* Copyright © 2007-2008 Andres Salomon <[email protected]>
7
*/
8
9
#include <linux/kernel.h>
10
#include <linux/init.h>
11
#include <linux/export.h>
12
#include <linux/delay.h>
13
#include <linux/io.h>
14
#include <linux/string.h>
15
#include <linux/platform_device.h>
16
#include <linux/of.h>
17
#include <linux/syscore_ops.h>
18
#include <linux/mutex.h>
19
#include <linux/olpc-ec.h>
20
21
#include <asm/geode.h>
22
#include <asm/setup.h>
23
#include <asm/olpc.h>
24
#include <asm/olpc_ofw.h>
25
26
struct olpc_platform_t olpc_platform_info;
27
EXPORT_SYMBOL_GPL(olpc_platform_info);
28
29
/* what the timeout *should* be (in ms) */
30
#define EC_BASE_TIMEOUT 20
31
32
/* the timeout that bugs in the EC might force us to actually use */
33
static int ec_timeout = EC_BASE_TIMEOUT;
34
35
static int __init olpc_ec_timeout_set(char *str)
36
{
37
if (get_option(&str, &ec_timeout) != 1) {
38
ec_timeout = EC_BASE_TIMEOUT;
39
printk(KERN_ERR "olpc-ec: invalid argument to "
40
"'olpc_ec_timeout=', ignoring!\n");
41
}
42
printk(KERN_DEBUG "olpc-ec: using %d ms delay for EC commands.\n",
43
ec_timeout);
44
return 1;
45
}
46
__setup("olpc_ec_timeout=", olpc_ec_timeout_set);
47
48
/*
49
* These {i,o}bf_status functions return whether the buffers are full or not.
50
*/
51
52
static inline unsigned int ibf_status(unsigned int port)
53
{
54
return !!(inb(port) & 0x02);
55
}
56
57
static inline unsigned int obf_status(unsigned int port)
58
{
59
return inb(port) & 0x01;
60
}
61
62
#define wait_on_ibf(p, d) __wait_on_ibf(__LINE__, (p), (d))
63
static int __wait_on_ibf(unsigned int line, unsigned int port, int desired)
64
{
65
unsigned int timeo;
66
int state = ibf_status(port);
67
68
for (timeo = ec_timeout; state != desired && timeo; timeo--) {
69
mdelay(1);
70
state = ibf_status(port);
71
}
72
73
if ((state == desired) && (ec_timeout > EC_BASE_TIMEOUT) &&
74
timeo < (ec_timeout - EC_BASE_TIMEOUT)) {
75
printk(KERN_WARNING "olpc-ec: %d: waited %u ms for IBF!\n",
76
line, ec_timeout - timeo);
77
}
78
79
return !(state == desired);
80
}
81
82
#define wait_on_obf(p, d) __wait_on_obf(__LINE__, (p), (d))
83
static int __wait_on_obf(unsigned int line, unsigned int port, int desired)
84
{
85
unsigned int timeo;
86
int state = obf_status(port);
87
88
for (timeo = ec_timeout; state != desired && timeo; timeo--) {
89
mdelay(1);
90
state = obf_status(port);
91
}
92
93
if ((state == desired) && (ec_timeout > EC_BASE_TIMEOUT) &&
94
timeo < (ec_timeout - EC_BASE_TIMEOUT)) {
95
printk(KERN_WARNING "olpc-ec: %d: waited %u ms for OBF!\n",
96
line, ec_timeout - timeo);
97
}
98
99
return !(state == desired);
100
}
101
102
/*
103
* This allows the kernel to run Embedded Controller commands. The EC is
104
* documented at <http://wiki.laptop.org/go/Embedded_controller>, and the
105
* available EC commands are here:
106
* <http://wiki.laptop.org/go/Ec_specification>. Unfortunately, while
107
* OpenFirmware's source is available, the EC's is not.
108
*/
109
static int olpc_xo1_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf,
110
size_t outlen, void *arg)
111
{
112
int ret = -EIO;
113
int i;
114
int restarts = 0;
115
116
/* Clear OBF */
117
for (i = 0; i < 10 && (obf_status(0x6c) == 1); i++)
118
inb(0x68);
119
if (i == 10) {
120
printk(KERN_ERR "olpc-ec: timeout while attempting to "
121
"clear OBF flag!\n");
122
goto err;
123
}
124
125
if (wait_on_ibf(0x6c, 0)) {
126
printk(KERN_ERR "olpc-ec: timeout waiting for EC to "
127
"quiesce!\n");
128
goto err;
129
}
130
131
restart:
132
/*
133
* Note that if we time out during any IBF checks, that's a failure;
134
* we have to return. There's no way for the kernel to clear that.
135
*
136
* If we time out during an OBF check, we can restart the command;
137
* reissuing it will clear the OBF flag, and we should be alright.
138
* The OBF flag will sometimes misbehave due to what we believe
139
* is a hardware quirk..
140
*/
141
pr_devel("olpc-ec: running cmd 0x%x\n", cmd);
142
outb(cmd, 0x6c);
143
144
if (wait_on_ibf(0x6c, 0)) {
145
printk(KERN_ERR "olpc-ec: timeout waiting for EC to read "
146
"command!\n");
147
goto err;
148
}
149
150
if (inbuf && inlen) {
151
/* write data to EC */
152
for (i = 0; i < inlen; i++) {
153
pr_devel("olpc-ec: sending cmd arg 0x%x\n", inbuf[i]);
154
outb(inbuf[i], 0x68);
155
if (wait_on_ibf(0x6c, 0)) {
156
printk(KERN_ERR "olpc-ec: timeout waiting for"
157
" EC accept data!\n");
158
goto err;
159
}
160
}
161
}
162
if (outbuf && outlen) {
163
/* read data from EC */
164
for (i = 0; i < outlen; i++) {
165
if (wait_on_obf(0x6c, 1)) {
166
printk(KERN_ERR "olpc-ec: timeout waiting for"
167
" EC to provide data!\n");
168
if (restarts++ < 10)
169
goto restart;
170
goto err;
171
}
172
outbuf[i] = inb(0x68);
173
pr_devel("olpc-ec: received 0x%x\n", outbuf[i]);
174
}
175
}
176
177
ret = 0;
178
err:
179
return ret;
180
}
181
182
static bool __init check_ofw_architecture(struct device_node *root)
183
{
184
const char *olpc_arch;
185
int propsize;
186
187
olpc_arch = of_get_property(root, "architecture", &propsize);
188
return propsize == 5 && strncmp("OLPC", olpc_arch, 5) == 0;
189
}
190
191
static u32 __init get_board_revision(struct device_node *root)
192
{
193
int propsize;
194
const __be32 *rev;
195
196
rev = of_get_property(root, "board-revision-int", &propsize);
197
if (propsize != 4)
198
return 0;
199
200
return be32_to_cpu(*rev);
201
}
202
203
static bool __init platform_detect(void)
204
{
205
struct device_node *root = of_find_node_by_path("/");
206
bool success;
207
208
if (!root)
209
return false;
210
211
success = check_ofw_architecture(root);
212
if (success) {
213
olpc_platform_info.boardrev = get_board_revision(root);
214
olpc_platform_info.flags |= OLPC_F_PRESENT;
215
216
pr_info("OLPC board revision %s%X\n",
217
((olpc_platform_info.boardrev & 0xf) < 8) ? "pre" : "",
218
olpc_platform_info.boardrev >> 4);
219
}
220
221
of_node_put(root);
222
return success;
223
}
224
225
static int __init add_xo1_platform_devices(void)
226
{
227
struct platform_device *pdev;
228
229
pdev = platform_device_register_simple("xo1-rfkill", -1, NULL, 0);
230
if (IS_ERR(pdev))
231
return PTR_ERR(pdev);
232
233
pdev = platform_device_register_simple("olpc-xo1", -1, NULL, 0);
234
235
return PTR_ERR_OR_ZERO(pdev);
236
}
237
238
static int olpc_xo1_ec_suspend(struct platform_device *pdev)
239
{
240
/*
241
* Squelch SCIs while suspended. This is a fix for
242
* <http://dev.laptop.org/ticket/1835>.
243
*/
244
return olpc_ec_cmd(EC_SET_SCI_INHIBIT, NULL, 0, NULL, 0);
245
}
246
247
static int olpc_xo1_ec_resume(struct platform_device *pdev)
248
{
249
/* Tell the EC to stop inhibiting SCIs */
250
olpc_ec_cmd(EC_SET_SCI_INHIBIT_RELEASE, NULL, 0, NULL, 0);
251
252
/*
253
* Tell the wireless module to restart USB communication.
254
* Must be done twice.
255
*/
256
olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
257
olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
258
259
return 0;
260
}
261
262
static struct olpc_ec_driver ec_xo1_driver = {
263
.suspend = olpc_xo1_ec_suspend,
264
.resume = olpc_xo1_ec_resume,
265
.ec_cmd = olpc_xo1_ec_cmd,
266
#ifdef CONFIG_OLPC_XO1_SCI
267
/*
268
* XO-1 EC wakeups are available when olpc-xo1-sci driver is
269
* compiled in
270
*/
271
.wakeup_available = true,
272
#endif
273
};
274
275
static struct olpc_ec_driver ec_xo1_5_driver = {
276
.ec_cmd = olpc_xo1_ec_cmd,
277
#ifdef CONFIG_OLPC_XO15_SCI
278
/*
279
* XO-1.5 EC wakeups are available when olpc-xo15-sci driver is
280
* compiled in
281
*/
282
.wakeup_available = true,
283
#endif
284
};
285
286
static int __init olpc_init(void)
287
{
288
int r = 0;
289
290
if (!olpc_ofw_present() || !platform_detect())
291
return 0;
292
293
/* register the XO-1 and 1.5-specific EC handler */
294
if (olpc_platform_info.boardrev < olpc_board_pre(0xd0)) /* XO-1 */
295
olpc_ec_driver_register(&ec_xo1_driver, NULL);
296
else
297
olpc_ec_driver_register(&ec_xo1_5_driver, NULL);
298
platform_device_register_simple("olpc-ec", -1, NULL, 0);
299
300
/* assume B1 and above models always have a DCON */
301
if (olpc_board_at_least(olpc_board(0xb1)))
302
olpc_platform_info.flags |= OLPC_F_DCON;
303
304
#ifdef CONFIG_PCI_OLPC
305
/* If the VSA exists let it emulate PCI, if not emulate in kernel.
306
* XO-1 only. */
307
if (olpc_platform_info.boardrev < olpc_board_pre(0xd0) &&
308
!cs5535_has_vsa2())
309
x86_init.pci.arch_init = pci_olpc_init;
310
#endif
311
312
if (olpc_platform_info.boardrev < olpc_board_pre(0xd0)) { /* XO-1 */
313
r = add_xo1_platform_devices();
314
if (r)
315
return r;
316
}
317
318
return 0;
319
}
320
321
postcore_initcall(olpc_init);
322
323