// 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);