root/arch/mips/loongson2ef/lemote-2f/ec_kb3310b.c
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Basic KB3310B Embedded Controller support for the YeeLoong 2F netbook
 *
 *  Copyright (C) 2008 Lemote Inc.
 *  Author: liujl <liujl@lemote.com>, 2008-04-20
 */

#include <linux/io.h>
#include <linux/export.h>
#include <linux/spinlock.h>
#include <linux/delay.h>

#include "ec_kb3310b.h"

static DEFINE_SPINLOCK(index_access_lock);
static DEFINE_SPINLOCK(port_access_lock);

unsigned char ec_read(unsigned short addr)
{
        unsigned char value;
        unsigned long flags;

        spin_lock_irqsave(&index_access_lock, flags);
        outb((addr & 0xff00) >> 8, EC_IO_PORT_HIGH);
        outb((addr & 0x00ff), EC_IO_PORT_LOW);
        value = inb(EC_IO_PORT_DATA);
        spin_unlock_irqrestore(&index_access_lock, flags);

        return value;
}
EXPORT_SYMBOL_GPL(ec_read);

void ec_write(unsigned short addr, unsigned char val)
{
        unsigned long flags;

        spin_lock_irqsave(&index_access_lock, flags);
        outb((addr & 0xff00) >> 8, EC_IO_PORT_HIGH);
        outb((addr & 0x00ff), EC_IO_PORT_LOW);
        outb(val, EC_IO_PORT_DATA);
        /*  flush the write action */
        inb(EC_IO_PORT_DATA);
        spin_unlock_irqrestore(&index_access_lock, flags);
}
EXPORT_SYMBOL_GPL(ec_write);

/*
 * This function is used for EC command writes and corresponding status queries.
 */
int ec_query_seq(unsigned char cmd)
{
        int timeout;
        unsigned char status;
        unsigned long flags;
        int ret = 0;

        spin_lock_irqsave(&port_access_lock, flags);

        /* make chip goto reset mode */
        udelay(EC_REG_DELAY);
        outb(cmd, EC_CMD_PORT);
        udelay(EC_REG_DELAY);

        /* check if the command is received by ec */
        timeout = EC_CMD_TIMEOUT;
        status = inb(EC_STS_PORT);
        while (timeout-- && (status & (1 << 1))) {
                status = inb(EC_STS_PORT);
                udelay(EC_REG_DELAY);
        }

        spin_unlock_irqrestore(&port_access_lock, flags);

        if (timeout <= 0) {
                printk(KERN_ERR "%s: deadable error : timeout...\n", __func__);
                ret = -EINVAL;
        } else
                printk(KERN_INFO
                           "(%x/%d)ec issued command %d status : 0x%x\n",
                           timeout, EC_CMD_TIMEOUT - timeout, cmd, status);

        return ret;
}
EXPORT_SYMBOL_GPL(ec_query_seq);

/*
 * Send query command to EC to get the proper event number
 */
int ec_query_event_num(void)
{
        return ec_query_seq(CMD_GET_EVENT_NUM);
}
EXPORT_SYMBOL(ec_query_event_num);

/*
 * Get event number from EC
 *
 * NOTE: This routine must follow the query_event_num function in the
 * interrupt.
 */
int ec_get_event_num(void)
{
        int timeout = 100;
        unsigned char value;
        unsigned char status;

        udelay(EC_REG_DELAY);
        status = inb(EC_STS_PORT);
        udelay(EC_REG_DELAY);
        while (timeout-- && !(status & (1 << 0))) {
                status = inb(EC_STS_PORT);
                udelay(EC_REG_DELAY);
        }
        if (timeout <= 0) {
                pr_info("%s: get event number timeout.\n", __func__);

                return -EINVAL;
        }
        value = inb(EC_DAT_PORT);
        udelay(EC_REG_DELAY);

        return value;
}
EXPORT_SYMBOL(ec_get_event_num);