Merge "drivers: thermal: qcom: bcl_pmic5: Add BPM feature support"

This commit is contained in:
QCTECMDR Service 2024-07-01 03:40:28 -07:00 committed by Gerrit - the friendly Code Review server
commit bd5f39c0fd
4 changed files with 436 additions and 69 deletions

View File

@ -9,6 +9,7 @@ obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o
obj-$(CONFIG_QCOM_LMH) += lmh.o
obj-$(CONFIG_QTI_CPU_PAUSE_COOLING_DEVICE) += thermal_pause.o
obj-$(CONFIG_QTI_BCL_PMIC5) += bcl_pmic5.o
bcl_pmic5-y += qti_bcl_pmic5.o qti_bcl_stats.o
obj-$(CONFIG_QTI_BCL_SOC_DRIVER) += bcl_soc.o
obj-$(CONFIG_QTI_POLICY_ENGINE_SENSOR) += policy_engine.o

View File

@ -0,0 +1,105 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/sched/clock.h>
#include <linux/thermal.h>
#define BPM_CLR_OFFSET 0xA1
#define MAX_BCL_LVL_COUNT 3
#define BCL_HISTORY_COUNT 10
#define BCL_STATS_NAME_LENGTH 30
enum bcl_dev_type {
BCL_IBAT_LVL0,
BCL_IBAT_LVL1,
BCL_VBAT_LVL0,
BCL_VBAT_LVL1,
BCL_VBAT_LVL2,
BCL_LVL0,
BCL_LVL1,
BCL_LVL2,
BCL_2S_IBAT_LVL0,
BCL_2S_IBAT_LVL1,
BCL_TYPE_MAX,
};
struct bcl_device;
struct bcl_data_history {
uint32_t vbat;
uint32_t ibat;
unsigned long long trigger_ts;
unsigned long long clear_ts;
};
struct bcl_bpm {
int lvl0_cnt;
int lvl1_cnt;
int lvl2_cnt;
int max_ibat;
int sync_vbat;
int min_vbat;
int sync_ibat;
};
struct bcl_lvl_stats {
uint32_t counter;
uint32_t self_cleared_counter;
bool trigger_state;
unsigned long long max_mitig_ts;
unsigned long long max_mitig_latency;
unsigned long long max_duration;
unsigned long long total_duration;
struct mutex stats_lock;
struct bcl_device *bcl_dev;
struct bcl_data_history bcl_history[BCL_HISTORY_COUNT];
};
struct bcl_peripheral_data {
int irq_num;
int status_bit_idx;
long trip_thresh;
int last_val;
struct mutex state_trans_lock;
bool irq_enabled;
enum bcl_dev_type type;
struct thermal_zone_device_ops ops;
struct thermal_zone_device *tz_dev;
struct bcl_device *dev;
};
struct bcl_device {
struct device *dev;
struct regmap *regmap;
uint16_t fg_bcl_addr;
uint8_t dig_major;
uint8_t dig_minor;
uint8_t ana_major;
uint8_t bcl_param_1;
uint8_t bcl_type;
uint8_t enable_bpm;
void *ipc_log;
bool ibat_ccm_enabled;
bool ibat_use_qg_adc;
bool no_bit_shift;
uint32_t ibat_ext_range_factor;
struct mutex stats_lock;
struct bcl_peripheral_data param[BCL_TYPE_MAX];
struct bcl_lvl_stats stats[MAX_BCL_LVL_COUNT];
};
void bcl_stats_init(char *bcl_name, struct bcl_device *bcl_perph,
uint32_t stats_len);
void bcl_update_clear_stats(struct bcl_lvl_stats *bcl_stat);
void bcl_update_trigger_stats(struct bcl_lvl_stats *bcl_stat,
int ibat, int vbat, unsigned long long trigger_ts);
int get_bpm_stats(struct bcl_device *bcl_dev, struct bcl_bpm *bpm_stats);

View File

@ -3,28 +3,24 @@
* Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2024, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#define pr_fmt(fmt) "%s:%s " fmt, KBUILD_MODNAME, __func__
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/kernel.h>
#include <linux/regmap.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/spmi.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/thermal.h>
#include <linux/slab.h>
#include <linux/nvmem-consumer.h>
#include <linux/ipc_logging.h>
#include "thermal_zone_internal.h"
#include "qti_bcl_common.h"
#define BCL_DRIVER_NAME "bcl_pmic5"
#define MAX_BCL_NAME_LENGTH 40
#define BCL_MONITOR_EN 0x46
#define BCL_IRQ_STATUS 0x08
#define BCL_REVISION1 0x0
@ -79,8 +75,21 @@
#define BCL_GEN4_ANA_MAJOR 3
#define BCL_IBAT_COTTID_SCALING 366220
#define BCL_TRIGGER_THRESHOLD 1
#define MAX_PERPH_COUNT 2
#define IPC_LOGPAGES 2
#define IPC_LOGPAGES 10
#define BPM_EN_OFFSET 0xA0
#define BPM_MAX_IBAT_OFFSET 0xA2
#define BPM_SYNC_VBAT_OFFSET 0xA4
#define BPM_MIN_VBAT_OFFSET 0xA6
#define BPM_SYNC_IBAT_OFFSET 0xA8
#define BCL_LVL0_CNT_OFFSET 0xAA
#define BCL_LVL1_CNT_OFFSET 0xAB
#define BCL_LVL2_CNT_OFFSET 0xAC
#define BPM_HOLD 0x81
#define BPM_CLR 0x80
#define EXTEND_BIT 15
#define BCL_IPC(dev, msg, args...) do { \
if ((dev) && (dev)->ipc_log) { \
@ -90,20 +99,6 @@
} \
} while (0)
enum bcl_dev_type {
BCL_IBAT_LVL0,
BCL_IBAT_LVL1,
BCL_VBAT_LVL0,
BCL_VBAT_LVL1,
BCL_VBAT_LVL2,
BCL_LVL0,
BCL_LVL1,
BCL_LVL2,
BCL_2S_IBAT_LVL0,
BCL_2S_IBAT_LVL1,
BCL_TYPE_MAX,
};
static char bcl_int_names[BCL_TYPE_MAX][25] = {
"bcl-ibat-lvl0",
"bcl-ibat-lvl1",
@ -130,38 +125,6 @@ static uint32_t bcl_ibat_ext_ranges[BCL_IBAT_RANGE_MAX] = {
25
};
struct bcl_device;
struct bcl_peripheral_data {
int irq_num;
int status_bit_idx;
long trip_thresh;
int last_val;
struct mutex state_trans_lock;
bool irq_enabled;
enum bcl_dev_type type;
struct thermal_zone_device_ops ops;
struct thermal_zone_device *tz_dev;
struct bcl_device *dev;
};
struct bcl_device {
struct device *dev;
struct regmap *regmap;
uint16_t fg_bcl_addr;
uint8_t dig_major;
uint8_t dig_minor;
uint8_t ana_major;
uint8_t bcl_param_1;
uint8_t bcl_type;
void *ipc_log;
bool ibat_ccm_enabled;
bool ibat_use_qg_adc;
bool no_bit_shift;
uint32_t ibat_ext_range_factor;
struct bcl_peripheral_data param[BCL_TYPE_MAX];
};
static struct bcl_device *bcl_devices[MAX_PERPH_COUNT];
static int bcl_device_ct;
@ -562,27 +525,44 @@ static int bcl_get_trend(struct thermal_zone_device *tz, int trip, enum thermal_
static int bcl_set_lbat(struct thermal_zone_device *tz, int low, int high)
{
uint32_t bcl_lvl = 0;
struct bcl_peripheral_data *bat_data =
(struct bcl_peripheral_data *)tz->devdata;
struct bcl_device *bcl_perph = bat_data->dev;
mutex_lock(&bat_data->state_trans_lock);
if (high == INT_MAX &&
bcl_lvl = bat_data->type - BCL_LVL0;
if (bcl_lvl >= MAX_BCL_LVL_COUNT) {
pr_err("Invalid sensor type level:%d\n", bat_data->type);
mutex_unlock(&bat_data->state_trans_lock);
return -EINVAL;
}
if (high != BCL_TRIGGER_THRESHOLD &&
bat_data->irq_num && bat_data->irq_enabled) {
disable_irq_nosync(bat_data->irq_num);
disable_irq_wake(bat_data->irq_num);
bat_data->irq_enabled = false;
pr_debug("lbat[%d]: disable irq:%d\n",
pr_debug("lbat[%d]: disable irq:%d low: %d high: %d\n",
bat_data->type,
bat_data->irq_num);
} else if (high != INT_MAX &&
bat_data->irq_num,
low, high);
} else if (high == BCL_TRIGGER_THRESHOLD &&
bat_data->irq_num && !bat_data->irq_enabled) {
bcl_update_clear_stats(&bcl_perph->stats[bcl_lvl]);
enable_irq(bat_data->irq_num);
enable_irq_wake(bat_data->irq_num);
bat_data->irq_enabled = true;
pr_debug("lbat[%d]: enable irq:%d\n",
pr_debug("lbat[%d]: enable irq:%d low: %d high: %d\n",
bat_data->type,
bat_data->irq_num);
bat_data->irq_num,
low, high);
BCL_IPC(bcl_perph,
"Irq %d cleared for bcl type %s.Rearm irq.low: %d, high: %d, irq_count: %d\n",
bat_data->irq_num,
bcl_int_names[bat_data->type],
low, high, bcl_perph->stats[bcl_lvl].counter);
}
mutex_unlock(&bat_data->state_trans_lock);
@ -619,32 +599,101 @@ static int bcl_read_lbat(struct thermal_zone_device *tz, int *adc_value)
goto bcl_read_exit;
}
bat_data->last_val = *adc_value;
pr_debug("lbat:%d val:%d\n", bat_data->type,
bat_data->last_val);
pr_debug("lbat:%d irq_status:%d lvl_val:%d\n", bat_data->type,
val, bat_data->last_val);
if (bcl_perph->param[BCL_IBAT_LVL0].tz_dev)
bcl_read_ibat(bcl_perph->param[BCL_IBAT_LVL0].tz_dev, &ibat);
else if (bcl_perph->param[BCL_2S_IBAT_LVL0].tz_dev)
bcl_read_ibat(bcl_perph->param[BCL_2S_IBAT_LVL0].tz_dev, &ibat);
if (bcl_perph->param[BCL_VBAT_LVL0].tz_dev)
bcl_read_vbat_tz(bcl_perph->param[BCL_VBAT_LVL0].tz_dev, &vbat);
BCL_IPC(bcl_perph, "LVLbat:%d val:%d\n", bat_data->type,
bat_data->last_val);
BCL_IPC(bcl_perph, "LVLbat:%d irq_status:%d val:%d\n", bat_data->type,
val, bat_data->last_val);
bcl_read_exit:
return ret;
}
int get_bpm_stats(struct bcl_device *bcl_dev,
struct bcl_bpm *bpm_stats)
{
unsigned int val = 0;
int ret = 0;
mutex_lock(&bcl_dev->stats_lock);
ret = bcl_write_register(bcl_dev, BPM_EN_OFFSET, BPM_HOLD);
if (ret)
goto bpm_exit;
ret = bcl_read_register(bcl_dev, BCL_LVL0_CNT_OFFSET, &bpm_stats->lvl0_cnt);
if (ret)
goto bpm_exit;
ret = bcl_read_register(bcl_dev, BCL_LVL1_CNT_OFFSET, &bpm_stats->lvl1_cnt);
if (ret)
goto bpm_exit;
ret = bcl_read_register(bcl_dev, BCL_LVL2_CNT_OFFSET, &bpm_stats->lvl2_cnt);
if (ret)
goto bpm_exit;
ret = bcl_read_multi_register(bcl_dev, BPM_MAX_IBAT_OFFSET, &val, 2);
if (ret)
goto bpm_exit;
bpm_stats->max_ibat = sign_extend32(val, EXTEND_BIT);
convert_adc_nu_to_mu_val(&bpm_stats->max_ibat,
BCL_IBAT_COTTID_SCALING);
ret = bcl_read_multi_register(bcl_dev, BPM_SYNC_VBAT_OFFSET, &val, 2);
if (ret)
goto bpm_exit;
bpm_stats->sync_vbat = sign_extend32(val, EXTEND_BIT);
convert_adc_nu_to_mu_val(&bpm_stats->sync_vbat,
BCL_VBAT_SCALING_REV5_NV);
ret = bcl_read_multi_register(bcl_dev, BPM_MIN_VBAT_OFFSET, &val, 2);
if (ret)
goto bpm_exit;
bpm_stats->min_vbat = sign_extend32(val, EXTEND_BIT);
convert_adc_nu_to_mu_val(&bpm_stats->min_vbat,
BCL_VBAT_SCALING_REV5_NV);
ret = bcl_read_multi_register(bcl_dev, BPM_SYNC_IBAT_OFFSET, &val, 2);
if (ret)
goto bpm_exit;
bpm_stats->sync_ibat = sign_extend32(val, EXTEND_BIT);
convert_adc_nu_to_mu_val(&bpm_stats->sync_ibat,
BCL_IBAT_COTTID_SCALING);
ret = bcl_write_register(bcl_dev, BPM_EN_OFFSET, BPM_CLR);
if (ret)
goto bpm_exit;
mutex_unlock(&bcl_dev->stats_lock);
pr_debug(
"BPM : lvl0 =%d lvl1 =%d lvl2 =%d ibat_max=%d sync_vbat=%d vbat_min=%d sync_ibat=%d\n",
bpm_stats->lvl0_cnt, bpm_stats->lvl1_cnt, bpm_stats->lvl2_cnt,
bpm_stats->max_ibat, bpm_stats->sync_vbat,
bpm_stats->min_vbat, bpm_stats->sync_ibat);
return 0;
bpm_exit:
mutex_unlock(&bcl_dev->stats_lock);
return ret;
}
static irqreturn_t bcl_handle_irq(int irq, void *data)
{
struct bcl_peripheral_data *perph_data =
(struct bcl_peripheral_data *)data;
unsigned int irq_status = 0;
int ibat = 0, vbat = 0;
uint32_t bcl_lvl = 0;
struct bcl_device *bcl_perph;
struct bcl_bpm bpm_stat;
unsigned long long start_ts = 0, end_ts = 0;
memset(&bpm_stat, 0, sizeof(bpm_stat));
if (!perph_data->tz_dev)
return IRQ_HANDLED;
bcl_perph = perph_data->dev;
bcl_lvl = perph_data->type - BCL_LVL0;
bcl_read_register(bcl_perph, BCL_IRQ_STATUS, &irq_status);
if (bcl_perph->param[BCL_IBAT_LVL0].tz_dev)
bcl_read_ibat(bcl_perph->param[BCL_IBAT_LVL0].tz_dev, &ibat);
@ -653,17 +702,44 @@ static irqreturn_t bcl_handle_irq(int irq, void *data)
if (bcl_perph->param[BCL_VBAT_LVL0].tz_dev)
bcl_read_vbat_tz(bcl_perph->param[BCL_VBAT_LVL0].tz_dev, &vbat);
if (bcl_lvl >= MAX_BCL_LVL_COUNT) {
pr_err("Invalid sensor type level:%d\n", perph_data->type);
return IRQ_HANDLED;
}
if (irq_status & perph_data->status_bit_idx) {
start_ts = sched_clock();
thermal_zone_device_update(perph_data->tz_dev,
THERMAL_TRIP_VIOLATED);
end_ts = sched_clock();
pr_debug(
"Irq:%d triggered for bcl type:%s. status:%u ibat=%d vbat=%d\n",
"Irq:%d triggered for bcl type:%s. status:%u ibat=%d vbat=%d.\n",
irq, bcl_int_names[perph_data->type],
irq_status, ibat, vbat);
BCL_IPC(bcl_perph,
"Irq:%d triggered for bcl type:%s. status:%u ibat=%d vbat=%d\n",
irq, bcl_int_names[perph_data->type],
irq_status, ibat, vbat);
thermal_zone_device_update(perph_data->tz_dev,
THERMAL_TRIP_VIOLATED);
if (bcl_perph->enable_bpm) {
get_bpm_stats(bcl_perph, &bpm_stat);
BCL_IPC(bcl_perph,
"bpm. lvl0:%d lvl1:%d lvl2:%d max_ibat:%d sync_vbat:%d min_vbat:%d sync_ibat:%d\n",
bpm_stat.lvl0_cnt, bpm_stat.lvl1_cnt, bpm_stat.lvl2_cnt,
bpm_stat.max_ibat, bpm_stat.sync_vbat,
bpm_stat.min_vbat, bpm_stat.sync_ibat);
}
bcl_update_trigger_stats(&bcl_perph->stats[bcl_lvl], ibat, vbat, start_ts);
if (bcl_perph->stats[bcl_lvl].max_mitig_latency < (end_ts - start_ts)) {
bcl_perph->stats[bcl_lvl].max_mitig_latency = end_ts - start_ts;
bcl_perph->stats[bcl_lvl].max_mitig_ts = start_ts;
}
if (perph_data->irq_enabled)
++bcl_perph->stats[bcl_lvl].self_cleared_counter;
} else {
++bcl_perph->stats[bcl_lvl].self_cleared_counter;
}
return IRQ_HANDLED;
@ -888,6 +964,7 @@ static int bcl_get_ibat_config(struct platform_device *pdev,
return 0;
}
static void bcl_probe_ibat(struct platform_device *pdev,
struct bcl_device *bcl_perph)
{
@ -1002,8 +1079,8 @@ static int bcl_remove(struct platform_device *pdev)
static int bcl_probe(struct platform_device *pdev)
{
struct bcl_device *bcl_perph = NULL;
char bcl_name[40];
int err = 0;
char bcl_name[MAX_BCL_NAME_LENGTH];
int err = 0, ret = 0;
if (bcl_device_ct >= MAX_PERPH_COUNT) {
dev_err(&pdev->dev, "Max bcl peripheral supported already.\n");
@ -1014,6 +1091,7 @@ static int bcl_probe(struct platform_device *pdev)
if (!bcl_devices[bcl_device_ct])
return -ENOMEM;
bcl_perph = bcl_devices[bcl_device_ct];
mutex_init(&bcl_perph->stats_lock);
bcl_perph->dev = &pdev->dev;
bcl_perph->regmap = dev_get_regmap(pdev->dev.parent, NULL);
@ -1038,9 +1116,13 @@ static int bcl_probe(struct platform_device *pdev)
bcl_probe_lvls(pdev, bcl_perph);
bcl_configure_bcl_peripheral(bcl_perph);
ret = bcl_write_register(bcl_perph, BPM_EN_OFFSET, BIT(7));
if (!ret)
bcl_perph->enable_bpm = 1;
dev_set_drvdata(&pdev->dev, bcl_perph);
snprintf(bcl_name, sizeof(bcl_name), "bcl_0x%04x_%d",
snprintf(bcl_name, MAX_BCL_NAME_LENGTH, "bcl_0x%04x_%d",
bcl_perph->fg_bcl_addr,
bcl_device_ct - 1);
@ -1049,6 +1131,7 @@ static int bcl_probe(struct platform_device *pdev)
if (!bcl_perph->ipc_log)
pr_err("%s: unable to create IPC Logging for %s\n",
__func__, bcl_name);
bcl_stats_init(bcl_name, bcl_perph, MAX_BCL_LVL_COUNT);
return 0;
}

View File

@ -0,0 +1,178 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include "qti_bcl_common.h"
static struct dentry *bcl_stats_parent;
static struct dentry *bcl_dev_parent;
void bcl_update_clear_stats(struct bcl_lvl_stats *bcl_stat)
{
uint32_t iter = 0;
unsigned long long last_duration;
struct bcl_data_history *hist_data;
if (!bcl_stat->trigger_state)
return;
iter = (bcl_stat->counter % BCL_HISTORY_COUNT);
hist_data = &bcl_stat->bcl_history[iter];
hist_data->clear_ts = sched_clock();
last_duration = DIV_ROUND_UP(
hist_data->clear_ts - hist_data->trigger_ts,
NSEC_PER_USEC);
bcl_stat->total_duration += last_duration;
if (last_duration > bcl_stat->max_duration)
bcl_stat->max_duration = last_duration;
bcl_stat->counter++;
bcl_stat->trigger_state = false;
}
void bcl_update_trigger_stats(struct bcl_lvl_stats *bcl_stat, int ibat, int vbat,
unsigned long long trigger_ts)
{
uint32_t iter = 0;
iter = (bcl_stat->counter % BCL_HISTORY_COUNT);
bcl_stat->bcl_history[iter].clear_ts = 0x0;
bcl_stat->bcl_history[iter].trigger_ts = trigger_ts;
bcl_stat->bcl_history[iter].ibat = ibat;
bcl_stat->bcl_history[iter].vbat = vbat;
bcl_stat->trigger_state = true;
}
static int bcl_lvl_show(struct seq_file *s, void *data)
{
struct bcl_lvl_stats *bcl_stat = s->private;
unsigned long long last_duration = 0;
int idx = 0, cur_counter = 0, loop_till = 0;
seq_printf(s, "%-30s: %d\n",
"SW Counter", bcl_stat->counter);
seq_printf(s, "%-30s: %d\n",
"Irq self cleared counter",
bcl_stat->self_cleared_counter);
seq_printf(s, "%-30s: %lu\n",
"Max Mitigation at", bcl_stat->max_mitig_ts);
seq_printf(s, "%-30s: %lu usec\n",
"Max Mitigation latency",
DIV_ROUND_UP(bcl_stat->max_mitig_latency,
NSEC_PER_USEC));
seq_printf(s, "%-30s: %lu usec\n",
"BCL mitigation residency", bcl_stat->max_duration);
seq_printf(s, "%-30s: %lu usec\n",
"Total residency", bcl_stat->total_duration);
seq_printf(s, "Last %d iterations :\n", BCL_HISTORY_COUNT);
seq_printf(s, "%s%10s%10s%15s%15s%16s\n", "idx", "ibat", "vbat",
"trigger_ts", "clear_ts", "duration(usec)");
cur_counter = (bcl_stat->counter % BCL_HISTORY_COUNT);
idx = cur_counter - 1;
loop_till = -2;
/* print history data as stack. latest entry first */
do {
last_duration = 0;
if (idx < 0) {
idx = BCL_HISTORY_COUNT - 1;
loop_till = cur_counter - 1;
continue;
}
if (bcl_stat->bcl_history[idx].clear_ts)
last_duration = DIV_ROUND_UP(
bcl_stat->bcl_history[idx].clear_ts -
bcl_stat->bcl_history[idx].trigger_ts,
NSEC_PER_USEC);
seq_printf(s, "[%d]%10d%10d%15lu%15lu%16lu\n", idx,
bcl_stat->bcl_history[idx].ibat,
bcl_stat->bcl_history[idx].vbat,
bcl_stat->bcl_history[idx].trigger_ts,
bcl_stat->bcl_history[idx].clear_ts,
last_duration);
--idx;
} while (idx > loop_till);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(bcl_lvl);
static int bpm_stats_show(struct seq_file *s, void *data)
{
struct bcl_device *bcl_perph = s->private;
struct bcl_bpm bpm_stat;
if (bcl_perph == NULL) {
pr_err("NULL\n");
return 0;
}
if (!bcl_perph->enable_bpm)
return 0;
memset(&bpm_stat, 0, sizeof(bpm_stat));
get_bpm_stats(bcl_perph, &bpm_stat);
seq_printf(s, "Max Ibat\t: %d \tSynchronus Vbat\t: %d\n",
bpm_stat.max_ibat, bpm_stat.sync_vbat);
seq_printf(s, "Min Vbat\t: %d \tSynchronus Ibat\t: %d\n",
bpm_stat.min_vbat, bpm_stat.sync_ibat);
seq_printf(s, "%-30s: %d\n",
"lvl0 Alarm counter", bpm_stat.lvl0_cnt);
seq_printf(s, "%-30s: %d\n",
"lvl1 Alarm counter", bpm_stat.lvl1_cnt);
seq_printf(s, "%-30s: %d\n",
"lvl2 Alarm counter", bpm_stat.lvl2_cnt);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(bpm_stats);
static int bpm_reset(void *data, u64 val)
{
struct bcl_device *bcl_perph = data;
unsigned int reset = 1;
uint16_t base;
int ret = 0;
if (bcl_perph == NULL) {
pr_err("bcl_perph is NULL\n");
return 0;
}
base = bcl_perph->fg_bcl_addr;
ret = regmap_write(bcl_perph->regmap, (base + BPM_CLR_OFFSET), reset);
if (ret < 0) {
pr_err("Error reading register:0x%04x val:0x%02x err:%d\n",
(base + BPM_CLR_OFFSET), reset, ret);
return ret;
}
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(bpm_reset_fops, NULL, bpm_reset, "%llu\n");
void bcl_stats_init(char *bcl_name, struct bcl_device *bcl_perph, uint32_t stats_len)
{
int idx = 0;
char stats_name[BCL_STATS_NAME_LENGTH];
struct bcl_lvl_stats *bcl_stats = bcl_perph->stats;
bcl_stats_parent = debugfs_lookup("bcl_stats", NULL);
if (bcl_stats_parent == NULL)
bcl_stats_parent = debugfs_create_dir("bcl_stats", NULL);
bcl_dev_parent = debugfs_create_dir(bcl_name, bcl_stats_parent);
for (idx = 0; idx < stats_len; idx++) {
snprintf(stats_name, BCL_STATS_NAME_LENGTH, "lvl%d_stats", idx);
mutex_init(&bcl_stats[idx].stats_lock);
debugfs_create_file(stats_name, 0444, bcl_dev_parent,
&bcl_stats[idx], &bcl_lvl_fops);
}
debugfs_create_file("bpm_stats", 0444, bcl_dev_parent,
bcl_perph, &bpm_stats_fops);
debugfs_create_file("reset", 0200, bcl_dev_parent,
bcl_perph, &bpm_reset_fops);
}