blob: 1d07ec8f99ee056f08564fc90a45cf65b3da7791 [file] [edit]
// SPDX-License-Identifier: GPL-2.0-only
/*
* google_bcl_data_logging.c Google bcl Data Logging driver
*
* Copyright (c) 2023, Google LLC. All rights reserved.
*
*/
#include <linux/threads.h>
#include <linux/time.h>
#if IS_ENABLED(CONFIG_REGULATOR_S2MPG14)
#include <soc/google/odpm.h>
#elif IS_ENABLED(CONFIG_REGULATOR_S2MPG12) || IS_ENABLED(CONFIG_REGULATOR_S2MPG10)
#include <soc/google/odpm-whi.h>
#endif
#include <uapi/linux/sched/types.h>
#include "bcl.h"
void compute_mitigation_modules(struct bcl_device *bcl_dev,
struct bcl_mitigation_conf *mitigation_conf, u32 *odpm_lpf_value)
{
int i;
for (i = 0; i < METER_CHANNEL_MAX; i++) {
if (odpm_lpf_value[i] >= mitigation_conf[i].threshold) {
atomic_or(BIT(mitigation_conf[i].module_id),
&bcl_dev->mitigation_module_ids);
}
}
}
#if IS_ENABLED(CONFIG_SOC_ZUMAPRO)
static void log_ifpmic_power(struct bcl_device *bcl_dev)
{
int idx, ret;
int i = 0;
if (bcl_dev->ifpmic != MAX77779)
return;
ret = bcl_vimon_read(bcl_dev);
if (ret <= 0)
return;
for (idx = 0; idx < ret / VIMON_BYTES_PER_ENTRY; idx = idx + 2) {
bcl_dev->br_stats->vimon_intf.v_data[i] = bcl_dev->vimon_intf.data[idx];
bcl_dev->br_stats->vimon_intf.i_data[i] = bcl_dev->vimon_intf.data[idx + 1];
i++;
}
bcl_dev->br_stats->vimon_intf.count = i;
}
#endif
static void data_logging_main_odpm_lpf_task(struct bcl_device *bcl_dev)
{
struct odpm_info *info = bcl_dev->main_odpm;
if (!info)
return;
#if IS_ENABLED(CONFIG_REGULATOR_S2MPG14)
/* select lpf power mode */
s2mpg1415_meter_set_lpf_mode(info->chip.hw_id, info->i2c, S2MPG1415_METER_POWER);
/* the acquisition time of lpf_data is around 1ms */
s2mpg1415_meter_read_lpf_data_reg(info->chip.hw_id, info->i2c,
(u32 *)bcl_dev->br_stats->main_odpm_lpf.value);
#elif IS_ENABLED(CONFIG_REGULATOR_S2MPG12) || IS_ENABLED(CONFIG_REGULATOR_S2MPG10)
/* select lpf power mode */
s2mpg1x_meter_set_lpf_mode(info->chip.hw_id, info->i2c, S2MPG1X_METER_POWER);
/* the acquisition time of lpf_data is around 1ms */
s2mpg1x_meter_read_lpf_data_reg(info->chip.hw_id, info->i2c,
(u32 *)bcl_dev->br_stats->main_odpm_lpf.value);
#endif
ktime_get_real_ts64((struct timespec64 *)&bcl_dev->br_stats->main_odpm_lpf.time);
compute_mitigation_modules(bcl_dev,
bcl_dev->main_mitigation_conf,
bcl_dev->br_stats->main_odpm_lpf.value);
}
static void data_logging_sub_odpm_lpf_task(struct bcl_device *bcl_dev)
{
struct odpm_info *info = bcl_dev->sub_odpm;
if (!info)
return;
#if IS_ENABLED(CONFIG_REGULATOR_S2MPG14)
/* select lpf power mode */
s2mpg1415_meter_set_lpf_mode(info->chip.hw_id, info->i2c, S2MPG1415_METER_POWER);
/* the acquisition time of lpf_data is around 1ms */
s2mpg1415_meter_read_lpf_data_reg(info->chip.hw_id, info->i2c,
(u32 *)bcl_dev->br_stats->sub_odpm_lpf.value);
#elif IS_ENABLED(CONFIG_REGULATOR_S2MPG12) || IS_ENABLED(CONFIG_REGULATOR_S2MPG10)
/* select lpf power mode */
s2mpg1x_meter_set_lpf_mode(info->chip.hw_id, info->i2c, S2MPG1X_METER_POWER);
/* the acquisition time of lpf_data is around 1ms */
s2mpg1x_meter_read_lpf_data_reg(info->chip.hw_id, info->i2c,
(u32 *)bcl_dev->br_stats->sub_odpm_lpf.value);
#endif
ktime_get_real_ts64((struct timespec64 *)&bcl_dev->br_stats->sub_odpm_lpf.time);
compute_mitigation_modules(bcl_dev,
bcl_dev->sub_mitigation_conf,
bcl_dev->br_stats->sub_odpm_lpf.value);
}
static void google_bcl_write_irq_triggered_event(struct bcl_device *bcl_dev, int idx)
{
ktime_get_real_ts64((struct timespec64 *)&bcl_dev->br_stats->triggered_time);
bcl_dev->br_stats->triggered_idx = idx;
}
static void google_bcl_init_brownout_stats(struct bcl_device *bcl_dev)
{
memset((void *)bcl_dev->br_stats, 0, bcl_dev->br_stats_size);
bcl_dev->br_stats->triggered_idx = TRIGGERED_SOURCE_MAX;
}
void google_bcl_upstream_state(struct bcl_zone *zone, enum MITIGATION_MODE state)
{
struct bcl_device *bcl_dev = zone->parent;
int idx = zone->idx;
if (!bcl_dev->enabled_br_stats)
return;
atomic_inc(&zone->last_triggered.triggered_cnt[state]);
zone->last_triggered.triggered_time[state] = ktime_to_ms(ktime_get());
zone->current_state = state;
if (idx == UVLO1)
sysfs_notify(&bcl_dev->mitigation_dev->kobj, "triggered_state", "uvlo1_triggered");
else if (idx == UVLO2)
sysfs_notify(&bcl_dev->mitigation_dev->kobj, "triggered_state", "uvlo2_triggered");
else if (idx == BATOILO1) {
sysfs_notify(&bcl_dev->mitigation_dev->kobj, "triggered_state", "oilo1_triggered");
#if IS_ENABLED(CONFIG_SOC_ZUMAPRO)
if (state == LIGHT)
log_ifpmic_power(bcl_dev);
#endif
}
else if (idx == BATOILO2)
sysfs_notify(&bcl_dev->mitigation_dev->kobj, "triggered_state", "oilo2_triggered");
else if (idx == SMPL_WARN)
sysfs_notify(&bcl_dev->mitigation_dev->kobj, "triggered_state", "smpl_triggered");
}
void google_bcl_start_data_logging(struct bcl_device *bcl_dev, int idx)
{
if (!bcl_dev->enabled_br_stats)
return;
if (!bcl_dev->data_logging_initialized)
return;
google_bcl_init_brownout_stats(bcl_dev);
google_bcl_write_irq_triggered_event(bcl_dev, idx);
bcl_dev->br_stats->triggered_state =
bcl_dev->zone[bcl_dev->br_stats->triggered_idx]->current_state;
data_logging_main_odpm_lpf_task(bcl_dev);
data_logging_sub_odpm_lpf_task(bcl_dev);
bcl_dev->triggered_idx = idx;
sysfs_notify(&bcl_dev->mitigation_dev->kobj, "br_stats", "triggered_idx");
}
void google_bcl_remove_data_logging(struct bcl_device *bcl_dev)
{
if (bcl_dev->data_logging_initialized)
kfree(bcl_dev->br_stats);
bcl_dev->data_logging_initialized = false;
}
int google_bcl_init_data_logging(struct bcl_device *bcl_dev)
{
bcl_dev->triggered_idx = TRIGGERED_SOURCE_MAX;
bcl_dev->br_stats_size = sizeof(struct brownout_stats);
bcl_dev->br_stats = kmalloc(bcl_dev->br_stats_size, GFP_KERNEL);
if (!bcl_dev->br_stats)
return -ENOMEM;
google_bcl_init_brownout_stats(bcl_dev);
bcl_dev->data_logging_initialized = true;
return 0;
}