Merge "drivers: thermal: qcom: bcl_pmic5: Add BPM feature support"
This commit is contained in:
commit
bd5f39c0fd
@ -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
|
||||
|
||||
|
105
drivers/thermal/qcom/qti_bcl_common.h
Normal file
105
drivers/thermal/qcom/qti_bcl_common.h
Normal 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);
|
@ -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;
|
||||
}
|
178
drivers/thermal/qcom/qti_bcl_stats.c
Normal file
178
drivers/thermal/qcom/qti_bcl_stats.c
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user