misc: add a6 driver
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index bc34a90..6d030b3 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -748,5 +748,6 @@
 source "drivers/misc/carma/Kconfig"
 source "drivers/misc/altera-stapl/Kconfig"
 source "drivers/misc/slimport_anx7808/Kconfig"
+source "drivers/misc/a6/Kconfig"
 
 endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 2fbb839..9468462 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -82,3 +82,4 @@
 obj-$(CONFIG_AMP_TFA9887)	+= tfa9887.o
 obj-$(CONFIG_AMP_TFA9887L)	+= tfa9887l.o
 obj-$(CONFIG_AMP_RT5501)	+= rt5501.o
+obj-$(CONFIG_A6)		+= a6/
diff --git a/drivers/misc/a6/Kconfig b/drivers/misc/a6/Kconfig
new file mode 100644
index 0000000..8580695
--- /dev/null
+++ b/drivers/misc/a6/Kconfig
@@ -0,0 +1,47 @@
+menuconfig A6_SUPPORT
+	tristate "Palm A6 support"
+	depends on I2C
+	default n
+	help
+	  This option enables support for Palm A6 controller.
+
+	  To compile this driver as a module, choose M here.
+
+if A6_SUPPORT
+
+config A6
+	tristate "Palm A6 charging controller"
+	depends on I2C
+	default n
+	help
+	 Say Y to include support for the Palm A6 charging controller.
+
+config A6_I2C_SINGLE_BYTE
+	tristate "Select A6 i2c single byte read"
+	depends on I2C && A6
+	default n
+	help
+	 Say Y to enable single byte i2c read for A6 driver.
+
+config A6_I2C_SINGLE_BYTE_WRITE
+	tristate "Select A6 i2c single byte write"
+	depends on I2C && A6
+	default n
+	help
+	 Say Y to enable single byte i2c write for A6 driver.
+
+config A6_ENABLE_DOCK_PS
+	tristate "Select A6 enable dock as separate power supply"
+	depends on I2C && A6
+	default n
+	help
+	 Say Y to enable dock showing as separate power supply with A6 driver.
+
+config A6_BATTERY_SCALED_MIN
+	int "Scaled battery percentage"
+	depends on I2C && A6
+	default 0
+	help
+	 Scale A6 reported battery percent where min percent is reported as 0
+
+endif # POWER_SUPPLY
diff --git a/drivers/misc/a6/Makefile b/drivers/misc/a6/Makefile
new file mode 100644
index 0000000..9d5ccd0
--- /dev/null
+++ b/drivers/misc/a6/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_A6)		+= a6.o
+obj-$(CONFIG_A6)		+= high_level_funcs.o
+obj-$(CONFIG_A6)		+= low_level_funcs.o
+obj-$(CONFIG_A6)		+= jtag_funcs.o
diff --git a/drivers/misc/a6/a6.c b/drivers/misc/a6/a6.c
new file mode 100644
index 0000000..81d5b54
--- /dev/null
+++ b/drivers/misc/a6/a6.c
@@ -0,0 +1,5193 @@
+/*
+ * Includes
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/wait.h>
+#include <linux/bitops.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <linux/a6_sbw_interface.h>
+#include <linux/a6.h>
+#ifdef CONFIG_HIGH_RES_TIMERS
+#include <linux/hrtimer.h>
+#endif
+#include <linux/cpufreq.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+#include <linux/uaccess.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <asm/io.h>
+#include <linux/gpio.h>
+#include <linux/cdev.h>
+#include <linux/debugfs.h>
+#include <linux/switch.h>
+#include <linux/power_supply.h>
+#include <mach/msm_hsusb.h>
+#include <linux/max8903b_charger.h>
+
+#include "high_level_funcs.h"
+
+void max8903b_set_connected_ps(unsigned connected);
+
+#define A2A_RD_BUFF_SIZE (4 * 1024)
+#define A2A_WR_BUFF_SIZE (4 * 1024)
+
+#define A6_DEBUG
+#define A6_PQ
+//#define A6_I2C_RETRY
+
+#ifdef A6_DEBUG
+#define ASSERT(i)  BUG_ON(!(i))
+
+#else
+#define ASSERT(i)  ((void)0)
+
+#endif
+
+
+enum {
+	A6_DEBUG_VERBOSE = 0x01,
+};
+
+static int a6_debug_mask = 0x0;
+static int a6_tp_irq_count = 0;
+static int a6_t2s_dup_correct = 0;
+static int a6_disable_dock_switch = 0;
+
+static int param_set_disable_dock_switch(const char *val, struct kernel_param *kp);
+param_check_int(disable_dock_switch, &(a6_disable_dock_switch));
+module_param_call(disable_dock_switch, param_set_disable_dock_switch,
+	param_get_int, &a6_disable_dock_switch,
+	S_IRUGO | S_IWUSR | S_IWGRP
+	);
+__MODULE_PARM_TYPE(disable_dock_switch, int);
+
+module_param_named(
+		   debug_mask, a6_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
+		  );
+
+module_param_named(
+		   a6_irq_count, a6_tp_irq_count, int, S_IRUGO | S_IWUSR | S_IWGRP
+		  );
+
+module_param_named(
+		   t2s_dup_correct, a6_t2s_dup_correct, int, S_IRUGO | S_IWUSR | S_IWGRP
+		  );
+
+#define A6_DPRINTK(mask, level, message, ...) \
+	do { \
+		if ((mask) & a6_debug_mask) \
+			printk(level message , ##__VA_ARGS__); \
+} while (0)
+
+#define PROFILE_USAGE
+#if defined PROFILE_USAGE
+bool reset_active = false;
+uint32_t start_time;
+int32_t diff_time;
+
+uint32_t start_last_a6_activity = 0;
+#endif
+
+long a6_last_ps_connect = 0;
+
+/* page 0x00 */
+/* host interrupts */
+#define TS2_I2C_INT_MASK_0                             0x0000
+#define TS2_I2C_INT_MASK_1                             0x0001
+#define TS2_I2C_INT_MASK_2                             0x0002
+#define TS2_I2C_INT_MASK_3                             0x0003
+#define TS2_I2C_INT_STATUS_0                           0x0004
+#define TS2_I2C_INT_STATUS_1                           0x0005
+#define TS2_I2C_INT_STATUS_2                           0x0006
+#define TS2_I2C_INT_STATUS_3                           0x0007
+#define TS2_I2C_INT_0_GPIOA_7                          0x80
+#define TS2_I2C_INT_0_GPIOA_6                          0x40
+#define TS2_I2C_INT_0_GPIOA_5                          0x20
+#define TS2_I2C_INT_0_GPIOA_4                          0x10
+#define TS2_I2C_INT_0_GPIOA_3                          0x08
+#define TS2_I2C_INT_0_GPIOA_2                          0x04
+#define TS2_I2C_INT_0_GPIOA_1                          0x02
+#define TS2_I2C_INT_0_GPIOA_0                          0x01
+#define TS2_I2C_INT_0_GPIOA_MASK                       0xff
+#define TS2_I2C_INT_1_COMM_RX_FULL                     0x02
+#define TS2_I2C_INT_1_COMM_TX_EMPTY                    0x01
+#define TS2_I2C_INT_2_BAT_TEMP_HIGH                    0x20
+#define TS2_I2C_INT_2_BAT_TEMP_LOW                     0x10
+#define TS2_I2C_INT_2_BAT_VOLT_LOW                     0x08
+#define TS2_I2C_INT_2_BAT_RARC_CRIT                    0x04
+#define TS2_I2C_INT_2_BAT_RARC_LOW2                    0x02
+#define TS2_I2C_INT_2_BAT_RARC_LOW1                    0x01
+#define TS2_I2C_INT_3_A2A_CONNECT_CHANGE               0x08
+#define TS2_I2C_INT_3_FLAGS_CHANGE                     0x04
+#define TS2_I2C_INT_3_LOG                              0x02
+#define TS2_I2C_INT_3_RESET                            0x01
+
+
+/* page 0x01 */
+/* battery (airboard only); interface defined by phone teams */
+#define TS2_I2C_BAT_STATUS                             0x0100
+#define TS2_I2C_BAT_RARC                               0x0101
+#define TS2_I2C_BAT_RSRC                               0x0102
+#define TS2_I2C_BAT_AVG_CUR_MSB                        0x0103
+#define TS2_I2C_BAT_AVG_CUR_LSB                        0x0104
+#define TS2_I2C_BAT_TEMP_MSB                           0x0105
+#define TS2_I2C_BAT_TEMP_LSB                           0x0106
+#define TS2_I2C_BAT_VOLT_MSB                           0x0107
+#define TS2_I2C_BAT_VOLT_LSB                           0x0108
+#define TS2_I2C_BAT_CUR_MSB                            0x0109
+#define TS2_I2C_BAT_CUR_LSB                            0x010a
+#define TS2_I2C_BAT_COULOMB_MSB                        0x010b
+#define TS2_I2C_BAT_COULOMB_LSB                        0x010c
+#define TS2_I2C_BAT_AS                                 0x010d
+#define TS2_I2C_BAT_FULL_MSB                           0x010e
+#define TS2_I2C_BAT_FULL_LSB                           0x010f
+#define TS2_I2C_BAT_FULL40_MSB                         0x0110
+#define TS2_I2C_BAT_FULL40_LSB                         0x0111
+#define TS2_I2C_BAT_RSNSP                              0x0112
+#define TS2_I2C_BAT_RAAC_MSB                           0x0113
+#define TS2_I2C_BAT_RAAC_LSB                           0x0114
+#define TS2_I2C_BAT_SACR_MSB                           0x0115
+#define TS2_I2C_BAT_SACR_LSB                           0x0116
+#define TS2_I2C_BAT_ASL                                0x0117
+#define TS2_I2C_BAT_FAC_MSB                            0x0118
+#define TS2_I2C_BAT_FAC_LSB                            0x0119
+
+#define TS2_I2C_BAT_ROMID_0                            0x0120
+#define TS2_I2C_BAT_ROMID(x) \
+        (TS2_I2C_BAT_ROMID_0 + (x))
+
+#define TS2_I2C_BAT_COMMAND_STATUS                     0x0140
+#define TS2_I2C_BAT_COMMAND_AUTH                 	0x81
+#define TS2_I2C_BAT_COMMAND_REFRESH              	0x82
+#define TS2_I2C_BAT_COMMAND_WAKE                 	0x83
+#define TS2_I2C_BAT_COMMAND_OFF                  	0xe9
+#define TS2_I2C_BAT_STATUS_AUTH_FAIL             	0x08
+#define TS2_I2C_BAT_STATUS_AUTH_PASS             	0x04
+#define TS2_I2C_BAT_STATUS_REGS_VALID            	0x02
+#define TS2_I2C_BAT_STATUS_BUSY                  	0x01
+
+
+/* battery configuration (airboard only) */
+#define TS2_I2C_BAT_TEMP_LOW_MSB                       0x0180
+#define TS2_I2C_BAT_TEMP_LOW_LSB                       0x0181
+#define TS2_I2C_BAT_TEMP_HIGH_MSB                      0x0182
+#define TS2_I2C_BAT_TEMP_HIGH_LSB                      0x0183
+#define TS2_I2C_BAT_VOLT_LOW_MSB                       0x0184
+#define TS2_I2C_BAT_VOLT_LOW_LSB                       0x0185
+#define TS2_I2C_BAT_RARC_CRIT                          0x0186
+#define TS2_I2C_BAT_RARC_LOW_2                         0x0187
+#define TS2_I2C_BAT_RARC_LOW_1                         0x0188
+
+#define TS2_I2C_BAT_CHALLENGE_0                        0x01e0
+#define TS2_I2C_BAT_CHALLENGE(x) \
+        (TS2_I2C_BAT_CHALLENGE_0 + (x))
+#define TS2_I2C_BAT_RESPONSE_0 \
+        (TS2_I2C_BAT_CHALLENGE_0 + 8)
+#define TS2_I2C_BAT_RESPONSE(x) \
+        (TS2_I2C_BAT_RESPONSE_0 + (x))
+
+
+/* page 0x02 */
+/* comms */
+#define TS2_I2C_COMM_STATUS                            0x0200
+#define TS2_I2C_COMM_STATUS_RX_FULL			0x02
+#define TS2_I2C_COMM_STATUS_TX_EMPTY			0x01
+#define TS2_I2C_COMM_TXDATA_RXDATA_RESERVED_1          0x0201 /* reserved 1 */
+#define TS2_I2C_COMM_TXDATA_RXDATA_RESERVED_2          0x0202 /* reserved 2 */
+#define TS2_I2C_COMM_TXDATA_RXDATA                     0x0203 /* side-effect */
+
+/* page 0x03 */
+/* log */
+#define TS2_I2C_LOG_LEVEL                              0x0300
+#define TS2_I2C_LOG_INT_THRESHOLD                      0x0301
+#define TS2_I2C_LOG_LOST_MSB_RESERVED_1                0x0302 /* reserved 1 */
+#define TS2_I2C_LOG_LOST_MSB_RESERVED_2                0x0303 /* reserved 2 */
+#define TS2_I2C_LOG_LOST_MSB                           0x0304 /* side-effect */
+#define TS2_I2C_LOG_LOST_LSB                           0x0305
+#define TS2_I2C_LOG_COUNT_MSB                          0x0306
+#define TS2_I2C_LOG_COUNT_LSB                          0x0307
+#define TS2_I2C_LOG_ENTRY_MSB_RESERVED_1               0x0308 /* reserved 1 */
+#define TS2_I2C_LOG_ENTRY_MSB_RESERVED_2               0x0309 /* reserved 2 */
+#define TS2_I2C_LOG_ENTRY_MSB                          0x030a /* side-effect */
+#define TS2_I2C_LOG_ENTRY_LSB                          0x030b
+
+
+/* page 0x04 */
+/* local enumeration structure */
+/* Enumeration registers (local) */
+#define	TS2_I2C_ENUM_STRUCT_VER                        0x0400
+#define	TS2_I2C_ENUM_STRUCT_LEN                        0x0401
+#define	TS2_I2C_ENUM_PROTOCOL_MAP_VER                  0x0402
+#define	TS2_I2C_ENUM_MFGR_ID_HI                        0x0403
+#define	TS2_I2C_ENUM_MFGR_ID_LO                        0x0404
+#define	TS2_I2C_ENUM_PRODUCT_TYPE_HI                   0x0405
+#define	TS2_I2C_ENUM_PRODUCT_TYPE_LO                   0x0406
+#define	TS2_I2C_ENUM_SERNO_7                           0x0407
+#define	TS2_I2C_ENUM_SERNO_6                           0x0408
+#define	TS2_I2C_ENUM_SERNO_5                           0x0409
+#define	TS2_I2C_ENUM_SERNO_4                           0x040A
+#define	TS2_I2C_ENUM_SERNO_3                           0x040B
+#define	TS2_I2C_ENUM_SERNO_2                           0x040C
+#define	TS2_I2C_ENUM_SERNO_1                           0x040D
+#define	TS2_I2C_ENUM_SERNO_0                           0x040E
+#define	TS2_I2C_ENUM_ASSY_REV                          0x040F
+#define	TS2_I2C_ENUM_FW_VER_2                          0x0410
+#define	TS2_I2C_ENUM_FW_VER_1                          0x0411
+#define	TS2_I2C_ENUM_FW_VER_0                          0x0412
+#define	TS2_I2C_ENUM_VNODE_MIN_HI                      0x0413
+#define	TS2_I2C_ENUM_VNODE_MIN_LO                      0x0414
+#define	TS2_I2C_ENUM_VNODE_MAX_HI                      0x0415
+#define	TS2_I2C_ENUM_VNODE_MAX_LO                      0x0416
+#define	TS2_I2C_ENUM_INODE_MIN_HI                      0x0417
+#define	TS2_I2C_ENUM_INODE_MIN_LO                      0x0418
+#define	TS2_I2C_ENUM_INODE_MAX_HI                      0x0419
+#define	TS2_I2C_ENUM_INODE_MAX_LO                      0x041A
+#define	TS2_I2C_ENUM_TNODE_MIN                         0x041B
+#define	TS2_I2C_ENUM_TNODE_MAX                         0x041C
+#define	TS2_I2C_ENUM_POWER_MAX                         0x041D
+
+#define	TS2_I2C_ENUM_ACCE_0                            0x0428
+#define	TS2_I2C_ENUM_ACCE_1                            0x0429
+#define	TS2_I2C_ENUM_ACCE_2                            0x042A
+#define	TS2_I2C_ENUM_ACCE_3                            0x042B
+#define	TS2_I2C_ENUM_ACCE_4                            0x042C
+#define	TS2_I2C_ENUM_ACCE_5                            0x042D
+#define	TS2_I2C_ENUM_ACCE_6                            0x042E
+#define	TS2_I2C_ENUM_ACCE_7                            0x042F
+#define	TS2_I2C_ENUM_ACCE_8                            0x0430
+#define	TS2_I2C_ENUM_ACCE_9                            0x0431
+#define	TS2_I2C_ENUM_ACCE_10                           0x0432
+#define	TS2_I2C_ENUM_ACCE_11                           0x0433
+#define	TS2_I2C_ENUM_ACCE_12                           0x0434
+#define	TS2_I2C_ENUM_ACCE_13                           0x0435
+#define	TS2_I2C_ENUM_ACCE_14                           0x0436
+#define	TS2_I2C_ENUM_ACCE_15                           0x0437
+#define	TS2_I2C_ENUM_MIN_PWM                           0x0438
+
+
+/* page 0x05 */
+/* remote enumeration structure */
+#define	TS2_I2C_ENUM_REMOTE_STRUCT_VER                 0x0500
+#define	TS2_I2C_ENUM_REMOTE_STRUCT_LEN                 0x0501
+#define	TS2_I2C_ENUM_REMOTE_PROTOCOL_MAP_VER           0x0502
+#define	TS2_I2C_ENUM_REMOTE_MFGR_ID_HI                 0x0503
+#define	TS2_I2C_ENUM_REMOTE_MFGR_ID_LO                 0x0504
+#define	TS2_I2C_ENUM_REMOTE_MFGR_ID_HI_V1              0x0505
+#define	TS2_I2C_ENUM_REMOTE_MFGR_ID_LO_V1              0x0506
+#define	TS2_I2C_ENUM_REMOTE_PRODUCT_TYPE_HI            0x0505
+#define	TS2_I2C_ENUM_REMOTE_PRODUCT_TYPE_LO            0x0506
+#define	TS2_I2C_ENUM_REMOTE_PRODUCT_TYPE_HI_V1         0x0507
+#define	TS2_I2C_ENUM_REMOTE_PRODUCT_TYPE_LO_V1         0x0508
+#define	TS2_I2C_ENUM_REMOTE_SERNO_7                    0x0507
+#define	TS2_I2C_ENUM_REMOTE_SERNO_6                    0x0508
+#define	TS2_I2C_ENUM_REMOTE_SERNO_5                    0x0509
+#define	TS2_I2C_ENUM_REMOTE_SERNO_4                    0x050A
+#define	TS2_I2C_ENUM_REMOTE_SERNO_3                    0x050B
+#define	TS2_I2C_ENUM_REMOTE_SERNO_2                    0x050C
+#define	TS2_I2C_ENUM_REMOTE_SERNO_1                    0x050D
+#define	TS2_I2C_ENUM_REMOTE_SERNO_0                    0x050E
+#define	TS2_I2C_ENUM_REMOTE_ASSY_REV                   0x050F
+#define	TS2_I2C_ENUM_REMOTE_FW_VER_2                   0x0510
+#define	TS2_I2C_ENUM_REMOTE_FW_VER_1                   0x0511
+#define	TS2_I2C_ENUM_REMOTE_FW_VER_0                   0x0512
+#define	TS2_I2C_ENUM_REMOTE_VNODE_MIN_HI               0x0513
+#define	TS2_I2C_ENUM_REMOTE_VNODE_MIN_LO               0x0514
+#define	TS2_I2C_ENUM_REMOTE_VNODE_MAX_HI               0x0515
+#define	TS2_I2C_ENUM_REMOTE_VNODE_MAX_LO               0x0516
+#define	TS2_I2C_ENUM_REMOTE_INODE_MIN_HI               0x0517
+#define	TS2_I2C_ENUM_REMOTE_INODE_MIN_LO               0x0518
+#define	TS2_I2C_ENUM_REMOTE_INODE_MAX_HI               0x0519
+#define	TS2_I2C_ENUM_REMOTE_INODE_MAX_LO               0x051A
+#define	TS2_I2C_ENUM_REMOTE_TNODE_MIN                  0x051B
+#define	TS2_I2C_ENUM_REMOTE_TNODE_MAX                  0x051C
+#define	TS2_I2C_ENUM_REMOTE_POWER_MAX                  0x051D
+
+
+
+#define	TS2_I2C_ENUM_REMOTE_ACCE_0                     0x0528
+#define	TS2_I2C_ENUM_REMOTE_ACCE_1                     0x0529
+#define	TS2_I2C_ENUM_REMOTE_ACCE_2                     0x052A
+#define	TS2_I2C_ENUM_REMOTE_ACCE_3                     0x052B
+#define	TS2_I2C_ENUM_REMOTE_ACCE_4                     0x052C
+#define	TS2_I2C_ENUM_REMOTE_ACCE_5                     0x052D
+#define	TS2_I2C_ENUM_REMOTE_ACCE_6                     0x052E
+#define	TS2_I2C_ENUM_REMOTE_ACCE_7                     0x052F
+#define	TS2_I2C_ENUM_REMOTE_ACCE_8                     0x0530
+#define	TS2_I2C_ENUM_REMOTE_ACCE_9                     0x0531
+#define	TS2_I2C_ENUM_REMOTE_ACCE_10                    0x0532
+#define	TS2_I2C_ENUM_REMOTE_ACCE_11                    0x0533
+#define	TS2_I2C_ENUM_REMOTE_ACCE_12                    0x0534
+#define	TS2_I2C_ENUM_REMOTE_ACCE_13                    0x0535
+#define	TS2_I2C_ENUM_REMOTE_ACCE_14                    0x0536
+#define	TS2_I2C_ENUM_REMOTE_ACCE_15                    0x0537
+
+
+/* page 0x06 */
+/* reserved */
+
+
+/* page 0x07 */
+/* voltage, current, temperature, power (puck only), pwm (puck only) */
+#define TS2_I2C_ID                                     0x0700
+#define TS2_I2C_ID_AIRBOARD_0				0x2c
+#define TS2_I2C_FLAGS_0                                0x0701
+#define	TS2_I2C_FLAGS_0_PUCK_PRIORITY			(1<<0)
+#define TS2_I2C_FLAGS_1                                0x0702
+#define TS2_I2C_FLAGS_2                                0x0703
+#define	TS2_I2C_FLAGS_2_A2A_CONNECT			0x80
+#define	TS2_I2C_FLAGS_2_A2A_ATTEMPT			0x40
+#define	TS2_I2C_FLAGS_2_BATTERY_DETECT			0x20
+#define	TS2_I2C_FLAGS_2_WIRED_CHARGE			0x10
+#define	TS2_I2C_FLAGS_2_PUCK_CHARGE			0x08
+#define TS2_I2C_FLAGS_2_WIRED				0x04
+#define TS2_I2C_FLAGS_2_PUCK				0x02
+#define TS2_I2C_FLAGS_2_PUCK_DETECT			0x01
+#define TS2_I2C_V_LOCAL                                0x0704
+#define TS2_I2C_I_LOCAL                                0x0705
+#define TS2_I2C_T_LOCAL                                0x0706
+#define TS2_I2C_P_LOCAL                                0x0707 /* puck only */
+#define TS2_I2C_V_REMOTE                               0x0708
+#define TS2_I2C_I_REMOTE                               0x0709
+#define TS2_I2C_T_REMOTE                               0x070a
+#define TS2_I2C_P_REMOTE                               0x070b /* puck only */
+#define TS2_I2C_PWM                                    0x070c /* puck only */
+#define TS2_I2C_PERIOD                                 0x070d /* puck only */
+
+/* config */
+#define TS2_I2C_TX_POWER_V1                            0x0740
+#define TS2_I2C_TX_POWER_V2                            0x0741
+
+/* puck only config */
+#define TS2_I2C_PWM_PERIOD                             0x0780
+#define TS2_I2C_PWM_DUTY_ENUM                          0x0781
+#define TS2_I2C_PWM_DUTY_MIN_RUN                       0x0782
+#define TS2_I2C_PWM_DUTY_MIN_BOOST                     0x0783
+#define TS2_I2C_PWM_DEADTIME                           0x0784
+#define TS2_I2C_PWM_EFFICIENCY_OFFSET                  0x0785
+#define TS2_I2C_PWM_EFFICIENCY_SCALE                   0x0786
+
+/* airboard only */
+#define TS2_I2C_V_OFFSET                               0x07c0
+#define TS2_I2C_WAKEUP_PERIOD                          0x07c1
+#define TS2_I2C_CURRENT_ADJ_FOR_V1_SLOPE               0x07c2
+#define TS2_I2C_CURRENT_ADJ_FOR_V1_OFFSET              0x07c3
+#define TS2_I2C_VOLTAGE_ADJ_FOR_V1_SLOPE               0x07c4
+#define TS2_I2C_VOLTAGE_ADJ_FOR_V1_OFFSET              0x07c5
+
+/* misc registers */
+#define TS2_I2C_COMMAND                                0x1000
+#define TS2_I2C_COMMAND_RESET_HOST               	 0x01
+#define TS2_I2C_COMMAND_CLEAR_BTN_SEQ_RESET_FLAG 	 0x02
+
+#define TS2_I2C_COMMAND_COMM_CONNECT             	 0x10
+#define TS2_I2C_COMMAND_COMM_DISCONNECT          	 0x11
+#define TS2_I2C_COMMAND_REENUMERATE              	 0x12
+
+#define TS2_I2C_COMMAND_FRAM_CHECKSUM_1400_READ  	 0x20
+#define TS2_I2C_COMMAND_FRAM_CHECKSUM_READ_1     	 0x21
+#define TS2_I2C_COMMAND_FRAM_CHECKSUM_READ_2     	 0x22
+
+#define TS2_I2C_COMMAND_MEM_READ_BYTE            	 0x80
+#define TS2_I2C_COMMAND_MEM_READ_CHAWMP          	 0x81
+#define TS2_I2C_COMMAND_MEM_WRITE_BYTE           	 0x82
+#define TS2_I2C_COMMAND_MEM_WRITE_CHAWMP         	 0x83
+#define TS2_I2C_COMMAND_PMIC_READ                	 0x84
+#define TS2_I2C_COMMAND_PMIC_WRITE               	 0x85
+#define TS2_I2C_COMMAND_SP_READ                  	 0x86
+#define TS2_I2C_COMMAND_SP_INDIRECT_READ         	 0x87
+#define TS2_I2C_COMMAND_ADDR_MSB                       0x1001
+#define TS2_I2C_COMMAND_ADDR_LSB                       0x1002
+#define TS2_I2C_COMMAND_DATA_MSB                       0x1003
+#define TS2_I2C_COMMAND_DATA_LSB                       0x1004
+
+#define TS2_I2C_DEBUG_TRACE_DATA_RESERVED_1            0x101e /* reserved 1 */
+#define TS2_I2C_DEBUG_TRACE_DATA_RESERVED_2            0x101f /* reserved 2 */
+#define TS2_I2C_DEBUG_TRACE_DATA                       0x1020 /* side-effect */
+
+#define A6_REG_TS2_I2C_INT_MASK_0	0
+#define A6_REG_TS2_I2C_INT_MASK_1	1
+#define A6_REG_TS2_I2C_INT_MASK_2	2
+#define A6_REG_TS2_I2C_INT_MASK_3	3
+#define A6_REG_TS2_I2C_INT_STATUS_0	4
+#define A6_REG_TS2_I2C_INT_STATUS_1	5
+#define A6_REG_TS2_I2C_INT_STATUS_2	6
+#define A6_REG_TS2_I2C_INT_STATUS_3	7
+#define A6_REG_TS2_I2C_BAT_STATUS	8
+#define A6_REG_TS2_I2C_BAT_RARC	9
+#define A6_REG_TS2_I2C_BAT_RSRC	10
+#define A6_REG_TS2_I2C_BAT_AVG_CUR_LSB_MSB	11
+#define A6_REG_TS2_I2C_BAT_TEMP_LSB_MSB	12
+#define A6_REG_TS2_I2C_BAT_VOLT_LSB_MSB	13
+#define A6_REG_TS2_I2C_BAT_CUR_LSB_MSB	14
+#define A6_REG_TS2_I2C_BAT_COULOMB_LSB_MSB	15
+// #define A6_REG_TS2_I2C_BAT_AS	16 // two entries? TODO
+#define A6_REG_TS2_I2C_BAT_FULL_LSB_MSB	17
+#define A6_REG_TS2_I2C_BAT_FULL40_LSB_MSB	18
+#define A6_REG_TS2_I2C_BAT_RSNSP	19
+#define A6_REG_TS2_I2C_BAT_ROMID_0	20
+#define A6_REG_TS2_I2C_BAT_COMMAND_STATUS	21
+#define A6_REG_TS2_I2C_BAT_TEMP_LOW_LSB_MSB	22
+#define A6_REG_TS2_I2C_BAT_TEMP_HIGH_LSB_MSB	23
+#define A6_REG_TS2_I2C_BAT_VOLT_LOW_LSB_MSB	24
+#define A6_REG_TS2_I2C_BAT_RARC_CRIT	25
+#define A6_REG_TS2_I2C_BAT_RARC_LOW_2	26
+#define A6_REG_TS2_I2C_BAT_RARC_LOW_1	27
+#define A6_REG_TS2_I2C_BAT_RAAC_MSB	28
+#define A6_REG_TS2_I2C_ID	29
+#define A6_REG_TS2_I2C_FLAGS_0	30
+#define A6_REG_TS2_I2C_FLAGS_2	31
+#define A6_REG_VERSION	32
+#define A6_REG_TS2_I2C_V_OFFSET	33
+#define A6_REG_TS2_I2C_WAKEUP_PERIOD	34
+#define A6_REG_TS2_I2C_COMMAND	35
+#define A6_REG_REMOTE_VERSION	36
+#define A6_REG_ACCESSORY_DATA_0	37
+#define A6_REG_ACCESSORY_DATA_1	38
+#define A6_REG_ACCESSORY_DATA_2	39
+#define A6_REG_ACCESSORY_DATA_3	40
+#define A6_REG_ACCESSORY_DATA_4	41
+#define A6_REG_ACCESSORY_DATA_5	42
+#define A6_REG_ACCESSORY_DATA_6	43
+#define A6_REG_ACCESSORY_DATA_7	44
+#define A6_REG_ACCESSORY_DATA_8	45
+#define A6_REG_ACCESSORY_DATA_9	46
+#define A6_REG_ACCESSORY_DATA_10	47
+#define A6_REG_ACCESSORY_DATA_11	48
+#define A6_REG_ACCESSORY_DATA_12	49
+#define A6_REG_REMOTE_ACCESSORY_DATA_0	50
+#define A6_REG_REMOTE_ACCESSORY_DATA_1	51
+#define A6_REG_REMOTE_ACCESSORY_DATA_2	52
+#define A6_REG_REMOTE_ACCESSORY_DATA_3	53
+#define A6_REG_REMOTE_ACCESSORY_DATA_4	54
+#define A6_REG_REMOTE_ACCESSORY_DATA_5	55
+#define A6_REG_REMOTE_ACCESSORY_DATA_6	56
+#define A6_REG_REMOTE_ACCESSORY_DATA_7	57
+#define A6_REG_REMOTE_ACCESSORY_DATA_8	58
+#define A6_REG_REMOTE_ACCESSORY_DATA_9	59
+#define A6_REG_REMOTE_ACCESSORY_DATA_10	60
+#define A6_REG_REMOTE_ACCESSORY_DATA_11	61
+#define A6_REG_REMOTE_ACCESSORY_DATA_12	62
+#define A6_REG_TS2_I2C_COMM_STATUS	63
+#define A6_REG_TS2_I2C_COMM_TXDATA_RXDATA	64
+#define A6_REG_TS2_I2C_BAT_SACR_LSB_MSB	65
+#define A6_REG_TS2_I2C_BAT_ASL	66
+// #define A6_REG_TS2_I2C_BAT_AS	67 // two entries? TODO
+#define A6_REG_TS2_I2C_BAT_FAC_LSB_MSB	68
+#define A6_REG_MAX_POWER_AVAILABLE	69
+#define A6_REG_TS2_I2C_ENUM_REMOTE_STRUCT_VER	70
+#define A6_REG_ACCESSORY_DATA_13	71
+#define A6_REG_ACCESSORY_DATA_14	72
+#define A6_REG_ACCESSORY_DATA_15	73
+#define A6_REG_REMOTE_ACCESSORY_DATA_13	74
+#define A6_REG_REMOTE_ACCESSORY_DATA_14	75
+#define A6_REG_REMOTE_ACCESSORY_DATA_15	76
+#define A6_REG_TS2_I2C_ENUM_MIN_PWM	77
+#define A6_REG_ACCESSORY_DATA_COMBO	78
+#define A6_REG_REMOTE_ACCESSORY_DATA_COMBO	79
+#define A6_REG_TS2_I2C_COMMAND_ADDR_LSB_MSB	80
+#define A6_REG_TS2_I2C_COMMAND_DATA_LSB_MSB	81
+#define A6_REG_REMOTE_MFGRID_V1	82
+#define A6_REG_REMOTE_MFGRID_V2	83
+#define A6_REG_REMOTE_PRODUCTID_V1	84
+#define A6_REG_REMOTE_PRODUCTID_V2	85
+#define A6_REG_REMOTE_SERNO_V1	86
+#define A6_REG_REMOTE_SERNO_V2	87
+#define A6_REG_LOCAL_MFGRID	88
+#define A6_REG_LOCAL_PRODUCTID	89
+#define A6_REG_LOCAL_SERNO	90
+
+
+#define RSENSE_DEFAULT	20;
+#define FORCE_WAKE_TIMER_EXPIRY (HZ/20)
+
+#define a6_wait_event_ex(wq, condition)					\
+do {									\
+	if (condition)							\
+		break;							\
+	do {								\
+		DEFINE_WAIT(__wait);					\
+									\
+		for (;;) {						\
+			prepare_to_wait_exclusive(&wq, &__wait,		\
+				TASK_UNINTERRUPTIBLE);			\
+			if (condition)					\
+				break;					\
+			schedule();					\
+		}							\
+		finish_wait(&wq, &__wait);				\
+	} while (0);							\
+} while (0)
+
+
+enum {
+	DEVICE_BUSY_BIT = 0,
+	IS_OPENED,
+	IS_INITIALIZED_BIT,
+	BOOTLOAD_ACTIVE_BIT,
+	FORCE_WAKE_ACTIVE_BIT,
+	READ_ACTIVE_BIT,
+	WRITE_ACTIVE_BIT,
+	A2A_CONNECTED,
+	EXTRACT_INITIATED,
+	// capabilities
+	CAP_PERIODIC_WAKE,
+#ifdef A6_PQ
+	STARTING_AID_TASK,
+	KILLING_AID_TASK,
+	IS_QUIESCED,
+#endif
+	IS_SUSPENDED,
+	INT_PENDING,
+	SIZE_FLAGS
+};
+
+struct a6_device_state {
+	struct i2c_client *i2c_dev;
+	struct a6_platform_data *plat_data;
+	struct file_operations fops;
+	struct miscdevice mdev;
+
+	struct mutex dev_mutex;
+	unsigned int timestamping;
+
+	struct timer_list	a6_force_wake_timer;
+	struct work_struct	a6_force_wake_work;
+	struct mutex		a6_force_wake_mutex;
+
+	wait_queue_head_t	dev_busyq;
+	struct work_struct	a6_irq_work;
+
+	int32_t cpufreq_hold_flag;
+	struct workqueue_struct* ka6d_workqueue;
+	struct workqueue_struct* ka6d_fw_workqueue;
+
+	uint32_t cached_rsense_val: 	16;
+	uint32_t busy_count:		 8;
+	DECLARE_BITMAP(flags, SIZE_FLAGS);
+
+#ifdef A6_PQ
+	struct completion aq_enq_complete;
+	struct completion aid_exit_complete;
+	struct mutex aq_mutex;
+	struct list_head aq_head;
+	struct task_struct* ai_dispatch_task;
+#ifdef A6_DEBUG
+	uint32_t dbgflg_kill_raid: 	1;
+
+	uint8_t debug_restart_aid;
+	uint8_t debug_flush_aiq;
+	uint8_t debug_unused_01;
+	uint8_t debug_unused_02;
+#endif
+#endif
+
+	int cpufreq_hold;
+
+	// pmem extract
+	struct file_operations pmem_fops;
+	struct miscdevice pmem_mdev;
+	char pmem_dev_name[16];
+	
+	char *a2a_rd_buf, *a2a_wr_buf;
+	char *a2a_rp, *a2a_wp;
+
+	enum chg_type otg_chg_type;
+	struct delayed_work charge_work;
+	struct delayed_work init_connected_ps_work;
+	int stop_heartbeat;
+	int last_percent;
+
+	struct switch_dev *dock_switch;
+};
+
+struct a6_device_state *batt_state;
+
+#ifdef A6_PQ
+int32_t a6_start_ai_dispatch_task(struct a6_device_state* state);
+int32_t a6_stop_ai_dispatch_task(struct a6_device_state* state);
+int32_t flush_a6_action_items(struct a6_device_state* state);
+#endif
+
+static ssize_t a6_generic_show(struct device *dev, struct device_attribute *dev_attr, char *buf);
+static ssize_t a6_generic_store(struct device *dev, struct device_attribute *dev_attr, const char *buf,
+				size_t count);
+
+typedef int32_t (*format_show_fn)(const struct a6_device_state* state, const uint8_t* val,
+		  uint8_t* fmt_buffer, uint32_t size_buffer);
+typedef int32_t (*format_store_fn)(const struct a6_device_state* state, const uint8_t* fmt_buffer,
+		  uint8_t* val, uint32_t size_buffer);
+
+int32_t format_current(const struct a6_device_state* state, const uint8_t* val,
+		       uint8_t* fmt_buffer, uint32_t size_buffer);
+int32_t format_voltage(const struct a6_device_state* state, const uint8_t* val,
+		       uint8_t* fmt_buffer, uint32_t size_buffer);
+int32_t r_format_voltage(const struct a6_device_state* state, const uint8_t* fmt_buffer,
+			 uint8_t* val, uint32_t size_buffer);
+int32_t format_rawcoulomb(const struct a6_device_state* state, const uint8_t* val,
+		       uint8_t* fmt_buffer, uint32_t size_buffer);
+int32_t format_coulomb(const struct a6_device_state* state, const uint8_t* val,
+		       uint8_t* fmt_buffer, uint32_t size_buffer);
+int32_t format_age(const struct a6_device_state* state, const uint8_t* val,
+		       uint8_t* fmt_buffer, uint32_t size_buffer);
+int32_t format_fullx(const struct a6_device_state* state, const uint8_t* val,
+		       uint8_t* fmt_buffer, uint32_t size_buffer);
+int32_t format_temp(const struct a6_device_state* state, const uint8_t* val,
+		       uint8_t* fmt_buffer, uint32_t size_buffer);
+int32_t r_format_temp(const struct a6_device_state* state, const uint8_t* fmt_buffer,
+			 uint8_t* val, uint32_t size_buffer);
+int32_t format_status(const struct a6_device_state* state, const uint8_t* val,
+		       uint8_t* fmt_buffer, uint32_t size_buffer);
+int32_t format_rsense(const struct a6_device_state* state, const uint8_t* val,
+		       uint8_t* fmt_buffer, uint32_t size_buffer);
+int32_t format_charge_source_status(const struct a6_device_state* state, const uint8_t* val,
+		      uint8_t* fmt_buffer, uint32_t size_buffer);
+int32_t format_version(const struct a6_device_state* state, const uint8_t* val,
+		       uint8_t* fmt_buffer, uint32_t size_buffer);
+int32_t format_v_offset(const struct a6_device_state* state, const uint8_t* val,
+			uint8_t* fmt_buffer, uint32_t size_buffer);
+int32_t format_command(const struct a6_device_state* state, const uint8_t* val,
+		    uint8_t* fmt_buffer, uint32_t size_buffer);
+int32_t format_accessory(const struct a6_device_state* state, const uint8_t* val,
+		    uint8_t* fmt_buffer, uint32_t size_buffer);
+int32_t format_comm_status(const struct a6_device_state* state, const uint8_t* val,
+		    uint8_t* fmt_buffer, uint32_t size_buffer);
+int32_t format_raw_unsigned(const struct a6_device_state* state, const uint8_t* val,
+			   uint8_t* fmt_buffer, uint32_t size_buffer);
+int32_t format_max_power_available(const struct a6_device_state* state, const uint8_t* val,
+				   uint8_t* fmt_buffer, uint32_t size_buffer);
+int32_t format_accessory_combo(const struct a6_device_state* state, const uint8_t* val,
+			       uint8_t* fmt_buffer, uint32_t size_buffer);
+int32_t format_u16_hex(const struct a6_device_state* state, const uint8_t* val,
+		       uint8_t* fmt_buffer, uint32_t size_buffer);
+int32_t format_serno_v1(const struct a6_device_state* state, const uint8_t* val,
+			uint8_t* fmt_buffer, uint32_t size_buffer);
+int32_t format_serno_v2(const struct a6_device_state* state, const uint8_t* val,
+			uint8_t* fmt_buffer, uint32_t size_buffer);
+
+static ssize_t a6_diag_show(struct device *dev, struct device_attribute *dev_attr, char *buf);
+static ssize_t a6_diag_store(struct device *dev, struct device_attribute *dev_attr, const char *buf,
+			     size_t count);
+static ssize_t a6_val_cksum_show(struct device *dev, struct device_attribute *attr, char *buf);
+
+static void a6_dock_update_state(struct a6_device_state *state);
+
+static void a6_update_connected_ps(void);
+
+void a6_force_wake_timer_callback(ulong data);
+/*
+ Note:  16-bit accesses comprising an LSB, MSB register pair must be specified in LSB:MSB order
+	in the corresponding a6_register_desc struct. This is because an a6-fw constraint
+	restricts 16-bit value accesses to MSB-leading only. The a6_i2c_read_reg and the
+	a6_i2c_write_reg functions take the LSB:MSB definition in the a6_register_desc struct
+	and re-orders the i2c txn to read in MSB:LSB order.
+*/
+struct a6_register_desc {
+#define id_size 20
+
+	const char* debug_name;
+	struct device_attribute dev_attr;
+	format_show_fn format;
+	format_store_fn r_format;
+	uint16_t id[id_size];
+	uint32_t num_ids: 5;
+	uint32_t ro: 1;
+} a6_register_desc_arr[] = {
+	/* interrupt control registers */
+	[0] = { .debug_name = "TS2_I2C_INT_MASK_0",
+		{{.name = "int_mask0", .mode = S_IRUGO | S_IWUGO},
+		 .show = a6_generic_show, .store = a6_generic_store},
+		.id = {TS2_I2C_INT_MASK_0}, .num_ids = 1, .ro = 0},
+	[1] = { .debug_name = "TS2_I2C_INT_MASK_1",
+		{{.name = "int_mask1", .mode = S_IRUGO | S_IWUGO},
+		 .show = a6_generic_show, .store = a6_generic_store},
+		.id = {TS2_I2C_INT_MASK_1}, .num_ids = 1, .ro = 0},
+	[2] = { .debug_name = "TS2_I2C_INT_MASK_2",
+		{{.name = "int_mask2", .mode = S_IRUGO | S_IWUGO},
+		 .show = a6_generic_show, .store = a6_generic_store},
+		.id = {TS2_I2C_INT_MASK_2}, .num_ids = 1, .ro = 0},
+	[3] = { .debug_name = "TS2_I2C_INT_MASK_3",
+		{{.name = "int_mask3", .mode = S_IRUGO | S_IWUGO},
+		 .show = a6_generic_show, .store = a6_generic_store},
+		.id = {TS2_I2C_INT_MASK_3}, .num_ids = 1, .ro = 0},
+	[4] = { .debug_name = "TS2_I2C_INT_STATUS_0",
+		{{.name = "int_status0", .mode = S_IRUGO | S_IWUGO},
+		 .show = a6_generic_show, .store = a6_generic_store},
+		.id = {TS2_I2C_INT_STATUS_0}, .num_ids = 1, .ro = 0},
+	[5] = { .debug_name = "TS2_I2C_INT_STATUS_1",
+		{{.name = "int_status1", .mode = S_IRUGO | S_IWUGO},
+		 .show = a6_generic_show, .store = a6_generic_store},
+		.id = {TS2_I2C_INT_STATUS_1}, .num_ids = 1, .ro = 0},
+	[6] = { .debug_name = "TS2_I2C_INT_STATUS_2",
+		{{.name = "int_status2", .mode = S_IRUGO | S_IWUGO},
+		 .show = a6_generic_show, .store = a6_generic_store},
+		.id = {TS2_I2C_INT_STATUS_2}, .num_ids = 1, .ro = 0},
+	[7] = { .debug_name = "TS2_I2C_INT_STATUS_3",
+		{{.name = "int_status3", .mode = S_IRUGO | S_IWUGO},
+		 .show = a6_generic_show, .store = a6_generic_store},
+		.id = {TS2_I2C_INT_STATUS_3}, .num_ids = 1, .ro = 0},
+
+	/* battery control registers */
+	[8] = { .debug_name = "TS2_I2C_BAT_STATUS",
+		{{.name = "status", .mode = S_IRUGO},
+		 .show = a6_generic_show, .store = NULL},
+		.format = format_status,
+		.id = {TS2_I2C_BAT_STATUS}, .num_ids = 1, .ro = 1},
+	[9] = { .debug_name = "TS2_I2C_BAT_RARC",
+		{{.name = "getpercent", .mode = S_IRUGO},
+		 .show = a6_generic_show, .store = NULL},
+		.id = {TS2_I2C_BAT_RARC}, .num_ids = 1, .ro = 1},
+	[10] = { .debug_name = "TS2_I2C_BAT_RSRC",
+		{{.name = "rsrc", .mode = S_IRUGO},
+		 .show = a6_generic_show, .store = NULL},
+		.id = {TS2_I2C_BAT_RSRC}, .num_ids = 1, .ro = 1},
+	[11] = { .debug_name = "TS2_I2C_BAT_AVG_CUR_LSB_MSB",
+		{{.name = "getavgcurrent", .mode = S_IRUGO},
+		 .show = a6_generic_show, .store = NULL},
+		.format = format_current,
+		.id = {TS2_I2C_BAT_AVG_CUR_LSB, TS2_I2C_BAT_AVG_CUR_MSB},
+			.num_ids = 2, .ro = 1},
+	[12] = { .debug_name = "TS2_I2C_BAT_TEMP_LSB_MSB",
+		{{.name = "gettemp", .mode = S_IRUGO},
+		 .show = a6_generic_show, .store = NULL},
+		.format = format_temp,
+		.id = {TS2_I2C_BAT_TEMP_LSB, TS2_I2C_BAT_TEMP_MSB},
+			.num_ids = 2, .ro = 1},
+	[13] = { .debug_name = "TS2_I2C_BAT_VOLT_LSB_MSB",
+		{{.name = "getvoltage", .mode = S_IRUGO},
+		 .show = a6_generic_show, .store = NULL},
+		.format = format_voltage,
+		.id = {TS2_I2C_BAT_VOLT_LSB, TS2_I2C_BAT_VOLT_MSB},
+			.num_ids = 2, .ro = 1},
+	[14] = { .debug_name = "TS2_I2C_BAT_CUR_LSB_MSB",
+		{{.name = "getcurrent", .mode = S_IRUGO},
+		 .show = a6_generic_show, .store = NULL},
+		.format = format_current,
+		.id = {TS2_I2C_BAT_CUR_LSB, TS2_I2C_BAT_CUR_MSB},
+			.num_ids = 2, .ro = 1},
+	[15] = { .debug_name = "TS2_I2C_BAT_COULOMB_LSB_MSB",
+		{{.name = "getrawcoulomb", .mode = S_IRUGO},
+		 .show = a6_generic_show, .store = NULL},
+		.format = format_rawcoulomb,
+		.id = {TS2_I2C_BAT_COULOMB_LSB, TS2_I2C_BAT_COULOMB_MSB},
+			.num_ids = 2, .ro = 1},
+	[16] = { .debug_name = "TS2_I2C_BAT_AS",
+		{{.name = "getage", .mode = S_IRUGO|S_IWUGO},
+		 .show = a6_generic_show, .store = a6_generic_store},
+		.format = format_age,
+		.id = {TS2_I2C_BAT_AS}, .num_ids = 1, .ro = 0},
+	[17] = { .debug_name = "TS2_I2C_BAT_FULL_LSB_MSB",
+		{{.name = "getfull", .mode = S_IRUGO},
+		 .show = a6_generic_show, .store = NULL},
+		.format = format_fullx,
+		.id = {TS2_I2C_BAT_FULL_LSB, TS2_I2C_BAT_FULL_MSB},
+			.num_ids = 2, .ro = 1},
+	[18] = {.debug_name = "TS2_I2C_BAT_FULL40_LSB_MSB",
+		{{.name = "getfull40", .mode = S_IRUGO},
+		 .show = a6_generic_show, .store = NULL},
+		.format = format_fullx,
+		.id = {TS2_I2C_BAT_FULL40_LSB, TS2_I2C_BAT_FULL40_MSB},
+			.num_ids = 2, .ro = 1},
+	[19] = {.debug_name = "TS2_I2C_BAT_RSNSP",
+		{{.name = "getrsense", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_rsense,
+		.id = {TS2_I2C_BAT_RSNSP}, .num_ids = 1, .ro = 1},
+
+	[20] = {.debug_name = "TS2_I2C_BAT_ROMID_0",
+		{{.name = "romid_0", .mode = S_IRUGO},
+		 .show = a6_generic_show, .store = NULL},
+		.id = {TS2_I2C_BAT_ROMID_0}, .num_ids = 1, .ro = 1},
+
+	[21] = {.debug_name = "TS2_I2C_BAT_COMMAND_STATUS",
+		{{.name = "command_status", .mode = S_IRUGO|S_IWUGO},
+		 .show = a6_generic_show, .store = a6_generic_store},
+		.id = {TS2_I2C_BAT_COMMAND_STATUS}, .num_ids = 1, .ro = 0},
+
+
+	[22] = {.debug_name = "TS2_I2C_BAT_TEMP_LOW_LSB_MSB",
+		{{.name = "temp_low", .mode = S_IRUGO|S_IWUGO},
+		 .show = a6_generic_show, .store = a6_generic_store},
+		.format = format_temp,
+		.r_format = r_format_temp,
+		.id = {TS2_I2C_BAT_TEMP_LOW_LSB, TS2_I2C_BAT_TEMP_LOW_MSB},
+			.num_ids = 2, .ro = 0},
+	[23] = {.debug_name = "TS2_I2C_BAT_TEMP_HIGH_LSB_MSB",
+		{{.name = "temp_high", .mode = S_IRUGO|S_IWUGO},
+		 .show = a6_generic_show, .store = a6_generic_store},
+		.format = format_temp,
+		.r_format = r_format_temp,
+		.id = {TS2_I2C_BAT_TEMP_HIGH_LSB, TS2_I2C_BAT_TEMP_HIGH_MSB},
+			.num_ids = 2, .ro = 0},
+	[24] = {.debug_name = "TS2_I2C_BAT_VOLT_LOW_LSB_MSB",
+		{{.name = "volt_low", .mode = S_IRUGO|S_IWUGO},
+		 .show = a6_generic_show, .store = a6_generic_store},
+		.format = format_voltage,
+		.r_format = r_format_voltage,
+		.id = {TS2_I2C_BAT_VOLT_LOW_LSB, TS2_I2C_BAT_VOLT_LOW_MSB},
+			.num_ids = 2, .ro = 0},
+	[25] = {.debug_name = "TS2_I2C_BAT_RARC_CRIT",
+		{{.name = "rarc_crit", .mode = S_IRUGO|S_IWUGO},
+		 .show = a6_generic_show, .store = a6_generic_store},
+		.id = {TS2_I2C_BAT_RARC_CRIT}, .num_ids = 1, .ro = 0},
+	[26] = {.debug_name = "TS2_I2C_BAT_RARC_LOW_2",
+		{{.name = "rarc_low_2", .mode = S_IRUGO|S_IWUGO},
+		 .show = a6_generic_show, .store = a6_generic_store},
+		.id = {TS2_I2C_BAT_RARC_LOW_2}, .num_ids = 1, .ro = 0},
+	[27] = {.debug_name = "TS2_I2C_BAT_RARC_LOW_1",
+		{{.name = "rarc_low_1", .mode = S_IRUGO|S_IWUGO},
+		 .show = a6_generic_show, .store = a6_generic_store},
+		.id = {TS2_I2C_BAT_RARC_LOW_1}, .num_ids = 1, .ro = 0},
+	[28] = {.debug_name = "TS2_I2C_BAT_RAAC_MSB",
+		{{.name = "getcoulomb", .mode = S_IRUGO|S_IWUGO},
+		 .show = a6_generic_show, .store = a6_generic_store},
+		.format = format_coulomb,
+		.id = {TS2_I2C_BAT_RAAC_LSB, TS2_I2C_BAT_RAAC_MSB},
+			.num_ids = 2, .ro = 0},
+
+	/* puck registers */
+	[29] = {.debug_name = "TS2_I2C_ID",
+		{{.name = "id", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.id = {TS2_I2C_ID}, .num_ids = 1, .ro = 1},
+	[30] = {.debug_name = "TS2_I2C_FLAGS_0",
+		{{.name = "puck_priority", .mode = S_IRUGO|S_IWUGO},
+		.show = a6_generic_show, .store = a6_generic_store},
+		.id = {TS2_I2C_FLAGS_0}, .num_ids = 1, .ro = 0},
+	[31] = {.debug_name = "TS2_I2C_FLAGS_2",
+		{{.name = "charger", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_charge_source_status,
+		.id = {TS2_I2C_FLAGS_2}, .num_ids = 1, .ro = 1},
+
+	/* enumeration registers */
+	[32] = {.debug_name = "VERSION",
+		{{.name = "getversion", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_version,
+		.id = {	TS2_I2C_ENUM_MFGR_ID_LO, TS2_I2C_ENUM_MFGR_ID_HI,
+			TS2_I2C_ENUM_PRODUCT_TYPE_LO, TS2_I2C_ENUM_PRODUCT_TYPE_HI,
+			TS2_I2C_ENUM_SERNO_0, TS2_I2C_ENUM_SERNO_1,
+			TS2_I2C_ENUM_SERNO_2, TS2_I2C_ENUM_SERNO_3,
+			TS2_I2C_ENUM_SERNO_4, TS2_I2C_ENUM_SERNO_5,
+			TS2_I2C_ENUM_SERNO_6, TS2_I2C_ENUM_SERNO_7,
+			TS2_I2C_ENUM_ASSY_REV, TS2_I2C_ENUM_FW_VER_0,
+			TS2_I2C_ENUM_FW_VER_1, TS2_I2C_ENUM_FW_VER_2},
+		.num_ids = 16, .ro = 1},
+
+	/* puck registers */
+	[33] = {.debug_name = "TS2_I2C_V_OFFSET",
+		{{.name = "v_offset", .mode = S_IRUGO|S_IWUGO},
+		.show = a6_generic_show, .store = a6_generic_store},
+		.format = format_v_offset,
+		.id = {TS2_I2C_V_OFFSET}, .num_ids = 1, .ro = 0},
+
+	/* self-wake registers */
+	[34] = {.debug_name = "TS2_I2C_WAKEUP_PERIOD",
+		{{.name = "periodic_wake_bit_params", .mode = S_IRUGO|S_IWUGO},
+		.show = a6_generic_show, .store = a6_generic_store},
+		.id = {TS2_I2C_WAKEUP_PERIOD}, .num_ids = 1, .ro = 0},
+
+	/* command registers */
+	[35] = {.debug_name = "TS2_I2C_COMMAND",
+		{{.name = "command", .mode = S_IWUGO},
+		.show = NULL, .store = a6_generic_store},
+		.id = {TS2_I2C_COMMAND}, .num_ids = 1, .ro = 0},
+
+	/* remote enumeration registers */
+	[36] = {.debug_name = "REMOTE_VERSION",
+		{{.name = "getremoteversion", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_version,
+		.id = {	TS2_I2C_ENUM_REMOTE_MFGR_ID_LO, TS2_I2C_ENUM_REMOTE_MFGR_ID_HI,
+			TS2_I2C_ENUM_REMOTE_PRODUCT_TYPE_LO, TS2_I2C_ENUM_REMOTE_PRODUCT_TYPE_HI,
+			TS2_I2C_ENUM_REMOTE_SERNO_0, TS2_I2C_ENUM_REMOTE_SERNO_1,
+			TS2_I2C_ENUM_REMOTE_SERNO_2, TS2_I2C_ENUM_REMOTE_SERNO_3,
+			TS2_I2C_ENUM_REMOTE_SERNO_4, TS2_I2C_ENUM_REMOTE_SERNO_5,
+			TS2_I2C_ENUM_REMOTE_SERNO_6, TS2_I2C_ENUM_REMOTE_SERNO_7,
+			TS2_I2C_ENUM_REMOTE_ASSY_REV, TS2_I2C_ENUM_REMOTE_FW_VER_0,
+			TS2_I2C_ENUM_REMOTE_FW_VER_1, TS2_I2C_ENUM_REMOTE_FW_VER_2},
+		.num_ids = 16, .ro = 1},
+
+	/* accessory data registers */
+	[37] = {.debug_name = "ACCESSORY_DATA_0",
+		{{.name = "acc_data_0", .mode = S_IRUGO|S_IWUGO},
+		.show = a6_generic_show, .store = a6_generic_store},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_ACCE_0}, .num_ids = 1, .ro = 0},
+
+	[38] = {.debug_name = "ACCESSORY_DATA_1",
+		{{.name = "acc_data_1", .mode = S_IRUGO|S_IWUGO},
+		.show = a6_generic_show, .store = a6_generic_store},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_ACCE_1}, .num_ids = 1, .ro = 0},
+
+	[39] = {.debug_name = "ACCESSORY_DATA_2",
+		{{.name = "acc_data_2", .mode = S_IRUGO|S_IWUGO},
+		.show = a6_generic_show, .store = a6_generic_store},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_ACCE_2}, .num_ids = 1, .ro = 0},
+
+	[40] = {.debug_name = "ACCESSORY_DATA_3",
+		{{.name = "acc_data_3", .mode = S_IRUGO|S_IWUGO},
+		.show = a6_generic_show, .store = a6_generic_store},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_ACCE_3}, .num_ids = 1, .ro = 0},
+
+	[41] = {.debug_name = "ACCESSORY_DATA_4",
+		{{.name = "acc_data_4", .mode = S_IRUGO|S_IWUGO},
+		.show = a6_generic_show, .store = a6_generic_store},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_ACCE_4}, .num_ids = 1, .ro = 0},
+
+	[42] = {.debug_name = "ACCESSORY_DATA_5",
+		{{.name = "acc_data_5", .mode = S_IRUGO|S_IWUGO},
+		.show = a6_generic_show, .store = a6_generic_store},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_ACCE_5}, .num_ids = 1, .ro = 0},
+
+	[43] = {.debug_name = "ACCESSORY_DATA_6",
+		{{.name = "acc_data_6", .mode = S_IRUGO|S_IWUGO},
+		.show = a6_generic_show, .store = a6_generic_store},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_ACCE_6}, .num_ids = 1, .ro = 0},
+
+	[44] = {.debug_name = "ACCESSORY_DATA_7",
+		{{.name = "acc_data_7", .mode = S_IRUGO|S_IWUGO},
+		.show = a6_generic_show, .store = a6_generic_store},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_ACCE_7}, .num_ids = 1, .ro = 0},
+
+	[45] = {.debug_name = "ACCESSORY_DATA_8",
+		{{.name = "acc_data_8", .mode = S_IRUGO|S_IWUGO},
+		.show = a6_generic_show, .store = a6_generic_store},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_ACCE_8}, .num_ids = 1, .ro = 0},
+
+	[46] = {.debug_name = "ACCESSORY_DATA_9",
+		{{.name = "acc_data_9", .mode = S_IRUGO|S_IWUGO},
+		.show = a6_generic_show, .store = a6_generic_store},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_ACCE_9}, .num_ids = 1, .ro = 0},
+
+	[47] = {.debug_name = "ACCESSORY_DATA_10",
+		{{.name = "acc_data_10", .mode = S_IRUGO|S_IWUGO},
+		.show = a6_generic_show, .store = a6_generic_store},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_ACCE_10}, .num_ids = 1, .ro = 0},
+
+	[48] = {.debug_name = "ACCESSORY_DATA_11",
+		{{.name = "acc_data_11", .mode = S_IRUGO|S_IWUGO},
+		.show = a6_generic_show, .store = a6_generic_store},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_ACCE_11}, .num_ids = 1, .ro = 0},
+
+	[49] = {.debug_name = "ACCESSORY_DATA_12",
+		{{.name = "acc_data_12", .mode = S_IRUGO|S_IWUGO},
+		.show = a6_generic_show, .store = a6_generic_store},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_ACCE_12}, .num_ids = 1, .ro = 0},
+
+	/* remote accessory data registers */
+	[50] = {.debug_name = "REMOTE_ACCESSORY_DATA_0",
+		{{.name = "remote_acc_data_0", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_REMOTE_ACCE_0}, .num_ids = 1, .ro = 1},
+
+	[51] = {.debug_name = "REMOTE_ACCESSORY_DATA_1",
+		{{.name = "remote_acc_data_1", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_REMOTE_ACCE_1}, .num_ids = 1, .ro = 1},
+
+	[52] = {.debug_name = "REMOTE_ACCESSORY_DATA_2",
+		{{.name = "remote_acc_data_2", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_REMOTE_ACCE_2}, .num_ids = 1, .ro = 1},
+
+	[53] = {.debug_name = "REMOTE_ACCESSORY_DATA_3",
+		{{.name = "remote_acc_data_3", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_REMOTE_ACCE_3}, .num_ids = 1, .ro = 1},
+
+	[54] = {.debug_name = "REMOTE_ACCESSORY_DATA_4",
+		{{.name = "remote_acc_data_4", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_REMOTE_ACCE_4}, .num_ids = 1, .ro = 1},
+
+	[55] = {.debug_name = "REMOTE_ACCESSORY_DATA_5",
+		{{.name = "remote_acc_data_5", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_REMOTE_ACCE_5}, .num_ids = 1, .ro = 1},
+
+	[56] = {.debug_name = "REMOTE_ACCESSORY_DATA_6",
+		{{.name = "remote_acc_data_6", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_REMOTE_ACCE_6}, .num_ids = 1, .ro = 1},
+
+	[57] = {.debug_name = "REMOTE_ACCESSORY_DATA_7",
+		{{.name = "remote_acc_data_7", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_REMOTE_ACCE_7}, .num_ids = 1, .ro = 1},
+
+	[58] = {.debug_name = "REMOTE_ACCESSORY_DATA_8",
+		{{.name = "remote_acc_data_8", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_REMOTE_ACCE_8}, .num_ids = 1, .ro = 1},
+
+	[59] = {.debug_name = "REMOTE_ACCESSORY_DATA_9",
+		{{.name = "remote_acc_data_9", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_REMOTE_ACCE_9}, .num_ids = 1, .ro = 1},
+
+	[60] = {.debug_name = "REMOTE_ACCESSORY_DATA_10",
+		{{.name = "remote_acc_data_10", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_REMOTE_ACCE_10}, .num_ids = 1, .ro = 1},
+
+	[61] = {.debug_name = "REMOTE_ACCESSORY_DATA_11",
+		{{.name = "remote_acc_data_11", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_REMOTE_ACCE_11}, .num_ids = 1, .ro = 1},
+
+	[62] = {.debug_name = "REMOTE_ACCESSORY_DATA_12",
+		{{.name = "remote_acc_data_12", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_REMOTE_ACCE_12}, .num_ids = 1, .ro = 1},
+
+	[63] = {.debug_name = "TS2_I2C_COMM_STATUS",
+		{{.name = "get_comm_status", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_comm_status,
+		.id = {TS2_I2C_COMM_STATUS}, .num_ids = 1, .ro = 1},
+
+	[64] = {.debug_name = "TS2_I2C_COMM_TXDATA_RXDATA",
+		{{.name = "comm_txdata_rx_data", .mode = S_IRUGO|S_IWUGO},
+		.show = a6_generic_show, .store = a6_generic_store},
+		.format = format_comm_status,
+		.id = {TS2_I2C_COMM_TXDATA_RXDATA}, .num_ids = 1, .ro = 0},
+
+	[65] = { .debug_name = "TS2_I2C_BAT_SACR_LSB_MSB",
+		{{.name = "getsacr", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_rawcoulomb,
+		.id = {TS2_I2C_BAT_SACR_LSB, TS2_I2C_BAT_SACR_MSB},
+			.num_ids = 2, .ro = 0},
+
+	[66] = { .debug_name = "TS2_I2C_BAT_ASL",
+		{{.name = "getasl", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_raw_unsigned,
+		.id = {TS2_I2C_BAT_ASL}, .num_ids = 1, .ro = 0},
+
+	[67] = { .debug_name = "TS2_I2C_BAT_AS",
+		{{.name = "getrawas", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_raw_unsigned,
+		.id = {TS2_I2C_BAT_AS}, .num_ids = 1, .ro = 0},
+
+	[68] = { .debug_name = "TS2_I2C_BAT_FAC_LSB_MSB",
+		{{.name = "getfac", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_rawcoulomb,
+		.id = {TS2_I2C_BAT_FAC_LSB, TS2_I2C_BAT_FAC_MSB},
+			.num_ids = 2, .ro = 0},
+
+	[69] = {.debug_name = "MAX_POWER_AVAILABLE",
+		{{.name = "getmaxpoweravail", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_max_power_available,
+		.id = { TS2_I2C_ENUM_REMOTE_VNODE_MAX_LO, TS2_I2C_ENUM_REMOTE_VNODE_MAX_HI,
+			TS2_I2C_ENUM_REMOTE_INODE_MAX_LO, TS2_I2C_ENUM_REMOTE_INODE_MAX_HI,
+			TS2_I2C_ENUM_REMOTE_POWER_MAX},
+		.num_ids = 5, .ro = 1},
+
+	[70] = {.debug_name = "TS2_I2C_ENUM_REMOTE_STRUCT_VER",
+		{{.name = "remote_struct_ver", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.id = {TS2_I2C_ENUM_REMOTE_STRUCT_VER}, .num_ids = 1, .ro = 1},
+
+	[71] = {.debug_name = "ACCESSORY_DATA_13",
+		{{.name = "acc_data_13", .mode = S_IRUGO|S_IWUGO},
+		.show = a6_generic_show, .store = a6_generic_store},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_ACCE_13}, .num_ids = 1, .ro = 0},
+
+	[72] = {.debug_name = "ACCESSORY_DATA_14",
+		{{.name = "acc_data_14", .mode = S_IRUGO|S_IWUGO},
+		.show = a6_generic_show, .store = a6_generic_store},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_ACCE_14}, .num_ids = 1, .ro = 0},
+
+	[73] = {.debug_name = "ACCESSORY_DATA_15",
+		{{.name = "acc_data_15", .mode = S_IRUGO|S_IWUGO},
+		.show = a6_generic_show, .store = a6_generic_store},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_ACCE_15}, .num_ids = 1, .ro = 0},
+
+	[74] = {.debug_name = "REMOTE_ACCESSORY_DATA_13",
+		{{.name = "remote_acc_data_13", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_REMOTE_ACCE_13}, .num_ids = 1, .ro = 1},
+
+	[75] = {.debug_name = "REMOTE_ACCESSORY_DATA_14",
+		{{.name = "remote_acc_data_14", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_REMOTE_ACCE_14}, .num_ids = 1, .ro = 1},
+
+	[76] = {.debug_name = "REMOTE_ACCESSORY_DATA_15",
+		{{.name = "remote_acc_data_15", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_accessory,
+		.id = {	TS2_I2C_ENUM_REMOTE_ACCE_15}, .num_ids = 1, .ro = 1},
+
+	[77] = {.debug_name = "TS2_I2C_ENUM_MIN_PWM",
+		{{.name = "min_pwm", .mode = S_IRUGO|S_IWUGO},
+		.show = a6_generic_show, .store = a6_generic_store},
+		.id = {	TS2_I2C_ENUM_MIN_PWM}, .num_ids = 1, .ro = 0},
+
+	[78] = {.debug_name = "ACCESSORY_DATA_COMBO",
+		{{.name = "acc_data_combo", .mode = S_IRUGO|S_IWUGO},
+		.show = a6_generic_show, .store = a6_generic_store},
+		.format = format_accessory_combo,
+		.id = { TS2_I2C_ENUM_ACCE_0, TS2_I2C_ENUM_ACCE_1, TS2_I2C_ENUM_ACCE_2,
+			TS2_I2C_ENUM_ACCE_3, TS2_I2C_ENUM_ACCE_4, TS2_I2C_ENUM_ACCE_5,
+			TS2_I2C_ENUM_ACCE_6, TS2_I2C_ENUM_ACCE_7, TS2_I2C_ENUM_ACCE_8,
+			TS2_I2C_ENUM_ACCE_9, TS2_I2C_ENUM_ACCE_10, TS2_I2C_ENUM_ACCE_11,
+			TS2_I2C_ENUM_ACCE_12, TS2_I2C_ENUM_ACCE_13, TS2_I2C_ENUM_ACCE_14,
+			TS2_I2C_ENUM_ACCE_15},
+		.num_ids = 16, .ro = 0},
+
+	[79] = {.debug_name = "REMOTE_ACCESSORY_DATA_COMBO",
+		{{.name = "remote_acc_data_combo", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_accessory_combo,
+		.id = { TS2_I2C_ENUM_REMOTE_ACCE_0, TS2_I2C_ENUM_REMOTE_ACCE_1,
+			TS2_I2C_ENUM_REMOTE_ACCE_2, TS2_I2C_ENUM_REMOTE_ACCE_3,
+			TS2_I2C_ENUM_REMOTE_ACCE_4, TS2_I2C_ENUM_REMOTE_ACCE_5,
+			TS2_I2C_ENUM_REMOTE_ACCE_6, TS2_I2C_ENUM_REMOTE_ACCE_7,
+			TS2_I2C_ENUM_REMOTE_ACCE_8, TS2_I2C_ENUM_REMOTE_ACCE_9,
+			TS2_I2C_ENUM_REMOTE_ACCE_10, TS2_I2C_ENUM_REMOTE_ACCE_11,
+			TS2_I2C_ENUM_REMOTE_ACCE_12, TS2_I2C_ENUM_REMOTE_ACCE_13,
+			TS2_I2C_ENUM_REMOTE_ACCE_14, TS2_I2C_ENUM_REMOTE_ACCE_15},
+		.num_ids = 16, .ro = 1},
+
+	[80] = {.debug_name = "TS2_I2C_COMMAND_ADDR_LSB_MSB",
+		{{.name = "command_addr", .mode = S_IRUGO|S_IWUGO},
+		.show = a6_generic_show, .store = a6_generic_store},
+		.id = {TS2_I2C_COMMAND_ADDR_LSB, TS2_I2C_COMMAND_ADDR_MSB},
+		.format = format_u16_hex,
+		.num_ids = 2, .ro = 0},
+	[81] = {.debug_name = "TS2_I2C_COMMAND_DATA_LSB_MSB",
+		{{.name = "command_data", .mode = S_IRUGO|S_IWUGO},
+		.show = a6_generic_show, .store = a6_generic_store},
+		.id = {TS2_I2C_COMMAND_DATA_LSB, TS2_I2C_COMMAND_DATA_MSB},
+		.format = format_u16_hex,
+		.num_ids = 2, .ro = 0},
+
+	[82] = {.debug_name = "REMOTE_MFGRID_V1",
+		{{.name = "remote_mfgrid_v1", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_u16_hex,
+		.id = { TS2_I2C_ENUM_REMOTE_MFGR_ID_LO_V1,
+			TS2_I2C_ENUM_REMOTE_MFGR_ID_HI_V1},
+		.num_ids = 2, .ro = 1},
+	[83] = {.debug_name = "REMOTE_MFGRID_V2",
+		{{.name = "remote_mfgrid_v2", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_u16_hex,
+		.id = { TS2_I2C_ENUM_REMOTE_MFGR_ID_LO,
+			TS2_I2C_ENUM_REMOTE_MFGR_ID_HI},
+		.num_ids = 2, .ro = 1},
+	[84] = {.debug_name = "REMOTE_PRODUCTID_V1",
+		{{.name = "remote_productid_v1", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_u16_hex,
+		.id = { TS2_I2C_ENUM_REMOTE_PRODUCT_TYPE_LO_V1,
+			TS2_I2C_ENUM_REMOTE_PRODUCT_TYPE_HI_V1},
+		.num_ids = 2, .ro = 1},
+	[85] = {.debug_name = "REMOTE_PRODUCTID_V2",
+		{{.name = "remote_productid_v2", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_u16_hex,
+		.id = { TS2_I2C_ENUM_REMOTE_PRODUCT_TYPE_LO,
+			TS2_I2C_ENUM_REMOTE_PRODUCT_TYPE_HI},
+		.num_ids = 2, .ro = 1},
+	[86] = {.debug_name = "REMOTE_SERNO_V1",
+		{{.name = "remote_serno_v1", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_serno_v1,
+		.id = { TS2_I2C_ENUM_REMOTE_SERNO_0, TS2_I2C_ENUM_REMOTE_SERNO_1,
+			TS2_I2C_ENUM_REMOTE_SERNO_2, TS2_I2C_ENUM_REMOTE_SERNO_3,
+			TS2_I2C_ENUM_REMOTE_SERNO_4, TS2_I2C_ENUM_REMOTE_SERNO_5},
+		.num_ids = 6, .ro = 1},
+	[87] = {.debug_name = "REMOTE_SERNO_V2",
+		{{.name = "remote_serno_v2", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_serno_v2,
+		.id = { TS2_I2C_ENUM_REMOTE_SERNO_0, TS2_I2C_ENUM_REMOTE_SERNO_1,
+			TS2_I2C_ENUM_REMOTE_SERNO_2, TS2_I2C_ENUM_REMOTE_SERNO_3,
+			TS2_I2C_ENUM_REMOTE_SERNO_4, TS2_I2C_ENUM_REMOTE_SERNO_5,
+			TS2_I2C_ENUM_REMOTE_SERNO_6, TS2_I2C_ENUM_REMOTE_SERNO_7},
+		.num_ids = 8, .ro = 1},
+	[88] = {.debug_name = "LOCAL_MFGRID",
+		{{.name = "local_mfgrid", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_u16_hex,
+		.id = { TS2_I2C_ENUM_MFGR_ID_LO, TS2_I2C_ENUM_MFGR_ID_HI},
+		.num_ids = 2, .ro = 1},
+	[89] = {.debug_name = "LOCAL_PRODUCTID",
+		{{.name = "local_productid", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_u16_hex,
+		.id = { TS2_I2C_ENUM_PRODUCT_TYPE_LO, TS2_I2C_ENUM_PRODUCT_TYPE_HI},
+		.num_ids = 2, .ro = 1},
+	[90] = {.debug_name = "LOCAL_SERNO",
+		{{.name = "local_serno", .mode = S_IRUGO},
+		.show = a6_generic_show, .store = NULL},
+		.format = format_serno_v2,
+		.id = { TS2_I2C_ENUM_SERNO_0, TS2_I2C_ENUM_SERNO_1,
+			TS2_I2C_ENUM_SERNO_2, TS2_I2C_ENUM_SERNO_3,
+			TS2_I2C_ENUM_SERNO_4, TS2_I2C_ENUM_SERNO_5,
+			TS2_I2C_ENUM_SERNO_6, TS2_I2C_ENUM_SERNO_7},
+			.num_ids = 8, .ro = 1},
+};
+
+
+struct device_attribute custom_devattr[] = {
+	{{.name = "a6_diag", .mode = S_IRUGO | S_IWUGO},
+	 .show = a6_diag_show, .store = a6_diag_store},
+ 	{{.name = "validate_cksum", .mode = S_IRUGO},
+ 	 .show = a6_val_cksum_show, .store = NULL},
+};
+
+static enum power_supply_property a6_fish_battery_properties[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_CHARGE_FULL,
+	POWER_SUPPLY_PROP_CHARGE_NOW,
+};
+
+static enum power_supply_property a6_fish_power_properties[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+};
+
+static char *supply_list[] = {
+	"battery",
+};
+
+static int a6_fish_power_get_property(struct power_supply *psy,
+				   enum power_supply_property psp,
+				   union power_supply_propval *val);
+
+static int a6_fish_battery_get_property(struct power_supply *psy,
+				     enum power_supply_property psp,
+				     union power_supply_propval *val);
+
+static struct power_supply a6_fish_power_supplies[] = {
+	{
+		.name = "battery",
+		.type = POWER_SUPPLY_TYPE_BATTERY,
+		.properties = a6_fish_battery_properties,
+		.num_properties = ARRAY_SIZE(a6_fish_battery_properties),
+		.get_property = a6_fish_battery_get_property,
+	},
+	{
+		.name = "ac",
+		.type = POWER_SUPPLY_TYPE_MAINS,
+		.supplied_to = supply_list,
+		.num_supplicants = ARRAY_SIZE(supply_list),
+		.properties = a6_fish_power_properties,
+		.num_properties = ARRAY_SIZE(a6_fish_power_properties),
+		.get_property = a6_fish_power_get_property,
+	},
+	{
+		.name = "usb",
+		.type = POWER_SUPPLY_TYPE_USB,
+		.supplied_to = supply_list,
+		.num_supplicants = ARRAY_SIZE(supply_list),
+		.properties = a6_fish_power_properties,
+		.num_properties = ARRAY_SIZE(a6_fish_power_properties),
+		.get_property = a6_fish_power_get_property,
+	},
+#ifdef CONFIG_A6_ENABLE_DOCK_PS
+	{
+		.name = "dock",
+		.type = POWER_SUPPLY_TYPE_MAINS,
+		.supplied_to = supply_list,
+		.num_supplicants = ARRAY_SIZE(supply_list),
+		.properties = a6_fish_power_properties,
+		.num_properties = ARRAY_SIZE(a6_fish_power_properties),
+		.get_property = a6_fish_power_get_property,
+	},
+#endif
+};
+
+#ifdef A6_PQ
+// a6 debugfs interface...
+#ifdef A6_DEBUG
+
+
+static int32_t a6_restart_aid_thread_fn(void* param)
+{
+	struct a6_device_state* state = param;
+	int32_t rc = 0;
+
+	daemonize("dbg_aidrst_%s", state->plat_data->dev_name);
+	while (!state->dbgflg_kill_raid) {
+		// stop aid task
+		rc = a6_stop_ai_dispatch_task(state);
+		if (rc) {
+			printk(KERN_ERR "%s: failed to stop ai_dispatch_task.\n", __func__);
+			break;
+		}
+
+		// re-start aid task
+		rc = a6_start_ai_dispatch_task(state);
+		if (rc) {
+			printk(KERN_ERR "%s: failed to start ai_dispatch_task.\n", __func__);
+			break;
+		}
+
+		// sleep
+		msleep(500);
+	}
+
+	rc = mutex_lock_interruptible(&state->dev_mutex);
+	if (rc) {
+		printk(KERN_ERR "%s: mutex_lock_interruptible interrupted\n", __func__);
+		return -ERESTARTSYS;
+	}
+	state->debug_restart_aid = 0;
+	state->dbgflg_kill_raid = 0;
+	mutex_unlock(&state->dev_mutex);
+
+	return rc;
+}
+
+static int a6_test_restart_aid_set(void *data, u64 val)
+{
+	int32_t rc = 0;
+	pid_t aiq_flush_pid = 0;
+	struct a6_device_state* state = data;
+	uint8_t in_val, curr_val;
+
+	in_val = val ? 1 : val;
+	rc = mutex_lock_interruptible(&state->dev_mutex);
+	if (rc) {
+		printk(KERN_ERR "%s: mutex_lock_interruptible interrupted\n", __func__);
+		return -ERESTARTSYS;
+	}
+	curr_val = state->debug_restart_aid;
+
+	// are we actually changing state?
+	if (in_val ^ curr_val) {
+		if (in_val) {
+			// prev task still being killed or active: fail
+			if (state->dbgflg_kill_raid || state->debug_restart_aid) {
+				printk(KERN_ERR "%s: prev task still being killed or active: fail\n",
+				       __func__);
+				goto err0;
+			}
+			// create ai dispatcher task...
+			aiq_flush_pid = kernel_thread(a6_restart_aid_thread_fn, state, CLONE_KERNEL);
+			ASSERT(aiq_flush_pid >= 0);
+			state->debug_restart_aid = in_val;
+		}
+		else {
+			// prev task still being killed or not active: fail
+			if (state->dbgflg_kill_raid || !state->debug_restart_aid) {
+				printk(KERN_ERR "%s: prev task still being killed or not active: fail\n",
+				       __func__);
+				goto err0;
+			}
+			// intent to kill task; task resets state as part of teardown
+			state->dbgflg_kill_raid = 1;
+		}
+	}
+	mutex_unlock(&state->dev_mutex);
+
+
+err0:
+	return rc;
+}
+static int a6_test_restart_aid_get(void *data, u64 *val)
+{
+	struct a6_device_state* state = data;
+
+	*val = state->debug_restart_aid;
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(fops_a6_test_restart_aid, a6_test_restart_aid_get, a6_test_restart_aid_set, "%llu\n");
+
+static int32_t a6_create_debug_interface(struct a6_device_state* state)
+{
+	int32_t rc = 0;
+	struct dentry *dentry_parent, *dentry_child;
+
+	dentry_parent = debugfs_create_dir("a6", 0);
+	if (IS_ERR(dentry_parent)) {
+		rc = PTR_ERR(dentry_parent);
+		goto err0;
+	}
+
+	dentry_child = debugfs_create_file("periodic_restart_aid", 0644,
+					   dentry_parent, state, &fops_a6_test_restart_aid);
+	if (IS_ERR(dentry_child)) {
+		rc = PTR_ERR(dentry_child);
+		goto err0;
+	}
+
+	return 0;
+
+err0:
+	debugfs_remove_recursive(dentry_parent);
+	return rc;
+}
+#endif
+#endif // A6_PQ
+
+#ifdef A6_PQ
+// a6 action item types
+enum {
+	AI_I2C_TYPE,
+};
+
+struct a6_action_item {
+	void* ai_payload;
+	uint32_t ai_type: 4;
+	uint32_t ai_complete:1;
+	int32_t (*ai_do_action)(void* payload_param);
+	int32_t* (*ai_ret_code)(void* payload_param);
+	wait_queue_head_t ai_waitq;
+	struct list_head list;
+};
+
+/*
+ enq_a6_action_item: q's action item and blocks till action completion...
+*/
+int32_t enq_a6_action_item(struct a6_device_state* state, struct a6_action_item* ai, bool enq_tail)
+{
+	int32_t rc = 0;
+
+	mutex_lock(&state->aq_mutex);
+	if (true == enq_tail) {
+		list_add_tail(&ai->list, &state->aq_head);
+	}
+	else {
+		list_add(&ai->list, &state->aq_head);
+	}
+	mutex_unlock(&state->aq_mutex);
+
+	// signal the action_item dispatcher thread
+	complete(&state->aq_enq_complete);
+
+	// ** wait for action_item to be processed
+	a6_wait_event_ex(ai->ai_waitq, ai->ai_complete);
+	rc = *ai->ai_ret_code(ai->ai_payload);
+
+	return rc;
+}
+
+/*
+ dq_a6_action_item: dq's head item from action item list...
+*/
+struct a6_action_item* dq_a6_action_item(struct a6_device_state* state)
+{
+	struct a6_action_item* ai = NULL;
+
+	mutex_lock(&state->aq_mutex);
+	if (!list_empty(&state->aq_head)) {
+		ai = list_first_entry(&state->aq_head, struct a6_action_item, list);
+		list_del(state->aq_head.next);
+	}
+	mutex_unlock(&state->aq_mutex);
+
+	return ai;
+}
+
+/*
+ flush_a6_action_items: iterates action item list, dq's each item, marks it cancelled
+			and complete and then wakes up the requestor...
+*/
+int32_t flush_a6_action_items(struct a6_device_state* state)
+{
+	int32_t rc = 0;
+	struct a6_action_item* ai = NULL;
+
+	// lock list until all items removed
+	mutex_lock(&state->aq_mutex);
+	while (!list_empty(&state->aq_head)) {
+		// get first entry...
+		ai = list_first_entry(&state->aq_head, struct a6_action_item, list);
+		// ... and remove from list
+		list_del(state->aq_head.next);
+		*ai->ai_ret_code(ai->ai_payload) = -ECANCELED;
+		// set completion indicator
+		ai->ai_complete = 1;
+		// signal ai requestor
+		wake_up(&ai->ai_waitq);
+	}
+	mutex_unlock(&state->aq_mutex);
+
+	return rc;
+}
+
+
+struct ai_i2c_payload {
+	struct i2c_client* client;
+	struct i2c_msg* msg;
+	uint32_t num_msgs;
+	int32_t rc;
+};
+
+/*
+ do_i2c_action_item: i2c-specific action implementation...
+*/
+int32_t do_i2c_action_item(void* payload_param)
+{
+	struct ai_i2c_payload* trf_data = (struct ai_i2c_payload*)payload_param;
+
+	trf_data->rc = i2c_transfer(trf_data->client->adapter, trf_data->msg, trf_data->num_msgs);
+	udelay(700);
+	if (trf_data->rc < 0) {
+		printk(KERN_ERR "%s: err code: %d\n", __func__, trf_data->rc);
+		goto err0;
+	}
+
+	trf_data->rc = 0;
+err0:
+	return trf_data->rc;
+}
+
+/*
+ i2c_ret_code: i2c-specific ret-code reference retrieval...
+*/
+int32_t* i2c_ret_code(void* payload_param)
+{
+	struct ai_i2c_payload* trf_data = (struct ai_i2c_payload*)payload_param;
+
+	return (&trf_data->rc);
+}
+
+/*
+ q_a6_i2c_action_item: creates the action item structure and enq's it...
+*/
+int32_t q_a6_i2c_action_item(struct i2c_client* client, struct i2c_msg* msg, uint32_t num_msgs)
+{
+	int32_t ret = 0;
+	struct ai_i2c_payload data = {
+		.client = client,
+		.msg = msg,
+		.num_msgs = num_msgs,
+		.rc = 0
+	};
+
+	struct a6_action_item a6_i2c_ai = {
+		.ai_payload = &data,
+		.ai_type = AI_I2C_TYPE,
+		.ai_complete = 0,
+		.ai_do_action = do_i2c_action_item,
+		.ai_ret_code = i2c_ret_code,
+		.list = LIST_HEAD_INIT(a6_i2c_ai.list)
+	};
+
+	init_waitqueue_head(&a6_i2c_ai.ai_waitq);
+	ret = enq_a6_action_item(i2c_get_clientdata(client), &a6_i2c_ai, true/*enq tail*/);
+
+	return ret;
+}
+
+/*
+ ai_dispatch_thread_fn: function that handles ai processing in the aid task...
+*/
+int ai_dispatch_thread_fn(void* param)
+{
+	int32_t rc = 0;
+	struct a6_action_item* ai;
+	struct a6_device_state* state = param;
+
+	daemonize("aid_%s", state->plat_data->dev_name);
+	do {
+		A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: about to wait for ai enq complete.\n", __func__);
+		// wait for ai to be enq'd
+		rc = wait_for_completion_interruptible(&state->aq_enq_complete);
+		if (rc >= 0) {
+			// check kill status: intent to kill?
+			if (test_bit(KILLING_AID_TASK, state->flags)) {
+				// remove all items from q
+				flush_a6_action_items(state);
+				// and signal killer before exiting stage...
+				complete(&state->aid_exit_complete);
+				break;
+			}
+
+			ai = dq_a6_action_item(state);
+			if (ai) {
+				int32_t ret_val;
+
+				A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: got ai.\n", __func__);
+				// invoke ai-specific action
+				ret_val = ai->ai_do_action(ai->ai_payload);
+				if (ret_val) {
+					printk(KERN_ERR "%s: ai_do_action failed.\n", __func__);
+				}
+
+				// set completion indicator
+				ai->ai_complete = 1;
+				// signal ai requestor
+				wake_up(&ai->ai_waitq);
+			}
+			else {
+				A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: no ai.\n", __func__);
+			}
+		}
+		// wait interrupted by -ERESTARTSYS: let's exit
+		else {
+			printk(KERN_ERR "%s: wait for action_item enq interrupted.\n",
+			       __func__);
+			// force a panic...
+			BUG_ON(rc < 0);
+		}
+	} while(rc >= 0);
+
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "*** %s: exiting ai_dispatch_thread_fn.***\n", __func__);
+
+	return rc;
+}
+#endif  // A6_PQ
+
+
+int32_t __a6_i2c_read_reg(struct i2c_client* client, const uint16_t* ids, uint32_t num_ids, uint8_t* out)
+{
+	int32_t ret = 0, i;
+	uint16_t swp_addr[num_ids];
+	struct i2c_msg msg[num_ids*2], *msg_itr;
+	struct a6_device_state* state = i2c_get_clientdata(client);
+#ifdef A6_I2C_PROFILE
+	ktime_t start, end;
+#endif
+
+#ifdef A6_PQ
+	if (test_bit(IS_QUIESCED, ((struct a6_device_state*)i2c_get_clientdata(client))->flags)) {
+		ret = -ECANCELED;
+		goto err0;
+	}
+#endif // A6_PQ
+
+	// force A6 wakeup...
+	if (start_last_a6_activity) {
+		long diff_time = (long)jiffies - (long)start_last_a6_activity;
+		A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: time since last activity: %ld ms\n",
+		       __func__, diff_time * 1000/HZ);
+	}
+	start_last_a6_activity = jiffies;
+
+	// prevent timer expiry during force_wake related action...
+	del_timer(&state->a6_force_wake_timer);
+
+	mutex_lock(&state->a6_force_wake_mutex);
+	// a6 external wake enabled?
+	if (test_bit(CAP_PERIODIC_WAKE, state->flags)) {
+		if (!test_bit(FORCE_WAKE_ACTIVE_BIT, state->flags)) {
+			struct a6_wake_ops* wake_ops = (struct a6_wake_ops*)state->plat_data->wake_ops;
+
+			A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR,
+			   	"%s: disabling periodic_wake and switching to force_wake\n",
+			   	__func__);
+
+			// periodic wake active: disable it and switch to force wake
+			if (wake_ops->disable_periodic_wake) {
+				wake_ops->disable_periodic_wake(wake_ops->data);
+			}
+			if (wake_ops->force_wake) {
+				wake_ops->force_wake(wake_ops->data);
+			}
+
+			set_bit(FORCE_WAKE_ACTIVE_BIT, state->flags);
+		}
+	}
+	mutex_unlock(&state->a6_force_wake_mutex);
+
+#ifdef A6_I2C_PROFILE
+	start = ktime_get();
+#endif
+	msg_itr = &msg[0];
+	for (i = num_ids-1; i >= 0; i--) {
+		msg_itr->addr = client->addr;
+		msg_itr->flags = 0;
+		msg_itr->len = sizeof(uint16_t);
+		swp_addr[i] = ids[i] >> 8 | ids[i] << 8;
+		msg_itr->buf = (uint8_t*)&swp_addr[i];
+
+		(msg_itr+1)->addr = client->addr;
+		(msg_itr+1)->flags = I2C_M_RD;
+		(msg_itr+1)->len = sizeof(uint8_t);
+		(msg_itr+1)->buf = &out[i];
+#ifdef CONFIG_A6_I2C_SINGLE_BYTE
+#ifdef A6_PQ
+		ret = q_a6_i2c_action_item(client, msg, 2);
+#else
+		ret = i2c_transfer(client->adapter, msg, 2);
+#endif // A6_PQ
+#endif
+		msg_itr += 2;
+	}
+
+
+#ifndef CONFIG_A6_I2C_SINGLE_BYTE
+#ifdef A6_PQ
+	ret = q_a6_i2c_action_item(client, msg, num_ids*2);
+#else
+	ret = i2c_transfer(client->adapter, msg, num_ids*2);
+#endif // A6_PQ
+#endif
+	//msleep(1);
+#ifdef A6_I2C_PROFILE
+	{
+		end = ktime_get();
+		printk(KERN_ERR "%s[0x%02x]: elpased time: %lld\n",
+		       __func__, client->addr, ktime_to_ns(ktime_sub(end, start)));
+	}
+#endif
+	if (ret < 0) {
+		printk(KERN_ERR "%s[0x%02x]: err code: %d\n", __func__, client->addr, ret);
+		// reset the force_wake timer inedependent of i2c failure
+		//goto err0;
+	}
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "ret val: 0x%x\n", *out);
+
+	if (test_bit(CAP_PERIODIC_WAKE, state->flags)) {
+		// [re]start force-wake expiry timer
+		mod_timer(&state->a6_force_wake_timer, jiffies+FORCE_WAKE_TIMER_EXPIRY);
+	}
+
+err0:
+	return ret;
+}
+
+int32_t a6_i2c_read_reg(struct i2c_client* client, const uint16_t* ids, uint32_t num_ids, uint8_t* out)
+{
+	int32_t ret;
+#ifdef A6_I2C_RETRY
+	int32_t retry = 5;
+#else
+	int32_t retry = 0;
+#endif
+
+	do {
+		ret = __a6_i2c_read_reg(client, ids, num_ids, out);
+		if (ret < 0) {
+			printk("%s: a6 i2c transaction failed. %s...\n", __func__, retry ? "retry" : " ");
+			msleep(30);
+		}
+	} while (0 != ret && retry-- > 0);
+
+	return ret;
+}
+
+int32_t __a6_i2c_write_reg(struct i2c_client* client, const uint16_t* ids, uint32_t num_ids, const uint8_t* in)
+{
+	int32_t ret = 0, i;
+	uint8_t i2c_buf[(2+1)*num_ids];
+	struct i2c_msg msg[num_ids], *msg_itr;
+	struct a6_device_state* state = i2c_get_clientdata(client);
+
+#ifdef A6_PQ
+	if (test_bit(IS_QUIESCED, ((struct a6_device_state*)i2c_get_clientdata(client))->flags)) {
+		ret = -ECANCELED;
+		goto err0;
+	}
+#endif // A6_PQ
+
+	// force A6 wakeup...
+	if (start_last_a6_activity) {
+		long diff_time = (long)jiffies - (long)start_last_a6_activity;
+		A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: time since last activity: %ld ms\n",
+		       __func__, diff_time * 1000/HZ);
+	}
+	start_last_a6_activity = jiffies;
+
+	// prevent timer expiry during force_wake related action...
+	del_timer(&state->a6_force_wake_timer);
+
+	mutex_lock(&state->a6_force_wake_mutex);
+	if (test_bit(CAP_PERIODIC_WAKE, state->flags)) {
+		if (!test_and_set_bit(FORCE_WAKE_ACTIVE_BIT, state->flags)) {
+			struct a6_wake_ops* wake_ops = (struct a6_wake_ops*)state->plat_data->wake_ops;
+
+			A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR,
+				"%s: disabling periodic_wake and switching to force_wake\n",
+				__func__);
+
+			// periodic wake active: disable it and switch to force wake
+			if (wake_ops->disable_periodic_wake) {
+				wake_ops->disable_periodic_wake(wake_ops->data);
+			}
+			if (wake_ops->force_wake) {
+				wake_ops->force_wake(wake_ops->data);
+			}
+
+			set_bit(FORCE_WAKE_ACTIVE_BIT, state->flags);
+		}
+	}
+	mutex_unlock(&state->a6_force_wake_mutex);
+
+	msg_itr = &msg[0];
+	for (i = num_ids-1; i >= 0; i--) {
+		i2c_buf[(i*(2+1))+0] = (uint8_t)(ids[i] >> 8);
+		i2c_buf[(i*(2+1))+1] = (uint8_t)(ids[i]);
+		i2c_buf[(i*(2+1))+2] = in[i];
+
+		msg_itr->addr = client->addr;
+		msg_itr->flags = 0;
+		msg_itr->len = 2+1;
+		msg_itr->buf = &i2c_buf[i*(2+1)];
+#ifdef CONFIG_A6_I2C_SINGLE_BYTE_WRITE
+#ifdef A6_PQ
+		ret = q_a6_i2c_action_item(client, msg_itr, 1);
+#else
+		ret = i2c_transfer(client->adapter, msg_itr, 1);
+#endif // A6_PQ
+		udelay(700);
+	      if (ret < 0)
+			break;
+#endif
+		msg_itr++;
+	}
+
+#ifndef CONFIG_A6_I2C_SINGLE_BYTE_WRITE
+#ifdef A6_PQ
+	ret = q_a6_i2c_action_item(client, msg, num_ids);
+#else
+	ret = i2c_transfer(client->adapter, msg, num_ids);
+#endif // A6_PQ
+	//msleep(1);
+#endif
+	if (ret < 0) {
+		printk(KERN_ERR "%s[0x%02x]: err code: %d\n", __func__, client->addr, ret);
+		// reset the force_wake timer inedependent of i2c failure
+		//goto err0;
+	}
+
+	if (test_bit(CAP_PERIODIC_WAKE, state->flags)) {
+		// [re]start force-wake expiry timer
+		mod_timer(&state->a6_force_wake_timer, jiffies+FORCE_WAKE_TIMER_EXPIRY);
+	}
+
+err0:
+	return ret;
+}
+
+int32_t a6_i2c_write_reg(struct i2c_client* client, const uint16_t* ids, uint32_t num_ids, const uint8_t* in)
+{
+	int32_t ret;
+#ifdef A6_I2C_RETRY
+	int32_t retry = 5;
+#else
+	int32_t retry = 0;
+#endif
+
+	do {
+		ret = __a6_i2c_write_reg(client, ids, num_ids, in);
+		if (ret < 0) {
+			printk("%s: a6 i2c transaction failed. %s...\n", __func__, retry ? "retry" : " ");
+			msleep(30);
+		}
+	} while (0 != ret && retry-- > 0);
+
+	return ret;
+}
+
+
+uint8_t _convert_hex_char_to_decimal(uint8_t x)
+{
+	x -= '0';
+	if (x > 9) {
+		x = x - ('A' - ('9' + 1));
+		if (x > 15) {
+			x = x - ('a' - 'A');
+		}
+	}
+
+	return x;
+}
+
+int32_t a6_init_state(struct i2c_client *client)
+{
+	int32_t ret = 0;
+	struct a6_device_state* state = i2c_get_clientdata(client);
+	struct a6_register_desc* reg_desc;
+	uint8_t vals[id_size];
+
+	// early initialization of cached rsense to prevent un-initialized usage
+	// due to early-boot i2c failures.
+	state->cached_rsense_val = RSENSE_DEFAULT;
+
+	/* (1) enable external/internal wake, if required */
+	/* periodic wake capability enabled? */
+	if (test_bit(CAP_PERIODIC_WAKE, state->flags)) {
+		struct a6_wake_ops* wake_ops = (struct a6_wake_ops*)state->plat_data->wake_ops;
+
+		// initialize timer used to force sleep after a force wake...
+		setup_timer(&state->a6_force_wake_timer, a6_force_wake_timer_callback, (ulong)state);
+
+		/* enable external periodic wake? */
+		if (wake_ops->enable_periodic_wake) {
+			printk(KERN_ERR "%s: enabling external PMIC-generated A6 wake.\n", __func__);
+
+			wake_ops->enable_periodic_wake(wake_ops->data);
+
+			/* disable a6 internal-wake... */
+			reg_desc = &a6_register_desc_arr[34];
+			/* format wakeup parameters in register format */
+			vals[0] = 0;
+			ret = a6_i2c_write_reg(client, reg_desc->id, reg_desc->num_ids, vals);
+			if (ret < 0) {
+				goto err0;
+			}
+		}
+		/* enable internal periodic wake? */
+		else if (wake_ops->internal_wake_enable_state) {
+			uint32_t wake_period = 0, wake_enable = 1;
+
+			printk(KERN_ERR "%s: enabling A6 internal wake.\n", __func__);
+			/* default wake_period, if required */
+			if (!wake_ops->internal_wake_period) {
+				wake_period = 0x0;
+			}
+			else {
+				wake_period = wake_ops->internal_wake_period(wake_ops->data);
+				if (wake_period > 0x100) {
+					wake_period = 0x100;
+				}
+			}
+
+			reg_desc = &a6_register_desc_arr[34];
+
+			if (!wake_period) {
+				wake_enable = 0x0;
+			}
+
+			// bit [4]: Enable sleep.
+			// bit [3]: Enable automatic wakeup.
+			// bits [2:0]: Sleep period.
+			if (wake_period) {
+				wake_period = find_last_bit((const unsigned long*)&wake_period, 32);
+				wake_period--;
+			}
+			vals[0] = 0x10 | wake_enable << 3 | (wake_period & 0x07);
+			printk(KERN_ERR "%s: TS2_I2C_WAKEUP_PERIOD = 0x%02x\n",
+			       __func__, vals[0]);
+			ret = a6_i2c_write_reg(client, reg_desc->id, reg_desc->num_ids, vals);
+			if (ret < 0) {
+				goto err0;
+			}
+		}
+		else {
+			BUG();
+		}
+	}
+	/* periodic wake capability not defined: force a6 awake using SBW_WAKEUP */
+	else {
+		printk(KERN_ERR "%s: permanently forcing A6 awake.\n", __func__);
+		gpio_set_value(state->plat_data->sbw_wkup_gpio, 1);
+	}
+
+	/* (2) cache rsense val */
+	reg_desc = &a6_register_desc_arr[19];
+
+	memset(vals, 0, sizeof(vals));
+	ret = a6_i2c_read_reg(client, reg_desc->id, reg_desc->num_ids, vals);
+	if (ret < 0) {
+		//goto err0;
+	}
+
+	/* rsense == 0: invalid and results in default
+	   (logic compatibile with legacy w1 driver implementation) */
+	if (vals[0]) {
+		state->cached_rsense_val = 1000/vals[0];
+		if (!state->cached_rsense_val) {
+			state->cached_rsense_val = RSENSE_DEFAULT;
+		}
+	}
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR,
+		   "%s: (cached) rsense value: %u\n", __func__, state->cached_rsense_val);
+
+	/* (3) enable irqs: MASK_A2A_CONNECT_CHANGE, MASK_FLAGS_CHANGE */
+	reg_desc = &a6_register_desc_arr[3];
+
+	vals[0] = 0xf3;
+	ret = a6_i2c_write_reg(client, reg_desc->id, reg_desc->num_ids, vals);
+	if (ret < 0) {
+		goto err0;
+	}
+
+	/* (4) enable irqs:
+	       MASK_BAT_TEMP_HIGH, MASK_BAT_TEMP_LOW,
+	       MASK_BAT_VOLT_LOW, MASK_BAT_RARC_CRIT,
+	       MASK_BAT_RARC_LOW2, MASK_BAT_RARC_LOW1
+	*/
+	reg_desc = &a6_register_desc_arr[2];
+
+	vals[0] = 0xc0;
+	ret = a6_i2c_write_reg(client, reg_desc->id, reg_desc->num_ids, vals);
+	if (ret < 0) {
+		goto err0;
+	}
+	/* read version */
+	reg_desc = &a6_register_desc_arr[32];
+
+	memset(vals, 0, sizeof(vals));
+	ret = a6_i2c_read_reg(client, reg_desc->id, reg_desc->num_ids, vals);
+	if (ret < 0) {
+		printk(KERN_ERR "%s: error reading A6 version\n", __func__);
+	}
+	else {
+		uint8_t buf[150];
+
+		reg_desc->format(state, vals, buf, sizeof(buf));
+		printk(KERN_INFO "%s", buf);
+	}
+
+	// if first init (device boot): clear all interrupt status regs because we might
+	// have missed the level-triggered a6-irq during device boot and the user-space
+	// components read a6 registers to set their initial state correctly...
+	if (!test_bit(IS_INITIALIZED_BIT, state->flags)) {
+		vals[0] = 0xff;
+
+		/* clear int_status3 */
+		reg_desc = &a6_register_desc_arr[7];
+		ret = a6_i2c_write_reg(state->i2c_dev, reg_desc->id, reg_desc->num_ids, vals);
+		if (ret < 0) {
+			printk(KERN_ERR "%s: error writing reg: %s, id: 0x%x\n",
+			__func__, reg_desc->debug_name, reg_desc->id[0]);
+			goto err0;
+		}
+
+		/* clear int_status2 */
+		reg_desc = &a6_register_desc_arr[6];
+		ret = a6_i2c_write_reg(state->i2c_dev, reg_desc->id, reg_desc->num_ids, vals);
+		if (ret < 0) {
+			printk(KERN_ERR "%s: error writing reg: %s, id: 0x%x\n",
+			__func__, reg_desc->debug_name, reg_desc->id[0]);
+			goto err0;
+		}
+
+		/* clear int_status1 */
+		reg_desc = &a6_register_desc_arr[5];
+		ret = a6_i2c_write_reg(state->i2c_dev, reg_desc->id, reg_desc->num_ids, vals);
+		if (ret < 0) {
+			printk(KERN_ERR "%s: error writing reg: %s, id: 0x%x\n",
+			__func__, reg_desc->debug_name, reg_desc->id[0]);
+			goto err0;
+		}
+	}
+
+	set_bit(IS_INITIALIZED_BIT, state->flags);
+
+err0:
+	return ret;
+}
+
+int32_t format_current(const struct a6_device_state* state, const uint8_t* val,
+		       uint8_t* fmt_buffer, uint32_t size_buffer)
+{
+	int32_t ret;
+	int32_t conv_val =  *(int16_t*)val; // (val[0]<<8) + val[1];
+
+	/* in uA */
+	/* TODO: Bootie source code states:
+	 *  Define R sense as 20 mOhms, as opposed as a variable and reading it
+	 *  from the pack, not all packs have the correct R sense programmed
+	 */
+	conv_val = (conv_val * 3125)/2/(int32_t)state->cached_rsense_val;
+
+	ret = scnprintf(fmt_buffer, size_buffer, "%d\n", conv_val);
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR,
+		   "Current value (uA): %s\n", fmt_buffer);
+	return ret;
+}
+
+int32_t format_voltage(const struct a6_device_state* state, const uint8_t* val,
+		       uint8_t* fmt_buffer, uint32_t size_buffer)
+{
+	int32_t ret;
+	int32_t conv_val =  *(int16_t*)val;  // (val[0]<<8) + val[1];
+
+	/* in uV -- 11-bit signed value, unit = 4880uV */
+	conv_val = (conv_val>>5) * 4880;
+	ret = scnprintf(fmt_buffer, size_buffer, "%d\n", conv_val);
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_INFO, "Voltage value (uV): %s\n",
+		fmt_buffer);
+	return ret;
+}
+
+int32_t r_format_voltage(const struct a6_device_state* state, const uint8_t* fmt_buffer,
+			 uint8_t* val, uint32_t size_buffer)
+{
+	int32_t ret;
+	uint32_t conv_val;
+	const char *bufp;
+	char *endp = NULL;
+
+	bufp = fmt_buffer;
+	// let strtoul perform base determination (handles '0x' and '0' prefixes) ...
+	conv_val = simple_strtoul(bufp, &endp, 0);
+	/* in uV -- 11-bit signed value, unit = 4880uV */
+	conv_val = (conv_val / 4880) << 5;
+	// capped at 16 bits...
+	if (conv_val >> 0x10) {
+		ret = 0;
+		goto err0;
+	}
+	ret = (uint32_t)endp - (uint32_t)bufp;
+
+	*(uint16_t*)val = conv_val;
+
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: ip: %s op: %d\n",
+		   __func__, fmt_buffer, conv_val);
+
+err0:
+	return ret;
+}
+/**
+ * Take the Accumulated Current Register (ACR), which is expressed in units of 6.25uVh
+ * and convert to uAh.
+ */
+int32_t format_rawcoulomb(const struct a6_device_state* state, const uint8_t* val,
+			  uint8_t* fmt_buffer, uint32_t size_buffer)
+{
+	int32_t ret;
+	int32_t conv_val =  *(int16_t*)val; // (val[0]<<8) + val[1];
+
+	// in uAh
+	conv_val = (conv_val * 6250) / (int32_t)state->cached_rsense_val;
+	ret = scnprintf(fmt_buffer, size_buffer, "%d.%03d\n", conv_val/1000,
+		       ((conv_val >= 0)? conv_val : -conv_val) % 1000);
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_INFO, "Raw Coulomb value (uAh): %s\n",
+		fmt_buffer);
+	return ret;
+}
+
+/**
+ * Take the Remaining Active Absolute Capacity (RAAC) register, which is in
+ * units of 1.6 mAh and convert to mAh
+ */
+int32_t format_coulomb(const struct a6_device_state* state, const uint8_t* val,
+			  uint8_t* fmt_buffer, uint32_t size_buffer)
+{
+	int32_t ret;
+	int32_t conv_val =  *(int16_t*)val;
+
+	// in mAh
+	conv_val = (conv_val * 8) / 5;
+	ret = scnprintf(fmt_buffer, size_buffer, "%d\n", conv_val);
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_INFO, "Coulomb value (mAh): %s\n",
+		fmt_buffer);
+	return ret;
+}
+
+int32_t format_age(const struct a6_device_state* state, const uint8_t* val,
+		   uint8_t* fmt_buffer, uint32_t size_buffer)
+{
+	int32_t ret, conv_val;
+
+	// Age conversion factor, in .01 percent. Raw value 0x80 = 100%
+	conv_val = (10000*val[0]) >> 7;
+	ret = scnprintf(fmt_buffer, size_buffer, "%d.%02d\n",
+		conv_val/100, conv_val%100);
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_INFO, "Battery life left (%%): %s\n",
+		fmt_buffer);
+	return ret;
+}
+
+int32_t format_fullx(const struct a6_device_state* state, const uint8_t* val,
+		     uint8_t* fmt_buffer, uint32_t size_buffer)
+{
+	int32_t ret;
+	int32_t conv_val =  *(int16_t*)val; // (val[0]<<8) + val[1];
+
+	// Convert 6.25uVh to uAh
+	conv_val = (conv_val * 6250) / state->cached_rsense_val;
+	ret = scnprintf(fmt_buffer, size_buffer,
+		"%d.%03d\n", conv_val/1000, conv_val%1000);
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_INFO, "Battery life left (uAh): %s\n",
+		fmt_buffer);
+	return ret;
+}
+
+int32_t format_temp(const struct a6_device_state* state, const uint8_t* val,
+		    uint8_t* fmt_buffer, uint32_t size_buffer)
+{
+	int32_t ret;
+	int conv_val = val[1];	/* Ignoring the fraction part */
+
+	// in C
+	ret = snprintf(fmt_buffer, size_buffer, "%d\n", (int8_t)conv_val);
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_INFO, "Temperature (C): %s\n",
+		fmt_buffer);
+	return ret;
+}
+
+int32_t format_command(const struct a6_device_state* state, const uint8_t* val,
+		    uint8_t* fmt_buffer, uint32_t size_buffer)
+{
+	int32_t ret;
+	int conv_command_debug_code = val[0];
+
+	ret = snprintf(fmt_buffer, size_buffer, "%d\n", (int8_t)conv_command_debug_code);
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_INFO, "debug code(C): %s\n",
+		fmt_buffer);
+	return ret;
+}
+
+int32_t format_accessory(const struct a6_device_state* state, const uint8_t* val,
+		    uint8_t* fmt_buffer, uint32_t size_buffer)
+{
+	int32_t ret;
+
+    ret = scnprintf(fmt_buffer, size_buffer, "%02X\n", val[0]);
+ 	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_INFO, "%s", fmt_buffer);
+	return ret;
+}
+
+int32_t format_comm_status(const struct a6_device_state* state, const uint8_t* val,
+		    uint8_t* fmt_buffer, uint32_t size_buffer)
+{
+	int32_t ret;
+
+    ret = scnprintf(fmt_buffer, size_buffer, "%02X\n", val[0]);
+ 	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_INFO, "%s", fmt_buffer);
+	return ret;
+}
+
+int32_t r_format_temp(const struct a6_device_state* state, const uint8_t* fmt_buffer,
+		      uint8_t* val, uint32_t size_buffer)
+{
+	int32_t ret;
+	int32_t conv_val;
+	const char *bufp;
+	char *endp = NULL;
+
+	bufp = fmt_buffer;
+	// let strtoul perform base determination (handles '0x' and '0' prefixes) ...
+	conv_val = simple_strtol(bufp, &endp, 0);
+	printk(KERN_ERR "conv_val : %d, hex: %x\n", conv_val, conv_val);
+	// capped at 8 bits...
+	if (conv_val < -128) {
+		ret = 0;
+		goto err0;
+	}
+	ret = (uint32_t)endp - (uint32_t)bufp;
+
+	/* Ignoring the fraction part */
+	*(uint16_t*)val = (uint8_t)conv_val << 8;
+
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: ip: %s op: %d\n",
+		   __func__, fmt_buffer, conv_val<<8);
+
+err0:
+	return ret;
+}
+
+int32_t format_status(const struct a6_device_state* state, const uint8_t* val,
+		      uint8_t* fmt_buffer, uint32_t size_buffer)
+{
+	uint32_t conv_val = *val;
+	int32_t count, i;
+
+	const char* status_bits[] = {
+		NULL,
+		"power-on-reset",
+		"undervoltage",
+		NULL,
+		"learn",
+		"standby-empty",
+		"active-empty",
+		"charge-termination"};
+
+	for (i = count = 0; i < sizeof(status_bits)/sizeof(status_bits[0]); i++) {
+		if ((conv_val & (1 << i)) && status_bits[i] != NULL) {
+			count += scnprintf(fmt_buffer + count, size_buffer-count, "%s\n", status_bits[i]);
+		}
+	}
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR,
+		   "Battery status: %s\n", fmt_buffer);
+
+	return count;
+}
+
+int32_t format_rsense(const struct a6_device_state* state, const uint8_t* val,
+		      uint8_t* fmt_buffer, uint32_t size_buffer)
+{
+	int32_t ret;
+	uint32_t conv_val = *val;
+
+	ret = scnprintf(fmt_buffer, size_buffer, "%u\n", (!conv_val) ? conv_val : 1000/conv_val);
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_INFO, "rsense (ohm): %s\n",
+		fmt_buffer);
+	return ret;
+}
+
+/**
+ * Determine what, if any chargers are attached to the system.  Only one
+ * result will be returned, even if multiple chargers are detected.
+ *
+ * Output is a combination of:
+ *   0x01 - Puck Detected
+ *   0x02 - Puck Connected
+ *   0x04 - Wired Power Detected
+ *   0x08 - Puck Power Connected
+ *   0x10 - Wired Power Connected
+ *   0x20 - Battery Present
+ *
+ * Not all values are currently supported.
+ */
+int32_t format_charge_source_status(const struct a6_device_state* state, const uint8_t* val,
+				    uint8_t* fmt_buffer, uint32_t size_buffer)
+{
+	struct charge_source_status_map {
+		uint32_t cs_mask;
+		const char* cs_name;
+	} cs_map[] = {
+		[0] = {0x01, "Puck Detected"},
+		[1] = {0x02, "Puck Connected"},
+		[2] = {0x04, "Wired Power Detected"},
+		[3] = {0x08, "Puck Power Connected"},
+		[4] = {0x10, "Wired Power Connected"},
+		[5] = {0x20, "Battery Present"},
+	};
+	uint32_t conv_val = *val, count = 0;
+	int32_t i;
+
+	for(i = 0; i < sizeof(cs_map)/sizeof(cs_map[0]); i++) {
+		if (conv_val & cs_map[i].cs_mask) {
+			count += scnprintf(fmt_buffer + count, (size_buffer - 1) - count, "%s\n", cs_map[i].cs_name);
+		}
+	}
+
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR,
+		   "charge source status (formatted): %s\n", fmt_buffer);
+
+#ifdef A6_REPORT_CONNECTED_ONLY
+	conv_val = 0;
+	/* map charge source status to user-space values */
+	/* TODO: differentiate wall charger from USB */
+	if (val[0] & TS2_I2C_FLAGS_2_WIRED_CHARGE) conv_val |= 1;
+	if (val[0] & TS2_I2C_FLAGS_2_PUCK_CHARGE) conv_val |= 4;
+#endif
+
+	/* output register contents */
+	count = scnprintf(fmt_buffer, size_buffer, "%u\n", conv_val);
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR,
+		   "charge source status (literal): %s\n", fmt_buffer);
+	return count;
+}
+
+int32_t format_version(const struct a6_device_state* state, const uint8_t* val,
+		       uint8_t* fmt_buffer, uint32_t size_buffer)
+{
+	int32_t ret;
+
+	ret = scnprintf(fmt_buffer, size_buffer,
+			"A6 Version: HW: %d, FW (M.m.B): %d.%d.%d, ManID: %d, ProdTyp: %d\n",
+			val[12], val[15], val[14], val[13],
+			*(int16_t*)&(val[0]), *(int16_t*)&(val[2]));
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_INFO, "%s", fmt_buffer);
+	return ret;
+}
+
+int32_t format_v_offset(const struct a6_device_state* state, const uint8_t* val,
+			uint8_t* fmt_buffer, uint32_t size_buffer)
+{
+	int32_t ret;
+	uint32_t conv_val = *val;
+
+	ret = scnprintf(fmt_buffer, size_buffer, "%u\n", conv_val);
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_INFO, "v_offset: %s\n", fmt_buffer);
+	return ret;
+}
+
+int32_t format_raw_unsigned(const struct a6_device_state* state, const uint8_t* val,
+			    uint8_t* fmt_buffer, uint32_t size_buffer)
+{
+	int32_t ret;
+	uint32_t conv_val = *val;
+
+	ret = scnprintf(fmt_buffer, size_buffer, "%u\n", conv_val);
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_INFO, "raw_unsigned: %s\n", fmt_buffer);
+	return ret;
+}
+
+int32_t format_u16_hex(const struct a6_device_state* state, const uint8_t* val,
+		       uint8_t* fmt_buffer, uint32_t size_buffer)
+{
+	int32_t ret;
+	uint16_t conv_val = *(uint16_t*)val;
+
+	ret = scnprintf(fmt_buffer, size_buffer, "0x%04hx\n", conv_val);
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_INFO, "u16_hex: %s\n", fmt_buffer);
+	return ret;
+}
+
+int32_t format_max_power_available(const struct a6_device_state* state, const uint8_t* val,
+				   uint8_t* fmt_buffer, uint32_t size_buffer)
+{
+	int32_t ret;
+	uint32_t vnode_max_val = *(uint16_t*)val;
+	uint32_t inode_max_val = *(uint16_t*)(val+2);
+	uint32_t power_val     = *(val+4);
+	uint32_t conv_val;
+
+	conv_val = (vnode_max_val * inode_max_val * power_val)/2560;
+	ret = scnprintf(fmt_buffer, size_buffer, "%u\n", conv_val);
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_INFO, "%s", fmt_buffer);
+	return ret;
+}
+
+int32_t format_accessory_combo(const struct a6_device_state* state, const uint8_t* val,
+			       uint8_t* fmt_buffer, uint32_t size_buffer)
+{
+	int32_t ret;
+
+	ret = scnprintf(fmt_buffer, size_buffer,
+			"0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x "
+			"0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x \n",
+			val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7],
+			val[8], val[9], val[10], val[11], val[12], val[13], val[14], val[15]);
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_INFO, "%s", fmt_buffer);
+	return ret;
+}
+
+int32_t format_serno_v1(const struct a6_device_state* state, const uint8_t* val,
+			       uint8_t* fmt_buffer, uint32_t size_buffer)
+{
+	int32_t ret;
+
+	ret = scnprintf(fmt_buffer, size_buffer, "%02x%02x%02x%02x%02x%02x\n",
+			val[0], val[1], val[2], val[3], val[4], val[5]);
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_INFO, "%s", fmt_buffer);
+	return ret;
+}
+
+int32_t format_serno_v2(const struct a6_device_state* state, const uint8_t* val,
+			uint8_t* fmt_buffer, uint32_t size_buffer)
+{
+	int32_t ret;
+
+	ret = scnprintf(fmt_buffer, size_buffer, "%02x%02x%02x%02x%02x%02x%02x%02x\n",
+			val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]);
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_INFO, "%s", fmt_buffer);
+	return ret;
+}
+
+static ssize_t a6_get_reg_vals(struct device *dev, struct device_attribute *attr, uint8_t *vals, unsigned num_vals)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	int32_t ret = 0;
+	struct a6_register_desc* reg_desc;
+	struct a6_device_state* state = i2c_get_clientdata(client);
+
+	// critsec for manipulating flags
+	ret = mutex_lock_interruptible(&state->dev_mutex);
+	if (ret) {
+		printk(KERN_ERR "%s: mutex_lock_interruptible interrupted(1)\n", __func__);
+		return -ERESTARTSYS;
+	}
+
+	// are we busy?
+	while (test_and_set_bit(DEVICE_BUSY_BIT, state->flags)) {
+		// yes: are we in bootload phase?
+		if (!test_bit(BOOTLOAD_ACTIVE_BIT, state->flags)) {
+			// no: so go ahead and allow concurrent i2c ops (these are
+			// synchronized separately to allow priority based execution).
+			break;
+		}
+
+		// in bootload phase: get on a waitq
+		mutex_unlock(&state->dev_mutex);
+		A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: about to wait for device non-busy...\n", __func__);
+
+		// bootload bit set? wait to be cleared (at least 5 mins: in jiffies)
+		ret = wait_event_interruptible_timeout(state->dev_busyq,
+				!test_bit(BOOTLOAD_ACTIVE_BIT, state->flags), 300*HZ);
+		if (!ret) {
+			printk(KERN_ERR "%s: wait on device busy timed-out/interrupted\n", __func__);
+			// reset busy state
+			clear_bit(DEVICE_BUSY_BIT, state->flags);
+			// and continue...
+		}
+
+		// we're about to manipulate flags again: acquire critsec
+		ret = mutex_lock_interruptible(&state->dev_mutex);
+		if (ret) {
+			printk(KERN_ERR "%s: mutex_lock_interruptible interrupted(2)\n", __func__);
+			return -ERESTARTSYS;
+		}
+	}
+
+	// increment busy refcount
+	state->busy_count++;
+	// done with flags: exit critsec
+	mutex_unlock(&state->dev_mutex);
+
+	reg_desc = container_of(attr, struct a6_register_desc, dev_attr);
+
+	memset(vals, 0, sizeof(vals[0])*num_vals);
+	ret = a6_i2c_read_reg(client, reg_desc->id, reg_desc->num_ids, vals);
+
+	// reset busy state
+	mutex_lock(&state->dev_mutex);
+	// decrement busy refcount
+	if (state->busy_count) {
+		state->busy_count--;
+	}
+	if (!state->busy_count) {
+		clear_bit(DEVICE_BUSY_BIT, state->flags);
+	}
+	mutex_unlock(&state->dev_mutex);
+	wake_up_interruptible(&state->dev_busyq);
+
+	return ret;
+}
+
+static ssize_t a6_generic_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int32_t ret = 0, i = 0;
+	uint8_t vals[id_size];
+	struct a6_register_desc* reg_desc;
+	struct i2c_client *client = to_i2c_client(dev);
+	struct a6_device_state* state = i2c_get_clientdata(client);
+
+	ret = a6_get_reg_vals(dev, attr, vals, id_size);
+
+	if (ret < 0) return ret;
+
+	reg_desc = container_of(attr, struct a6_register_desc, dev_attr);
+
+	if (reg_desc->format) {
+		ret = reg_desc->format(state, vals, buf, PAGE_SIZE);
+	}
+	else {
+		/* if output restricted to 2 8-bit values, show as int16_t
+		   (common case of msb+lsb retrieval) */
+		if (2 == reg_desc->num_ids) {
+			ret = scnprintf(buf, PAGE_SIZE, "%d\n", *(int16_t*)vals);
+		}
+		/* if output > or < 2 8-bit values, show as
+		 * space-delimited list of int8_t values
+		 */
+		else {
+			i = ret = 0;
+			while(i < reg_desc->num_ids) {
+				ret += scnprintf(buf+ret, PAGE_SIZE-ret, "%d ", (int8_t)vals[i]);
+				i++;
+			}
+			ret += scnprintf(buf+ret, PAGE_SIZE-ret, "\n");
+		}
+
+		A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "Buffer: %s\n", buf);
+	}
+
+#ifdef A6_DEBUG
+	{
+		uint8_t d_ids[reg_desc->num_ids * (4+2+1) + 1];
+		uint8_t d_vals[reg_desc->num_ids * (2+2+1) + 1];
+		int32_t i = 0, ret_ids = 0, ret_vals = 0;
+
+		while (i < reg_desc->num_ids) {
+			ret_ids += sprintf(d_ids+ret_ids, "0x%04x ", reg_desc->id[i]);
+			ret_vals += sprintf(d_vals+ret_vals, "0x%02x ", vals[i]);
+			i++;
+		}
+
+		A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_INFO,
+			"showing reg name: %s, num_ids: %d, ids: %s, vals: %s\n",
+			reg_desc->debug_name, reg_desc->num_ids, d_ids, d_vals);
+	}
+#endif
+
+	return ret;
+}
+
+static ssize_t a6_reg_get(struct device *dev, unsigned regnum, void *buf)
+{
+	int32_t ret = 0;
+	uint8_t vals[id_size];
+	struct i2c_client *client = to_i2c_client(dev);
+	struct a6_device_state* state = i2c_get_clientdata(client);
+	struct device_attribute *attr = &a6_register_desc_arr[regnum].dev_attr;
+	int32_t rsense = (int32_t)state->cached_rsense_val;
+
+	ret = a6_get_reg_vals(dev, attr, vals, id_size);
+
+	if (ret < 0) {
+		printk(KERN_ERR "%s: a6_get_reg_vals failed for regnum %u\n",
+				__func__, regnum);
+		return ret;
+	}
+
+	ret = 0;
+
+	switch (regnum) {
+		case A6_REG_TS2_I2C_FLAGS_2:
+			{
+				uint32_t *out_val = (uint32_t*)buf;
+				*out_val = *vals;
+			}
+			break;
+		case A6_REG_TS2_I2C_BAT_RARC:
+			{
+				int32_t *out_val = (int32_t*)buf;
+				*out_val = *vals;
+			}
+			break;
+		case A6_REG_TS2_I2C_BAT_AVG_CUR_LSB_MSB:
+		case A6_REG_TS2_I2C_BAT_CUR_LSB_MSB:
+			{
+				int32_t *out_val = (int32_t*)buf;
+				*out_val = *(int16_t*)vals;
+				*out_val = (*out_val * 3125) / 2 / rsense;
+			}
+			break;
+		case A6_REG_TS2_I2C_BAT_VOLT_LSB_MSB:
+			{
+				int32_t *out_val = (int32_t*)buf;
+				*out_val = *(int16_t*)vals;
+				*out_val = (*out_val >> 5) * 4800;
+			}
+			break;
+		case A6_REG_TS2_I2C_BAT_FULL40_LSB_MSB:
+		case A6_REG_TS2_I2C_BAT_COULOMB_LSB_MSB:
+			{
+				int32_t *out_val = (int32_t*)buf;
+				*out_val = *(int16_t*)vals;
+				*out_val = (*out_val * 6250) / rsense;
+			}
+			break;
+		case A6_REG_TS2_I2C_BAT_TEMP_LSB_MSB:
+			{
+				int32_t *out_val = (int32_t*)buf;
+				int8_t temp_val = ((int8_t*)vals)[1];
+				*out_val = temp_val * 10;
+			}
+			break;
+		default:
+			{
+				printk(KERN_ERR "%s: Invalid register %u\n", __func__, regnum);
+				ret = -EINVAL;
+			}
+	}
+
+	return ret;
+}
+
+// constraints:
+// * multi-valued stores must have individual values delimited by whitespace
+// * multi-valued stores have individual values constrained to reg size (8-bit)
+// * single-valued stores can specify 16-bit values to update two regs (msb+lsb)
+// * value count must exactly match for multi-values stores
+// * value-count for single value stores may be one less than the reg count specified.
+static ssize_t a6_generic_store(struct device *dev, struct device_attribute *attr, const char *buf,
+				size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	int32_t ret = 0, num_ids, val_cnt, i;
+	uint32_t val;
+	char *endp = NULL, *bufp;
+	uint16_t parsed_vals[id_size];
+	uint8_t in_vals[id_size];
+	struct a6_register_desc* reg_desc;
+	struct a6_device_state* state = i2c_get_clientdata(client);
+
+	// critsec for manipulating flags
+	ret = mutex_lock_interruptible(&state->dev_mutex);
+	if (ret) {
+		printk(KERN_ERR "%s: mutex_lock_interruptible interrupted(1)\n", __func__);
+		return -ERESTARTSYS;
+	}
+
+	// are we busy?
+	while (test_and_set_bit(DEVICE_BUSY_BIT, state->flags)) {
+		// yes: are we in bootload phase?
+		if (!test_bit(BOOTLOAD_ACTIVE_BIT, state->flags)) {
+			// no: so go ahead and allow concurrent i2c ops (these are
+			// synchronized separately to allow priority based execution).
+			break;
+		}
+
+		// in bootload phase: get on a waitq
+		mutex_unlock(&state->dev_mutex);
+		A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: about to wait for device non-busy...\n", __func__);
+
+		// bootload bit set? wait to be cleared (at least 5 mins: in jiffies)
+		ret = wait_event_interruptible_timeout(state->dev_busyq,
+				!test_bit(BOOTLOAD_ACTIVE_BIT, state->flags), 300*HZ);
+		if (!ret) {
+			printk(KERN_ERR "%s: wait on device busy timed-out/interrupted\n", __func__);
+			// reset busy state
+			clear_bit(DEVICE_BUSY_BIT, state->flags);
+			// and continue...
+		}
+
+		// we're about to manipulate flags again: acquire critsec
+		ret = mutex_lock_interruptible(&state->dev_mutex);
+		if (ret) {
+			printk(KERN_ERR "%s: mutex_lock_interruptible interrupted(2)\n", __func__);
+			return -ERESTARTSYS;
+		}
+	}
+
+	// increment busy refcount
+	state->busy_count++;
+	// done with flags: exit critsec
+	mutex_unlock(&state->dev_mutex);
+
+	reg_desc = container_of(attr, struct a6_register_desc, dev_attr);
+	num_ids = reg_desc->num_ids;
+
+	if (reg_desc->r_format)
+	{
+		ret = reg_desc->r_format(state, buf, in_vals, count);
+	}
+	else
+	{
+		bufp = (char*)buf;
+		val_cnt = 0;
+		do {
+			// let strtoul perform base determination (handles '0x' and '0' prefixes) ...
+			val = (uint16_t)simple_strtoul(bufp, &endp, 0);
+			// each space-delimited write unit is capped at 16 bits...
+			if (val > 0xffff) {
+				ret = -EINVAL;
+				goto err0;
+			}
+
+			parsed_vals[val_cnt] = val;
+
+			// skip whitespace
+			while (*endp && isspace(*endp)) {
+				endp++;
+			}
+			// reset for next iteration
+			bufp = endp;
+		} while ((++val_cnt < num_ids) && ((endp - buf) < count));
+
+		// three levels of value count calidation:
+		// * if extra values: fail
+		// * if multiple values and does not match reg count specified: fail
+		// * if single value and reg count > 2: fail
+		if (*endp) {
+			ret = -EINVAL;
+			goto err0;
+		}
+		else if (val_cnt > 1) {
+			if (val_cnt != num_ids) {
+				ret = -EINVAL;
+				goto err0;
+			}
+		}
+		else {
+			if (num_ids > 2) {
+				ret = -EINVAL;
+				goto err0;
+			}
+		}
+
+		// if multi-valued store or single-valued store to one reg: we constrain each
+		// value to reg size (8 bits)
+		if ((val_cnt > 1) || (1 == num_ids)) {
+			for (i = 0; i < val_cnt; i++) {
+				if (parsed_vals[i] > 0xff) {
+					ret = -EINVAL;
+					goto err0;
+				}
+
+				in_vals[i] = parsed_vals[i];
+			}
+		}
+		// single-valued store can be 8-bit or 16-bit (for msb+lsb)
+		else {
+			((uint16_t*)in_vals)[0] = ((uint16_t*)parsed_vals)[0];
+		}
+	}
+
+#ifdef A6_DEBUG
+	{
+		uint8_t d_ids[reg_desc->num_ids * (4+2+1)];
+		uint8_t d_vals[reg_desc->num_ids * (2+2+1)];
+		int32_t i = 0, ret_ids = 0, ret_vals = 0;
+
+		while (i < reg_desc->num_ids) {
+			ret_ids = sprintf(d_ids+ret_ids, "0x%04x ", reg_desc->id[i]);
+			ret_vals = sprintf(d_vals+ret_vals, "0x%02x ", in_vals[i]);
+			i++;
+		}
+
+		A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR,
+			"storing reg name: %s, num_ids: %d, ids: %s, vals: %s\n",
+			reg_desc->debug_name, reg_desc->num_ids, d_ids, d_vals);
+	}
+#endif
+
+	ret = a6_i2c_write_reg(client, reg_desc->id, num_ids, in_vals);
+	if (ret < 0) {
+		goto err0;
+	}
+
+	ret = count;
+
+err0:
+	// reset busy state
+	mutex_lock(&state->dev_mutex);
+	// decrement busy refcount
+	if (state->busy_count) {
+		state->busy_count--;
+	}
+	if (!state->busy_count) {
+		clear_bit(DEVICE_BUSY_BIT, state->flags);
+	}
+	mutex_unlock(&state->dev_mutex);
+	wake_up_interruptible(&state->dev_busyq);
+
+	return ret;
+}
+
+enum {
+	ACTIVATE_EXTRACT,
+	NONE
+};
+
+static ssize_t a6_val_cksum_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	int32_t rc = 0, reloop = 0, failed = 0;
+	struct a6_device_state* state = i2c_get_clientdata(client);
+	uint16_t cksum1, cksum2, cksum_cycles, cksum_errors;
+	struct a6_register_desc *reg_desc;
+	uint8_t vals[id_size];
+
+
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: enter\n", __func__);
+
+	// critsec for manipulating flags
+	rc = mutex_lock_interruptible(&state->dev_mutex);
+	if (rc) {
+		printk(KERN_ERR "%s: mutex_lock interrupted\n", __func__);
+		return -EIO;
+	}
+
+	// are we busy?
+	while (test_and_set_bit(DEVICE_BUSY_BIT, state->flags)) {
+		// yes: get on a waitq
+		mutex_unlock(&state->dev_mutex);
+
+		A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: about to wait for device non-busy...\n", __func__);
+
+		// busy bit set? wait to be cleared (at least 5 minutes: in jiffies)
+		rc = wait_event_interruptible_timeout(state->dev_busyq,
+				!test_bit(DEVICE_BUSY_BIT, state->flags), 300*HZ);
+		if (!rc) {
+			printk(KERN_ERR "%s: wait on device busy timed-out/interrupted\n", __func__);
+			// reset busy state
+			clear_bit(DEVICE_BUSY_BIT, state->flags);
+			// and continue...
+		}
+
+		// we're about to manipulate flags again: acquire critsec
+		rc = mutex_lock_interruptible(&state->dev_mutex);
+		if (rc) {
+			printk(KERN_ERR "%s: mutex_lock interrupted(2)\n", __func__);
+			goto err0;
+		}
+	}
+
+	rc = test_and_set_bit(BOOTLOAD_ACTIVE_BIT, state->flags);
+	ASSERT(!rc);
+
+	// we're done with flags: exit critsec
+	mutex_unlock(&state->dev_mutex);
+
+	do {
+		/* write command (cksum1) */
+		reg_desc = &a6_register_desc_arr[35];
+		vals[0] = TS2_I2C_COMMAND_FRAM_CHECKSUM_READ_1;
+		rc = a6_i2c_write_reg(state->i2c_dev, reg_desc->id, reg_desc->num_ids, vals);
+		if (rc < 0) {
+			printk(KERN_ERR "%s: error writing reg: %s, id: 0x%x\n",
+			__func__, reg_desc->debug_name, reg_desc->id[0]);
+			break;
+		}
+
+		/* read cksum1 */
+		reg_desc = &a6_register_desc_arr[80];
+		memset(vals, 0, sizeof(vals));
+		rc = a6_i2c_read_reg(state->i2c_dev, reg_desc->id, reg_desc->num_ids, vals);
+		if (rc < 0) {
+			printk(KERN_ERR "%s: error reading reg: %s, ids: 0x%x 0x%x\n",
+			__func__, reg_desc->debug_name, reg_desc->id[0], reg_desc->id[1]);
+			break;
+		}
+		cksum1 = vals[0] | vals[1] << 8;
+
+		/* read cksum cycle counter */
+		reg_desc = &a6_register_desc_arr[81];
+		memset(vals, 0, sizeof(vals));
+		rc = a6_i2c_read_reg(state->i2c_dev, reg_desc->id, reg_desc->num_ids, vals);
+		if (rc < 0) {
+			printk(KERN_ERR "%s: error reading reg: %s, ids: 0x%x 0x%x\n",
+			__func__, reg_desc->debug_name, reg_desc->id[0], reg_desc->id[1]);
+			break;
+		}
+		cksum_cycles = vals[0] | vals[1] << 8;
+
+		/* write command (cksum2) */
+		reg_desc = &a6_register_desc_arr[35];
+		vals[0] = TS2_I2C_COMMAND_FRAM_CHECKSUM_READ_2;
+		rc = a6_i2c_write_reg(state->i2c_dev, reg_desc->id, reg_desc->num_ids, vals);
+		if (rc < 0) {
+			printk(KERN_ERR "%s: error writing reg: %s, id: 0x%x\n",
+			__func__, reg_desc->debug_name, reg_desc->id[0]);
+			break;
+		}
+
+		/* read cksum2 */
+		reg_desc = &a6_register_desc_arr[80];
+		memset(vals, 0, sizeof(vals));
+		rc = a6_i2c_read_reg(state->i2c_dev, reg_desc->id, reg_desc->num_ids, vals);
+		if (rc < 0) {
+			printk(KERN_ERR "%s: error reading reg: %s, ids: 0x%x 0x%x\n",
+			__func__, reg_desc->debug_name, reg_desc->id[0], reg_desc->id[1]);
+			break;
+		}
+		cksum2 = vals[0] | vals[1] << 8;
+
+		/* read cksum error counter */
+		reg_desc = &a6_register_desc_arr[81];
+		memset(vals, 0, sizeof(vals));
+		rc = a6_i2c_read_reg(state->i2c_dev, reg_desc->id, reg_desc->num_ids, vals);
+		if (rc < 0) {
+			printk(KERN_ERR "%s: error reading reg: %s, ids: 0x%x 0x%x\n",
+			       __func__, reg_desc->debug_name, reg_desc->id[0], reg_desc->id[1]);
+			break;
+		}
+		cksum_errors = vals[0] | vals[1] << 8;
+
+		/* validate cksum */
+		if (reloop || !cksum1 || !cksum2 || (cksum1 != cksum2)) {
+			printk(KERN_ERR "A6 checksum (%s) validation failure:\n"
+					"cksum1: 0x%02hx; cksum2: 0x%02hx; cksum_cycles: 0x%02hx;"
+					" cksum_errors: 0x%02hx\n",
+					(!reloop) ? "first-stage" : "second-stage",
+					cksum1, cksum2, cksum_cycles, cksum_errors);
+			if (reloop) {
+				reloop--;
+				failed = 1;
+			}
+			else {
+				reloop++;
+			}
+		}
+	} while (reloop);
+
+	if (rc < 0) {
+		printk(KERN_ERR "%s: checksum retrieval enountered i2c errors: "
+				"fallback to sbw(jtag) access.\n", __func__);
+		get_checksum_data_sbw((struct a6_sbw_interface*)state->plat_data->sbw_ops, &cksum1,
+				       &cksum2, &cksum_cycles, &cksum_errors);
+	}
+
+	/* validate cksum */
+	if (failed || !cksum1 || !cksum2 || (cksum1 != cksum2)) {
+		printk(KERN_ERR "A6 checksum validation failed:\n"
+				"cksum1: 0x%02hx; cksum2: 0x%02hx; cksum_cycles: 0x%02hx;"
+				" cksum_errors: 0x%02hx\n",
+				cksum1, cksum2, cksum_cycles, cksum_errors);
+		rc = snprintf(buf, PAGE_SIZE, "%d\n", 0);
+	}
+	else {
+		rc = snprintf(buf, PAGE_SIZE, "%d\n", 1);
+	}
+
+err0:
+	mutex_lock(&state->dev_mutex);
+	clear_bit(BOOTLOAD_ACTIVE_BIT, state->flags);
+	clear_bit(DEVICE_BUSY_BIT, state->flags);
+	mutex_unlock(&state->dev_mutex);
+	wake_up_interruptible(&state->dev_busyq);
+
+	return rc;
+}
+
+
+static char* activation_strlist[] = {
+	[ACTIVATE_EXTRACT] = "extract",
+	[NONE] = "none"
+};
+
+static ssize_t a6_diag_store(struct device *dev, struct device_attribute *attr, const char *buf,
+				size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	int32_t rc = 0, ret_val, action_i = 0;
+	struct a6_device_state* state = i2c_get_clientdata(client);
+	bool skip = false;
+
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: enter\n", __func__);
+
+	action_i = (sizeof(activation_strlist)/sizeof(char*));
+	do {
+		if (0 == strncmp(buf, activation_strlist[action_i-1],
+		    strlen(activation_strlist[action_i-1]))) {
+			break;
+		}
+	} while(action_i--);
+
+	if (!action_i) {
+		return -EINVAL;
+	}
+
+	// store the action index
+	action_i--;
+
+	// critsec for manipulating flags
+	rc = mutex_lock_interruptible(&state->dev_mutex);
+	if (rc) {
+		printk(KERN_ERR "%s: mutex_lock interrupted\n", __func__);
+		return -EIO;
+	}
+
+	// are we busy?
+	while (test_and_set_bit(DEVICE_BUSY_BIT, state->flags)) {
+		// yes: get on a waitq
+		mutex_unlock(&state->dev_mutex);
+
+		A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: about to wait for device non-busy...\n", __func__);
+
+		// busy bit set? wait to be cleared (at least 5 minutes: in jiffies)
+		rc = wait_event_interruptible_timeout(state->dev_busyq,
+				!test_bit(DEVICE_BUSY_BIT, state->flags), 300*HZ);
+		if (!rc) {
+			printk(KERN_ERR "%s: wait on device busy timed-out/interrupted\n", __func__);
+			// reset busy state
+			clear_bit(DEVICE_BUSY_BIT, state->flags);
+			// and continue...
+		}
+
+		// we're about to manipulate flags again: acquire critsec
+		rc = mutex_lock_interruptible(&state->dev_mutex);
+		if (rc) {
+			printk(KERN_ERR "%s: mutex_lock interrupted(2)\n", __func__);
+			goto err0;
+		}
+	}
+
+	if (ACTIVATE_EXTRACT == action_i) {
+		if (test_bit(EXTRACT_INITIATED, state->flags)) {
+			skip = true;
+		}
+		else {
+			ret_val = test_and_set_bit(BOOTLOAD_ACTIVE_BIT, state->flags);
+			ASSERT(!ret_val);
+			ret_val = test_and_set_bit(EXTRACT_INITIATED, state->flags);
+			ASSERT(!ret_val);
+		}
+	}
+	else if (NONE == action_i) {
+		if (!(test_bit(EXTRACT_INITIATED, state->flags))) {
+			skip = true;
+		}
+	}
+	else {
+		// invalid
+	}
+
+	// we're done with flags: exit critsec
+	mutex_unlock(&state->dev_mutex);
+
+	if (true == skip) goto err0;
+	
+	if (ACTIVATE_EXTRACT == action_i) {
+		// reset force-wake state to always force wake on first i2c txn
+		// after pmem extraction
+		del_timer(&state->a6_force_wake_timer);
+		mutex_lock(&state->a6_force_wake_mutex);
+		if (test_bit(CAP_PERIODIC_WAKE, state->flags)) {
+			clear_bit(FORCE_WAKE_ACTIVE_BIT, state->flags);
+		}
+		mutex_unlock(&state->a6_force_wake_mutex);
+
+		// start pmem extract
+		printk(KERN_ERR "%s: starting ttf_extract.\n", __func__);
+		rc = ttf_extract_fw_sbw((struct a6_sbw_interface*)state->plat_data->sbw_ops);
+		if (rc) {
+			printk(KERN_ERR "Failed to ttf_extract a6 pmem.\n");
+			goto err0;
+		}
+		else {	
+			printk(KERN_ERR "A6: Completed ttf_extract a6 pmem.\n");
+			// wait for the A6 to boot...
+			msleep(3000);
+			// - re-init state
+			// - if init fails: ignore
+			a6_init_state(state->i2c_dev);
+		}
+	}
+	else if (NONE == action_i) {
+		printk(KERN_ERR "%s: clearing ttf_extract cache.\n", __func__);
+		rc = ttf_extract_cache_clear();
+	}
+	else {
+		// invalid
+	}
+
+err0:
+	mutex_lock(&state->dev_mutex);
+	if (false == skip) {
+		if (rc || NONE == action_i) {
+			if (test_bit(EXTRACT_INITIATED, state->flags)) {
+				clear_bit(EXTRACT_INITIATED, state->flags);
+			}
+		}
+		if (test_bit(BOOTLOAD_ACTIVE_BIT, state->flags)) {
+			clear_bit(BOOTLOAD_ACTIVE_BIT, state->flags);
+		}
+	}
+	clear_bit(DEVICE_BUSY_BIT, state->flags);
+	mutex_unlock(&state->dev_mutex);
+	wake_up_interruptible(&state->dev_busyq);
+
+	return (rc ? rc : count);
+}
+
+static ssize_t a6_diag_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	int32_t rc = 0;
+	struct a6_device_state* state = i2c_get_clientdata(client);
+
+	
+	mutex_lock(&state->dev_mutex);
+	if (test_bit(EXTRACT_INITIATED, state->flags)) {
+		rc = snprintf(buf, PAGE_SIZE, "%s\n", activation_strlist[ACTIVATE_EXTRACT]);
+	}
+	mutex_unlock(&state->dev_mutex);
+
+	return rc;
+}
+
+int32_t a6_create_dev_files(struct a6_device_state* state, struct device* dev)
+{
+	int32_t rc = 0, reg_cnt = sizeof(a6_register_desc_arr)/sizeof(struct a6_register_desc);
+	int32_t idx = 0, cust_idx = 0;
+
+	for (idx = 0; idx < reg_cnt; idx++) {
+		rc = device_create_file(dev, &a6_register_desc_arr[idx].dev_attr);
+		if (rc < 0) {
+			printk(KERN_ERR "%s: failed to create dev_attr file for %s.\n",
+			       __func__, a6_register_desc_arr[idx].dev_attr.attr.name);
+			goto err0;
+		}
+	}
+
+	reg_cnt = sizeof(custom_devattr)/sizeof(struct device_attribute);
+	for (cust_idx = 0; cust_idx < reg_cnt; cust_idx++) {
+		rc = device_create_file(dev, &custom_devattr[cust_idx]);
+		if (rc < 0) {
+			printk(KERN_ERR "%s: failed to create dev_attr file for %s.\n",
+			       __func__, custom_devattr[cust_idx].attr.name);
+			goto err0;
+		}
+	}
+
+	rc = sysfs_create_link(&state->mdev.this_device->kobj, &dev->kobj, "regs");
+	if (rc) {
+		printk(KERN_ERR "%s: error in creating symlink.\n", __func__);
+	}
+
+	return 0;
+
+err0:
+	// error: cleanup files already created.
+	for (idx--; idx >= 0; idx--) {
+		device_remove_file(dev, &a6_register_desc_arr[idx].dev_attr);
+	}
+	for (cust_idx--; cust_idx >= 0; cust_idx--) {
+		device_remove_file(dev, &custom_devattr[cust_idx]);
+	}
+	return rc;
+
+}
+
+void a6_remove_dev_files(struct a6_device_state* state, struct device* dev)
+{
+	int32_t reg_cnt = sizeof(a6_register_desc_arr)/sizeof(struct a6_register_desc);
+	int32_t idx;
+
+	for (idx = 0; idx < reg_cnt; idx++) {
+		device_remove_file(dev, &a6_register_desc_arr[idx].dev_attr);
+	}
+
+	reg_cnt = sizeof(custom_devattr)/sizeof(struct device_attribute);
+	for (idx = 0; idx < reg_cnt; idx++) {
+		device_remove_file(dev, &custom_devattr[idx]);
+	}
+}
+
+typedef enum {
+	A6_PROGAM_AND_VERIFY_FW = 1,
+	A6_VERIFY_FW
+} a6_pgm_thread_op;
+
+struct a6_pgm_thread_params {
+	struct a6_sbw_interface* sbw_ops;
+	uint32_t buffer_p;
+	int32_t ret_code;
+	a6_pgm_thread_op op;
+	struct completion a6_flash_thread_nice;
+	struct completion a6_flash_thread_exit;
+};
+
+int a6_pgm_thread_fn(void* param)
+{
+	int32_t ret_val;
+
+	struct a6_pgm_thread_params* t_p = (struct a6_pgm_thread_params*)param;
+
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: buffer_p val: 0x%x\n", __func__, t_p->buffer_p);
+
+	// wait for parent re-nice completion...
+	ret_val = wait_for_completion_interruptible(&t_p->a6_flash_thread_nice);
+	if (-ERESTARTSYS == ret_val) {
+		printk(KERN_ERR "A6: waiting for parent re-nice completion interrupted.\n");
+		t_p->ret_code = -EINTR;
+		goto err0;
+	}
+
+	if (A6_PROGAM_AND_VERIFY_FW == t_p->op) {
+		ret_val = program_device_sbw(t_p->sbw_ops, t_p->buffer_p);
+	}
+	else {
+		ret_val = verify_device_sbw(t_p->sbw_ops, t_p->buffer_p);
+		// ignore error returns for the moment as the verification fn
+		// does not differentiate between code and r/w data sections.
+		// also, the fw section allocation changes fairly dynamically
+		// between fw drops and we don't yet support parameterizing the
+		// section map for the verification code.
+		ret_val = 0;
+	}
+	if (ret_val) {
+		t_p->ret_code = -EINVAL;
+	}
+
+	// signal thread completion
+	complete(&t_p->a6_flash_thread_exit);
+
+err0:
+	return 0;
+}
+
+static long a6_ioctl(struct file *file,
+                         unsigned int cmd, unsigned long args)
+{
+	int32_t rc = 0;
+	struct a6_device_state* state = file->private_data;
+	void* usr_ptr   = (void*)args;
+	//uint32_t usr_bytes = _IOC_SIZE(cmd);
+	//uint32_t usr_val = 0x0;
+
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: cmd: %d\n", __func__, _IOC_NR(cmd));
+
+	// critsec for manipulating flags
+	rc = mutex_lock_interruptible(&state->dev_mutex);
+	if (rc) {
+		printk(KERN_ERR "%s: mutex_lock interrupted(1)\n", __func__);
+		return -ERESTARTSYS;
+	}
+
+	// are we busy?
+	while (test_and_set_bit(DEVICE_BUSY_BIT, state->flags)) {
+		// yes: get on a waitq
+		mutex_unlock(&state->dev_mutex);
+
+		A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: about to wait for device non-busy...\n", __func__);
+
+		// busy bit set? wait to be cleared (at least 3 seconds: in jiffies)
+		rc = wait_event_interruptible_timeout(state->dev_busyq,
+		                                      !test_bit(DEVICE_BUSY_BIT, state->flags), 3*HZ);
+		if (!rc) {
+			printk(KERN_ERR "%s: wait on device busy timed-out/interrupted\n", __func__);
+			// reset busy state
+			clear_bit(DEVICE_BUSY_BIT, state->flags);
+			// and continue...
+		}
+
+		// we're about to manipulate flags again: acquire critsec
+		rc = mutex_lock_interruptible(&state->dev_mutex);
+		if (rc) {
+			printk(KERN_ERR "%s: mutex_lock interrupted(2)\n", __func__);
+			return -ERESTARTSYS;
+		}
+	}
+
+	// bootload request: set flag in critsec
+	if (A6_IOCTL_SET_FW_DATA == cmd || A6_IOCTL_VERIFY_FW_DATA == cmd) {
+		int32_t ret_val;
+		ret_val = test_and_set_bit(BOOTLOAD_ACTIVE_BIT, state->flags);
+		ASSERT(!ret_val);
+	}
+
+	// we're done with flags: exit critsec
+	mutex_unlock(&state->dev_mutex);
+
+	switch (cmd) {
+		case A6_IOCTL_SET_FW_DATA:
+		case A6_IOCTL_VERIFY_FW_DATA:
+		{
+			uint8_t *buffer_p = NULL;
+			uint32_t payload_size = 0;
+			struct a6_sbw_interface* sbw_ops =
+					(struct a6_sbw_interface*)state->plat_data->sbw_ops;
+			uint8_t* a6_fw_buffer;
+			struct task_struct* pgm_worker_task;
+			pid_t pgm_worker_task_pid;
+			struct a6_pgm_thread_params t_params;
+
+			// reset force-wake state to always force wake on first i2c txn
+			// after flashing/verification
+			del_timer(&state->a6_force_wake_timer);
+			mutex_lock(&state->a6_force_wake_mutex);
+			if (test_bit(CAP_PERIODIC_WAKE, state->flags)) {
+				clear_bit(FORCE_WAKE_ACTIVE_BIT, state->flags);
+			}
+			mutex_unlock(&state->a6_force_wake_mutex);
+
+			// copy payload ptr from ioctl parameter
+			if (copy_from_user(&buffer_p, usr_ptr, sizeof(buffer_p))) {
+				rc = -EFAULT;
+				break;
+			}
+			A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "buffer_p val: 0x%p\n", buffer_p);
+
+			// copy payload size from ioctl parameter
+			if (copy_from_user(&payload_size, (uint8_t*)usr_ptr + sizeof(uint32_t), sizeof(payload_size))) {
+				rc = -EFAULT;
+				break;
+			}
+
+			A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "payload_size: %d\n", payload_size);
+
+			// alloc kernel buffer for payload
+			a6_fw_buffer = (uint8_t*)kmalloc(payload_size, GFP_KERNEL);
+			if (!a6_fw_buffer) {
+				printk(KERN_ERR "A6: failed to allocate fw buffer.\n");
+				rc = -ENOMEM;
+				break;
+			}
+			A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "fw buffer ptr_val: 0x%p\n", a6_fw_buffer);
+
+			// copy-in payload data
+			if (copy_from_user(a6_fw_buffer, buffer_p, payload_size)) {
+				rc = -EFAULT;
+				break;
+			}
+
+			printk(KERN_ERR "A6: Starting flashing sequence.\n");
+			A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "A6: parent task nice value: %d; pri: %d\n",
+				   task_nice(current), task_prio(current));
+
+			// initialize worker task params
+			init_completion(&t_params.a6_flash_thread_nice);
+			init_completion(&t_params.a6_flash_thread_exit);
+			t_params.sbw_ops = sbw_ops;
+			t_params.buffer_p = (uint32_t)a6_fw_buffer;
+			t_params.ret_code = 0;
+			t_params.op = (A6_IOCTL_SET_FW_DATA == cmd) ?
+					A6_PROGAM_AND_VERIFY_FW : A6_VERIFY_FW;
+
+			// create worker task...
+			pgm_worker_task_pid = kernel_thread(a6_pgm_thread_fn, &t_params,
+					CLONE_KERNEL);
+			if (pgm_worker_task_pid < 0) {
+				printk(KERN_ERR "A6: failed to create pgm worker task.\n");
+				rc = -EIO;
+				break;
+			}
+
+			// retrieve worker task struct
+			pgm_worker_task = get_pid_task(find_get_pid(pgm_worker_task_pid), PIDTYPE_PID);
+			// re-nice worker task
+			//set_user_nice(pgm_worker_task, 10);
+			A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "A6: pgm worker task nice value: %d; pri: %d\n",
+				   task_nice(pgm_worker_task), task_prio(pgm_worker_task));
+
+			/* hold cpu at max freq before signaling worker thread to commence sbw */
+#ifdef CONFIG_CPU_FREQ_GOV_ONDEMAND_TICKLE
+			CPUFREQ_HOLD_CHECK(&state->cpufreq_hold_flag);
+#endif
+			// signal re-nice completion...
+			complete(&t_params.a6_flash_thread_nice);
+
+			// wait for worker task-end completion...
+			rc = wait_for_completion_interruptible(&t_params.a6_flash_thread_exit);
+			/* unhold cpu */
+#ifdef CONFIG_CPU_FREQ_GOV_ONDEMAND_TICKLE
+			CPUFREQ_UNHOLD_CHECK(&state->cpufreq_hold_flag);
+#endif
+			kfree(a6_fw_buffer);
+
+			if (-ERESTARTSYS == rc) {
+				printk(KERN_ERR "A6: waiting for pgm worker start interrupted.\n");
+				break;
+			}
+
+			A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "A6: pgm worker task exit code: %d\n",
+				   t_params.ret_code);
+			if (t_params.ret_code) {
+				printk(KERN_ERR "A6: Failed to completed flashing sequence. ret: %d\n",
+				       t_params.ret_code);
+				// propagate error to caller...
+				rc = t_params.ret_code;
+			}
+			else {
+				printk(KERN_ERR "A6: Completed flashing sequence.\n");
+				// wait for the A6 to boot...
+				msleep(3000);
+				// - flashing fw forces a device power-up sequence: re-init state
+				// - if init fails: treat as fw flash failure by returning rc
+				rc = a6_init_state(state->i2c_dev);
+				if (rc < 0) {
+					printk(KERN_ERR "%s: failed to initialize, err: %d\n", A6_DRIVER, rc);
+				}
+			}
+
+			mutex_lock(&state->dev_mutex);
+			clear_bit(BOOTLOAD_ACTIVE_BIT, state->flags);
+			mutex_unlock(&state->dev_mutex);
+		}
+		break;
+
+		default:
+		{
+			rc = -EINVAL;
+		}
+		break;
+	}
+
+
+//Done:
+	mutex_lock(&state->dev_mutex);
+	if (rc) {
+		if (test_bit(BOOTLOAD_ACTIVE_BIT, state->flags)) {
+			clear_bit(BOOTLOAD_ACTIVE_BIT, state->flags);
+		}
+		clear_bit(DEVICE_BUSY_BIT, state->flags);
+	}
+	else {
+		if (!test_bit(BOOTLOAD_ACTIVE_BIT, state->flags)) {
+			clear_bit(DEVICE_BUSY_BIT, state->flags);
+		}
+	}
+	mutex_unlock(&state->dev_mutex);
+
+	wake_up_interruptible(&state->dev_busyq);
+
+	return rc;
+}
+
+static int a6_open(struct inode *inode, struct file *file)
+{
+	struct a6_device_state* state;
+
+	/* get device */
+	state = container_of(file->f_op, struct a6_device_state, fops);
+
+	/* Allow only read. */
+	//if ((file->f_mode & (FMODE_READ|FMODE_WRITE)) != FMODE_READ) {
+	//	    return -EINVAL;
+	//}
+
+	///* check if it is in use */
+	//if (test_and_set_bit(IS_OPENED, state->flags)) {
+	//	return -EBUSY;
+	//}
+
+	/* attach private data */
+	file->private_data = state;
+
+	return 0;
+}
+
+static int a6_close(struct inode *inode, struct file *file)
+{
+	struct a6_device_state* state = (struct  a6_device_state*) file->private_data;
+
+	state = state;
+	///* mark it as unused */
+	//clear_bit(IS_OPENED, state->flags);
+
+	return 0;
+}
+
+#define A2A_DGRAM_PREAMBLE (0x5AC35AC3)
+struct a2a_dgram_hdr {
+	uint32_t preamble;
+	uint16_t len;
+	uint16_t cksum;
+};
+
+static ssize_t a6_read(struct file *file, char __user *buf, size_t count, loff_t *ppos )
+{
+# define A2A_RX_MISS_THRESHOLD (300)
+
+	ssize_t rc = 0;
+	struct a6_device_state* state;
+	struct a6_register_desc *reg_desc_comm_status, *reg_desc_comm_rxtx;
+	uint8_t vals[id_size];
+	int32_t miss_count, rd_count;
+	//struct a2a_dgram_hdr hdr;
+	uint32_t start_time;
+	uint8_t elt_val = 0, prev_byte;
+
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: enter\n", __func__);
+
+	start_time = jiffies;
+	/* input validations */
+	if (!count) {
+		return -EINVAL;
+	}
+	else if (count > A2A_RD_BUFF_SIZE) {
+		return -EFBIG;
+	}
+
+	/* get state */
+	state = container_of(file->f_op, struct a6_device_state, fops);
+
+	// acquire critsec
+	rc = mutex_lock_interruptible(&state->dev_mutex);
+	if (rc) {
+		printk(KERN_ERR "%s: mutex_lock interrupted.\n", __func__);
+		return -ERESTARTSYS;
+	}
+
+	/* not connected? exit */
+	if (!test_bit(A2A_CONNECTED, state->flags)) {
+		mutex_unlock(&state->dev_mutex);
+		printk(KERN_ERR "%s: no a2a connection detected.\n", __func__);
+		return -EINVAL;
+	}
+
+	// busy?
+	if (test_and_set_bit(READ_ACTIVE_BIT, state->flags)) {
+		// yes: get on a waitq
+		mutex_unlock(&state->dev_mutex);
+		printk(KERN_ERR "%s: re-entrant call disallowed.\n", __func__);
+		return -EINVAL;
+	}
+
+	mutex_unlock(&state->dev_mutex);
+
+	/* init a2a read ptr */
+	state->a2a_rp = state->a2a_rd_buf;
+
+	rd_count = 0;
+	miss_count = 0;
+	prev_byte = 0;
+	do {
+		/* read comm status */
+		reg_desc_comm_status = &a6_register_desc_arr[63];
+		memset(vals, 0, sizeof(vals));
+		rc = a6_i2c_read_reg(state->i2c_dev, reg_desc_comm_status->id,
+				     reg_desc_comm_status->num_ids, vals);
+		if (rc < 0) {
+			printk(KERN_ERR "%s: error reading reg: %s, id: 0x%x\n",
+			       __func__, reg_desc_comm_status->debug_name, reg_desc_comm_status->id[0]);
+			goto err0;
+		}
+
+		if (!(vals[0] & TS2_I2C_COMM_STATUS_RX_FULL)) {
+			if (++miss_count > A2A_RX_MISS_THRESHOLD) {
+				if (test_bit(A2A_CONNECTED, state->flags)) {
+					continue;
+				}
+				else {
+					break;
+				}
+			}
+			continue;
+		}
+
+		/* read rx data */
+		memset(vals, 0, sizeof(vals));
+		reg_desc_comm_rxtx = &a6_register_desc_arr[64];
+		rc = a6_i2c_read_reg(state->i2c_dev, reg_desc_comm_rxtx->id,
+				      reg_desc_comm_rxtx->num_ids, vals);
+		if (rc < 0) {
+			printk(KERN_ERR "%s: error writing reg: %s, id: 0x%x\n",
+			       __func__, reg_desc_comm_rxtx->debug_name, reg_desc_comm_rxtx->id[0]);
+			rc = -EIO;
+			goto err0;
+		}
+
+		A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: read byte: %d successfully.\n",
+			   __func__, rd_count);
+
+		if (a6_t2s_dup_correct) {
+			if ((prev_byte & 0x80) ^ (vals[0] & 0x80)) {
+				prev_byte = vals[0];
+				if (vals[0] & 0x80) {
+					elt_val = _convert_hex_char_to_decimal(vals[0] & 0x7f) << 4;
+					continue;
+				}
+				else {
+					elt_val |= (_convert_hex_char_to_decimal(vals[0]) & 0x0f);
+				}
+			}
+			else {
+				printk(KERN_ERR "%s: t2s duplicate detected; char: 0x%02x.\n",
+				       __func__, vals[0]);
+				continue;
+			}
+		}
+		else {
+			elt_val = vals[0];
+		}
+
+		*state->a2a_rp = elt_val;
+		state->a2a_rp++;
+		rd_count++;
+	} while (rd_count < count);
+
+	if (!rd_count) {
+		printk(KERN_ERR "%s: rx failed; A2A connection terminated.\n", __func__);
+		rc = -EINVAL;
+	}
+	else {
+		long diff_time;
+
+		if (copy_to_user(buf, state->a2a_rd_buf, rd_count)) {
+			rc = -EFAULT;
+			goto err0;
+		}
+		rc = rd_count;
+		diff_time = (long)jiffies - (long)start_time;
+		printk(KERN_ERR "%s: elapsed time: %ld ms; count: %u\n",
+		       __func__, diff_time * 1000/HZ, rd_count);
+	}
+
+
+err0:
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: exit\n", __func__);
+	clear_bit(READ_ACTIVE_BIT, state->flags);
+	return rc;
+}
+
+static ssize_t a6_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos )
+{
+# define A2A_TX_MISS_THRESHOLD (300)
+
+	ssize_t rc = 0;
+	struct a6_device_state* state;
+	struct a6_register_desc *reg_desc_comm_status, *reg_desc_comm_rxtx;
+	uint8_t vals[id_size];
+	int32_t miss_count, wr_count;
+	//struct a2a_dgram_hdr hdr;
+	uint32_t start_time;
+	uint8_t elt_buf[2], *elt_bufp = NULL;
+	int elt_bufsize;
+
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: enter\n", __func__);
+
+	start_time = jiffies;
+	/* input validations */
+	if (!count) {
+		return -EINVAL;
+	}
+	else if (count > A2A_WR_BUFF_SIZE) {
+		return -EFBIG;
+	}
+
+	/* get state */
+	state = container_of(file->f_op, struct a6_device_state, fops);
+
+	// acquire critsec
+	rc = mutex_lock_interruptible(&state->dev_mutex);
+	if (rc) {
+		printk(KERN_ERR "%s: mutex_lock interrupted.\n", __func__);
+		return -ERESTARTSYS;
+	}
+
+	/* not connected? exit */
+	if (!test_bit(A2A_CONNECTED, state->flags)) {
+		mutex_unlock(&state->dev_mutex);
+		printk(KERN_ERR "%s: no a2a connection detected.\n", __func__);
+		return -EINVAL;
+	}
+
+	// busy?
+	if (test_and_set_bit(WRITE_ACTIVE_BIT, state->flags)) {
+		mutex_unlock(&state->dev_mutex);
+		printk(KERN_ERR "%s: re-entrant call disallowed.\n", __func__);
+		return -EINVAL;
+	}
+
+	mutex_unlock(&state->dev_mutex);
+
+	/* copy to kernel buffer */
+	//hdr.preamble = A2A_DGRAM_PREAMBLE;
+	//hdr.len = count;
+	//hdr.cksum = 0;
+	///* header */
+	//if (copy_from_user(state->a2a_wr_buf, &hdr, sizeof(hdr))) {
+	//	rc = -EFAULT;
+	//	mutex_unlock(&state->dev_mutex);
+	//	goto err0;
+	//}
+	/* data */
+	if (copy_from_user(state->a2a_wr_buf/*+sizeof(hdr)*/, buf, count)) {
+		rc = -EFAULT;
+		goto err0;
+	}
+	/* init a2a write ptr */
+	state->a2a_wp = state->a2a_wr_buf;
+
+	wr_count = 0;
+	miss_count = 0;
+	elt_bufsize = 0;
+	do {
+		/* read comm status */
+		reg_desc_comm_status = &a6_register_desc_arr[63];
+		memset(vals, 0, sizeof(vals));
+		rc = a6_i2c_read_reg(state->i2c_dev, reg_desc_comm_status->id,
+				      reg_desc_comm_status->num_ids, vals);
+		if (rc < 0) {
+			printk(KERN_ERR "%s: error reading reg: %s, id: 0x%x\n",
+			__func__, reg_desc_comm_status->debug_name, reg_desc_comm_status->id[0]);
+			goto err0;
+		}
+
+		if (!(vals[0] & TS2_I2C_COMM_STATUS_TX_EMPTY)) {
+			if (++miss_count > A2A_TX_MISS_THRESHOLD) {
+				if (test_bit(A2A_CONNECTED, state->flags)) {
+					continue;
+				}
+				else {
+					break;
+				}
+			}
+			continue;
+		}
+
+		/* write tx data */
+		reg_desc_comm_rxtx = &a6_register_desc_arr[64];
+		if (!elt_bufsize) {
+			if (a6_t2s_dup_correct) {
+				elt_bufsize = 2;
+				sprintf(elt_buf, "%02x", *state->a2a_wp);
+				elt_buf[0] |= 0x80;
+			}
+			else {
+				elt_bufsize = 1;
+				elt_buf[0] = *state->a2a_wp;
+			}
+			elt_bufp = elt_buf;
+		}
+		rc = a6_i2c_write_reg(state->i2c_dev, reg_desc_comm_rxtx->id,
+				       reg_desc_comm_rxtx->num_ids, elt_bufp);
+		if (rc < 0) {
+			printk(KERN_ERR "%s: error writing reg: %s, id: 0x%x\n",
+			__func__, reg_desc_comm_rxtx->debug_name, reg_desc_comm_rxtx->id[0]);
+			rc = -EIO;
+			goto err0;
+		}
+		elt_bufp++;
+		elt_bufsize--;
+
+		if (!elt_bufsize) {
+			A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: written byte: %d successfully.\n",
+				   __func__, wr_count);
+			state->a2a_wp++;
+			wr_count++;
+		}
+	} while (wr_count < count);
+
+	if (!wr_count) {
+		printk(KERN_ERR "%s: tx failed; A2A connection terminated.\n",
+		       __func__);
+		rc = -EIO;
+		goto err0;
+	}
+	else {
+		long diff_time;
+		rc = wr_count;
+		diff_time = (long)jiffies - (long)start_time;
+		printk(KERN_ERR "%s: elapsed time: %ld ms; count: %u\n", __func__,
+		       diff_time * 1000/HZ, wr_count);
+	}
+
+
+err0:
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: exit\n", __func__);
+	clear_bit(WRITE_ACTIVE_BIT, state->flags);
+	return rc;
+}
+
+static unsigned int a6_poll(struct file* file, struct poll_table_struct* wait)
+{
+	unsigned int  mask = 0;
+
+	return mask;
+}
+
+struct file_operations a6_fops = {
+	.owner   = THIS_MODULE,
+	.read    = a6_read,
+	.write    = a6_write,
+	.poll    = a6_poll,
+	.unlocked_ioctl   = a6_ioctl,
+	.open    = a6_open,
+	.release = a6_close,
+};
+
+/*
+ *  a6 interrupt handler
+ */
+static irqreturn_t a6_irq(int irq, void *dev_id)
+{
+	struct a6_device_state* state = (struct a6_device_state *)dev_id;
+
+	a6_tp_irq_count++;
+#if defined PROFILE_USAGE
+	/*
+	if (true == reset_active) {
+		diff_time = (long)jiffies - (long)start_time;
+		reset_active = false;
+		A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "A6_IRQ toggle post power-cycle after: %d ms\n",
+				 diff_time*1000/HZ);
+	}
+	*/
+#endif
+
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: entry.\n", __func__);
+
+	if (test_bit(IS_SUSPENDED, state->flags)) {
+		set_bit(INT_PENDING, state->flags);
+	} else {
+		queue_work(state->ka6d_workqueue, &state->a6_irq_work);
+	}
+	
+	return IRQ_HANDLED;
+}
+
+void a6_irq_work_handler(struct work_struct *work)
+{
+	struct a6_device_state* state =
+			container_of(work, struct a6_device_state, a6_irq_work);
+	struct a6_register_desc *reg_desc_status3, *reg_desc_status2;
+	uint8_t vals[id_size], reg_val_status3 = 0, reg_val_status2 = 0;
+	bool charge_source_changed = false;
+	bool battery_changed = false;
+	int32_t ret = 0;
+	char *envp[] = {
+		[0] = "A6_ACTION=EMERGENCY_RESET_NOTIFY",
+		[1] = NULL,
+		[2] = "A6_ACTION=LOG_THRESHOLD_NOTIFY",
+		[3] = NULL,
+		[4] = "A6_ACTION=CHARGE_SOURCE_NOTIFY",
+		[5] = NULL,
+		[6] = "A6_ACTION=PERCENT_LOW_WARN1_NOTIFY",
+		[7] = NULL,
+		[8] = "A6_ACTION=PERCENT_LOW_WARN2_NOTIFY",
+		[9] = NULL,
+		[10] = "A6_ACTION=PECENT_LOW_CRIT_NOTIFY",
+		[11] = NULL,
+		[12] = "A6_ACTION=VOLTAGE_LOW_CRIT_NOTIFY",
+		[13] = NULL,
+		[14] = "A6_ACTION=TEMP_LOW_CRIT_NOTIFY",
+		[15] = NULL,
+		[16] = "A6_ACTION=TEMP_HIGH_CRIT_NOTIFY",
+		[17] = NULL,
+		[18] = "A6_ACTION=A2A_CONNECT_NOTIFY",
+		[19] = NULL
+	};
+
+	// block irq processing while a6 fw flashing is in progress because i2c requests
+	// will fail anyway and we dont' want to fiddle with SBW_WKUP while flashing is
+	// in progress...
+
+	// critsec for manipulating flags
+	mutex_lock(&state->dev_mutex);
+
+	// busy?
+	while (test_and_set_bit(DEVICE_BUSY_BIT, state->flags)) {
+		// yes: are we in bootload phase?
+		if (!test_bit(BOOTLOAD_ACTIVE_BIT, state->flags)) {
+			// no: so go ahead and allow concurrent i2c ops (these are
+			// synchronized separately to allow priority based execution).
+			break;
+		}
+
+		// in bootload phase: get on a waitq
+		mutex_unlock(&state->dev_mutex);
+		printk(KERN_ERR "%s: about to wait for device non-busy...\n", __func__);
+
+		// bootload bit set? wait to be cleared (at least 5 mins: in jiffies)
+		ret = wait_event_interruptible_timeout(state->dev_busyq,
+				!test_bit(BOOTLOAD_ACTIVE_BIT, state->flags), 300*HZ);
+		if (!ret) {
+			printk(KERN_ERR "%s: wait on device busy timed-out/interrupted\n", __func__);
+			// reset busy state
+			clear_bit(BOOTLOAD_ACTIVE_BIT, state->flags);
+			clear_bit(DEVICE_BUSY_BIT, state->flags);
+			// and continue...
+		}
+
+		// we're about to manipulate flags again: acquire critsec
+		mutex_lock(&state->dev_mutex);
+	}
+
+	/* increment busy refcount */
+	state->busy_count++;
+
+	// done with flags: exit critsec
+	mutex_unlock(&state->dev_mutex);
+
+	/* determine irq cause */
+	reg_desc_status3 = &a6_register_desc_arr[7];
+	reg_desc_status2 = &a6_register_desc_arr[6];
+
+	/* (1) read int_status3: emergency reset and charge-source change */
+	memset(vals, 0, sizeof(vals));
+	ret = a6_i2c_read_reg(state->i2c_dev, reg_desc_status3->id, reg_desc_status3->num_ids, vals);
+	if (ret < 0) {
+		printk(KERN_ERR "%s: error reading reg: %s, id: 0x%x\n",
+		       __func__, reg_desc_status3->debug_name, reg_desc_status3->id[0]);
+		goto err0;
+	}
+	reg_val_status3 = vals[0];
+
+	/* (2) read int_status2: other irq causes */
+	memset(vals, 0, sizeof(vals));
+	ret = a6_i2c_read_reg(state->i2c_dev, reg_desc_status2->id, reg_desc_status2->num_ids, vals);
+	if (ret < 0) {
+		printk(KERN_ERR "%s: error reading reg: %s, id: 0x%x\n",
+		       __func__, reg_desc_status2->debug_name, reg_desc_status2->id[0]);
+		goto err0;
+	}
+	reg_val_status2 = vals[0];
+
+	/* (3) now clear all status bits: this "releases" the irq line early */
+	if (reg_val_status3) {
+		vals[0] = reg_val_status3;
+		ret = a6_i2c_write_reg(state->i2c_dev, reg_desc_status3->id, reg_desc_status3->num_ids, vals);
+		if (ret < 0) {
+			printk(KERN_ERR "%s: error writing reg: %s, id: 0x%x\n",
+			       __func__, reg_desc_status3->debug_name, reg_desc_status3->id[0]);
+			goto err0;
+		}
+	}
+	if (reg_val_status2) {
+		vals[0] = reg_val_status2;
+		ret = a6_i2c_write_reg(state->i2c_dev, reg_desc_status2->id, reg_desc_status2->num_ids, vals);
+		if (ret < 0) {
+			printk(KERN_ERR "%s: error writing reg: %s, id: 0x%x\n",
+			       __func__, reg_desc_status3->debug_name, reg_desc_status3->id[0]);
+			goto err0;
+		}
+	}
+
+	/* (4) process emergency reset and charge-source changes...*/
+	if (reg_val_status3) {
+		/* emergency reset? */
+		if (reg_val_status3 & TS2_I2C_INT_3_RESET) {
+			A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: emergency reset detected.\n",
+				   __func__);
+
+			/* Send uevent */
+			kobject_uevent_env(&state->i2c_dev->dev.kobj, KOBJ_CHANGE, &envp[0]);
+			/* this condition overrides all others and we can probably skip
+			 resetting the status bits */
+			goto err0;
+		}
+
+		/* a2a connect change? */
+		if (reg_val_status3 & TS2_I2C_INT_3_A2A_CONNECT_CHANGE) {
+			struct a6_register_desc *reg_desc_charger;
+			uint8_t chg_vals[id_size];
+
+			A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: a2a connect change detected.\n",
+				   __func__);
+			printk(KERN_ERR "%s: a2a connect change detected.\n", __func__);
+			reg_desc_charger = &a6_register_desc_arr[31];
+			memset(chg_vals, 0, sizeof(chg_vals));
+			if (a6_i2c_read_reg(state->i2c_dev, reg_desc_charger->id,
+					     reg_desc_charger->num_ids, chg_vals) < 0) {
+				printk(KERN_ERR "%s: error reading reg: %s, id: 0x%x\n",
+				       __func__, reg_desc_charger->debug_name,
+				       reg_desc_charger->id[0]);
+			}
+			else {
+				if (chg_vals[0] & TS2_I2C_FLAGS_2_A2A_CONNECT) {
+					set_bit(A2A_CONNECTED, state->flags);
+				}
+				else {
+					clear_bit(A2A_CONNECTED, state->flags);
+				}
+				/* Send uevent */
+				kobject_uevent_env(&state->i2c_dev->dev.kobj, KOBJ_CHANGE, &envp[18]);
+			}
+		}
+
+		/* charger-source change? */
+		if (reg_val_status3 & TS2_I2C_INT_3_FLAGS_CHANGE) {
+			A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: charger-source change detected.\n",
+				   __func__);
+
+			/* next, unblock task on charger_source_notify node */
+			//sysfs_notify_dirent(state->notify_nodes[DIRENT_CHG_SRC_NOTIFY]);
+			/* Send uevent */
+			kobject_uevent_env(&state->i2c_dev->dev.kobj, KOBJ_CHANGE, &envp[4]);
+			charge_source_changed = true;
+		}
+
+		/* log threshold change? */
+		if (reg_val_status3 & TS2_I2C_INT_3_LOG) {
+			A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: log threshold detected.\n",
+				   __func__);
+
+			/* Send uevent */
+			kobject_uevent_env(&state->i2c_dev->dev.kobj, KOBJ_CHANGE, &envp[2]);
+		}
+	}
+
+	/* (5) process other irq causes... */
+	if (reg_val_status2) {
+		/* battery low critical? */
+		if (reg_val_status2 & TS2_I2C_INT_2_BAT_RARC_CRIT) {
+			A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: battery low critical detected.\n",
+				   __func__);
+
+			/* Send uevent */
+			kobject_uevent_env(&state->i2c_dev->dev.kobj, KOBJ_CHANGE, &envp[10]);
+			battery_changed = true;
+		}
+
+		/* battery voltage low critical? */
+		if (reg_val_status2 & TS2_I2C_INT_2_BAT_VOLT_LOW) {
+			A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: battery voltage low critical detected.\n",
+				   __func__);
+
+			/* Send uevent */
+			kobject_uevent_env(&state->i2c_dev->dev.kobj, KOBJ_CHANGE, &envp[12]);
+			battery_changed = true;
+		}
+
+		/* battery temp high critical? */
+		if (reg_val_status2 & TS2_I2C_INT_2_BAT_TEMP_HIGH) {
+			A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: battery temp high critical detected.\n",
+				   __func__);
+
+			/* Send uevent */
+			kobject_uevent_env(&state->i2c_dev->dev.kobj, KOBJ_CHANGE, &envp[16]);
+		}
+
+		/* battery temp low critical? */
+		if (reg_val_status2 & TS2_I2C_INT_2_BAT_TEMP_LOW) {
+			A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: battery temp low critical detected.\n",
+				   __func__);
+
+			/* Send uevent */
+			kobject_uevent_env(&state->i2c_dev->dev.kobj, KOBJ_CHANGE, &envp[14]);
+		}
+
+		/* battery low percent warn2? */
+		if (reg_val_status2 & TS2_I2C_INT_2_BAT_RARC_LOW2) {
+			A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: battery low percent warn2 detected.\n",
+				   __func__);
+
+			/* Send uevent */
+			kobject_uevent_env(&state->i2c_dev->dev.kobj, KOBJ_CHANGE, &envp[8]);
+			battery_changed = true;
+		}
+
+		/* battery low percent warn1? */
+		if (reg_val_status2 & TS2_I2C_INT_2_BAT_RARC_LOW1) {
+			A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: battery low percent warn1 detected.\n",
+				   __func__);
+
+			/* Send uevent */
+			kobject_uevent_env(&state->i2c_dev->dev.kobj, KOBJ_CHANGE, &envp[6]);
+			battery_changed = true;
+		}
+	}
+
+err0:
+	/* decrement busy refcount */
+	if (state->busy_count)
+		state->busy_count--;
+	if (!state->busy_count)
+		clear_bit(DEVICE_BUSY_BIT, state->flags);
+
+	if (charge_source_changed) {
+		a6_dock_update_state(state);
+	}
+	if (state->plat_data->power_supply_connected == 1 && batt_state != NULL) {
+		if (battery_changed) {
+			power_supply_changed(&a6_fish_power_supplies[0]);
+		}
+		if (charge_source_changed) {
+			power_supply_changed(&a6_fish_power_supplies[1]);
+			power_supply_changed(&a6_fish_power_supplies[2]);
+#ifdef CONFIG_A6_ENABLE_DOCK_PS
+			power_supply_changed(&a6_fish_power_supplies[3]);
+#endif
+			a6_update_connected_ps();
+		}
+	}
+
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: Visited\n", __func__);
+}
+
+
+#ifdef A6_PQ
+int32_t a6_stop_ai_dispatch_task(struct a6_device_state* state)
+{
+	int32_t rc = 0;
+
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: entered.\n", __func__);
+	// critsec for manipulating flags
+	rc = mutex_lock_interruptible(&state->dev_mutex);
+	if (rc) {
+		printk(KERN_ERR "%s: mutex_lock interrupted\n", __func__);
+		return -ERESTARTSYS;
+	}
+	// stopping during a start? fail
+	if (test_bit(STARTING_AID_TASK, state->flags)) {
+		printk(KERN_ERR "%s: aid task not fully started. failing op.\n", __func__);
+		rc = -EBUSY;
+		mutex_unlock(&state->dev_mutex);
+		goto err0;
+	}
+
+	// stopping during a stop? fail
+	if (test_bit(KILLING_AID_TASK, state->flags)) {
+		printk(KERN_ERR "%s: aid task being stopped. failing op.\n", __func__);
+		rc = -EBUSY;
+		mutex_unlock(&state->dev_mutex);
+		goto err0;
+	}
+
+	// task never started? fail
+	if (!state->ai_dispatch_task) {
+		printk(KERN_ERR "%s: no aid task. failing op.\n", __func__);
+		rc = -EPERM;
+		mutex_unlock(&state->dev_mutex);
+		goto err0;
+	}
+
+	// transition to quiesced state: stops accepting new requests
+	set_bit(IS_QUIESCED, state->flags);
+	// declare intent to kill ai dispatch task
+	set_bit(KILLING_AID_TASK, state->flags);
+	// exit critsec
+	mutex_unlock(&state->dev_mutex);
+
+	// kick task if asleep
+	complete(&state->aq_enq_complete);
+
+	// wait for ai dispatch task exit
+	rc = wait_for_completion_interruptible(&state->aid_exit_complete);
+	if (rc < 0) {
+		printk(KERN_ERR "%s: wait for ai dispatch task exit interrupted.\n",
+		       __func__);
+	}
+
+	// critsec for manipulating flags
+	rc = mutex_lock_interruptible(&state->dev_mutex);
+	if (rc) {
+		printk(KERN_ERR "%s: mutex_lock interrupted(1)\n", __func__);
+		return -ERESTARTSYS;
+	}
+	// ai dispatch task exited (assume it did if we get interrupted): reset state
+	clear_bit(KILLING_AID_TASK, state->flags);
+	state->ai_dispatch_task = NULL;
+	// exit critsec
+	mutex_unlock(&state->dev_mutex);
+
+
+err0:
+	return rc;
+}
+
+int32_t a6_start_ai_dispatch_task(struct a6_device_state* state)
+{
+	int32_t rc = 0;
+	pid_t ai_dispatch_pid;
+
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: entered.\n", __func__);
+
+	// critsec for manipulating flags
+	rc = mutex_lock_interruptible(&state->dev_mutex);
+	if (rc) {
+		printk(KERN_ERR "%s: mutex_lock interrupted\n", __func__);
+		return -ERESTARTSYS;
+	}
+	// starting during a start? fail
+	if (test_bit(STARTING_AID_TASK, state->flags)) {
+		printk(KERN_ERR "%s: aid task not fully started. failing op.\n", __func__);
+		rc = -EBUSY;
+		mutex_unlock(&state->dev_mutex);
+		goto err0;
+	}
+
+	// starting during a stop? fail
+	if (test_bit(KILLING_AID_TASK, state->flags)) {
+		printk(KERN_ERR "%s: aid task being stopped. failing op.\n", __func__);
+		rc = -EBUSY;
+		mutex_unlock(&state->dev_mutex);
+		goto err0;
+	}
+
+	// task never stopped? fail
+	if (state->ai_dispatch_task) {
+		printk(KERN_ERR "%s: aid task exists. failing op.\n", __func__);
+		rc = -EPERM;
+		mutex_unlock(&state->dev_mutex);
+		goto err0;
+	}
+
+	// declare intent to start ai dispatch task
+	set_bit(STARTING_AID_TASK, state->flags);
+	// exit critsec
+	mutex_unlock(&state->dev_mutex);
+
+
+	// create ai dispatcher task...
+	ai_dispatch_pid = kernel_thread(ai_dispatch_thread_fn, state,
+					CLONE_KERNEL);
+	if (ai_dispatch_pid < 0) {
+		printk(KERN_ERR "%s: failed to create ai dispatcher task.\n", __func__);
+		rc = -EIO;
+		goto err0;
+	}
+
+	rc = mutex_lock_interruptible(&state->dev_mutex);
+	if (rc) {
+		printk(KERN_ERR "%s: mutex_lock interrupted(1)\n", __func__);
+		return -ERESTARTSYS;
+	}
+	// retrieve worker task struct
+	state->ai_dispatch_task = get_pid_task(find_get_pid(ai_dispatch_pid), PIDTYPE_PID);
+	ASSERT(state->ai_dispatch_task);
+
+	// transition to active state: start accepting new requests
+	clear_bit(IS_QUIESCED, state->flags);
+	// ai dispatch task started: reset state
+	clear_bit(STARTING_AID_TASK, state->flags);
+	// exit critsec
+	mutex_unlock(&state->dev_mutex);
+
+err0:
+	return rc;
+}
+#endif // A6_PQ
+
+void a6_force_wake_work_handler(struct work_struct *work)
+{
+	struct a6_device_state* state =
+			container_of(work, struct a6_device_state, a6_force_wake_work);
+	struct a6_wake_ops* wake_ops = (struct a6_wake_ops*)state->plat_data->wake_ops;
+	long diff_time;
+
+	diff_time = (long)jiffies - (long)start_last_a6_activity;
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: forcing sleep after: %ld ms\n",
+		   __func__, diff_time * 1000/HZ);
+
+	start_last_a6_activity = 0;
+
+	// force A6 sleep and switch back to periodic wake...
+	// * timer may be scheduled just after we clear FORCE_WAKE_ACTIVE_BIT 
+	//   and hence will result in the callback being invoked with
+	//   FORCE_WAKE_ACTIVE_BIT cleared. We just ignore this case...
+	mutex_lock(&state->a6_force_wake_mutex);
+	if (!test_bit(DEVICE_BUSY_BIT, state->flags)) {
+		if (test_bit(FORCE_WAKE_ACTIVE_BIT, state->flags)) {
+			A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR,
+				"%s: disabling force_wake and enabling periodic_wake\n",
+				__func__);
+			/* force A6 sleep */
+			if (wake_ops->force_sleep) {
+				wake_ops->force_sleep(wake_ops->data);
+			}
+			/* enable periodic a6 wake (if defined) */
+			if (wake_ops->enable_periodic_wake) {
+				wake_ops->enable_periodic_wake(wake_ops->data);
+			}
+			/* now we are ready to clear FORCE_WAKE_ACTIVE_BIT */
+			clear_bit(FORCE_WAKE_ACTIVE_BIT, state->flags);
+		}
+	}
+	mutex_unlock(&state->a6_force_wake_mutex);
+}
+
+
+// timer callback used to force sleep after a force wake
+void a6_force_wake_timer_callback(ulong data)
+{
+	struct a6_device_state* state = (struct a6_device_state*)data;
+	int32_t rc;
+
+	rc = queue_work(state->ka6d_fw_workqueue, &state->a6_force_wake_work);
+	if (!rc) {
+		printk(KERN_ERR "**** %s: failed queueing force_wake work item.\n", __func__);
+	}
+}
+
+static int a6_pmem_open(struct inode *inode, struct file *file)
+{
+	struct a6_device_state* state;
+
+	/* get device */
+	state = container_of(file->f_op, struct a6_device_state, pmem_fops);
+
+	/* Allow only read. */
+	if ((file->f_mode & (FMODE_READ|FMODE_WRITE)) != FMODE_READ) {
+		    return -EINVAL;
+	}
+
+	/* check if it is in use */
+	if (test_and_set_bit(IS_OPENED, state->flags)) {
+		return -EBUSY;
+	}
+
+	/* attach private data */
+	file->private_data = state;
+	return 0;
+}
+
+static int a6_pmem_close(struct inode *inode, struct file *file)
+{
+	struct a6_device_state* state = (struct  a6_device_state*) file->private_data;
+
+	/* mark it as unused */
+	clear_bit(IS_OPENED, state->flags);
+	return 0;
+}
+
+static ssize_t a6_pmem_read(struct file *file, char __user *buf, size_t count, loff_t *ppos )
+{
+
+	ssize_t rc = 0;
+	struct a6_device_state* state;
+
+	A6_DPRINTK(A6_DEBUG_VERBOSE, KERN_ERR, "%s: enter\n", __func__);
+
+	/* input validations */
+	if (!count) {
+		return -EINVAL;
+	}
+
+	/* get state */
+	state = container_of(file->f_op, struct a6_device_state, fops);
+	rc = ttf_image_read(buf, count, ppos);
+	
+	return rc;
+}
+
+struct file_operations a6_pmem_fops = {
+	.owner   = THIS_MODULE,
+	.read    = a6_pmem_read,
+	.open    = a6_pmem_open,
+	.release = a6_pmem_close,
+};
+
+static int a6_fish_battery_get_percent(struct device *dev)
+{
+	int temp_val = 0;
+
+	a6_reg_get (dev, A6_REG_TS2_I2C_BAT_RARC, &temp_val);
+
+#if defined(CONFIG_A6_BATTERY_SCALED_MIN) && CONFIG_A6_BATTERY_SCALED_MIN != 0
+	temp_val = (temp_val - CONFIG_A6_BATTERY_SCALED_MIN) * 100
+				/ (100 - CONFIG_A6_BATTERY_SCALED_MIN);
+	if (temp_val < 0) temp_val = 0;
+#endif
+
+	return temp_val;
+}
+
+static unsigned a6_calc_connected_ps(void)
+{
+	struct power_supply *psy;
+	struct a6_device_state *state;
+	unsigned int temp_val = 0;
+	unsigned connected = 0;
+
+	psy = &a6_fish_power_supplies[0];
+
+	if (!(psy->dev)) {
+		printk(KERN_ERR "%s: psy->dev is NULL\n", __func__);
+		return 0;
+	}
+	if (!(psy->dev->parent)) {
+		printk(KERN_ERR "%s: psy->dev->parent is NULL\n", __func__);
+		return 0;
+	}
+
+	state = (struct a6_device_state*)dev_get_drvdata(psy->dev->parent);
+
+	a6_reg_get (psy->dev->parent, A6_REG_TS2_I2C_FLAGS_2, &temp_val);
+
+	if (state->otg_chg_type == USB_CHG_TYPE__WALLCHARGER) {
+		connected |= MAX8903B_CONNECTED_PS_AC;
+	}
+
+	if (state->otg_chg_type == USB_CHG_TYPE__SDP
+			&& (temp_val & TS2_I2C_FLAGS_2_WIRED_CHARGE)) {
+		/* NOTE: USB will not show as connected if DOCK is connected */
+		connected |= MAX8903B_CONNECTED_PS_USB;
+	}
+
+	if(temp_val & TS2_I2C_FLAGS_2_PUCK_CHARGE) {
+		connected |= MAX8903B_CONNECTED_PS_DOCK;
+	}
+
+	return connected;
+}
+
+static void a6_update_connected_ps()
+{
+	unsigned connected = a6_calc_connected_ps();
+
+	printk(KERN_INFO "%s: ac=%d usb=%d dock=%d\n", __func__,
+			(connected & MAX8903B_CONNECTED_PS_AC) ? 1 : 0,
+			(connected & MAX8903B_CONNECTED_PS_USB) ? 1 : 0,
+			(connected & MAX8903B_CONNECTED_PS_DOCK) ? 1 : 0);
+
+	max8903b_set_connected_ps(connected);
+	a6_last_ps_connect = (long)jiffies;
+}
+
+static int a6_fish_power_get_property(struct power_supply *psy,
+				   enum power_supply_property psp,
+				   union power_supply_propval *val)
+{
+	unsigned connected;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		connected = a6_calc_connected_ps();
+
+		if (
+				(psy->type == POWER_SUPPLY_TYPE_MAINS
+					&& (connected & MAX8903B_CONNECTED_PS_AC)
+#ifdef CONFIG_A6_ENABLE_DOCK_PS
+					&& !(connected & MAX8903B_CONNECTED_PS_DOCK)
+					&& !strcmp(psy->name, "ac")
+#endif
+					)
+						||
+				(psy->type == POWER_SUPPLY_TYPE_MAINS
+				 	&& (connected & MAX8903B_CONNECTED_PS_DOCK)
+#ifdef CONFIG_A6_ENABLE_DOCK_PS
+					&& !strcmp(psy->name, "dock")
+#endif
+					)
+						||
+				(psy->type == POWER_SUPPLY_TYPE_USB 
+				 	&& (connected & MAX8903B_CONNECTED_PS_USB)
+				 	&& !(connected & MAX8903B_CONNECTED_PS_DOCK)
+					)
+				) {
+			val->intval = 1;
+		} else {
+			val->intval = 0;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+#define A6_BATT_STATS_DELAY (msecs_to_jiffies (15*1000))
+static int a6_fish_battery_get_property(struct power_supply *psy,
+				     enum power_supply_property psp,
+				     union power_supply_propval *val)
+{
+	int temp_val = 0;
+	unsigned connected;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		if (a6_fish_battery_get_percent(psy->dev->parent) == 100) {
+			val->intval = POWER_SUPPLY_STATUS_FULL;
+		} else if (a6_last_ps_connect &&
+				jiffies > a6_last_ps_connect + A6_BATT_STATS_DELAY) {
+			a6_reg_get (psy->dev->parent, 
+					A6_REG_TS2_I2C_BAT_AVG_CUR_LSB_MSB, &temp_val);
+			if (temp_val > 0) {
+				val->intval = POWER_SUPPLY_STATUS_CHARGING;
+			} else {
+				val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+			}
+		} else {
+			connected = a6_calc_connected_ps();
+			if (connected && !(connected == MAX8903B_CONNECTED_PS_USB)) {
+				val->intval = POWER_SUPPLY_STATUS_CHARGING;
+			} else {
+				val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+			}
+		}
+		break;
+	case POWER_SUPPLY_PROP_HEALTH:
+#if 0
+		/* NOTE: *_BAT_STATUS not supported by a6_reg_get yet */
+		a6_reg_get(psy->dev->parent, A6_REG_TS2_I2C_BAT_STATUS, &temp_uval);
+#endif
+		// TODO: parse temps and set OVERHEAT, parse "age" and set DEAD */
+
+		val->intval = POWER_SUPPLY_HEALTH_GOOD;
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = 1;
+		break;
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		val->intval = a6_fish_battery_get_percent(psy->dev->parent);
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+		a6_reg_get(psy->dev->parent, 
+				A6_REG_TS2_I2C_BAT_CUR_LSB_MSB, &temp_val);
+		val->intval = temp_val;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		a6_reg_get (psy->dev->parent,
+			A6_REG_TS2_I2C_BAT_VOLT_LSB_MSB, &temp_val);
+		val->intval = temp_val;
+		break;
+	case POWER_SUPPLY_PROP_TEMP:
+		a6_reg_get (psy->dev->parent,
+			A6_REG_TS2_I2C_BAT_TEMP_LSB_MSB, &temp_val);
+		val->intval = temp_val;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_FULL:
+		a6_reg_get (psy->dev->parent,
+			A6_REG_TS2_I2C_BAT_FULL40_LSB_MSB, &temp_val);
+		val->intval = temp_val;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_NOW:
+		a6_reg_get (psy->dev->parent,
+			A6_REG_TS2_I2C_BAT_COULOMB_LSB_MSB, &temp_val);
+		val->intval = temp_val;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+void a6_charger_event (enum chg_type otg_chg_type)
+{
+	if (batt_state) {
+		batt_state->otg_chg_type = otg_chg_type;
+
+		power_supply_changed(&a6_fish_power_supplies[1]);
+		power_supply_changed(&a6_fish_power_supplies[2]);
+#ifdef CONFIG_A6_ENABLE_DOCK_PS
+		power_supply_changed(&a6_fish_power_supplies[3]);
+#endif
+		a6_update_connected_ps();
+	}
+	return;
+}
+EXPORT_SYMBOL (a6_charger_event);
+
+#define A6_BATT_HB_PERIOD (msecs_to_jiffies (5*60*1000))
+#define A6_INIT_CONNECTED_PS_DELAY (msecs_to_jiffies (60*1000))
+static void a6_battery_heartbeat(struct work_struct *a6_battery_work)
+{
+	int percent = 0;
+
+	struct delayed_work *temp_charge_work =
+			container_of (a6_battery_work, struct delayed_work, work);
+	struct a6_device_state* state =
+			container_of(temp_charge_work, struct a6_device_state, charge_work);
+
+	if (state->stop_heartbeat)
+		return;
+
+	if ( (percent = a6_fish_battery_get_percent(&state->i2c_dev->dev))
+			!= state->last_percent){
+
+		state->last_percent = percent;
+		power_supply_changed(&a6_fish_power_supplies[0]);
+	}
+
+	if (!state->stop_heartbeat)
+		schedule_delayed_work(&state->charge_work,
+						A6_BATT_HB_PERIOD);
+}
+
+static void a6_init_connected_ps(struct work_struct *work)
+{
+	a6_update_connected_ps();
+}
+
+static int a6_fish_battery_probe(struct a6_device_state *state)
+{
+	int i;
+	int rc = 0;
+	struct i2c_client *client = state->i2c_dev;
+
+	/* init power supplier framework */
+	for (i = 0; i < ARRAY_SIZE(a6_fish_power_supplies); i++) {
+	
+		rc = power_supply_register(&client->dev, &a6_fish_power_supplies[i]);
+		if (rc)
+			pr_err("%s: Failed to register power supply (%d)\n",
+			       __func__, rc);
+	}
+
+	INIT_DELAYED_WORK(&state->charge_work, a6_battery_heartbeat);
+	schedule_delayed_work(&state->charge_work, A6_BATT_HB_PERIOD);
+
+	INIT_DELAYED_WORK(&state->init_connected_ps_work, a6_init_connected_ps);
+	schedule_delayed_work(&state->init_connected_ps_work, A6_INIT_CONNECTED_PS_DELAY);
+
+	return rc;
+}
+
+
+static int a6_fish_battery_remove(struct a6_device_state *state)
+{
+	int i;
+
+	state->stop_heartbeat = true;
+	cancel_delayed_work_sync(&state->charge_work);
+
+	/* init power supplier framework */
+	for (i = 0; i < ARRAY_SIZE(a6_fish_power_supplies); i++) {
+		power_supply_unregister(&a6_fish_power_supplies[i]);
+	}
+
+	return 0;
+}
+
+static int a6_fish_battery_suspend (struct a6_device_state *state)
+{
+
+	if (state->plat_data->power_supply_connected == 1 &&
+			delayed_work_pending(&state->charge_work)) {
+		state->stop_heartbeat = true;
+		smp_mb();
+		cancel_delayed_work_sync(&state->charge_work);
+	}
+
+	return 0;
+}
+
+static int a6_fish_battery_resume (struct a6_device_state *state)
+{
+	if (state->plat_data->power_supply_connected == 1) {
+		state->stop_heartbeat = false;
+		schedule_delayed_work(&state->charge_work,
+				A6_BATT_HB_PERIOD);
+
+		power_supply_changed(&a6_fish_power_supplies[0]);
+	}
+	return 0;
+}
+
+static ssize_t a6_dock_print_name(struct switch_dev *sdev, char *buf)
+{
+	bool docked = switch_get_state(sdev) != 0;
+	return sprintf(buf, docked ? "DESK\n" : "None\n");
+}
+
+static void a6_dock_update_state(struct a6_device_state *state)
+{
+	unsigned int value, dock;
+
+	if (state->dock_switch == NULL) {
+		return;
+	}
+
+	a6_reg_get (&state->i2c_dev->dev, A6_REG_TS2_I2C_FLAGS_2, &value);
+
+	if (a6_disable_dock_switch) {
+		dock = 0;
+	} else {
+		dock = value & TS2_I2C_FLAGS_2_PUCK ? 1 : 0;
+	}
+	switch_set_state(state->dock_switch, dock);
+}
+
+static int param_set_disable_dock_switch(const char *val,
+		struct kernel_param *kp)
+{
+	struct a6_device_state *state;
+
+	state = batt_state;
+
+	param_set_int(val, kp);
+	a6_dock_update_state(state);
+
+	return 0;
+}
+
+static int a6_dock_probe(struct a6_device_state *state)
+{
+	int ret;
+
+	state->dock_switch = kzalloc(sizeof(struct switch_dev), GFP_KERNEL);
+	if (state->dock_switch == NULL) {
+		return -ENOMEM;
+	}
+
+	state->dock_switch->name = "dock";
+	state->dock_switch->print_name = a6_dock_print_name;
+
+	ret = switch_dev_register(state->dock_switch);
+	if (ret < 0) {
+		kfree(state->dock_switch);
+		state->dock_switch = NULL;
+		return ret;
+	}
+
+	a6_dock_update_state(state);
+
+	return 0;
+}
+
+static void a6_dock_remove(struct a6_device_state *state)
+{
+	switch_dev_unregister(state->dock_switch);
+	kfree(state->dock_switch);
+	state->dock_switch = NULL;
+}
+
+/******************************************************************************
+* a6_i2c_probe()
+******************************************************************************/
+static int a6_i2c_probe(struct i2c_client *client, const struct i2c_device_id *dev_id)
+{
+	int rc = 0;
+	struct a6_device_state* state = NULL;
+	struct a6_platform_data* plat_data = client->dev.platform_data;
+	struct a6_wake_ops *wake_ops = (struct a6_wake_ops *) plat_data->wake_ops;
+
+	if (plat_data == NULL) {
+		/* should this be -ENODEV ? TODO */
+		return ENODEV;
+	}
+
+	state = kzalloc(sizeof(struct a6_device_state), GFP_KERNEL);
+	if(!state) {
+		/* should this be -ENOMEM ? TODO */
+		return ENOMEM;
+	}
+
+	state->a2a_rd_buf = kzalloc(A2A_RD_BUFF_SIZE, GFP_KERNEL);
+	if(!state->a2a_rd_buf) {
+		rc = -ENOMEM;
+		goto err0;
+	}
+	state->a2a_wr_buf = kzalloc(A2A_WR_BUFF_SIZE, GFP_KERNEL);
+	if(!state->a2a_wr_buf) {
+		rc = -ENOMEM;
+		goto err1;
+	}
+
+	// store i2c client device in state
+	state->i2c_dev = client;
+
+	// set platform data in device-specific driver data
+	state->plat_data = plat_data;
+
+	mutex_init(&state->dev_mutex);
+#ifdef A6_PQ
+	init_completion(&state->aq_enq_complete);
+	init_completion(&state->aid_exit_complete);
+	mutex_init(&state->aq_mutex);
+	INIT_LIST_HEAD(&state->aq_head);
+#endif // A6_PQ
+	mutex_init(&state->a6_force_wake_mutex);
+
+	// zero-init wait flags
+	bitmap_zero(state->flags, SIZE_FLAGS);
+
+	// a6 external wake enabled?
+	if (wake_ops) {
+		set_bit(CAP_PERIODIC_WAKE, state->flags);
+	}
+
+	state->ka6d_workqueue = create_workqueue("ka6d");
+	if (!state->ka6d_workqueue) {
+		printk(KERN_ERR "%s: Failed to create ka6d workqueue.\n", A6_DRIVER);
+		goto err3;
+	}
+
+	// separate wq for handling force wake timer expiry...
+	state->ka6d_fw_workqueue  = create_workqueue("ka6d_fwd");
+	if (!state->ka6d_fw_workqueue) {
+		printk(KERN_ERR "%s: Failed to create ka6d_fwd workqueue.\n", A6_DRIVER);
+		goto err4;
+	}
+
+	init_waitqueue_head(&state->dev_busyq);
+
+	INIT_WORK(&state->a6_irq_work, a6_irq_work_handler);
+	if (test_bit(CAP_PERIODIC_WAKE, state->flags)) {
+		INIT_WORK(&state->a6_force_wake_work, a6_force_wake_work_handler);
+	}
+
+	state->cpufreq_hold_flag = 0;
+
+	// set device-specific driver data
+	i2c_set_clientdata(client, state);
+
+	// configure sbw_tck
+	rc = gpio_request(plat_data->sbw_tck_gpio, "a6_sbwtck");
+	if (rc != 0) {
+		printk(KERN_ERR "%s: request failed for sbw_tck gpio, val: %d.\n",
+		       A6_DRIVER, plat_data->sbw_tck_gpio);
+		goto err5;
+	}
+
+	rc = gpio_direction_output(plat_data->sbw_tck_gpio, 0);
+	if (rc != 0) {
+		printk(KERN_ERR "%s: set direction output failed for sbw_tck gpio, val: %d.\n",
+		       A6_DRIVER, plat_data->sbw_tck_gpio);
+		goto err6;
+	}
+
+	// configure sbw_wkup (this is the app -> a6 wakeup used in the JTAG handshaking)
+	rc = gpio_request(plat_data->sbw_wkup_gpio, "a6_sbwwkup");
+	if (rc != 0) {
+		printk(KERN_ERR "%s: request failed for sbw_wkup gpio, val: %d.\n",
+		       A6_DRIVER, plat_data->sbw_wkup_gpio);
+		goto err6;
+	}
+
+	rc = gpio_direction_output(plat_data->sbw_wkup_gpio, 0);
+	if (rc != 0) {
+		printk(KERN_ERR "%s: set direction output failed for sbw_wkup gpio, val: %d.\n",
+		       A6_DRIVER, plat_data->sbw_wkup_gpio);
+		goto err7;
+	}
+
+	// configure sbw_tdio (initially configured as output and the re-configured on use)
+	rc = gpio_request(plat_data->sbw_tdio_gpio, "a6_sbwtdio");
+	if (rc != 0) {
+		printk(KERN_ERR "%s: request failed for sbw_tdio gpio, val: %d.\n",
+		       A6_DRIVER, plat_data->sbw_tdio_gpio);
+		goto err7;
+	}
+
+	rc = gpio_direction_output(plat_data->sbw_tdio_gpio, 1);
+	if (rc != 0) {
+		printk(KERN_ERR "%s: set direction output failed for sbw_tdio gpio, val: %d.\n",
+		       A6_DRIVER, plat_data->sbw_tdio_gpio);
+		goto err8;
+	}
+
+	// configure pwr interrupt (a6 -> app)...
+	rc = gpio_request(plat_data->pwr_gpio, "a6_pwr");
+	if (rc != 0) {
+		printk(KERN_ERR "%s: request failed for pwr gpio, val: %d., err: %d\n",
+		       A6_DRIVER, plat_data->pwr_gpio, rc);
+		goto err8;
+	}
+
+	rc = request_irq(gpio_to_irq(plat_data->pwr_gpio), a6_irq,
+			 IRQF_TRIGGER_FALLING,
+			 "a6", state);
+	if (rc != 0) {
+		printk(KERN_ERR "%s: request irq failed for pwr_gpio: val: %d, err: %d\n", A6_DRIVER,
+		       plat_data->pwr_gpio, rc);
+		goto err9;
+	}
+
+#if 0
+	/* register as misc device */
+	memcpy(&state->fops, &a6_fops, sizeof(struct file_operations));
+	state->mdev.minor = MISC_DYNAMIC_MINOR;
+	state->mdev.name = plat_data->dev_name;
+	state->mdev.fops = &state->fops;
+	rc = misc_register(&state->mdev);
+	if (rc < 0) {
+		printk(KERN_ERR "%s: Failed to register as misc device\n", A6_DRIVER);
+		goto err10;
+	}
+
+	memcpy(&state->pmem_fops, &a6_pmem_fops, sizeof(struct file_operations));
+	state->pmem_mdev.minor = MISC_DYNAMIC_MINOR;
+	snprintf(state->pmem_dev_name, sizeof(state->pmem_dev_name),
+		 "%s_diag", plat_data->dev_name);
+	state->pmem_mdev.name = state->pmem_dev_name;
+	state->pmem_mdev.fops = &state->pmem_fops;
+	rc = misc_register(&state->pmem_mdev);
+	if (rc < 0) {
+		printk(KERN_ERR "%s: Failed to register as a6 pmem misc device\n", A6_DRIVER);
+		goto err11;
+	}
+
+	rc = a6_create_dev_files(state, &client->dev);
+	if (rc < 0) {
+		goto err12;
+	}
+#endif
+
+#ifdef A6_PQ
+	rc = a6_start_ai_dispatch_task(state);
+	if (rc < 0) {
+		goto err13;
+	}
+#endif // A6_PQ
+
+	// ignore errors during initialization: these may be symptomatic of missing/corrupt
+	// a6 fw which will need to be remedied via the A6_IOCTL_SET_FW_DATA ioctl to
+	// re-flash fw. so its important the driver initializes successfully to handle
+	// the request.
+	rc = a6_init_state(client);
+	if (rc < 0) {
+		printk(KERN_ERR "%s: failed to initialize, err: %d\n", A6_DRIVER, rc);
+		rc = 0;
+	}
+
+	// not needed: pending work items will "flow through" if no status changes
+	// are detected...
+	//flush_workqueue(ka6d_workqueue);
+
+#ifdef A6_PQ
+#ifdef A6_DEBUG
+	rc = a6_create_debug_interface(state);
+	if (rc < 0) {
+		printk(KERN_ERR "%s: Failed to create A6 debug interface.\n", A6_DRIVER);
+		rc = 0;
+	}
+#endif
+#endif
+
+	if (plat_data->power_supply_connected == 1){
+		if ( (rc = a6_fish_battery_probe (state)) < 0) {
+			printk(KERN_ERR "%s: Failed to register power supplies, rc: %d.\n",
+					A6_DRIVER, rc);
+			rc = 0;
+		}
+
+		batt_state = state;
+
+		if ((rc = a6_dock_probe(state)) < 0) {
+			printk(KERN_ERR "%s: Failed to register dock device, rc: %d.\n",
+					A6_DRIVER, rc);
+			rc = 0;
+		}
+	}
+
+	printk(KERN_NOTICE "A6 driver initialized successfully!\n");
+	return 0;
+
+err13:
+#if 0
+	a6_remove_dev_files(state, &client->dev);
+err12:
+	 misc_register(&state->pmem_mdev);
+err11:
+	misc_unregister(&state->mdev);
+err10:
+#endif
+	free_irq(gpio_to_irq(plat_data->pwr_gpio), state);
+err9:
+	gpio_free(plat_data->pwr_gpio);
+err8:
+	gpio_free(plat_data->sbw_tdio_gpio);
+err7:
+	gpio_free(plat_data->sbw_wkup_gpio);
+err6:
+	gpio_free(plat_data->sbw_tck_gpio);
+err5:
+	destroy_workqueue(state->ka6d_fw_workqueue);
+err4:
+	destroy_workqueue(state->ka6d_workqueue);
+err3:
+	kfree(state->a2a_wr_buf);
+err1:
+	kfree(state->a2a_rd_buf);
+err0:
+	kfree(state);
+
+	return rc;
+}
+
+/******************************************************************************
+* a6_i2c_remove()
+******************************************************************************/
+static int a6_i2c_remove(struct i2c_client *client)
+{
+	struct a6_device_state* state = (struct a6_device_state*)i2c_get_clientdata(client);
+
+	if (state->plat_data->power_supply_connected == 1){
+		a6_fish_battery_remove (state);
+	}
+	if (state->dock_switch) {
+		a6_dock_remove(state);
+	}
+
+#if 0
+	a6_remove_dev_files(state, &client->dev);
+#endif
+
+	if (state->ka6d_workqueue) {
+		destroy_workqueue(state->ka6d_workqueue);
+	}
+
+	if (state->ka6d_fw_workqueue) {
+		destroy_workqueue(state->ka6d_fw_workqueue);
+	}
+
+	if (state->a2a_rd_buf) {
+		kfree(state->a2a_rd_buf);
+	}
+
+	if (state->a2a_wr_buf) {
+		kfree(state->a2a_wr_buf);
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+/******************************************************************************
+* a6_i2c_suspend
+******************************************************************************/
+static int a6_i2c_suspend(struct i2c_client *dev, pm_message_t event)
+{
+	struct a6_device_state* state = (struct a6_device_state*)i2c_get_clientdata(dev);
+
+	set_bit(IS_SUSPENDED, state->flags);
+	// configure a6 irq as wake-source to handle wake on a6 notifications
+	// (charge source detection, various threshold transgressions and emergency
+	// reset detection)...
+	if (state->plat_data->pwr_gpio_wakeup_cap) {
+		enable_irq_wake(gpio_to_irq(state->plat_data->pwr_gpio));
+	}
+	
+	a6_fish_battery_suspend (state);
+
+	return 0;
+}
+
+/******************************************************************************
+* a6_i2c_resume
+******************************************************************************/
+static int a6_i2c_resume (struct i2c_client *dev)
+{
+	struct a6_device_state* state = (struct a6_device_state*)i2c_get_clientdata(dev);
+
+	// un-configure a6 irq as wake-source...
+	if (state->plat_data->pwr_gpio_wakeup_cap) {
+		disable_irq_wake(gpio_to_irq(state->plat_data->pwr_gpio));
+	}
+	
+	clear_bit(IS_SUSPENDED, state->flags);
+	if (test_and_clear_bit(INT_PENDING, state->flags)) {
+		queue_work(state->ka6d_workqueue, &state->a6_irq_work);
+	}
+
+	a6_fish_battery_resume (state);
+
+	return 0;
+}
+#else
+#define a6_i2c_suspend  NULL
+#define a6_i2c_resume   NULL
+#endif  /* CONFIG_PM */
+
+static const struct i2c_device_id a6_ids[] = {
+	{A6_DEVICE_0, },
+	{A6_DEVICE_1, },
+	{},
+};
+
+MODULE_DEVICE_TABLE(i2c, a6_ids);
+
+
+static struct i2c_driver a6_i2c_driver = {
+	.driver = {
+		.name = A6_DRIVER,
+		.owner = THIS_MODULE,
+	},
+		.id_table	= a6_ids,
+		.probe		= a6_i2c_probe,
+		.remove		= __devexit_p(a6_i2c_remove),
+		.suspend	= a6_i2c_suspend,
+		.resume		= a6_i2c_resume,
+};
+
+/*********************************************************************************
+* a6_module_init(void)
+***********************************************************************************/
+static int __init a6_module_init(void)
+{
+	printk(KERN_INFO "Before a6 call to i2c_add_driver.\n");
+	return i2c_add_driver(&a6_i2c_driver);
+}
+
+/*********************************************************************************
+* cy8c24894_module_exit(void)
+***********************************************************************************/
+static void __exit a6_module_exit(void)
+{
+	i2c_del_driver(&a6_i2c_driver);
+}
+
+module_init(a6_module_init);
+module_exit(a6_module_exit);
+
+MODULE_DESCRIPTION("A6 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/a6/a6_host_adapter.h b/drivers/misc/a6/a6_host_adapter.h
new file mode 100644
index 0000000..c8696d3
--- /dev/null
+++ b/drivers/misc/a6/a6_host_adapter.h
@@ -0,0 +1,59 @@
+#ifndef A6_HOST_ADAPTER_H
+#define A6_HOST_ADAPTER_H
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/hres_counter.h>
+
+#include <linux/a6_sbw_interface.h>
+#include <linux/a6.h>
+
+#ifndef __BYTEWORD__
+#define __BYTEWORD__
+typedef unsigned short int   word;
+typedef unsigned char   byte;
+#endif
+
+//---------------- Should be selected desired option ------------------------
+
+#define ACTIVATE_MAGIC_PATTERN 1
+
+
+//---------------------------------------------------------------------------
+
+#ifndef __DATAFORMATS__
+#define __DATAFORMATS__
+#define F_BYTE                     8
+#define F_WORD                     16
+#define F_ADDR                     20
+#define F_LONG                     32
+#endif
+
+// Constants for runoff status
+#define STATUS_ERROR     0      // false
+#define STATUS_OK        1      // true
+#define STATUS_FUSEBLOWN 2      // GetDevice returns if the security fuse is blown
+
+#define STATUS_ACTIVE    2
+#define STATUS_IDLE      3
+
+#define   nNOPS   {delay(1);}  //{ _NOP(); _NOP(); _NOP(); _NOP();  _NOP(); _NOP(); _NOP(); }
+
+
+/********/
+/* Host adapter for the sbw layer */
+/********/
+// per-target functions (separate implementation per target)
+#define   DisableInterrupts(flags)   (a6_disable_interrupts(flags))
+#define   EnableInterrupts(flags)    (a6_enable_interrupts(flags))
+
+#define MsDelay(milliseconds) {delay(milliseconds * 1000);}      // millisecond delay loop
+#define	usDelay(microseconds)  {delay(microseconds);}             // microsecond delay loop
+
+#endif
diff --git a/drivers/misc/a6/high_level_funcs.c b/drivers/misc/a6/high_level_funcs.c
new file mode 100644
index 0000000..cc84b51
--- /dev/null
+++ b/drivers/misc/a6/high_level_funcs.c
@@ -0,0 +1,527 @@
+/****************************************************************************/
+/* Includes                                                                 */
+/****************************************************************************/
+#include "a6_host_adapter.h"       // Maps function calls to host porting-layer implementations
+#include "jtag_funcs.h"	        	// Spy-by-wire JTAG functions
+#include "low_level_funcs.h"		// low level user functions
+
+#define LOCAL_TRACE 0
+
+/****************************************************************************/
+/* Global types                                                             */
+/****************************************************************************/
+
+/****************************************************************************/
+/* Main section of Replicator program: User can modify/insert code as needed*/
+/****************************************************************************/
+/*
+   Note: All High Level JTAG Functions are applied here.
+*/
+
+// definition for current implementation mappings used by the sbw code...
+uint16_t (*SetSBWTCK)(void) = NULL;
+uint16_t (*ClrSBWTCK)(void) = NULL;
+uint16_t (*SetSBWTDIO)(void) = NULL;
+uint16_t (*ClrSBWTDIO)(void) = NULL;
+uint16_t (*SetInSBWTDIO)(void) = NULL;
+uint16_t (*SetOutSBWTDIO)(void) = NULL;
+uint16_t (*GetSBWTDIO)(void) = NULL;
+uint16_t (*SetSBWAKEUP)(void) = NULL;
+uint16_t (*ClrSBWAKEUP)(void) = NULL;
+void (*delay)(uint32_t delay_us) = NULL;
+//
+
+typedef enum {
+	SBW_OK = 0,
+	SBW_TOK,
+	SBW_EOL,
+	SBW_EOS,
+	SBW_SOS,
+	SBW_EOI,
+	SBW_STATE_ERROR
+} SBW_STATE_CODE;
+
+#define SIZEOF_NEWLINE (1)
+
+static int hexval(char c)
+{
+	if (c >= '0' && c <= '9')
+		return c - '0';
+	else if (c >= 'a' && c <= 'f')
+		return c - 'a' + 10;
+	else if (c >= 'A' && c <= 'F')
+		return c - 'A' + 10;
+
+	return 0;
+}
+
+SBW_STATE_CODE sbw_get_token(uint8_t* read_p, uint8_t* write_p, uint32_t* read_len_p, uint32_t* write_len_p)
+{
+	SBW_STATE_CODE ret;
+
+	//assert(read_p && read_len_p && write_len_p);
+
+
+	// end-of-line
+	if (0x0d == *read_p && 0x0a == *(read_p+1)) {
+		*read_len_p = 2;
+		*write_len_p = 0;
+		ret = SBW_EOL;
+
+		//printk("<EOL>\n");
+	}
+	// end-of-image
+	else if (('q' == *read_p) || ('Q' == *read_p)) {
+		*read_len_p = 1;
+		*write_len_p = 0;
+		ret = SBW_EOI;
+
+		//printk("<EOI>\n");
+	}
+	else {
+		uint32_t val = 0;
+
+		// section start
+		if ('@' == read_p[0]) {
+			ret = SBW_SOS;
+			val = hexval(read_p[1 + 0]) << 12;
+			val |= hexval(read_p[1 +1]) << 8;
+			val |= hexval(read_p[1 +2]) << 4;
+			val |= hexval(read_p[1 +3]);
+
+			*read_len_p = 1+4+2; //'@' + XXXX + CRLF
+			*write_len_p = 2;    // two bytes written
+			//printk("<SOS>\n");
+		}
+		// data
+		else {
+			ret = SBW_TOK;
+			val = hexval(read_p[0]) << 4;
+			val |= hexval(read_p[1]);
+
+			// handle variation: the last 2-byte value on a line may not include trailing space
+			*read_len_p = 2+1; //XX + ' '
+			//*read_len_p = 2 + (' ' == read_p[2]) ? 1 : 0; //XX + ' '
+			*write_len_p = 1;    // one byte written
+		}
+
+		// no target? skip the actual write...
+		if (write_p) {
+			//*((uint32_t*)write_p) = val;
+			write_p[0] = val & 0x000000ff;
+			write_p[1] = (val >> 8) & 0x000000ff;
+			write_p[2] = (val >> 16) & 0x000000ff;
+			write_p[3] = (val >> 24) & 0x000000ff;
+		}
+
+		//printk("%02x ", val);
+	}
+
+	return ret;
+}
+
+
+SBW_STATE_CODE sbw_parse_line(uint8_t* read_p, uint8_t* write_p, uint32_t* read_len_p, uint32_t* write_len_p)
+{
+	SBW_STATE_CODE ret;
+	uint32_t total_read_len = 0, total_write_len = 0, val = 0, r_len = 0, w_len = 0;
+
+	do {
+		ret = sbw_get_token(read_p, (uint8_t*)&val, &r_len, &w_len);
+		// end-of-line; break out of loop
+		if (SBW_EOL == ret) {
+			total_read_len += r_len;
+			*read_len_p = total_read_len;
+			total_write_len += w_len;
+			*write_len_p = total_write_len;
+		}
+		// regular token; keep looping
+		else if (SBW_TOK == ret) {
+			*((uint8_t*)write_p) = (uint8_t)val;
+			total_read_len += r_len;
+			read_p += r_len;
+			total_write_len += w_len;
+			write_p += w_len;
+		}
+		// map start-of-section/end-of-image to end-of-section
+		else if ((SBW_SOS == ret) || (SBW_EOI == ret)) {
+			*read_len_p = *write_len_p = 0;
+			ret = SBW_EOS;
+		}
+		// state mismatch
+		else {
+			printk("SBW_ERROR[sbw_parse_line]: wrong state returned; state:  %d\n", ret);
+			ret =  SBW_STATE_ERROR;
+		}
+	} while (SBW_TOK == ret);
+
+
+	return ret;
+}
+
+
+SBW_STATE_CODE sbw_parse_section(uint8_t* read_p, uint8_t* write_p, uint32_t* read_len_p, uint32_t* write_len_p)
+{
+	SBW_STATE_CODE ret;
+	uint32_t total_read_len = 0, total_write_len = 0, r_len = 0, w_len = 0;
+
+	do {
+		ret = sbw_parse_line(read_p, write_p, &r_len, &w_len);
+		// end-of-section; break out of loop
+		if (SBW_EOS == ret){
+			total_read_len += r_len;
+			*read_len_p = total_read_len;
+			total_write_len += w_len;
+			*write_len_p = total_write_len;
+		}
+		// end-of-line; keep looping
+		else if (SBW_EOL == ret) {
+			total_read_len += r_len;
+			read_p += r_len;
+			total_write_len += w_len;
+			write_p += w_len;
+		}
+		// state mismatch
+		else {
+			printk("SBW_ERROR[sbw_parse_section]: wrong state returned; state:  %d\n", ret);
+			ret =  SBW_STATE_ERROR;
+		}
+	} while (SBW_EOL == ret);
+
+
+	return ret;
+}
+
+typedef struct {
+	uint32_t sec_addr[75];
+	uint32_t sec_len[75];
+	uint32_t num_sections;
+} sec_info_struct;
+
+sec_info_struct sec_info;
+int32_t sec_index = 0;
+
+
+SBW_STATE_CODE sbw_parse_image(uint8_t* read_p, uint8_t* write_p, uint32_t* read_len_p, uint32_t* write_len_p)
+{
+	SBW_STATE_CODE ret;
+	uint32_t total_read_len = 0, total_write_len = 0, r_len = 0, w_len = 0, val = 0;
+
+	memset(&sec_info, 0, sizeof(sec_info));
+
+
+	do {
+		ret = sbw_get_token(read_p, (uint8_t*)&val, &r_len, &w_len);
+		if (SBW_SOS != ret) {
+			if (!sec_info.num_sections) {
+				printk("SBW_ERROR[sbw_parse_image]: does not start with section; value:  %d\n", ret);
+				return SBW_STATE_ERROR;
+			}
+
+			if (SBW_EOI == ret) {
+				*read_len_p = total_read_len;
+				*write_len_p = total_write_len;
+				ret = SBW_OK;
+				break;
+			}
+			else {
+				printk("SBW_ERROR[sbw_parse_image]: wrong state returned; state:  %d\n", ret);
+				ret =  SBW_STATE_ERROR;
+				break;
+			}
+		}
+
+		//printk("[Status]: SOS detected; address: %x, r_len: %d, w_len: %d\n", val, r_len, w_len);
+
+		total_read_len += r_len;
+		read_p += r_len;
+
+		sec_info.sec_addr[sec_info.num_sections] = val;
+
+		ret = sbw_parse_section(read_p, write_p, &r_len, &w_len);
+		// end-of-section; keep looping
+		if (SBW_EOS == ret) {
+			total_read_len += r_len;
+			read_p += r_len;
+
+			if (w_len & 1) {
+				write_p[w_len] = 0xff;
+				w_len++;
+			}
+			total_write_len += w_len;
+			write_p += w_len;
+
+			// sec_len converted to A6 words (16-bit)
+			sec_info.sec_len[sec_info.num_sections] = w_len/2;
+			sec_info.num_sections++;
+		}
+		// state mismatch
+		else {
+			printk("SBW_ERROR[sbw_parse_image:1]: wrong state returned; state:  %d\n", ret);
+			ret =  SBW_STATE_ERROR;
+		}
+	} while (SBW_EOS == ret);
+
+	if (SBW_OK == ret) {
+		int idx = 0;
+
+		printk("Parsing complete. Read size: %d, Write size: %d. Num sections: %d\n",
+		       *read_len_p, *write_len_p, sec_info.num_sections);
+		while (idx < (int)sec_info.num_sections) {
+			printk("Section idx: %d; Addr: 0x%04x; Length: %d\n",
+			       idx, sec_info.sec_addr[idx], sec_info.sec_len[idx]);
+			idx++;
+		}
+
+/*
+		printk("\nDumping converted data:\n");
+		for (idx = 0; idx < (int)*write_len_p; idx++) {
+			if (!(idx % 16)) {
+				printk("\n");
+			}
+
+			printk("%02x ", (write_p-*write_len_p)[idx]);
+		}
+*/
+	}
+
+
+	return ret;
+}
+
+int program_device_sbw(struct a6_sbw_interface* sbw_ops, uint32_t read_address)
+{
+	uint32_t read_len = 0, write_len = 0;
+	SBW_STATE_CODE parse_ret;
+	int retry = 0, ret_val = 0;
+	uint16_t addr;
+
+
+	if (read_address & 1) {
+		printk("program_fw: Please enter an even read address.\n");
+		return -1;
+	}
+
+	// set up the current mappings for the sbw code...
+	SetSBWTCK = sbw_ops->a6_per_device_interface.SetSBWTCK;
+	ClrSBWTCK = sbw_ops->a6_per_device_interface.ClrSBWTCK;
+	SetSBWTDIO = sbw_ops->a6_per_device_interface.SetSBWTDIO;
+	ClrSBWTDIO = sbw_ops->a6_per_device_interface.ClrSBWTDIO;
+	SetInSBWTDIO = sbw_ops->a6_per_device_interface.SetInSBWTDIO;
+	SetOutSBWTDIO = sbw_ops->a6_per_device_interface.SetOutSBWTDIO;
+	GetSBWTDIO = sbw_ops->a6_per_device_interface.GetSBWTDIO;
+	SetSBWAKEUP = sbw_ops->a6_per_device_interface.SetSBWAKEUP;
+	ClrSBWAKEUP = sbw_ops->a6_per_device_interface.ClrSBWAKEUP;
+	delay = sbw_ops->a6_per_target_interface.delay;
+
+	parse_ret = sbw_parse_image((uint8_t*)read_address, (uint8_t*)read_address/*write_p*/, &read_len, &write_len);
+	if (SBW_OK != parse_ret) {
+		printk("Error in parsing A6 fw file...\n");
+		return -1;
+	}
+
+/* TEMP: Workaround for occasional verification failure. Not root-Caused yet but,
+   empirically, a retry always works. Revisit.*/
+retry_0:
+
+	InitTarget();
+
+	// Start of SBW access to the Target
+	if (GetDevice() != STATUS_OK)         // Set DeviceId
+	{
+		printk("Error in GetDevice()\n");      // stop here if invalid JTAG ID or
+	                                               // time-out. (error: red LED is ON)
+		ret_val = -1;
+		goto err0;
+	}
+
+
+	// Program the boot code
+	if (!WriteAllSections((const unsigned short*)read_address, (const unsigned long *)&sec_info.sec_addr[0],
+			      (const unsigned long *)&sec_info.sec_len[0], sec_info.num_sections))
+	{
+		printk("Error in WriteAllSections(all)\n");
+		ret_val = -1;
+		goto err0;
+	}
+
+
+	if (!VerifyAllSections((const unsigned short*)read_address, (const unsigned long *)&sec_info.sec_addr[0],
+			       (const unsigned long *)&sec_info.sec_len[0], sec_info.num_sections))
+	{
+		printk("Error in VerifyAllSections(all)\n");
+		printk("Retrying...\n\n");
+		if (retry++ < 15) {
+			addr = ReadMem_430Xv2(F_WORD, V_RESET);
+			ReleaseDevice(addr, ERROR);               // set PC to V_RESET contents
+			ReleaseTarget();
+			goto retry_0;
+		}
+		else {
+			printk("Failure to write and verify fw file after %d retries\n", retry);
+			ret_val = -1;
+		}
+	}
+
+
+err0:
+
+	addr = ReadMem_430Xv2(F_WORD, V_RESET);
+	if (ReleaseDevice(addr, PROGRAM) < 0) {               // set PC to V_RESET contents
+		printk(KERN_ERR "Checksum validation failed post-flashing.\n");
+		if (retry < 15) {
+			printk(KERN_ERR "Retrying...\n\n");
+			retry++;
+			ReleaseTarget();
+			goto retry_0;
+		}
+		else {
+			printk(KERN_ERR "Failure to program fw after %d retries.\n", retry);
+			ret_val = -1;
+		}
+	}
+
+	// if fail to set JTAG mode
+	if (ret_val == -1 && retry == 0 ) {
+		retry++;
+		ret_val = 0;
+		goto  retry_0;
+	}
+
+	ReleaseTarget();
+	return ret_val;
+}
+
+
+int verify_device_sbw(struct a6_sbw_interface* sbw_ops, uint32_t read_address)
+{
+	uint32_t read_len = 0, write_len = 0;
+	SBW_STATE_CODE parse_ret;
+	int ret_val = 0;
+	uint16_t addr;
+
+
+	if (read_address & 1) {
+		printk("program_fw: Please enter an even read address.\n");
+		return -1;
+	}
+
+	// set up the current mappings for the sbw code...
+	SetSBWTCK = sbw_ops->a6_per_device_interface.SetSBWTCK;
+	ClrSBWTCK = sbw_ops->a6_per_device_interface.ClrSBWTCK;
+	SetSBWTDIO = sbw_ops->a6_per_device_interface.SetSBWTDIO;
+	ClrSBWTDIO = sbw_ops->a6_per_device_interface.ClrSBWTDIO;
+	SetInSBWTDIO = sbw_ops->a6_per_device_interface.SetInSBWTDIO;
+	SetOutSBWTDIO = sbw_ops->a6_per_device_interface.SetOutSBWTDIO;
+	GetSBWTDIO = sbw_ops->a6_per_device_interface.GetSBWTDIO;
+	SetSBWAKEUP = sbw_ops->a6_per_device_interface.SetSBWAKEUP;
+	ClrSBWAKEUP = sbw_ops->a6_per_device_interface.ClrSBWAKEUP;
+	delay = sbw_ops->a6_per_target_interface.delay;
+
+	parse_ret = sbw_parse_image( (uint8_t*)read_address,
+				     (uint8_t*)read_address/*write_p*/,
+				      &read_len, &write_len);
+	if (SBW_OK != parse_ret) {
+		printk("Error in parsing A6 fw file...\n");
+		return -1;
+	}
+
+	InitTarget();
+
+	// Start of SBW access to the Target
+	if (GetDevice() != STATUS_OK)         // Set DeviceId
+	{
+		printk("Error in GetDevice()\n");      // stop here if invalid JTAG ID or
+	                                               // time-out. (error: red LED is ON)
+		ret_val = -1;
+		goto err0;
+	}
+
+	if (!VerifyAllSections((const unsigned short*)read_address,
+	     (const unsigned long *)&sec_info.sec_addr[0],
+	     (const unsigned long *)&sec_info.sec_len[0], sec_info.num_sections)) {
+		printk("Error in VerifyAllSections(all)\n");
+		ret_val = -1;
+	}
+
+
+err0:
+	addr = ReadMem_430Xv2(F_WORD, V_RESET);
+	ReleaseDevice(addr, VERIFY);  // set PC to V_RESET contents
+	ReleaseTarget();
+
+	return ret_val;
+}
+
+int ttf_extract_fw_sbw(struct a6_sbw_interface* sbw_ops)
+{
+	int ret_val = 0;
+	uint16_t addr;
+
+
+	// set up the current mappings for the sbw code...
+	SetSBWTCK = sbw_ops->a6_per_device_interface.SetSBWTCK;
+	ClrSBWTCK = sbw_ops->a6_per_device_interface.ClrSBWTCK;
+	SetSBWTDIO = sbw_ops->a6_per_device_interface.SetSBWTDIO;
+	ClrSBWTDIO = sbw_ops->a6_per_device_interface.ClrSBWTDIO;
+	SetInSBWTDIO = sbw_ops->a6_per_device_interface.SetInSBWTDIO;
+	SetOutSBWTDIO = sbw_ops->a6_per_device_interface.SetOutSBWTDIO;
+	GetSBWTDIO = sbw_ops->a6_per_device_interface.GetSBWTDIO;
+	SetSBWAKEUP = sbw_ops->a6_per_device_interface.SetSBWAKEUP;
+	ClrSBWAKEUP = sbw_ops->a6_per_device_interface.ClrSBWAKEUP;
+	delay = sbw_ops->a6_per_target_interface.delay;
+
+	InitTarget();
+
+	// Start of SBW access to the Target
+	if (GetDevice() != STATUS_OK)         // Set DeviceId
+	{
+		printk("Error in GetDevice()\n");      // stop here if invalid JTAG ID or
+	                                               // time-out. (error: red LED is ON)
+		ret_val = -1;
+		goto err0;
+	}
+
+	if (!TTFExtractAllSections()) {
+		printk("Error in TTFExtractAllSections\n");
+		ret_val = -1;
+	}
+
+
+err0:
+
+	addr = ReadMem_430Xv2(F_WORD, V_RESET);
+	ReleaseDevice(addr, VERIFY);  // set PC to V_RESET contents
+	ReleaseTarget();
+	return ret_val;
+}
+
+int ttf_image_read(char *buf, size_t count, loff_t *ppos)
+{
+	return TTFImageRead(buf, count, ppos);
+}
+
+int ttf_extract_cache_clear(void)
+{
+	TTFExtractCacheClear();
+	return 0;
+}
+
+int get_checksum_data_sbw(struct a6_sbw_interface* sbw_ops, unsigned short* cksum1,
+			  unsigned short* cksum2, unsigned short* cksum_cycles,
+			  unsigned short* cksum_errors)
+{
+	// set up the current mappings for the sbw code...
+	SetSBWTCK = sbw_ops->a6_per_device_interface.SetSBWTCK;
+	ClrSBWTCK = sbw_ops->a6_per_device_interface.ClrSBWTCK;
+	SetSBWTDIO = sbw_ops->a6_per_device_interface.SetSBWTDIO;
+	ClrSBWTDIO = sbw_ops->a6_per_device_interface.ClrSBWTDIO;
+	SetInSBWTDIO = sbw_ops->a6_per_device_interface.SetInSBWTDIO;
+	SetOutSBWTDIO = sbw_ops->a6_per_device_interface.SetOutSBWTDIO;
+	GetSBWTDIO = sbw_ops->a6_per_device_interface.GetSBWTDIO;
+	SetSBWAKEUP = sbw_ops->a6_per_device_interface.SetSBWAKEUP;
+	ClrSBWAKEUP = sbw_ops->a6_per_device_interface.ClrSBWAKEUP;
+	delay = sbw_ops->a6_per_target_interface.delay;
+
+	return GetChecksumData(cksum1, cksum2, cksum_cycles, cksum_errors);
+}
diff --git a/drivers/misc/a6/high_level_funcs.h b/drivers/misc/a6/high_level_funcs.h
new file mode 100644
index 0000000..d63d7e7
--- /dev/null
+++ b/drivers/misc/a6/high_level_funcs.h
@@ -0,0 +1,13 @@
+#ifndef _high_level_funcs_h_
+#define _high_level_funcs_h_
+
+int program_device_sbw(struct a6_sbw_interface* sbw_ops, uint32_t read_address);
+int verify_device_sbw(struct a6_sbw_interface* sbw_ops, uint32_t read_address);
+int ttf_extract_fw_sbw(struct a6_sbw_interface* sbw_ops);
+int ttf_extract_cache_clear(void);
+int ttf_image_read(char *buf, size_t count, loff_t *ppos);
+int get_checksum_data_sbw(struct a6_sbw_interface* sbw_ops, unsigned short* cksum1,
+	unsigned short* cksum2, unsigned short* cksum_cycles,
+	unsigned short* cksum_errors);
+
+#endif
diff --git a/drivers/misc/a6/jtag_funcs.c b/drivers/misc/a6/jtag_funcs.c
new file mode 100644
index 0000000..e37e109
--- /dev/null
+++ b/drivers/misc/a6/jtag_funcs.c
@@ -0,0 +1,1070 @@
+#include "a6_host_adapter.h"
+#include "low_level_funcs.h"
+#include "jtag_funcs.h"
+
+#define LOCAL_TRACE 1
+
+// declarations for active implementation mappings used by the sbw code...
+extern uint16_t (*SetSBWTCK)(void);
+extern uint16_t (*ClrSBWTCK)(void);
+extern uint16_t (*SetSBWTDIO)(void);
+extern uint16_t (*ClrSBWTDIO)(void);
+extern uint16_t (*SetInSBWTDIO)(void);
+extern uint16_t (*SetOutSBWTDIO)(void);
+extern uint16_t (*GetSBWTDIO)(void);
+extern uint16_t (*SetSBWAKEUP)(void);
+extern uint16_t (*ClrSBWAKEUP)(void);
+extern void (*delay)(uint32_t delay_us);
+//
+
+/****************************************************************************/
+/* Low level routines for accessing the target device via JTAG:             */
+/****************************************************************************/
+
+#define VCC_LEVEL  36
+static int8_t SetTargetVcc(int8_t level) {
+
+    return level;
+}
+
+
+//----------------------------------------------------------------------------
+/* Function for shifting a given 16-bit word into the JTAG data register
+   through TDI.
+   Arguments: word data (16-bit data, MSB first)
+   Result:    word (value is shifted out via TDO simultaneously)
+*/
+static word DR_Shift16(word data)
+{
+    // JTAG FSM state = Run-Test/Idle
+    if (TCLK_saved)
+    {
+        TMSH_TDIH();
+    }
+    else
+    {
+        TMSH_TDIL();
+    }
+    // JTAG FSM state = Select DR-Scan
+    TMSL_TDIH();
+    // JTAG FSM state = Capture-DR
+    TMSL_TDIH();
+    // JTAG FSM state = Shift-DR, Shift in TDI (16-bit)
+    return(AllShifts(F_WORD, data));
+    // JTAG FSM state = Run-Test/Idle
+}
+
+//----------------------------------------------------------------------------
+/* Function for shifting a given 20-bit address word into the
+   JTAG address register through TDI.
+   Arguments: unsigned long address (20-bit address word, MSB first)
+   Result:    unsigned long TDOvalue (is shifted out via TDO simultaneously)
+*/
+static unsigned long DR_Shift20(unsigned long address)
+{
+    // JTAG FSM state = Run-Test/Idle
+    if (TCLK_saved)
+    {
+        TMSH_TDIH();
+    }
+    else
+    {
+        TMSH_TDIL();
+    }
+    // JTAG FSM state = Select DR-Scan
+    TMSL_TDIH();
+    // JTAG FSM state = Capture-DR
+    TMSL_TDIH();
+    // JTAG FSM state = Shift-DR, Shift in TDI (16-bit)
+    return(AllShifts(F_ADDR, address));
+    // JTAG FSM state = Run-Test/Idle
+}
+
+//----------------------------------------------------------------------------
+/* Function for shifting a new instruction into the JTAG instruction
+   register through TDI (MSB first, but with interchanged MSB - LSB, to
+   simply use the same shifting function, Shift(), as used in DR_Shift16).
+   Arguments: byte Instruction (8bit JTAG instruction, MSB first)
+   Result:    word TDOword (value shifted out from TDO = JTAG ID)
+*/
+static word IR_Shift(byte instruction)
+{
+    // JTAG FSM state = Run-Test/Idle
+    if (TCLK_saved)
+    {
+        TMSH_TDIH();
+    }
+    else
+    {
+        TMSH_TDIL();
+    }
+    // JTAG FSM state = Select DR-Scan
+    TMSH_TDIH();
+
+    // JTAG FSM state = Select IR-Scan
+    TMSL_TDIH();
+    // JTAG FSM state = Capture-IR
+    TMSL_TDIH();
+    // JTAG FSM state = Shift-IR, Shift in TDI (8-bit)
+    return(AllShifts(F_BYTE, instruction));
+    // JTAG FSM state = Run-Test/Idle
+}
+
+//----------------------------------------------------------------------------
+/* Reset target JTAG interface and perform fuse-HW check.
+   Arguments: None
+   Result:    None
+*/
+static void ResetTAP(void)
+{
+    word i;
+
+    // Now fuse is checked, Reset JTAG FSM
+    for (i = 6; i > 0; i--)      // 6 is nominal
+    {
+        TMSH_TDIH();
+    }
+    // JTAG FSM is now in Test-Logic-Reset
+    TMSL_TDIH();                 // now in Run/Test Idle
+}
+
+//----------------------------------------------------------------------------
+/* Function to execute a Power-On Reset (POR) using JTAG CNTRL SIG register
+   Arguments: None
+   Result:    word (STATUS_OK if target is in Full-Emulation-State afterwards,
+                    STATUS_ERROR otherwise)
+*/
+static word ExecutePOR_430Xv2(void)
+{
+  word i = 0;
+
+  // provide one clock
+  ClrTCLK();
+  SetTCLK();
+
+  // prepare access to the JTAG CNTRL SIG register  
+  IR_Shift(IR_CNTRL_SIG_16BIT);
+  // release CPUSUSP signal and apply POR signal
+  DR_Shift16(0x0C01);
+  // release POR signal again
+  DR_Shift16(0x0401);
+  
+  // provide 5 clock cycles
+  for (i = 0; i < 5; i++)
+  {
+    ClrTCLK();
+    SetTCLK();
+  }
+  // now set CPUSUSP signal again
+  DR_Shift16(0x0501);
+  // and provide one more clock
+  ClrTCLK();
+  SetTCLK();
+  // the CPU is now in 'Full-Emulation-State'
+  
+  // disable Watchdog Timer on target device now by setting the HOLD signal
+  // in the WDT_CNTRL register
+  WriteMem_430Xv2(F_WORD, 0x015C, 0x5A80);
+
+  // Check if device is again in Full-Emulation-State and return status
+  IR_Shift(IR_CNTRL_SIG_CAPTURE);
+  if(DR_Shift16(0) & 0x0301)
+  {
+    return(STATUS_OK);
+  }
+  
+  return(STATUS_ERROR);
+}
+
+//----------------------------------------------------------------------------
+/* Load a given address into the target CPU's program counter (PC).
+   Argument: unsigned long Addr (destination address)
+   Result:   None
+*/
+static void SetPC_430Xv2(unsigned long Addr)
+{
+  unsigned short Mova;
+  unsigned short Pc_l;
+  
+  Mova  = 0x0080;
+  Mova += (unsigned short)((Addr>>8) & 0x00000F00);
+  Pc_l  = (unsigned short)((Addr & 0xFFFF));
+  
+  // Check Full-Emulation-State at the beginning
+  IR_Shift(IR_CNTRL_SIG_CAPTURE);
+  if(DR_Shift16(0) & 0x0301)
+  {
+    // MOVA #imm20, PC
+    ClrTCLK();
+    // take over bus control during clock LOW phase
+    IR_Shift(IR_DATA_16BIT);
+    SetTCLK();
+    DR_Shift16(Mova);
+    IR_Shift(IR_CNTRL_SIG_16BIT);
+    DR_Shift16(0x1400);
+    IR_Shift(IR_DATA_16BIT);
+    ClrTCLK();
+    SetTCLK();
+    DR_Shift16(Pc_l);
+    ClrTCLK();
+    SetTCLK();
+    DR_Shift16(0x4303);    
+    ClrTCLK();
+    IR_Shift(IR_ADDR_CAPTURE);    
+    DR_Shift20(0x00000);
+    SetTCLK();
+  }
+}
+
+/****************************************************************************/
+/* High level routines for accessing the target device via JTAG:            */
+/*                                                                          */
+/* From the following, the user is relieved from coding anything.           */
+/* To provide better understanding and clearness, some functionality is     */
+/* coded generously. (Code and speed optimization enhancements may          */
+/* be desired)                                                              */
+/****************************************************************************/
+
+#define MAX_ENTRY_TRY 4
+
+static word JtagId = 0;
+static word CoreId = 0;
+static unsigned long DeviceIdPointer = 0;
+static word DeviceId = 0;
+
+static void ConnectJTAG(void)
+{
+	/* 8051-CODE */
+	// drive JTAG/TEST signals
+	{
+		DrvSignals();
+
+                //P2_7 = 0;
+		ClrSBWAKEUP(); // prepare to close sbw isolation switch
+		ClrSBWTCK();
+		usDelay(10);
+
+                //P2_7 = 1;
+		SetSBWAKEUP();
+		usDelay(4);
+		SetSBWTCK();
+		usDelay(4);
+
+		//P2_7 = 0;
+		ClrSBWAKEUP();
+		usDelay(4);
+		ClrSBWTCK();
+		usDelay(4);     }  // <=== got left out of first email
+}
+
+
+static void StartJtag(void)
+{
+
+	/* 8051-CODE */
+        // reset TEST logic, spybiwire jtag restart
+	ClrSBWTCK();
+	usDelay(100);
+
+        // ensure A6 powered up
+	SetSBWAKEUP();
+	usDelay(100);
+
+        // ensure Rst negated
+	SetSBWTDIO();
+	usDelay(100); 
+
+        // PHASE 1 -> TEST PIN TO 1
+	SetSBWTCK(); // prepare to activate TEST logic
+	usDelay(100); 
+
+        // PHASE 2 -> TEST PIN TO 0
+	ClrSBWTCK(); // issue phantom spybiwire clock
+
+        // PHASE 3
+	usDelay(1); // low time < 7 uSec to enalbe mcu sbw jtag
+
+        // phase 4 -> TEST PIN TO 1
+	SetSBWTCK();
+	usDelay(100);
+
+        // phase 5
+        // MsDelay(5);
+}
+
+static void StopJtag (void)
+{
+	/* 8051-CODE */
+	// release JTAG/TEST signals
+	{
+		RlsSignals();
+		//P2MDIN &= ~0x80;
+		//P2MDOUT &= ~0x80;
+		//P2_7 = 1;
+		SetSBWAKEUP();
+		MsDelay(1);
+	}
+}
+
+static word GetCoreID (void)
+{
+  word i;
+  for (i = 0; i < MAX_ENTRY_TRY; i++)
+  {
+    // initialize JtagId with an invalid value
+    JtagId = 0;  
+    // release JTAG/TEST signals to savely reset the test logic
+    StopJtag();        
+    // establish the physical connection to the JTAG interface
+    ConnectJTAG();               
+    // Apply again 4wire/SBW entry Sequence. 
+    // set ResetPin =1    
+      StartJtag();
+    // reset TAP state machine -> Run-Test/Idle
+    ResetTAP();  
+    // shift out JTAG ID
+    JtagId = (word)IR_Shift(IR_CNTRL_SIG_CAPTURE);  
+     
+    //printk("JTAG ID returned: %x; expected: %x\n", JtagId, JTAG_ID91);
+    // break if a valid JTAG ID is being returned
+    if(JtagId == JTAG_ID91) 
+      break;
+    // 
+    SetTargetVcc( 0 );
+    MsDelay(200);
+    SetTargetVcc( VCC_LEVEL );
+    ConnectJTAG(); 
+      StartJtag();
+    ResetTAP(); 
+
+    JtagId = (word)IR_Shift(IR_CNTRL_SIG_CAPTURE);
+    //printk("(2) JTAG ID returned: %x; expected: %x\n", JtagId, JTAG_ID91);
+    if(JtagId == JTAG_ID91)
+      break;
+  }
+  if(i >= MAX_ENTRY_TRY)
+  {         
+    // if connected device is MSP4305438 JTAG Mailbox is not usable
+    #ifdef ACTIVATE_MAGIC_PATTERN
+    /* xxx for(i = 0; i < MAX_ENTRY_TRY; i++)
+    {
+        // if no JTAG ID is  beeing returnd -> apply magic pattern to stop user cd excecution     
+      if((JtagId = magicPattern()) == 1 || i >= MAX_ENTRY_TRY)
+      {
+          // if magic pattern faild and 4 tries passed -> return status error
+          return(STATUS_ERROR);
+      }
+      else
+      {
+          break; 
+      }
+    }*/
+    // is MSP4305438 mailbox is not usalbe
+    #else
+       return(STATUS_ERROR);
+    #endif   
+  }
+  if(JtagId == JTAG_ID91)
+  {
+    // Get Core identification info
+    IR_Shift(IR_COREIP_ID);
+    CoreId = DR_Shift16(0);
+    //printk("CoreId returned: %x\n", CoreId);
+    if(CoreId == 0)
+    {
+      return(STATUS_ERROR);
+    }
+    IR_Shift(IR_DEVICE_ID);
+    DeviceIdPointer = DR_Shift20(0);
+    // The ID pointer is an un-scrambled 20bit value
+    DeviceIdPointer = ((DeviceIdPointer & 0xFFFF) << 4 )  + (DeviceIdPointer >> 16 );
+    //printk("DeviceIdPointer returned: %lx\n", DeviceIdPointer);
+    
+    return(STATUS_OK);
+  }
+  else
+  {
+    return(STATUS_ERROR);
+  }
+}
+
+static word SyncJtag_AssertPor (void)
+{
+  word i = 0;
+  word jid = 0;
+
+  IR_Shift(IR_CNTRL_SIG_16BIT);
+  DR_Shift16(0x1501);                  // Set device into JTAG mode + read
+  jid = IR_Shift(IR_CNTRL_SIG_CAPTURE);
+  if (jid != JTAG_ID91)
+  {
+    printk("%s: Failed JTAGID verification. Expected: %x; ret: %x\n", __func__, JTAG_ID91, jid);
+    return(STATUS_ERROR);
+  }
+  // wait for sync
+  while(!(DR_Shift16(0) & 0x0200) && i < 50)
+  {
+    i++;
+  };
+  // continues if sync was sucessfull
+  if(i >= 50)
+  {
+    return(STATUS_ERROR);
+  }
+
+  // execute a Power-On-Reset
+  if(ExecutePOR_430Xv2() != STATUS_OK)
+  {
+    printk("%s: Failed ExecutePOR_430Xv2.\n", __func__);
+    return(STATUS_ERROR);
+  }
+  
+  return(STATUS_OK);
+}
+//----------------------------------------------------------------------------
+/* Function to clear and confirm watchdog disabled.
+   Result:    word (STATUS_ERROR if unable to confirm VMON_MODE
+                    bit 7 has been cleared)
+*/
+
+byte pmicWriteRead(byte addr, byte wdat)
+{
+  byte rdat;
+
+  // PB(DIR|OUT)  (bit 7 = sel (adr), 4 = rw (wrt),  bit 0 = en)
+
+  // spg430 control (port 3) control
+  WriteMem_430Xv2(F_BYTE, 0x222, 0x90 ); // set adr/wrt, clear en
+  WriteMem_430Xv2(F_BYTE, 0x224, 0x91 ); // enable control outputs
+
+//	bic.b	#0x01, &PBOUT_L		; clear en	(0x222)
+//	or.b	#0x90, &PBOUT_L		; set rw, sel	(0x222)
+//	or.b	#0x91, &PBDIR_L		; enable ctl	(0x224)
+
+  // spg430 data (port 2) output
+  WriteMem_430Xv2(F_BYTE, 0x205, 0xFF ); // enable data outputs
+  //              F_WORD, 0x204, 0xFF00
+
+//	mov.b	#0xff, &PADIR_H		; enable data	(0x205)
+
+  WriteMem_430Xv2(F_BYTE, 0x203, addr ); // select vmon mode reg
+  //              F_WORD, 0x202, 0x7E00
+  WriteMem_430Xv2(F_BYTE, 0x222, 0x91 ); // set en
+  WriteMem_430Xv2(F_BYTE, 0x222, 0x90 ); // clear en
+
+//	mov.b	#PMIC_VMON_MODE, &PAOUT_H	;	(0x203)
+//	or.b	#0x01, &PBOUT_L		; set en	(0x222)
+//	nop
+//	bic.b	#0x01, &PBOUT_L		; clear en	(0x222)
+
+  WriteMem_430Xv2(F_BYTE, 0x203, wdat ); // set write data
+  //              F_WORD, 0x202, 0x0200
+  WriteMem_430Xv2(F_BYTE, 0x222, 0x10 ); // clear sel
+  WriteMem_430Xv2(F_BYTE, 0x222, 0x11 ); // set en
+  WriteMem_430Xv2(F_BYTE, 0x222, 0x10 ); // clear en
+
+//	mov.b	#PMIC_SBW_EN, &PAOUT_H		;	(0x203)
+//	bic.b	#0x80, &PBOUT_L		; clear sel	(0x222)
+//	or.b	#0x01, &PBOUT_L		; set en	(0x222)
+//	nop
+//	bic.b	#0x01, &PBOUT_L		; clear en	(0x222)
+
+  // spg430 data (port 2) input
+  WriteMem_430Xv2(F_BYTE, 0x205, 0x00 ); // disable data outputs
+  //              F_WORD, 0x204, 0x0000
+
+//	mov.b	#0x00, &PADIR_H		; disable data	(0x205)
+
+  WriteMem_430Xv2(F_BYTE, 0x222, 0x00 ); // clear sel,rw
+  WriteMem_430Xv2(F_BYTE, 0x222, 0x01 ); // set en
+  rdat = (byte) ReadMem_430Xv2(F_BYTE, 0x201); // readback vmon mode reg
+  //                         F_WORD, 0x200
+  WriteMem_430Xv2(F_BYTE, 0x222, 0x00 ); // clear en
+
+//	bic.b	#0x00, &PBOUT_L		; clear sel,rw 	(0x222)
+//	or.b	#0x01, &PBOUT_L		; set en	(0x222)
+//	nop
+//	nop
+//	nop
+//	mov.b	&PAOUT_H, DATA		; read data	(0x201)
+//	bic.b	#0x01, &PBOUT_L		; clear en	(0x222)
+
+  return rdat;
+}
+
+signed char ClearWatchDogEnable(void)
+{
+
+   if (pmicWriteRead(0x7e,0x02) !=  0x02) // vmon_mode reg
+       return(STATUS_ERROR);
+
+   return(STATUS_OK); // wdt clear, sbw set
+}
+
+
+//----------------------------------------------------------------------------
+/* Function to take target device under JTAG control. Disables the target
+   watchdog. Sets the global DEVICE variable as read from the target device.
+   Arguments: None
+   Result:    word (STATUS_ERROR if fuse is blown, incorrect JTAG ID or
+                    synchronizing time-out; STATUS_OK otherwise)
+*/
+word GetDevice_430Xv2(void)
+{
+  uint32_t t1_val, t2_val, t3_val, t4_val;
+  int32_t i;
+  word wd_stat;
+  unsigned long flags = 0;
+
+  i = 20;
+  DisableInterrupts(flags);
+  do {
+	t1_val = hres_get_counter();
+	if(GetCoreID () != STATUS_OK) {
+		printk("GetCoreID failed.\n");
+	}
+
+	t2_val = hres_get_counter();
+	if(SyncJtag_AssertPor() != STATUS_OK) {
+		printk("SyncJtag_AssertPor failed.\n");
+	}
+
+	t3_val = hres_get_counter();
+	wd_stat = ClearWatchDogEnable();
+	t4_val = hres_get_counter();
+
+	// disable write protection...
+	WriteMem_430Xv2(F_WORD, 0x120, 0xa500 );
+	WriteMem_430Xv2(F_WORD, 0x122, 0x0000 );
+
+	pmicWriteRead(0x2c,0x00); // 1-wire mode_status: clr DQ
+
+	printk("T1 %lu T2 %lu T3 %lu Total %lu\n",
+	       hres_get_delta_usec(t1_val, t2_val)/USEC_PER_MSEC,
+	       hres_get_delta_usec(t2_val, t3_val)/USEC_PER_MSEC,
+	       hres_get_delta_usec(t3_val, t4_val)/USEC_PER_MSEC,
+	       hres_get_delta_usec(t1_val, t4_val)/USEC_PER_MSEC);
+	t1_val = t2_val = t3_val = t4_val = 0;
+	if ( wd_stat == STATUS_OK ) {
+		break;
+	}
+	else {
+		printk("ClearWatchDogEnable failed.\n");
+	}
+  } while(--i);
+
+  EnableInterrupts(flags);
+
+  if (i == 0) return(STATUS_ERROR);
+
+  // CPU is now in Full-Emulation-State
+
+  // TEMP-HACK: CPU now under JTAG control and will transition to sleep; release WAKEUP...
+  //ClrSBWAKEUP();
+
+  // read DeviceId from memory
+  ReadMemQuick_430Xv2(DeviceIdPointer + 4, 1, (word*)&DeviceId);
+    
+  return(STATUS_OK);
+}
+
+
+//----------------------------------------------------------------------------
+/* Function to release the target device from JTAG control
+   Argument: word Addr (0xFFFE: Perform Reset, means Load Reset Vector into PC,
+                        otherwise: Load Addr into PC)
+   Result:   None
+*/
+int ReleaseDevice_430Xv2(unsigned long Addr, byte Stat)
+{
+	word i,prev,check,count;
+	word error_count = 0, cksum_cycle_count = 0;
+	int ret = 0;
+
+	i = 1;
+	do {
+		ClrSBWAKEUP(); // to permit A6 firmware to clear sbw_en
+
+		switch(Addr)
+		{
+		case V_BOR: // no longer approved for use
+			//// perform a BOR via JTAG - we loose control of the device then...
+			//IR_Shift(IR_TEST_REG);
+			//DR_Shift16(0x0200);
+			//MsDelay(5);     // wait some time before doing any other action
+			// JTAG control is lost now - GetDevice() needs to be called again to gain control.
+			break;
+		case V_RESET:
+			IR_Shift(IR_CNTRL_SIG_16BIT);
+			DR_Shift16(0x0C01);                 // Perform a reset
+			DR_Shift16(0x0401);
+			IR_Shift(IR_CNTRL_SIG_RELEASE);
+			break;
+		default:
+			SetPC_430Xv2(Addr);                 // Set target CPU's PC
+
+			IR_Shift(IR_CNTRL_SIG_16BIT);
+			DR_Shift16(0x0401);
+			IR_Shift(IR_ADDR_CAPTURE);
+			IR_Shift(IR_CNTRL_SIG_RELEASE);
+		}
+
+		ReleaseTarget();
+
+		if (i) { // entry pass
+
+			if (Addr == V_BOR) { // boot code
+				MsDelay(1000);
+			}
+			else {            // mcu restart
+				MsDelay(250);
+			}
+
+			GetDevice_430Xv2(); // halt to inquire value computed over code
+			check = ReadMem_430Xv2(F_WORD, 0x1A1A); // current/1a1a
+			prev = ReadMem_430Xv2(F_WORD, 0x1A1E); // previous/1a1e
+			printk(KERN_ERR "A6 checksum validation: master: 0x%04x, current: 0x%04x\n", prev, check);
+
+			if (check != prev) {
+				ret = -1;
+				printk(KERN_ERR "Error: A6 checksum validation failed!!\n");
+			}
+			// cheksum validated...
+			error_count = ReadMem_430Xv2(F_WORD, 0x1a20); // read error count
+			cksum_cycle_count = ReadMem_430Xv2(F_WORD, 0x1a22); // read cksum cycle count
+			printk(KERN_ERR "A6 error_count: %d; cksum cycle count: %d\n", error_count, cksum_cycle_count);
+
+			if (Stat == PROGRAM && 0 == ret) {
+				count = ReadMem_430Xv2(F_WORD, 0xFFD0); // read prog cycle count
+				printk(KERN_ERR "A6 program counter: %d\n", count);
+				WriteMem_430Xv2(F_WORD, 0xFFD0, count+1); // increment write back
+			}
+		}
+
+	} while (i--);
+
+	return ret;
+}
+
+//----------------------------------------------------------------------------
+/* This function writes one byte/word at a given address ( <0xA00)
+   Arguments: word Format (F_BYTE or F_WORD)
+              word Addr (Address of data to be written)
+              word Data (shifted data)
+   Result:    None
+*/
+void WriteMem_430Xv2(word Format, unsigned long Addr, word Data)
+{
+  // Check Init State at the beginning
+  IR_Shift(IR_CNTRL_SIG_CAPTURE);
+  if(DR_Shift16(0) & 0x0301)
+  {
+    ClrTCLK();
+    IR_Shift(IR_CNTRL_SIG_16BIT);
+    if  (Format == F_WORD)
+    {
+      DR_Shift16(0x0500);
+    }
+    else
+    {
+      DR_Shift16(0x0510);
+    }
+    IR_Shift(IR_ADDR_16BIT);
+    DR_Shift20(Addr);
+    
+    SetTCLK();
+    // New style: Only apply data during clock high phase
+    IR_Shift(IR_DATA_TO_ADDR);
+    DR_Shift16(Data);           // Shift in 16 bits
+    ClrTCLK();
+    IR_Shift(IR_CNTRL_SIG_16BIT);
+    DR_Shift16(0x0501);
+    SetTCLK();
+    // one or more cycle, so CPU is driving correct MAB
+    ClrTCLK();
+    SetTCLK();
+    // Processor is now again in Init State
+  }
+}
+
+//----------------------------------------------------------------------------
+/* This function writes an array of words into the target memory.
+   Arguments: word StartAddr (Start address of target memory)
+              word Length (Number of words to be programmed)
+              word *DataArray (Pointer to array with the data)
+   Result:    None
+*/
+void WriteMemQuick_430Xv2(unsigned long StartAddr, unsigned long Length, word *DataArray)
+{
+  unsigned long i;
+  
+  for (i = 0; i < Length; i++)
+  {
+    WriteMem_430Xv2(F_WORD, StartAddr, DataArray[i]);
+    StartAddr += 2;
+  }
+}
+
+//----------------------------------------------------------------------------
+/* This function programs a set of data arrays of words into a Memeory
+   by using the "WriteMemQuick()" function. It conforms with the
+   "CodeArray" structure convention of file "Target_Code.s43" or "Target_Code.h".
+   Arguments: 	const unsigned int  *DataArray (Pointer to array with the data)
+		const unsigned long *address (Pointer to array with the startaddresses)
+		const unsigned long *length_of_sections (Pointer to array with the number of words counting from startaddress)
+		const unsigned long sections (Number of sections in code file)
+   Result:      word (STATUS_OK if verification was successful,
+                    STATUS_ERROR otherwise)
+*/
+word WriteAllSections_430Xv2(const unsigned short *data, const unsigned long *address, const unsigned long *length_of_sections, const unsigned long sections)
+{
+    int i, pos = 0;
+    //time_t start_time;
+    
+    //start_time = current_time();
+
+    printk("\n\n");
+    for(i = 0; i < (int)sections; i++)
+    {
+	    printk("WriteMemQuick: addr: 0x%lx; length(d): %ld", address[i], length_of_sections[i]);
+	    /*printk("; first 4 bytes: 0x%02x 0x%02x 0x%02x 0x%02x",
+		   *(char*)&data[pos], *((char*)&data[pos]+1), *((char*)&data[pos]+2), *((char*)&data[pos]+3)); */
+	    printk("\n");
+
+	    WriteMemQuick(address[i],length_of_sections[i],(word*)&data[pos]);
+	    pos+=length_of_sections[i];
+	    yield();
+    }
+
+    //printk("[WriteAllSections] elapsed time: %d ms\n", current_time() - start_time);
+
+
+    return(STATUS_OK);
+}
+
+//----------------------------------------------------------------------------
+/* This function verifies a set of data arrays of words from Memeory
+   by using the "VerifyMem()" function. It conforms with the
+   "CodeArray" structure convention of file "Target_Code.s43" or "Target_Code.h".
+   Arguments: 	const unsigned int  *DataArray (Pointer to array with the data)
+		const unsigned long *address (Pointer to array with the startaddresses)
+		const unsigned long *length_of_sections (Pointer to array with the number of words counting from startaddress)
+		const unsigned long sections (Number of sections in code file)
+   Result:      word (STATUS_OK if verification was successful,
+                    STATUS_ERROR otherwise)
+*/
+word VerifyAllSections_430Xv2(const unsigned short *data, const unsigned long *address, const unsigned long *length_of_sections, const unsigned long sections)
+{
+    int i, pos = 0;
+    word ret = STATUS_OK, latched_ret = STATUS_OK;
+
+    printk("\n\n");
+    for(i = 0; i < (int)sections; i++)
+    {
+	    printk("VerifyMem: addr: 0x%lx; length(d): %ld", address[i], length_of_sections[i]);
+	    /*printk("; first 4 bytes: 0x%02x 0x%02x 0x%02x 0x%02x",
+		    *(char*)&data[pos], *((char*)&data[pos]+1), *((char*)&data[pos]+2), *((char*)&data[pos]+3));*/
+	    printk("\n");
+
+	    ret = VerifyMem(address[i],length_of_sections[i],(word*)&data[pos]);
+	    if (STATUS_ERROR == ret) {
+		    latched_ret = STATUS_ERROR;
+		    printk("VerifyAllSections: Failed for section addr: 0x%lx; length(d): %ld\n",
+			   address[i], length_of_sections[i]);
+	    }
+	    yield();
+
+	    pos+=length_of_sections[i];
+    }    
+
+    return(latched_ret);
+}
+
+word TTFExtractSection_430Xv2
+(const unsigned long sec_addr, const unsigned long sec_len,
+ unsigned char* sec_databuf, extract_conv_fn ttf_conv,
+ unsigned long* sec_fmt_len)
+{
+	unsigned int idx;
+	unsigned short data;
+	int conv_bytes;
+	unsigned char* w_buf = sec_databuf;
+
+	SetPC_430Xv2(sec_addr);
+	IR_Shift(IR_CNTRL_SIG_16BIT);
+	DR_Shift16(0x0501);
+	IR_Shift(IR_ADDR_CAPTURE);
+
+	IR_Shift(IR_DATA_QUICK);
+
+	for (idx = 0; idx < sec_len; idx++)
+	{
+		SetTCLK();
+		ClrTCLK();
+		data = DR_Shift16(0);  // Read data from memory.
+		conv_bytes = ttf_conv(data, w_buf, idx);
+		w_buf += conv_bytes;
+	}
+
+	if (idx % 8) {
+		sprintf(w_buf, "\x0d\x0a");
+		w_buf += 2;
+	}
+
+	IR_Shift(IR_CNTRL_SIG_CAPTURE);
+	*sec_fmt_len = (unsigned long)(w_buf - sec_databuf);
+
+	return STATUS_OK;
+}
+
+#define NUM_PMEM_SECTIONS  (4)
+#define SECTION_PREFIX_LEN (7)
+
+struct section_map_desc {
+	unsigned long sec_addr[NUM_PMEM_SECTIONS];
+	unsigned long sec_len[NUM_PMEM_SECTIONS];
+	void* sec_databuf[NUM_PMEM_SECTIONS];
+	unsigned long sec_fmt_len[NUM_PMEM_SECTIONS];
+};
+
+struct section_map_desc ttf_extract_smap =
+{
+	.sec_addr = {0x1400, 0x1800, 0x1c00, 0xc800},
+	.sec_len =  {0x200,  0x200,  0x200,  0x1c00}, // double-byte length
+};
+
+int ttf_extract_conv_fn
+	(const unsigned short inp_data, unsigned char* op_data, unsigned int count)
+{
+	int ret;
+	
+	ret = sprintf(op_data, "%02X %02X ",
+		      (unsigned char)inp_data,
+		      (unsigned char)(inp_data >> 8));
+	if (0 == (count+1) % 8) {
+		ret += sprintf(op_data + ret, "\x0d\x0a");
+	}
+
+	return ret;
+}
+
+word TTFExtractAllSections_430Xv2(void)
+{
+	int idx = 0, hdr_len;
+	unsigned char* sec_buf;
+	unsigned long sec_fmt_len = 0;
+
+	for (idx = 0; idx < NUM_PMEM_SECTIONS; idx++) {
+		/* allocate buffers */
+		sec_buf = kzalloc(
+			(ttf_extract_smap.sec_len[idx] * 6) +         // 4 digits + 2 spaces
+			((ttf_extract_smap.sec_len[idx] / 8) * 2) +   // newline for row of 8
+			0x14,					      // overhead
+			GFP_KERNEL);
+		ttf_extract_smap.sec_databuf[idx] = sec_buf;
+
+		hdr_len = sprintf(sec_buf, "@%04hX\x0d\x0a",
+				  (unsigned short)ttf_extract_smap.sec_addr[idx]);
+		TTFExtractSection(
+			ttf_extract_smap.sec_addr[idx],
+			ttf_extract_smap.sec_len[idx],
+			sec_buf + hdr_len,
+			ttf_extract_conv_fn,
+			&sec_fmt_len);
+		ttf_extract_smap.sec_fmt_len[idx] = sec_fmt_len + SECTION_PREFIX_LEN;
+
+		printk(KERN_ERR "Section Start (fmt len: %ld)\n", sec_fmt_len);
+	}
+
+	return STATUS_OK;
+}
+
+int TTFImageRead_430Xv2(char *buf, size_t count, loff_t *ppos)
+{
+	int idx, s_off, b_off = 0, ret;
+	unsigned int accum_size = 0, copy_size = 0, rem_count = count;
+	loff_t offset = *ppos;
+
+	for (idx = 0; idx < NUM_PMEM_SECTIONS; idx++) {
+		if (offset < (accum_size + ttf_extract_smap.sec_fmt_len[idx])) {
+			s_off = offset - accum_size;
+			copy_size = ((ttf_extract_smap.sec_fmt_len[idx] - s_off) >  rem_count) ?
+					rem_count : (ttf_extract_smap.sec_fmt_len[idx] - s_off);
+			ret = copy_to_user(buf + b_off,
+					   ((char*)ttf_extract_smap.sec_databuf[idx]) + s_off,
+					   copy_size);
+			rem_count -= copy_size;
+			offset += copy_size;
+			b_off += copy_size;
+		}
+
+		if (!rem_count) break;
+		accum_size += ttf_extract_smap.sec_fmt_len[idx];
+	}
+
+	*ppos = offset;
+	return (count - rem_count);
+}
+
+
+word TTFExtractCacheClear_430Xv2(void)
+{
+	int idx = 0;
+
+	for (idx = 0; idx < NUM_PMEM_SECTIONS; idx++) {
+		/* free buffers */
+		if (ttf_extract_smap.sec_databuf[idx]) {
+			kfree(ttf_extract_smap.sec_databuf[idx]);
+			ttf_extract_smap.sec_databuf[idx] = NULL;
+			ttf_extract_smap.sec_fmt_len[idx] = 0;
+		}
+	}
+
+	return STATUS_OK;
+}
+
+
+int GetChecksumData_430Xv2(unsigned short* cksum1, unsigned short* cksum2,
+			   unsigned short* cksum_cycles, unsigned short* cksum_errors)
+{
+	word prev, check;
+	word error_count = 0, cycle_count = 0;
+	int ret = 0;
+	word addr;
+
+	InitTarget();
+	// halt to inquire value computed over code
+	GetDevice_430Xv2();
+
+	check = ReadMem_430Xv2(F_WORD, 0x1A1A); // current/1a1a
+	prev = ReadMem_430Xv2(F_WORD, 0x1A1E); // previous/1a1e
+	error_count = ReadMem_430Xv2(F_WORD, 0x1a20); // read error count
+	cycle_count = ReadMem_430Xv2(F_WORD, 0x1a22); // read cksum cycle count
+
+	if (cksum1) *cksum1 = prev;
+	if (cksum2) *cksum2 = check;
+	if (cksum_cycles) *cksum_cycles = cycle_count;
+	if (cksum_errors) *cksum_errors = error_count;
+
+	// reset
+	addr = ReadMem_430Xv2(F_WORD, V_RESET);
+	SetPC_430Xv2(addr);                 // Set target CPU's PC
+	IR_Shift(IR_CNTRL_SIG_16BIT);
+	DR_Shift16(0x0401);
+	IR_Shift(IR_ADDR_CAPTURE);
+	IR_Shift(IR_CNTRL_SIG_RELEASE);
+
+	MsDelay(1000);
+
+	return ret;
+}
+//----------------------------------------------------------------------------
+/* This function reads one byte/word from a given address in memory
+   Arguments: word Format (F_BYTE or F_WORD)
+              word Addr (address of memory)
+   Result:    word (content of the addressed memory location)
+*/
+word ReadMem_430Xv2(word Format, unsigned long Addr)
+{
+  word TDOword = 0;
+  
+  // Check Init State at the beginning
+  IR_Shift(IR_CNTRL_SIG_CAPTURE);
+  if(DR_Shift16(0) & 0x0301)
+  {
+    // Read Memory
+    ClrTCLK();
+    IR_Shift(IR_CNTRL_SIG_16BIT);
+    if  (Format == F_WORD)
+    {
+      DR_Shift16(0x0501);             // Set word read
+    }
+    else
+    {
+      DR_Shift16(0x0511);             // Set byte read
+    }
+    IR_Shift(IR_ADDR_16BIT);
+    DR_Shift20(Addr);                   // Set address
+    IR_Shift(IR_DATA_TO_ADDR);
+    SetTCLK();
+    ClrTCLK();
+    TDOword = DR_Shift16(0x0000);       // Shift out 16 bits
+    
+    SetTCLK();
+    // one or more cycle, so CPU is driving correct MAB
+    ClrTCLK();
+    SetTCLK();
+    // Processor is now again in Init State
+  }
+  
+  return TDOword;
+}
+
+//----------------------------------------------------------------------------
+/* This function reads an array of words from a memory.
+   Arguments: word StartAddr (Start address of memory to be read)
+              word Length (Number of words to be read)
+              word *DataArray (Pointer to array for the data)
+   Result:    None
+*/
+void ReadMemQuick_430Xv2(unsigned long StartAddr, unsigned long Length, word *DataArray)
+{
+  unsigned long i;
+  
+  SetPC_430Xv2(StartAddr);
+  IR_Shift(IR_CNTRL_SIG_16BIT);
+  DR_Shift16(0x0501);
+  IR_Shift(IR_ADDR_CAPTURE);
+  
+  IR_Shift(IR_DATA_QUICK);
+  
+  for (i = 0; i < Length; i++)
+  {
+    SetTCLK();
+    ClrTCLK();
+    *DataArray++   = DR_Shift16(0);  // Read data from memory.
+  }
+  IR_Shift(IR_CNTRL_SIG_CAPTURE);
+}
+
+//----------------------------------------------------------------------------
+/* This function performs a Verification over the given memory range
+   Arguments: word StartAddr (Start address of memory to be verified)
+              word Length (Number of words to be verified)
+              word *DataArray (Pointer to array with the data)
+   Result:    word (STATUS_OK if verification was successful, STATUS_ERROR otherwise)
+*/
+word VerifyMem_430Xv2(unsigned long StartAddr, unsigned long Length, word *DataArray)
+{
+  unsigned long i;
+  word Data;
+  char err_flag = 0;
+  
+  SetPC_430Xv2(StartAddr);
+  IR_Shift(IR_CNTRL_SIG_16BIT);
+  DR_Shift16(0x0501);
+  IR_Shift(IR_ADDR_CAPTURE);
+  
+  IR_Shift(IR_DATA_QUICK);
+
+  for (i = 0; i < Length; i++)
+  {
+    SetTCLK();
+    ClrTCLK();
+    Data = DR_Shift16(0);  // Read data from memory.
+
+    if(Data != DataArray[i])
+    {
+	    printk("VerifyMem failed. Idx; 0x%lx; Expected: 0x%04x; Read; 0x%04x\n", i, DataArray[i], Data);
+      err_flag = 1;
+      //break;
+    }
+  }
+  IR_Shift(IR_CNTRL_SIG_CAPTURE);
+  
+  return((err_flag == 0) ? STATUS_OK : STATUS_ERROR);
+}
+
+/****************************************************************************/
+/*                         END OF SOURCE FILE                               */
+/****************************************************************************/
diff --git a/drivers/misc/a6/jtag_funcs.h b/drivers/misc/a6/jtag_funcs.h
new file mode 100644
index 0000000..a11eaba
--- /dev/null
+++ b/drivers/misc/a6/jtag_funcs.h
@@ -0,0 +1,126 @@
+#ifndef __BYTEWORD__
+#define __BYTEWORD__
+typedef unsigned int  word;
+typedef unsigned char byte;
+#endif
+
+/****************************************************************************/
+/* Define section for constants                                             */
+/****************************************************************************/
+
+// Constants for the JTAG instruction register (IR, requires LSB first).
+// The MSB has been interchanged with LSB due to use of the same shifting
+// function as used for the JTAG data register (DR, requires MSB first).
+
+// Instructions for the JTAG control signal register
+#define IR_CNTRL_SIG_16BIT         0xC8   // 0x13 original values
+#define IR_CNTRL_SIG_CAPTURE       0x28   // 0x14
+#define IR_CNTRL_SIG_RELEASE       0xA8   // 0x15
+// Instructions for the JTAG Fuse
+#define IR_PREPARE_BLOW            0x44   // 0x22
+#define IR_EX_BLOW                 0x24   // 0x24
+// Instructions for the JTAG data register
+#define IR_DATA_16BIT              0x82   // 0x41
+#define IR_DATA_QUICK              0xC2   // 0x43
+// Instructions for the JTAG PSA mode
+#define IR_DATA_PSA                0x22   // 0x44
+#define IR_SHIFT_OUT_PSA           0x62   // 0x46
+// Instructions for the JTAG address register
+#define IR_ADDR_16BIT              0xC1   // 0x83
+#define IR_ADDR_CAPTURE            0x21   // 0x84
+#define IR_DATA_TO_ADDR            0xA1   // 0x85
+// Bypass instruction
+#define IR_BYPASS                  0xFF   // 0xFF
+
+// JTAG identification value for all existing Flash-based MSP430 devices
+#define JTAG_ID                    0x89
+#define JTAG_ID91                  0x91
+// Jtag 17 
+#define DEVICE_HAS_JTAG17          1         
+
+// additional instructions for JTAG_ID91 architectures
+#define IR_COREIP_ID               0xE8   // 0x17
+#define IR_DEVICE_ID               0xE1   // 0x87
+// Instructions for the JTAG mailbox
+#define IR_JMB_EXCHANGE            0x86   // 0x61
+#define IR_TEST_REG                0x54   // 0x2A
+
+// Constants for JTAG mailbox data exchange
+#define OUT1RDY 0x0008
+#define IN0RDY  0x0001
+#define JMB32B  0x0010
+#define OUTREQ  0x0004
+#define INREQ   0x0001
+
+// Constants for data formats, dedicated addresses
+#ifndef __DATAFORMATS__
+#define __DATAFORMATS__
+#define F_BYTE                     8
+#define F_WORD                     16
+#define F_ADDR                     20
+#define F_LONG                     32
+#endif
+#define V_RESET                    0xFFFE
+#define V_BOR                      0x1B08
+
+// Constants for VPP connection at Blow-Fuse
+#define VPP_ON_TDI                 0
+#define VPP_ON_TEST                1
+
+// ReleaseDevice parameters
+#define INIT    0 // ReleaseDevice() status
+#define ERROR   1 // inc verify error count
+#define VERIFY  2 // inc verify pass count
+#define PROGRAM 3 // inc reprogram count
+
+/****************************************************************************/
+/* Function prototypes                                                      */
+/****************************************************************************/
+
+// Low level JTAG functions
+//static word DR_Shift16(word Data);
+//static unsigned long DR_Shift20(unsigned long address);
+//static word IR_Shift(byte Instruction);
+//static void ResetTAP(void);
+//static word ExecutePOR_430Xv2(void);
+//static void SetPC_430Xv2(unsigned long Addr);
+word VerifyPSA_430Xv2(unsigned long StartAddr, unsigned long Length, word *DataArray);
+
+// High level JTAG functions
+word GetDevice_430Xv2(void);
+#define GetDevice GetDevice_430Xv2
+int ReleaseDevice_430Xv2(unsigned long Addr, byte Stat);
+#define ReleaseDevice ReleaseDevice_430Xv2
+void WriteMem_430Xv2(word Format, unsigned long Addr, word Data);
+#define WriteMem WriteMem_430Xv2
+void WriteMemQuick_430Xv2(unsigned long StartAddr, unsigned long Length, word *DataArray);
+#define WriteMemQuick WriteMemQuick_430Xv2
+word WriteAllSections_430Xv2(const unsigned short *data, const unsigned long *address, const unsigned long *length_of_sections, const unsigned long sections);
+#define WriteAllSections WriteAllSections_430Xv2
+word VerifyAllSections_430Xv2(const unsigned short *data, const unsigned long *address, const unsigned long *length_of_sections, const unsigned long sections);
+#define VerifyAllSections VerifyAllSections_430Xv2
+word ReadMem_430Xv2(word Format, unsigned long Addr);
+#define ReadMem ReadMem_430Xv2
+void ReadMemQuick_430Xv2(unsigned long StartAddr, unsigned long Length, word *DataArray);
+#define ReadMemQuick ReadMemQuick_430Xv2
+word VerifyMem_430Xv2(unsigned long StartAddr, unsigned long Length, word *DataArray);
+#define VerifyMem VerifyMem_430Xv2
+
+
+typedef int (*extract_conv_fn)( const unsigned short inp_data,
+	      unsigned char* op_data, unsigned int count);
+
+word TTFExtractSection_430Xv2
+	(const unsigned long sec_addr, const unsigned long sec_len,
+	 unsigned char* sec_databuf, extract_conv_fn ttf_conv,
+	 unsigned long* sec_fmt_len);
+#define TTFExtractSection TTFExtractSection_430Xv2
+word TTFExtractAllSections_430Xv2(void);
+#define TTFExtractAllSections TTFExtractAllSections_430Xv2
+word TTFExtractCacheClear_430Xv2(void);
+#define TTFExtractCacheClear TTFExtractCacheClear_430Xv2
+int TTFImageRead_430Xv2(char *buf, size_t count, loff_t *ppos);
+#define TTFImageRead TTFImageRead_430Xv2
+int GetChecksumData_430Xv2(unsigned short* cksum1, unsigned short* cksum2,
+	unsigned short* cksum_cycles, unsigned short* cksum_errors);
+#define GetChecksumData GetChecksumData_430Xv2
\ No newline at end of file
diff --git a/drivers/misc/a6/low_level_funcs.c b/drivers/misc/a6/low_level_funcs.c
new file mode 100644
index 0000000..7c7a27f
--- /dev/null
+++ b/drivers/misc/a6/low_level_funcs.c
@@ -0,0 +1,237 @@
+#include "a6_host_adapter.h"
+#include "low_level_funcs.h"
+
+// declarations for active implementation mappings used by the sbw code...
+extern uint16_t (*SetSBWTCK)(void);
+extern uint16_t (*ClrSBWTCK)(void);
+extern uint16_t (*SetSBWTDIO)(void);
+extern uint16_t (*ClrSBWTDIO)(void);
+extern uint16_t (*SetInSBWTDIO)(void);
+extern uint16_t (*SetOutSBWTDIO)(void);
+extern uint16_t (*GetSBWTDIO)(void);
+extern uint16_t (*SetSBWAKEUP)(void);
+extern uint16_t (*ClrSBWAKEUP)(void);
+extern void (*delay)(uint32_t delay_us);
+//
+
+byte tdo_bit;               //holds the value of TDO-bit
+byte TCLK_saved = 1;  // holds the last value of TCLK before entering a JTAG sequence
+
+/****************************************************************************/
+/* Function declarations which have to be programmed by the user for use    */
+/* with hosts other than the MSP430F149.                                    */
+/*                                                                          */
+/* The following MSP430F149-specific code can be used as a reference as to  */
+/* how to implement the required JTAG communication on additional hosts.    */
+/****************************************************************************/
+
+//-------------------------------
+//  combinations of sbw-cycles (TMS, TDI, TDO)
+void TMSL_TDIL(void)
+{
+    unsigned long flags = 0;
+   
+    DisableInterrupts(flags);
+    TMSL  TDIL  TDOsbw
+    EnableInterrupts(flags);
+
+}
+//---------------------------------
+void TMSH_TDIL(void)
+{
+    unsigned long flags = 0;
+
+    DisableInterrupts(flags);
+    TMSH  TDIL  TDOsbw
+    EnableInterrupts(flags);
+}
+//------------------------------------
+void TMSL_TDIH(void)
+{
+    unsigned long flags = 0;
+
+    DisableInterrupts(flags);
+    TMSL  TDIH  TDOsbw
+    EnableInterrupts(flags);
+}
+//-------------------------------------
+void TMSH_TDIH(void)
+{
+    unsigned long flags = 0;
+
+    DisableInterrupts(flags);
+    TMSH  TDIH  TDOsbw
+    EnableInterrupts(flags);
+}
+//------------------------------------
+void TMSL_TDIH_TDOrd(void)
+{
+    unsigned long flags = 0;
+
+    DisableInterrupts(flags);
+    TMSL  TDIH  TDO_RD
+    EnableInterrupts(flags);
+}
+//------------------------------------
+void TMSL_TDIL_TDOrd(void)
+{
+    unsigned long flags = 0;
+
+    DisableInterrupts(flags);
+    TMSL  TDIL  TDO_RD
+    EnableInterrupts(flags);
+}
+//------------------------------------
+void TMSH_TDIH_TDOrd(void)
+{
+    unsigned long flags = 0;
+
+    DisableInterrupts(flags);
+    TMSH  TDIH  TDO_RD
+    EnableInterrupts(flags);
+}
+//------------------------------------
+void TMSH_TDIL_TDOrd(void)
+{
+    unsigned long flags = 0;
+
+    DisableInterrupts(flags);
+    TMSH  TDIL  TDO_RD
+    EnableInterrupts(flags);
+}
+//----------------------------------------------
+// enters with TCLK_saved and exits with TCLK = 0
+void ClrTCLK_sbw(void)
+{
+    unsigned long flags = 0;
+
+    DisableInterrupts(flags);
+    if (TCLK_saved)
+    {
+        TMSLDH
+    }
+    else
+    {
+        TMSL
+    }
+
+    ClrSBWTDIO();
+
+    TDIL TDOsbw    //ExitTCLK
+    TCLK_saved = 0;
+    EnableInterrupts(flags);
+}
+
+//----------------------------------------------
+// enters with TCLK_saved and exits with TCLK = 1
+void SetTCLK_sbw(void)
+{
+   unsigned long flags = 0;
+
+   DisableInterrupts(flags);
+   if (TCLK_saved)
+   {
+        TMSLDH
+   }
+   else
+   {
+        TMSL
+   }
+
+   SetSBWTDIO();
+
+   TDIH TDOsbw    //ExitTCLK
+   TCLK_saved = 1;
+   EnableInterrupts(flags);
+}
+
+//----------------------------------------------------------------------------
+/*  Shift a value into TDI (MSB first) and simultaneously shift out a value
+    from TDO (MSB first).
+    Arguments: word Format (number of bits shifted, 8 (F_BYTE), 16 (F_WORD), 
+               20 (F_ADDR) or 32 (F_LONG))
+               unsigned long Data (data to be shifted into TDI)
+    Result:    unsigned long (scanned TDO value)
+*/
+
+unsigned long AllShifts(word Format, unsigned long Data)
+{
+   unsigned long TDOword = 0x00000000;
+   unsigned long MSB = 0x00000000;
+   word i;
+
+   switch(Format)
+   {
+   case F_BYTE: MSB = 0x00000080;
+     break;
+   case F_WORD: MSB = 0x00008000;
+     break;
+   case F_ADDR: MSB = 0x00080000;
+     break;
+   case F_LONG: MSB = 0x80000000;
+     break;
+   default: // this is an unsupported format, function will just return 0
+     return TDOword;
+   }
+   // shift in bits
+   for (i = Format; i > 0; i--)
+   {
+        if (i == 1)                     // last bit requires TMS=1; TDO one bit before TDI
+        {
+          ((Data & MSB) == 0) ? TMSH_TDIL_TDOrd() : TMSH_TDIH_TDOrd();
+        }
+        else
+        {
+          ((Data & MSB) == 0) ? TMSL_TDIL_TDOrd() : TMSL_TDIH_TDOrd();
+        }
+        Data <<= 1;
+        if (tdo_bit)
+            TDOword++;
+        if (i > 1)
+            TDOword <<= 1;               // TDO could be any port pin
+   }
+   TMSH_TDIH();                         // update IR
+   if (TCLK_saved)
+   {
+        TMSL_TDIH();
+   }
+   else
+   {
+        TMSL_TDIL();
+   }
+
+   // de-scramble bits on a 20bit shift
+   if(Format == F_ADDR)
+   {
+     TDOword = ((TDOword << 16) + (TDOword >> 4)) & 0x000FFFFF;
+   }
+   
+   return(TDOword);
+}
+
+void	DrvSignals(void)
+{
+	SetSBWTDIO();
+	ClrSBWTCK();
+}
+
+void	RlsSignals(void)
+{
+	SetSBWTDIO();
+	ClrSBWTCK();
+}
+
+void	InitTarget(void)
+{
+	DrvSignals();
+}
+
+void	ReleaseTarget(void)
+{
+	RlsSignals();
+}
+
+
+/****************************************************************************/
+/*                         END OF SOURCE FILE                               */
+/****************************************************************************/
diff --git a/drivers/misc/a6/low_level_funcs.h b/drivers/misc/a6/low_level_funcs.h
new file mode 100644
index 0000000..cd46642
--- /dev/null
+++ b/drivers/misc/a6/low_level_funcs.h
@@ -0,0 +1,71 @@
+#ifndef _low_level_funcs_h_
+#define _low_level_funcs_h_
+
+/****************************************************************************/
+/* Macros and Pin-to-Signal assignments which have to be programmed         */
+/* by the user. This implementation assumes use of an MSP430F149 as the host*/
+/* controller and the corresponding hardware given in the application       */
+/* report TBD Appendix A.                                                   */
+/*                                                                          */
+/* The following MSP430 example acts as a hint of how to generally          */
+/* implement a micro-controller programmer solution for the MSP430 flash-   */
+/* based devices.                                                           */
+/****************************************************************************/
+
+#ifndef __BYTEWORD__
+#define __BYTEWORD__
+typedef unsigned int   word;
+typedef unsigned char   byte;
+#endif
+
+//----------------------------------------------------------------------------
+// Pin-to-Signal Assignments
+//----------------------------------------------------------------------------
+
+#define   TMSH    {SetSBWTDIO();   nNOPS ClrSBWTCK(); nNOPS                         SetSBWTCK();}// TMS = 1
+#define   TMSL    {ClrSBWTDIO();   nNOPS ClrSBWTCK(); nNOPS                         SetSBWTCK();} // TMS = 0
+#define   TMSLDH  {ClrSBWTDIO();   nNOPS ClrSBWTCK(); nNOPS SetSBWTDIO();           SetSBWTCK();} // TMS = 0, then TCLK(TDI) immediately = 1
+#define   TDIH    {SetSBWTDIO();   nNOPS ClrSBWTCK(); nNOPS                         SetSBWTCK();} // TDI = 1
+#define   TDIL    {ClrSBWTDIO();   nNOPS ClrSBWTCK(); nNOPS                         SetSBWTCK();} // TDI = 0
+#define   TDOsbw  {SetSBWTDIO();SetInSBWTDIO(); nNOPS ClrSBWTCK(); nNOPS SetSBWTCK(); SetOutSBWTDIO();} // TDO cycle without reading TDO
+#define   TDO_RD  {SetSBWTDIO();SetInSBWTDIO(); nNOPS ClrSBWTCK(); nNOPS tdo_bit = GetSBWTDIO(); SetSBWTCK(); SetOutSBWTDIO();} // TDO cycle with TDO read
+
+ void ClrTCLK_sbw(void);
+ void SetTCLK_sbw(void);
+ #define ClrTCLK()       ClrTCLK_sbw()
+ #define SetTCLK()       SetTCLK_sbw() 
+ 
+ #define SetRST()	SetSBWTDIO()
+ #define ClrRST()	ClrSBWTDIO()
+ #define ReleaseRST()   () 
+ #define SetTST()	SetSBWTCK()
+ #define ClrTST()	ClrSBWTCK() 
+ 
+
+/*----------------------------------------------------------------------------
+   Definition of global variables
+*/
+extern byte TCLK_saved;      // holds the last value of TCLK before entering a JTAG sequence
+
+
+/*----------------------------------------------------------------------------
+   Low Level function prototypes
+*/
+
+void	TMSL_TDIL(void);
+void	TMSH_TDIL(void);
+void	TMSL_TDIH(void);
+void	TMSH_TDIH(void);
+void	TMSL_TDIH_TDOrd(void);
+void	TMSL_TDIL_TDOrd(void);
+void	TMSH_TDIH_TDOrd(void);
+void	TMSH_TDIL_TDOrd(void);
+
+unsigned long AllShifts(word Format, unsigned long Data);
+void	DrvSignals(void);
+void	RlsSignals(void);
+void	InitTarget(void);
+void	ReleaseTarget(void);
+
+
+#endif
diff --git a/include/linux/a6.h b/include/linux/a6.h
new file mode 100644
index 0000000..99361ed
--- /dev/null
+++ b/include/linux/a6.h
@@ -0,0 +1,76 @@
+/*
+ * linux/include/linux/a6.h
+ *
+ * Driver for the A6 TP.
+ *
+ * Copyright (C) 2008 Palm, Inc.
+ * Author: Raj Mojumder <raj.mojumder@palm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License.
+ */
+
+#ifndef _A6_H
+#define _A6_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+#include <mach/msm_hsusb.h>
+
+#define  A6_DEVICE_0   "a6_0"
+#define  A6_DEVICE_1   "a6_1"
+#define  A6_DRIVER     "a6"
+
+#define  A6_DEVICE     A6_DEVICE_0
+
+#define MAX8903B_CONNECTED_PS_AC	(1U << 1)
+#define MAX8903B_CONNECTED_PS_USB	(1U << 2)
+#define MAX8903B_CONNECTED_PS_DOCK	(1U << 3)
+#define MAX8903B_DOCK_DRAW_MA		1400
+
+/* IOCTLs */
+#define A6_IOCTL_SET_FW_DATA		_IOW('c', 0x01, int)
+#define A6_IOCTL_VERIFY_FW_DATA		_IOW('c', 0x02, int)
+
+/* Touch panel platform data structure */
+struct a6_platform_data {
+	char*	dev_name;   // device name
+	int	pwr_gpio;
+	int	sbw_tck_gpio;
+	int 	sbw_tdio_gpio;
+	int	sbw_wkup_gpio;
+	void*	sbw_ops;
+	void*	wake_ops;
+
+	void*	sbw_init_gpio_config;
+	int	sbw_init_gpio_config_size;
+	void*	sbw_deinit_gpio_config;
+	int	sbw_deinit_gpio_config_size;
+
+	int	(*sbw_init)(struct a6_platform_data*);
+	int	(*sbw_deinit)(struct a6_platform_data*);
+
+	int	pwr_gpio_wakeup_cap;  /* set if pwr_gpio is wakeup capable */
+	int	power_supply_connected;	/* Set to 1 if this is the a6 connected to battery, etc */
+};
+
+struct a6_wake_ops {
+	void*	data;
+	
+	// external periodic sleep/wake interface
+	int	(*enable_periodic_wake)(void *);
+	int	(*disable_periodic_wake)(void *);
+
+	// internal sleep/wake interface
+	int	(*internal_wake_enable_state)(void*);
+	int	(*internal_wake_period)(void*);
+
+	// force sleep/wake interface (needed to force-wake A6 when
+	// internal/external periodic sleep/wake in effect...
+	int	(*force_wake)(void *);
+	int	(*force_sleep)(void *);
+};
+
+void a6_charger_event (enum chg_type otg_chg_type);
+
+#endif // _A6_H
diff --git a/include/linux/a6_sbw_interface.h b/include/linux/a6_sbw_interface.h
new file mode 100644
index 0000000..2927684
--- /dev/null
+++ b/include/linux/a6_sbw_interface.h
@@ -0,0 +1,51 @@
+/*
+ * linux/include/linux/a6_sbw_interface.h
+ *
+ * Public interface for the SBW protocol layer. Declares callbacks used by the core protocol.
+ * Interfaces include:
+ * - per-A6-device interface: every A6 device must define its own implementation of this interface.
+ * - per-target interfaces : each board-type must define its own implementation of these interfaces.
+ * - per-host system: operating system specific implementations must be defined.
+ *
+ * Copyright (C) 2008 Palm, Inc.
+ * Author: Raj Mojumder <raj.mojumder@palm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License.
+ */
+
+#ifndef _A6_SBW_INTERFACE_H_
+#define _A6_SBW_INTERFACE_H_
+
+struct a6_sbw_interface {
+        // per-A6-device interface (separate instantiation for every a6 device)
+	struct {
+		uint16_t (*SetSBWTCK)(void);
+		uint16_t (*ClrSBWTCK)(void);
+		uint16_t (*SetSBWTDIO)(void);
+		uint16_t (*ClrSBWTDIO)(void);
+		uint16_t (*SetInSBWTDIO)(void);
+		uint16_t (*SetOutSBWTDIO)(void);
+		uint16_t (*GetSBWTDIO)(void);
+		uint16_t (*SetSBWAKEUP)(void);
+		uint16_t (*ClrSBWAKEUP)(void);
+	} a6_per_device_interface;
+
+        // per-target interface (separate instantiation for every board)
+	struct {
+		void (*delay)(uint32_t delay_us);
+	} a6_per_target_interface;
+};
+
+
+// per-host system: (operating system specific)
+#ifdef __KERNEL__
+#define a6_disable_interrupts(flags) {flags=flags;local_irq_save(flags);}
+#define a6_enable_interrupts(flags)  {local_irq_restore(flags);}
+#else
+#define a6_disable_interrupts(flags) {i_need_definition();}
+#define a6_enable_interrupts(flags)  {i_need_definition();}
+#endif
+
+
+#endif // _A6_SBW_INTERFACE_H_