Merge branch 'x86-platform-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[pandora-kernel.git] / arch / x86 / platform / olpc / olpc.c
1 /*
2  * Support for the OLPC DCON and OLPC EC access
3  *
4  * Copyright © 2006  Advanced Micro Devices, Inc.
5  * Copyright © 2007-2008  Andres Salomon <dilinger@debian.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  */
12
13 #include <linux/kernel.h>
14 #include <linux/init.h>
15 #include <linux/module.h>
16 #include <linux/delay.h>
17 #include <linux/spinlock.h>
18 #include <linux/io.h>
19 #include <linux/string.h>
20 #include <linux/platform_device.h>
21 #include <linux/of.h>
22
23 #include <asm/geode.h>
24 #include <asm/setup.h>
25 #include <asm/olpc.h>
26 #include <asm/olpc_ofw.h>
27
28 struct olpc_platform_t olpc_platform_info;
29 EXPORT_SYMBOL_GPL(olpc_platform_info);
30
31 static DEFINE_SPINLOCK(ec_lock);
32
33 /* what the timeout *should* be (in ms) */
34 #define EC_BASE_TIMEOUT 20
35
36 /* the timeout that bugs in the EC might force us to actually use */
37 static int ec_timeout = EC_BASE_TIMEOUT;
38
39 static int __init olpc_ec_timeout_set(char *str)
40 {
41         if (get_option(&str, &ec_timeout) != 1) {
42                 ec_timeout = EC_BASE_TIMEOUT;
43                 printk(KERN_ERR "olpc-ec:  invalid argument to "
44                                 "'olpc_ec_timeout=', ignoring!\n");
45         }
46         printk(KERN_DEBUG "olpc-ec:  using %d ms delay for EC commands.\n",
47                         ec_timeout);
48         return 1;
49 }
50 __setup("olpc_ec_timeout=", olpc_ec_timeout_set);
51
52 /*
53  * These {i,o}bf_status functions return whether the buffers are full or not.
54  */
55
56 static inline unsigned int ibf_status(unsigned int port)
57 {
58         return !!(inb(port) & 0x02);
59 }
60
61 static inline unsigned int obf_status(unsigned int port)
62 {
63         return inb(port) & 0x01;
64 }
65
66 #define wait_on_ibf(p, d) __wait_on_ibf(__LINE__, (p), (d))
67 static int __wait_on_ibf(unsigned int line, unsigned int port, int desired)
68 {
69         unsigned int timeo;
70         int state = ibf_status(port);
71
72         for (timeo = ec_timeout; state != desired && timeo; timeo--) {
73                 mdelay(1);
74                 state = ibf_status(port);
75         }
76
77         if ((state == desired) && (ec_timeout > EC_BASE_TIMEOUT) &&
78                         timeo < (ec_timeout - EC_BASE_TIMEOUT)) {
79                 printk(KERN_WARNING "olpc-ec:  %d: waited %u ms for IBF!\n",
80                                 line, ec_timeout - timeo);
81         }
82
83         return !(state == desired);
84 }
85
86 #define wait_on_obf(p, d) __wait_on_obf(__LINE__, (p), (d))
87 static int __wait_on_obf(unsigned int line, unsigned int port, int desired)
88 {
89         unsigned int timeo;
90         int state = obf_status(port);
91
92         for (timeo = ec_timeout; state != desired && timeo; timeo--) {
93                 mdelay(1);
94                 state = obf_status(port);
95         }
96
97         if ((state == desired) && (ec_timeout > EC_BASE_TIMEOUT) &&
98                         timeo < (ec_timeout - EC_BASE_TIMEOUT)) {
99                 printk(KERN_WARNING "olpc-ec:  %d: waited %u ms for OBF!\n",
100                                 line, ec_timeout - timeo);
101         }
102
103         return !(state == desired);
104 }
105
106 /*
107  * This allows the kernel to run Embedded Controller commands.  The EC is
108  * documented at <http://wiki.laptop.org/go/Embedded_controller>, and the
109  * available EC commands are here:
110  * <http://wiki.laptop.org/go/Ec_specification>.  Unfortunately, while
111  * OpenFirmware's source is available, the EC's is not.
112  */
113 int olpc_ec_cmd(unsigned char cmd, unsigned char *inbuf, size_t inlen,
114                 unsigned char *outbuf,  size_t outlen)
115 {
116         unsigned long flags;
117         int ret = -EIO;
118         int i;
119         int restarts = 0;
120
121         spin_lock_irqsave(&ec_lock, flags);
122
123         /* Clear OBF */
124         for (i = 0; i < 10 && (obf_status(0x6c) == 1); i++)
125                 inb(0x68);
126         if (i == 10) {
127                 printk(KERN_ERR "olpc-ec:  timeout while attempting to "
128                                 "clear OBF flag!\n");
129                 goto err;
130         }
131
132         if (wait_on_ibf(0x6c, 0)) {
133                 printk(KERN_ERR "olpc-ec:  timeout waiting for EC to "
134                                 "quiesce!\n");
135                 goto err;
136         }
137
138 restart:
139         /*
140          * Note that if we time out during any IBF checks, that's a failure;
141          * we have to return.  There's no way for the kernel to clear that.
142          *
143          * If we time out during an OBF check, we can restart the command;
144          * reissuing it will clear the OBF flag, and we should be alright.
145          * The OBF flag will sometimes misbehave due to what we believe
146          * is a hardware quirk..
147          */
148         pr_devel("olpc-ec:  running cmd 0x%x\n", cmd);
149         outb(cmd, 0x6c);
150
151         if (wait_on_ibf(0x6c, 0)) {
152                 printk(KERN_ERR "olpc-ec:  timeout waiting for EC to read "
153                                 "command!\n");
154                 goto err;
155         }
156
157         if (inbuf && inlen) {
158                 /* write data to EC */
159                 for (i = 0; i < inlen; i++) {
160                         if (wait_on_ibf(0x6c, 0)) {
161                                 printk(KERN_ERR "olpc-ec:  timeout waiting for"
162                                                 " EC accept data!\n");
163                                 goto err;
164                         }
165                         pr_devel("olpc-ec:  sending cmd arg 0x%x\n", inbuf[i]);
166                         outb(inbuf[i], 0x68);
167                 }
168         }
169         if (outbuf && outlen) {
170                 /* read data from EC */
171                 for (i = 0; i < outlen; i++) {
172                         if (wait_on_obf(0x6c, 1)) {
173                                 printk(KERN_ERR "olpc-ec:  timeout waiting for"
174                                                 " EC to provide data!\n");
175                                 if (restarts++ < 10)
176                                         goto restart;
177                                 goto err;
178                         }
179                         outbuf[i] = inb(0x68);
180                         pr_devel("olpc-ec:  received 0x%x\n", outbuf[i]);
181                 }
182         }
183
184         ret = 0;
185 err:
186         spin_unlock_irqrestore(&ec_lock, flags);
187         return ret;
188 }
189 EXPORT_SYMBOL_GPL(olpc_ec_cmd);
190
191 static bool __init check_ofw_architecture(struct device_node *root)
192 {
193         const char *olpc_arch;
194         int propsize;
195
196         olpc_arch = of_get_property(root, "architecture", &propsize);
197         return propsize == 5 && strncmp("OLPC", olpc_arch, 5) == 0;
198 }
199
200 static u32 __init get_board_revision(struct device_node *root)
201 {
202         int propsize;
203         const __be32 *rev;
204
205         rev = of_get_property(root, "board-revision-int", &propsize);
206         if (propsize != 4)
207                 return 0;
208
209         return be32_to_cpu(*rev);
210 }
211
212 static bool __init platform_detect(void)
213 {
214         struct device_node *root = of_find_node_by_path("/");
215         bool success;
216
217         if (!root)
218                 return false;
219
220         success = check_ofw_architecture(root);
221         if (success) {
222                 olpc_platform_info.boardrev = get_board_revision(root);
223                 olpc_platform_info.flags |= OLPC_F_PRESENT;
224         }
225
226         of_node_put(root);
227         return success;
228 }
229
230 static int __init add_xo1_platform_devices(void)
231 {
232         struct platform_device *pdev;
233
234         pdev = platform_device_register_simple("xo1-rfkill", -1, NULL, 0);
235         if (IS_ERR(pdev))
236                 return PTR_ERR(pdev);
237
238         pdev = platform_device_register_simple("olpc-xo1", -1, NULL, 0);
239         if (IS_ERR(pdev))
240                 return PTR_ERR(pdev);
241
242         return 0;
243 }
244
245 static int __init olpc_init(void)
246 {
247         int r = 0;
248
249         if (!olpc_ofw_present() || !platform_detect())
250                 return 0;
251
252         spin_lock_init(&ec_lock);
253
254         /* assume B1 and above models always have a DCON */
255         if (olpc_board_at_least(olpc_board(0xb1)))
256                 olpc_platform_info.flags |= OLPC_F_DCON;
257
258         /* get the EC revision */
259         olpc_ec_cmd(EC_FIRMWARE_REV, NULL, 0,
260                         (unsigned char *) &olpc_platform_info.ecver, 1);
261
262 #ifdef CONFIG_PCI_OLPC
263         /* If the VSA exists let it emulate PCI, if not emulate in kernel.
264          * XO-1 only. */
265         if (olpc_platform_info.boardrev < olpc_board_pre(0xd0) &&
266                         !cs5535_has_vsa2())
267                 x86_init.pci.arch_init = pci_olpc_init;
268 #endif
269
270         printk(KERN_INFO "OLPC board revision %s%X (EC=%x)\n",
271                         ((olpc_platform_info.boardrev & 0xf) < 8) ? "pre" : "",
272                         olpc_platform_info.boardrev >> 4,
273                         olpc_platform_info.ecver);
274
275         if (olpc_platform_info.boardrev < olpc_board_pre(0xd0)) { /* XO-1 */
276                 r = add_xo1_platform_devices();
277                 if (r)
278                         return r;
279         }
280
281         return 0;
282 }
283
284 postcore_initcall(olpc_init);