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