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_