Files
android_kernel_samsung_sm8750/drivers/power/supply/qcom/smblite-remote-bms.c
2025-08-12 22:16:57 +02:00

872 lines
21 KiB
C
Executable File

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#define pr_fmt(fmt) "REMOTE-FG: %s: " fmt, __func__
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/iio/consumer.h>
#include <linux/kernel.h>
#include <linux/ktime.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/nvmem-consumer.h>
#include <linux/pmic-voter.h>
#include <linux/power_supply.h>
#include <linux/soc/qcom/slate_events_bridge_intf.h>
#include <linux/uaccess.h>
#include <dt-bindings/iio/qti_power_supply_iio.h>
#include "battery-profile-loader.h"
#include "smb5-iio.h"
#include "smblite-remote-bms.h"
static struct smblite_remote_bms *the_bms;
#define DEBUG_BATT_ID_LOW 6000
#define DEBUG_BATT_ID_HIGH 8500
static bool is_debug_batt_id(struct smblite_remote_bms *bms)
{
if (is_between(DEBUG_BATT_ID_LOW, DEBUG_BATT_ID_HIGH,
bms->batt_id_ohm))
return true;
return false;
}
static int remote_bms_read_prop_from_sdam(struct smblite_remote_bms *bms,
int offset,
int *val)
{
if (!bms->nvmem)
return -EINVAL;
return nvmem_device_read(bms->nvmem, offset, 1, val);
}
static int get_bms_param_from_smb5_param(int smb_param, int *offset)
{
int rc = 0;
switch (smb_param) {
case SMB5_QG_CHARGE_FULL:
*offset = CHARGE_FULL;
break;
case SMB5_QG_CHARGE_FULL_DESIGN:
*offset = CHARGE_FULL_DESIGN;
break;
case SMB5_QG_CURRENT_NOW:
*offset = CURRENT_NOW;
break;
case SMB5_QG_CAPACITY:
*offset = CAPACITY;
break;
case SMB5_QG_TIME_TO_FULL_NOW:
*offset = TIME_TO_FULL;
break;
case SMB5_QG_CYCLE_COUNT:
*offset = CYCLE_COUNT;
break;
case SMB5_QG_CHARGE_COUNTER:
*offset = CHARGE_COUNTER;
break;
default:
pr_err_ratelimited("Unsupported SMB param\n");
rc = -EINVAL;
break;
}
return rc;
}
static int bms_get_buffered_data(int channel, int *val, int src)
{
int rc = 0, offset;
rc = get_bms_param_from_smb5_param(channel, &offset);
if (rc < 0) {
pr_err("Unsupported remote bms property %d\n", channel);
return rc;
}
if (src == BMS_SDAM) {
rc = remote_bms_read_prop_from_sdam(the_bms, offset, val);
if (rc < 0) {
pr_err("SDAM read failed for property %d, rc=%d\n",
channel, rc);
return rc;
}
} else {
*val = the_bms->rx_params[offset].data;
rc = 0;
}
pr_debug("remote_bms_param:%u src:%d value:%d\n", offset, src, *val);
return rc;
}
int remote_bms_get_prop(int channel, int *val, int src)
{
int rc = 0;
if (!the_bms)
return -EINVAL;
switch (channel) {
case SMB5_QG_CHARGE_FULL:
case SMB5_QG_CHARGE_FULL_DESIGN:
case SMB5_QG_CURRENT_NOW:
case SMB5_QG_TIME_TO_FULL_NOW:
case SMB5_QG_CYCLE_COUNT:
case SMB5_QG_CHARGE_COUNTER:
rc = bms_get_buffered_data(channel, val, src);
break;
case SMB5_QG_CAPACITY:
if (is_debug_batt_id(the_bms)) {
*val = REMOTE_FG_DEBUG_BATT_SOC;
} else if (!the_bms->is_seb_up || !the_bms->received_first_data) {
pr_debug("Waiting for Co-Proc Up: %d / QBG Data : %d\n",
the_bms->is_seb_up, the_bms->received_first_data);
rc = -EAGAIN;
} else {
rc = bms_get_buffered_data(channel, val, src);
}
break;
case SMB5_QG_VOLTAGE_NOW:
rc = iio_read_channel_processed(the_bms->batt_volt_chan, val);
break;
case SMB5_QG_DEBUG_BATTERY:
*val = is_debug_batt_id(the_bms);
break;
case SMB5_QG_TEMP:
rc = iio_read_channel_processed(the_bms->batt_temp_chan, val);
break;
case SMB5_QG_RESISTANCE_ID:
*val = the_bms->batt_id_ohm;
break;
default:
pr_err("Invalid channel read for remote-fg\n");
rc = -EINVAL;
break;
}
if ((rc < 0) && (rc != -EAGAIN))
pr_err("Failed to read remote bms property %d, rc=%d\n", channel, rc);
else
pr_debug("Read Remote bms param:%u value:%d\n", channel, *val);
return rc;
}
static int remote_bms_get_data(struct smblite_remote_bms *bms)
{
int rc = 0, buf_ptr = 0;
char *tx_buf;
unsigned int tx_buf_size;
if (!bms->seb_handle) {
pr_err("Not registered with Slate event bridge, cannot request data\n");
return -EINVAL;
}
mutex_lock(&bms->tx_lock);
tx_buf_size = SEB_BUF_HEADER_SIZE + SEB_EACH_OPCODE_SIZE;
tx_buf = kzalloc(tx_buf_size, GFP_KERNEL);
if (!tx_buf) {
mutex_unlock(&bms->tx_lock);
return -ENOMEM;
}
tx_buf[buf_ptr++] = BMS_READ;
/* One opcode REQUEST_DATA to request data from remote-fg */
tx_buf[buf_ptr++] = 1;
tx_buf[buf_ptr++] = REQUEST_DATA;
rc = seb_send_event_to_slate(bms->seb_handle, GMI_SLATE_EVENT_QBG, tx_buf,
tx_buf_size);
if (rc < 0) {
pr_err("Failed to send REQUEST_DATA event to remote-fg, rc=%d\n",
rc);
goto free_tx_buf;
}
pr_debug("Send REQUEST_DATA event to remote-fg successful\n");
free_tx_buf:
kfree(tx_buf);
mutex_unlock(&bms->tx_lock);
return rc;
}
static void periodic_fg_work(struct work_struct *work)
{
struct smblite_remote_bms *bms = container_of(work,
struct smblite_remote_bms,
periodic_fg_work.work);
int rc = 0;
pr_debug("Read runtime data work expired, requesting remote-fg for data\n");
rc = remote_bms_get_data(bms);
if (rc < 0) {
pr_err("Couldn't request data from remote-fg, rc=%d\n", rc);
return;
}
schedule_delayed_work(&bms->periodic_fg_work,
msecs_to_jiffies(BMS_READ_INTERVAL_MS));
}
static int remote_bms_send_data(struct smblite_remote_bms *bms)
{
int rc = 0;
char *tx_buf;
unsigned int tx_buf_size, buf_ptr = 0;
mutex_lock(&bms->tx_lock);
tx_buf_size = SEB_BUF_HEADER_SIZE + (TX_MAX * SEB_EACH_PARAM_SIZE);
tx_buf = kzalloc(tx_buf_size, GFP_KERNEL);
if (!tx_buf) {
mutex_unlock(&bms->tx_lock);
return -ENOMEM;
}
tx_buf[buf_ptr++] = BMS_WRITE;
tx_buf[buf_ptr++] = TX_MAX;
tx_buf[buf_ptr++] = CHARGE_STATUS;
*(unsigned int *)(tx_buf + buf_ptr) = (unsigned int)bms->charge_status;
buf_ptr += 4;
tx_buf[buf_ptr++] = CHARGE_TYPE;
*(unsigned int *)(tx_buf + buf_ptr) = (unsigned int)bms->charge_type;
buf_ptr += 4;
tx_buf[buf_ptr++] = CHARGER_PRESENT;
*(unsigned int *)(tx_buf + buf_ptr) = (unsigned int)bms->charger_present;
buf_ptr += 4;
rc = seb_send_event_to_slate(bms->seb_handle, GMI_SLATE_EVENT_QBG, tx_buf,
tx_buf_size);
if (rc < 0) {
pr_err("Failed to send event to remote-fg, rc=%d\n", rc);
goto free_tx_buf;
}
pr_debug("Send event to remote-fg successful, charge_status:%d charger_present:%d chg_type:%d\n",
bms->charge_status, bms->charger_present, bms->charge_type);
free_tx_buf:
kfree(tx_buf);
mutex_unlock(&bms->tx_lock);
return rc;
}
static int remote_bms_handle_recharge(struct smblite_remote_bms *bms)
{
int rc = 0;
union power_supply_propval prop = {0, };
int force_recharge = bms->rx_params[RECHARGE_TRIGGER].data;
int recharge_fv = bms->rx_params[RECHARGE_FV].data;
int recharge_iterm = bms->rx_params[RECHARGE_ITERM].data;
if (force_recharge != bms->force_recharge) {
bms->force_recharge = force_recharge;
if (!force_recharge)
return 0;
pr_debug("Recharge configuration requested with FV:%duV Iterm:%dmA\n",
recharge_fv, recharge_iterm);
if ((recharge_fv >= 0) && (recharge_fv != bms->recharge_float_voltage)) {
prop.intval = bms->recharge_float_voltage = recharge_fv;
rc = power_supply_set_property(bms->batt_psy,
POWER_SUPPLY_PROP_VOLTAGE_MAX, &prop);
if (rc < 0) {
pr_err("Failed to set recharge voltage_max property on batt_psy, rc=%d\n",
rc);
return rc;
}
}
/* Configure charger iterm only if digital termination is used */
if (bms->default_iterm_ma && (bms->default_iterm_ma != -EINVAL) &&
(recharge_iterm != bms->recharge_iterm)) {
prop.intval = bms->recharge_iterm = recharge_iterm;
/* smblite charger expects termination current with -ve sign */
prop.intval = (-1 * prop.intval);
rc = power_supply_set_property(bms->batt_psy,
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, &prop);
if (rc < 0) {
pr_err("Failed to set recharge charge_term_current property on batt_psy, rc=%d\n",
rc);
return rc;
}
}
rc = bms->iio_write(bms->dev, PSY_IIO_FORCE_RECHARGE, bms->force_recharge);
if (rc < 0)
pr_err("Failed to set force recharge, rc=%d\n", rc);
pr_debug("Recharge configuration completed with FV:%duV Iterm:%dmA\n",
bms->recharge_float_voltage, bms->recharge_iterm);
}
return rc;
}
static void rx_data_work(struct work_struct *work)
{
struct smblite_remote_bms *bms = container_of(work,
struct smblite_remote_bms,
rx_data_work);
int rc = -EINVAL, i;
unsigned int buf_ptr = 0, param;
unsigned char num_params;
char *rx_buf;
vote(bms->awake_votable, REMOTE_FG_VOTER, true, 0);
mutex_lock(&bms->rx_lock);
num_params = *(unsigned char *)(bms->rx_buf + 1);
pr_debug("Processing %d parameteres\n", num_params);
rx_buf = (char *)(bms->rx_buf + SEB_BUF_HEADER_SIZE);
if (num_params > SEB_MAX_RX_PARAMS)
num_params = SEB_MAX_RX_PARAMS;
for (i = 0, buf_ptr = 0; i < num_params; i++) {
param = rx_buf[buf_ptr++];
switch (param) {
case CAPACITY:
case CURRENT_NOW:
case VOLTAGE_OCV:
case CYCLE_COUNT:
case CHARGE_COUNTER:
case CHARGE_FULL:
case CHARGE_FULL_DESIGN:
case TIME_TO_FULL:
case TIME_TO_EMPTY:
case SOH:
case RECHARGE_TRIGGER:
case RECHARGE_FV:
case RECHARGE_ITERM:
case REQUEST_CHG_DATA:
bms->rx_params[param].data =
*(unsigned int *)(rx_buf + buf_ptr);
buf_ptr += 4;
pr_debug("param:%u data:%u\n", param, bms->rx_params[param].data);
break;
default:
pr_debug("Unsupported remote-fg parameter %d with value %d\n",
param, *(unsigned int *)(rx_buf + buf_ptr));
buf_ptr += 4;
break;
}
}
if (!bms->received_first_data) {
pr_debug("First SOC reported from Co-Proc : %d\n",
bms->rx_params[CAPACITY].data);
bms->received_first_data = true;
}
rc = remote_bms_handle_recharge(bms);
if (rc < 0) {
pr_err("Failed to handle recharge, rc=%d\n",
rc);
goto out;
}
if (bms->rx_params[REQUEST_CHG_DATA].data) {
pr_debug("Charger data requested by remote-fg\n");
rc = remote_bms_send_data(bms);
if (rc < 0) {
pr_err("Failed to send data to remote-fg, rc=%d\n",
rc);
goto out;
}
}
if (!rc)
pr_debug("Finished processing rx buf from remote-fg\n");
/* Notify Clients so that they pick-up latest data from QBG */
if (bms->batt_psy)
power_supply_changed(bms->batt_psy);
out:
mutex_unlock(&bms->rx_lock);
vote(bms->awake_votable, REMOTE_FG_VOTER, false, 0);
}
static int seb_notifier_cb(struct notifier_block *nb,
unsigned long event, void *data)
{
struct smblite_remote_bms *bms = container_of(nb,
struct smblite_remote_bms, seb_nb);
int rc, buf_size;
u8 command, num_params;
if (is_debug_batt_id(bms))
return 0;
if (event == GLINK_CHANNEL_STATE_UP) {
bms->is_seb_up = true;
rc = remote_bms_get_data(bms);
if (rc < 0) {
pr_err("Couldn't request runtime data from QBG, rc=%d\n", rc);
return rc;
}
pr_debug("Slate-UP, requested first data from QBG\n");
/* send the first charger status */
if (!work_pending(&bms->psy_status_change_work))
schedule_work(&bms->psy_status_change_work);
schedule_delayed_work(&bms->periodic_fg_work,
msecs_to_jiffies(BMS_READ_INTERVAL_MS));
return 0;
} else if (event != GMI_SLATE_EVENT_QBG) {
pr_debug("SEB event is not for GMI_SLATE_EVENT_QBG\n");
return 0;
}
/* Always process latest data from remote-fg */
cancel_work_sync(&bms->rx_data_work);
if (!data) {
pr_err("Invalid buffer received from remote-fg\n");
return -EINVAL;
}
command = *(u8 *)data;
if (command != BMS_READ) {
pr_err("Packet with invalid command %d received, cannot process\n", command);
return -EINVAL;
}
num_params = *(u8 *)(data + 1);
if (!num_params) {
pr_err("No params received from remote-fg\n");
return -EINVAL;
}
pr_debug("Remote-fg data received: event:%lu command:%d num_params:%d\n",
event, command, num_params);
buf_size = SEB_BUF_HEADER_SIZE + (num_params * SEB_EACH_PARAM_SIZE);
if (buf_size > SEB_RX_BUF_SIZE) {
pr_err("Received more parameters from remote-fg, processing only till %d bytes\n",
SEB_RX_BUF_SIZE);
}
memcpy(bms->rx_buf, data, SEB_RX_BUF_SIZE);
/* Process QBG data */
schedule_work(&bms->rx_data_work);
/* Cancel and re-schedule periodic work to read QBG data */
cancel_delayed_work(&bms->periodic_fg_work);
schedule_delayed_work(&bms->periodic_fg_work,
msecs_to_jiffies(BMS_READ_INTERVAL_MS));
return 0;
}
static int remote_bms_set_battery_params(struct smblite_remote_bms *bms)
{
int rc;
union power_supply_propval prop = {0, };
prop.intval = bms->float_volt_uv;
rc = power_supply_set_property(bms->batt_psy,
POWER_SUPPLY_PROP_VOLTAGE_MAX, &prop);
if (rc < 0) {
pr_err("Failed to set voltage_max property on batt_psy, rc=%d\n",
rc);
return rc;
}
prop.intval = bms->fastchg_curr_ma * 1000;
rc = power_supply_set_property(bms->batt_psy,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &prop);
if (rc < 0) {
pr_err("Failed to set constant_charge_current_max property on batt_psy, rc=%d\n",
rc);
return rc;
}
if (bms->default_iterm_ma != -EINVAL) {
prop.intval = bms->default_iterm_ma;
rc = power_supply_set_property(bms->batt_psy,
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, &prop);
if (rc < 0) {
pr_err("Failed to set charge_current_max property on batt_psy, rc=%d\n",
rc);
return rc;
}
}
bms->recharge_float_voltage = bms->recharge_iterm = 0;
pr_debug("Set iterm:%dmA max voltage:%duV and max_current:%dmA to charger\n",
bms->default_iterm_ma, bms->float_volt_uv, bms->fastchg_curr_ma);
return rc;
}
static bool is_batt_available(struct smblite_remote_bms *bms)
{
int rc;
union power_supply_propval prop = {0, };
if (!bms->batt_psy) {
bms->batt_psy = power_supply_get_by_name("battery");
if (!bms->batt_psy)
return false;
/*
* Read termination current from charger,
* Use it to configure iterm after recharge upon USB removal.
*/
rc = power_supply_get_property(bms->batt_psy,
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, &prop);
if (!rc)
bms->default_iterm_ma = prop.intval;
rc = remote_bms_set_battery_params(bms);
return (rc < 0) ? false : true;
}
return true;
}
static bool is_usb_available(struct smblite_remote_bms *bms)
{
if (!bms->usb_psy)
bms->usb_psy = power_supply_get_by_name("usb");
if (!bms->usb_psy)
return false;
return true;
}
static void psy_status_change_work(struct work_struct *work)
{
struct smblite_remote_bms *bms = container_of(work,
struct smblite_remote_bms,
psy_status_change_work);
int rc;
int charge_status, charge_type, charger_present;
union power_supply_propval prop = {0, };
vote(bms->awake_votable, REMOTE_FG_VOTER, true, 0);
if (!bms->seb_handle) {
pr_err("Not registered with Slate event bridge, exiting\n");
goto out;
}
if (!is_batt_available(bms))
goto out;
if (!is_usb_available(bms))
goto out;
rc = power_supply_get_property(bms->batt_psy, POWER_SUPPLY_PROP_STATUS,
&prop);
if (rc < 0) {
pr_err("Failed to get battery charge status, rc=%d\n", rc);
goto out;
}
charge_status = prop.intval;
rc = power_supply_get_property(bms->usb_psy, POWER_SUPPLY_PROP_PRESENT,
&prop);
if (rc < 0) {
pr_err("Failed to get USB present, rc=%d\n", rc);
goto out;
}
charger_present = prop.intval;
rc = power_supply_get_property(bms->batt_psy,
POWER_SUPPLY_PROP_CHARGE_TYPE, &prop);
if (rc < 0) {
pr_err("Failed to get charge type, rc=%d\n", rc);
goto out;
}
charge_type = prop.intval;
/*
* Set FCC and iterm back to default value on charger removal
* as they could have been updated during recharge.
*/
if (bms->charger_present && !charger_present) {
rc = remote_bms_set_battery_params(bms);
if (rc < 0) {
pr_err("Failed to set battery FCC and FV on charger removal\n");
goto out;
}
}
if ((charge_status != bms->charge_status) ||
(charger_present != bms->charger_present) ||
(charge_type != bms->charge_type)) {
bms->charge_status = charge_status;
bms->charger_present = charger_present;
bms->charge_type = charge_type;
pr_debug("Charger update: charger_status:%d charger_present:%d charger_type:%d\n",
bms->charge_status, bms->charger_present, bms->charge_type);
rc = remote_bms_send_data(bms);
if (rc < 0)
pr_err("Failed to send data to remote-fg, rc=%d\n", rc);
}
out:
vote(bms->awake_votable, REMOTE_FG_VOTER, false, 0);
}
static int bms_psy_notifier_cb(struct notifier_block *nb, unsigned long event,
void *data)
{
struct power_supply *psy = data;
struct smblite_remote_bms *bms = container_of(nb,
struct smblite_remote_bms,
psy_nb);
if (is_debug_batt_id(bms))
return NOTIFY_OK;
if (event != PSY_EVENT_PROP_CHANGED)
return NOTIFY_OK;
if (work_pending(&bms->psy_status_change_work))
return NOTIFY_OK;
if ((strcmp(psy->desc->name, "battery") == 0)
|| (strcmp(psy->desc->name, "usb") == 0)) {
schedule_work(&bms->psy_status_change_work);
}
return NOTIFY_OK;
}
static int remote_bms_get_adc_props(struct smblite_remote_bms *bms)
{
int rc = 0;
bms->batt_id_chan = iio_channel_get(bms->dev, "batt-id");
if (IS_ERR(bms->batt_id_chan)) {
rc = PTR_ERR(bms->batt_id_chan);
if (rc != -EPROBE_DEFER)
pr_err("batt-id channel unavailable, rc=%d\n", rc);
bms->batt_id_chan = NULL;
return rc;
}
rc = iio_read_channel_processed(bms->batt_id_chan, &bms->batt_id_ohm);
if (rc < 0) {
pr_err("Couldn't read battery ID channel rc=%d\n", rc);
return rc;
}
bms->batt_temp_chan = iio_channel_get(bms->dev, "batt-temp");
if (IS_ERR(bms->batt_temp_chan)) {
rc = PTR_ERR(bms->batt_temp_chan);
if (rc != -EPROBE_DEFER)
pr_err("batt-temp channel unavailable, rc=%d\n", rc);
bms->batt_temp_chan = NULL;
return rc;
}
bms->batt_volt_chan = iio_channel_get(bms->dev, "batt-volt");
if (IS_ERR(bms->batt_volt_chan)) {
rc = PTR_ERR(bms->batt_volt_chan);
if (rc != -EPROBE_DEFER)
pr_err("batt-volt channel unavailable, rc=%d\n", rc);
bms->batt_volt_chan = NULL;
return rc;
}
return rc;
}
static int remote_bms_parse_profile(struct smblite_remote_bms *bms)
{
struct device_node *profile_node;
int rc;
bms->batt_node = of_find_node_by_name(bms->dev->of_node, "qcom,battery-data");
if (!bms->batt_node) {
pr_err("Batterydata not available\n");
return -ENODEV;
}
profile_node = of_batterydata_get_best_profile(bms->batt_node,
bms->batt_id_ohm / 1000, NULL);
if (IS_ERR_OR_NULL(profile_node)) {
rc = profile_node ? PTR_ERR(profile_node) : -EINVAL;
pr_err("Failed to detect valid battery profile %d\n", rc);
return 0;
}
rc = of_property_read_string(profile_node, "qcom,battery-type",
&bms->batt_profile_name);
if (rc < 0) {
pr_err("Failed to get battery profile name, rc=%d\n", rc);
return rc;
}
rc = of_property_read_u32(profile_node, "qcom,max-voltage-uv",
&bms->float_volt_uv);
if (rc < 0) {
pr_err("Failed to read battery float-voltage rc:%d\n", rc);
bms->float_volt_uv = -EINVAL;
}
rc = of_property_read_u32(profile_node, "qcom,fastchg-current-ma",
&bms->fastchg_curr_ma);
if (rc < 0) {
pr_err("Failed to read battery fastcharge current rc:%d\n", rc);
bms->fastchg_curr_ma = -EINVAL;
}
pr_debug("batt_id=%d Ohm profile=%s, FV=%d uV FCC=%d mA\n",
bms->batt_id_ohm, bms->batt_profile_name, bms->float_volt_uv,
bms->fastchg_curr_ma);
return rc;
}
int remote_bms_init(struct smblite_remote_bms *bms)
{
int rc;
struct seb_notif_info *seb_handle;
if (!bms->iio_read || !bms->iio_write) {
pr_err("Invalid iio read/write pointers\n");
return -EINVAL;
}
rc = remote_bms_get_adc_props(bms);
if (rc < 0) {
pr_err("Couldn't get adc props, rc=%d\n", rc);
return rc;
}
rc = remote_bms_parse_profile(bms);
if (rc < 0) {
pr_err("Couldn't parse battery profile, rc=%d\n", rc);
return rc;
}
bms->awake_votable = find_votable("AWAKE");
if (bms->awake_votable == NULL) {
rc = -EINVAL;
pr_err("Couldn't find AWAKE votable rc=%d\n", rc);
return rc;
}
bms->is_seb_up = false;
bms->received_first_data = false;
mutex_init(&bms->data_lock);
mutex_init(&bms->tx_lock);
mutex_init(&bms->rx_lock);
INIT_DELAYED_WORK(&bms->periodic_fg_work, periodic_fg_work);
INIT_WORK(&bms->psy_status_change_work, psy_status_change_work);
INIT_WORK(&bms->rx_data_work, rx_data_work);
bms->seb_nb.notifier_call = seb_notifier_cb;
seb_handle = seb_register_for_slate_event(GMI_SLATE_EVENT_QBG, &bms->seb_nb);
if (IS_ERR_OR_NULL(seb_handle)) {
rc = seb_handle ? PTR_ERR(seb_handle) : -EINVAL;
pr_err("Failed to register with Slate event bridge, rc=%d\n", rc);
return rc;
}
bms->seb_handle = seb_handle;
bms->psy_nb.notifier_call = bms_psy_notifier_cb;
rc = power_supply_reg_notifier(&bms->psy_nb);
if (rc < 0)
pr_err("Failed to register psy notifier,rc = %d\n", rc);
the_bms = bms;
pr_info("smblite remote-fg registered - battery detected:%s\n",
is_debug_batt_id(bms) ? "debug_board" : bms->batt_profile_name);
return rc;
}
int remote_bms_deinit(void)
{
int rc = 0;
if (!the_bms)
return -ENODEV;
cancel_delayed_work_sync(&the_bms->periodic_fg_work);
cancel_work_sync(&the_bms->psy_status_change_work);
cancel_work_sync(&the_bms->rx_data_work);
rc = seb_unregister_for_slate_event(the_bms->seb_handle, &the_bms->seb_nb);
if (rc < 0) {
pr_err("Couldn't unregister with slate event bridge, rc=%d\n", rc);
return rc;
}
mutex_destroy(&the_bms->data_lock);
mutex_destroy(&the_bms->tx_lock);
mutex_destroy(&the_bms->rx_lock);
the_bms = NULL;
return rc;
}
int remote_bms_resume(void)
{
if (!the_bms)
return -ENODEV;
if (is_debug_batt_id(the_bms))
return 0;
schedule_delayed_work(&the_bms->periodic_fg_work, 0);
return 0;
}
int remote_bms_suspend(void)
{
if (!the_bms)
return -ENODEV;
cancel_delayed_work(&the_bms->periodic_fg_work);
cancel_work_sync(&the_bms->psy_status_change_work);
cancel_work_sync(&the_bms->rx_data_work);
return 0;
}