mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-09 05:39:54 -04:00
Merge "power: qcom: Add QTI QBG(Qualcomm Battery Gauging) driver"
This commit is contained in:
commit
e3e00bfbe0
11 changed files with 2971 additions and 0 deletions
|
|
@ -101,4 +101,16 @@ config SMB358_CHARGER
|
|||
The driver supports charger enable/disable.
|
||||
The driver reports the charger status via the power supply framework.
|
||||
A charger status change triggers an IRQ via the device STAT pin.
|
||||
|
||||
config QTI_QBG
|
||||
tristate "QTI Battery Gauge"
|
||||
depends on MFD_SPMI_PMIC && IIO
|
||||
help
|
||||
Say Y here to enable the Qualcomm Technologies, Inc. Battery Gauge
|
||||
driver which uses the periodic samples of the battery voltage and
|
||||
current to determine the battery state-of-charge (SOC) and supports
|
||||
other battery management features.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called qti-qbg-main.
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -13,3 +13,5 @@ qcom-smb1355-charger-y += smb1355-charger.o pmic-voter.o
|
|||
obj-$(CONFIG_SMB1351_USB_CHARGER) += smb1351-charger.o pmic-voter.o
|
||||
obj-$(CONFIG_SMB1390_CHARGE_PUMP_PSY) += qcom-smb1390-charger.o
|
||||
qcom-smb1390-charger-y += smb1390-charger-psy.o pmic-voter.o
|
||||
obj-$(CONFIG_QTI_QBG) += qti-qbg-main.o
|
||||
qti-qbg-main-y += qti-qbg.o qbg-sdam.o qbg-battery-profile.o battery-profile-loader.o
|
||||
|
|
|
|||
589
drivers/power/supply/qcom/qbg-battery-profile.c
Normal file
589
drivers/power/supply/qcom/qbg-battery-profile.c
Normal file
|
|
@ -0,0 +1,589 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2021 The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "QBG-K: %s: " fmt, __func__
|
||||
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <uapi/linux/qbg-profile.h>
|
||||
|
||||
#include "qbg-battery-profile.h"
|
||||
|
||||
static int table_temperatures[] = { -20, -10, 0, 10, 25, 40, 50 };
|
||||
|
||||
static int qbg_battery_data_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct qbg_battery_data *battery = container_of(inode->i_cdev,
|
||||
struct qbg_battery_data, battery_cdev);
|
||||
|
||||
pr_debug("battery_data device opened\n");
|
||||
file->private_data = battery;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long qbg_battery_data_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct qbg_battery_data *battery = file->private_data;
|
||||
struct battery_config __user *profile_user;
|
||||
struct battery_profile_table bp_table;
|
||||
struct battery_profile_table __user *bp_table_user;
|
||||
struct battery_data_table *table;
|
||||
int rc = 0, table_index, table_type;
|
||||
|
||||
if (!battery->profile_node) {
|
||||
pr_err("Invalid battery profile node\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!arg) {
|
||||
pr_err("Invalid user pointer\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case BPIOCXBP:
|
||||
profile_user = (struct battery_config __user *)arg;
|
||||
|
||||
if (copy_to_user(profile_user, &battery->bp, sizeof(battery->bp))) {
|
||||
pr_err("Failed to copy battery profile to user\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
break;
|
||||
case BPIOCXBPTABLE:
|
||||
bp_table_user = (struct battery_profile_table __user *)arg;
|
||||
|
||||
if (copy_from_user(&bp_table, bp_table_user, sizeof(bp_table))) {
|
||||
pr_err("Failed to copy battery_profile_table from user\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
table_index = bp_table.table_index;
|
||||
table_type = bp_table.table_type;
|
||||
|
||||
if ((table_type != CHARGE_TABLE) &&
|
||||
(table_type != DISCHARGE_TABLE))
|
||||
return -EFAULT;
|
||||
|
||||
if (((table_type == CHARGE_TABLE) &&
|
||||
(table_index >= battery->num_ctables)) ||
|
||||
((table_type == DISCHARGE_TABLE) &&
|
||||
(table_index >= battery->num_dtables)))
|
||||
return -EFAULT;
|
||||
|
||||
table = (table_type == CHARGE_TABLE) ?
|
||||
battery->bp_charge_tables[table_index] :
|
||||
battery->bp_discharge_tables[table_index];
|
||||
|
||||
if (copy_to_user(bp_table.table, table, sizeof(*table))) {
|
||||
pr_err("Failed to copy battery profile table to user\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
pr_debug("Copied %s table %d to user\n",
|
||||
table_type == CHARGE_TABLE ? "Charge" : "Discharge", table_index);
|
||||
break;
|
||||
default:
|
||||
pr_err_ratelimited("IOCTL %u not supported\n", cmd);
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int qbg_battery_data_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
pr_debug("battery_data device closed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations qbg_battery_data_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = qbg_battery_data_open,
|
||||
.unlocked_ioctl = qbg_battery_data_ioctl,
|
||||
.compat_ioctl = qbg_battery_data_ioctl,
|
||||
.release = qbg_battery_data_release,
|
||||
};
|
||||
|
||||
#define QBG_PON_TEMPERATURE 25
|
||||
static int qbg_parse_table0(struct device_node *profile_node,
|
||||
char *table_name, struct battery_data_table0 *table)
|
||||
{
|
||||
struct device_node *node;
|
||||
int rc = 0, temperature;
|
||||
|
||||
node = of_find_node_by_name(profile_node, table_name);
|
||||
if (!node) {
|
||||
pr_err("%s not found\n", table_name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
rc = of_property_read_s32(node, "qcom,temperature", &temperature);
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to read %s temperature\n", table_name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (temperature != QBG_PON_TEMPERATURE) {
|
||||
pr_err("Invalid table0 found, temperature:%d\n",
|
||||
temperature);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = of_property_count_elems_of_size(node, "qcom,soc", sizeof(int));
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to get soc-length for %s, rc=%d\n",
|
||||
table_name, rc);
|
||||
goto out;
|
||||
}
|
||||
table->soc_length = rc;
|
||||
|
||||
table->soc = kcalloc(table->soc_length, sizeof(*table->soc), GFP_KERNEL);
|
||||
if (!table->soc) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = of_property_read_u32_array(node, "qcom,soc", table->soc,
|
||||
table->soc_length);
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to read qcom,soc\n");
|
||||
rc = -EINVAL;
|
||||
goto cleanup_soc;
|
||||
}
|
||||
|
||||
rc = of_property_count_elems_of_size(node, "qcom,ocv", sizeof(int));
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to get ocv-length for %s, rc=%d\n",
|
||||
table_name, rc);
|
||||
goto cleanup_soc;
|
||||
}
|
||||
table->ocv_length = rc;
|
||||
|
||||
table->ocv = kcalloc(table->ocv_length, sizeof(*table->ocv), GFP_KERNEL);
|
||||
if (!table->ocv) {
|
||||
rc = -ENOMEM;
|
||||
goto cleanup_soc;
|
||||
}
|
||||
|
||||
rc = of_property_read_u32_array(node, "qcom,ocv", table->ocv,
|
||||
table->ocv_length);
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to read qcom,ocv\n");
|
||||
rc = -EINVAL;
|
||||
goto cleanup_ocv;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup_ocv:
|
||||
kfree(table->ocv);
|
||||
cleanup_soc:
|
||||
kfree(table->soc);
|
||||
out:
|
||||
of_node_put(node);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int qbg_parse_table(struct device_node *profile_node,
|
||||
int index, char *table_name, struct battery_data_table *bp_table)
|
||||
{
|
||||
struct device_node *node;
|
||||
struct property *prop;
|
||||
const __be32 *data;
|
||||
int rc = 0, j, k, temperature;
|
||||
u32 rows, cols;
|
||||
|
||||
node = of_find_node_by_name(profile_node, table_name);
|
||||
if (!node) {
|
||||
pr_err("%s not found\n", table_name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
rc = of_property_read_s32(node, "qcom,temperature", &temperature);
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to read %s temperature\n", table_name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (temperature != table_temperatures[index]) {
|
||||
pr_err("Invalid table at wrong index %d temperature:%d\n",
|
||||
index, temperature);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(node, "qcom,nrows", &rows);
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to read %s\n", table_name);
|
||||
goto out;
|
||||
}
|
||||
bp_table->nrows = rows;
|
||||
|
||||
rc = of_property_read_u32(node, "qcom,ncols", &cols);
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to read %s\n", table_name);
|
||||
goto out;
|
||||
}
|
||||
bp_table->ncols = cols;
|
||||
|
||||
rc = of_property_read_u32_array(node, "qcom,conv-factor",
|
||||
bp_table->unit_conv_factor,
|
||||
MAX_BP_LUT_COLS);
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to read conv-factor\n");
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
prop = of_find_property(node, "qcom,data", NULL);
|
||||
if (!prop) {
|
||||
pr_err("Failed to find lut-data\n");
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
data = prop->value;
|
||||
|
||||
for (j = 0; j < bp_table->nrows; j++) {
|
||||
for (k = 0; k < bp_table->ncols; k++)
|
||||
bp_table->table[j][k] = be32_to_cpup(data++);
|
||||
}
|
||||
|
||||
pr_debug("Profile %s parsed rows=%d cols=%d\n", table_name,
|
||||
bp_table->nrows, bp_table->ncols);
|
||||
out:
|
||||
of_node_put(node);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int qbg_parse_u32_dt_array(struct device_node *node,
|
||||
const char *prop_name, int *buf, int len)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = of_property_count_elems_of_size(node, prop_name, sizeof(u32));
|
||||
if (rc < 0) {
|
||||
pr_err("Property %s not found, rc=%d\n", prop_name, rc);
|
||||
return rc;
|
||||
} else if (rc != len) {
|
||||
pr_err("Incorrect length %d for %s, rc=%d\n", len, prop_name,
|
||||
rc);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = of_property_read_u32_array(node, prop_name, buf, len);
|
||||
if (rc < 0) {
|
||||
pr_err("Error in reading %s, rc=%d\n", prop_name, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qbg_parse_battery_profile(struct qbg_battery_data *battery)
|
||||
{
|
||||
struct device_node *node = battery->profile_node;
|
||||
struct device_node *child;
|
||||
struct battery_config *bp = &battery->bp;
|
||||
char buf[32];
|
||||
const char *battery_name = NULL;
|
||||
int rc, i = 0;
|
||||
u32 temp[2];
|
||||
|
||||
rc = of_property_read_string(node, "qcom,battery-type", &battery_name);
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to get battery type, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
strlcpy(bp->bp_profile_name, battery_name, MAX_PROFILE_NAME_LENGTH);
|
||||
|
||||
rc = of_property_read_u32(node, "qcom,batt-id-kohm", &bp->bp_batt_id);
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to get battery id, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(node, "qcom,capacity", &bp->capacity);
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to get battery capacity, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(node, "qcom,checksum", &bp->bp_checksum);
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to get checksum, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = qbg_parse_u32_dt_array(node, "qcom,soh-range", temp, 2);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (temp[0] > 100 || temp[1] > 100 || (temp[0] > temp[1])) {
|
||||
pr_err("Incorrect SOH range [%d %d]\n", temp[0],
|
||||
temp[1]);
|
||||
return -ERANGE;
|
||||
}
|
||||
bp->soh_range_low = temp[0];
|
||||
bp->soh_range_high = temp[1];
|
||||
|
||||
rc = qbg_parse_u32_dt_array(node, "qcom,battery-impedance", temp, 2);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
bp->normal_impedance = temp[0];
|
||||
bp->aged_impedance = temp[1];
|
||||
|
||||
rc = qbg_parse_u32_dt_array(node, "qcom,battery-capacity", temp, 2);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
bp->normal_capacity = temp[0];
|
||||
bp->aged_capacity = temp[1];
|
||||
|
||||
rc = of_property_read_u32(node, "qcom,recharge-soc-delta",
|
||||
&bp->recharge_soc_delta);
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to get recharege soc delta, rc=%d\n",
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(node, "qcom,recharge-vflt-delta",
|
||||
&bp->recharge_vflt_delta);
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to get recharge vflt delta, rc=%d\n",
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(node, "qcom,recharge-iterm-ma",
|
||||
&bp->recharge_iterm);
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to get recharge iterm, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
for_each_available_child_of_node(battery->profile_node, child) {
|
||||
if (of_node_name_prefix(child, "qcom,bp-c-table"))
|
||||
battery->num_ctables++;
|
||||
else if (of_node_name_prefix(child, "qcom,bp-d-table"))
|
||||
battery->num_dtables++;
|
||||
}
|
||||
|
||||
if (!battery->num_ctables || !battery->num_dtables) {
|
||||
pr_err("ctable or dtable missing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Battery profile contains additional table (table0) */
|
||||
if (battery->num_ctables)
|
||||
battery->num_ctables--;
|
||||
if (battery->num_dtables)
|
||||
battery->num_dtables--;
|
||||
|
||||
battery->bp_discharge_tables = kcalloc(battery->num_dtables,
|
||||
sizeof(*battery->bp_discharge_tables), GFP_KERNEL);
|
||||
if (!battery->bp_discharge_tables)
|
||||
return -ENOMEM;
|
||||
|
||||
battery->bp_charge_tables = kcalloc(battery->num_ctables,
|
||||
sizeof(*battery->bp_charge_tables), GFP_KERNEL);
|
||||
if (!battery->bp_charge_tables)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Parse c-table-0 */
|
||||
scnprintf(buf, sizeof(buf), "qcom,bp-c-table-0");
|
||||
rc = qbg_parse_table0(battery->profile_node, buf, &battery->table0[0]);
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to parse %s, rc=%d\n", buf, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Parse d-table-0 */
|
||||
scnprintf(buf, sizeof(buf), "qcom,bp-d-table-0");
|
||||
rc = qbg_parse_table0(battery->profile_node, buf, &battery->table0[1]);
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to parse %s, rc=%d\n", buf, rc);
|
||||
goto cleanup_ctable;
|
||||
}
|
||||
|
||||
for (i = 0; i < battery->num_dtables; i++) {
|
||||
battery->bp_discharge_tables[i] = kzalloc(
|
||||
sizeof(*battery->bp_discharge_tables[i]), GFP_KERNEL);
|
||||
if (!battery->bp_discharge_tables[i]) {
|
||||
rc = -ENOMEM;
|
||||
goto cleanup_ctable;
|
||||
}
|
||||
|
||||
scnprintf(buf, sizeof(buf), "qcom,bp-d-table-%d", i + 1);
|
||||
|
||||
rc = qbg_parse_table(battery->profile_node, i, buf,
|
||||
battery->bp_discharge_tables[i]);
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to parse %s, rc=%d\n", buf, rc);
|
||||
goto cleanup_dtable;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < battery->num_ctables; i++) {
|
||||
battery->bp_charge_tables[i] = kzalloc(
|
||||
sizeof(*battery->bp_charge_tables[i]), GFP_KERNEL);
|
||||
if (!battery->bp_charge_tables[i]) {
|
||||
rc = -ENOMEM;
|
||||
goto cleanup_ctable;
|
||||
}
|
||||
|
||||
scnprintf(buf, sizeof(buf), "qcom,bp-c-table-%d", i + 1);
|
||||
|
||||
rc = qbg_parse_table(battery->profile_node, i, buf,
|
||||
battery->bp_charge_tables[i]);
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to parse %s, rc=%d\n", buf, rc);
|
||||
goto cleanup_ctable;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup_ctable:
|
||||
for (; i > 0; i--)
|
||||
kfree(battery->bp_charge_tables[i]);
|
||||
i = battery->num_dtables;
|
||||
cleanup_dtable:
|
||||
for (; i > 0; i--)
|
||||
kfree(battery->bp_discharge_tables[i]);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int qbg_batterydata_init(struct device_node *profile_node,
|
||||
struct qbg_battery_data *battery)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
/* char device to access battery-profile data */
|
||||
rc = alloc_chrdev_region(&battery->dev_no, 0, 1, "qbg_battery");
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to allocate chrdev, rc=%d\n", rc);
|
||||
goto free_battery;
|
||||
}
|
||||
|
||||
cdev_init(&battery->battery_cdev, &qbg_battery_data_fops);
|
||||
rc = cdev_add(&battery->battery_cdev, battery->dev_no, 1);
|
||||
if (rc) {
|
||||
pr_err("Failed to add battery_cdev, rc=%d\n", rc);
|
||||
goto unregister_chrdev;
|
||||
}
|
||||
|
||||
battery->battery_class = class_create(THIS_MODULE, "qbg_battery");
|
||||
if (IS_ERR_OR_NULL(battery->battery_class)) {
|
||||
pr_err("Failed to create qbg-battery class (%d)\n",
|
||||
PTR_ERR(battery->battery_class));
|
||||
rc = -ENODEV;
|
||||
goto delete_cdev;
|
||||
}
|
||||
|
||||
battery->battery_device = device_create(battery->battery_class, NULL,
|
||||
battery->dev_no, NULL,
|
||||
"qbg_battery");
|
||||
if (IS_ERR_OR_NULL(battery->battery_device)) {
|
||||
pr_err("Failed to create battery_device device (%d)\n",
|
||||
PTR_ERR(battery->battery_device));
|
||||
rc = -ENODEV;
|
||||
goto destroy_class;
|
||||
}
|
||||
|
||||
battery->profile_node = profile_node;
|
||||
|
||||
/* parse the battery profile */
|
||||
rc = qbg_parse_battery_profile(battery);
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to parse battery profile, rc=%d\n", rc);
|
||||
goto destroy_device;
|
||||
}
|
||||
|
||||
pr_info("QBG Battery-profile loaded, id:%d name:%s\n",
|
||||
battery->bp.bp_batt_id, battery->bp.bp_profile_name);
|
||||
|
||||
return 0;
|
||||
|
||||
destroy_device:
|
||||
device_destroy(battery->battery_class, battery->dev_no);
|
||||
destroy_class:
|
||||
class_destroy(battery->battery_class);
|
||||
delete_cdev:
|
||||
cdev_del(&battery->battery_cdev);
|
||||
unregister_chrdev:
|
||||
unregister_chrdev_region(battery->dev_no, 1);
|
||||
free_battery:
|
||||
kfree(battery);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void qbg_batterydata_exit(struct qbg_battery_data *battery)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!battery) {
|
||||
pr_err("Battery cannot be null\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* unregister the device node */
|
||||
device_destroy(battery->battery_class, battery->dev_no);
|
||||
class_destroy(battery->battery_class);
|
||||
cdev_del(&battery->battery_cdev);
|
||||
unregister_chrdev_region(battery->dev_no, 1);
|
||||
|
||||
/* delete all the battery profile memory */
|
||||
for (i = 0; i < battery->num_ctables; i++)
|
||||
kfree(battery->bp_charge_tables[i]);
|
||||
|
||||
for (i = 0; i < battery->num_dtables; i++)
|
||||
kfree(battery->bp_discharge_tables[i]);
|
||||
}
|
||||
|
||||
int qbg_lookup_soc_ocv(struct qbg_battery_data *battery, int *pon_soc, int ocv, bool charging)
|
||||
{
|
||||
struct battery_data_table0 *lut;
|
||||
int i;
|
||||
|
||||
*pon_soc = -EINVAL;
|
||||
|
||||
lut = charging ? &battery->table0[0] : &battery->table0[1];
|
||||
for (i = 0; i < lut->ocv_length; i++) {
|
||||
if (ocv == lut->ocv[i]) {
|
||||
*pon_soc = lut->soc[i];
|
||||
break;
|
||||
} else if (is_between(lut->ocv[i], lut->ocv[i+1], ocv)) {
|
||||
*pon_soc = (lut->soc[i] + lut->soc[i+1]) / 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (*pon_soc == -EINVAL) {
|
||||
pr_debug("%d ocv wasn't found in the LUT returning 100%\n", ocv);
|
||||
*pon_soc = 10000;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
55
drivers/power/supply/qcom/qbg-battery-profile.h
Normal file
55
drivers/power/supply/qcom/qbg-battery-profile.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2021 The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __QBG_BATTERY_PROFILE_H__
|
||||
#define __QBG_BATTERY_PROFILE_H__
|
||||
|
||||
#define is_between(left, right, value) \
|
||||
(((left) >= (right) && (left) >= (value) \
|
||||
&& (value) >= (right)) \
|
||||
|| ((left) <= (right) && (left) <= (value) \
|
||||
&& (value) <= (right)))
|
||||
|
||||
struct battery_data_table0 {
|
||||
int soc_length;
|
||||
int ocv_length;
|
||||
int *soc;
|
||||
int *ocv;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct qbg_battery_data - Structure for QBG battery data
|
||||
* @dev_no: Device number for QBG battery char device
|
||||
* @profile_node: Pointer to devicetree node handle of profile
|
||||
* @battery_class: Pointer to battery class
|
||||
* @battery_device: Pointer to battery class device
|
||||
* @battery_cdev: QBG battery char device
|
||||
* @bp: QBG battery configuration
|
||||
* @bp_charge_tables: Charge tables in QBG battery profile
|
||||
* @bp_discharge_tables: Discharge tables in QBG battery profile
|
||||
* @table0: Two tables for PON OCV to SOC mapping
|
||||
* @num_ctables: Number of charge tables
|
||||
* @num_dtables: Number of discharge tables
|
||||
*/
|
||||
|
||||
struct qbg_battery_data {
|
||||
dev_t dev_no;
|
||||
struct device_node *profile_node;
|
||||
struct class *battery_class;
|
||||
struct device *battery_device;
|
||||
struct cdev battery_cdev;
|
||||
struct battery_config bp;
|
||||
struct battery_data_table **bp_charge_tables;
|
||||
struct battery_data_table **bp_discharge_tables;
|
||||
struct battery_data_table0 table0[2];
|
||||
int num_ctables;
|
||||
int num_dtables;
|
||||
};
|
||||
|
||||
int qbg_batterydata_init(struct device_node *node,
|
||||
struct qbg_battery_data *battery);
|
||||
void qbg_batterydata_exit(struct qbg_battery_data *battery);
|
||||
int qbg_lookup_soc_ocv(struct qbg_battery_data *battery, int *pon_soc, int ocv, bool charging);
|
||||
#endif /* __QBG_BATTERY_PROFILE_H__ */
|
||||
178
drivers/power/supply/qcom/qbg-core.h
Normal file
178
drivers/power/supply/qcom/qbg-core.h
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2021 The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __QBG_CORE_H__
|
||||
#define __QBG_CORE_H__
|
||||
|
||||
#define qbg_dbg(chip, reason, fmt, ...) \
|
||||
do { \
|
||||
if (*chip->debug_mask & (reason)) \
|
||||
pr_err(fmt, ##__VA_ARGS__); \
|
||||
else \
|
||||
pr_debug(fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
enum debug_mask {
|
||||
QBG_DEBUG_BUS_READ = BIT(0),
|
||||
QBG_DEBUG_BUS_WRITE = BIT(1),
|
||||
QBG_DEBUG_FIFO = BIT(2),
|
||||
QBG_DEBUG_IRQ = BIT(3),
|
||||
QBG_DEBUG_DEVICE = BIT(4),
|
||||
QBG_DEBUG_PROFILE = BIT(5),
|
||||
QBG_DEBUG_SOC = BIT(6),
|
||||
QBG_DEBUG_SDAM = BIT(7),
|
||||
QBG_DEBUG_STATUS = BIT(8),
|
||||
QBG_DEBUG_PON = BIT(9),
|
||||
};
|
||||
|
||||
enum qbg_sdam {
|
||||
SDAM_CTRL0 = 0,
|
||||
SDAM_CTRL1,
|
||||
SDAM_DATA0,
|
||||
SDAM_DATA1,
|
||||
SDAM_DATA2,
|
||||
SDAM_DATA3,
|
||||
SDAM_DATA4,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct qti_qbg - Structure for QTI QBG device
|
||||
* @dev: Pointer to QBG device structure
|
||||
* @regmap: Pointer to regmap structure
|
||||
* @qbg_psy: Pointer to QBG power supply
|
||||
* @batt_psy: Pointer to Battery power supply
|
||||
* @qbg_class: Pointer to QBG class
|
||||
* @qbg_device: Pointer to QBG device
|
||||
* @qbg_cdev: Member for QBG char device
|
||||
* @dev_no: Device number for QBG char device
|
||||
* @batt_node: Pointer to battery device node
|
||||
* @indio_dev: Pointer to QBG IIO device
|
||||
* @iio_chan: Pointer to QBG IIO channels
|
||||
* @sdam: Pointer to multiple QBG SDAMs
|
||||
* @fifo: QBG FIFO data
|
||||
* @essential_params: QBG essential params
|
||||
* @status_change_work: Power supply status change work
|
||||
* @udata_work: User space data change work
|
||||
* @nb: Power supply notifier block
|
||||
* @kdata: QBG Kernel space data structure
|
||||
* @udata: QBG user space data structure
|
||||
* @battery: Pointer to QBG battery data structure
|
||||
* @fifo_lock: Lock for reading FIFO data
|
||||
* @data_lock: Lock for reading kdata from QBG char device
|
||||
* @batt_id_chan: IIO channel to read battery ID
|
||||
* @batt_temp_chan: IIO channel to read battery temperature
|
||||
* @rtc: RTC device to read real time
|
||||
* @last_fast_char_time: Timestamp of last time QBG in fast char mode
|
||||
* @qbg_wait_q: Wait queue for reads to QBG char device
|
||||
* @irq_name: QBG interrupt name
|
||||
* @batt_type_str: String array denoting battery type
|
||||
* @irq: QBG irq number
|
||||
* @base: Base address of QBG HW
|
||||
* @num_sdams: Number of sdams used for QBG
|
||||
* @batt_id_ohm: Battery resistance in ohms
|
||||
* @debug_mask: Debug mask to enable/disable debug prints
|
||||
* @pon_ocv: Power-on OCV of QBG device
|
||||
* @pon_ibat: Power-on current of QBG device
|
||||
* @pon_soc: Power-on SOC of QBG device
|
||||
* @soc: Monotonic SOC of QBG device
|
||||
* @batt_soc: Battery SOC
|
||||
* @sys_soc: Battery system SOC
|
||||
* @esr: Battery equivalent series resistance
|
||||
* @ocv_uv: Battery open circuit voltage
|
||||
* @voltage_now: Battery voltage
|
||||
* @current_now: Battery current
|
||||
* @tbat: Battery temperature
|
||||
* @charge_cycle_count: Battery charge cycle count
|
||||
* @nominal_capacity: Battery nominal capacity
|
||||
* @learned_capacity: Battery learned capacity
|
||||
* @ttf: Time to full
|
||||
* @tte: Time to empty
|
||||
* @soh: Battery state of health
|
||||
* @charge_type: Charging type
|
||||
* @float_volt_uv: Battery maximum voltage
|
||||
* @fastchg_curr_ma: Battery fast charge current
|
||||
* @vbat_cutoff_mv: Battery cutoff voltage
|
||||
* @ibat_cutoff_ma: Battery cutoff current
|
||||
* @vph_min_mv: Battery minimum power
|
||||
* @iterm_ma: Charge Termination current
|
||||
* @rconn_mohm: Battery connector resistance
|
||||
* @previous_ep_time: Previous timestamp when essential params stored
|
||||
* @current_time: Current time stamp
|
||||
* @profile_loaded: Flag to indicated battery profile is loaded
|
||||
* @battery_missing: Flag to indicate battery is missing
|
||||
* @data_ready: Flag to indicate QBG data is ready
|
||||
* @in_fast_char: Flag to indicate QBG is in fast char mode
|
||||
*/
|
||||
struct qti_qbg {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct power_supply *qbg_psy;
|
||||
struct power_supply *batt_psy;
|
||||
struct class *qbg_class;
|
||||
struct device *qbg_device;
|
||||
struct cdev qbg_cdev;
|
||||
dev_t dev_no;
|
||||
struct device_node *batt_node;
|
||||
struct iio_dev *indio_dev;
|
||||
struct iio_chan_spec *iio_chan;
|
||||
struct nvmem_device **sdam;
|
||||
struct fifo_data fifo[MAX_FIFO_COUNT];
|
||||
struct qbg_essential_params essential_params;
|
||||
struct work_struct status_change_work;
|
||||
struct work_struct udata_work;
|
||||
struct notifier_block nb;
|
||||
struct qbg_kernel_data kdata;
|
||||
struct qbg_user_data udata;
|
||||
struct qbg_battery_data *battery;
|
||||
struct mutex fifo_lock;
|
||||
struct mutex data_lock;
|
||||
struct iio_channel *batt_id_chan;
|
||||
struct iio_channel *batt_temp_chan;
|
||||
struct rtc_device *rtc;
|
||||
ktime_t last_fast_char_time;
|
||||
wait_queue_head_t qbg_wait_q;
|
||||
const char *irq_name;
|
||||
const char *batt_type_str;
|
||||
int irq;
|
||||
u32 base;
|
||||
u32 sdam_base;
|
||||
u32 num_sdams;
|
||||
u32 num_data_sdams;
|
||||
u32 batt_id_ohm;
|
||||
u32 *debug_mask;
|
||||
int pon_ocv;
|
||||
int pon_ibat;
|
||||
int pon_tbat;
|
||||
int pon_soc;
|
||||
int soc;
|
||||
int batt_soc;
|
||||
int sys_soc;
|
||||
int esr;
|
||||
int ocv_uv;
|
||||
int voltage_now;
|
||||
int current_now;
|
||||
int tbat;
|
||||
int charge_cycle_count;
|
||||
int nominal_capacity;
|
||||
int learned_capacity;
|
||||
int ttf;
|
||||
int tte;
|
||||
int soh;
|
||||
int charge_type;
|
||||
int float_volt_uv;
|
||||
int fastchg_curr_ma;
|
||||
int vbat_cutoff_mv;
|
||||
int ibat_cutoff_ma;
|
||||
int vph_min_mv;
|
||||
int iterm_ma;
|
||||
int rconn_mohm;
|
||||
unsigned long previous_ep_time;
|
||||
unsigned long current_time;
|
||||
bool profile_loaded;
|
||||
bool battery_missing;
|
||||
bool data_ready;
|
||||
bool in_fast_char;
|
||||
};
|
||||
#endif /* __QBG_CORE_H__ */
|
||||
56
drivers/power/supply/qcom/qbg-iio.h
Normal file
56
drivers/power/supply/qcom/qbg-iio.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2021 The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __QBG_IIO_H__
|
||||
#define __QBG_IIO_H__
|
||||
|
||||
struct qbg_iio_channels {
|
||||
const char *datasheet_name;
|
||||
int channel_num;
|
||||
enum iio_chan_type type;
|
||||
long info_mask;
|
||||
};
|
||||
|
||||
#define QBG_IIO_CHAN(_name, _num, _type, _mask) \
|
||||
{ \
|
||||
.datasheet_name = _name, \
|
||||
.channel_num = _num, \
|
||||
.type = _type, \
|
||||
.info_mask = _mask, \
|
||||
},
|
||||
|
||||
#define QBG_CHAN_ENERGY(_name, _num) \
|
||||
QBG_IIO_CHAN(_name, _num, IIO_ENERGY, \
|
||||
BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#define QBG_CHAN_TEMP(_name, _num) \
|
||||
QBG_IIO_CHAN(_name, _num, IIO_TEMP, \
|
||||
BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#define QBG_CHAN_VOLT(_name, _num) \
|
||||
QBG_IIO_CHAN(_name, _num, IIO_VOLTAGE, \
|
||||
BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#define QBG_CHAN_TSTAMP(_name, _num) \
|
||||
QBG_IIO_CHAN(_name, _num, IIO_TIMESTAMP, \
|
||||
BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#define QBG_CHAN_RES(_name, _num) \
|
||||
QBG_IIO_CHAN(_name, _num, IIO_RESISTANCE, \
|
||||
BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#define QBG_CHAN_INDEX(_name, _num) \
|
||||
QBG_IIO_CHAN(_name, _num, IIO_INDEX, \
|
||||
BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#define QBG_CHAN_CUR(_name, _num) \
|
||||
QBG_IIO_CHAN(_name, _num, IIO_CURRENT, \
|
||||
BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#define QBG_CHAN_COUNT(_name, _num) \
|
||||
QBG_IIO_CHAN(_name, _num, IIO_COUNT, \
|
||||
BIT(IIO_CHAN_INFO_PROCESSED))
|
||||
|
||||
#endif /* __QBG_IIO_H__ */
|
||||
137
drivers/power/supply/qcom/qbg-sdam.c
Normal file
137
drivers/power/supply/qcom/qbg-sdam.c
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/nvmem-consumer.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <uapi/linux/qbg.h>
|
||||
|
||||
#include "qbg-core.h"
|
||||
#include "qbg-sdam.h"
|
||||
|
||||
int qbg_sdam_read(struct qti_qbg *chip, int offset, u8 *data, int len)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = regmap_bulk_read(chip->regmap, offset, data, len);
|
||||
if (rc < 0)
|
||||
pr_err("Failed to read QBG SDAM offset:%04x rc=%d\n",
|
||||
offset, rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int qbg_sdam_write(struct qti_qbg *chip, int offset, u8 *data, int len)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = regmap_bulk_write(chip->regmap, offset, data, len);
|
||||
if (rc < 0)
|
||||
pr_err("Failed to write QBG SDAM offset:%04x rc=%d\n",
|
||||
offset, rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int qbg_sdam_get_fifo_data(struct qti_qbg *chip, struct fifo_data *fifo,
|
||||
u32 fifo_count)
|
||||
{
|
||||
int rc, i;
|
||||
u32 num_sdams, fifo_data_length, bytes_read = 0, bytes_remaining;
|
||||
|
||||
fifo_data_length = fifo_count * QBG_ONE_FIFO_LENGTH;
|
||||
num_sdams = fifo_data_length / QBG_SDAM_ONE_FIFO_REGION_SIZE;
|
||||
|
||||
if (num_sdams > chip->num_sdams) {
|
||||
pr_err("Fifo count exceeds QBG SDAMs\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
qbg_dbg(chip, QBG_DEBUG_FIFO, "Completely filled sdams=%d\n",
|
||||
num_sdams);
|
||||
|
||||
/* First read SDAMs that are completely filled */
|
||||
for (i = 0; i < num_sdams; i++) {
|
||||
if (!chip->sdam[i]) {
|
||||
pr_err("Number of SDAMs not sufficient to read FIFOs\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
rc = qbg_sdam_read(chip,
|
||||
QBG_SDAM_DATA_START_OFFSET(chip, SDAM_DATA0 + i),
|
||||
(((u8 *)fifo) + bytes_read),
|
||||
QBG_SDAM_ONE_FIFO_REGION_SIZE);
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to read QBG SDAM%d, rc=%d\n", i, rc);
|
||||
return rc;
|
||||
}
|
||||
bytes_read += QBG_SDAM_ONE_FIFO_REGION_SIZE;
|
||||
|
||||
}
|
||||
|
||||
/* Next read SDAM that is partially filled */
|
||||
bytes_remaining = fifo_data_length - bytes_read;
|
||||
qbg_dbg(chip, QBG_DEBUG_FIFO, "Valid data in partially filled sdam:%d bytes\n",
|
||||
bytes_remaining);
|
||||
|
||||
if (bytes_remaining) {
|
||||
rc = qbg_sdam_read(chip,
|
||||
QBG_SDAM_DATA_START_OFFSET(chip, SDAM_DATA0 + i),
|
||||
(((u8 *)fifo) + bytes_read),
|
||||
bytes_remaining);
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to read remaining bytes from QBG SDAM%d, rc=%d\n",
|
||||
i, rc);
|
||||
return rc;
|
||||
}
|
||||
bytes_read += bytes_remaining;
|
||||
}
|
||||
|
||||
qbg_dbg(chip, QBG_DEBUG_FIFO, "Total bytes read=%d\n", bytes_read);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int qbg_sdam_get_essential_params(struct qti_qbg *chip, u8 *params)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!chip || !params)
|
||||
return -EINVAL;
|
||||
|
||||
rc = qbg_sdam_read(chip,
|
||||
chip->sdam_base + (SDAM_CTRL1 * 0x100) +
|
||||
QBG_ESSENTIAL_PARAMS_START_OFFSET,
|
||||
(u8 *)params, sizeof(struct qbg_essential_params));
|
||||
if (rc < 0)
|
||||
pr_err("Failed to read QBG essential params, rc=%d\n", rc);
|
||||
else
|
||||
qbg_dbg(chip, QBG_DEBUG_FIFO, "Read QBG essential params\n");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int qbg_sdam_set_essential_params(struct qti_qbg *chip, u8 *params)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!chip || !params)
|
||||
return -EINVAL;
|
||||
|
||||
rc = qbg_sdam_write(chip,
|
||||
chip->sdam_base + (SDAM_CTRL1 * 0x100) +
|
||||
QBG_ESSENTIAL_PARAMS_START_OFFSET,
|
||||
(u8 *)params, sizeof(struct qbg_essential_params));
|
||||
if (rc < 0)
|
||||
pr_err("Failed to write QBG essential params, rc=%d\n", rc);
|
||||
else
|
||||
qbg_dbg(chip, QBG_DEBUG_FIFO, "Written QBG essential params\n");
|
||||
|
||||
return rc;
|
||||
}
|
||||
31
drivers/power/supply/qcom/qbg-sdam.h
Normal file
31
drivers/power/supply/qcom/qbg-sdam.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2021 The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __QBG_SDAM_H__
|
||||
#define __QBG_SDAM_H__
|
||||
|
||||
#define QBG_SDAM_PBS_STATUS_OFFSET 0x45
|
||||
#define QBG_SDAM_FIFO_COUNT_OFFSET 0x46
|
||||
#define QBG_ESSENTIAL_PARAMS_START_OFFSET 0x47
|
||||
#define QBG_SDAM_START_OFFSET 0x4b
|
||||
#define QBG_SDAM_INT_TEST1 0xe0
|
||||
#define QBG_SDAM_INT_TEST_VAL 0xe1
|
||||
#define QBG_SDAM_DATA_PUSH_COUNTER_OFFSET 0x46
|
||||
|
||||
#define QBG_SDAM_TEST_VAL_1_SET 0x2
|
||||
#define QBG_SDAM_ONE_FIFO_REGION_SIZE 117 /* 117 bytes for FIFO starting from 0x4B */
|
||||
#define QBG_SDAM_ESR_PULSE_FIFO_INDEX 11
|
||||
|
||||
#define QBG_SDAM_DATA_START_OFFSET(chip, index) \
|
||||
(chip->sdam_base + (index * 0x100) + QBG_SDAM_START_OFFSET)
|
||||
|
||||
int qbg_sdam_read(struct qti_qbg *chip, int offset, u8 *data, int length);
|
||||
int qbg_sdam_write(struct qti_qbg *chip, int offset, u8 *data, int length);
|
||||
int qbg_sdam_get_fifo_data(struct qti_qbg *chip, struct fifo_data *fifo,
|
||||
u32 fifo_count);
|
||||
int qbg_sdam_get_essential_params(struct qti_qbg *chip, u8 *params);
|
||||
int qbg_sdam_set_essential_params(struct qti_qbg *chip, u8 *params);
|
||||
|
||||
#endif /* __QBG_SDAM_H__ */
|
||||
1718
drivers/power/supply/qcom/qti-qbg.c
Normal file
1718
drivers/power/supply/qcom/qti-qbg.c
Normal file
File diff suppressed because it is too large
Load diff
51
include/uapi/linux/qbg-profile.h
Normal file
51
include/uapi/linux/qbg-profile.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
|
||||
/*
|
||||
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __QBG_PROFILE_H__
|
||||
#define __QBG_PROFILE_H__
|
||||
|
||||
#define MAX_BP_LUT_ROWS 35
|
||||
#define MAX_BP_LUT_COLS 8
|
||||
#define MAX_PROFILE_NAME_LENGTH 256
|
||||
|
||||
enum profile_table_type {
|
||||
CHARGE_TABLE = 0,
|
||||
DISCHARGE_TABLE,
|
||||
};
|
||||
|
||||
struct battery_data_table {
|
||||
unsigned short int table[MAX_BP_LUT_ROWS][MAX_BP_LUT_COLS];
|
||||
int unit_conv_factor[MAX_BP_LUT_COLS];
|
||||
unsigned short int nrows;
|
||||
unsigned short int ncols;
|
||||
};
|
||||
|
||||
struct battery_config {
|
||||
char bp_profile_name[MAX_PROFILE_NAME_LENGTH];
|
||||
int bp_batt_id;
|
||||
int capacity;
|
||||
int bp_checksum;
|
||||
int soh_range_high;
|
||||
int soh_range_low;
|
||||
int normal_impedance;
|
||||
int aged_impedance;
|
||||
int normal_capacity;
|
||||
int aged_capacity;
|
||||
int recharge_soc_delta;
|
||||
int recharge_vflt_delta;
|
||||
int recharge_iterm;
|
||||
};
|
||||
|
||||
struct battery_profile_table {
|
||||
enum profile_table_type table_type;
|
||||
int table_index;
|
||||
struct battery_data_table *table;
|
||||
};
|
||||
|
||||
/* IOCTLs to query battery profile data */
|
||||
#define BPIOCXBP _IOWR('B', 0x01, struct battery_config) /* Battery configuration */
|
||||
#define BPIOCXBPTABLE _IOWR('B', 0x02, struct battery_profile_table) /* Battery profile table */
|
||||
|
||||
#endif
|
||||
142
include/uapi/linux/qbg.h
Normal file
142
include/uapi/linux/qbg.h
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
|
||||
/*
|
||||
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __QBG_H__
|
||||
#define __QBG_H__
|
||||
|
||||
#define MAX_FIFO_COUNT 36
|
||||
|
||||
enum QBG_SDAM_DATA_OFFSET {
|
||||
QBG_ACC0_OFFSET = 0,
|
||||
QBG_ACC1_OFFSET = 2,
|
||||
QBG_ACC2_OFFSET = 4,
|
||||
QBG_TBAT_OFFSET = 6,
|
||||
QBG_IBAT_OFFSET = 8,
|
||||
QBG_VREF_OFFSET = 10,
|
||||
QBG_DATA_TAG_OFFSET = 12,
|
||||
QBG_QG_STS_OFFSET,
|
||||
QBG_STS1_OFFSET,
|
||||
QBG_STS2_OFFSET,
|
||||
QBG_STS3_OFFSET,
|
||||
QBG_ONE_FIFO_LENGTH,
|
||||
};
|
||||
|
||||
enum qbg {
|
||||
QBG_PARAM_SOC,
|
||||
QBG_PARAM_BATT_SOC,
|
||||
QBG_PARAM_SYS_SOC,
|
||||
QBG_PARAM_ESR,
|
||||
QBG_PARAM_OCV_UV,
|
||||
QBG_PARAM_MAX_LOAD_NOW,
|
||||
QBG_PARAM_MAX_LOAD_AVG,
|
||||
QBG_PARAM_HOLD_SOC_100PCT,
|
||||
QBG_PARAM_CHARGE_CYCLE_COUNT,
|
||||
QBG_PARAM_LEARNED_CAPACITY,
|
||||
QBG_PARAM_TTF_100MS,
|
||||
QBG_PARAM_TTE_100MS,
|
||||
QBG_PARAM_SOH,
|
||||
QBG_PARAM_TBAT,
|
||||
QBG_PARAM_SYS_SOC_HOLD_100PCT,
|
||||
QBG_PARAM_JEITA_COOL_THRESHOLD,
|
||||
QBG_PARAM_TOTAL_IMPEDANCE,
|
||||
QBG_PARAM_PON_OCV,
|
||||
QBG_PARAM_PON_IBAT,
|
||||
QBG_PARAM_PON_SOC,
|
||||
QBG_PARAM_BATT_ID,
|
||||
QBG_PARAM_FIFO_TIMESTAMP,
|
||||
QBG_PARAM_MAX,
|
||||
};
|
||||
|
||||
struct qbg_essential_params {
|
||||
short int msoc;
|
||||
short int cutoff_soc;
|
||||
short int full_soc;
|
||||
short int x0;
|
||||
short int x1;
|
||||
short int x2;
|
||||
short int soh_r;
|
||||
short int soh_c;
|
||||
short int theta0;
|
||||
short int theta1;
|
||||
short int theta2;
|
||||
short int i1full;
|
||||
short int i2full;
|
||||
short int i1cutoff;
|
||||
short int i2cutoff;
|
||||
short int syssoc;
|
||||
int discharge_cycle_count;
|
||||
int charge_cycle_count;
|
||||
unsigned int rtc_time;
|
||||
short int batt_therm;
|
||||
unsigned short int ocv;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct fifo_data {
|
||||
unsigned short int v1;
|
||||
unsigned short int v2;
|
||||
unsigned short int i;
|
||||
unsigned short int tbat;
|
||||
unsigned short int ibat;
|
||||
unsigned short int vref;
|
||||
char data_tag;
|
||||
char qg_sts;
|
||||
char sts1;
|
||||
char sts2;
|
||||
char sts3;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct k_fifo_data {
|
||||
unsigned int v1;
|
||||
unsigned int v2;
|
||||
unsigned int i;
|
||||
unsigned int tbat;
|
||||
unsigned int ibat;
|
||||
unsigned int vref;
|
||||
unsigned int data_tag;
|
||||
unsigned int qg_sts;
|
||||
unsigned int sts1;
|
||||
unsigned int sts2;
|
||||
unsigned int sts3;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct qbg_config {
|
||||
unsigned int batt_id;
|
||||
unsigned int pon_ocv;
|
||||
unsigned int pon_ibat;
|
||||
unsigned int pon_tbat;
|
||||
unsigned int pon_soc;
|
||||
unsigned int float_volt_uv;
|
||||
unsigned int fastchg_curr_ma;
|
||||
unsigned int vbat_cutoff_mv;
|
||||
unsigned int ibat_cutoff_ma;
|
||||
unsigned int vph_min_mv;
|
||||
unsigned int iterm_ma;
|
||||
unsigned int rconn_mohm;
|
||||
unsigned long current_time;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct qbg_param {
|
||||
unsigned int data;
|
||||
_Bool valid;
|
||||
};
|
||||
|
||||
struct qbg_kernel_data {
|
||||
unsigned int seq_no;
|
||||
unsigned int fifo_time;
|
||||
unsigned int fifo_count;
|
||||
struct k_fifo_data fifo[MAX_FIFO_COUNT];
|
||||
struct qbg_param param[QBG_PARAM_MAX];
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct qbg_user_data {
|
||||
struct qbg_param param[QBG_PARAM_MAX];
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
/* IOCTLs to read & write QBG config and essential params */
|
||||
#define QBGIOCXCFG _IOR('B', 0x01, struct qbg_config)
|
||||
#define QBGIOCXEPR _IOR('B', 0x02, struct qbg_essential_params)
|
||||
#define QBGIOCXEPW _IOWR('B', 0x03, struct qbg_essential_params)
|
||||
|
||||
#endif
|
||||
Loading…
Reference in a new issue