blob: b7da02ecd1cd12a8a2200dda1405f9bf3bfd5d77 [file] [edit]
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* include/linux/mfd/samsung/s2mpg1415-meter.h
*
* Copyright (C) 2022 Samsung Electronics
*
* header file including shared meter functions of s2mpg1415
*/
#ifndef __LINUX_MFD_S2MPG1415_METER_H
#define __LINUX_MFD_S2MPG1415_METER_H
#include <linux/i2c.h>
#include <linux/mfd/samsung/s2mpg1415.h>
#include "s2mpg1415-register.h"
#include <linux/mfd/samsung/s2mpg14-register.h>
#include <linux/mfd/samsung/s2mpg15-register.h>
#include <linux/fs.h>
#define ADDRESS_AT(id, suffix) \
((id) == ID_S2MPG14 ? S2MPG14_METER_##suffix : S2MPG15_METER_##suffix)
static int s2mpg1415_meter_get_acquisition_time_us(enum s2mpg1415_int_samp_rate samp_rate_sel)
{
/* The internal data and sample count are not updated at the same timing
* on Pro due to the duty cycling function, and the ASYNC_RD will depend
* on internal sampling rate.
* So at least waiting for internal rail to complete a sampling will be
* necessary to prevent the mismatch of irregular timing. (b/209886118)
*/
switch (samp_rate_sel) {
case INT_7P_8125HZ:
return INT_7P_8125HZ_ACQUISITION_TIME_US;
case INT_15P_625HZ:
return INT_15P_625HZ_ACQUISITION_TIME_US;
case INT_31P_25HZ:
return INT_31P_25HZ_ACQUISITION_TIME_US;
case INT_62P_5HZ:
return INT_62P_5HZ_ACQUISITION_TIME_US;
case INT_125HZ:
return INT_125HZ_ACQUISITION_TIME_US;
case INT_250HZ:
return INT_250HZ_ACQUISITION_TIME_US;
case INT_1000HZ:
return INT_1000HZ_ACQUISITION_TIME_US;
case INT_FREQ_COUNT:
case INT_FREQ_NONE:
return 0;
};
return 0;
}
#define ACQUISITION_TIME_DIVISOR 16
static int s2mpg1415_meter_set_async_blocking(enum s2mpg1415_id id,
struct i2c_client *i2c,
u64 *timestamp_capture,
enum s2mpg1415_int_samp_rate samp_rate_sel)
{
u8 val = 0xFF;
int ret;
u8 reg = ADDRESS_AT(id, CTRL2);
const u32 acquisition_time_us =
s2mpg1415_meter_get_acquisition_time_us(samp_rate_sel);
const u32 min_acquisition_time_us = acquisition_time_us /
ACQUISITION_TIME_DIVISOR;
int acquisition_delay_count = 0;
/* When 1 is written into ASYNC_RD bit, */
/* transfer the accumulator data to readable registers->self-cleared */
ret = s2mpg1415_update_reg(id, i2c, reg, ASYNC_RD_MASK, ASYNC_RD_MASK);
if (ret != 0) {
/* Immediate return if failed */
return ret;
}
if (timestamp_capture)
*timestamp_capture = ktime_get_boottime_ns();
/* Verify if acquisition is already complete before a polled delay.
* Return immediately to reduce refresh time if so.
*/
ret = s2mpg1415_read_reg(id, i2c, reg, &val);
if (ret == 0 && (val & ASYNC_RD_MASK) == 0x00)
return ret; /* Read success */
/* Reading has failed OR we sampled during acquisition, so wait the
* acquisition time and return based on the values read.
*/
do {
usleep_range(min_acquisition_time_us,
min_acquisition_time_us + 100);
ret = s2mpg1415_read_reg(id, i2c, reg, &val);
if (ret != 0 || (val & ASYNC_RD_MASK) == 0x00)
return ret;
acquisition_delay_count++;
} while (acquisition_delay_count < ACQUISITION_TIME_DIVISOR);
pr_err("odpm: acquisition_time_us: %d not enough\n", acquisition_time_us);
return -1; /* ASYNC value has not changed */
}
static inline ssize_t s2mpg1415_meter_format_channel(char *buf, ssize_t count,
int ch, const char *name,
const char *units,
u64 acc_data,
u32 resolution,
u32 acc_count)
{
/* Note: resolution is in iq30
* --> Convert it back to a human readable decimal value
*/
const u32 one_billion = 1000000000;
u64 resolution_max = _IQ30_to_int((u64)resolution * one_billion);
return scnprintf(buf + count, PAGE_SIZE - count,
"CH%d[%s]: 0x%016llx * %lld.%09llu / 0x%08x %s\n", ch, name,
acc_data, resolution_max / one_billion,
resolution_max % one_billion, acc_count, units);
}
static inline int s2mpg1415_meter_set_buck_channel_en(enum s2mpg1415_id id,
struct i2c_client *i2c,
u8 *channels,
int num_bytes)
{
if (num_bytes > S2MPG1415_METER_BUCKEN_BUF)
return -EINVAL;
return s2mpg1415_bulk_write(id, i2c, ADDRESS_AT(id, BUCKEN1),
num_bytes, channels);
}
static inline int s2mpg1415_meter_set_ext_channel_en(enum s2mpg1415_id id,
struct i2c_client *i2c,
u8 channels)
{
return s2mpg1415_update_reg(id, i2c, ADDRESS_AT(id, CTRL2),
channels << EXT_METER_CHANNEL_EN_OFFSET,
EXT_METER_CHANNEL_EN_MASK);
}
static inline int s2mpg1415_meter_set_int_samp_rate(enum s2mpg1415_id id,
struct i2c_client *i2c,
enum s2mpg1415_int_samp_rate samp_rate_sel)
{
return s2mpg1415_update_reg(id, i2c, ADDRESS_AT(id, CTRL1),
samp_rate_sel << INT_SAMP_RATE_SHIFT,
INT_SAMP_RATE_MASK);
}
static inline int s2mpg1415_meter_set_ext_samp_rate(enum s2mpg1415_id id,
struct i2c_client *i2c,
enum s2mpg1415_ext_samp_rate samp_rate_sel)
{
return s2mpg1415_update_reg(id, i2c, ADDRESS_AT(id, CTRL2),
samp_rate_sel, EXT_SAMP_RATE_MASK);
}
static inline int s2mpg1415_meter_set_lpf_coefficient(enum s2mpg1415_id id,
struct i2c_client *i2c,
int ch,
u32 val)
{
if (ch >= S2MPG1415_METER_CHANNEL_MAX)
return -EINVAL;
return s2mpg1415_write_reg(id, i2c, ADDRESS_AT(id, LPF_C0_0) +
ch, val);
}
static inline void s2mpg1415_meter_set_mode(enum s2mpg1415_id id,
struct i2c_client *i2c,
enum s2mpg1415_meter_mode mode,
bool is_acc_mode)
{
int mode_addr = is_acc_mode ? ADDRESS_AT(id, CTRL4) : ADDRESS_AT(id, CTRL6);
switch (mode) {
case S2MPG1415_METER_POWER:
s2mpg1415_write_reg(id, i2c, mode_addr, 0x00);
s2mpg1415_update_reg(id, i2c, mode_addr + 1,
0x00, /* mask= */ 0x0F);
break;
case S2MPG1415_METER_CURRENT:
s2mpg1415_write_reg(id, i2c, mode_addr, 0xFF);
s2mpg1415_update_reg(id, i2c, mode_addr + 1,
0x0F, /* mask= */ 0x0F);
break;
}
}
static inline void s2mpg1415_meter_set_acc_mode(enum s2mpg1415_id id,
struct i2c_client *i2c,
enum s2mpg1415_meter_mode mode)
{
s2mpg1415_meter_set_mode(id, i2c, mode, /* is_acc_mode= */ true);
}
static inline void s2mpg1415_meter_set_lpf_mode(enum s2mpg1415_id id,
struct i2c_client *i2c,
enum s2mpg1415_meter_mode mode)
{
s2mpg1415_meter_set_mode(id, i2c, mode, /* is_acc_mode= */ false);
}
static inline void s2mpg1415_meter_read_acc_data_reg(enum s2mpg1415_id id,
struct i2c_client *i2c,
u64 *data)
{
int i;
u8 buf[S2MPG1415_METER_ACC_BUF];
u8 reg = ADDRESS_AT(id, ACC_DATA_CH0_1); /* first acc data register */
for (i = 0; i < S2MPG1415_METER_CHANNEL_MAX; i++) {
s2mpg1415_bulk_read(id, i2c, reg, S2MPG1415_METER_ACC_BUF, buf);
/* 41 bits of data */
data[i] = ((u64)buf[0] << 0) | ((u64)buf[1] << 8) |
((u64)buf[2] << 16) | ((u64)buf[3] << 24) |
((u64)buf[4] << 32) | (((u64)buf[5] & 0x1) << 40);
reg += S2MPG1415_METER_ACC_BUF;
}
}
static inline void s2mpg1415_meter_read_acc_count(enum s2mpg1415_id id,
struct i2c_client *i2c,
u32 *count)
{
u8 data[S2MPG1415_METER_COUNT_BUF];
u8 reg = ADDRESS_AT(id, ACC_COUNT_1); /* first count register */
s2mpg1415_bulk_read(id, i2c, reg, S2MPG1415_METER_COUNT_BUF, data);
/* ACC_COUNT is 20-bit data */
*count = (data[0] << 0) | (data[1] << 8) | ((data[2] & 0x0F) << 16);
}
static inline void s2mpg1415_meter_read_lpf_data_reg(enum s2mpg1415_id id,
struct i2c_client *i2c,
u32 *data)
{
int i;
u8 buf[S2MPG1415_METER_LPF_BUF];
u8 reg = ADDRESS_AT(id, LPF_DATA_CH0_1); /* first lpf data register */
for (i = 0; i < S2MPG1415_METER_CHANNEL_MAX; i++) {
s2mpg1415_bulk_read(id, i2c, reg, S2MPG1415_METER_LPF_BUF, buf);
/* LPF is 21-bit data */
data[i] = buf[0] + (buf[1] << 8) + ((buf[2] & 0x1F) << 16);
reg += S2MPG1415_METER_LPF_BUF;
}
}
/**
* Load measurement into registers and read measurement from the registers
*
* Note: data must be an array with length S2MPG1415_METER_CHANNEL_MAX
*/
static inline int s2mpg1415_meter_measure_acc(enum s2mpg1415_id id,
struct i2c_client *i2c,
struct mutex *meter_lock,
enum s2mpg1415_meter_mode mode,
u64 *data,
u32 *count,
u64 *timestamp_capture,
enum s2mpg1415_int_samp_rate samp_rate_sel)
{
mutex_lock(meter_lock);
s2mpg1415_meter_set_acc_mode(id, i2c, mode);
s2mpg1415_meter_set_async_blocking(id, i2c,
timestamp_capture,
samp_rate_sel);
if (data)
s2mpg1415_meter_read_acc_data_reg(id, i2c, data);
if (count)
s2mpg1415_meter_read_acc_count(id, i2c, count);
mutex_unlock(meter_lock);
return 0;
}
#define SW_RESET_DELAYTIME_US 2
static inline int s2mpg1415_meter_sw_reset(enum s2mpg1415_id id,
struct i2c_client *i2c)
{
int ret;
ret = s2mpg1415_update_reg(id, i2c, ADDRESS_AT(id, CTRL5), 0x40,
SOFT_RST_MASK);
if (ret != 0)
pr_err("odpm: s2mpg1%d-odpm: failed to update meter_ctrl5 bit_6 to 1\n", id + 4);
usleep_range(SW_RESET_DELAYTIME_US, SW_RESET_DELAYTIME_US + 100);
ret = s2mpg1415_update_reg(id, i2c, ADDRESS_AT(id, CTRL1), 0x01,
METER_EN_MASK);
if (ret != 0)
pr_err("odpm: s2mpg1%d-odpm: failed to update meter_ctrl1 bit_0 to 1\n", id + 4);
return ret;
}
#endif /* __LINUX_MFD_S2MPG1415_METER_H */