--- linux-2.6.10/drivers/acpi/ec.c	2004/12/31 10:28:34	1.1
+++ linux-2.6.10/drivers/acpi/ec.c	2004/12/31 15:53:07
@@ -137,7 +137,7 @@ acpi_ec_wait (
 }
 
 
-static int
+int
 acpi_ec_read (
 	struct acpi_ec		*ec,
 	u8			address,
@@ -187,9 +187,9 @@ end:
 
 	return_VALUE(result);
 }
+EXPORT_SYMBOL(acpi_ec_read);
 
-
-static int
+int
 acpi_ec_write (
 	struct acpi_ec		*ec,
 	u8			address,
@@ -239,6 +239,7 @@ end:
 
 	return_VALUE(result);
 }
+EXPORT_SYMBOL(acpi_ec_write);
 
 /*
  * Externally callable EC access functions. For now, assume 1 EC only
diff -ruNapx '*.orig' linux-2.6.11-rc1/drivers/i2c/Kconfig linux-2.6.11-rc1-smartbatt/drivers/i2c/Kconfig
--- linux-2.6.11-rc1/drivers/i2c/Kconfig	2004-12-25 05:34:26.000000000 +0800
+++ linux-2.6.11-rc1-smartbatt/drivers/i2c/Kconfig	2005-01-20 01:46:20.000000000 +0800
@@ -34,6 +34,16 @@ config I2C_CHARDEV
 	  This support is also available as a module.  If so, the module 
 	  will be called i2c-dev.
 
+config I2C_ACPI_EC
+	tristate "I2C ACPI Embedded Controller interface"
+	depends on I2C
+	help
+	  Say Y here to compile the ACPI Embedded Controller SMBus interface.
+	  This allow access to smart batteries.
+
+	  This support is also available as a module.  If so, the module 
+	  will be called i2c-acpi-ec.
+
 source drivers/i2c/algos/Kconfig
 source drivers/i2c/busses/Kconfig
 source drivers/i2c/chips/Kconfig
diff -ruNapx '*.orig' linux-2.6.11-rc1/drivers/i2c/Makefile linux-2.6.11-rc1-smartbatt/drivers/i2c/Makefile
--- linux-2.6.11-rc1/drivers/i2c/Makefile	2004-12-25 05:33:49.000000000 +0800
+++ linux-2.6.11-rc1-smartbatt/drivers/i2c/Makefile	2005-01-20 01:44:48.000000000 +0800
@@ -5,6 +5,7 @@
 obj-$(CONFIG_I2C)		+= i2c-core.o
 obj-$(CONFIG_I2C_CHARDEV)	+= i2c-dev.o
 obj-$(CONFIG_I2C_SENSOR)	+= i2c-sensor.o
+obj-$(CONFIG_I2C_ACPI_EC)	+= i2c-acpi-ec.o
 obj-y				+= busses/ chips/ algos/
 
 i2c-sensor-objs := i2c-sensor-detect.o i2c-sensor-vid.o
diff -ruNapx '*.orig' linux-2.6.11-rc1/drivers/i2c/i2c-acpi-ec.c linux-2.6.11-rc1-smartbatt/drivers/i2c/i2c-acpi-ec.c
--- linux-2.6.11-rc1/drivers/i2c/i2c-acpi-ec.c	1970-01-01 08:00:00.000000000 +0800
+++ linux-2.6.11-rc1-smartbatt/drivers/i2c/i2c-acpi-ec.c	2005-01-03 22:31:35.000000000 +0800
@@ -0,0 +1,412 @@
+/*
+ * SMBus driver for ACPI Embedded Controller.
+ *
+ * Copyright (c) 2002 Ducrot Bruno.
+ *
+ * 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 version 2.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/acpi.h>
+
+
+#define _COMPONENT		ACPI_EC_COMPONENT
+ACPI_MODULE_NAME		("acpi_smbus")
+
+#define ACPI_EC_HC_COMPONENT	0x00100000
+#define ACPI_EC_HC_CLASS	"ec_hc_smbus"
+#define ACPI_EC_HC_HID		"ACPI0001"
+#define ACPI_EC_HC_DRIVER_NAME	"ACPI EC HC smbus driver"
+#define ACPI_EC_HC_DEVICE_NAME	"EC HC smbus"
+
+/* TODO move this to an include file */
+struct acpi_ec {
+	acpi_handle			handle;
+	unsigned long			uid;
+	unsigned long			gpe_bit;
+	struct acpi_generic_address	status_addr;
+	struct acpi_generic_address	command_addr;
+	struct acpi_generic_address	data_addr;
+	unsigned long			global_lock;
+	spinlock_t			lock;
+};
+extern int acpi_ec_read(struct acpi_ec *ec, u8 address, u32 *data);
+extern int acpi_ec_write(struct acpi_ec *ec, u8 address, u8 data);
+
+
+
+
+static int acpi_ec_hc_add(struct acpi_device *device);
+static int acpi_ec_hc_remove(struct acpi_device *device, int type);
+
+static struct acpi_driver acpi_ec_hc_driver = {
+	.name =		ACPI_EC_HC_DRIVER_NAME,
+	.class =	ACPI_EC_HC_CLASS,
+	.ids =		ACPI_EC_HC_HID,
+	.ops =		{
+				.add =		acpi_ec_hc_add,
+				.remove =	acpi_ec_hc_remove,
+			},
+};
+
+struct acpi_ec_smbus {
+	struct i2c_adapter adapter;
+	struct acpi_ec  *ec;
+	int             base;
+	int		alert;
+};
+
+struct acpi_ec_hc {
+	acpi_handle			handle;
+	struct acpi_ec_smbus		*smbus;
+};
+
+/* Various bit mask for EC_SC (R) */
+#define OBF		0x01
+#define IBF		0x02
+#define CMD		0x08
+#define BURST		0x10
+#define SCI_EVT		0x20
+#define SMI_EVT		0x40
+
+/* Commands for EC_SC (W) */
+#define RD_EC		0x80
+#define WR_EC		0x81
+#define BE_EC		0x82
+#define BD_EC		0x83
+#define QR_EC		0x84
+
+/*
+ * ACPI 2.0 chapter 13 SMBus 2.0 EC register model
+ */
+
+#define ACPI_EC_SMB_PRTCL	0x00	/* protocol, PEC */
+#define ACPI_EC_SMB_STS		0x01	/* status */
+#define ACPI_EC_SMB_ADDR	0x02	/* address */
+#define ACPI_EC_SMB_CMD		0x03	/* command */
+#define ACPI_EC_SMB_DATA	0x04	/* 32 data registers */
+#define ACPI_EC_SMB_BCNT	0x24	/* number of data bytes */
+#define ACPI_EC_SMB_ALRM_A	0x25	/* alarm address */
+#define ACPI_EC_SMB_ALRM_D	0x26	/* 2 bytes alarm data */
+
+#define ACPI_EC_SMB_STS_DONE	0x80
+#define ACPI_EC_SMB_STS_ALRM	0x40
+#define ACPI_EC_SMB_STS_RES	0x20
+#define ACPI_EC_SMB_STS_STATUS	0x1f
+
+#define ACPI_EC_SMB_STATUS_OK		0x00
+#define ACPI_EC_SMB_STATUS_FAIL		0x07
+#define ACPI_EC_SMB_STATUS_DNAK		0x10
+#define ACPI_EC_SMB_STATUS_DERR		0x11
+#define ACPI_EC_SMB_STATUS_CMD_DENY	0x12
+#define ACPI_EC_SMB_STATUS_UNKNOWN	0x13
+#define ACPI_EC_SMB_STATUS_ACC_DENY	0x17
+#define ACPI_EC_SMB_STATUS_TIMEOUT	0x18
+#define ACPI_EC_SMB_STATUS_NOTSUP	0x19
+#define ACPI_EC_SMB_STATUS_BUSY	0x1A
+#define ACPI_EC_SMB_STATUS_PEC	0x1F
+
+#define ACPI_EC_SMB_PRTCL_WRITE			0x00
+#define ACPI_EC_SMB_PRTCL_READ			0x01
+#define ACPI_EC_SMB_PRTCL_QUICK			0x02
+#define ACPI_EC_SMB_PRTCL_BYTE			0x04
+#define ACPI_EC_SMB_PRTCL_BYTE_DATA		0x06
+#define ACPI_EC_SMB_PRTCL_WORD_DATA		0x08
+#define ACPI_EC_SMB_PRTCL_BLOCK_DATA		0x0a
+#define ACPI_EC_SMB_PRTCL_PROC_CALL		0x0c
+#define ACPI_EC_SMB_PRTCL_BLOCK_PROC_CALL	0x0d
+#define ACPI_EC_SMB_PRTCL_I2C_BLOCK_DATA	0x4a
+#define ACPI_EC_SMB_PRTCL_PEC			0x80
+
+#define __ec_read ec_read
+#define __ec_write ec_write
+
+static inline int 
+acpi_ec_smb_read(struct acpi_ec_smbus * smbus, u8 address, u8 * data)
+{
+	u32 val;
+	int err;
+
+	err = acpi_ec_read(smbus->ec, smbus->base + address, &val);
+	if (!err)
+		*data = val;
+	return err;
+}
+
+static inline int 
+acpi_ec_smb_write(struct acpi_ec_smbus * smbus, u8 address, u8 data)
+{
+	return (acpi_ec_write(smbus->ec, smbus->base + address, data));
+}
+
+s32 
+acpi_ec_smb_access(struct i2c_adapter * adap, u16 addr, unsigned short flags,
+	 char read_write, u8 command, int size, union i2c_smbus_data * data)
+{
+	struct acpi_ec_smbus *smbus = adap->algo_data;
+	unsigned char   protocol, len, pec, temp[2];
+	int             i;
+
+	protocol = (read_write == I2C_SMBUS_READ) ? ACPI_EC_SMB_PRTCL_READ : ACPI_EC_SMB_PRTCL_WRITE;
+	pec = (flags & I2C_CLIENT_PEC) ? ACPI_EC_SMB_PRTCL_PEC : 0;
+
+	switch (size) {
+
+	case I2C_SMBUS_QUICK:
+		protocol |= ACPI_EC_SMB_PRTCL_QUICK;
+		read_write = I2C_SMBUS_WRITE;
+		break;
+
+	case I2C_SMBUS_BYTE:
+		if (read_write == I2C_SMBUS_WRITE)
+			acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->byte);
+		protocol |= ACPI_EC_SMB_PRTCL_BYTE;
+		break;
+
+	case I2C_SMBUS_BYTE_DATA:
+		acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command);
+		if (read_write == I2C_SMBUS_WRITE)
+			acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->byte);
+		protocol |= ACPI_EC_SMB_PRTCL_BYTE_DATA;
+		break;
+
+	case I2C_SMBUS_WORD_DATA_PEC:
+		protocol |= ACPI_EC_SMB_PRTCL_PEC;
+	case I2C_SMBUS_WORD_DATA:
+		acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command);
+		if (read_write == I2C_SMBUS_WRITE) {
+			acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->word);
+			acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + 1, data->word >> 8);
+		}
+		protocol |= ACPI_EC_SMB_PRTCL_WORD_DATA | pec;
+		break;
+
+	case I2C_SMBUS_BLOCK_DATA_PEC:
+		protocol |= ACPI_EC_SMB_PRTCL_PEC;
+	case I2C_SMBUS_BLOCK_DATA:
+		acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command);
+		if (read_write == I2C_SMBUS_WRITE) {
+			len = min_t(u8, data->block[0], 32);
+			acpi_ec_smb_write(smbus, ACPI_EC_SMB_BCNT, len);
+			for (i = 0; i < len; i++)
+				acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + i, data->block[i + 1]);
+		}
+		protocol |= ACPI_EC_SMB_PRTCL_BLOCK_DATA | pec;
+		break;
+
+	case I2C_SMBUS_I2C_BLOCK_DATA:
+		len = min_t(u8, data->block[0], 32);
+		acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command);
+		acpi_ec_smb_write(smbus, ACPI_EC_SMB_BCNT, len);
+		if (read_write == I2C_SMBUS_WRITE)
+			for (i = 0; i < len; i++)
+				acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + i, data->block[i + 1]);
+		protocol |= ACPI_EC_SMB_PRTCL_I2C_BLOCK_DATA;
+		break;
+
+	case I2C_SMBUS_PROC_CALL_PEC:
+		protocol |= ACPI_EC_SMB_PRTCL_PEC;
+	case I2C_SMBUS_PROC_CALL:
+		acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command);
+		acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->word);
+		acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + 1, data->word >> 8);
+		protocol = ACPI_EC_SMB_PRTCL_PROC_CALL | pec;
+		read_write = I2C_SMBUS_READ;
+		break;
+
+	case I2C_SMBUS_BLOCK_PROC_CALL_PEC:
+		protocol |= ACPI_EC_SMB_PRTCL_PEC;
+	case I2C_SMBUS_BLOCK_PROC_CALL:
+		protocol |= pec;
+		len = min_t(u8, data->block[0], 31);
+		acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command);
+		acpi_ec_smb_write(smbus, ACPI_EC_SMB_BCNT, len);
+		for (i = 0; i < len; i++)
+			acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + i, data->block[i + 1]);
+		protocol = ACPI_EC_SMB_PRTCL_BLOCK_PROC_CALL | pec;
+		read_write = I2C_SMBUS_READ;
+		break;
+
+	default:
+		printk(KERN_WARNING "i2c-acpi-ec.c: Unsupported transaction %d\n", size);
+		return -1;
+	}
+
+	acpi_ec_smb_write(smbus, ACPI_EC_SMB_ADDR, addr << 1);
+	acpi_ec_smb_write(smbus, ACPI_EC_SMB_PRTCL, protocol);
+
+	acpi_ec_smb_read(smbus, ACPI_EC_SMB_STS, temp + 0);
+
+	if (~temp[0] & ACPI_EC_SMB_STS_DONE) {
+		udelay(500);
+		acpi_ec_smb_read(smbus, ACPI_EC_SMB_STS, temp + 0);
+	}
+	if (~temp[0] & ACPI_EC_SMB_STS_DONE) {
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout(HZ / 100);
+		acpi_ec_smb_read(smbus, ACPI_EC_SMB_STS, temp + 0);
+	}
+	if ((~temp[0] & ACPI_EC_SMB_STS_DONE) || (temp[0] & ACPI_EC_SMB_STS_STATUS))
+		return -1;
+
+	if (read_write == I2C_SMBUS_WRITE)
+		return 0;
+
+	switch (size) {
+
+	case I2C_SMBUS_BYTE:
+	case I2C_SMBUS_BYTE_DATA:
+		acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA, &data->byte);
+		break;
+
+	case I2C_SMBUS_WORD_DATA:
+	case I2C_SMBUS_WORD_DATA_PEC:
+	case I2C_SMBUS_PROC_CALL:
+	case I2C_SMBUS_PROC_CALL_PEC:
+		acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA, temp + 0);
+		acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA + 1, temp + 1);
+		data->word = (temp[1] << 8) | temp[0];
+		break;
+
+	case I2C_SMBUS_BLOCK_DATA:
+	case I2C_SMBUS_BLOCK_DATA_PEC:
+	case I2C_SMBUS_BLOCK_PROC_CALL:
+	case I2C_SMBUS_BLOCK_PROC_CALL_PEC:
+		acpi_ec_smb_read(smbus, ACPI_EC_SMB_BCNT, &len);
+		len = min_t(u8, len, 32);
+	case I2C_SMBUS_I2C_BLOCK_DATA:
+		for (i = 0; i < len; i++)
+			acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA + i, data->block + i + 1);
+		data->block[0] = len;
+		break;
+	}
+
+	return 0;
+}
+
+u32 
+acpi_ec_smb_func(struct i2c_adapter * adapter)
+{
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
+	       I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_WORD_DATA_PEC |
+	       I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_DATA_PEC |
+	       I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_PROC_CALL_PEC |
+	       I2C_FUNC_SMBUS_BLOCK_PROC_CALL | I2C_FUNC_SMBUS_BLOCK_PROC_CALL_PEC |
+	       I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_HWPEC_CALC;
+}
+
+static struct i2c_algorithm acpi_ec_smbus_algorithm = {
+	.name = "Non-I2C SMBus adapter",
+	.id = I2C_ALGO_SMBUS,
+	.smbus_xfer = acpi_ec_smb_access,
+	.functionality = acpi_ec_smb_func,
+};
+
+static int acpi_ec_hc_add(struct acpi_device *device)
+{
+	int			status;
+	unsigned long		val;
+	struct acpi_ec_hc	*ec_hc;
+	struct acpi_ec_smbus	*smbus;
+
+	ACPI_FUNCTION_TRACE("acpi_ec_hc_add");
+
+	if (!device)
+		return_VALUE(-EINVAL);
+
+	ec_hc = kmalloc(sizeof(struct acpi_ec_hc), GFP_KERNEL);
+	if (!ec_hc)
+		return_VALUE(-ENOMEM);
+	memset(ec_hc, 0, sizeof(struct acpi_ec_hc));
+
+	smbus = kmalloc(sizeof(struct acpi_ec_smbus), GFP_KERNEL);
+	if (!smbus) {
+		kfree(ec_hc);
+		return_VALUE(-ENOMEM);
+	}
+	memset(smbus, 0, sizeof(struct acpi_ec_smbus));
+
+	ec_hc->handle = device->handle;
+	strcpy(acpi_device_name(device), ACPI_EC_HC_DEVICE_NAME);
+	strcpy(acpi_device_class(device), ACPI_EC_HC_CLASS);
+	acpi_driver_data(device) = ec_hc;
+
+	status = acpi_evaluate_integer(ec_hc->handle, "_EC", NULL, &val);
+	if (ACPI_FAILURE(status)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error obtaining _EC\n"));
+		kfree(ec_hc->smbus);
+		kfree(smbus);
+		return_VALUE(-EIO);
+	}
+
+	smbus->ec = acpi_driver_data(device->parent);
+	smbus->base = (val & 0xff00ull) >> 8;
+	smbus->alert = val & 0xffull;
+
+	printk(KERN_DEBUG "base: 0x%.x    alert: 0x%.2x\n", smbus->base, smbus->alert);
+
+	smbus->adapter.owner = THIS_MODULE;
+	sprintf(smbus->adapter.name, "ACPI EC SMBus adapter at %04x", smbus->base);
+	smbus->adapter.id = I2C_ALGO_SMBUS | I2C_HW_ACPI_EC;
+	smbus->adapter.algo = &acpi_ec_smbus_algorithm;
+	smbus->adapter.algo_data = smbus;
+
+	if (i2c_add_adapter(&smbus->adapter)) {
+		printk(KERN_WARNING "i2c-acpi-ec.c: Failed to register adapter.\n");
+		kfree(ec_hc->smbus);
+		kfree(smbus);
+		return_VALUE(-EIO);
+	}
+
+	printk(KERN_INFO "i2c-acpi-ec.c: ACPI EC SMBus adapter at %#x\n", smbus->base);
+
+
+	return_VALUE(AE_OK);
+}
+
+static int acpi_ec_hc_remove(struct acpi_device *device, int type)
+{
+	struct acpi_ec_hc	*ec_hc;
+	ACPI_FUNCTION_TRACE("acpi_ec_hc_remove");
+
+	if (!device)
+		return_VALUE(-EINVAL);
+
+	ec_hc = acpi_driver_data(device);
+
+	i2c_del_adapter(&ec_hc->smbus->adapter);
+	kfree(ec_hc->smbus);
+	kfree(ec_hc);
+
+	return_VALUE(AE_OK);
+}
+	
+static int __init
+acpi_ec_hc_init(void)
+{
+	return acpi_bus_register_driver((&acpi_ec_hc_driver));
+}
+
+static void __exit
+acpi_ec_hc_exit(void)
+{
+	acpi_bus_unregister_driver(&acpi_ec_hc_driver);
+}
+
+module_init(acpi_ec_hc_init);
+module_exit(acpi_ec_hc_exit);
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ducrot Bruno");
+MODULE_DESCRIPTION("ACPI EC SMBus driver");

