linux/arch/x86/platform/olpc/olpc.c
<<
>>
Prefs
   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/export.h>
  16#include <linux/delay.h>
  17#include <linux/io.h>
  18#include <linux/string.h>
  19#include <linux/platform_device.h>
  20#include <linux/of.h>
  21#include <linux/syscore_ops.h>
  22#include <linux/mutex.h>
  23#include <linux/olpc-ec.h>
  24
  25#include <asm/geode.h>
  26#include <asm/setup.h>
  27#include <asm/olpc.h>
  28#include <asm/olpc_ofw.h>
  29
  30struct olpc_platform_t olpc_platform_info;
  31EXPORT_SYMBOL_GPL(olpc_platform_info);
  32
  33/* EC event mask to be applied during suspend (defining wakeup sources). */
  34static u16 ec_wakeup_mask;
  35
  36/* what the timeout *should* be (in ms) */
  37#define EC_BASE_TIMEOUT 20
  38
  39/* the timeout that bugs in the EC might force us to actually use */
  40static int ec_timeout = EC_BASE_TIMEOUT;
  41
  42static int __init olpc_ec_timeout_set(char *str)
  43{
  44        if (get_option(&str, &ec_timeout) != 1) {
  45                ec_timeout = EC_BASE_TIMEOUT;
  46                printk(KERN_ERR "olpc-ec:  invalid argument to "
  47                                "'olpc_ec_timeout=', ignoring!\n");
  48        }
  49        printk(KERN_DEBUG "olpc-ec:  using %d ms delay for EC commands.\n",
  50                        ec_timeout);
  51        return 1;
  52}
  53__setup("olpc_ec_timeout=", olpc_ec_timeout_set);
  54
  55/*
  56 * These {i,o}bf_status functions return whether the buffers are full or not.
  57 */
  58
  59static inline unsigned int ibf_status(unsigned int port)
  60{
  61        return !!(inb(port) & 0x02);
  62}
  63
  64static inline unsigned int obf_status(unsigned int port)
  65{
  66        return inb(port) & 0x01;
  67}
  68
  69#define wait_on_ibf(p, d) __wait_on_ibf(__LINE__, (p), (d))
  70static int __wait_on_ibf(unsigned int line, unsigned int port, int desired)
  71{
  72        unsigned int timeo;
  73        int state = ibf_status(port);
  74
  75        for (timeo = ec_timeout; state != desired && timeo; timeo--) {
  76                mdelay(1);
  77                state = ibf_status(port);
  78        }
  79
  80        if ((state == desired) && (ec_timeout > EC_BASE_TIMEOUT) &&
  81                        timeo < (ec_timeout - EC_BASE_TIMEOUT)) {
  82                printk(KERN_WARNING "olpc-ec:  %d: waited %u ms for IBF!\n",
  83                                line, ec_timeout - timeo);
  84        }
  85
  86        return !(state == desired);
  87}
  88
  89#define wait_on_obf(p, d) __wait_on_obf(__LINE__, (p), (d))
  90static int __wait_on_obf(unsigned int line, unsigned int port, int desired)
  91{
  92        unsigned int timeo;
  93        int state = obf_status(port);
  94
  95        for (timeo = ec_timeout; state != desired && timeo; timeo--) {
  96                mdelay(1);
  97                state = obf_status(port);
  98        }
  99
 100        if ((state == desired) && (ec_timeout > EC_BASE_TIMEOUT) &&
 101                        timeo < (ec_timeout - EC_BASE_TIMEOUT)) {
 102                printk(KERN_WARNING "olpc-ec:  %d: waited %u ms for OBF!\n",
 103                                line, ec_timeout - timeo);
 104        }
 105
 106        return !(state == desired);
 107}
 108
 109/*
 110 * This allows the kernel to run Embedded Controller commands.  The EC is
 111 * documented at <http://wiki.laptop.org/go/Embedded_controller>, and the
 112 * available EC commands are here:
 113 * <http://wiki.laptop.org/go/Ec_specification>.  Unfortunately, while
 114 * OpenFirmware's source is available, the EC's is not.
 115 */
 116static int olpc_xo1_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf,
 117                size_t outlen, void *arg)
 118{
 119        int ret = -EIO;
 120        int i;
 121        int restarts = 0;
 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
 138restart:
 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                        pr_devel("olpc-ec:  sending cmd arg 0x%x\n", inbuf[i]);
 161                        outb(inbuf[i], 0x68);
 162                        if (wait_on_ibf(0x6c, 0)) {
 163                                printk(KERN_ERR "olpc-ec:  timeout waiting for"
 164                                                " EC accept data!\n");
 165                                goto err;
 166                        }
 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;
 185err:
 186        return ret;
 187}
 188
 189void olpc_ec_wakeup_set(u16 value)
 190{
 191        ec_wakeup_mask |= value;
 192}
 193EXPORT_SYMBOL_GPL(olpc_ec_wakeup_set);
 194
 195void olpc_ec_wakeup_clear(u16 value)
 196{
 197        ec_wakeup_mask &= ~value;
 198}
 199EXPORT_SYMBOL_GPL(olpc_ec_wakeup_clear);
 200
 201/*
 202 * Returns true if the compile and runtime configurations allow for EC events
 203 * to wake the system.
 204 */
 205bool olpc_ec_wakeup_available(void)
 206{
 207        if (!machine_is_olpc())
 208                return false;
 209
 210        /*
 211         * XO-1 EC wakeups are available when olpc-xo1-sci driver is
 212         * compiled in
 213         */
 214#ifdef CONFIG_OLPC_XO1_SCI
 215        if (olpc_platform_info.boardrev < olpc_board_pre(0xd0)) /* XO-1 */
 216                return true;
 217#endif
 218
 219        /*
 220         * XO-1.5 EC wakeups are available when olpc-xo15-sci driver is
 221         * compiled in
 222         */
 223#ifdef CONFIG_OLPC_XO15_SCI
 224        if (olpc_platform_info.boardrev >= olpc_board_pre(0xd0)) /* XO-1.5 */
 225                return true;
 226#endif
 227
 228        return false;
 229}
 230EXPORT_SYMBOL_GPL(olpc_ec_wakeup_available);
 231
 232int olpc_ec_mask_write(u16 bits)
 233{
 234        if (olpc_platform_info.flags & OLPC_F_EC_WIDE_SCI) {
 235                __be16 ec_word = cpu_to_be16(bits);
 236                return olpc_ec_cmd(EC_WRITE_EXT_SCI_MASK, (void *) &ec_word, 2,
 237                                   NULL, 0);
 238        } else {
 239                unsigned char ec_byte = bits & 0xff;
 240                return olpc_ec_cmd(EC_WRITE_SCI_MASK, &ec_byte, 1, NULL, 0);
 241        }
 242}
 243EXPORT_SYMBOL_GPL(olpc_ec_mask_write);
 244
 245int olpc_ec_sci_query(u16 *sci_value)
 246{
 247        int ret;
 248
 249        if (olpc_platform_info.flags & OLPC_F_EC_WIDE_SCI) {
 250                __be16 ec_word;
 251                ret = olpc_ec_cmd(EC_EXT_SCI_QUERY,
 252                        NULL, 0, (void *) &ec_word, 2);
 253                if (ret == 0)
 254                        *sci_value = be16_to_cpu(ec_word);
 255        } else {
 256                unsigned char ec_byte;
 257                ret = olpc_ec_cmd(EC_SCI_QUERY, NULL, 0, &ec_byte, 1);
 258                if (ret == 0)
 259                        *sci_value = ec_byte;
 260        }
 261
 262        return ret;
 263}
 264EXPORT_SYMBOL_GPL(olpc_ec_sci_query);
 265
 266static bool __init check_ofw_architecture(struct device_node *root)
 267{
 268        const char *olpc_arch;
 269        int propsize;
 270
 271        olpc_arch = of_get_property(root, "architecture", &propsize);
 272        return propsize == 5 && strncmp("OLPC", olpc_arch, 5) == 0;
 273}
 274
 275static u32 __init get_board_revision(struct device_node *root)
 276{
 277        int propsize;
 278        const __be32 *rev;
 279
 280        rev = of_get_property(root, "board-revision-int", &propsize);
 281        if (propsize != 4)
 282                return 0;
 283
 284        return be32_to_cpu(*rev);
 285}
 286
 287static bool __init platform_detect(void)
 288{
 289        struct device_node *root = of_find_node_by_path("/");
 290        bool success;
 291
 292        if (!root)
 293                return false;
 294
 295        success = check_ofw_architecture(root);
 296        if (success) {
 297                olpc_platform_info.boardrev = get_board_revision(root);
 298                olpc_platform_info.flags |= OLPC_F_PRESENT;
 299        }
 300
 301        of_node_put(root);
 302        return success;
 303}
 304
 305static int __init add_xo1_platform_devices(void)
 306{
 307        struct platform_device *pdev;
 308
 309        pdev = platform_device_register_simple("xo1-rfkill", -1, NULL, 0);
 310        if (IS_ERR(pdev))
 311                return PTR_ERR(pdev);
 312
 313        pdev = platform_device_register_simple("olpc-xo1", -1, NULL, 0);
 314        if (IS_ERR(pdev))
 315                return PTR_ERR(pdev);
 316
 317        return 0;
 318}
 319
 320static int olpc_xo1_ec_probe(struct platform_device *pdev)
 321{
 322        /* get the EC revision */
 323        olpc_ec_cmd(EC_FIRMWARE_REV, NULL, 0,
 324                        (unsigned char *) &olpc_platform_info.ecver, 1);
 325
 326        /* EC version 0x5f adds support for wide SCI mask */
 327        if (olpc_platform_info.ecver >= 0x5f)
 328                olpc_platform_info.flags |= OLPC_F_EC_WIDE_SCI;
 329
 330        pr_info("OLPC board revision %s%X (EC=%x)\n",
 331                        ((olpc_platform_info.boardrev & 0xf) < 8) ? "pre" : "",
 332                        olpc_platform_info.boardrev >> 4,
 333                        olpc_platform_info.ecver);
 334
 335        return 0;
 336}
 337static int olpc_xo1_ec_suspend(struct platform_device *pdev)
 338{
 339        olpc_ec_mask_write(ec_wakeup_mask);
 340
 341        /*
 342         * Squelch SCIs while suspended.  This is a fix for
 343         * <http://dev.laptop.org/ticket/1835>.
 344         */
 345        return olpc_ec_cmd(EC_SET_SCI_INHIBIT, NULL, 0, NULL, 0);
 346}
 347
 348static int olpc_xo1_ec_resume(struct platform_device *pdev)
 349{
 350        /* Tell the EC to stop inhibiting SCIs */
 351        olpc_ec_cmd(EC_SET_SCI_INHIBIT_RELEASE, NULL, 0, NULL, 0);
 352
 353        /*
 354         * Tell the wireless module to restart USB communication.
 355         * Must be done twice.
 356         */
 357        olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
 358        olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
 359
 360        return 0;
 361}
 362
 363static struct olpc_ec_driver ec_xo1_driver = {
 364        .probe = olpc_xo1_ec_probe,
 365        .suspend = olpc_xo1_ec_suspend,
 366        .resume = olpc_xo1_ec_resume,
 367        .ec_cmd = olpc_xo1_ec_cmd,
 368};
 369
 370static struct olpc_ec_driver ec_xo1_5_driver = {
 371        .probe = olpc_xo1_ec_probe,
 372        .ec_cmd = olpc_xo1_ec_cmd,
 373};
 374
 375static int __init olpc_init(void)
 376{
 377        int r = 0;
 378
 379        if (!olpc_ofw_present() || !platform_detect())
 380                return 0;
 381
 382        /* register the XO-1 and 1.5-specific EC handler */
 383        if (olpc_platform_info.boardrev < olpc_board_pre(0xd0)) /* XO-1 */
 384                olpc_ec_driver_register(&ec_xo1_driver, NULL);
 385        else
 386                olpc_ec_driver_register(&ec_xo1_5_driver, NULL);
 387        platform_device_register_simple("olpc-ec", -1, NULL, 0);
 388
 389        /* assume B1 and above models always have a DCON */
 390        if (olpc_board_at_least(olpc_board(0xb1)))
 391                olpc_platform_info.flags |= OLPC_F_DCON;
 392
 393#ifdef CONFIG_PCI_OLPC
 394        /* If the VSA exists let it emulate PCI, if not emulate in kernel.
 395         * XO-1 only. */
 396        if (olpc_platform_info.boardrev < olpc_board_pre(0xd0) &&
 397                        !cs5535_has_vsa2())
 398                x86_init.pci.arch_init = pci_olpc_init;
 399#endif
 400
 401        if (olpc_platform_info.boardrev < olpc_board_pre(0xd0)) { /* XO-1 */
 402                r = add_xo1_platform_devices();
 403                if (r)
 404                        return r;
 405        }
 406
 407        return 0;
 408}
 409
 410postcore_initcall(olpc_init);
 411