android_kernel_modules_leno.../drivers/hwmon/psh_ia_common.c

1028 lines
25 KiB
C

/*
* psh_ia_common.c - Intel PSH IA side driver common lib
*
* Copyright 2012-2013 Intel Corporation All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License v2 as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <linux/ctype.h>
#include "psh_ia_common.h"
#include <asm/intel-mid.h>
#include <linux/hrtimer.h>
#define TOLOWER(x) ((x) | 0x20)
/* translate string to unsigned long value */
unsigned long _strtoul(const char *str, char **endp, unsigned int base)
{
unsigned long value = 0;
if (base == 0) {
if (str[0] == '0') {
if (TOLOWER(str[1]) == 'x' && isxdigit(str[2]))
base = 16;
else
base = 8;
} else
base = 10;
}
if (base == 16 && str[0] == '0' && TOLOWER(str[1]) == 'x')
str += 2;
while (isxdigit(*str)) {
unsigned int temp_value;
if ('0' <= *str && *str <= '9')
temp_value = *str - '0';
else
temp_value = TOLOWER(*str) - 'a' + 10;
if (temp_value >= base)
break;
value = value * base + temp_value;
str++;
}
if (endp)
*endp = (char *)str;
return value;
}
long trans_strtol(const char *str, char **endp, unsigned int base)
{
if (*str == '-')
return -_strtoul(str + 1, endp, base);
return _strtoul(str, endp, base);
}
void ia_lbuf_read_init(struct loop_buffer *lbuf,
u8 *buf, u16 size, update_finished_f uf)
{
lbuf->addr = buf;
lbuf->length = size;
lbuf->off_head = lbuf->off_tail = 0;
lbuf->update_finished = uf;
lbuf->in_reading = 0;
}
void ia_lbuf_read_reset(struct loop_buffer *lbuf)
{
if (lbuf) {
lbuf->off_head = lbuf->off_tail = 0;
lbuf->in_reading = 0;
}
}
int ia_lbuf_read_next(struct psh_ia_priv *psh_ia_data,
struct loop_buffer *lbuf,
u8 **buf, u16 *size)
{
struct frame_head *fhead =
(struct frame_head *)(lbuf->addr + lbuf->off_head);
*buf = NULL;
*size = 0;
if (lbuf->in_reading) {
lbuf->in_reading = 0;
/* go over previous frame has been read */
lbuf->off_head += frame_size(fhead->length);
lbuf->off_tail = lbuf->off_head;
fhead = (struct frame_head *)(lbuf->addr + lbuf->off_head);
}
if (fhead->sign == LBUF_DISCARD_SIGN) {
fhead = (struct frame_head *)lbuf->addr;
lbuf->off_head = lbuf->off_tail = 0;
}
if (fhead->sign == LBUF_CELL_SIGN) {
if (fhead->length > LBUF_MAX_CELL_SIZE)
goto f_out;
*buf = lbuf->addr + lbuf->off_head + sizeof(*fhead);
*size = fhead->length;
lbuf->in_reading = 1;
}
f_out:
if (lbuf->update_finished &&
(!lbuf->in_reading && lbuf->off_head)) {
/* no more data frame, inform FW to update its HEAD */
lbuf->update_finished(psh_ia_data, lbuf->off_head);
}
return !lbuf->in_reading;
}
void ia_circ_reset_off(struct circ_buf *circ)
{
circ->head = 0;
circ->tail = 0;
}
void ia_circ_put_data(struct circ_buf *circ, const char *buf, u32 size)
{
int tail_size, cnt;
if (CIRC_SPACE(circ->head, circ->tail, CIRC_SIZE) < size)
return;
tail_size = CIRC_SPACE_TO_END(circ->head, circ->tail, CIRC_SIZE);
cnt = size;
if (cnt > tail_size)
cnt = tail_size;
memcpy(circ->buf + circ->head, buf, cnt);
cnt = size - cnt;
if (cnt)
memcpy(circ->buf, buf + tail_size, cnt);
circ->head += size;
circ->head &= (CIRC_SIZE - 1);
}
void ia_circ_dbg_put_data(struct psh_ia_priv *psh_ia_data,
const char *buf, u32 size)
{
int temp_count;
struct circ_buf *circ;
if (size > CIRC_SIZE -1)
return;
temp_count = 0;
circ = &psh_ia_data->circ_dbg;
if (CIRC_SPACE(circ->head, circ->tail, CIRC_SIZE) < size) {
mutex_lock(&psh_ia_data->circ_dbg_mutex);
circ->tail = circ->head + size + 1;
circ->tail &= (CIRC_SIZE - 1);
while (circ->buf[circ->tail++] != '\n') {
circ->tail &= (CIRC_SIZE - 1);
temp_count++;
if (temp_count > (CIRC_SIZE -1))
break;
}
mutex_unlock(&psh_ia_data->circ_dbg_mutex);
}
ia_circ_put_data(circ, buf, size);
}
int ia_circ_get_data(struct circ_buf *circ, char *buf, u32 size)
{
int avail, avail_tail, cnt;
avail = CIRC_CNT(circ->head, circ->tail, CIRC_SIZE);
if (!avail)
return 0;
avail_tail = CIRC_CNT_TO_END(circ->head, circ->tail, CIRC_SIZE);
if (avail_tail) {
cnt = size;
if (cnt > avail_tail)
cnt = avail_tail;
memcpy(buf, circ->buf + circ->tail, cnt);
size -= cnt;
avail -= cnt;
circ->tail += cnt;
if (!avail || !size)
return cnt;
}
cnt = size;
if (cnt > avail)
cnt = avail;
memcpy(buf + avail_tail, circ->buf, cnt);
circ->tail += cnt;
circ->tail &= (CIRC_SIZE - 1);
return avail_tail + cnt;
}
int ia_circ_dbg_get_data(struct psh_ia_priv *psh_ia_data, char *buf, u32 size)
{
int cnt;
struct circ_buf *circ = &psh_ia_data->circ_dbg;
mutex_lock(&psh_ia_data->circ_dbg_mutex);
cnt = ia_circ_get_data(circ, buf, size);
mutex_unlock(&psh_ia_data->circ_dbg_mutex);
return cnt;
}
static int ia_sync_timestamp(struct psh_ia_priv *psh_ia_data, u8 check_interval)
{
static u64 tick_old = 0;
struct ia_cmd cmd_timestamp = { 0 };
struct cmd_ia_notify_param *param = (struct cmd_ia_notify_param *)cmd_timestamp.param;
u8 *linux_base_ns = param->extra;
timestamp_t base_ns = 0;
int ret;
if (check_interval) {
if (!tick_old) {
tick_old = jiffies;
return 0;
} else {
if (jiffies - tick_old < (120 * HZ))
return 0;
}
}
cmd_timestamp.tran_id = 0;
cmd_timestamp.cmd_id = CMD_IA_NOTIFY;
cmd_timestamp.sensor_id = 0;
param->id = IA_NOTIFY_TIMESTAMP_SYNC;
base_ns = ktime_to_ns(ktime_get_boottime());
tick_old = jiffies;
*(timestamp_t *)linux_base_ns = base_ns;
ret = process_send_cmd(psh_ia_data, PSH2IA_CHANNEL0,
&cmd_timestamp, sizeof(struct ia_cmd) - CMD_PARAM_MAX_SIZE
+ sizeof(struct cmd_ia_notify_param) + sizeof(base_ns));
return ret;
}
int ia_send_cmd(struct psh_ia_priv *psh_ia_data,
struct ia_cmd *cmd, int len)
{
int ret = 0;
static struct resp_cmd_ack cmd_ack;
ia_sync_timestamp(psh_ia_data, 0);
mutex_lock(&psh_ia_data->cmd_mutex);
if (cmd->cmd_id == CMD_RESET) {
cmd->tran_id = 0;
ia_circ_reset_off(&psh_ia_data->circ);
}
cmd_ack.cmd_id = cmd->cmd_id;
psh_ia_data->cmd_ack = &cmd_ack;
psh_ia_data->cmd_in_progress = cmd->cmd_id;
ret = process_send_cmd(psh_ia_data, PSH2IA_CHANNEL0,
cmd, len);
psh_ia_data->cmd_in_progress = CMD_INVALID;
if (ret) {
psh_err("send cmd (id = %d) failed, ret=%d\n",
cmd->cmd_id, ret);
goto f_out;
}
if (cmd->cmd_id == CMD_FW_UPDATE)
goto f_out;
ack_wait:
if (!wait_for_completion_timeout(&psh_ia_data->cmd_comp,
5 * HZ)) {
psh_err("no CMD_ACK for %d back, timeout!\n",
cmd_ack.cmd_id);
ret = -ETIMEDOUT;
} else if (cmd_ack.ret) {
if (cmd_ack.ret == E_CMD_ASYNC)
goto ack_wait;
psh_err("CMD %d return error %d!\n", cmd_ack.cmd_id,
cmd_ack.ret);
ret = -EREMOTEIO;
}
f_out:
psh_ia_data->cmd_ack = NULL;
mutex_unlock(&psh_ia_data->cmd_mutex);
return ret;
}
ssize_t ia_start_control(struct device *dev,
struct device_attribute *attr,
const char *str, size_t count)
{
struct psh_ia_priv *psh_ia_data =
(struct psh_ia_priv *)dev_get_drvdata(dev);
struct ia_cmd cmd_user = { 0 };
u8 *ptr = (u8 *)&cmd_user;
char *s;
long val;
int token = 0;
int ret;
while (*str && (token < sizeof(cmd_user))) {
val = trans_strtol(str, &s, 0);
if (str == s) {
str++;
continue;
}
str = s;
*ptr++ = (u8)val;
token++;
}
if (cmd_user.cmd_id == CMD_SETUP_DDR) {
ret = do_setup_ddr(dev);
if (ret) {
psh_err("do_setup_ddr failed\n");
return ret;
} else {
return count;
}
}
ret = ia_send_cmd(psh_ia_data, &cmd_user, token);
if (ret)
return ret;
else
return count;
}
ssize_t ia_read_data_size(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct psh_ia_priv *psh_ia_data =
(struct psh_ia_priv *)dev_get_drvdata(dev);
int avail = CIRC_CNT(psh_ia_data->circ.head,
psh_ia_data->circ.tail, CIRC_SIZE);
return snprintf(buf, PAGE_SIZE, "%d\n", avail);
}
ssize_t ia_read_data(struct file *file, struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct psh_ia_priv *psh_ia_data =
(struct psh_ia_priv *)dev_get_drvdata(dev);
return ia_circ_get_data(&psh_ia_data->circ, buf, count);
}
ssize_t ia_read_debug_data(struct file *file, struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct psh_ia_priv *psh_ia_data =
(struct psh_ia_priv *)dev_get_drvdata(dev);
return ia_circ_dbg_get_data(psh_ia_data, buf, count);
}
ssize_t ia_set_dbg_mask(struct device *dev,
struct device_attribute *attr,
const char *str, size_t count)
{
struct ia_cmd cmd;
struct cmd_debug_param *param = (struct cmd_debug_param *)cmd.param;
struct psh_ia_priv *psh_ia_data =
(struct psh_ia_priv *)dev_get_drvdata(dev);
int token = 0;
char *s;
long val;
while (*str) {
val = trans_strtol(str, &s, 0);
if (str == s) {
str++;
continue;
}
switch (token) {
case 0:
param->mask_out = val;
break;
case 1:
param->mask_level = val;
break;
default:
break;
}
str = s;
if (++token == 2)
break;
}
if (token == 2) {
int ret;
cmd.cmd_id = CMD_DEBUG;
cmd.sensor_id = 0;
param->sub_cmd = SCMD_DEBUG_SET_MASK;
ret = ia_send_cmd(psh_ia_data, &cmd, 10);
if (ret)
return ret;
else
return count;
} else {
psh_err("wrong input for \"<mask_out> <mask_level>\"\n");
return -EPERM;
}
}
ssize_t ia_get_dbg_mask(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ia_cmd cmd;
struct cmd_debug_param *param = (struct cmd_debug_param *)cmd.param;
struct psh_ia_priv *psh_ia_data =
(struct psh_ia_priv *)dev_get_drvdata(dev);
int ret;
cmd.cmd_id = CMD_DEBUG;
cmd.sensor_id = 0;
param->sub_cmd = SCMD_DEBUG_GET_MASK;
ret = ia_send_cmd(psh_ia_data, &cmd, 8);
if (ret)
return snprintf(buf, PAGE_SIZE, "%s failed!\n", __func__);
return snprintf(buf, PAGE_SIZE, "mask_out:0x%x mask_level:0x%x\n",
psh_ia_data->dbg_mask.mask_out,
psh_ia_data->dbg_mask.mask_level);
}
static inline int is_port_sensor(enum sensor_type type)
{
return ((type > PORT_SENSOR_BASE) && (type < PORT_SENSOR_MAX_NUM));
}
static void ia_handle_snr_info(struct psh_ia_priv *psh_ia_data,
const struct snr_info *sinfo)
{
char buf[STR_BUFF_SIZE];
ssize_t str_size;
int i;
static int snr_info_start;
static int sensor_map_setup;
if (!snr_info_start) {
snr_info_start++;
str_size = snprintf(buf, STR_BUFF_SIZE,
"******** Start Sensor Status ********\n");
ia_circ_dbg_put_data(psh_ia_data, buf, str_size);
}
if (!sinfo) {
if (snr_info_start) {
snr_info_start = 0;
sensor_map_setup = 1;
str_size = snprintf(buf, STR_BUFF_SIZE,
"******** End Sensor Status ********\n");
ia_circ_dbg_put_data(psh_ia_data, buf, str_size);
}
return;
}
if (!sensor_map_setup && snr_info_start) {
int len = strlen(sinfo->name) + 1;
struct sensor_db *sensor_obj =
kmalloc(sizeof(struct sensor_db), GFP_KERNEL);
if (sensor_obj == NULL) {
psh_err("ia_handle_snr_info failed kmalloc sensor_obj\n");
BUG();
return;
}
sensor_obj->sid = sinfo->id;
memcpy(sensor_obj->sensor_name, sinfo->name,
len < SNR_NAME_MAX_LEN ? len : SNR_NAME_MAX_LEN);
sensor_obj->sensor_name[SNR_NAME_MAX_LEN - 1] = '\0';
list_add_tail(&sensor_obj->list, &psh_ia_data->sensor_list);
}
str_size = snprintf(buf, STR_BUFF_SIZE,
"***** Sensor %5s(%d) Status *****\n",
sinfo->name, sinfo->id);
ia_circ_dbg_put_data(psh_ia_data, buf, str_size);
str_size = snprintf(buf, STR_BUFF_SIZE,
" freq=%d, freq_max=%d\n"
" status=0x%x, bit_cfg=0x%x\n"
" data_cnt=%d, priv=0x%x\n"
" attri=0x%x, health=%d\n",
sinfo->freq, sinfo->freq_max,
sinfo->status, sinfo->bit_cfg,
sinfo->data_cnt, sinfo->priv,
sinfo->attri, sinfo->health);
ia_circ_dbg_put_data(psh_ia_data, buf, str_size);
for (i = 0; i < sinfo->link_num; i++) {
const struct link_info *linfo = &sinfo->linfo[i];
str_size = snprintf(buf, STR_BUFF_SIZE,
" %s%s=%3d, rpt_freq=%d\n",
(linfo->ltype == LINK_AS_REPORTER) ?
"REPORTER" : "CLIENT",
(linfo->ltype == LINK_AS_MONITOR) ?
"(M)" : "",
linfo->sid,
linfo->rpt_freq);
ia_circ_dbg_put_data(psh_ia_data, buf, str_size);
}
str_size = snprintf(buf, STR_BUFF_SIZE,
"*****************************\n");
ia_circ_dbg_put_data(psh_ia_data, buf, str_size);
}
ssize_t ia_set_status_mask(struct device *dev,
struct device_attribute *attr,
const char *str, size_t count)
{
char *s;
long val = 0;
struct psh_ia_priv *psh_ia_data =
(struct psh_ia_priv *)dev_get_drvdata(dev);
while (*str) {
val = trans_strtol(str, &s, 0);
if (str == s) {
str++;
continue;
} else
break;
}
psh_ia_data->status_bitmask = val;
pr_debug("set status_bitmask as 0x%x\n", psh_ia_data->status_bitmask);
return count;
}
ssize_t ia_get_status_mask(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct psh_ia_priv *psh_ia_data =
(struct psh_ia_priv *)dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "status_mask=0x%x\n",
psh_ia_data->status_bitmask);
}
ssize_t ia_trig_get_status(struct device *dev,
struct device_attribute *attr,
const char *str, size_t count)
{
struct ia_cmd cmd;
struct get_status_param *param = (struct get_status_param *)cmd.param;
struct psh_ia_priv *psh_ia_data =
(struct psh_ia_priv *)dev_get_drvdata(dev);
int ret;
if (str[0] == 'm')
param->snr_bitmask = psh_ia_data->status_bitmask;
else if (str[0] == 'a')
param->snr_bitmask = (u32)-1;
else if (str[0] == 'r')
param->snr_bitmask = ((u32)-1) & ~SNR_RUNONLY_BITMASK;
cmd.cmd_id = CMD_GET_STATUS;
cmd.sensor_id = 0;
ret = ia_send_cmd(psh_ia_data, &cmd, 7);
if (ret)
return ret;
return count;
}
ssize_t ia_get_counter(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret;
struct ia_cmd cmd;
struct cmd_counter_param *param = (struct cmd_counter_param *)cmd.param;
struct psh_ia_priv *psh_ia_data =
(struct psh_ia_priv *)dev_get_drvdata(dev);
cmd.cmd_id = CMD_COUNTER;
cmd.sensor_id = 0;
param->sub_cmd = SCMD_GET_COUNTER;
ret = ia_send_cmd(psh_ia_data, &cmd, 5);
if (ret)
return ret;
return snprintf(buf, PAGE_SIZE, " Module Counter(hex)\n"
" GPIO %x\n"
" I2C %x\n"
" DMA %x\n"
" PRINT %x\n",
psh_ia_data->counter.gpio_counter,
psh_ia_data->counter.i2c_counter,
psh_ia_data->counter.dma_counter,
psh_ia_data->counter.print_counter);
}
ssize_t ia_clear_counter(struct device *dev,
struct device_attribute *attr,
const char *str, size_t count)
{
int ret;
struct ia_cmd cmd;
struct cmd_counter_param *param = (struct cmd_counter_param *)cmd.param;
struct psh_ia_priv *psh_ia_data =
(struct psh_ia_priv *)dev_get_drvdata(dev);
cmd.cmd_id = CMD_COUNTER;
cmd.sensor_id = 0;
param->sub_cmd = SCMD_CLEAR_COUNTER;
ret = ia_send_cmd(psh_ia_data, &cmd, 5);
if (ret)
return ret;
else
return count;
}
ssize_t ia_get_version(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret;
struct ia_cmd cmd;
struct psh_ia_priv *psh_ia_data =
(struct psh_ia_priv *)dev_get_drvdata(dev);
cmd.cmd_id = CMD_GET_VERSION;
cmd.sensor_id = 0;
ret = ia_send_cmd(psh_ia_data, &cmd, 3);
if (ret)
return ret;
return snprintf(buf, PAGE_SIZE, "%s\n", psh_ia_data->version_str);
}
static SENSOR_DEVICE_ATTR(status_mask, S_IRUSR | S_IWUSR,
ia_get_status_mask, ia_set_status_mask, 0);
static SENSOR_DEVICE_ATTR(status_trig, S_IWUSR, NULL, ia_trig_get_status, 1);
static SENSOR_DEVICE_ATTR(debug, S_IRUSR | S_IWUSR ,
ia_get_dbg_mask, ia_set_dbg_mask, 0);
static SENSOR_DEVICE_ATTR(control, S_IWUSR, NULL, ia_start_control, 1);
static SENSOR_DEVICE_ATTR(data_size, S_IRUSR, ia_read_data_size, NULL, 2);
static SENSOR_DEVICE_ATTR(counter, S_IWUSR | S_IRUSR, ia_get_counter,
ia_clear_counter, 0);
static SENSOR_DEVICE_ATTR(fw_version, S_IRUSR, ia_get_version, NULL, 0);
static struct bin_attribute bin_attr = {
.attr = { .name = "data", .mode = S_IRUSR },
.read = ia_read_data
};
static struct bin_attribute dbg_attr = {
.attr = { .name = "trace", .mode = S_IRUSR },
.read = ia_read_debug_data
};
const char *sensor_get_name(u8 sid, struct psh_ia_priv *psh_ia_data)
{
if (sid == PSH_ITSELF)
return "_PSH_";
else if (is_port_sensor(sid))
return sensor_port_str[sid - PORT_SENSOR_BASE - 1];
else {
struct sensor_db *sensor_obj;
list_for_each_entry(sensor_obj,
&psh_ia_data->sensor_list, list) {
if (sid == sensor_obj->sid)
return sensor_obj->sensor_name;
}
}
return "?????";
}
static const char level_str[8][6] = {
"FATAL",
"ERROR",
" WARN",
" INFO",
"DEBUG",
"C_TRC",
"D_TRC",
"M_TRC",
};
static const char ctrace_str[12][6] = {
"SCORE",
"S_CFG",
"HW_PD",
"HW_PU",
"HW_PI",
" C_IN",
"C_OUT",
"IIDLE",
"OIDLE",
"SUSPD",
"RESUM",
};
static const char dtrace_str[5][6] = {
" MISC",
"D_RDY",
" D_IN",
"D_OUT",
"DDUMP"
};
static const char mtrace_str[3][6] = {
"TLOCK",
" LOCK",
"ULOCK",
};
const char *_get_evt_str(u16 level, u16 evt)
{
if (level == PSH_DBG_CTRACE)
return ctrace_str[evt];
else if (level == PSH_DBG_DTRACE)
return dtrace_str[evt];
if (level == PSH_DBG_MTRACE)
return mtrace_str[evt];
else {
int level_index = 0;
u16 mid_val = level;
while (mid_val > 1) {
mid_val /= 2;
level_index++;
}
return level_str[level_index];
}
}
/*
* return value = 0 no valid data frame
* return value > 0 data frame
* return value < 0 error data frame
*/
int ia_handle_frame(struct psh_ia_priv *psh_ia_data, void *dbuf, int size)
{
struct cmd_resp *resp = dbuf;
const struct snr_info *sinfo;
const struct resp_version *version;
const struct resp_cmd_ack *cmd_ack;
u32 curtime;
int len;
const char *sensor_name;
const char *event_name;
const char *context_name;
struct trace_data *out_data;
char msg_str[STR_BUFF_SIZE];
ia_sync_timestamp(psh_ia_data, 1);
switch (resp->type) {
case RESP_CMD_ACK:
cmd_ack = (struct resp_cmd_ack *)resp->buf;
if (!psh_ia_data->cmd_ack)
psh_err("Unexpected CMD_ACK recevied, %d\n", cmd_ack->cmd_id);
else if (cmd_ack->cmd_id == psh_ia_data->cmd_ack->cmd_id) {
psh_ia_data->cmd_ack->ret = cmd_ack->ret;
complete(&psh_ia_data->cmd_comp);
} else
psh_err("Unmatched CMD_ACK recevied, %d(EXP: %d)\n",
cmd_ack->cmd_id,
psh_ia_data->cmd_ack->cmd_id);
break;
case RESP_BIST_RESULT:
break;
case RESP_DEBUG_MSG:
ia_circ_dbg_put_data(psh_ia_data,
resp->buf, resp->data_len);
return 0;
case RESP_GET_STATUS:
sinfo = (struct snr_info *)resp->buf;
if (!resp->data_len)
ia_handle_snr_info(psh_ia_data, NULL);
else if (SNR_INFO_SIZE(sinfo) == resp->data_len)
ia_handle_snr_info(psh_ia_data, sinfo);
else {
psh_err("Wrong RESP_GET_STATUS!\n");
return 0;
}
break;
case RESP_DEBUG_GET_MASK:
memcpy(&psh_ia_data->dbg_mask, resp->buf,
sizeof(psh_ia_data->dbg_mask));
return 0;
case RESP_COUNTER:
memcpy(&psh_ia_data->counter, resp->buf,
sizeof(psh_ia_data->counter));
return 0;
case RESP_GET_VERSION:
version = (struct resp_version *)resp->buf;
if (likely(version->str_len < VERSION_STR_MAX_SIZE))
memcpy(psh_ia_data->version_str, version->str,
version->str_len + 1);
else {
memcpy(psh_ia_data->version_str, version->str,
VERSION_STR_MAX_SIZE - 1);
psh_ia_data->version_str[VERSION_STR_MAX_SIZE - 1]
= '\0';
}
return 0;
case RESP_TRACE_MSG:
out_data = (struct trace_data *)resp->buf;
while ((char *)out_data < resp->buf + resp->data_len) {
curtime = out_data->timestamp;
sensor_name = sensor_get_name(out_data->sensor_id,
psh_ia_data);
event_name = _get_evt_str(out_data->type,
out_data->event);
context_name = sensor_get_name(out_data->sensor_cnt,
psh_ia_data);
len = snprintf(msg_str, STR_BUFF_SIZE,
"[%u,%s,%s,%s]\n",
curtime, sensor_name,
event_name, context_name);
ia_circ_dbg_put_data(psh_ia_data,
msg_str, len);
out_data++;
}
return 0;
default:
break;
}
pr_debug("one DDR frame, data of sensor %d, size %d\n",
resp->sensor_id, size);
ia_circ_put_data(&psh_ia_data->circ, dbuf, size);
return size;
}
int psh_ia_common_init(struct device *dev, struct psh_ia_priv **data)
{
int ret = -1;
struct psh_ia_priv *psh_ia_data;
psh_ia_data = kzalloc(sizeof(*psh_ia_data), GFP_KERNEL);
if (!psh_ia_data) {
dev_err(dev, "can not allocate psh_ia_data\n");
goto priv_err;
}
*data = psh_ia_data;
mutex_init(&psh_ia_data->cmd_mutex);
psh_ia_data->cmd_in_progress = CMD_INVALID;
mutex_init(&psh_ia_data->circ_dbg_mutex);
init_completion(&psh_ia_data->cmd_load_comp);
init_completion(&psh_ia_data->cmd_comp);
INIT_LIST_HEAD(&psh_ia_data->sensor_list);
psh_ia_data->circ.buf = kmalloc(CIRC_SIZE, GFP_KERNEL);
if (!psh_ia_data->circ.buf) {
dev_err(dev, "can not allocate circ buffer\n");
goto circ_err;
}
psh_ia_data->circ_dbg.buf = kmalloc(CIRC_SIZE, GFP_KERNEL);
if (!psh_ia_data->circ_dbg.buf) {
dev_err(dev, "can not allocate circ buffer\n");
goto dbg_err;
}
psh_ia_data->version_str = kmalloc(VERSION_STR_MAX_SIZE, GFP_KERNEL);
if (!psh_ia_data->version_str) {
dev_err(dev, "can not allocate version string\n");
goto ver_err;
}
psh_ia_data->status_bitmask = ((u32)-1) & ~SNR_RUNONLY_BITMASK;
dev_set_drvdata(dev, psh_ia_data);
ret = sysfs_create_file(&dev->kobj,
&sensor_dev_attr_status_mask.dev_attr.attr);
ret += sysfs_create_file(&dev->kobj,
&sensor_dev_attr_status_trig.dev_attr.attr);
ret += sysfs_create_file(&dev->kobj,
&sensor_dev_attr_debug.dev_attr.attr);
ret += sysfs_create_file(&dev->kobj,
&sensor_dev_attr_control.dev_attr.attr);
ret += sysfs_create_file(&dev->kobj,
&sensor_dev_attr_data_size.dev_attr.attr);
ret += sysfs_create_file(&dev->kobj,
&sensor_dev_attr_counter.dev_attr.attr);
ret += sysfs_create_file(&dev->kobj,
&sensor_dev_attr_fw_version.dev_attr.attr);
ret += sysfs_create_bin_file(&dev->kobj, &bin_attr);
ret += sysfs_create_bin_file(&dev->kobj, &dbg_attr);
if (ret) {
dev_err(dev, "can not create sysfs\n");
goto sysfs_err;
}
return 0;
sysfs_err:
sysfs_remove_file(&dev->kobj,
&sensor_dev_attr_status_mask.dev_attr.attr);
sysfs_remove_file(&dev->kobj,
&sensor_dev_attr_status_trig.dev_attr.attr);
sysfs_remove_file(&dev->kobj,
&sensor_dev_attr_debug.dev_attr.attr);
sysfs_remove_file(&dev->kobj,
&sensor_dev_attr_control.dev_attr.attr);
sysfs_remove_file(&dev->kobj,
&sensor_dev_attr_data_size.dev_attr.attr);
sysfs_remove_file(&dev->kobj,
&sensor_dev_attr_counter.dev_attr.attr);
sysfs_remove_file(&dev->kobj,
&sensor_dev_attr_fw_version.dev_attr.attr);
sysfs_remove_bin_file(&dev->kobj, &bin_attr);
sysfs_remove_bin_file(&dev->kobj, &dbg_attr);
kfree(psh_ia_data->version_str);
ver_err:
kfree(psh_ia_data->circ_dbg.buf);
dbg_err:
kfree(psh_ia_data->circ.buf);
circ_err:
kfree(psh_ia_data);
priv_err:
return ret;
}
void psh_ia_common_deinit(struct device *dev)
{
struct sensor_db *sensor_obj, *sensor_tmp;
struct psh_ia_priv *psh_ia_data =
(struct psh_ia_priv *)dev_get_drvdata(dev);
sysfs_remove_file(&dev->kobj,
&sensor_dev_attr_status_mask.dev_attr.attr);
sysfs_remove_file(&dev->kobj,
&sensor_dev_attr_status_trig.dev_attr.attr);
sysfs_remove_file(&dev->kobj,
&sensor_dev_attr_debug.dev_attr.attr);
sysfs_remove_file(&dev->kobj,
&sensor_dev_attr_control.dev_attr.attr);
sysfs_remove_file(&dev->kobj,
&sensor_dev_attr_data_size.dev_attr.attr);
sysfs_remove_file(&dev->kobj,
&sensor_dev_attr_counter.dev_attr.attr);
sysfs_remove_file(&dev->kobj,
&sensor_dev_attr_fw_version.dev_attr.attr);
sysfs_remove_bin_file(&dev->kobj, &bin_attr);
sysfs_remove_bin_file(&dev->kobj, &dbg_attr);
list_for_each_entry_safe(sensor_obj, sensor_tmp,
&psh_ia_data->sensor_list, list) {
list_del(&sensor_obj->list);
kfree(sensor_obj);
}
kfree(psh_ia_data->version_str);
kfree(psh_ia_data->circ.buf);
kfree(psh_ia_data->circ_dbg.buf);
kfree(psh_ia_data);
}
int psh_ia_comm_suspend(struct device *dev)
{
struct psh_ia_priv *psh_ia_data =
(struct psh_ia_priv *)dev_get_drvdata(dev);
struct ia_cmd cmd = {
.cmd_id = CMD_IA_NOTIFY,
};
struct cmd_ia_notify_param *param =
(struct cmd_ia_notify_param *)cmd.param;
int ret;
param->id = IA_NOTIFY_SUSPEND;
ret = ia_send_cmd(psh_ia_data, &cmd, 4);
if (ret)
dev_warn(dev, "PSH: IA_NOTIFY_SUSPEND ret=%d\n", ret);
return 0;
}
int psh_ia_comm_resume(struct device *dev)
{
struct psh_ia_priv *psh_ia_data =
(struct psh_ia_priv *)dev_get_drvdata(dev);
struct ia_cmd cmd = {
.cmd_id = CMD_IA_NOTIFY,
};
struct cmd_ia_notify_param *param =
(struct cmd_ia_notify_param *)cmd.param;
int ret;
param->id = IA_NOTIFY_RESUME;
ret = ia_send_cmd(psh_ia_data, &cmd, 4);
if (ret)
dev_warn(dev, "PSH: IA_NOTIFY_RESUME ret=%d\n", ret);
return 0;
}