• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/arch/x86/kernel/
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
21#include <asm/geode.h>
22#include <asm/setup.h>
23#include <asm/olpc.h>
24#include <asm/olpc_ofw.h>
25
26struct olpc_platform_t olpc_platform_info;
27EXPORT_SYMBOL_GPL(olpc_platform_info);
28
29static DEFINE_SPINLOCK(ec_lock);
30
31/* what the timeout *should* be (in ms) */
32#define EC_BASE_TIMEOUT 20
33
34/* the timeout that bugs in the EC might force us to actually use */
35static int ec_timeout = EC_BASE_TIMEOUT;
36
37static int __init olpc_ec_timeout_set(char *str)
38{
39	if (get_option(&str, &ec_timeout) != 1) {
40		ec_timeout = EC_BASE_TIMEOUT;
41		printk(KERN_ERR "olpc-ec:  invalid argument to "
42				"'olpc_ec_timeout=', ignoring!\n");
43	}
44	printk(KERN_DEBUG "olpc-ec:  using %d ms delay for EC commands.\n",
45			ec_timeout);
46	return 1;
47}
48__setup("olpc_ec_timeout=", olpc_ec_timeout_set);
49
50/*
51 * These {i,o}bf_status functions return whether the buffers are full or not.
52 */
53
54static inline unsigned int ibf_status(unsigned int port)
55{
56	return !!(inb(port) & 0x02);
57}
58
59static inline unsigned int obf_status(unsigned int port)
60{
61	return inb(port) & 0x01;
62}
63
64#define wait_on_ibf(p, d) __wait_on_ibf(__LINE__, (p), (d))
65static int __wait_on_ibf(unsigned int line, unsigned int port, int desired)
66{
67	unsigned int timeo;
68	int state = ibf_status(port);
69
70	for (timeo = ec_timeout; state != desired && timeo; timeo--) {
71		mdelay(1);
72		state = ibf_status(port);
73	}
74
75	if ((state == desired) && (ec_timeout > EC_BASE_TIMEOUT) &&
76			timeo < (ec_timeout - EC_BASE_TIMEOUT)) {
77		printk(KERN_WARNING "olpc-ec:  %d: waited %u ms for IBF!\n",
78				line, ec_timeout - timeo);
79	}
80
81	return !(state == desired);
82}
83
84#define wait_on_obf(p, d) __wait_on_obf(__LINE__, (p), (d))
85static int __wait_on_obf(unsigned int line, unsigned int port, int desired)
86{
87	unsigned int timeo;
88	int state = obf_status(port);
89
90	for (timeo = ec_timeout; state != desired && timeo; timeo--) {
91		mdelay(1);
92		state = obf_status(port);
93	}
94
95	if ((state == desired) && (ec_timeout > EC_BASE_TIMEOUT) &&
96			timeo < (ec_timeout - EC_BASE_TIMEOUT)) {
97		printk(KERN_WARNING "olpc-ec:  %d: waited %u ms for OBF!\n",
98				line, ec_timeout - timeo);
99	}
100
101	return !(state == desired);
102}
103
104/*
105 * This allows the kernel to run Embedded Controller commands.  The EC is
106 * documented at <http://wiki.laptop.org/go/Embedded_controller>, and the
107 * available EC commands are here:
108 * <http://wiki.laptop.org/go/Ec_specification>.  Unfortunately, while
109 * OpenFirmware's source is available, the EC's is not.
110 */
111int olpc_ec_cmd(unsigned char cmd, unsigned char *inbuf, size_t inlen,
112		unsigned char *outbuf,  size_t outlen)
113{
114	unsigned long flags;
115	int ret = -EIO;
116	int i;
117	int restarts = 0;
118
119	spin_lock_irqsave(&ec_lock, flags);
120
121	/* Clear OBF */
122	for (i = 0; i < 10 && (obf_status(0x6c) == 1); i++)
123		inb(0x68);
124	if (i == 10) {
125		printk(KERN_ERR "olpc-ec:  timeout while attempting to "
126				"clear OBF flag!\n");
127		goto err;
128	}
129
130	if (wait_on_ibf(0x6c, 0)) {
131		printk(KERN_ERR "olpc-ec:  timeout waiting for EC to "
132				"quiesce!\n");
133		goto err;
134	}
135
136restart:
137	/*
138	 * Note that if we time out during any IBF checks, that's a failure;
139	 * we have to return.  There's no way for the kernel to clear that.
140	 *
141	 * If we time out during an OBF check, we can restart the command;
142	 * reissuing it will clear the OBF flag, and we should be alright.
143	 * The OBF flag will sometimes misbehave due to what we believe
144	 * is a hardware quirk..
145	 */
146	pr_devel("olpc-ec:  running cmd 0x%x\n", cmd);
147	outb(cmd, 0x6c);
148
149	if (wait_on_ibf(0x6c, 0)) {
150		printk(KERN_ERR "olpc-ec:  timeout waiting for EC to read "
151				"command!\n");
152		goto err;
153	}
154
155	if (inbuf && inlen) {
156		/* write data to EC */
157		for (i = 0; i < inlen; i++) {
158			if (wait_on_ibf(0x6c, 0)) {
159				printk(KERN_ERR "olpc-ec:  timeout waiting for"
160						" EC accept data!\n");
161				goto err;
162			}
163			pr_devel("olpc-ec:  sending cmd arg 0x%x\n", inbuf[i]);
164			outb(inbuf[i], 0x68);
165		}
166	}
167	if (outbuf && outlen) {
168		/* read data from EC */
169		for (i = 0; i < outlen; i++) {
170			if (wait_on_obf(0x6c, 1)) {
171				printk(KERN_ERR "olpc-ec:  timeout waiting for"
172						" EC to provide data!\n");
173				if (restarts++ < 10)
174					goto restart;
175				goto err;
176			}
177			outbuf[i] = inb(0x68);
178			pr_devel("olpc-ec:  received 0x%x\n", outbuf[i]);
179		}
180	}
181
182	ret = 0;
183err:
184	spin_unlock_irqrestore(&ec_lock, flags);
185	return ret;
186}
187EXPORT_SYMBOL_GPL(olpc_ec_cmd);
188
189#ifdef CONFIG_OLPC_OPENFIRMWARE
190static void __init platform_detect(void)
191{
192	size_t propsize;
193	__be32 rev;
194	const void *args[] = { NULL, "board-revision-int", &rev, (void *)4 };
195	void *res[] = { &propsize };
196
197	if (olpc_ofw("getprop", args, res) || propsize != 4) {
198		printk(KERN_ERR "ofw: getprop call failed!\n");
199		rev = cpu_to_be32(0);
200	}
201	olpc_platform_info.boardrev = be32_to_cpu(rev);
202}
203#else
204static void __init platform_detect(void)
205{
206	/* stopgap until OFW support is added to the kernel */
207	olpc_platform_info.boardrev = olpc_board(0xc2);
208}
209#endif
210
211static int __init olpc_init(void)
212{
213	unsigned char *romsig;
214
215	/* The ioremap check is dangerous; limit what we run it on */
216	if (!is_geode() || cs5535_has_vsa2())
217		return 0;
218
219	spin_lock_init(&ec_lock);
220
221	romsig = ioremap(0xffffffc0, 16);
222	if (!romsig)
223		return 0;
224
225	if (strncmp(romsig, "CL1   Q", 7))
226		goto unmap;
227	if (strncmp(romsig+6, romsig+13, 3)) {
228		printk(KERN_INFO "OLPC BIOS signature looks invalid.  "
229				"Assuming not OLPC\n");
230		goto unmap;
231	}
232
233	printk(KERN_INFO "OLPC board with OpenFirmware %.16s\n", romsig);
234	olpc_platform_info.flags |= OLPC_F_PRESENT;
235
236	/* get the platform revision */
237	platform_detect();
238
239	/* assume B1 and above models always have a DCON */
240	if (olpc_board_at_least(olpc_board(0xb1)))
241		olpc_platform_info.flags |= OLPC_F_DCON;
242
243	/* get the EC revision */
244	olpc_ec_cmd(EC_FIRMWARE_REV, NULL, 0,
245			(unsigned char *) &olpc_platform_info.ecver, 1);
246
247#ifdef CONFIG_PCI_OLPC
248	/* If the VSA exists let it emulate PCI, if not emulate in kernel */
249	if (!cs5535_has_vsa2())
250		x86_init.pci.arch_init = pci_olpc_init;
251#endif
252
253	printk(KERN_INFO "OLPC board revision %s%X (EC=%x)\n",
254			((olpc_platform_info.boardrev & 0xf) < 8) ? "pre" : "",
255			olpc_platform_info.boardrev >> 4,
256			olpc_platform_info.ecver);
257
258unmap:
259	iounmap(romsig);
260	return 0;
261}
262
263postcore_initcall(olpc_init);
264