aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpaul.chen <paul.chen@wtmec.com>2013-03-02 14:28:36 +0800
committerLubomir Rintel <lkundrak@v3.sk>2019-07-22 19:40:04 +0200
commit7db1057b31daa2b980c98016879ab447a829dae0 (patch)
tree07fc99830c9be32916dd6fac7c2018f908c73c66
parente5e59fae58d5566d349870f188637895f3e3b631 (diff)
downloadlinux-mmp3-dell-ariel-7db1057b31daa2b980c98016879ab447a829dae0.tar.gz
Driver <input> EC driver
update EC SPI support modified: arch/arm/configs/qseven_defconfig modified: drivers/input/serio/Kconfig new file: drivers/input/serio/eneec.c new file: drivers/input/serio/eneec_ioc.h new file: drivers/input/serio/eneec_spi.c new file: drivers/rtc/rtc-idt1338.c new file: include/linux/i2c/eneec.h (cherry picked from commit a576418776bd5ab88a9f50414a1ca4005c948679)
-rwxr-xr-x[-rw-r--r--]arch/arm/configs/qseven_defconfig2
-rw-r--r--drivers/input/serio/Kconfig8
-rw-r--r--drivers/input/serio/eneec.c1475
-rw-r--r--drivers/input/serio/eneec_ioc.h55
-rw-r--r--drivers/input/serio/eneec_spi.c610
-rw-r--r--drivers/rtc/rtc-idt1338.c176
-rw-r--r--include/linux/i2c/eneec.h11
7 files changed, 2333 insertions, 4 deletions
diff --git a/arch/arm/configs/qseven_defconfig b/arch/arm/configs/qseven_defconfig
index 19e52ae2eca9b1..28a6834d9ac2ff 100644..100755
--- a/arch/arm/configs/qseven_defconfig
+++ b/arch/arm/configs/qseven_defconfig
@@ -1157,6 +1157,8 @@ CONFIG_SERIO_LIBPS2=y
# CONFIG_SERIO_RAW is not set
# CONFIG_SERIO_ALTERA_PS2 is not set
# CONFIG_SERIO_PS2MULT is not set
+CONFIG_SERIO_ENEEC_KB3930_I2C=y
+CONFIG_SERIO_ENEEC_KB3930_SPI=y
# CONFIG_GAMEPORT is not set
#
diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index 0595d1a6b7e9a6..216b0efc8c177b 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -248,9 +248,9 @@ config SERIO_ENEEC_KB3930_I2C
To compile this driver as a module, choose M here: the
module will be called ennec.
-#config SERIO_ENEEC_KB3930_SPI
-# tristate "ENE EC serio driver over SPI"
-# default y
-# depends on ARM && I2C
+config SERIO_ENEEC_KB3930_SPI
+ tristate "ENE EC serio driver over SPI"
+ default y
+ depends on ARM && SERIO_ENEEC_KB3930_I2C
endif
diff --git a/drivers/input/serio/eneec.c b/drivers/input/serio/eneec.c
new file mode 100644
index 00000000000000..3423553335e26a
--- /dev/null
+++ b/drivers/input/serio/eneec.c
@@ -0,0 +1,1475 @@
+/*
+ * ENE EC i2c driver for Linux
+ *
+ * v.1.2
+ * - Add XBI access functions.
+ * v.1.1
+ * - for kernel 2.6.31 (new i2c model)
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/power_supply.h>
+#include <linux/platform_device.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include <asm/mach-types.h>
+
+#include "eneec_ioc.h"
+
+#define DRIVER_DESC "ENEEC i2c driver"
+#include <linux/param.h>
+#include <linux/pm.h>
+#include <mach/mfp-mmp2.h>
+//#include <plat/mfp.h>
+
+MODULE_AUTHOR("Victoria/flychen");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+//#define POLLING
+#define POLL_INTERVAL 100
+#define EC_BATTERY_SUPPORT
+#define ENE_DEBUG(format, args...) \
+ pr_debug("%s(%d): "format"\n", __func__, __LINE__, ##args)
+
+struct battery_device_attribute{
+ struct device_attribute dev_attr;
+ int index;
+};
+#define to_battery_dev_attr(_dev_attr) \
+ container_of(_dev_attr, struct battery_device_attribute, dev_attr)
+
+#define BATTERY_ATTR(_name, _mode, _show, _store, _index) \
+ { .dev_attr = __ATTR(_name, _mode, _show, _store), \
+ .index = _index }
+
+#define BATTERY_DEVICE_ATTR(_name, _mode, _show, _store, _index) \
+struct battery_device_attribute battery_dev_attr_##_name \
+ = BATTERY_ATTR(_name, _mode, _show, _store, _index)
+
+
+/*
+ * Be sure system is added with one i2c board info for one of the KBC chip
+ * at mainboard arch_initcall() code, for example,
+ *
+ * static struct i2c_board_info system_i2c_devs[] = {
+ * { .type = "KB39XX", // one of i2c_device_id.name below;
+ * .addr = 0x18, // i2c slave address for EC, which must be the same as EC's SMBRSA register(offset 0xFFA4) bit7:1.
+ * .irq = 220, // irq used for request_irq().
+ * },
+ * };
+ *
+ * static void __init mini2440_machine_init()
+ * {
+ * i2c_register_board_info(0, system_i2c_devs, ARRAY_SIZE(system_i2c_devs));
+ * }
+ */
+static const struct i2c_device_id eneec_idtable[] = {
+ // name driver_data
+ { "KB39XX", 0x3926F0 }, // doesn't matter
+ { "KB9010A", 0x9010A0 },
+ { "KB9010B", 0x9010B0 },
+ { "KB37XX", 0x3730A0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, eneec_idtable);
+
+/*
+ * The word read from EC
+ * bit[0]=toggle bit;
+ * bit[1]=0: command ack or response from mouse or keyboard;
+ * =1: keyboard scancode or mouse position data;
+ * bit[6:4]=3: from keyboard; =4: from mouse;
+ */
+#define PORT_KBD 0x30
+#define PORT_MOU 0x40
+#define PORT_ECRAM 0x80
+#define PORT_XBI 0xF0
+#define DATA_TO_PORT(data) (data & 0xf0)
+
+/* Define the used RAM register address for reading content purpose */
+//#define MAX_ECRAM_REG 4
+static const u8 mimas_ram_reg[] = {
+ 0x01, 0x02, 0x03, 0x11, 0x12, 0x13, 0x14, 0x15,
+ 0x16, 0x20, 0x21, 0x22, 0x30, 0x31, 0x32
+};
+
+static const u8 titan_ram_reg[] = {
+ 0x01, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+ 0x19 ,0x20, 0x21, 0x22, 0x23, 0x24, 0x30, 0x31,
+ 0x32, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
+ 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x51 ,0x52,
+ 0x53, 0x54, 0x55, 0x56, 0x57, 0x5A, 0x5B, 0x5C,
+ 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64,
+ 0x65, 0x66 ,0x67, 0x68, 0x69, 0x74, 0x75, 0xF0,
+ 0xF1, 0xF2, 0xF3
+};
+
+static const u8 ariel_ram_reg[] = {
+ 0x01, 0x02, 0x03, 0x11, 0x12, 0x13, 0x14, 0x15,
+ 0x16, 0x20, 0x21, 0x22, 0x30, 0x31, 0x32
+};
+
+struct eneec_port {
+ struct serio *serio;
+ struct eneec *eneec;
+ unsigned char port_no; // PORT_KBD or PORT_MOU.
+};
+
+struct eneec_device_info {
+ struct device *dev;
+
+ /* Data is valid after calling eneec_battery_read_status() */
+ unsigned long update_time; /* jiffies when data read */
+ int charge_status;
+ int temp_tK;
+ int current_mV;
+ int current_mA;
+ int average_mA;
+ int capacity_P;
+ int current_mAh;
+ int absolute_mAh;
+ int current_empty_time_Min;
+ int average_empty_time_Min;
+ int average_full_time_Min;
+ int design_mAh;
+
+ struct power_supply bat;
+ struct workqueue_struct *monitor_wqueue;
+ struct delayed_work monitor_work;
+};
+
+struct eneec {
+ struct i2c_client *client;
+ const u8 *ram_reg;
+ int ram_reg_size;
+ int serio_over_i2c;
+ struct eneec_device_info *battery_info;
+ int irq; // don't remove it although also in client->irq. for indicating if irq requested.
+ unsigned short chip_id;
+ unsigned char rev_id;
+ struct workqueue_struct *work_queue;
+#ifdef POLLING
+ struct delayed_work read_work;
+ bool stop_polling;
+#else
+ struct work_struct read_work;
+#endif
+ struct eneec_port kbd_port;
+ struct eneec_port mou_port;
+ int toggle;
+ bool is_37xx;
+// For exposing our userspace API.
+ // The main reason to have this class is to make mdev/udev create the
+ // /dev/eneec character device nodes exposing our userspace API.
+ struct class *eneec_class;
+ struct cdev cdev;
+ dev_t devt;
+// ~
+// struct power_supply bat;
+ int charge_status;
+ int temp_tC;
+ u16 Manufacture;
+ u16 Battery_mode;
+ u16 current_mV;
+ short current_mA;
+ short average_mA;
+ u16 charge_mV;
+ short charge_mA;
+ int capacity_P;
+ u8 abs_capacity_P;
+ int current_mAh;
+ int absolute_mAh;
+ int current_empty_time_Min;
+ int average_empty_time_Min;
+ int average_full_time_Min;
+ int design_mAh;
+ u16 Cell_1_volt;
+ u16 Cell_2_volt;
+ u16 Cell_3_volt;
+ u16 Cell_4_volt;
+ u16 Max_Error;
+
+};
+
+static struct i2c_client *ene_client = NULL ;
+
+static struct eneec_device_info *di_proc = NULL ;
+static unsigned int cache_time = 20000;
+static int eneec_register_update_all(void);
+struct power_supply *ec_psy = NULL;
+
+static int eneec_probe_battery(struct eneec *);
+static int eneec_remove_battery(struct eneec *);
+
+int ene_bat_proc_write(struct file *file, const char *buffer,unsigned long count, void *data)
+{
+ return count;
+}
+
+int ene_bat_proc_read(char* page, char** start, off_t off, int count,int* eof,
+ void* data)
+{
+ int len = 0;
+// struct eceec_device_info *di = to_eneec_device_info(ec_psy);
+// len += sprintf(page+len, "Charge_status = %d\n", (struct eceec_device_info *)di_proc->charge_status);
+
+
+ len += sprintf(page+len, "Charge_status = %d\n", di_proc->charge_status);
+ len += sprintf(page+len, "temp_tK = %d\n", di_proc->temp_tK);
+ len += sprintf(page+len, "current_mV = %d\n", di_proc->current_mV);
+ len += sprintf(page+len, "current_mA = %d\n", di_proc->current_mA);
+ len += sprintf(page+len, "average_mA = %d\n", di_proc->average_mA);
+ len += sprintf(page+len, "capacity_P = %d\n", di_proc->capacity_P);
+ len += sprintf(page+len, "current_mAh = %d\n", di_proc->current_mAh);
+ len += sprintf(page+len, "absolute_mAh = %d\n", di_proc->absolute_mAh);
+ len += sprintf(page+len, "current_empty_time_Min = %d\n", di_proc->current_empty_time_Min);
+ len += sprintf(page+len, "average_empty_time_Min = %d\n", di_proc->average_empty_time_Min);
+ len += sprintf(page+len, "average_full_time_Min = %d\n", di_proc->average_full_time_Min);
+ len += sprintf(page+len, "design_mAh = %d\n", di_proc->design_mAh);
+
+ return len;
+}
+
+/*
+ * Battery Driver
+ */
+
+static int eneec_battery_read_status(struct eneec_device_info *di)
+{
+ int old_charge_status = di->charge_status;
+ struct eneec *eneec ;
+
+ if (ene_client == NULL)
+ return 0;
+
+ eneec = i2c_get_clientdata(ene_client);
+
+ /* Poll the device at most once in a cache_time */
+ if (time_before(jiffies, di->update_time + msecs_to_jiffies(cache_time)))
+ return 0;
+
+ /* Read the Gauge status through the I2C */
+ di->update_time = jiffies; /* last update time */
+
+ eneec_register_update_all();
+
+ di->temp_tK = (int)eneec->temp_tC*10+2730;
+ di->current_mV = (int)eneec->current_mV;
+ di->current_mA = (int)eneec->current_mA;
+ di->average_mA = (int)eneec->average_mA;
+ di->current_mAh = (int)eneec->current_mAh;
+ di->absolute_mAh= (int)eneec->absolute_mAh;
+ di->capacity_P = (int)eneec->capacity_P;
+ di->current_empty_time_Min = 0 ;
+ di->average_empty_time_Min = 0 ;
+ di->average_full_time_Min = 0 ;
+ di->design_mAh = (int)eneec->design_mAh;
+
+ if (di->current_mA & 0x80000000)
+ di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
+ else
+ di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
+
+ if ((di->current_mA & ~0x80000000) < 10) {
+ pr_debug("current_mA %04x \n",di->current_mA);
+ di->charge_status = POWER_SUPPLY_STATUS_FULL;
+ }
+
+ if (di->charge_status != old_charge_status) {
+ power_supply_changed(&di->bat);
+ cancel_delayed_work(&di->monitor_work);
+ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ/10);
+ }
+
+ dev_dbg(di->dev, "Charge_status = %d\n", di->charge_status);
+ dev_dbg(di->dev, "temp_tK = %d\n", di->temp_tK);
+ dev_dbg(di->dev, "current_mV = %d\n", di->current_mV);
+ dev_dbg(di->dev, "current_mA = %d\n", di->current_mA);
+ dev_dbg(di->dev, "average_mA = %d\n", di->average_mA);
+ dev_dbg(di->dev, "capacity_P = %d\n", di->capacity_P);
+ dev_dbg(di->dev, "current_mAh = %d\n", di->current_mAh);
+ dev_dbg(di->dev, "absolute_mAh = %d\n", di->absolute_mAh);
+ dev_dbg(di->dev, "current_empty_time_Min = %d\n", di->current_empty_time_Min);
+ dev_dbg(di->dev, "average_empty_time_Min = %d\n", di->average_empty_time_Min);
+ dev_dbg(di->dev, "average_full_time_Min = %d\n", di->average_full_time_Min);
+ dev_dbg(di->dev, "design_mAh = %d\n", di->design_mAh);
+ return 0;
+}
+
+static struct proc_dir_entry *bat_proc_entry;
+extern struct proc_dir_entry proc_root;
+
+static int battery_proc_init(void)
+{
+ bat_proc_entry = create_proc_entry("battery", 0666, &proc_root);
+ bat_proc_entry->read_proc = ene_bat_proc_read;
+ bat_proc_entry->write_proc = ene_bat_proc_write;
+ return 0;
+}
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Utilities
+
+static u8 ecram_val[256];
+
+static int eneec_register_update(u8 addr)
+{
+ struct eneec *eneec = i2c_get_clientdata(ene_client);
+ int err;
+ u16 data_1;
+
+ struct i2c_client *client = ene_client;
+
+ if (eneec->serio_over_i2c)
+ disable_irq_nosync(eneec->irq);
+
+ data_1 = addr | (((u16) 0x0) << 8);
+ err = i2c_smbus_write_word_data(client, PORT_ECRAM|0x01, data_1);
+ if (err) {
+ if (eneec->serio_over_i2c)
+ enable_irq(eneec->irq);
+
+ pr_err("kb3930 i2c bus error(0x%04x)",err);
+ return 1;
+ }
+
+// ENE_DEBUG("kb3930 Read_Write: id:%02x reg:%02x \n", (PORT_ECRAM|0x01), addr );
+ data_1 = i2c_smbus_read_word_data(client, 0);
+// ENE_DEBUG("kb3930 Read_Resp: id:%02x reg:%02x data:%02x\n", (data_1&0xFF), addr, (data_1>>8));
+ ecram_val[addr]=(u8)(data_1>>8);
+
+ if (eneec->serio_over_i2c)
+ enable_irq(eneec->irq);
+ return 0;
+
+}
+static int eneec_register_update_all(void)
+{
+ struct eneec *eneec = i2c_get_clientdata(ene_client);
+ int i,err;
+ u16 data_1;
+
+ struct i2c_client *client = ene_client;
+
+ if (eneec->serio_over_i2c)
+ disable_irq_nosync(eneec->irq);
+
+ for (i=0; i < eneec->ram_reg_size; i++) {
+ u8 reg = eneec->ram_reg[i];
+ data_1 = reg | (((u16) 0x0) << 8);
+ err = i2c_smbus_write_word_data(client, PORT_ECRAM|0x01, data_1);
+ if (err) {
+ if (eneec->serio_over_i2c)
+ enable_irq(eneec->irq);
+
+ pr_err("kb3930 i2c bus error(0x%04x)",err);
+ return 1;
+ }
+ data_1 = i2c_smbus_read_word_data(client, 0);
+ ecram_val[reg]=(u8)(data_1>>8);
+ }
+
+ if (machine_is_titan()) {
+ u16 tmp;
+ eneec->current_mV = ecram_val[VOLTAGE_L]+ecram_val[VOLTAGE_H]*256;
+ tmp=ecram_val[CURRENT_H];
+ tmp= (tmp<<8)+ecram_val[CURRENT_L];
+ eneec->current_mA = (short)tmp;
+ tmp=ecram_val[AVG_CURRENT_H];
+ tmp= (tmp<<8)+ecram_val[AVG_CURRENT_L];
+ eneec->average_mA = (short)tmp;
+ eneec->charge_mV = ecram_val[CHARGE_V_L]+ecram_val[CHARGE_V_H]*256;
+ tmp=ecram_val[CHARGE_C_H];
+ tmp= (tmp<<8)+ecram_val[CHARGE_C_L];
+ eneec->charge_mA = (short)tmp;
+ eneec->capacity_P = ecram_val[RSOC];
+ eneec->current_mAh = ecram_val[CAP_REMAIN_L]+ecram_val[CAP_REMAIN_H]*256;
+ eneec->absolute_mAh = ecram_val[CAP_FULL_L]+ecram_val[CAP_FULL_H]*256;
+ eneec->Cell_1_volt = ecram_val[CELL_1_V_L]+ecram_val[CELL_1_V_H]*256;
+ eneec->Cell_2_volt = ecram_val[CELL_2_V_L]+ecram_val[CELL_2_V_H]*256;
+ eneec->Cell_3_volt = ecram_val[CELL_3_V_L]+ecram_val[CELL_3_V_H]*256;
+ eneec->Cell_4_volt = ecram_val[CELL_4_V_L]+ecram_val[CELL_4_V_H]*256;
+ eneec->Max_Error = ecram_val[MAX_ERROR_L]+ecram_val[MAX_ERROR_H]*256;
+ eneec->temp_tC = ecram_val[TEMP_C];
+ eneec->Manufacture = ecram_val[MANUFACTURE_L]+ecram_val[MANUFACTURE_H]*256;
+ eneec->Battery_mode = ecram_val[BATTERY_MODE_L]+ecram_val[BATTERY_MODE_H]*256;
+ eneec->abs_capacity_P = ecram_val[ASOC];
+ eneec->design_mAh = ecram_val[CAP_DESIGN_L]+ecram_val[CAP_DESIGN_H]*256;
+ }
+
+ if (eneec->serio_over_i2c)
+ enable_irq(eneec->irq);
+ return 0;
+}
+
+//
+// Read XBI register through i2c bus.
+static int eneec_read_reg(struct eneec *eneec, unsigned short reg, unsigned char *val)
+{
+ s32 data;
+
+ if (eneec->is_37xx)
+ return 0;
+
+ if ((reg & 0xFFF0) != 0) { // e.g., for XBI reg 0xFEAB, caller should pass in 0xB.
+ pr_err("eneec_read_reg(reg), reg[8:15] is not zeros.\n");
+ return -EINVAL;
+ }
+
+ // low high
+ // Read XBI through smbus: {slave_adr | XBI_ID + XBI_ofs[3:0] | slave_adr in | XBI_ID + 0x02 | data}
+ data = i2c_smbus_read_word_data(eneec->client, PORT_XBI | reg);
+ if ((u8) data != (PORT_XBI | 0x02)) { // low byte should be (XBI_ID | 0x02).
+ pr_err("eneec_read_xbi() does not get XBI_ID 0x%X from EC.\n", PORT_XBI);
+ return -EIDRM;
+ }
+
+ *val = data >> 8; // high byte is the data from reg.
+ return 0;
+}
+
+//
+// Write XBI register through i2c bus.
+static int eneec_write_reg(struct eneec *eneec, unsigned short reg, unsigned char val)
+{
+ int err = 0;
+ u16 data;
+
+ if (eneec->is_37xx)
+ return 0;
+
+ if ((reg & 0xFFF0) != 0) { // e.g., for XBI reg 0xFEAB, caller should pass in 0xB.
+ pr_err("eneec_read_reg(reg), reg[8:15] is not zeros.\n");
+ return -EINVAL;
+ }
+
+ // low high
+ // Write XBI through smbus: { slave_adr | XBI_ID | XBI_ofs[3:0] | val}
+ data = reg | (((u16) val) << 8);
+ err = i2c_smbus_write_word_data(eneec->client, PORT_XBI, data);
+
+ if (err < 0)
+ pr_err("i2c_smbus_write_word_data(client, 0x%X, 0x%X) failed (%d)\n", PORT_XBI, data, err);
+
+ return err;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Codes for interfacing with user AP.
+
+//
+// Handle the get-chipid ioctl call.
+// sizeof(*arg) = 4.
+// *arg=output chipIdL-chipIdH-revId.
+static int ioctl_get_chipid(struct eneec *eneec, unsigned long arg)
+{
+ unsigned long id_rev = eneec->chip_id | (eneec->rev_id << 16);
+ return __put_user(id_rev, (unsigned long __user *) arg);
+}
+
+static int ioctl_enter_code_in_ram(struct eneec *eneec)
+{
+ if (eneec->is_37xx)
+ return 0;
+
+ return i2c_smbus_write_word_data(eneec->client, PORT_XBI, 0x10);
+}
+
+static int ioctl_exit_code_in_ram(struct eneec *eneec)
+{
+ if (eneec->is_37xx)
+ return 0;
+
+ return i2c_smbus_write_word_data(eneec->client, PORT_XBI, 0xF0);
+}
+
+//
+// Handle the read-reg ioctl call.
+// sizeof(*arg) = 2.
+// *arg=input regL-regH, also, =output byte.
+static int ioctl_read_reg(struct eneec *eneec, unsigned long arg)
+{
+ unsigned short reg;
+ unsigned char val;
+ int retval;
+
+ // Get register to read.
+ val = 0;
+ retval = __get_user(reg, (unsigned short __user *) arg);
+ if (retval < 0)
+ return retval;
+
+ if (!eneec->is_37xx) {
+ if (reg < 0xFEA0 || reg > 0xFEAD) { // only for XBI if non-37XX.
+ pr_warning("ioctl_read_reg(reg), reg is not between 0xFEA0~0xFEAD for non-37XX\n");
+ return -EINVAL;
+ }
+ reg &= 0x0F; // we don't need 0xFEA
+ }
+
+ retval = eneec_read_reg(eneec, reg, &val);
+ if (retval < 0)
+ return retval;
+
+ return __put_user(val, (unsigned char __user *) arg);
+}
+
+//
+// Handle the write-reg ioctl call.
+// sizeof(*arg) = 4.
+// *arg=input regL-regH-byte
+static int ioctl_write_reg(struct eneec *eneec, unsigned long arg)
+{
+ unsigned long reg_data;
+ unsigned short reg;
+ unsigned char byte;
+ int retval;
+
+ // Get register and data to write.
+ retval = __get_user(reg_data, (unsigned long __user *) arg);
+
+ if (retval < 0) {
+ pr_warning("get_user %d\n", retval);
+ return retval;
+ }
+
+ // [15:0] is reg, [23:16] is data.
+ reg = (unsigned short) reg_data;
+ byte = (unsigned char) (reg_data >> 16);
+
+ if (!eneec->is_37xx) {
+ if (reg < 0xFEA0 || reg > 0xFEAD) { // only for XBI if non-37XX.
+ pr_warning("ioctl_write_reg(reg), reg is not between 0xFEA0~0xFEAD for non-37XX\n");
+ return -EINVAL;
+ }
+ reg &= 0x0F; // we don't need 0xFEA
+ }
+
+ retval = eneec_write_reg(eneec, reg, byte);
+
+ return retval;
+}
+
+static long eneec_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct eneec *eneec = filp->private_data;
+ int retval = 0;
+
+
+ // Check type and command number
+ if (_IOC_TYPE(cmd) != ENEEC_IOC_MAGIC) {
+ pr_debug("Not ENEEC_IOC_MAGIC\n");
+ return -ENOTTY;
+ }
+
+ switch (cmd)
+ {
+ case ENEEC_IOC_READ_REG:
+ retval = ioctl_read_reg(eneec, arg);
+ break;
+ case ENEEC_IOC_WRITE_REG:
+ retval = ioctl_write_reg(eneec, arg);
+ break;
+ case ENEEC_IOC_GET_CHIPID:
+ retval = ioctl_get_chipid(eneec, arg);
+ break;
+ case ENEEC_IOC_ENTER_CODE_IN_RAM:
+ retval = ioctl_enter_code_in_ram(eneec);
+ break;
+ case ENEEC_IOC_EXIT_CODE_IN_RAM:
+ retval = ioctl_exit_code_in_ram(eneec);
+ break;
+ default:
+ pr_warning("Unsupported ioctl\n");
+ retval = -ENOTTY;
+ break;
+ }
+
+ return retval;
+}
+
+static int eneec_open(struct inode *inode, struct file *filp)
+{
+ struct eneec *eneec = container_of(inode->i_cdev, struct eneec, cdev);
+
+ pr_debug("eneec_open()\n");
+
+ filp->private_data = eneec;
+
+ return 0;
+}
+
+static int eneec_release(struct inode *inode, struct file *filp)
+{
+ pr_debug("eneec_release()\n");
+ return 0;
+}
+
+static const struct file_operations eneec_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = eneec_ioctl,
+ .open = eneec_open,
+ .release = eneec_release,
+};
+
+//
+// Undo eneec_create_cdev_node().
+// Call this only if the char device node was ever created successfully.
+void eneec_destroy_cdev_node(struct eneec *eneec)
+{
+ device_destroy(eneec->eneec_class, eneec->devt);
+ cdev_del(&eneec->cdev);
+ unregister_chrdev_region(eneec->devt, 1);
+ class_destroy(eneec->eneec_class);
+}
+
+int eneec_create_cdev_node(struct eneec *eneec)
+{
+ int status;
+ dev_t devt;
+ struct device *dev;
+ struct class *eneec_class;
+ bool is_class_created = false, is_region_allocated = false, is_cdev_added = false, is_device_created = false;
+
+ // Create class
+ eneec_class = class_create(THIS_MODULE, "eneec");
+ status = IS_ERR(eneec_class) ? PTR_ERR(eneec_class) : 0;
+ if (status < 0) {
+ pr_err("[eneec] class_create() failed -- %d\n", status);
+ goto error_exit;
+ }
+ is_class_created = true;
+
+ // Alloc chrdev region.
+ status = alloc_chrdev_region(&devt, 0, 1, "eneec");
+ if (status < 0) {
+ pr_err("[eneec] alloc_chrdev_region() failed -- %d\n", status);
+ goto error_exit;
+ }
+ is_region_allocated = true;
+
+ // Add cdev.
+ cdev_init(&eneec->cdev, &eneec_fops);
+ status = cdev_add(&eneec->cdev, devt, 1);
+ if (status < 0) {
+ pr_err("[eneec] cdev_add() failed -- %d\n", status);
+ goto error_exit;
+ }
+ is_cdev_added = true;
+
+ // Create device
+ dev = device_create(
+ eneec_class,
+ &eneec->client->dev, // parent device (struct device *)
+ devt,
+ eneec, // caller's context
+ "eneec");
+
+ status = IS_ERR(dev) ? PTR_ERR(dev) : 0;
+ if (status < 0) {
+ pr_err("[eneec] device_create() failed -- %d\n", status);
+ goto error_exit;
+ }
+ is_device_created = true;
+
+ // Succeed.
+ eneec->eneec_class = eneec_class;
+ eneec->devt = devt;
+
+ return 0;
+
+error_exit:
+
+ if (is_device_created)
+ device_destroy(eneec_class, devt);
+ if (is_cdev_added)
+ cdev_del(&eneec->cdev);
+ if (is_region_allocated)
+ unregister_chrdev_region(devt, 1);
+ if (is_class_created)
+ class_destroy(eneec_class);
+
+ return status;
+}
+
+static void ennec_interrupt(u8 reg,u8 stat)
+{
+ switch (reg) {
+ /*LID Sitch*/
+ case 0x23 :
+ pr_debug("LID reg = 0x%2x , stat = 0x%2x",reg,stat);
+ break;
+ case 0x24 :
+ pr_debug("AC IN reg = 0x%2x , stat = 0x%2x",reg,stat);
+ break;
+ default :
+ return ;
+ }
+ return ;
+}
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Codes for interfacing with kernel kbd, mouse.
+#ifdef EC_BATTERY_SUPPORT
+static void eneec_battery_work(struct work_struct *work)
+{
+ struct eneec_device_info *di = container_of(work,
+ struct eneec_device_info, monitor_work.work);
+ const int interval = HZ * 60;
+
+ dev_dbg(di->dev, "%s\n", __func__);
+
+// eneec_battery_read_status(di);
+ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, interval);
+}
+
+#define to_eneec_device_info(x) container_of((x), struct eneec_device_info, \
+ bat);
+#endif
+
+static void eneec_read_work(struct work_struct *work)
+{
+#ifdef POLLING
+ struct eneec *eneec = container_of(work, struct eneec, read_work.work);
+#else
+ struct eneec *eneec = container_of(work, struct eneec, read_work);
+#endif
+ struct i2c_client *client = eneec->client;
+ s32 data;
+ u8 port;
+ int retry;
+
+ data = i2c_smbus_read_word_data(client, 0);
+
+#ifdef POLLING
+ if (eneec->toggle != (data & 1)) // has toggle bit: valid data
+ ENE_DEBUG("IN : 0x%04x\n", (u16) data);
+ else
+ goto queue_next_work;
+#else
+ // ENE_DEBUG("IN : 0x%04x\n", (u16) data);
+
+ if (unlikely((eneec->toggle ^ (data & 1)) == 0)) {
+ printk("WARNING: invalid toggle bit. Interrupts might be missed !\n");
+ }
+#endif
+
+ eneec->toggle = data & 1;
+
+ for (retry = 0; retry < 3; retry++) {
+ // e.g., data=0xfa31, 0xfa=ack, 0x30=kbd, 1=toggle bit.
+ port = DATA_TO_PORT(data);
+
+ if (eneec->serio_over_i2c) {
+ if (port == PORT_KBD) {
+ serio_interrupt(eneec->kbd_port.serio,
+ (u8) (data >> 8), 0);
+ printk("Invalid data: 0x%08x\n", data);
+ break;
+ }
+
+ if (port == PORT_MOU) {
+ serio_interrupt(eneec->mou_port.serio,
+ (u8) (data >> 8), 0);
+ break;
+ }
+ }
+
+ if (port == PORT_ECRAM) {
+ ennec_interrupt((u8) (data >> 8),(u8) (data &0xff));
+ break;
+ } else {
+ ENE_DEBUG("Invalid data: 0x%08x\n", data);
+ data = i2c_smbus_read_word_data(client, 0);
+ ENE_DEBUG("IN : 0x%04x (retry)\n", (u16) data);
+ }
+ }
+
+#ifdef POLLING
+queue_next_work:
+ if (!eneec->stop_polling)
+ queue_delayed_work(eneec->work_queue, &eneec->read_work, msecs_to_jiffies(POLL_INTERVAL));
+#else
+ if (eneec->serio_over_i2c)
+ enable_irq(eneec->irq);
+#endif
+}
+
+#ifndef POLLING
+static irqreturn_t eneec_interrupt(int irq, void *dev_id)
+{
+ struct i2c_client *client = dev_id;
+ struct eneec *eneec = i2c_get_clientdata(client);
+
+ printk("eneec_interrupt\n");
+
+ if (eneec->serio_over_i2c)
+ disable_irq_nosync(irq);
+
+ queue_work(eneec->work_queue, &eneec->read_work);
+// lpm_mfpr_edge_detect_clear_config(MFP_PIN_GPIO60);
+ return IRQ_HANDLED;
+}
+#endif
+
+static int eneec_write(struct serio *serio, unsigned char byte)
+{
+ int err;
+ struct eneec_port *port = serio->port_data;
+ struct eneec *eneec = port->eneec;
+
+ //ENE_DEBUG("OUT: 0x%02x%02x\n", byte, port->port_no);
+
+ err = i2c_smbus_write_word_data(eneec->client, port->port_no, byte);
+
+ return err;
+}
+
+static int eneec_create_kbd_port(struct eneec *eneec)
+{
+ struct i2c_client *client = eneec->client;
+ struct serio *serio;
+
+ serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+ if (!serio)
+ return -ENOMEM;
+
+ serio->id.type = SERIO_8042_XL;
+ serio->write = eneec_write;
+ serio->port_data = &eneec->kbd_port;
+ serio->dev.parent = &client->dev;
+ strlcpy(serio->name, "eneec kbd port", sizeof(serio->name));
+ strlcpy(serio->phys, "eneec/serio0", sizeof(serio->phys));
+
+ eneec->kbd_port.serio = serio;
+ eneec->kbd_port.port_no = PORT_KBD;
+ eneec->kbd_port.eneec = eneec;
+
+ return 0;
+}
+
+static int eneec_create_mouse_port(struct eneec *eneec)
+{
+ struct i2c_client *client = eneec->client;
+ struct serio *serio;
+
+ serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+ if (!serio)
+ return -ENOMEM;
+
+ serio->id.type = SERIO_8042;
+ serio->write = eneec_write;
+ serio->port_data = &eneec->mou_port;
+ serio->dev.parent = &client->dev;
+ strlcpy(serio->name, "eneec mouse port", sizeof(serio->name));
+ strlcpy(serio->phys, "eneec/serio1", sizeof(serio->phys));
+
+ eneec->mou_port.serio = serio;
+ eneec->mou_port.port_no = PORT_MOU;
+ eneec->mou_port.eneec = eneec;
+
+ return 0;
+}
+/*
+ * sysfs
+ */
+static ssize_t read_ec_ram_value(struct device *dev, struct device_attribute
+ *devattr, char *buf) {
+ int i,count;
+ struct eneec *eneec = i2c_get_clientdata(ene_client);
+
+ eneec_register_update_all();
+ count = sprintf(buf, "Reg:Data\n");
+
+ for (i=0; i < eneec->ram_reg_size; i++) {
+ u8 reg = eneec->ram_reg[i];
+ count += sprintf(buf + count,"%02X:%02X\n",reg,ecram_val[reg]);
+ }
+
+ if (machine_is_titan()) {
+ count += sprintf(buf + count,"Manufacturer Access: 0x%04X\n",eneec->Manufacture);
+ count += sprintf(buf + count,"Battery Mode: 0x%04X\n",eneec->Battery_mode);
+ count += sprintf(buf + count,"Temp : %d (C)\n",eneec->temp_tC);
+ count += sprintf(buf + count,"Voltage(mV): %d\n",eneec->current_mV);
+ count += sprintf(buf + count,"Current(mA): %d\n",eneec->current_mA);
+ count += sprintf(buf + count,"Average Current(mA): %d\n",eneec->average_mA);
+ count += sprintf(buf + count,"Max Error : %d\n",eneec->Max_Error);
+ count += sprintf(buf + count,"RSOC(%%): %d %%\n",eneec->capacity_P);
+ count += sprintf(buf + count,"ASOC(%%): %d %%\n",eneec->abs_capacity_P);
+ count += sprintf(buf + count,"Remaining Capacity(mAh): %d\n",eneec->current_mAh);
+ count += sprintf(buf + count,"Full Capacity(mAh): %d\n",eneec->absolute_mAh);
+ count += sprintf(buf + count,"Charging Voltage(mV): %d\n",eneec->charge_mV);
+ count += sprintf(buf + count,"charging Current(mA): %d\n",eneec->charge_mA);
+ count += sprintf(buf + count,"Cell 1 voltage (mV): %d\n",eneec->Cell_1_volt);
+ count += sprintf(buf + count,"Cell 2 voltage (mV): %d\n",eneec->Cell_2_volt);
+ count += sprintf(buf + count,"Cell 3 voltage (mV): %d\n",eneec->Cell_3_volt);
+ count += sprintf(buf + count,"Cell 4 voltage (mV): %d\n",eneec->Cell_4_volt);
+
+ eneec->temp_tC = ecram_val[TEMP_C];
+ eneec->Manufacture = ecram_val[MANUFACTURE_L]+ecram_val[MANUFACTURE_H]*256;
+ eneec->Battery_mode = ecram_val[BATTERY_MODE_L]+ecram_val[BATTERY_MODE_H]*256;
+ }
+ return count;
+}
+
+static ssize_t set_ec_ram_value(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count) {
+ struct eneec *eneec = i2c_get_clientdata(ene_client);
+ u8 cmd = 0;
+ int ret;
+ struct i2c_client *client = eneec->client;
+ u8 buffer[2]="", *temp;
+ u16 data_1;
+
+ /* Write buffer value to ECRAM */
+ if (count>3) {
+ cmd = PORT_ECRAM;
+ buffer[0] = (u8) simple_strtoul(buf,NULL,16);
+ temp = strchr(buf, 0x20); //string with space character
+ buffer[1] = (u8) simple_strtoul(&temp[1],NULL,16);
+ data_1 = buffer[0] | (((u16) buffer[1]) << 8); // Register addr | data value
+ ret = i2c_smbus_write_word_data(client, PORT_ECRAM, data_1);
+ //ENE_DEBUG("kb3930 Write: ID:%02x Data:%04x\n", cmd, data_1);
+ } else if (count) {
+ /* Read ECRAM test */
+ data_1 = (u16) simple_strtoul(buf,NULL,16);
+ ret = i2c_smbus_write_word_data(client, PORT_ECRAM|0x01, data_1);
+ data_1 = i2c_smbus_read_word_data(client, PORT_ECRAM);
+ //ENE_DEBUG("kb3930: R:%04x\n", data_1);
+ }
+
+ return count;
+}
+
+static ssize_t battery_RSOC(struct device *dev, struct device_attribute
+ *devattr, char *buf) {
+ struct eneec *eneec = i2c_get_clientdata(ene_client);
+ eneec_register_update(RSOC);
+ eneec->capacity_P = ecram_val[RSOC];
+ return sprintf(buf, "%d %%\n", eneec->capacity_P);
+}
+static ssize_t battery_remain_cap(struct device *dev, struct device_attribute
+ *devattr, char *buf) {
+ struct eneec *eneec = i2c_get_clientdata(ene_client);
+ eneec_register_update(CAP_REMAIN_L);
+ eneec_register_update(CAP_REMAIN_H);
+ eneec->current_mAh = ecram_val[CAP_REMAIN_L]+ecram_val[CAP_REMAIN_H]*256;
+ return sprintf(buf, "%d mAh\n", eneec->current_mAh);
+}
+static ssize_t battery_full_cap(struct device *dev, struct device_attribute
+ *devattr, char *buf) {
+ struct eneec *eneec = i2c_get_clientdata(ene_client);
+ eneec_register_update(CAP_FULL_L);
+ eneec_register_update(CAP_FULL_H);
+ eneec->absolute_mAh = ecram_val[CAP_FULL_L]+ecram_val[CAP_FULL_H]*256;
+ return sprintf(buf, "%d mAh\n", eneec->absolute_mAh);
+}
+static ssize_t battery_design_cap(struct device *dev, struct device_attribute
+ *devattr, char *buf) {
+ struct eneec *eneec = i2c_get_clientdata(ene_client);
+ return sprintf(buf, "%d mAh\n", eneec->design_mAh);
+}
+static ssize_t ec_LID(struct device *dev, struct device_attribute
+ *devattr, char *buf) {
+ struct eneec *eneec = i2c_get_clientdata(ene_client);
+ eneec_register_update(LID_SWITCH);
+ return sprintf(buf, "LID switch %s (%d)\n", (ecram_val[LID_SWITCH]!=0)? "ON":"OFF",ecram_val[LID_SWITCH]);
+}
+static ssize_t ec_ac_line(struct device *dev, struct device_attribute
+ *devattr, char *buf) {
+ struct eneec *eneec = i2c_get_clientdata(ene_client);
+ eneec_register_update(AC_IN);
+ return sprintf(buf, "AC line status : %s AC IN (%d)\n", (ecram_val[AC_IN]!=0)? "":"NO",ecram_val[AC_IN]);
+}
+
+static ssize_t ec_version(struct device *dev, struct device_attribute
+ *devattr, char *buf) {
+ int count=0;
+ switch (ecram_val[MB_TYPE]){
+ case 'M':
+ count = sprintf(buf, "MIMAS Board ");
+ break;
+ case 'T':
+ count = sprintf(buf, "TITAN+ Board ");
+ break;
+ case 'I':
+ count = sprintf(buf, "TITAN12 Board ");
+ break;
+ case 'A':
+ count = sprintf(buf, "ARIEL Board ");
+ break;
+ default:
+ count = sprintf(buf, "UNKNOW Board ");
+ }
+
+ switch (ecram_val[VERSION_1]& 0x0f) {
+ case 1:
+ count += sprintf(buf + count, "EVT Build ");
+ break;
+ case 2:
+ count += sprintf(buf + count, "DVT Build ");
+ break;
+ case 3:
+ count += sprintf(buf + count, "PVT Build ");
+ break;
+ case 4:
+ count += sprintf(buf + count, "MP Build ");
+ break;
+ }
+ count += sprintf(buf + count, "Ver: %d.%d \n",((ecram_val[VERSION_2]& 0xf0)>>4),(ecram_val[VERSION_2]& 0x0f));
+
+ return count;
+}
+
+BATTERY_DEVICE_ATTR(capacity_P, S_IRUGO, battery_RSOC, NULL,0);
+BATTERY_DEVICE_ATTR(capacity_remain, S_IRUGO, battery_remain_cap, NULL,0);
+BATTERY_DEVICE_ATTR(capacity_full, S_IRUGO, battery_full_cap, NULL,0);
+BATTERY_DEVICE_ATTR(capacity_design, S_IRUGO, battery_design_cap, NULL,0);
+BATTERY_DEVICE_ATTR(LID, S_IRUGO, ec_LID, NULL,0);
+BATTERY_DEVICE_ATTR(ac_line, S_IRUGO, ec_ac_line, NULL,0);
+BATTERY_DEVICE_ATTR(ec_ram, S_IRWXUGO, read_ec_ram_value, set_ec_ram_value ,0);
+BATTERY_DEVICE_ATTR(ec_version, S_IRUGO, ec_version, NULL ,0);
+
+static struct attribute *titan_ec_attributes[] = {
+ &battery_dev_attr_capacity_P.dev_attr.attr,
+ &battery_dev_attr_capacity_remain.dev_attr.attr,
+ &battery_dev_attr_capacity_full.dev_attr.attr,
+ &battery_dev_attr_capacity_design.dev_attr.attr,
+ &battery_dev_attr_LID.dev_attr.attr,
+ &battery_dev_attr_ac_line.dev_attr.attr,
+ &battery_dev_attr_ec_ram.dev_attr.attr,
+ &battery_dev_attr_ec_version.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group titan_ec_group = {
+ .attrs = titan_ec_attributes,
+};
+
+static struct attribute *mimas_ec_attributes[] = {
+ &battery_dev_attr_ec_ram.dev_attr.attr,
+ &battery_dev_attr_ec_version.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group mimas_ec_group = {
+ .attrs = mimas_ec_attributes,
+};
+
+static struct attribute *ariel_ec_attributes[] = {
+ &battery_dev_attr_ec_ram.dev_attr.attr,
+ &battery_dev_attr_ec_version.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group ariel_ec_group = {
+ .attrs = ariel_ec_attributes,
+};
+
+static int eneec_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct eneec *eneec = 0;
+ int err = 0;
+ s32 data;
+ int retry;
+
+ pr_info("eneec_probe with name = %s, addr = 0x%x, irq = %d\n",
+ client->name, client->addr, client->irq);
+
+ eneec = kzalloc(sizeof(struct eneec), GFP_KERNEL);
+ if (eneec == NULL)
+ return -ENOMEM;
+
+ eneec->client = client;
+ i2c_set_clientdata(client, eneec);
+
+ if (machine_is_mimas()) {
+ eneec->ram_reg = mimas_ram_reg;
+ eneec->ram_reg_size = ARRAY_SIZE(mimas_ram_reg);
+ eneec->serio_over_i2c = 1;
+ }else if (machine_is_titan()){
+ eneec->ram_reg = titan_ram_reg;
+ eneec->ram_reg_size = ARRAY_SIZE(titan_ram_reg);
+ eneec->serio_over_i2c = 0;
+ }else if (machine_is_qseven()){
+ eneec->ram_reg = ariel_ram_reg;
+ eneec->ram_reg_size = ARRAY_SIZE(ariel_ram_reg);
+ eneec->serio_over_i2c = 0;
+ }
+
+ eneec->chip_id = id->driver_data >> 8;
+ eneec->rev_id = (unsigned char) id->driver_data;
+ eneec->is_37xx = ((eneec->chip_id & 0xFF00) == 0x3700);
+ ene_client = eneec->client;
+
+ // Drain old data out of device.
+ data = i2c_smbus_read_word_data(client, 0);
+ //ENE_DEBUG("Drain data : 0x%04x\n", (u16) data);
+ for (retry = 0; retry < 9; retry++) {
+ eneec->toggle = i2c_smbus_read_word_data(client , 0);
+ if (eneec->toggle == data) // no new data
+ break;
+ else {
+ printk("Drain data : 0x%04x\n", (u16) eneec->toggle);
+ data = eneec->toggle;
+ }
+ }
+ eneec->toggle = data & 1;
+// eneec_register_update_all();
+
+ if ((eneec->work_queue = create_singlethread_workqueue("eneec"))) {
+#ifdef POLLING
+ INIT_DELAYED_WORK(&eneec->read_work, eneec_read_work);
+ queue_delayed_work(eneec->work_queue, &eneec->read_work,
+ msecs_to_jiffies(POLL_INTERVAL));
+#else
+ INIT_WORK(&eneec->read_work, eneec_read_work);
+#endif
+ } else
+ goto error_exit;
+
+#ifndef POLLING
+ if (eneec->serio_over_i2c) {
+ unsigned long val = 0;
+ err = request_irq(client->irq, eneec_interrupt,
+ IRQF_TRIGGER_RISING, "eneec", client );
+ if (err) {
+ pr_err("Failed to request IRQ %d -- %d\n", client->irq, err);
+ goto error_exit;
+ }
+ printk ("IRQ reuest finish\n");
+// val = MFPR_EDGE_RISE_EN ;
+// lpm_mfpr_edge_detect_config(MFP_PIN_GPIO60, val);
+ }
+
+ eneec->irq = client->irq;
+#endif
+
+ if (eneec->serio_over_i2c) {
+ if ((err = eneec_create_kbd_port(eneec)))
+ goto error_exit;
+
+ serio_register_port(eneec->kbd_port.serio);
+
+ /*
+ if ((err = eneec_create_mouse_port(eneec)))
+ goto error_exit;
+
+ serio_register_port(eneec->mou_port.serio);
+ */
+ }
+
+ if ((err = eneec_create_cdev_node(eneec)))
+ goto error_exit;
+
+ /* create the sysfs file for ec_ram_write */
+ if (machine_is_mimas())
+ err = sysfs_create_group(&client->dev.kobj, &mimas_ec_group);
+
+ if (machine_is_titan()) {
+ eneec_probe_battery(eneec);
+ err = sysfs_create_group(&client->dev.kobj, &titan_ec_group);
+ }
+
+ if (machine_is_qseven())
+ err = sysfs_create_group(&client->dev.kobj, &ariel_ec_group);
+
+ return err;
+error_exit:
+ if (eneec->serio_over_i2c) {
+ if (eneec->mou_port.serio)
+ serio_unregister_port(eneec->mou_port.serio);
+ if (eneec->kbd_port.serio)
+ serio_unregister_port(eneec->kbd_port.serio);
+ if (eneec->irq)
+ free_irq(eneec->irq, client);
+ }
+
+ if (eneec && eneec->work_queue) {
+#ifdef POLLING
+ eneec->stop_polling = true;
+ cancel_delayed_work_sync(&eneec->read_work);
+#endif
+ destroy_workqueue(eneec->work_queue);
+ }
+
+ if (eneec)
+ kfree(eneec);
+
+ return err;
+}
+
+static int eneec_remove(struct i2c_client *client)
+{
+ struct eneec *eneec = i2c_get_clientdata(client);
+
+ eneec_destroy_cdev_node(eneec);
+ eneec_remove_battery(eneec);
+
+ if (machine_is_mimas())
+ sysfs_remove_group(&client->dev.kobj, &mimas_ec_group);
+ if (machine_is_titan())
+ sysfs_remove_group(&client->dev.kobj, &titan_ec_group);
+ if (machine_is_qseven())
+ sysfs_remove_group(&client->dev.kobj, &ariel_ec_group);
+
+ if (eneec->serio_over_i2c) {
+ if (eneec->mou_port.serio)
+ serio_unregister_port(eneec->mou_port.serio);
+
+ if (eneec->kbd_port.serio)
+ serio_unregister_port(eneec->kbd_port.serio);
+
+ if (eneec->irq)
+ free_irq(eneec->irq, client);
+ }
+
+ if (eneec->work_queue) {
+#ifdef POLLING
+ eneec->stop_polling = true;
+ cancel_delayed_work_sync(&eneec->read_work);
+#endif
+ destroy_workqueue(eneec->work_queue);
+ }
+
+ kfree(eneec);
+ return 0;
+}
+
+static int eneec_suspend(struct i2c_client *client)
+{
+ struct eneec *eneec = i2c_get_clientdata(client);
+
+ if (enable_irq_wake(eneec->irq/*273*/) == 0) {
+ unsigned long val = 0;
+ val = MFPR_SLEEP_OE_N
+ | MFPR_EDGE_FALL_EN | MFPR_SLEEP_SEL;
+ lpm_mfpr_edge_detect_config(MFP_PIN_GPIO60, val);
+ }
+ return 0;
+#if 0
+ struct eneec *eneec = i2c_get_clientdata(ene_client);
+ struct eneec_device_info *di = eneec->battery_info;
+ s32 data;
+ int retry;
+
+ data = i2c_smbus_read_word_data(client, 0);
+ ENE_DEBUG("Drain data : 0x%04x\n", (u16) data);
+ for (retry = 0; retry < 9; retry++) {
+ eneec->toggle = i2c_smbus_read_word_data(client , 0);
+ if (eneec->toggle == data) // no new data
+ break;
+ else {
+ ENE_DEBUG("Drain data : 0x%04x\n", (u16) eneec->toggle);
+ data = eneec->toggle;
+ }
+ }
+ eneec->toggle = data & 1;
+
+ di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;
+ return 0;
+#endif
+}
+
+static int eneec_resume(struct i2c_client *client)
+{
+ //if (enable_irq_wake(ts->irq) == 0) {
+
+ lpm_mfpr_edge_detect_clear_config(MFP_PIN_GPIO60);
+ return 0;
+
+}
+
+
+#ifdef EC_BATTERY_SUPPORT
+static void eneec_battery_external_power_changed(struct power_supply *psy)
+{
+ struct eneec_device_info *di = to_eneec_device_info(psy);
+
+ dev_dbg(di->dev, "%s\n", __func__);
+
+ cancel_delayed_work(&di->monitor_work);
+ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ/10);
+}
+
+static int eneec_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct eneec_device_info *di = to_eneec_device_info(psy);
+
+ eneec_battery_read_status(di);
+
+ /*
+ * All voltages, currents, charges, energies, time and temperatures in uV,
+ * µA, µAh, µWh, seconds and tenths of degree Celsius
+ */
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = 1;
+ break;
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = di->charge_status;
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ /* convert Kelvin to tenths of degree Celsius */
+ val->intval = (di->temp_tK - 2730);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = (di->current_mV * 1000);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = (di->current_mA * 1000);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_AVG:
+ val->intval = (di->average_mA * 1000);
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = di->capacity_P;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ val->intval = (di->current_mAh * 1000);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ val->intval = (di->absolute_mAh * 1000);
+ break;
+ case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
+ val->intval = (di->current_empty_time_Min * 60);
+ break;
+ case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
+ val->intval = (di->average_empty_time_Min * 60);
+ break;
+ case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
+ val->intval = (di->average_full_time_Min * 60);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ val->intval = (di->design_mAh * 1000);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static enum power_supply_property eneec_battery_props[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CURRENT_AVG,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
+ POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+};
+
+static int eneec_probe_battery(struct eneec *eneec)
+{
+ int retval = 0;
+ struct eneec_device_info *di;
+
+ di = kzalloc(sizeof(struct eneec_device_info), GFP_KERNEL);
+ if (!di) {
+ retval = -ENOMEM;
+ goto di_alloc_failed;
+ }
+
+ eneec->battery_info = di;
+
+ di->dev = &eneec->client->dev;
+ di->update_time = jiffies;
+ di->bat.name = "eneec-battery";
+ di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
+ di->bat.properties = eneec_battery_props;
+ di->bat.num_properties = ARRAY_SIZE(eneec_battery_props);
+ di->bat.get_property = eneec_battery_get_property;
+ di->bat.external_power_changed = eneec_battery_external_power_changed;
+
+ di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;
+
+ retval = power_supply_register(&eneec->client->dev, &di->bat);
+ if (retval) {
+ dev_err(di->dev, "failed to register battery\n");
+ goto batt_failed;
+ }
+
+ INIT_DELAYED_WORK(&di->monitor_work, eneec_battery_work);
+ di->monitor_wqueue = create_singlethread_workqueue("eneec-battery");
+ if (!di->monitor_wqueue) {
+ retval = -ESRCH;
+ goto workqueue_failed;
+ }
+ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ * 1);
+
+ battery_proc_init();
+
+ di_proc = di;
+
+ pr_info("Eneec Power Supply driver loaded successfully. \n");
+ return retval;
+
+workqueue_failed:
+ power_supply_unregister(&di->bat);
+batt_failed:
+ kfree(di);
+di_alloc_failed:
+success:
+ return retval;
+}
+
+static int eneec_remove_battery(struct eneec *eneec)
+{
+ struct eneec_device_info *di = eneec->battery_info;
+
+ cancel_rearming_delayed_workqueue(di->monitor_wqueue,
+ &di->monitor_work);
+ destroy_workqueue(di->monitor_wqueue);
+ power_supply_unregister(&di->bat);
+
+ return 0;
+}
+#endif /*EC_BATTERY_SUPPORT */
+
+static struct i2c_driver eneec_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "eneec",
+ },
+ .id_table = eneec_idtable,
+ .probe = eneec_probe,
+ .remove = eneec_remove,
+ .suspend = eneec_suspend,
+ .resume = eneec_resume,
+};
+
+static int __init eneec_init(void)
+{
+ return i2c_add_driver(&eneec_driver);
+}
+
+static void __exit eneec_exit(void)
+{
+ i2c_del_driver(&eneec_driver);
+}
+
+module_init(eneec_init);
+module_exit(eneec_exit);
diff --git a/drivers/input/serio/eneec_ioc.h b/drivers/input/serio/eneec_ioc.h
new file mode 100644
index 00000000000000..b35c955f1f7a61
--- /dev/null
+++ b/drivers/input/serio/eneec_ioc.h
@@ -0,0 +1,55 @@
+//
+// Copyright (C) ENE TECHNOLOGY INC. 2009
+
+#ifndef _ENEEC_IOC_H_
+#define _ENEEC_IOC_H_
+
+#define ENEEC_IOC_MAGIC ('e' + 0x80)
+
+#define ENEEC_IOC_GET_CHIPID _IOR (ENEEC_IOC_MAGIC, 1, unsigned long) // *arg=output chipIdL-chipIdH-revId.
+#define ENEEC_IOC_ENTER_CODE_IN_RAM _IO (ENEEC_IOC_MAGIC, 2)
+#define ENEEC_IOC_EXIT_CODE_IN_RAM _IO (ENEEC_IOC_MAGIC, 3)
+#define ENEEC_IOC_READ_REG _IOWR(ENEEC_IOC_MAGIC, 11, unsigned short) // *arg=input regL-regH, also, =output byte.
+#define ENEEC_IOC_WRITE_REG _IOW (ENEEC_IOC_MAGIC, 12, unsigned long) // *arg=input regL-regH-byte.
+
+#define LID_SWITCH 0x23 //LID state
+#define AC_IN 0x24 //AC IN/OUT state
+#define MB_TYPE 0x30 //Mimas mother board = 'M' Titan MB ='T'
+#define VERSION_1 0x31
+#define VERSION_2 0x32
+#define RSOC 0x40 //Relative state of charge(%)
+#define CHARGE_C_L 0x41 //Charge Current Low byte(mA)
+#define CHARGE_C_H 0x42 //Charge Current High byte(mA)
+#define CHARGE_V_L 0x43 //Charge Voltage Low byte(mV)
+#define CHARGE_V_H 0x44 //Charge Voltage High byte(mV)
+#define CURRENT_L 0x49 //Current Low byte(mA)
+#define CURRENT_H 0x4A //Current High byte(mA)
+#define VOLTAGE_L 0x4B //Voltage Low byte(mV)
+#define VOLTAGE_H 0x4C //Voltage High byte(mV)
+#define CAP_REMAIN_L 0x4D //Remaining Capacity Low Byte(mAh)
+#define CAP_REMAIN_H 0x4E //Remaining Capacity High Byte(mAH)
+#define TEMP_C 0x51 //Temperature tC.
+#define MANUFACTURE_L 0x52 //Manufacture Access Low Byte
+#define MANUFACTURE_H 0x53 //Manufacture Access High Byte
+#define BATTERY_MODE_L 0x54 //Battery mode Low Byte
+#define BATTERY_MODE_H 0x55 //Battery mode High Byte
+#define AVG_CURRENT_L 0x56 //Average Current Low byte(mA)
+#define AVG_CURRENT_H 0x57 //Average Current High byte(mA)
+#define ASOC 0x5A //Absolute State of charge(%)
+#define CAP_FULL_L 0x5C //FULL Charge Capacity Low Byte(mAh)
+#define CAP_FULL_H 0x5D //Full Charge Capacity High Byte(mAh)
+#define CELL_1_V_L 0x60 //Cell 1 Voltage Low byte(mV)
+#define CELL_1_V_H 0x61 //Cell 1 Voltage High byte(mV)
+#define CELL_2_V_L 0x62 //Cell 2 Voltage Low byte(mV)
+#define CELL_2_V_H 0x63 //Cell 2 Voltage High byte(mV)
+#define CELL_3_V_L 0x64 //Cell 3 Voltage Low byte(mV)
+#define CELL_3_V_H 0x65 //Cell 3 Voltage High byte(mV)
+#define CELL_4_V_L 0x66 //Cell 4 Voltage Low byte(mV)
+#define CELL_4_V_H 0x67 //Cell 4 Voltage High byte(mV)
+#define MAX_ERROR_L 0x68
+#define MAX_ERROR_H 0x69
+#define CAP_DESIGN_L 0x74 //Design Capacity Low Byte(mAh)
+#define CAP_DESIGN_H 0x75 //Design Capacity High Byte(mAH)
+
+
+#endif // _ENEEC_IOC_H_
diff --git a/drivers/input/serio/eneec_spi.c b/drivers/input/serio/eneec_spi.c
new file mode 100644
index 00000000000000..a7cc208c143ae1
--- /dev/null
+++ b/drivers/input/serio/eneec_spi.c
@@ -0,0 +1,610 @@
+
+/*
+ * ENE EC SPI driver for Linux
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <asm/uaccess.h>
+#include <linux/spi/spi.h>
+#include "eneec_ioc.h"
+
+#define DRIVER_DESC "ENEEC SPI driver"
+
+MODULE_AUTHOR("Victoria/flychen");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+//#define POLLING
+#define POLL_INTERVAL 100
+
+/*
+ * Be sure system is added with one SPI board info at mainboard arch_initcall() code, for example,
+ *
+ * static struct spi_board_info mini2440_spi_board_info[] __initdata = {
+ * { .modalias = "eneec",
+ * .platform_data = NULL,
+ * .irq = IRQ_EINT8, // irq used for EC depending on customer platform.
+ * .max_speed_hz = 1*1000*1000,
+ * .chip_select = 0, // chip-select for EC.
+ * .bus_num = 0, // SPI bus number EC attached to.
+ * .mode = 0
+ * },
+ * ...
+ * };
+ *
+ * static void __init mini2440_machine_init()
+ * {
+ * spi_register_board_info(mini2440_spi_board_info, ARRAY_SIZE(mini2440_spi_board_info));
+ * }
+ */
+
+#define PORT_KBD 0x30
+#define PORT_MOU 0x40
+#define PORT_XBI 0xF0
+#define DATA_TO_PORT(data) (data & 0x70)
+
+struct eneec_port {
+ struct serio *serio;
+ struct eneec *eneec;
+ unsigned char port_no; // PORT_KBD or PORT_MOU.
+};
+
+#pragma pack(1)
+struct rspdata {
+ u8 resv;
+ u8 toggle : 2;
+ u8 count : 2;
+ u8 type : 4; // RSP_XXX
+ u8 data[3];
+};
+#pragma pack()
+
+enum { RSP_UNKNOWN = 0,
+ RSP_WRITE_COMPLETE = 1,
+ RSP_READ = 2,
+ RSP_KB_RESPONSE = 3,
+ RSP_AUX_RESPONSE = 4,
+ RSP_PORT_RESPONSE = 5,
+ RSP_KB_DATA = 12,
+ RSP_AUX_DATA = 13,
+ RSP_PORT_DATA = 14,
+ RSP_RESET = 15,
+};
+
+struct eneec {
+ struct spi_device *client;
+ struct mutex lock;
+ int irq; // don't remove it although also in client->irq. for indicating if irq requested.
+ unsigned short chip_id;
+ unsigned char rev_id;
+ struct workqueue_struct *work_queue;
+#ifdef POLLING
+ struct delayed_work read_work;
+ bool stop_polling;
+#else
+ struct work_struct read_work;
+#endif
+ struct eneec_port kbd_port;
+ struct eneec_port mou_port;
+ int toggle;
+ bool is_37xx;
+
+// For exposing our userspace API.
+ // The main reason to have this class is to make mdev/udev create the
+ // /dev/eneec character device nodes exposing our userspace API.
+ struct class *eneec_class;
+ struct cdev cdev;
+ dev_t devt;
+// ~
+};
+
+static int eneec_create_kbd_port(struct eneec *eneec);
+static int eneec_create_mouse_port(struct eneec *eneec);
+static int eneec_spi_create_cdev_node(struct eneec *eneec);
+static int eneec_read_rspdata(struct eneec *eneec, struct rspdata *rspdata);
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Utilities
+
+static int eneec_read_rspdata(struct eneec *eneec, struct rspdata *rspdata)
+{
+#define RSP_LEN 5
+ u8 tx_buf[RSP_LEN];
+ int ret;
+ int i;
+ struct spi_device *spi = eneec->client;
+ struct spi_transfer t =
+ {
+ .tx_buf = tx_buf,
+ .rx_buf = rspdata,
+ .len = RSP_LEN,
+ };
+ struct spi_message m;
+
+ tx_buf[0] = 0x00; // Read command
+ tx_buf[1] = 0x5A; // Rx1
+ tx_buf[2] = 0xA5; // Rx2
+ tx_buf[3] = 0x00; // Rx3
+ tx_buf[4] = 0x00; // dummy
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+
+ mutex_lock(&eneec->lock);
+ ret =spi_sync(spi, &m);
+ mutex_unlock(&eneec->lock);
+
+ if (ret < 0) {
+ pr_err("ERROR: spi_sync fail\n");
+ return ret;
+ }
+
+#if 0
+ if (rspdata->type == 12) {
+ pr_debug("type = %d, toggle = %d, count = %d\n", rspdata->type, rspdata->toggle, rspdata->count);
+ pr_debug("data = ");
+ for (i = 0; i < rspdata->count; i++)
+ pr_debug("%02x ", rspdata->data[i]);
+ pr_debug("\n");
+ }
+#endif
+
+ return 0;
+}
+
+static long eneec_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ return 0;
+}
+
+static int eneec_open(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+static int eneec_release(struct inode *inode, struct file *filp)
+{
+ pr_debug("eneec_spi_release()\n");
+ return 0;
+}
+
+static const struct file_operations eneec_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = eneec_ioctl,
+ .open = eneec_open,
+ .release = eneec_release,
+};
+
+//
+// Undo eneec_create_cdev_node().
+// Call this only if the char device node was ever created successfully.
+void eneec_spi_destroy_cdev_node(struct eneec *eneec)
+{
+ device_destroy(eneec->eneec_class, eneec->devt);
+ cdev_del(&eneec->cdev);
+ unregister_chrdev_region(eneec->devt, 1);
+ class_destroy(eneec->eneec_class);
+}
+
+int eneec_spi_create_cdev_node(struct eneec *eneec)
+{
+ int status;
+ dev_t devt;
+ struct device *dev;
+ struct class *eneec_class;
+ bool is_class_created = false, is_region_allocated = false, is_cdev_added = false, is_device_created = false;
+
+
+ pr_debug("eneec_spi_create_cdev_node .. \n");
+
+ // Create class
+ eneec_class = class_create(THIS_MODULE, "eneec_spi");
+ status = IS_ERR(eneec_class) ? PTR_ERR(eneec_class) : 0;
+ if (status < 0) {
+ pr_err("eneec_SPI class_create() failed -- %d\n", status);
+ goto error_exit;
+ }
+ is_class_created = true;
+
+ // Alloc chrdev region.
+ status = alloc_chrdev_region(&devt, 0, 1, "eneec_spi");
+ if(status < 0) {
+ pr_err("eneec_spi alloc_chrdev_region() failed -- %d\n", status);
+ goto error_exit;
+ }
+ is_region_allocated = true;
+
+ // Add cdev.
+ cdev_init(&eneec->cdev, &eneec_fops);
+ status = cdev_add(&eneec->cdev, devt, 1);
+ if(status < 0) {
+ pr_err("cdev_add() failed -- %d\n", status);
+ goto error_exit;
+ }
+ is_cdev_added = true;
+
+ // Create device
+ dev = device_create
+ (
+ eneec_class,
+ &eneec->client->dev, // parent device (struct device *)
+ devt,
+ eneec, // caller's context
+ "eneec_spi"
+ );
+ status = IS_ERR(dev) ? PTR_ERR(dev) : 0;
+ if (status < 0) {
+ pr_err("device_create() failed -- %d\n", status);
+ goto error_exit;
+ }
+ is_device_created = true;
+
+ // Succeed.
+ eneec->eneec_class = eneec_class;
+ eneec->devt = devt;
+
+ return 0;
+
+error_exit:
+
+ if(is_device_created)
+ device_destroy(eneec_class, devt);
+ if(is_cdev_added)
+ cdev_del(&eneec->cdev);
+ if(is_region_allocated)
+ unregister_chrdev_region(devt, 1);
+ if(is_class_created)
+ class_destroy(eneec_class);
+
+ return status;
+
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Codes for interfacing with kernel kbd, mouse.
+
+static void eneec_read_work(struct work_struct *work)
+{
+#ifdef POLLING
+ struct eneec *eneec = container_of(work, struct eneec, read_work.work);
+#else
+ struct eneec *eneec = container_of(work, struct eneec, read_work);
+#endif
+
+ int i;
+ struct rspdata rspdata;
+
+ if(eneec_read_rspdata(eneec, &rspdata) < 0) {
+ pr_err("ERROR: eneec_read_rspdata fail \n");
+ goto exit_enable_irq;
+ }
+
+ if (eneec->toggle == rspdata.toggle) {
+ printk("WARNING: got the same toggle bit %d\n", rspdata.toggle);
+ goto exit_enable_irq;
+ }
+
+ eneec->toggle = rspdata.toggle;
+
+ if ((rspdata.type == RSP_KB_RESPONSE) || (rspdata.type == RSP_KB_DATA)) {
+ if(eneec->kbd_port.serio) { // interrupt might assert before probe() done.
+ for (i = 0; i < rspdata.count; i++) {
+ printk("scan code %02x\n",rspdata.data[i]);
+ serio_interrupt(eneec->kbd_port.serio, rspdata.data[i], 0);
+ }
+ }
+ } else if ((rspdata.type == RSP_AUX_RESPONSE) || (rspdata.type == RSP_AUX_DATA)) {
+ if(eneec->mou_port.serio) { // interrupt might assert before probe() done.
+ for (i = 0; i < rspdata.count; i++) {
+ serio_interrupt(eneec->mou_port.serio, rspdata.data[i], 0);
+ }
+ }
+ }
+ else {
+ pr_warning( "WARNING: Not mou or kbd data\n");
+ goto exit_enable_irq;
+ }
+
+exit_enable_irq:
+
+#ifdef POLLING
+ if(!eneec->stop_polling)
+ queue_delayed_work(eneec->work_queue, &eneec->read_work, msecs_to_jiffies(POLL_INTERVAL));
+#else
+ enable_irq(eneec->irq);
+#endif
+}
+
+#ifndef POLLING
+
+static irqreturn_t eneec_interrupt(int irq, void *dev_id)
+{
+ struct spi_device *spi = dev_id;
+ struct eneec *eneec = spi_get_drvdata(spi);
+
+ disable_irq_nosync(irq);
+
+ queue_work(eneec->work_queue, &eneec->read_work);
+
+ return IRQ_HANDLED;
+}
+
+#endif
+
+static int eneec_write(struct serio *serio, unsigned char byte)
+{
+#define WRITE_LEN 4
+ u8 portno;
+ u8 tx_buf[WRITE_LEN] = {0,0,0,0};
+ u8 rx_buf[WRITE_LEN] = {0,0,0,0};
+ int ret;
+ struct spi_transfer t =
+ {
+ .tx_buf = tx_buf,
+ .rx_buf = rx_buf,
+ .len = WRITE_LEN,
+ };
+ struct eneec_port *port = serio->port_data;
+ struct eneec *eneec = port->eneec;
+ struct spi_device *spi = eneec->client;
+ struct spi_message m;
+
+ portno = port->port_no;
+
+ if(portno == PORT_KBD) {
+ tx_buf[0] = 0x30; // write kbd
+ }
+ else if(portno == PORT_MOU) {
+ tx_buf[0] = 0x40; // write aux
+ mdelay(2);
+ }
+ else {
+ pr_err("ERROR: eneec_write with bad port no\n");
+ return -1;
+ }
+
+ tx_buf[1] = byte;
+ tx_buf[2] = 0x00; // dummy
+ tx_buf[3] = 0x00; // dummy
+
+//TEST
+//tx_buf[0] = 0x5d; // dummy
+//tx_buf[1] = 0x00; // dummy
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+
+ mutex_lock(&eneec->lock);
+ ret = spi_sync(spi, &m);
+ mutex_unlock(&eneec->lock);
+
+ if (ret < 0) {
+ pr_err("ERROR: eneec_write() fail ..\n");
+ return ret;
+ }
+
+#if 0
+ printk( KERN_EMERG "tx[0] = %02x, tx[1] = %02x, tx[2] = %02x, tx[3] = %02x\n", tx_buf[0], tx_buf[1], tx_buf[2], tx_buf[3]);
+ printk( KERN_EMERG "rx[0] = %02x, rx[1] = %02x, rx[2] = %02x, rx[3] = %02x\n", rx_buf[0], rx_buf[1], rx_buf[2], rx_buf[3]);
+#endif
+
+ return 0;
+}
+
+static int eneec_create_kbd_port(struct eneec *eneec)
+{
+ struct spi_device *client = eneec->client;
+ struct serio *serio;
+
+ serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+ if (!serio)
+ return -ENOMEM;
+
+ serio->id.type = SERIO_8042_XL;
+ serio->write = eneec_write;
+ serio->port_data = &eneec->kbd_port;
+ serio->dev.parent = &client->dev;
+ strlcpy(serio->name, "eneec spi kbd port", sizeof(serio->name));
+ strlcpy(serio->phys, "eneec_spi/serio0", sizeof(serio->phys));
+
+ eneec->kbd_port.serio = serio;
+ eneec->kbd_port.port_no = PORT_KBD;
+ eneec->kbd_port.eneec = eneec;
+
+ return 0;
+
+}
+
+static int eneec_create_mouse_port(struct eneec *eneec)
+{
+ struct serio *serio;
+ struct spi_device *client = eneec->client;;
+
+ serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+ if (!serio)
+ return -ENOMEM;
+
+ serio->id.type = SERIO_PS_PSTHRU;//SERIO_8042;
+ serio->write = eneec_write;
+ serio->port_data = &eneec->mou_port;
+ serio->dev.parent = &client->dev;
+ strlcpy(serio->name, "eneec spi mouse port", sizeof(serio->name));
+ strlcpy(serio->phys, "eneec_spi/serio1", sizeof(serio->phys));
+
+ eneec->mou_port.serio = serio;
+ eneec->mou_port.port_no = PORT_MOU;
+ eneec->mou_port.eneec = eneec;
+
+ return 0;
+}
+
+static int eneec_probe(struct spi_device *spi)
+{
+ struct eneec *eneec = 0;
+ struct rspdata rspdata;
+ int err = 0 , retry = 0;
+
+ printk("eneec_spi_probe with modalias = %s, irq = %d\n", spi->modalias, spi->irq);
+
+ if(spi->irq <= 0) {
+ pr_err("ERROR: spi->irq is not specified.\n");
+ return -ENXIO;
+ }
+
+ if (!(eneec = kzalloc(sizeof(struct eneec), GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ eneec->client = spi;
+ spi_set_drvdata(spi, eneec);
+ mutex_init(&eneec->lock);
+
+ // Read rspdata to sync toggle bit.
+ if(eneec_read_rspdata(eneec, &rspdata) < 0) {
+ printk("eneec_spi_read_rspdata fail \n");
+ goto error_exit;
+ }
+ eneec->toggle = rspdata.toggle;
+ printk("Drain data : 0x%04x\n", (u16) rspdata.data[1]);
+ for(retry = 0; retry < 9; retry++) {
+ eneec_read_rspdata(eneec, &rspdata);
+ if(eneec->toggle == rspdata.toggle) // no new data
+ break;
+ else {
+ printk("Drain data : 0x%04x\n", (u16) rspdata.data[1]);
+ eneec->toggle=rspdata.toggle;
+ }
+ }
+
+ if((eneec->work_queue = create_singlethread_workqueue("eneec_spi"))) {
+#ifdef POLLING
+ INIT_DELAYED_WORK(&eneec->read_work, eneec_read_work);
+ queue_delayed_work(eneec->work_queue, &eneec->read_work, msecs_to_jiffies(POLL_INTERVAL));
+#else
+ INIT_WORK(&eneec->read_work, eneec_read_work);
+#endif
+ } else
+ goto error_exit;
+
+
+#ifndef POLLING
+ if ((err = request_irq(spi->irq, eneec_interrupt, IRQF_TRIGGER_RISING , "eneec_spi", spi ))) {
+ printk("Failed to request IRQ %d -- %d\n", spi->irq, err);
+ goto error_exit;
+ } else {
+ printk("IRQ %d requested\n", spi->irq);
+ eneec->irq = spi->irq;
+ }
+#endif
+
+ if ((err = eneec_create_kbd_port(eneec)))
+ goto error_exit;
+
+ serio_register_port(eneec->kbd_port.serio);
+
+ if ((err = eneec_create_mouse_port(eneec)))
+ goto error_exit;
+
+ serio_register_port(eneec->mou_port.serio);
+
+ if ((err = eneec_spi_create_cdev_node(eneec)))
+ goto error_exit;
+
+ // add by allen for test
+ //eneec_read_rspdata(eneec, &rspdata);
+
+ return 0;
+
+error_exit:
+
+ if(eneec && eneec->irq) // deregister irq first, so safe once if kbd/mou is sending data.
+ free_irq(eneec->irq, spi);
+
+ if(eneec && eneec->mou_port.serio) {
+ serio_unregister_port(eneec->mou_port.serio);
+ }
+
+ if(eneec && eneec->kbd_port.serio) {
+ serio_unregister_port(eneec->kbd_port.serio);
+ }
+
+ if(eneec && eneec->work_queue) {
+#ifdef POLLING
+ eneec->stop_polling = true;
+ cancel_delayed_work_sync(&eneec->read_work);
+#endif
+ destroy_workqueue(eneec->work_queue);
+ }
+
+ if(eneec)
+ kfree(eneec);
+
+ return err;
+
+}
+
+static int eneec_remove(struct spi_device *spi)
+{
+ struct eneec *eneec = spi_get_drvdata(spi);
+
+ pr_debug("eneec_spi_remove\n");
+
+ eneec_spi_destroy_cdev_node(eneec);
+
+ if(eneec->mou_port.serio) {
+ serio_unregister_port(eneec->mou_port.serio);
+ }
+ if(eneec->kbd_port.serio) {
+ serio_unregister_port(eneec->kbd_port.serio);
+ }
+ if(eneec->irq) {
+ free_irq(eneec->irq, spi);
+ }
+ if(eneec->work_queue) {
+#ifdef POLLING
+ eneec->stop_polling = true;
+ cancel_delayed_work_sync(&eneec->read_work);
+#endif
+ destroy_workqueue(eneec->work_queue);
+ }
+
+ kfree(eneec);
+
+ return 0;
+
+}
+
+static struct spi_driver eneec_driver = {
+ .driver = {
+ .name = "eneec_spi",
+ .owner = THIS_MODULE,
+ },
+ .probe = eneec_probe,
+ .remove = __devexit_p(eneec_remove),
+};
+
+static int __init eneec_init(void)
+{
+ printk("eneec_spi_init\n");
+
+ return spi_register_driver(&eneec_driver);
+}
+
+static void __exit eneec_exit(void)
+{
+ pr_debug("eneec_spi_exit\n");
+
+ spi_unregister_driver(&eneec_driver);
+}
+
+module_init(eneec_init);
+module_exit(eneec_exit);
diff --git a/drivers/rtc/rtc-idt1338.c b/drivers/rtc/rtc-idt1338.c
new file mode 100644
index 00000000000000..8e29a20e65bc16
--- /dev/null
+++ b/drivers/rtc/rtc-idt1338.c
@@ -0,0 +1,176 @@
+/*
+ * IDT1338 rtc class driver
+ *
+ * Copyright 2011 Saadia Husain Baloch <saadia at laptop.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/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/slab.h>
+
+#define RTC_SECONDS 0
+#define RTC_MINUTES 1
+#define RTC_HOURS 2
+#define RTC_DAY_OF_WEEK 3
+#define RTC_DATE 4
+#define RTC_MONTH 5
+#define RTC_YEAR 6
+#define BYTE_NUM 7
+
+static const struct i2c_device_id idt1338_id[] = {
+ { "rtc_idt1338", 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, idt1338_id);
+
+struct idt1338 {
+ struct rtc_device *rtc;
+};
+
+static int idt1338_get_time(struct i2c_client *client, struct rtc_time *tm)
+{
+ unsigned int year, month, date, hour, minute, second, week;
+ u8 buf[BYTE_NUM] = {0};
+ int err = 0;
+
+ err = i2c_master_send(client, buf, 1); /* Send 0 */
+ if (err < 0)
+ return (err);
+ err = i2c_master_recv(client, buf, BYTE_NUM);
+
+ second = buf[RTC_SECONDS] & 0x7f;
+ minute = buf[RTC_MINUTES];
+ hour = buf[RTC_HOURS] & 0xbf;
+ week = buf[RTC_DAY_OF_WEEK] & 0x07;
+ date = buf[RTC_DATE] & 0x3f;
+ month = buf[RTC_MONTH];
+ year = buf[RTC_YEAR];
+
+ tm->tm_sec = bcd2bin(second);
+ tm->tm_min = bcd2bin(minute);
+ tm->tm_hour = bcd2bin(hour);
+ tm->tm_wday = bcd2bin(week);
+ tm->tm_mday = bcd2bin(date);
+ tm->tm_mon = bcd2bin(month) - 1; /* read in 1-12 range */
+ tm->tm_year = 100 + bcd2bin(year); /* read delta from 2000 */
+
+ err = rtc_valid_tm(tm);
+ if (err < 0)
+ rtc_time_to_tm(0, tm);
+
+ return err;
+}
+
+static int idt1338_read_time(struct device *dev, struct rtc_time *tm)
+{
+ return idt1338_get_time(to_i2c_client(dev), tm);
+}
+
+static int idt1338_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ u8 buf[BYTE_NUM] = {0};
+ int err = 0;
+
+ err = i2c_master_send(client, buf, 1); /* Send 0 */
+ if (err < 0)
+ return (err);
+
+ buf[RTC_SECONDS] = bin2bcd(tm->tm_sec) & 0x7f;
+ buf[RTC_MINUTES] = bin2bcd(tm->tm_min) & 0x7f;
+ /* Retain 24 hour mode by keeping bit 6 of HOURS register low */
+ buf[RTC_HOURS] = bin2bcd(tm->tm_hour) & 0x3f;
+ buf[RTC_DAY_OF_WEEK] = bin2bcd(tm->tm_wday) & 0x07;
+ buf[RTC_DATE] = bin2bcd(tm->tm_mday) & 0x3f;
+ buf[RTC_MONTH] = bin2bcd(tm->tm_mon + 1) & 0x1f;
+ buf[RTC_YEAR] = bin2bcd((tm->tm_year > 100)?
+ tm->tm_year-100:tm->tm_year);
+
+ err = i2c_master_send(client, (char *)buf, BYTE_NUM);
+ return err;
+}
+
+static const struct rtc_class_ops idt1338_ops = {
+ .read_time = idt1338_read_time,
+ .set_time = idt1338_set_time,
+};
+
+static struct i2c_driver idt1338_driver;
+
+static int __devinit idt1338_probe(struct i2c_client *client,
+ const struct i2c_device_id *idp)
+{
+ struct idt1338 *idt1338;
+ int err = 0;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ idt1338 = kzalloc(sizeof(struct idt1338), GFP_KERNEL);
+ if (!idt1338)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, idt1338);
+
+ idt1338->rtc = rtc_device_register(idt1338_driver.driver.name,
+ &client->dev, &idt1338_ops,
+ THIS_MODULE);
+ if (IS_ERR(idt1338->rtc)) {
+ err = PTR_ERR(idt1338->rtc);
+ kfree (idt1338);
+ return err;
+ }
+ return 0;
+}
+
+static int idt1338_remove(struct i2c_client *client)
+{
+ struct idt1338 *idt1338 = i2c_get_clientdata(client);
+ if (idt1338->rtc)
+ rtc_device_unregister(idt1338->rtc);
+
+ kfree(idt1338);
+ return 0;
+}
+
+static struct i2c_driver idt1338_driver = {
+ .driver = {
+ .name = "rtc_idt1338",
+ },
+ .probe = idt1338_probe,
+ .remove = __devexit_p(idt1338_remove),
+ .id_table = idt1338_id,
+};
+
+static int __init idt1338_init(void)
+{
+ int err;
+
+ err = i2c_add_driver(&idt1338_driver);
+ if (err)
+ printk(KERN_ERR "IDT1338 RTC init err=%d\n", err);
+ return err;
+}
+
+static void __exit idt1338_exit(void)
+{
+ i2c_del_driver(&idt1338_driver);
+}
+
+MODULE_AUTHOR("Saadia Baloch <saadia at laptop.org");
+MODULE_DESCRIPTION("IDT 1338 RTC driver");
+MODULE_LICENSE("GPL");
+
+module_init(idt1338_init);
+module_exit(idt1338_exit);
diff --git a/include/linux/i2c/eneec.h b/include/linux/i2c/eneec.h
new file mode 100644
index 00000000000000..65135c5453c0f7
--- /dev/null
+++ b/include/linux/i2c/eneec.h
@@ -0,0 +1,11 @@
+#ifndef __ENEEC_H
+#define __ENEEC_H
+
+#define ENEEC_CAP_KEYBOARD (1 << 0)
+#define ENEEC_CAP_PS2MOUSE (1 << 1)
+
+struct eneec_platform_data {
+ unsigned int capabilities;
+};
+
+#endif /* __ENEEC_H */