1189 lines
30 KiB
C

/*
* aw_monitor.c
*
* Copyright (c) 2021 AWINIC Technology CO., LTD
*
* Author: Barry <zhaozhongbo@awinic.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/power_supply.h>
#include <linux/of.h>
#include <linux/power_supply.h>
#include <linux/hrtimer.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/firmware.h>
#include <linux/platform_device.h>
#include <linux/proc_fs.h>
#include <linux/io.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/gameport.h>
#include <linux/moduleparam.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
#include "aw87xxx.h"
#include "aw_log.h"
#include "aw_monitor.h"
#include "aw_dsp.h"
#include "aw_bin_parse.h"
#include "aw_device.h"
#define AW_MONITOT_BIN_PARSE_VERSION "V0.1.0"
#define AW_GET_32_DATA(w, x, y, z) \
((uint32_t)((((uint8_t)w) << 24) | (((uint8_t)x) << 16) | \
(((uint8_t)y) << 8) | ((uint8_t)z)))
/****************************************************************************
*
* aw87xxx monitor bin check
*
****************************************************************************/
static int aw_monitor_check_header_v_1_0_0(struct device *dev,
char *data, uint32_t data_len)
{
int i = 0;
struct aw_bin_header *header = (struct aw_bin_header *)data;
if (header->bin_data_type != DATA_TYPE_MONITOR_ANALOG) {
AW_DEV_LOGE(dev, "monitor data_type check error!");
return -EINVAL;
}
if (header->bin_data_size != AW_MONITOR_HDR_DATA_SIZE) {
AW_DEV_LOGE(dev, "monitor data_size error!");
return -EINVAL;
}
if (header->data_byte_len != AW_MONITOR_HDR_DATA_BYTE_LEN) {
AW_DEV_LOGE(dev, "monitor data_byte_len error!");
return -EINVAL;
}
for (i = 0; i < AW_MONITOR_DATA_VER_MAX; i++) {
if (header->bin_data_ver == i) {
AW_LOGD("monitor bin_data_ver[0x%x]", i);
break;
}
}
if (i == AW_MONITOR_DATA_VER_MAX)
return -EINVAL;
return 0;
}
static int aw_monitor_check_data_v1_size(struct device *dev,
char *data, int32_t data_len)
{
int32_t bin_header_len = sizeof(struct aw_bin_header);
int32_t monitor_header_len = sizeof(struct aw_monitor_header);
int32_t monitor_data_len = sizeof(struct vmax_step_config);
int32_t len = 0;
struct aw_monitor_header *monitor_header = NULL;
AW_DEV_LOGD(dev, "enter");
if (data_len < bin_header_len + monitor_header_len) {
AW_DEV_LOGE(dev, "bin len is less than aw_bin_header and monitoor_header,check failed");
return -EINVAL;
}
monitor_header = (struct aw_monitor_header *)(data + bin_header_len);
len = data_len - bin_header_len - monitor_header_len;
if (len < monitor_header->step_count * monitor_data_len) {
AW_DEV_LOGE(dev, "bin data len is not enough,check failed");
return -EINVAL;
}
AW_DEV_LOGD(dev, "succeed");
return 0;
}
static int aw_monitor_check_data_size(struct device *dev,
char *data, int32_t data_len)
{
int ret = -1;
struct aw_bin_header *header = (struct aw_bin_header *)data;
switch (header->bin_data_ver) {
case AW_MONITOR_DATA_VER:
ret = aw_monitor_check_data_v1_size(dev, data, data_len);
if (ret < 0)
return ret;
break;
default:
AW_DEV_LOGE(dev, "bin data_ver[0x%x] non support",
header->bin_data_ver);
return -EINVAL;
}
return 0;
}
static int aw_monitor_check_bin_header(struct device *dev,
char *data, int32_t data_len)
{
int ret = -1;
struct aw_bin_header *header = NULL;
if (data_len < sizeof(struct aw_bin_header)) {
AW_DEV_LOGE(dev, "bin len is less than aw_bin_header,check failed");
return -EINVAL;
}
header = (struct aw_bin_header *)data;
switch (header->header_ver) {
case HEADER_VERSION_1_0_0:
ret = aw_monitor_check_header_v_1_0_0(dev, data, data_len);
if (ret < 0) {
AW_DEV_LOGE(dev, "monitor bin haeder info check error!");
return ret;
}
break;
default:
AW_DEV_LOGE(dev, "bin version[0x%x] non support",
header->header_ver);
return -EINVAL;
}
return 0;
}
static int aw_monitor_bin_check_sum(struct device *dev,
char *data, int32_t data_len)
{
int i, data_sum = 0;
uint32_t *check_sum = (uint32_t *)data;
for (i = 4; i < data_len; i++)
data_sum += data[i];
if (*check_sum != data_sum) {
AW_DEV_LOGE(dev, "check_sum[%d] is not equal to data_sum[%d]",
*check_sum, data_sum);
return -ENOMEM;
}
AW_DEV_LOGD(dev, "succeed");
return 0;
}
static int aw_monitor_bin_check(struct device *dev,
char *monitor_data, uint32_t data_len)
{
int ret = -1;
if (monitor_data == NULL || data_len == 0) {
AW_DEV_LOGE(dev, "none data to parse");
return -EINVAL;
}
ret = aw_monitor_bin_check_sum(dev, monitor_data, data_len);
if (ret < 0) {
AW_DEV_LOGE(dev, "bin data check sum failed");
return ret;
}
ret = aw_monitor_check_bin_header(dev, monitor_data, data_len);
if (ret < 0) {
AW_DEV_LOGE(dev, "bin data len check failed");
return ret;
}
ret = aw_monitor_check_data_size(dev, monitor_data, data_len);
if (ret < 0) {
AW_DEV_LOGE(dev, "bin header info check failed");
return ret;
}
return 0;
}
/*****************************************************************************
*
* aw87xxx monitor header bin parse
*
*****************************************************************************/
static void aw_monitor_write_to_table_v1(struct device *dev,
struct vmax_step_config *vmax_step,
char *vmax_data, uint32_t step_count)
{
int i = 0;
int index = 0;
int vmax_step_size = (int)sizeof(struct vmax_step_config);
for (i = 0; i < step_count; i++) {
index = vmax_step_size * i;
vmax_step[i].vbat_min =
AW_GET_32_DATA(vmax_data[index + 3],
vmax_data[index + 2],
vmax_data[index + 1],
vmax_data[index + 0]);
vmax_step[i].vbat_max =
AW_GET_32_DATA(vmax_data[index + 7],
vmax_data[index + 6],
vmax_data[index + 5],
vmax_data[index + 4]);
vmax_step[i].vmax_vol =
AW_GET_32_DATA(vmax_data[index + 11],
vmax_data[index + 10],
vmax_data[index + 9],
vmax_data[index + 8]);
}
for (i = 0; i < step_count; i++)
AW_DEV_LOGI(dev, "vbat_min:%d, vbat_max%d, vmax_vol:0x%x",
vmax_step[i].vbat_min,
vmax_step[i].vbat_max,
vmax_step[i].vmax_vol);
}
static int aw_monitor_parse_vol_data_v1(struct device *dev,
struct aw_monitor *monitor, char *monitor_data)
{
uint32_t step_count = 0;
char *vmax_data = NULL;
struct vmax_step_config *vmax_step = NULL;
AW_DEV_LOGD(dev, "enter");
step_count = monitor->monitor_hdr.step_count;
if (step_count) {
vmax_step = devm_kzalloc(dev, sizeof(struct vmax_step_config) * step_count,
GFP_KERNEL);
if (vmax_step == NULL) {
AW_DEV_LOGE(dev, "vmax_cfg vmalloc failed");
return -ENOMEM;
}
memset(vmax_step, 0,
sizeof(struct vmax_step_config) * step_count);
}
vmax_data = monitor_data + sizeof(struct aw_bin_header) +
sizeof(struct aw_monitor_header);
aw_monitor_write_to_table_v1(dev, vmax_step, vmax_data, step_count);
monitor->vmax_cfg = vmax_step;
AW_DEV_LOGI(dev, "vmax_data parse succeed");
return 0;
}
static int aw_monitor_parse_data_v1(struct device *dev,
struct aw_monitor *monitor, char *monitor_data)
{
int ret = -1;
int header_len = 0;
struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr;
header_len = sizeof(struct aw_bin_header);
memcpy(monitor_hdr, monitor_data + header_len,
sizeof(struct aw_monitor_header));
AW_DEV_LOGI(dev, "monitor_switch:%d, monitor_time:%d (ms), monitor_count:%d, step_count:%d",
monitor_hdr->monitor_switch, monitor_hdr->monitor_time,
monitor_hdr->monitor_count, monitor_hdr->step_count);
ret = aw_monitor_parse_vol_data_v1(dev, monitor, monitor_data);
if (ret < 0) {
AW_DEV_LOGE(dev, "vmax_data parse failed");
return ret;
}
monitor->bin_status = AW_MONITOR_CFG_OK;
return 0;
}
static int aw_monitor_parse_v_1_0_0(struct device *dev,
struct aw_monitor *monitor, char *monitor_data)
{
int ret = -1;
struct aw_bin_header *header = (struct aw_bin_header *)monitor_data;
switch (header->bin_data_ver) {
case AW_MONITOR_DATA_VER:
ret = aw_monitor_parse_data_v1(dev, monitor, monitor_data);
if (ret < 0)
return ret;
break;
default:
return -EINVAL;
}
return 0;
}
void aw_monitor_cfg_free(struct aw_monitor *monitor)
{
struct aw87xxx *aw87xxx =
container_of(monitor, struct aw87xxx, monitor);
monitor->bin_status = AW_MONITOR_CFG_WAIT;
memset(&monitor->monitor_hdr, 0,
sizeof(struct aw_monitor_header));
if (monitor->vmax_cfg) {
devm_kfree(aw87xxx->dev, monitor->vmax_cfg);
monitor->vmax_cfg = NULL;
}
}
int aw_monitor_bin_parse(struct device *dev,
char *monitor_data, uint32_t data_len)
{
int ret = -1;
struct aw87xxx *aw87xxx = dev_get_drvdata(dev);
struct aw_monitor *monitor = NULL;
struct aw_bin_header *bin_header = NULL;
if (aw87xxx == NULL) {
AW_DEV_LOGE(dev, "get struct aw87xxx failed");
return -EINVAL;
}
monitor = &aw87xxx->monitor;
monitor->bin_status = AW_MONITOR_CFG_WAIT;
AW_DEV_LOGI(dev, "monitor bin parse version: %s",
AW_MONITOT_BIN_PARSE_VERSION);
ret = aw_monitor_bin_check(dev, monitor_data, data_len);
if (ret < 0) {
AW_DEV_LOGE(dev, "monitor bin check failed");
return ret;
}
bin_header = (struct aw_bin_header *)monitor_data;
switch (bin_header->bin_data_ver) {
case DATA_VERSION_V1:
ret = aw_monitor_parse_v_1_0_0(dev, monitor,
monitor_data);
if (ret < 0) {
aw_monitor_cfg_free(monitor);
return ret;
}
break;
default:
AW_DEV_LOGE(dev, "Unrecognized this bin data version[0x%x]",
bin_header->bin_data_ver);
}
return 0;
}
/***************************************************************************
*
* aw87xxx monitor get adjustment vmax of power
*
***************************************************************************/
static int aw_monitor_get_battery_capacity(struct device *dev,
struct aw_monitor *monitor,
uint32_t *vbat_capacity)
{
char name[] = "battery";
int ret = -1;
union power_supply_propval prop = { 0 };
struct power_supply *psy = NULL;
psy = power_supply_get_by_name(name);
if (psy == NULL) {
AW_DEV_LOGE(dev, "no struct power supply name:%s", name);
return -EINVAL;
}
ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_CAPACITY, &prop);
if (ret < 0) {
AW_DEV_LOGE(dev, "get vbat capacity failed");
return -EINVAL;
}
*vbat_capacity = prop.intval;
AW_DEV_LOGI(dev, "The percentage is %d",
*vbat_capacity);
return 0;
}
static int aw_search_vmax_from_table(struct device *dev,
struct aw_monitor *monitor,
const int vbat_vol, int *vmax_vol)
{
int i = 0;
int vmax_set = 0;
uint32_t vmax_flag = 0;
struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr;
struct vmax_step_config *vmax_cfg = monitor->vmax_cfg;
if (monitor->bin_status == AW_MONITOR_CFG_WAIT) {
AW_DEV_LOGE(dev, "vmax_cfg not loaded or parse failed");
return -ENODATA;
}
for (i = 0; i < monitor_hdr->step_count; i++) {
if (vbat_vol == AW_VBAT_MAX) {
vmax_set = AW_VMAX_MAX;
vmax_flag = 1;
AW_DEV_LOGD(dev, "vbat=%d, setting vmax=0x%x",
vbat_vol, vmax_set);
break;
}
if (vbat_vol >= vmax_cfg[i].vbat_min &&
vbat_vol < vmax_cfg[i].vbat_max) {
vmax_set = vmax_cfg[i].vmax_vol;
vmax_flag = 1;
AW_DEV_LOGD(dev, "read setting vmax=0x%x, step[%d]: vbat_min=%d,vbat_max=%d",
vmax_set, i,
vmax_cfg[i].vbat_min,
vmax_cfg[i].vbat_max);
break;
}
}
if (!vmax_flag) {
AW_DEV_LOGE(dev, "vmax_cfg not found");
return -ENODATA;
}
*vmax_vol = vmax_set;
return 0;
}
/***************************************************************************
*
*monitor_esd_func
*
***************************************************************************/
static int aw_chip_status_recover(struct aw87xxx *aw87xxx)
{
int ret = -1;
struct aw_monitor *monitor = &aw87xxx->monitor;
char *profile = aw87xxx->current_profile;
AW_DEV_LOGD(aw87xxx->dev, "enter");
ret = aw87xxx_esd_update_profile(aw87xxx, profile);
if (ret < 0) {
AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ",
profile);
return ret;
}
AW_DEV_LOGI(aw87xxx->dev, "current prof[%s], dev_index[%d] ",
profile, aw87xxx->dev_index);
monitor->pre_vmax = AW_VMAX_INIT_VAL;
monitor->first_entry = AW_FIRST_ENTRY;
monitor->timer_cnt = 0;
monitor->vbat_sum = 0;
return 0;
}
static int aw_monitor_chip_esd_check_work(struct aw87xxx *aw87xxx)
{
int ret = 0;
int i = 0;
for (i = 0; i < REG_STATUS_CHECK_MAX; i++) {
AW_DEV_LOGD(aw87xxx->dev, "reg_status_check[%d]", i);
ret = aw_dev_esd_reg_status_check(&aw87xxx->aw_dev);
if (ret < 0) {
aw_chip_status_recover(aw87xxx);
} else {
AW_DEV_LOGD(aw87xxx->dev, "chip status check succeed");
break;
}
msleep(AW_ESD_CHECK_DELAY);
}
if (ret < 0) {
AW_DEV_LOGE(aw87xxx->dev, "chip status recover failed,chip off");
aw87xxx_esd_update_profile(aw87xxx, aw87xxx->prof_off_name);
return ret;
}
return 0;
}
/***************************************************************************
*
* aw87xxx monitor work with dsp
*
***************************************************************************/
static int aw_monitor_update_vmax_to_dsp(struct device *dev,
struct aw_monitor *monitor, int vmax_set)
{
int ret = -1;
uint32_t enable = 0;
if (monitor->pre_vmax != vmax_set) {
ret = aw_dsp_get_rx_module_enable(&enable);
if (!enable || ret < 0) {
AW_DEV_LOGE(dev, "get rx failed or rx disable, ret=%d, enable=%d",
ret, enable);
return -EPERM;
}
ret = aw_dsp_set_vmax(vmax_set, monitor->dev_index);
if (ret) {
AW_DEV_LOGE(dev, "set dsp msg fail, ret=%d", ret);
return ret;
}
AW_DEV_LOGI(dev, "set dsp vmax=0x%x sucess", vmax_set);
monitor->pre_vmax = vmax_set;
} else {
AW_DEV_LOGI(dev, "vmax=0x%x no change", vmax_set);
}
return 0;
}
static void aw_monitor_with_dsp_vmax_work(struct device *dev,
struct aw_monitor *monitor)
{
int ret = -1;
int vmax_set = 0;
uint32_t vbat_capacity = 0;
uint32_t ave_capacity = 0;
struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr;
AW_DEV_LOGD(dev, "enter with dsp monitor");
ret = aw_monitor_get_battery_capacity(dev, monitor, &vbat_capacity);
if (ret < 0)
return;
if (monitor->timer_cnt < monitor_hdr->monitor_count) {
monitor->timer_cnt++;
monitor->vbat_sum += vbat_capacity;
AW_DEV_LOGI(dev, "timer_cnt = %d",
monitor->timer_cnt);
}
if ((monitor->timer_cnt >= monitor_hdr->monitor_count) ||
(monitor->first_entry == AW_FIRST_ENTRY)) {
if (monitor->first_entry == AW_FIRST_ENTRY)
monitor->first_entry = AW_NOT_FIRST_ENTRY;
ave_capacity = monitor->vbat_sum / monitor->timer_cnt;
if (monitor->custom_capacity)
ave_capacity = monitor->custom_capacity;
AW_DEV_LOGI(dev, "get average capacity = %d", ave_capacity);
ret = aw_search_vmax_from_table(dev, monitor,
ave_capacity, &vmax_set);
if (ret < 0)
AW_DEV_LOGE(dev, "not find vmax_vol");
else
aw_monitor_update_vmax_to_dsp(dev, monitor, vmax_set);
monitor->timer_cnt = 0;
monitor->vbat_sum = 0;
}
}
static void aw_monitor_work_func(struct work_struct *work)
{
int ret = 0;
struct aw87xxx *aw87xxx = container_of(work,
struct aw87xxx, monitor.with_dsp_work.work);
struct device *dev = aw87xxx->dev;
struct aw_monitor *monitor = &aw87xxx->monitor;
struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr;
AW_DEV_LOGD(dev, "enter");
if (monitor->esd_enable) {
ret = aw_monitor_chip_esd_check_work(aw87xxx);
if (ret < 0)
return;
}
if (monitor_hdr->monitor_switch && !(aw87xxx->aw_dev.is_rec_mode) &&
monitor->open_dsp_en && monitor->bin_status == AW_ACF_UPDATE) {
AW_DEV_LOGD(dev, "start low power protection");
aw_monitor_with_dsp_vmax_work(dev, monitor);
}
if (monitor->esd_enable || (monitor_hdr->monitor_switch &&
!(aw87xxx->aw_dev.is_rec_mode) && monitor->open_dsp_en &&
monitor->bin_status == AW_ACF_UPDATE)) {
schedule_delayed_work(&monitor->with_dsp_work,
msecs_to_jiffies(monitor_hdr->monitor_time));
}
}
void aw_monitor_stop(struct aw_monitor *monitor)
{
struct aw87xxx *aw87xxx =
container_of(monitor, struct aw87xxx, monitor);
AW_DEV_LOGD(aw87xxx->dev, "enter");
cancel_delayed_work_sync(&monitor->with_dsp_work);
}
void aw_monitor_start(struct aw_monitor *monitor)
{
struct aw87xxx *aw87xxx =
container_of(monitor, struct aw87xxx, monitor);
int ret = 0;
ret = aw_dev_check_reg_is_rec_mode(&aw87xxx->aw_dev);
if (ret < 0) {
AW_DEV_LOGE(aw87xxx->dev, "get reg current mode failed");
return;
}
if (monitor->esd_enable || (monitor->monitor_hdr.monitor_switch &&
!(aw87xxx->aw_dev.is_rec_mode) && monitor->open_dsp_en
&& monitor->bin_status == AW_ACF_UPDATE)) {
AW_DEV_LOGD(aw87xxx->dev, "enter");
monitor->pre_vmax = AW_VMAX_INIT_VAL;
monitor->first_entry = AW_FIRST_ENTRY;
monitor->timer_cnt = 0;
monitor->vbat_sum = 0;
schedule_delayed_work(&monitor->with_dsp_work,
msecs_to_jiffies(monitor->monitor_hdr.monitor_time));
}
}
/***************************************************************************
*
* aw87xxx no dsp monitor func
*
***************************************************************************/
int aw_monitor_no_dsp_get_vmax(struct aw_monitor *monitor, int32_t *vmax)
{
int vbat_capacity = 0;
int ret = -1;
int vmax_vol = 0;
struct aw87xxx *aw87xxx =
container_of(monitor, struct aw87xxx, monitor);
struct device *dev = aw87xxx->dev;
ret = aw_monitor_get_battery_capacity(dev, monitor, &vbat_capacity);
if (ret < 0)
return ret;
if (monitor->custom_capacity)
vbat_capacity = monitor->custom_capacity;
AW_DEV_LOGI(dev, "get_battery_capacity is[%d]", vbat_capacity);
ret = aw_search_vmax_from_table(dev, monitor,
vbat_capacity, &vmax_vol);
if (ret < 0) {
AW_DEV_LOGE(dev, "not find vmax_vol");
return ret;
}
*vmax = vmax_vol;
return 0;
}
/***************************************************************************
*
* aw87xxx monitor sysfs nodes
*
***************************************************************************/
static ssize_t aw_attr_get_esd_enable(struct device *dev,
struct device_attribute *attr, char *buf)
{
ssize_t len = 0;
struct aw87xxx *aw87xxx = dev_get_drvdata(dev);
struct aw_monitor *monitor = &aw87xxx->monitor;
if (monitor->esd_enable) {
AW_DEV_LOGI(aw87xxx->dev, "esd-enable=true");
len += snprintf(buf + len, PAGE_SIZE - len,
"esd-enable=true\n");
} else {
AW_DEV_LOGI(aw87xxx->dev, "esd-enable=false");
len += snprintf(buf + len, PAGE_SIZE - len,
"esd-enable=false\n");
}
return len;
}
static ssize_t aw_attr_set_esd_enable(struct device *dev,
struct device_attribute *attr, const char *buf, size_t len)
{
char esd_enable[16] = {0};
struct aw87xxx *aw87xxx = dev_get_drvdata(dev);
struct aw_monitor *monitor = &aw87xxx->monitor;
if (sscanf(buf, "%s", esd_enable) == 1) {
AW_DEV_LOGD(aw87xxx->dev, "input esd-enable=[%s]", esd_enable);
if (!strcmp(esd_enable, "true"))
monitor->esd_enable = AW_ESD_ENABLE;
else
monitor->esd_enable = AW_ESD_DISABLE;
AW_DEV_LOGI(dev, "set esd-enable=[%s]",
monitor->esd_enable ? "true" : "false");
} else {
AW_DEV_LOGE(aw87xxx->dev, "input esd-enable error");
return -EINVAL;
}
return len;
}
static ssize_t aw_attr_get_vbat(struct device *dev,
struct device_attribute *attr, char *buf)
{
ssize_t len = 0;
int ret = -1;
int vbat_capacity = 0;
struct aw87xxx *aw87xxx = dev_get_drvdata(dev);
struct aw_monitor *monitor = &aw87xxx->monitor;
if (monitor->custom_capacity == 0) {
ret = aw_monitor_get_battery_capacity(dev, monitor,
&vbat_capacity);
if (ret < 0) {
AW_DEV_LOGE(aw87xxx->dev, "get battery_capacity failed");
return ret;
}
len += snprintf(buf + len, PAGE_SIZE - len,
"vbat capacity=%d\n", vbat_capacity);
} else {
len += snprintf(buf + len, PAGE_SIZE - len,
"vbat capacity=%d\n",
monitor->custom_capacity);
}
return len;
}
static ssize_t aw_attr_set_vbat(struct device *dev,
struct device_attribute *attr, const char *buf, size_t len)
{
int ret = -1;
uint32_t capacity = 0;
struct aw87xxx *aw87xxx = dev_get_drvdata(dev);
struct aw_monitor *monitor = &aw87xxx->monitor;
ret = kstrtouint(buf, 0, &capacity);
if (ret < 0)
return ret;
AW_DEV_LOGI(aw87xxx->dev, "set capacity = %d", capacity);
if (capacity >= AW_VBAT_CAPACITY_MIN &&
capacity <= AW_VBAT_CAPACITY_MAX){
monitor->custom_capacity = capacity;
} else {
AW_DEV_LOGE(aw87xxx->dev, "vbat_set=invalid,please input value [%d-%d]",
AW_VBAT_CAPACITY_MIN, AW_VBAT_CAPACITY_MAX);
return -EINVAL;
}
return len;
}
static ssize_t aw_attr_get_vmax(struct device *dev,
struct device_attribute *attr, char *buf)
{
ssize_t len = 0;
int ret = -1;
uint32_t vbat_capacity = 0;
int vmax_get = 0;
struct aw87xxx *aw87xxx = dev_get_drvdata(dev);
struct aw_monitor *monitor = &aw87xxx->monitor;
if (monitor->open_dsp_en) {
ret = aw_dsp_get_vmax(&vmax_get, aw87xxx->dev_index);
if (ret < 0) {
AW_DEV_LOGE(aw87xxx->dev,
"get dsp vmax fail, ret=%d", ret);
return ret;
}
len += snprintf(buf + len, PAGE_SIZE - len,
"get_vmax=0x%x\n", vmax_get);
} else {
ret = aw_monitor_get_battery_capacity(dev, monitor,
&vbat_capacity);
if (ret < 0)
return ret;
AW_DEV_LOGI(aw87xxx->dev, "get_battery_capacity is [%d]",
vbat_capacity);
if (monitor->custom_capacity) {
vbat_capacity = monitor->custom_capacity;
AW_DEV_LOGI(aw87xxx->dev, "get custom_capacity is [%d]",
vbat_capacity);
}
ret = aw_search_vmax_from_table(aw87xxx->dev, monitor,
vbat_capacity, &vmax_get);
if (ret < 0) {
AW_DEV_LOGE(aw87xxx->dev, "not find vmax_vol");
len += snprintf(buf + len, PAGE_SIZE - len,
"not_find_vmax_vol\n");
return len;
}
len += snprintf(buf + len, PAGE_SIZE - len,
"0x%x\n", vmax_get);
AW_DEV_LOGI(aw87xxx->dev, "0x%x", vmax_get);
}
return len;
}
static ssize_t aw_attr_set_vmax(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
uint32_t vmax_set = 0;
int ret = -1;
struct aw87xxx *aw87xxx = dev_get_drvdata(dev);
struct aw_monitor *monitor = &aw87xxx->monitor;
ret = kstrtouint(buf, 0, &vmax_set);
if (ret < 0)
return ret;
AW_DEV_LOGI(aw87xxx->dev, "vmax_set=0x%x", vmax_set);
if (monitor->open_dsp_en) {
ret = aw_dsp_set_vmax(vmax_set, aw87xxx->dev_index);
if (ret < 0) {
AW_DEV_LOGE(aw87xxx->dev, "send dsp_msg error, ret = %d",
ret);
return ret;
}
msleep(2);
} else {
AW_DEV_LOGE(aw87xxx->dev, "no_dsp system,vmax_set invalid");
return -EINVAL;
}
return count;
}
static ssize_t aw_attr_get_monitor_switch(struct device *dev,
struct device_attribute *attr, char *buf)
{
ssize_t len = 0;
struct aw87xxx *aw87xxx = dev_get_drvdata(dev);
struct aw_monitor *monitor = &aw87xxx->monitor;
struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr;
len += snprintf(buf + len, PAGE_SIZE - len,
"aw87xxx monitor switch: %d\n",
monitor_hdr->monitor_switch);
return len;
}
static ssize_t aw_attr_set_monitor_switch(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
uint32_t enable = 0;
int ret = -1;
struct aw87xxx *aw87xxx = dev_get_drvdata(dev);
struct aw_monitor *monitor = &aw87xxx->monitor;
struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr;
ret = kstrtouint(buf, 0, &enable);
if (ret < 0)
return ret;
AW_DEV_LOGI(aw87xxx->dev, "monitor switch set =%d", enable);
if (!monitor->bin_status) {
AW_DEV_LOGE(aw87xxx->dev, "bin parse faile or not loaded,set invalid");
return -EINVAL;
}
if (enable > 0)
monitor_hdr->monitor_switch = 1;
else
monitor_hdr->monitor_switch = 0;
if (monitor->open_dsp_en && enable) {
monitor_hdr->monitor_switch = 1;
monitor->pre_vmax = AW_VMAX_INIT_VAL;
monitor->first_entry = AW_FIRST_ENTRY;
monitor->timer_cnt = 0;
monitor->vbat_sum = 0;
} else if (monitor->open_dsp_en && !enable) {
monitor_hdr->monitor_switch = 0;
}
return count;
}
static ssize_t aw_attr_get_monitor_time(struct device *dev,
struct device_attribute *attr, char *buf)
{
ssize_t len = 0;
struct aw87xxx *aw87xxx = dev_get_drvdata(dev);
struct aw_monitor *monitor = &aw87xxx->monitor;
struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr;
len += snprintf(buf + len, PAGE_SIZE - len,
"aw_monitor_timer = %d(ms)\n",
monitor_hdr->monitor_time);
return len;
}
static ssize_t aw_attr_set_monitor_time(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
unsigned int timer_val = 0;
int ret = -1;
struct aw87xxx *aw87xxx = dev_get_drvdata(dev);
struct aw_monitor *monitor = &aw87xxx->monitor;
struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr;
ret = kstrtouint(buf, 0, &timer_val);
if (ret < 0)
return ret;
AW_DEV_LOGI(aw87xxx->dev, "input monitor timer=%d(ms)", timer_val);
if (!monitor->bin_status) {
AW_DEV_LOGE(aw87xxx->dev, "bin parse faile or not loaded,set invalid");
return -EINVAL;
}
if (timer_val != monitor_hdr->monitor_time)
monitor_hdr->monitor_time = timer_val;
else
AW_DEV_LOGI(aw87xxx->dev, "no_change monitor_time");
return count;
}
static ssize_t aw_attr_get_monitor_count(struct device *dev,
struct device_attribute *attr, char *buf)
{
ssize_t len = 0;
struct aw87xxx *aw87xxx = dev_get_drvdata(dev);
struct aw_monitor *monitor = &aw87xxx->monitor;
struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr;
len += snprintf(buf + len, PAGE_SIZE - len,
"aw_monitor_count = %d\n",
monitor_hdr->monitor_count);
return len;
}
static ssize_t aw_attr_set_monitor_count(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
unsigned int monitor_count = 0;
int ret = -1;
struct aw87xxx *aw87xxx = dev_get_drvdata(dev);
struct aw_monitor *monitor = &aw87xxx->monitor;
struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr;
ret = kstrtouint(buf, 0, &monitor_count);
if (ret < 0)
return ret;
AW_DEV_LOGI(aw87xxx->dev, "input monitor count=%d", monitor_count);
if (!monitor->bin_status) {
AW_DEV_LOGE(aw87xxx->dev, "bin parse faile or not loaded,set invalid");
return -EINVAL;
}
if (monitor_count != monitor_hdr->monitor_count)
monitor_hdr->monitor_count = monitor_count;
else
AW_DEV_LOGI(aw87xxx->dev, "no_change monitor_count");
return count;
}
static ssize_t aw_attr_get_rx(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct aw87xxx *aw87xxx = dev_get_drvdata(dev);
struct aw_monitor *monitor = &aw87xxx->monitor;
ssize_t len = 0;
int ret = -1;
uint32_t enable = 0;
if (monitor->open_dsp_en) {
ret = aw_dsp_get_rx_module_enable(&enable);
if (ret) {
AW_DEV_LOGE(aw87xxx->dev, "dsp_msg error, ret=%d", ret);
return ret;
}
len += snprintf(buf + len, PAGE_SIZE - len,
"aw87xxx rx: %d\n", enable);
} else {
len += snprintf(buf + len, PAGE_SIZE - len,
"command is invalid\n");
}
return len;
}
static ssize_t aw_attr_set_rx(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct aw87xxx *aw87xxx = dev_get_drvdata(dev);
struct aw_monitor *monitor = &aw87xxx->monitor;
int ret = -1;
uint32_t enable;
ret = kstrtouint(buf, 0, &enable);
if (ret < 0)
return ret;
if (monitor->open_dsp_en) {
AW_DEV_LOGI(aw87xxx->dev, "set rx enable=%d", enable);
ret = aw_dsp_set_rx_module_enable(enable);
if (ret < 0) {
AW_DEV_LOGE(aw87xxx->dev, "dsp_msg error, ret=%d",
ret);
return ret;
}
} else {
AW_DEV_LOGE(aw87xxx->dev, "command is invalid");
return -EINVAL;
}
return count;
}
static DEVICE_ATTR(esd_enable, S_IWUSR | S_IRUGO,
aw_attr_get_esd_enable, aw_attr_set_esd_enable);
static DEVICE_ATTR(vbat, S_IWUSR | S_IRUGO,
aw_attr_get_vbat, aw_attr_set_vbat);
static DEVICE_ATTR(vmax, S_IWUSR | S_IRUGO,
aw_attr_get_vmax, aw_attr_set_vmax);
static DEVICE_ATTR(monitor_switch, S_IWUSR | S_IRUGO,
aw_attr_get_monitor_switch, aw_attr_set_monitor_switch);
static DEVICE_ATTR(monitor_time, S_IWUSR | S_IRUGO,
aw_attr_get_monitor_time, aw_attr_set_monitor_time);
static DEVICE_ATTR(monitor_count, S_IWUSR | S_IRUGO,
aw_attr_get_monitor_count, aw_attr_set_monitor_count);
static DEVICE_ATTR(rx, S_IWUSR | S_IRUGO,
aw_attr_get_rx, aw_attr_set_rx);
static struct attribute *aw_monitor_vol_adjust[] = {
&dev_attr_esd_enable.attr,
&dev_attr_vbat.attr,
&dev_attr_vmax.attr,
NULL
};
static struct attribute_group aw_monitor_vol_adjust_group = {
.attrs = aw_monitor_vol_adjust,
};
static struct attribute *aw_monitor_control[] = {
&dev_attr_monitor_switch.attr,
&dev_attr_monitor_time.attr,
&dev_attr_monitor_count.attr,
&dev_attr_rx.attr,
NULL
};
static struct attribute_group aw_monitor_control_group = {
.attrs = aw_monitor_control,
};
/***************************************************************************
*
* aw87xxx monitor init
*
***************************************************************************/
static void aw_monitor_dtsi_parse(struct device *dev,
struct aw_monitor *monitor,
struct device_node *dev_node)
{
int ret = -1;
const char *esd_enable;
ret = of_property_read_string(dev_node, "esd-enable", &esd_enable);
if (ret < 0) {
AW_DEV_LOGI(dev, "esd_enable parse failed, user default[disable]");
monitor->esd_enable = AW_ESD_DISABLE;
} else {
if (!strcmp(esd_enable, "true"))
monitor->esd_enable = AW_ESD_ENABLE;
else
monitor->esd_enable = AW_ESD_DISABLE;
AW_DEV_LOGI(dev, "parse esd-enable=[%s]",
monitor->esd_enable ? "true" : "false");
}
}
void aw_monitor_init(struct device *dev, struct aw_monitor *monitor,
struct device_node *dev_node)
{
int ret = -1;
struct aw87xxx *aw87xxx =
container_of(monitor, struct aw87xxx, monitor);
monitor->dev_index = aw87xxx->dev_index;
monitor->monitor_hdr.monitor_time = AW_DEFAULT_MONITOR_TIME;
aw_monitor_dtsi_parse(dev, monitor, dev_node);
/* get platform open dsp type */
monitor->open_dsp_en = aw_dsp_isEnable();
ret = sysfs_create_group(&dev->kobj, &aw_monitor_vol_adjust_group);
if (ret < 0)
AW_DEV_LOGE(dev, "failed to create monitor vol_adjust sysfs nodes");
INIT_DELAYED_WORK(&monitor->with_dsp_work, aw_monitor_work_func);
if (monitor->open_dsp_en) {
ret = sysfs_create_group(&dev->kobj, &aw_monitor_control_group);
if (ret < 0)
AW_DEV_LOGE(dev, "failed to create monitor dsp control sysfs nodes");
}
if (!ret)
AW_DEV_LOGI(dev, "monitor init succeed");
}
void aw_monitor_exit(struct aw_monitor *monitor)
{
struct aw87xxx *aw87xxx =
container_of(monitor, struct aw87xxx, monitor);
/*rm attr node*/
sysfs_remove_group(&aw87xxx->dev->kobj,
&aw_monitor_vol_adjust_group);
aw_monitor_stop(monitor);
if (monitor->open_dsp_en) {
sysfs_remove_group(&aw87xxx->dev->kobj,
&aw_monitor_control_group);
}
}