add a6, hres_counter drivers from HPalm
This commit is contained in:
@@ -1030,6 +1030,7 @@ CONFIG_PMIC8058_XOADC=y
|
||||
# CONFIG_PMIC8058_BATTALARM is not set
|
||||
CONFIG_TZCOM=y
|
||||
# CONFIG_QFP_FUSE is not set
|
||||
CONFIG_HRES_COUNTER=y
|
||||
# CONFIG_C2PORT is not set
|
||||
|
||||
#
|
||||
@@ -1048,6 +1049,11 @@ CONFIG_TZCOM=y
|
||||
# CONFIG_TI_ST is not set
|
||||
# CONFIG_SENSORS_LIS3_SPI is not set
|
||||
# CONFIG_SENSORS_LIS3_I2C is not set
|
||||
CONFIG_A6_SUPPORT=y
|
||||
CONFIG_A6=y
|
||||
# CONFIG_A6_I2C_SINGLE_BYTE is not set
|
||||
# CONFIG_A6_I2C_SINGLE_BYTE_WRITE is not set
|
||||
CONFIG_A6_BATTERY=y
|
||||
CONFIG_HAVE_IDE=y
|
||||
# CONFIG_IDE is not set
|
||||
|
||||
|
||||
@@ -651,6 +651,11 @@ config QFP_FUSE
|
||||
to the fuse block. Currently this is supported only
|
||||
on FSM targets.
|
||||
|
||||
config HRES_COUNTER
|
||||
tristate "High resolution counter"
|
||||
---help---
|
||||
Select Y if you want to enable High resolution counter.
|
||||
|
||||
source "drivers/misc/c2port/Kconfig"
|
||||
source "drivers/misc/eeprom/Kconfig"
|
||||
source "drivers/misc/cb710/Kconfig"
|
||||
@@ -658,5 +663,6 @@ source "drivers/misc/iwmc3200top/Kconfig"
|
||||
source "drivers/misc/ti-st/Kconfig"
|
||||
source "drivers/misc/lis3lv02d/Kconfig"
|
||||
source "drivers/misc/carma/Kconfig"
|
||||
source "drivers/misc/a6/Kconfig"
|
||||
|
||||
endif # MISC_DEVICES
|
||||
|
||||
@@ -68,3 +68,5 @@ obj-$(CONFIG_QFP_FUSE) += qfp_fuse.o
|
||||
obj-$(CONFIG_KERNEL_LOG) += klog.o
|
||||
obj-$(CONFIG_HSUART) += hsuart.o
|
||||
obj-$(CONFIG_USER_PINS) += user-pins.o
|
||||
obj-$(CONFIG_HRES_COUNTER) += hres_counter.o
|
||||
obj-$(CONFIG_A6) += a6/
|
||||
|
||||
40
drivers/misc/a6/Kconfig
Normal file
40
drivers/misc/a6/Kconfig
Normal file
@@ -0,0 +1,40 @@
|
||||
menuconfig A6_SUPPORT
|
||||
tristate "Palm A6 support"
|
||||
depends on I2C
|
||||
default y
|
||||
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 y
|
||||
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_BATTERY
|
||||
bool "Palm A6 battery driver"
|
||||
depends on A6
|
||||
default y
|
||||
help
|
||||
Say Y to include support for the Palm A6 battery.
|
||||
|
||||
endif # POWER_SUPPLY
|
||||
5
drivers/misc/a6/Makefile
Normal file
5
drivers/misc/a6/Makefile
Normal file
@@ -0,0 +1,5 @@
|
||||
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
|
||||
obj-$(CONFIG_A6_BATTERY) += a6_battery.o
|
||||
4268
drivers/misc/a6/a6.c
Normal file
4268
drivers/misc/a6/a6.c
Normal file
File diff suppressed because it is too large
Load Diff
832
drivers/misc/a6/a6_battery.c
Normal file
832
drivers/misc/a6/a6_battery.c
Normal file
@@ -0,0 +1,832 @@
|
||||
#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/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/debugfs.h>
|
||||
#include "high_level_funcs.h"
|
||||
#include <linux/firmware.h>
|
||||
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/max8903_charger.h>
|
||||
#ifdef CONFIG_HAS_EARLYSUSPEND
|
||||
#include <linux/earlysuspend.h>
|
||||
#include <linux/suspend.h>
|
||||
#endif
|
||||
|
||||
/* 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_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))
|
||||
|
||||
#if defined(current)
|
||||
#undef current
|
||||
#endif
|
||||
struct a6_battery_info {
|
||||
struct i2c_client *client;
|
||||
struct power_supply battery;
|
||||
struct delayed_work poll_work;
|
||||
struct workqueue_struct *poll_workqueue;
|
||||
#ifdef CONFIG_HAS_EARLYSUSPEND
|
||||
suspend_state_t pm_state;
|
||||
struct early_suspend early_suspend;
|
||||
#endif
|
||||
int present;
|
||||
int authentic;
|
||||
int percentage;
|
||||
int voltage;
|
||||
int current;
|
||||
int temperature;
|
||||
unsigned int capacity; /* in uAh */
|
||||
};
|
||||
static struct a6_battery_info battery_info;
|
||||
typedef enum {
|
||||
A6_ERR_OK = 0,
|
||||
A6_ERROR = -1,
|
||||
A6_ERR_I2C = -2,
|
||||
A6_ERR_TIMEOUT = -3,
|
||||
A6_ERR_NOT_SUPPORTED = -4,
|
||||
A6_ERR_BUSY = -5,
|
||||
} a6_err_code;
|
||||
|
||||
typedef enum {
|
||||
A6_CMD_REG_READ ,
|
||||
A6_CMD_REG_WRITE,
|
||||
} a6_reg_cmd_code;
|
||||
|
||||
enum {
|
||||
DEVICE_BUSY_BIT = 0,
|
||||
IS_OPENED,
|
||||
IS_INITIALIZED_BIT,
|
||||
BOOTLOAD_ACTIVE_BIT,
|
||||
FORCE_WAKE_ACTIVE_BIT,
|
||||
|
||||
// capabilities
|
||||
CAP_PERIODIC_WAKE,
|
||||
#ifdef A6_PQ
|
||||
STARTING_AID_TASK,
|
||||
KILLING_AID_TASK,
|
||||
IS_QUIESCED,
|
||||
#endif
|
||||
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;
|
||||
uint32_t cached_rsense_val: 16;
|
||||
struct workqueue_struct* ka6d_workqueue;
|
||||
struct workqueue_struct* ka6d_fw_workqueue;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
struct a6_reg_cmd
|
||||
{
|
||||
a6_reg_cmd_code cmd;
|
||||
uint16_t reg;
|
||||
uint8_t data;
|
||||
};
|
||||
|
||||
#define CHALLENGE_SIZE 8
|
||||
#define MAC_SIZE 20
|
||||
|
||||
#define R_SENSE r_sense
|
||||
|
||||
#define SIGN_EXTEND16(x) ((x)-(((x)&0x8000)?65536:0))
|
||||
#define CURRENT_VALUE(x) ((SIGN_EXTEND16(x)*3125)/2/R_SENSE) // in uA
|
||||
#define VOLTAGE_VALUE(x) (4880*((x)>>5)) // in micro volt
|
||||
#define COULOMB_VALUE(x) ((6250*(x))/R_SENSE)
|
||||
#define REG_COULOMB_VALUE(x) ((R_SENSE*(x))/6250)
|
||||
#define CAPACITY_VALUE(x) (1600*(x)) // in micro Ahr
|
||||
#define CAPACITY_PERCENT(x) (392*x) // in thousands of %
|
||||
|
||||
#define BATTERY_DEFAULT_CHALLENGE \
|
||||
{ 0x74, 0xca, 0x85, 0x99, 0x19, 0xde, 0xd1, 0xb3 }
|
||||
#define BATTERY_DEFAULT_RESPONSE \
|
||||
{ 0x87, 0xed, 0x20, 0x89, 0xad, 0x68, 0xa2, 0xcd, 0x6f, 0x93, \
|
||||
0x13, 0x03, 0x07, 0x5a, 0x29, 0x85, 0xdc, 0x2e, 0xe9, 0x50 }
|
||||
|
||||
static int battery_authentic = 0;
|
||||
static int r_sense = 20;
|
||||
|
||||
#define A6_BATTERY_POLLING_DELAY_MS (10)
|
||||
#define A6_BATTERY_POLLING_TIMEOUT_CNT (20)
|
||||
void a6_delay(uint32_t delay_ms){
|
||||
msleep(delay_ms);
|
||||
}
|
||||
int32_t a6_i2c_read_reg(struct i2c_client* client, const uint16_t* ids, uint32_t num_ids, uint8_t* out);
|
||||
int32_t a6_i2c_write_reg(struct i2c_client* client, const uint16_t* ids, uint32_t num_ids, const uint8_t* in);
|
||||
|
||||
|
||||
a6_err_code a6_send_reg_commands(struct a6_reg_cmd *cmdlist, int count)
|
||||
{
|
||||
a6_err_code res = A6_ERR_OK;
|
||||
int i;
|
||||
struct a6_reg_cmd *p_cmd = cmdlist;
|
||||
struct a6_device_state* state = i2c_get_clientdata(battery_info.client);
|
||||
|
||||
/* return if a6 un-initialized */
|
||||
if (!test_bit(IS_INITIALIZED_BIT, state->flags)) {
|
||||
pr_debug("%s: a6 un-initialized: exiting.\n", __func__);
|
||||
res = A6_ERROR;
|
||||
return res;
|
||||
}
|
||||
/* loop through to send all commands */
|
||||
for (i = 0; i < count; i++) {
|
||||
int i2c_res;
|
||||
|
||||
if (p_cmd->cmd == A6_CMD_REG_READ ) {
|
||||
|
||||
i2c_res = a6_i2c_read_reg(battery_info.client, &(p_cmd->reg), 1, &(p_cmd->data));
|
||||
|
||||
if (i2c_res < 0) {
|
||||
res = A6_ERR_I2C;
|
||||
}
|
||||
} else if (p_cmd->cmd == A6_CMD_REG_WRITE) {
|
||||
i2c_res = a6_i2c_write_reg(battery_info.client, &(p_cmd->reg), 1, &(p_cmd->data));
|
||||
|
||||
if (i2c_res < 0) {
|
||||
res = A6_ERR_I2C;
|
||||
}
|
||||
} else {
|
||||
printk("A6 a6_send_reg_commands : Unknown Cmd %x\n", p_cmd->cmd);
|
||||
res = A6_ERROR;
|
||||
}
|
||||
|
||||
//Workaround for A6 I2C read issue
|
||||
//a6_delay(1);
|
||||
|
||||
p_cmd++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
static a6_err_code a6_battery_wait_busy(uint8_t clear_flags, uint8_t set_flags, uint8_t *p_status) {
|
||||
int i;
|
||||
a6_err_code res = A6_ERR_OK;
|
||||
uint8_t status = 0;
|
||||
static struct a6_reg_cmd cmdlist[] = {
|
||||
{
|
||||
.cmd = A6_CMD_REG_READ,
|
||||
.reg = TS2_I2C_BAT_COMMAND_STATUS,
|
||||
},
|
||||
};
|
||||
|
||||
for ( i = A6_BATTERY_POLLING_TIMEOUT_CNT; i > 0; i-- ) {
|
||||
res = a6_send_reg_commands(cmdlist, 1);
|
||||
|
||||
if (res != A6_ERR_OK) {
|
||||
goto a6_battery_wait_busy_exit;
|
||||
} else {
|
||||
status = cmdlist[0].data;
|
||||
}
|
||||
|
||||
if ( ( !set_flags || (status & set_flags) != 0) && ( !clear_flags || ((~status) & clear_flags) != 0 )) {
|
||||
break;
|
||||
}
|
||||
|
||||
a6_delay(A6_BATTERY_POLLING_DELAY_MS);
|
||||
};
|
||||
|
||||
if ( i <= 0 ) {
|
||||
res = A6_ERROR;
|
||||
} else if ( p_status ) {
|
||||
*p_status = status;
|
||||
}
|
||||
a6_battery_wait_busy_exit:
|
||||
return res;
|
||||
}
|
||||
//:TODO: Add busy handling - probably return the last value
|
||||
//:TODO: Add A6 capabilites handling
|
||||
|
||||
int battery_get_percentage(void)
|
||||
{
|
||||
ushort percentage = 0;
|
||||
|
||||
static struct a6_reg_cmd cmdlist[] = {
|
||||
{
|
||||
.cmd = A6_CMD_REG_READ,
|
||||
.reg = TS2_I2C_BAT_RARC,
|
||||
},
|
||||
};
|
||||
|
||||
if ( A6_ERR_OK != a6_battery_wait_busy(0, TS2_I2C_BAT_STATUS_REGS_VALID, NULL)){
|
||||
goto battery_get_percentage_exit;
|
||||
}
|
||||
|
||||
if (A6_ERR_OK != a6_send_reg_commands(cmdlist, 1)) {
|
||||
goto battery_get_percentage_exit;
|
||||
} else {
|
||||
percentage = cmdlist[0].data;
|
||||
}
|
||||
|
||||
battery_get_percentage_exit:
|
||||
return percentage;
|
||||
}
|
||||
|
||||
int battery_get_voltage(void)
|
||||
{
|
||||
int res = 0;
|
||||
ushort voltage = 0;
|
||||
|
||||
static struct a6_reg_cmd cmdlist[] = {
|
||||
{
|
||||
.cmd = A6_CMD_REG_READ,
|
||||
.reg = TS2_I2C_BAT_VOLT_MSB,
|
||||
},
|
||||
{
|
||||
.cmd = A6_CMD_REG_READ,
|
||||
.reg = TS2_I2C_BAT_VOLT_LSB,
|
||||
},
|
||||
};
|
||||
|
||||
if ( A6_ERR_OK != a6_battery_wait_busy(0, TS2_I2C_BAT_STATUS_REGS_VALID, NULL)){
|
||||
goto battery_get_voltage_exit;
|
||||
}
|
||||
|
||||
if (A6_ERR_OK != a6_send_reg_commands(cmdlist, 2)) {
|
||||
goto battery_get_voltage_exit;
|
||||
} else {
|
||||
voltage = (cmdlist[0].data << 8) | cmdlist[1].data;
|
||||
res = VOLTAGE_VALUE(voltage);
|
||||
}
|
||||
|
||||
battery_get_voltage_exit :
|
||||
return res;
|
||||
}
|
||||
|
||||
static int battery_get_r_sense(void)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
static struct a6_reg_cmd cmdlist[] = {
|
||||
{
|
||||
.cmd = A6_CMD_REG_READ,
|
||||
.reg = TS2_I2C_BAT_RSNSP,
|
||||
},
|
||||
};
|
||||
|
||||
if ( A6_ERR_OK != a6_battery_wait_busy(0, TS2_I2C_BAT_STATUS_REGS_VALID, NULL)){
|
||||
goto battery_get_r_sense_exit;
|
||||
}
|
||||
|
||||
if (A6_ERR_OK != a6_send_reg_commands(cmdlist, 1)) {
|
||||
goto battery_get_r_sense_exit;
|
||||
} else {
|
||||
res = 1000 / cmdlist[0].data;
|
||||
}
|
||||
|
||||
battery_get_r_sense_exit :
|
||||
return res;
|
||||
}
|
||||
|
||||
int battery_get_current(void)
|
||||
{
|
||||
int res = 0;
|
||||
ushort cur;
|
||||
|
||||
static struct a6_reg_cmd cmdlist[] = {
|
||||
{
|
||||
.cmd = A6_CMD_REG_READ,
|
||||
.reg = TS2_I2C_BAT_CUR_MSB,
|
||||
},
|
||||
{
|
||||
.cmd = A6_CMD_REG_READ,
|
||||
.reg = TS2_I2C_BAT_CUR_LSB,
|
||||
},
|
||||
};
|
||||
|
||||
if ( A6_ERR_OK != a6_battery_wait_busy(0, TS2_I2C_BAT_STATUS_REGS_VALID, NULL)){
|
||||
goto battery_get_current_exit;
|
||||
}
|
||||
|
||||
if (A6_ERR_OK != a6_send_reg_commands(cmdlist, 2)) {
|
||||
goto battery_get_current_exit;
|
||||
} else {
|
||||
cur = (cmdlist[0].data << 8) | cmdlist[1].data;
|
||||
res = CURRENT_VALUE(cur);
|
||||
}
|
||||
|
||||
battery_get_current_exit :
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the battery temperature.
|
||||
*
|
||||
* Temperature value is a 11-bit two's-complement integer with a 1/8
|
||||
* degrees Centigrade resolution:
|
||||
* MSB: 7 6 5 4 3 2 1 0 LSB: 7 6 5 4 3 2 1 0
|
||||
* s i i i i i i i f f f _ _ _ _ _
|
||||
*
|
||||
* s: sign
|
||||
* i: integer part
|
||||
* f: fraction part
|
||||
* _: N/A
|
||||
*
|
||||
* We discard the fraction and only return the signed integer value.
|
||||
*/
|
||||
int battery_get_temperature(void)
|
||||
{
|
||||
int res = 0;
|
||||
static struct a6_reg_cmd cmdlist[] = {
|
||||
{
|
||||
.cmd = A6_CMD_REG_READ,
|
||||
.reg = TS2_I2C_BAT_TEMP_MSB,
|
||||
},
|
||||
{
|
||||
.cmd = A6_CMD_REG_READ,
|
||||
.reg = TS2_I2C_BAT_TEMP_LSB,
|
||||
},
|
||||
};
|
||||
|
||||
if ( A6_ERR_OK != a6_battery_wait_busy(0, TS2_I2C_BAT_STATUS_REGS_VALID, NULL)){
|
||||
goto battery_get_temperature_exit;
|
||||
}
|
||||
|
||||
if (A6_ERR_OK != a6_send_reg_commands(cmdlist, 2)) {
|
||||
goto battery_get_temperature_exit;
|
||||
} else {
|
||||
/* Only return the signed integer value */
|
||||
res = (int8_t)cmdlist[0].data;
|
||||
}
|
||||
|
||||
battery_get_temperature_exit:
|
||||
return res;
|
||||
}
|
||||
|
||||
unsigned int battery_get_capacity(void)
|
||||
{
|
||||
unsigned int res = 0;
|
||||
|
||||
static struct a6_reg_cmd cmdlist[] = {
|
||||
{
|
||||
.cmd = A6_CMD_REG_READ,
|
||||
.reg = TS2_I2C_BAT_RAAC_MSB,
|
||||
},
|
||||
{
|
||||
.cmd = A6_CMD_REG_READ,
|
||||
.reg = TS2_I2C_BAT_RAAC_LSB,
|
||||
},
|
||||
};
|
||||
|
||||
if ( A6_ERR_OK != a6_battery_wait_busy(0, TS2_I2C_BAT_STATUS_REGS_VALID, NULL)){
|
||||
goto battery_get_capacity_exit;
|
||||
}
|
||||
|
||||
if (A6_ERR_OK != a6_send_reg_commands(cmdlist, 2)) {
|
||||
goto battery_get_capacity_exit;
|
||||
} else {
|
||||
uint32_t capacity = (cmdlist[0].data << 8) | cmdlist[1].data;
|
||||
res = CAPACITY_VALUE(capacity);
|
||||
}
|
||||
|
||||
battery_get_capacity_exit:
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int battery_is_present(void)
|
||||
{
|
||||
int rc = 0;
|
||||
if (battery_get_voltage() == 0 && battery_get_current() == 0)
|
||||
{
|
||||
// We do not have a battery, or best case the battery we have is unusable
|
||||
rc = -1;
|
||||
}
|
||||
return (rc < 0) ? 0 : 1;
|
||||
}
|
||||
|
||||
int battery_reauthenticate(void)
|
||||
{
|
||||
int res = 0;
|
||||
int i;
|
||||
uint8_t batt_cmd_status = 0;
|
||||
static int cmd_lists_initiated = 0;
|
||||
static const uint8_t dc[] = BATTERY_DEFAULT_CHALLENGE;
|
||||
static const uint8_t dr[] = BATTERY_DEFAULT_RESPONSE;
|
||||
static struct a6_reg_cmd send_challenge_cmdlist[CHALLENGE_SIZE];
|
||||
static struct a6_reg_cmd get_response_cmdlist[MAC_SIZE];
|
||||
static struct a6_reg_cmd authenticate_cmdlist[] = {
|
||||
{
|
||||
.cmd = A6_CMD_REG_WRITE,
|
||||
.reg = TS2_I2C_BAT_COMMAND_STATUS,
|
||||
.data = TS2_I2C_BAT_COMMAND_AUTH,
|
||||
},
|
||||
};
|
||||
|
||||
printk("Starting battery authentication...\n");
|
||||
|
||||
if ( !cmd_lists_initiated ) {
|
||||
|
||||
//Init the challenge a6 cmd list
|
||||
for ( i = 0; i < CHALLENGE_SIZE; i ++ ) {
|
||||
send_challenge_cmdlist[i].cmd = A6_CMD_REG_WRITE;
|
||||
send_challenge_cmdlist[i].reg = TS2_I2C_BAT_CHALLENGE(i);
|
||||
send_challenge_cmdlist[i].data = dc[i];
|
||||
}
|
||||
|
||||
//Init the response a6 cmd list
|
||||
for ( i = 0; i < MAC_SIZE; i ++ ) {
|
||||
get_response_cmdlist[i].cmd = A6_CMD_REG_READ;
|
||||
get_response_cmdlist[i].reg = TS2_I2C_BAT_RESPONSE(i);
|
||||
}
|
||||
|
||||
cmd_lists_initiated = 1;
|
||||
}
|
||||
|
||||
if ( A6_ERR_OK != a6_battery_wait_busy(TS2_I2C_BAT_STATUS_BUSY, 0, NULL)){
|
||||
goto battery_reauthenticate_exit;
|
||||
}
|
||||
|
||||
if (A6_ERR_OK != a6_send_reg_commands(send_challenge_cmdlist, CHALLENGE_SIZE)) {
|
||||
goto battery_reauthenticate_exit;
|
||||
}
|
||||
|
||||
if (A6_ERR_OK != a6_send_reg_commands(authenticate_cmdlist, 1)) {
|
||||
goto battery_reauthenticate_exit;
|
||||
}
|
||||
|
||||
a6_delay(10);
|
||||
|
||||
if ( A6_ERR_OK != a6_battery_wait_busy(TS2_I2C_BAT_STATUS_BUSY, 0, &batt_cmd_status)){
|
||||
goto battery_reauthenticate_exit;
|
||||
}
|
||||
|
||||
if ( batt_cmd_status & TS2_I2C_BAT_STATUS_AUTH_FAIL ) {
|
||||
goto battery_reauthenticate_exit;
|
||||
}
|
||||
|
||||
if (A6_ERR_OK != a6_send_reg_commands(get_response_cmdlist, MAC_SIZE)) {
|
||||
goto battery_reauthenticate_exit;
|
||||
}
|
||||
|
||||
res = 1;
|
||||
//Validate response
|
||||
for ( i = 0; i < MAC_SIZE; i++) {
|
||||
if ( dr[i] != get_response_cmdlist[i].data ) {
|
||||
res = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == res) {
|
||||
printk("*** Battery Authentication Failure: ***\n");
|
||||
printk("*** [Expected and actual response dump] ***\n");
|
||||
for ( i = 0; i < MAC_SIZE; i++) {
|
||||
printk("exp-resp: 0x%02x act-resp: 0x%02x\n", dr[i], get_response_cmdlist[i].data);
|
||||
}
|
||||
} else {
|
||||
printk("Battery authentication successful.\n");
|
||||
}
|
||||
|
||||
battery_reauthenticate_exit :
|
||||
|
||||
battery_authentic = res;
|
||||
return res;
|
||||
}
|
||||
|
||||
int battery_set_authentic(bool authentic)
|
||||
{
|
||||
battery_authentic = authentic;
|
||||
return battery_authentic;
|
||||
}
|
||||
|
||||
static a6_err_code a6_battery_send_command(uint8_t command)
|
||||
{
|
||||
a6_err_code res = A6_ERR_OK;
|
||||
|
||||
res = a6_battery_wait_busy(TS2_I2C_BAT_STATUS_BUSY, 0, NULL);
|
||||
|
||||
if ( A6_ERR_OK == res ){
|
||||
struct a6_reg_cmd cmdlist[] = {
|
||||
{
|
||||
.cmd = A6_CMD_REG_WRITE,
|
||||
.reg = TS2_I2C_BAT_COMMAND_STATUS,
|
||||
.data = command,
|
||||
}
|
||||
};
|
||||
|
||||
res = a6_send_reg_commands(cmdlist, 1);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static a6_err_code battery_refresh_regs(void)
|
||||
{
|
||||
a6_err_code res = A6_ERR_OK;
|
||||
|
||||
res = a6_battery_send_command(TS2_I2C_BAT_COMMAND_REFRESH);
|
||||
|
||||
if ( res == A6_ERR_OK ) {
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void battery_startup(void)
|
||||
{
|
||||
if ( a6_battery_send_command(TS2_I2C_BAT_COMMAND_WAKE) == A6_ERR_OK ) {
|
||||
//Wait for 200 ms for battery to be woken up
|
||||
msleep(200);
|
||||
|
||||
//Refresh all the registers
|
||||
if ( battery_refresh_regs() == A6_ERR_OK ) {
|
||||
int r_sense_tmp;
|
||||
|
||||
r_sense_tmp = battery_get_r_sense();
|
||||
|
||||
if ( r_sense_tmp ) {
|
||||
r_sense = r_sense_tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void battery_sleep(void)
|
||||
{
|
||||
a6_battery_send_command(TS2_I2C_BAT_COMMAND_OFF);
|
||||
}
|
||||
|
||||
int battery_read(struct a6_battery_info *batt_info)
|
||||
{
|
||||
battery_refresh_regs();
|
||||
|
||||
batt_info->present = battery_is_present();
|
||||
if (!batt_info->present) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
batt_info->authentic = battery_authentic; /* read once on init */
|
||||
batt_info->percentage = battery_get_percentage();
|
||||
batt_info->voltage = battery_get_voltage();
|
||||
batt_info->current = battery_get_current();
|
||||
batt_info->temperature = battery_get_temperature();
|
||||
batt_info->capacity = battery_get_capacity();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum power_supply_property a6_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_TEMP,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_TECHNOLOGY,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
};
|
||||
static void a6_battery_ext_power_changed(struct power_supply *psy)
|
||||
{
|
||||
//battery_read(&battery_info);
|
||||
//pr_debug("%s(%d) : battery_info.percentage = %d, battery_info.voltage = %d, battery_info.current = %d, battery_info.temperature = %d\n",
|
||||
// __func__, __LINE__, battery_info.percentage, battery_info.voltage, battery_info.current, battery_info.temperature);
|
||||
//power_supply_changed(psy);
|
||||
|
||||
pr_debug("%s(%d): battery_info.pm_state = %d", __func__, __LINE__, battery_info.pm_state);
|
||||
if (battery_info.pm_state < PM_SUSPEND_MEM) {
|
||||
if (delayed_work_pending(&(battery_info.poll_work))) {
|
||||
cancel_delayed_work_sync(&(battery_info.poll_work));
|
||||
}
|
||||
queue_delayed_work(battery_info.poll_workqueue , &(battery_info.poll_work), msecs_to_jiffies(10));
|
||||
}
|
||||
}
|
||||
static int a6_battery_get_property(struct power_supply *psy,
|
||||
enum power_supply_property prop,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
|
||||
int status, ret = 0;
|
||||
bool charger_is_charging = false, charger_is_connecting = false;
|
||||
|
||||
switch (prop) {
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
charger_is_charging = max8903_charger_is_charging();
|
||||
charger_is_connecting = max8903_charger_is_connecting();
|
||||
|
||||
//status
|
||||
if (!battery_info.present) {
|
||||
status = POWER_SUPPLY_STATUS_UNKNOWN;
|
||||
} else {
|
||||
if ((battery_info.percentage== 100) && charger_is_connecting && !charger_is_charging)
|
||||
status = POWER_SUPPLY_STATUS_FULL;
|
||||
else if (charger_is_connecting && !charger_is_charging)
|
||||
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
else if (!charger_is_connecting)
|
||||
status = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
else
|
||||
status = POWER_SUPPLY_STATUS_CHARGING;
|
||||
}
|
||||
pr_debug("A6_BATTEERY : (%s) (%d) : prop = %d, status = %d, percentage = %d, charger_is_connecting = %d, charger_is_charging = %d\n",
|
||||
__func__, __LINE__, prop, status, battery_info.percentage, charger_is_connecting, charger_is_charging);
|
||||
val->intval = status;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
//capacity
|
||||
pr_debug("A6_BATTEERY : (%s) (%d) : prop = %d, capacity = %d\n", __func__, __LINE__, prop, battery_info.percentage);
|
||||
val->intval = battery_info.percentage;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
pr_debug("A6_BATTEERY : (%s) (%d) : prop = %d, voltage_mV = %d\n", __func__, __LINE__, prop, battery_info.voltage);
|
||||
val->intval = battery_info.voltage;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_TEMP:
|
||||
//temp
|
||||
pr_debug("A6_BATTEERY : (%s) (%d) : prop = %d, temp_c = %d\n", __func__, __LINE__, prop, battery_info.temperature);
|
||||
val->intval = battery_info.temperature * 10;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_HEALTH:
|
||||
val->intval = POWER_SUPPLY_HEALTH_GOOD;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_TECHNOLOGY:
|
||||
val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
val->intval = battery_info.present;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
void a6_battery_poll_work_handler(struct work_struct *work)
|
||||
{
|
||||
battery_read(&battery_info);
|
||||
pr_debug("%s(%d) : battery_info.percentage = %d, battery_info.voltage = %d, battery_info.current = %d, battery_info.temperature = %d\n",
|
||||
__func__, __LINE__, battery_info.percentage, battery_info.voltage, battery_info.current, battery_info.temperature);
|
||||
power_supply_changed(&(battery_info.battery));
|
||||
queue_delayed_work(battery_info.poll_workqueue , &(battery_info.poll_work), msecs_to_jiffies(10 * 1000));
|
||||
}
|
||||
#ifdef CONFIG_HAS_EARLYSUSPEND
|
||||
void batt_early_suspend(struct early_suspend *h)
|
||||
{
|
||||
pr_debug("%s(%d): enter\n", __func__, __LINE__);
|
||||
|
||||
if (delayed_work_pending(&(battery_info.poll_work))) {
|
||||
cancel_delayed_work_sync(&(battery_info.poll_work));
|
||||
}
|
||||
battery_info.pm_state = PM_SUSPEND_MEM;
|
||||
pr_debug("%s: exit\n", __func__);
|
||||
}
|
||||
|
||||
void batt_late_resume(struct early_suspend *h)
|
||||
{
|
||||
pr_debug("%s(%d): enter\n", __func__, __LINE__);
|
||||
if (delayed_work_pending(&(battery_info.poll_work))) {
|
||||
cancel_delayed_work_sync(&(battery_info.poll_work));
|
||||
}
|
||||
queue_delayed_work(battery_info.poll_workqueue , &(battery_info.poll_work), msecs_to_jiffies(1));
|
||||
battery_info.pm_state = PM_SUSPEND_ON;
|
||||
pr_debug("%s(%d): exit\n", __func__, __LINE__);
|
||||
}
|
||||
|
||||
#endif
|
||||
void a6_battery_init(struct i2c_client *a6_i2c_client)
|
||||
{
|
||||
battery_info.battery.name = "battery";
|
||||
battery_info.battery.type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
battery_info.battery.properties = a6_battery_props;
|
||||
battery_info.battery.num_properties = ARRAY_SIZE(a6_battery_props);
|
||||
battery_info.battery.get_property = a6_battery_get_property;
|
||||
battery_info.battery.external_power_changed = a6_battery_ext_power_changed;
|
||||
battery_info.client = a6_i2c_client;
|
||||
#ifdef CONFIG_HAS_EARLYSUSPEND
|
||||
battery_info.pm_state = PM_SUSPEND_ON;
|
||||
battery_info.early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;
|
||||
battery_info.early_suspend.suspend =batt_early_suspend;
|
||||
battery_info.early_suspend.resume = batt_late_resume;
|
||||
register_early_suspend(&battery_info.early_suspend);
|
||||
#endif
|
||||
battery_startup();
|
||||
battery_reauthenticate();
|
||||
battery_read(&battery_info);
|
||||
printk("battery_info.percentage = %d, battery_info.voltage = %d, battery_info.current = %d, battery_info.temperature = %d\n",
|
||||
battery_info.percentage, battery_info.voltage, battery_info.current, battery_info.temperature);
|
||||
battery_info.poll_workqueue = create_workqueue("a6_battery_poll_workqueue");
|
||||
INIT_DELAYED_WORK(&(battery_info.poll_work), a6_battery_poll_work_handler);
|
||||
power_supply_register(&(a6_i2c_client->dev), &(battery_info.battery));
|
||||
queue_delayed_work(battery_info.poll_workqueue , &(battery_info.poll_work), msecs_to_jiffies(10 * 1000));
|
||||
}
|
||||
|
||||
59
drivers/misc/a6/a6_host_adapter.h
Normal file
59
drivers/misc/a6/a6_host_adapter.h
Normal file
@@ -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
|
||||
527
drivers/misc/a6/high_level_funcs.c
Normal file
527
drivers/misc/a6/high_level_funcs.c
Normal file
@@ -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);
|
||||
}
|
||||
13
drivers/misc/a6/high_level_funcs.h
Normal file
13
drivers/misc/a6/high_level_funcs.h
Normal file
@@ -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
|
||||
1070
drivers/misc/a6/jtag_funcs.c
Normal file
1070
drivers/misc/a6/jtag_funcs.c
Normal file
File diff suppressed because it is too large
Load Diff
126
drivers/misc/a6/jtag_funcs.h
Normal file
126
drivers/misc/a6/jtag_funcs.h
Normal file
@@ -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
|
||||
241
drivers/misc/a6/low_level_funcs.c
Normal file
241
drivers/misc/a6/low_level_funcs.c
Normal file
@@ -0,0 +1,241 @@
|
||||
#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;
|
||||
//unsigned long flags = 0;
|
||||
|
||||
//DisableInterrupts(flags);
|
||||
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
|
||||
//EnableInterrupts(flags);
|
||||
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;
|
||||
}
|
||||
//EnableInterrupts(flags);
|
||||
|
||||
return(TDOword);
|
||||
}
|
||||
|
||||
void DrvSignals(void)
|
||||
{
|
||||
SetSBWTDIO();
|
||||
ClrSBWTCK();
|
||||
}
|
||||
|
||||
void RlsSignals(void)
|
||||
{
|
||||
SetSBWTDIO();
|
||||
ClrSBWTCK();
|
||||
}
|
||||
|
||||
void InitTarget(void)
|
||||
{
|
||||
DrvSignals();
|
||||
}
|
||||
|
||||
void ReleaseTarget(void)
|
||||
{
|
||||
RlsSignals();
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************/
|
||||
/* END OF SOURCE FILE */
|
||||
/****************************************************************************/
|
||||
71
drivers/misc/a6/low_level_funcs.h
Normal file
71
drivers/misc/a6/low_level_funcs.h
Normal file
@@ -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
|
||||
575
drivers/misc/hres_counter.c
Normal file
575
drivers/misc/hres_counter.c
Normal file
@@ -0,0 +1,575 @@
|
||||
/*
|
||||
* linux/drivers/misc/hres_counter.c
|
||||
*
|
||||
* Copyright (C) 2008 Palm, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/hres_counter.h>
|
||||
#include <linux/sysrq.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#define NUM_CHANNELS 32
|
||||
#define NUM_EVENTS 512
|
||||
|
||||
struct count_ch {
|
||||
u32 count;
|
||||
u32 total;
|
||||
u32 tstamp;
|
||||
u32 active;
|
||||
};
|
||||
|
||||
struct hres_event {
|
||||
u32 tstamp;
|
||||
char* type;
|
||||
u32 arg1;
|
||||
u32 arg2;
|
||||
};
|
||||
|
||||
struct dev_ctxt {
|
||||
void *timer;
|
||||
int suspended;
|
||||
struct platform_device *pdev;
|
||||
struct count_ch ch[NUM_CHANNELS];
|
||||
|
||||
int (*init_hres_timer)(void **);
|
||||
int (*release_hres_timer)(void *);
|
||||
int (*suspend_hres_timer)(void *);
|
||||
int (*resume_hres_timer)(void *);
|
||||
|
||||
u32 (*read_hres_timer)(void *);
|
||||
u32 (*convert_hres_timer)(u32);
|
||||
};
|
||||
|
||||
static struct dev_ctxt *gdev = NULL;
|
||||
|
||||
static int evlog_on = 1;
|
||||
static int evlog_cnt = 0;
|
||||
static int evlog_num = 0;
|
||||
static struct hres_event evlog[NUM_EVENTS];
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static inline u32
|
||||
hres_read_tick ( void ) {
|
||||
if( unlikely(gdev == NULL))
|
||||
return 0;
|
||||
|
||||
return gdev->read_hres_timer( gdev->timer );
|
||||
}
|
||||
|
||||
/*
|
||||
* Current value of high res counter
|
||||
*/
|
||||
u32
|
||||
hres_get_counter ( void )
|
||||
{
|
||||
return hres_read_tick();
|
||||
}
|
||||
EXPORT_SYMBOL(hres_get_counter);
|
||||
|
||||
/*
|
||||
* Return the delta in useconds from start to end
|
||||
*/
|
||||
u32
|
||||
hres_get_delta_usec ( u32 start, u32 end )
|
||||
{
|
||||
return gdev->convert_hres_timer(end - start);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset channel
|
||||
*/
|
||||
void
|
||||
hres_ch_reset ( uint ch )
|
||||
{
|
||||
if( unlikely(gdev == NULL))
|
||||
return;
|
||||
|
||||
if( unlikely(ch >= NUM_CHANNELS))
|
||||
return;
|
||||
|
||||
memset ( &gdev->ch[ch], 0, sizeof(struct count_ch));
|
||||
}
|
||||
EXPORT_SYMBOL(hres_ch_reset);
|
||||
|
||||
/*
|
||||
* Increment Event count
|
||||
*/
|
||||
void
|
||||
hres_event_cnt ( uint ch )
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if( unlikely(gdev == NULL))
|
||||
return;
|
||||
|
||||
if( unlikely(ch >= NUM_CHANNELS))
|
||||
return;
|
||||
|
||||
local_irq_save(flags);
|
||||
gdev->ch[ch].count++;
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
EXPORT_SYMBOL(hres_event_cnt);
|
||||
|
||||
/*
|
||||
* Mark Event Start
|
||||
*/
|
||||
void
|
||||
hres_event_start ( uint ch )
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if( unlikely(gdev == NULL))
|
||||
return;
|
||||
|
||||
if( unlikely(ch >= NUM_CHANNELS))
|
||||
return;
|
||||
|
||||
local_irq_save(flags);
|
||||
gdev->ch[ch].tstamp = hres_read_tick();
|
||||
gdev->ch[ch].active = 1;
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
EXPORT_SYMBOL(hres_event_start);
|
||||
|
||||
/*
|
||||
* Mark Event End and count
|
||||
*/
|
||||
u32
|
||||
hres_event_end ( uint ch )
|
||||
{
|
||||
u32 ts = 0;
|
||||
unsigned long flags;
|
||||
|
||||
if( unlikely(gdev == NULL))
|
||||
return 0;
|
||||
|
||||
if( unlikely(ch >= NUM_CHANNELS))
|
||||
return 0;
|
||||
|
||||
if(!gdev->ch[ch].active)
|
||||
return 0;
|
||||
|
||||
local_irq_save(flags);
|
||||
ts = gdev->read_hres_timer( gdev->timer ) - gdev->ch[ch].tstamp;
|
||||
gdev->ch[ch].total += ts;
|
||||
gdev->ch[ch].count++;
|
||||
gdev->ch[ch].active = 0;
|
||||
local_irq_restore(flags);
|
||||
return ts;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL(hres_event_end);
|
||||
|
||||
|
||||
/*
|
||||
* Mark Event End and count
|
||||
*/
|
||||
void
|
||||
hres_event ( char *type, u32 arg1, u32 arg2 )
|
||||
{
|
||||
unsigned long flags;
|
||||
struct hres_event *evt;
|
||||
|
||||
if( unlikely(gdev == NULL))
|
||||
return;
|
||||
|
||||
if( !evlog_on )
|
||||
return;
|
||||
|
||||
local_irq_save(flags);
|
||||
evt = evlog + evlog_cnt;
|
||||
evlog_cnt++;
|
||||
if( evlog_cnt > evlog_num )
|
||||
evlog_num = evlog_cnt;
|
||||
if( evlog_cnt == NUM_EVENTS )
|
||||
evlog_cnt = 0;
|
||||
evt->tstamp = hres_read_tick();
|
||||
evt->type = type;
|
||||
evt->arg1 = arg1;
|
||||
evt->arg2 = arg2;
|
||||
local_irq_restore(flags);
|
||||
return;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL(hres_event);
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void
|
||||
hres_evlog_reset(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
evlog_cnt = 0;
|
||||
evlog_num = 0;
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
EXPORT_SYMBOL(hres_evlog_reset);
|
||||
|
||||
|
||||
/*
|
||||
* Enable logging
|
||||
*/
|
||||
int
|
||||
hres_evlog_enable(void)
|
||||
{
|
||||
int rc = evlog_on;
|
||||
evlog_on = 1;
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(hres_evlog_enable);
|
||||
|
||||
/*
|
||||
* Disable logging
|
||||
*/
|
||||
int
|
||||
hres_evlog_disable(void)
|
||||
{
|
||||
int rc = evlog_on;
|
||||
evlog_on = 0;
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(hres_evlog_disable);
|
||||
|
||||
/*
|
||||
* Print log
|
||||
*/
|
||||
void
|
||||
hres_evlog_print ( void )
|
||||
{
|
||||
int i;
|
||||
u32 last_tstamp = 0;
|
||||
struct hres_event *evt = evlog;
|
||||
|
||||
printk ("evlog: beg\n");
|
||||
for (i = 0; i < evlog_num; i++, evt++ ) {
|
||||
printk ( "%010d (d%10d): %12d (0x%08x) %12d (0x%08x) %s\n",
|
||||
gdev->convert_hres_timer(evt->tstamp),
|
||||
gdev->convert_hres_timer(evt->tstamp - last_tstamp),
|
||||
evt->arg1, evt->arg1,
|
||||
evt->arg2, evt->arg2,
|
||||
evt->type );
|
||||
last_tstamp = evt->tstamp;
|
||||
}
|
||||
printk ("evlog: end\n");
|
||||
}
|
||||
EXPORT_SYMBOL(hres_evlog_print);
|
||||
|
||||
|
||||
/*
|
||||
* Sysfs
|
||||
*/
|
||||
static ssize_t
|
||||
counter_show ( struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf )
|
||||
{
|
||||
return snprintf( buf, PAGE_SIZE, "%d\n", hres_read_tick());
|
||||
}
|
||||
|
||||
static DEVICE_ATTR( counter, S_IRUGO | S_IWUSR, counter_show, NULL );
|
||||
|
||||
|
||||
|
||||
static ssize_t
|
||||
evlog_show ( struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf )
|
||||
{
|
||||
hres_evlog_print ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
evlog_store( struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count )
|
||||
{
|
||||
hres_evlog_reset();
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
static DEVICE_ATTR( evlog, S_IRUGO | S_IWUSR, evlog_show, evlog_store );
|
||||
|
||||
|
||||
|
||||
static ssize_t
|
||||
channels_show ( struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf )
|
||||
{
|
||||
int ch;
|
||||
ssize_t len = 0;
|
||||
|
||||
for ( ch = 0; ch < NUM_CHANNELS; ch++ ) {
|
||||
len += snprintf( buf + len, PAGE_SIZE - len,
|
||||
"%02d: %010u %4d\n", ch,
|
||||
gdev->convert_hres_timer(gdev->ch[ch].total),
|
||||
gdev->ch[ch].count
|
||||
);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
channels_store( struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count )
|
||||
{
|
||||
int ch;
|
||||
|
||||
ch = simple_strtol ( buf, NULL, 10 );
|
||||
if( ch == -1 ) {
|
||||
for ( ch = 0; ch < NUM_CHANNELS; ch++ )
|
||||
hres_ch_reset ( ch );
|
||||
}
|
||||
else {
|
||||
hres_ch_reset ((uint) ch );
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR( channels, S_IRUGO | S_IWUSR, channels_show, channels_store);
|
||||
|
||||
|
||||
static void
|
||||
hres_sysrq_show_evlog(int key, struct tty_struct *tty)
|
||||
{
|
||||
hres_evlog_print();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static int
|
||||
hres_panic(struct notifier_block *this, unsigned long event, void *ptr)
|
||||
{
|
||||
hres_evlog_print();
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block panic_block = {
|
||||
.notifier_call = hres_panic,
|
||||
};
|
||||
|
||||
/*
|
||||
* Sys req related
|
||||
*/
|
||||
static struct sysrq_key_op sysrq_show_log_op = {
|
||||
.handler = hres_sysrq_show_evlog,
|
||||
.help_msg = "show-evLog",
|
||||
.action_msg = "Show HiRes EvLog",
|
||||
};
|
||||
|
||||
static void
|
||||
hres_sysrq_reset_evlog(int key, struct tty_struct *tty)
|
||||
{
|
||||
hres_evlog_reset();
|
||||
}
|
||||
|
||||
|
||||
static struct sysrq_key_op sysrq_reset_log_op = {
|
||||
.handler = hres_sysrq_reset_evlog,
|
||||
.help_msg = "reset-evlog(c)",
|
||||
.action_msg = "Reset Hires EvLog",
|
||||
};
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static int __devexit
|
||||
hres_counter_remove ( struct platform_device *pdev )
|
||||
{
|
||||
struct dev_ctxt *ctxt = gdev;
|
||||
|
||||
gdev = NULL;
|
||||
|
||||
device_remove_file ( &pdev->dev, &dev_attr_counter );
|
||||
device_remove_file ( &pdev->dev, &dev_attr_channels );
|
||||
device_remove_file ( &pdev->dev, &dev_attr_evlog );
|
||||
|
||||
gdev->release_hres_timer ( ctxt->timer );
|
||||
|
||||
platform_set_drvdata( pdev, NULL );
|
||||
|
||||
kfree ( ctxt );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static int __init
|
||||
hres_counter_probe ( struct platform_device *pdev )
|
||||
{
|
||||
int rc;
|
||||
struct dev_ctxt *ctxt;
|
||||
struct hres_counter_platform_data *pdata = pdev->dev.platform_data;
|
||||
|
||||
ctxt = kzalloc ( sizeof(struct dev_ctxt), GFP_KERNEL );
|
||||
if( ctxt == NULL )
|
||||
goto ret_nodev;
|
||||
|
||||
ctxt->pdev = pdev;
|
||||
ctxt->init_hres_timer = pdata->init_hres_timer;
|
||||
ctxt->release_hres_timer = pdata->release_hres_timer;
|
||||
ctxt->read_hres_timer = pdata->read_hres_timer;
|
||||
ctxt->convert_hres_timer = pdata->convert_hres_timer;
|
||||
|
||||
ctxt->suspend_hres_timer = pdata->suspend_hres_timer;
|
||||
ctxt->resume_hres_timer = pdata->resume_hres_timer;
|
||||
|
||||
rc = ctxt->init_hres_timer(&ctxt->timer);
|
||||
if( rc )
|
||||
goto free_ctxt;
|
||||
|
||||
platform_set_drvdata ( pdev, ctxt );
|
||||
|
||||
rc = device_create_file ( &pdev->dev, &dev_attr_counter );
|
||||
if( rc )
|
||||
goto free_timer;
|
||||
|
||||
rc = device_create_file ( &pdev->dev, &dev_attr_channels );
|
||||
if( rc )
|
||||
goto free_sysfs_counter;
|
||||
|
||||
rc = device_create_file ( &pdev->dev, &dev_attr_evlog );
|
||||
if( rc )
|
||||
goto free_sysfs_channels;
|
||||
|
||||
gdev = ctxt;
|
||||
|
||||
printk( KERN_INFO "Initialize %s device\n",
|
||||
pdev->name);
|
||||
#if 0
|
||||
#ifndef CONFIG_HRES_COUNTER_TIMER32K
|
||||
printk( KERN_INFO "Initialize %s device (%dMHz)\n",
|
||||
pdev->name, clk_rate );
|
||||
#else
|
||||
printk( KERN_INFO "Initialize %s device (32KHz)\n", pdev->name);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// register sys request key
|
||||
register_sysrq_key( 'l', &sysrq_show_log_op );
|
||||
register_sysrq_key( 'c', &sysrq_reset_log_op );
|
||||
|
||||
// register panic callback
|
||||
atomic_notifier_chain_register(&panic_notifier_list, &panic_block);
|
||||
|
||||
return 0;
|
||||
|
||||
free_sysfs_channels:
|
||||
device_remove_file ( &pdev->dev, &dev_attr_channels );
|
||||
free_sysfs_counter:
|
||||
device_remove_file ( &pdev->dev, &dev_attr_counter );
|
||||
free_timer:
|
||||
ctxt->release_hres_timer ( ctxt->timer );
|
||||
free_ctxt:
|
||||
kfree ( ctxt );
|
||||
ret_nodev:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_HRES_COUNTER_TIMER32K
|
||||
#undef CONFIG_PM
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int
|
||||
hres_counter_suspend( struct platform_device *dev, pm_message_t state )
|
||||
{
|
||||
struct dev_ctxt *ctxt = gdev;
|
||||
|
||||
if( ctxt->suspended )
|
||||
return 0;
|
||||
|
||||
ctxt->resume_hres_timer(gdev->timer);
|
||||
|
||||
ctxt->suspended = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
hres_counter_resume ( struct platform_device *dev )
|
||||
{
|
||||
struct dev_ctxt *ctxt = gdev;
|
||||
|
||||
if(!ctxt->suspended )
|
||||
return 0;
|
||||
|
||||
ctxt->suspend_hres_timer(gdev->timer);
|
||||
|
||||
ctxt->suspended = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define hres_counter_suspend NULL
|
||||
#define hres_counter_resume NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
static struct platform_driver hres_counter_driver = {
|
||||
.driver = {
|
||||
.name = "hres_counter",
|
||||
},
|
||||
.probe = hres_counter_probe,
|
||||
.remove = __devexit_p(hres_counter_remove),
|
||||
.suspend = hres_counter_suspend,
|
||||
.resume = hres_counter_resume,
|
||||
};
|
||||
|
||||
static int __init
|
||||
hres_counter_init(void)
|
||||
{
|
||||
platform_driver_register ( &hres_counter_driver );
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void __exit
|
||||
hres_counter_exit(void)
|
||||
{
|
||||
platform_driver_unregister ( &hres_counter_driver );
|
||||
return;
|
||||
}
|
||||
|
||||
module_init(hres_counter_init);
|
||||
module_exit(hres_counter_exit);
|
||||
|
||||
MODULE_DESCRIPTION("OMAP High resolution counter driver" );
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
66
include/linux/a6.h
Normal file
66
include/linux/a6.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
|
||||
#define A6_DEVICE_0 "a6_0"
|
||||
#define A6_DEVICE_1 "a6_1"
|
||||
#define A6_DRIVER "a6"
|
||||
|
||||
#define A6_DEVICE A6_DEVICE_0
|
||||
|
||||
|
||||
/* 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*);
|
||||
};
|
||||
|
||||
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 *);
|
||||
};
|
||||
#endif // _A6_H
|
||||
45
include/linux/a6_sbw_interface.h
Normal file
45
include/linux/a6_sbw_interface.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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)
|
||||
#define a6_disable_interrupts(flags) {flags=flags;local_irq_save(flags);}
|
||||
#define a6_enable_interrupts(flags) {local_irq_restore(flags);}
|
||||
|
||||
#endif // _A6_SBW_INTERFACE_H_
|
||||
64
include/linux/hres_counter.h
Normal file
64
include/linux/hres_counter.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#ifndef __HRES_COUNTER_INCLUDED__
|
||||
#define __HRES_COUNTER_INCLUDED__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct hres_counter_platform_data {
|
||||
/* Initialize/obtain the timer resource */
|
||||
int (*init_hres_timer)(void **);
|
||||
|
||||
/* Release the timer resource*/
|
||||
int (*release_hres_timer)(void *);
|
||||
|
||||
/* PM functions */
|
||||
int (*suspend_hres_timer)(void *);
|
||||
int (*resume_hres_timer)(void *);
|
||||
|
||||
/* Read native timer count value */
|
||||
u32 (*read_hres_timer)(void *);
|
||||
|
||||
/* Convert native timer value to desired human */
|
||||
/* readable format (usec or msec, etc) */
|
||||
u32 (*convert_hres_timer)(u32);
|
||||
};
|
||||
|
||||
#define LOG_MMC_TIMEOUT_TIMING_MEASUREMENTS 1
|
||||
#if !defined(CONFIG_HRES_COUNTER) && LOG_MMC_TIMEOUT_TIMING_MEASUREMENTS
|
||||
#error "MMC timeout measurements can only be done with hires counters"
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_HRES_COUNTER
|
||||
|
||||
extern u32 hres_get_counter ( void );
|
||||
extern u32 hres_get_delta_usec ( u32 start, u32 end );
|
||||
extern void hres_ch_reset ( uint ch );
|
||||
extern void hres_event_cnt ( uint ch );
|
||||
extern void hres_event_start ( uint ch );
|
||||
extern u32 hres_event_end ( uint ch );
|
||||
extern void hres_event ( char *type, u32 arg1, u32 arg2 );
|
||||
extern int hres_evlog_enable ( void );
|
||||
extern int hres_evlog_disable ( void );
|
||||
extern void hres_evlog_print ( void );
|
||||
extern void hres_evlog_reset ( void );
|
||||
|
||||
#else
|
||||
|
||||
#define hres_get_counter(args...)
|
||||
#define hres_get_delta_usec(args...)
|
||||
#define hres_ch_reset(args...)
|
||||
#define hres_event_cnt(args...)
|
||||
#define hres_event_start(args...)
|
||||
#define hres_event_end(args...)
|
||||
#define hres_event(args...)
|
||||
#define hres_evlog_enable(args...)
|
||||
#define hres_evlog_disable(args...)
|
||||
#define hres_evlog_print(args...)
|
||||
#define hres_evlog_reset(args...)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif // __HRES_COUNTER_INCLUDED__
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user