[funini.com] -> [kei@sodan] -> Kernel Reading

root/arch/x86/kernel/olpc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. olpc_ec_timeout_set
  2. ibf_status
  3. obf_status
  4. __wait_on_ibf
  5. __wait_on_obf
  6. olpc_ec_cmd
  7. platform_detect
  8. platform_detect
  9. olpc_init

/*
 * Support for the OLPC DCON and OLPC EC access
 *
 * Copyright © 2006  Advanced Micro Devices, Inc.
 * Copyright © 2007-2008  Andres Salomon <dilinger@debian.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/string.h>
#include <asm/geode.h>
#include <asm/olpc.h>

#ifdef CONFIG_OPEN_FIRMWARE
#include <asm/ofw.h>
#endif

struct olpc_platform_t olpc_platform_info;
EXPORT_SYMBOL_GPL(olpc_platform_info);

static DEFINE_SPINLOCK(ec_lock);

/* what the timeout *should* be (in ms) */
#define EC_BASE_TIMEOUT 20

/* the timeout that bugs in the EC might force us to actually use */
static int ec_timeout = EC_BASE_TIMEOUT;

static int __init olpc_ec_timeout_set(char *str)
{
        if (get_option(&str, &ec_timeout) != 1) {
                ec_timeout = EC_BASE_TIMEOUT;
                printk(KERN_ERR "olpc-ec:  invalid argument to "
                                "'olpc_ec_timeout=', ignoring!\n");
        }
        printk(KERN_DEBUG "olpc-ec:  using %d ms delay for EC commands.\n",
                        ec_timeout);
        return 1;
}
__setup("olpc_ec_timeout=", olpc_ec_timeout_set);

/*
 * These {i,o}bf_status functions return whether the buffers are full or not.
 */

static inline unsigned int ibf_status(unsigned int port)
{
        return !!(inb(port) & 0x02);
}

static inline unsigned int obf_status(unsigned int port)
{
        return inb(port) & 0x01;
}

#define wait_on_ibf(p, d) __wait_on_ibf(__LINE__, (p), (d))
static int __wait_on_ibf(unsigned int line, unsigned int port, int desired)
{
        unsigned int timeo;
        int state = ibf_status(port);

        for (timeo = ec_timeout; state != desired && timeo; timeo--) {
                mdelay(1);
                state = ibf_status(port);
        }

        if ((state == desired) && (ec_timeout > EC_BASE_TIMEOUT) &&
                        timeo < (ec_timeout - EC_BASE_TIMEOUT)) {
                printk(KERN_WARNING "olpc-ec:  %d: waited %u ms for IBF!\n",
                                line, ec_timeout - timeo);
        }

        return !(state == desired);
}

#define wait_on_obf(p, d) __wait_on_obf(__LINE__, (p), (d))
static int __wait_on_obf(unsigned int line, unsigned int port, int desired)
{
        unsigned int timeo;
        int state = obf_status(port);

        for (timeo = ec_timeout; state != desired && timeo; timeo--) {
                mdelay(1);
                state = obf_status(port);
        }

        if ((state == desired) && (ec_timeout > EC_BASE_TIMEOUT) &&
                        timeo < (ec_timeout - EC_BASE_TIMEOUT)) {
                printk(KERN_WARNING "olpc-ec:  %d: waited %u ms for OBF!\n",
                                line, ec_timeout - timeo);
        }

        return !(state == desired);
}

/*
 * This allows the kernel to run Embedded Controller commands.  The EC is
 * documented at <http://wiki.laptop.org/go/Embedded_controller>, and the
 * available EC commands are here:
 * <http://wiki.laptop.org/go/Ec_specification>.  Unfortunately, while
 * OpenFirmware's source is available, the EC's is not.
 */
int olpc_ec_cmd(unsigned char cmd, unsigned char *inbuf, size_t inlen,
                unsigned char *outbuf,  size_t outlen)
{
        unsigned long flags;
        int ret = -EIO;
        int i;

        spin_lock_irqsave(&ec_lock, flags);

        /* Clear OBF */
        for (i = 0; i < 10 && (obf_status(0x6c) == 1); i++)
                inb(0x68);
        if (i == 10) {
                printk(KERN_ERR "olpc-ec:  timeout while attempting to "
                                "clear OBF flag!\n");
                goto err;
        }

        if (wait_on_ibf(0x6c, 0)) {
                printk(KERN_ERR "olpc-ec:  timeout waiting for EC to "
                                "quiesce!\n");
                goto err;
        }

restart:
        /*
         * Note that if we time out during any IBF checks, that's a failure;
         * we have to return.  There's no way for the kernel to clear that.
         *
         * If we time out during an OBF check, we can restart the command;
         * reissuing it will clear the OBF flag, and we should be alright.
         * The OBF flag will sometimes misbehave due to what we believe
         * is a hardware quirk..
         */
        printk(KERN_DEBUG "olpc-ec:  running cmd 0x%x\n", cmd);
        outb(cmd, 0x6c);

        if (wait_on_ibf(0x6c, 0)) {
                printk(KERN_ERR "olpc-ec:  timeout waiting for EC to read "
                                "command!\n");
                goto err;
        }

        if (inbuf && inlen) {
                /* write data to EC */
                for (i = 0; i < inlen; i++) {
                        if (wait_on_ibf(0x6c, 0)) {
                                printk(KERN_ERR "olpc-ec:  timeout waiting for"
                                                " EC accept data!\n");
                                goto err;
                        }
                        printk(KERN_DEBUG "olpc-ec:  sending cmd arg 0x%x\n",
                                        inbuf[i]);
                        outb(inbuf[i], 0x68);
                }
        }
        if (outbuf && outlen) {
                /* read data from EC */
                for (i = 0; i < outlen; i++) {
                        if (wait_on_obf(0x6c, 1)) {
                                printk(KERN_ERR "olpc-ec:  timeout waiting for"
                                                " EC to provide data!\n");
                                goto restart;
                        }
                        outbuf[i] = inb(0x68);
                        printk(KERN_DEBUG "olpc-ec:  received 0x%x\n",
                                        outbuf[i]);
                }
        }

        ret = 0;
err:
        spin_unlock_irqrestore(&ec_lock, flags);
        return ret;
}
EXPORT_SYMBOL_GPL(olpc_ec_cmd);

#ifdef CONFIG_OPEN_FIRMWARE
static void __init platform_detect(void)
{
        size_t propsize;
        __be32 rev;

        if (ofw("getprop", 4, 1, NULL, "board-revision-int", &rev, 4,
                        &propsize) || propsize != 4) {
                printk(KERN_ERR "ofw: getprop call failed!\n");
                rev = cpu_to_be32(0);
        }
        olpc_platform_info.boardrev = be32_to_cpu(rev);
}
#else
static void __init platform_detect(void)
{
        /* stopgap until OFW support is added to the kernel */
        olpc_platform_info.boardrev = 0xc2;
}
#endif

static int __init olpc_init(void)
{
        unsigned char *romsig;

        /* The ioremap check is dangerous; limit what we run it on */
        if (!is_geode() || geode_has_vsa2())
                return 0;

        spin_lock_init(&ec_lock);

        romsig = ioremap(0xffffffc0, 16);
        if (!romsig)
                return 0;

        if (strncmp(romsig, "CL1   Q", 7))
                goto unmap;
        if (strncmp(romsig+6, romsig+13, 3)) {
                printk(KERN_INFO "OLPC BIOS signature looks invalid.  "
                                "Assuming not OLPC\n");
                goto unmap;
        }

        printk(KERN_INFO "OLPC board with OpenFirmware %.16s\n", romsig);
        olpc_platform_info.flags |= OLPC_F_PRESENT;

        /* get the platform revision */
        platform_detect();

        /* assume B1 and above models always have a DCON */
        if (olpc_board_at_least(olpc_board(0xb1)))
                olpc_platform_info.flags |= OLPC_F_DCON;

        /* get the EC revision */
        olpc_ec_cmd(EC_FIRMWARE_REV, NULL, 0,
                        (unsigned char *) &olpc_platform_info.ecver, 1);

        /* check to see if the VSA exists */
        if (geode_has_vsa2())
                olpc_platform_info.flags |= OLPC_F_VSA;

        printk(KERN_INFO "OLPC board revision %s%X (EC=%x)\n",
                        ((olpc_platform_info.boardrev & 0xf) < 8) ? "pre" : "",
                        olpc_platform_info.boardrev >> 4,
                        olpc_platform_info.ecver);

unmap:
        iounmap(romsig);
        return 0;
}

postcore_initcall(olpc_init);

/* [<][>][^][v][top][bottom][index][help] */

[funini.com] -> [kei@sodan] -> Kernel Reading