1189 lines
30 KiB
C
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);
|
|
}
|
|
}
|
|
|