linux/arch/x86/platform/olpc/olpc.c
<<
>>
Prefs
   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 <dilinger@debian.org>
   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
  26struct olpc_platform_t olpc_platform_info;
  27EXPORT_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 */
  33static int ec_timeout = EC_BASE_TIMEOUT;
  34
  35static 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
  52static inline unsigned int ibf_status(unsigned int port)
  53{
  54        return !!(inb(port) & 0x02);
  55}
  56
  57static 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))
  63static 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))
  83static 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 */
 109static 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
 131restart:
 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;
 178err:
 179        return ret;
 180}
 181
 182static 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
 191static 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
 203static 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
 225static 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
 238static 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
 247static 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
 262static 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
 275static struct olpc_ec_driver ec_xo1_5_driver = {
 276        .ec_cmd = olpc_xo1_ec_cmd,
 277#ifdef CONFIG_OLPC_XO1_5_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
 286static 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
 321postcore_initcall(olpc_init);
 322