// 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)	"QBG_K: %s: " fmt, __func__

#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/debugfs.h>
#include <linux/errno.h>
#include <linux/iio/iio.h>
#include <linux/iio/consumer.h>
#include <linux/interrupt.h>
#include <linux/ioctl.h>
#include <linux/kernel.h>
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/poll.h>
#include <linux/power_supply.h>
#include <linux/pm_wakeup.h>
#include <linux/qti_power_supply.h>
#include <linux/regmap.h>
#include <linux/rtc.h>
#include <linux/slab.h>
#include <linux/suspend.h>
#include <linux/uaccess.h>
#include <linux/workqueue.h>
#include <linux/time64.h>

#include <dt-bindings/iio/qti_power_supply_iio.h>

#include <uapi/linux/qbg.h>
#include <uapi/linux/qbg-profile.h>
#include "qbg-iio.h"
#include "qbg-battery-profile.h"
#include "qbg-core.h"
#include "qbg-sdam.h"
#include "battery-profile-loader.h"

/* QBG register definitions */
#define QBG_MAIN_STATUS1				0x08
#define QBG_OK_BIT					BIT(7)

#define QBG_MAIN_STATUS2				0x09
#define QBG_BATTERY_MISSING_BIT				BIT(2)
#define QBG_ADC_CONV_FAULT_OCCURRED_BIT			BIT(1)
#define QBG_DATA_RESET_BIT				BIT(0)

#define QBG_MAIN_STATUS3				0x0a
#define QBG_FSM_STATE_MASK				GENMASK(3, 0)

#define QBG_MAIN_QBG_STATE_FORCE_CMD			0x41
#define FORCE_FAST_CHAR_MODE_BIT			BIT(3)
#define FORCE_FAST_CHAR_SHIFT				3

#define QBG_MAIN_EN_CTL					0x46
#define QBG_EN_BIT					BIT(7)

#define QBG_MAIN_MPM_TO_LPM_IBAT_THRESH			0x50

#define QBG_MAIN_LPM_TO_MPM_IBAT_THRESH			0x51

#define QBG_MAIN_HPM_TO_MPM_IBAT_THRESH			0x52

#define QBG_MAIN_MPM_TO_HPM_IBAT_THRESH			0x53

#define QBG_MAIN_VBAT_EMPTY_THRESH			0x56

#define QBG_MAIN_HPM_MEAS_CTL2				0x5d

#define QBG_MAIN_MPM_MEAS_CTL2				0x61

#define QBG_MAIN_LPM_MEAS_CTL2				0x65
#define QBG_NUM_OF_ACCUM_MASK				GENMASK(7, 5)
#define QBG_NUM_OF_ACCUM_SHIFT				5
#define QBG_ACCUM_INTERVAL_MASK				GENMASK(2, 0)

#define QBG_MAIN_FAST_CHAR_MEAS_CTL2			0x69

#define QBG_MAIN_PON_OCV_ACC0_DATA0			0x70

#define QBG_MAIN_LAST_ADC_ACC0_DATA0			0x9A

#define QBG_MAIN_LAST_BURST_AVG_ACC0_DATA0		0xA0

#define QBG_MAIN_LAST_BURST_AVG_ACC2_DATA0		0xA4

/* PM5100 REVID */
#define REVID_REVISION4					0x103
#define REVISION_V1					0x1

#define QBG_FAST_CHAR_DELTA_MS				100000
#define VBATT_1S_LSB					19463
#define IBATT_10A_LSB					6103
#define VBAT_0PCT_OCV					300000000ULL
#define ICHG_FS_10A					1
#define TBAT_LSB					7500
#define TBAT_DENOMINATOR				115600

#define BOOST_COMPENSATION_UV				400000
#define OCV_BOOST_MIN_UV				4100000
#define OCV_BOOST_MAX_UV				5600000
#define OCV_BOOST_STEP_UV				100000

#define BATT_HOT_DECIDEGREE_MAX				600

#define MILLI_TO_10NANO				100000
#define MICRO_TO_10NANO				100
#define MILLI_TO_MICRO				1000

/* PM5100 ADC_CMN and ADC_CMN_WB register definitions */
#define BAT_THERM_IBAT_CTL_2			0x62
#define VADC_ANA_PARAM_4			0x46

static int qbg_debug_mask;

enum qbg_esr_fcc_reduction {
	ESR_FCC_150MA = 0x6,
};

#define ESR_PULSE_10NA(x)	(-(x * 25 * MILLI_TO_10NANO))
#define ESR_IBAT_QUALIFY_OFFSET_10NA	(-5000000)

static const unsigned int qbg_accum_interval[8] = {
	10000, 20000, 50000, 100000, 150000, 200000, 500000, 1000000};
static const unsigned int qbg_lpm_accum_interval[8] = {
	100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 100000000};
static const unsigned int qbg_fast_char_avg_interval[8] = {
	6250, 12500, 25000, 50000, 100000, 200000, 400000, 800000};

static const struct qbg_iio_channels qbg_iio_psy_channels[] = {
	QBG_CHAN_INDEX("debug_battery", PSY_IIO_DEBUG_BATTERY)
	QBG_CHAN_ENERGY("capacity", PSY_IIO_CAPACITY)
	QBG_CHAN_ENERGY("real_capacity", PSY_IIO_REAL_CAPACITY)
	QBG_CHAN_TEMP("temp", PSY_IIO_TEMP)
	QBG_CHAN_VOLT("voltage_max", PSY_IIO_VOLTAGE_MAX)
	QBG_CHAN_VOLT("voltage_now", PSY_IIO_VOLTAGE_NOW)
	QBG_CHAN_VOLT("voltage_ocv", PSY_IIO_VOLTAGE_OCV)
	QBG_CHAN_VOLT("voltage_avg", PSY_IIO_VOLTAGE_AVG)
	QBG_CHAN_CUR("current_now", PSY_IIO_CURRENT_NOW)
	QBG_CHAN_RES("resistance_id", PSY_IIO_RESISTANCE_ID)
	QBG_CHAN_TSTAMP("time_to_full_avg", PSY_IIO_TIME_TO_FULL_AVG)
	QBG_CHAN_TSTAMP("time_to_full_now", PSY_IIO_TIME_TO_FULL_NOW)
	QBG_CHAN_TSTAMP("time_to_empty_avg", PSY_IIO_TIME_TO_EMPTY_AVG)
	QBG_CHAN_RES("esr", PSY_IIO_ESR_ACTUAL)
	QBG_CHAN_INDEX("soh", PSY_IIO_SOH)
	QBG_CHAN_COUNT("cycle_count", PSY_IIO_CYCLE_COUNT)
	QBG_CHAN_ENERGY("charge_full", PSY_IIO_CHARGE_FULL)
	QBG_CHAN_ENERGY("charge_full_design", PSY_IIO_CHARGE_FULL_DESIGN)
	QBG_CHAN_ENERGY("charge_counter", PSY_IIO_CHARGE_COUNTER)
};

int qbg_read(struct qti_qbg *chip, u32 addr, u8 *val, int len)
{
	int rc;

	rc = regmap_bulk_read(chip->regmap, chip->base + addr, val, len);
	if (rc < 0) {
		pr_err("Failed to read from address %04x rc=%d\n", chip->base + addr, rc);
		return rc;
	}

	if (*chip->debug_mask & QBG_DEBUG_BUS_READ)
		pr_info("length %d addr=%#x data:%*ph\n", len, chip->base + addr, len, val);

	return 0;
}

int qbg_write(struct qti_qbg *chip, u32 addr, u8 *val, int len)
{
	int rc;

	rc = regmap_bulk_write(chip->regmap, chip->base + addr, val, len);
	if (rc < 0) {
		pr_err("Failed to write to address %04x rc=%d\n",
				chip->base + addr, rc);
		return rc;
	}

	if (*chip->debug_mask & QBG_DEBUG_BUS_WRITE)
		pr_info("length %d addr=%#x data:%*ph\n", len, chip->base + addr, len, val);

	return 0;
}

static bool is_chan_valid(struct qti_qbg *chip, enum qbg_ext_iio_channels chan)
{
	int rc;

	if (IS_ERR(chip->ext_iio_chans[chan]))
		return false;

	if (!chip->ext_iio_chans[chan]) {
		chip->ext_iio_chans[chan] = iio_channel_get(chip->dev,
					qbg_ext_iio_chan_name[chan]);
		if (IS_ERR(chip->ext_iio_chans[chan])) {
			rc = PTR_ERR(chip->ext_iio_chans[chan]);
			if (rc == -EPROBE_DEFER)
				chip->ext_iio_chans[chan] = NULL;

			pr_err("Failed to get IIO channel %s, rc=%d\n",
				qbg_ext_iio_chan_name[chan], rc);
			return false;
		}
	}

	return true;
}

static int qbg_read_iio_chan(struct qti_qbg *chip, enum qbg_ext_iio_channels chan,
				int *val)
{
	int rc;

	if (is_chan_valid(chip, chan)) {
		rc = iio_read_channel_processed(chip->ext_iio_chans[chan], val);
		return (rc < 0) ? rc : 0;
	}

	return -EINVAL;
}

static int qbg_write_iio_chan(struct qti_qbg *chip, enum qbg_ext_iio_channels chan,
				int val)
{
	if (is_chan_valid(chip, chan))
		return iio_write_channel_raw(chip->ext_iio_chans[chan], val);

	return -EINVAL;
}

static void qbg_notify_charger(struct qti_qbg *chip)
{
	union power_supply_propval prop = {0, };
	int rc;

	if (!chip->batt_psy || !chip->profile_loaded)
		return;

	prop.intval = chip->float_volt_uv;
	rc = power_supply_set_property(chip->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;
	}

	prop.intval = chip->fastchg_curr_ma * 1000;
	rc = power_supply_set_property(chip->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;
	}

	if (chip->default_iterm_ma != -EINVAL) {
		prop.intval = chip->default_iterm_ma;
		rc = power_supply_set_property(chip->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;
		}
	}

	pr_debug("Notified charger on float voltage:%d uV and FCC:%d mA iterm:%d mA\n",
			chip->float_volt_uv, chip->fastchg_curr_ma, chip->default_iterm_ma);
}

static bool is_batt_available(struct qti_qbg *chip)
{
	int rc;
	union power_supply_propval prop = {0, };

	if (chip->batt_psy)
		return true;

	chip->batt_psy = power_supply_get_by_name("battery");
	if (!chip->batt_psy)
		return false;

	/* batt_psy is initialized, set the fcc and fv */
	qbg_notify_charger(chip);

	/*
	 * Read termination current from charger,
	 * Use it to configure iterm after recharge upon USB removal.
	 */
	rc = power_supply_get_property(chip->batt_psy,
			POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, &prop);
	if (!rc)
		chip->default_iterm_ma = prop.intval;

	return true;
}

static bool is_usb_available(struct qti_qbg *chip)
{
	if (chip->usb_psy)
		return true;

	chip->usb_psy = power_supply_get_by_name("usb");
	if (!chip->usb_psy)
		return false;

	return true;
}

#define DEFAULT_RECHARGE_SOC 95
static int qbg_charge_full_update(struct qti_qbg *chip)
{
	union power_supply_propval prop = {0, };
	int rc, recharge_soc, health, val;

	/* Nothing to process */
	if (!chip->charger_present || !chip->charge_done)
		return 0;

	rc = power_supply_get_property(chip->batt_psy,
			POWER_SUPPLY_PROP_HEALTH, &prop);
	if (rc < 0) {
		pr_err("Failed to get battery health, rc=%d\n", rc);
		return rc;
	}
	health = prop.intval;

	rc = qbg_read_iio_chan(chip, RECHARGE_SOC, &val);
	if (rc < 0 || val < 0) {
		pr_debug("Failed to get recharge-soc, rc=%d\n", rc);
		recharge_soc = DEFAULT_RECHARGE_SOC;
	} else {
		recharge_soc = val;
	}
	chip->recharge_soc = recharge_soc;

qbg_dbg(chip, QBG_DEBUG_STATUS, "msoc=%d sys_soc:%d charge_done:%d charge_status=%d recharge_soc=%d in_recharge:%u\n",
			chip->soc, chip->sys_soc, chip->charge_done,
			chip->charge_status, chip->recharge_soc, chip->in_recharge);

	if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING) {
		/* Charger is charging in recharge */
		return 0;
	}

	/* System SOC has not dropped below recharge SOC to trigger recharge */
	if (chip->sys_soc > recharge_soc)
		return 0;

	if (!chip->in_recharge) {
		if (chip->recharge_vflt_delta_mv && chip->recharge_vflt_delta_mv != -EINVAL) {
			prop.intval = (chip->float_volt_uv) - (chip->recharge_vflt_delta_mv * 1000);
			rc = power_supply_set_property(chip->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;
			}
		}

		if (chip->recharge_iterm_ma && chip->recharge_iterm_ma != -EINVAL) {
			prop.intval = chip->recharge_iterm_ma;
			prop.intval = (-1 * prop.intval);
			rc = power_supply_set_property(chip->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;
			}
		}
		chip->in_recharge = true;
	}

	return rc;
}

static void status_change_work(struct work_struct *work)
{
	struct qti_qbg *chip = container_of(work, struct qti_qbg,
						status_change_work);
	union power_supply_propval prop = {0, };
	int rc, val, charger_present = 0;

	if (!is_batt_available(chip))
		return;

	if (!is_usb_available(chip))
		return;

	rc = power_supply_get_property(chip->batt_psy,
			POWER_SUPPLY_PROP_CHARGE_TYPE, &prop);
	if (rc < 0)
		pr_err("Failed to get charge-type, rc=%d\n", rc);
	else
		chip->charge_type = prop.intval;

	rc = power_supply_get_property(chip->batt_psy,
			POWER_SUPPLY_PROP_STATUS, &prop);
	if (rc < 0)
		pr_err("Failed to get charger status, rc=%d\n", rc);
	else
		chip->charge_status = prop.intval;

	rc = qbg_read_iio_chan(chip, CHARGE_DONE, &val);
	if (rc < 0)
		pr_err("Failed to get charge done status, rc=%d\n", rc);
	else
		chip->charge_done = val;

	rc = power_supply_get_property(chip->usb_psy,
			POWER_SUPPLY_PROP_PRESENT, &prop);
	if (rc < 0)
		pr_err("Failed to get charger present, rc=%d\n", rc);
	else
		charger_present = prop.intval;

	qbg_dbg(chip, QBG_DEBUG_STATUS, "charge_status=%d charge_done=%d charger_present:%d\n",
			chip->charge_status, chip->charge_done, chip->charger_present);

	/*
	 * Set FCC and iterm back to default value on charger removal
	 * as they could have been updated during recharge.
	 */
	if (chip->charger_present && !charger_present) {
		qbg_notify_charger(chip);
		chip->in_recharge = false;
	}
	chip->charger_present = charger_present;

	rc = qbg_charge_full_update(chip);
	if (rc < 0) {
		pr_err("Failed in charge full update, rc=%d\n", rc);
		return;
	}
}

static int qbg_get_max_fifo_count(struct qti_qbg *chip)
{
	int rc = 0;
	u8 val[2];

	rc = qbg_sdam_read(chip,
		QBG_SDAM_BASE(chip, SDAM_CTRL0) + QBG_SDAM_MAX_FIFO_COUNT_OFFSET,
		val, 2);
	if (rc < 0) {
		pr_err("Failed to read QBG SDAM_MAX_FIFO_COUNT_OFFSET, rc=%d\n", rc);
		return rc;
	}

	chip->max_fifo_count = (val[1] << 8) | val[0];
	qbg_dbg(chip, QBG_DEBUG_SDAM, "max FIFO count=%d\n", chip->max_fifo_count);

	return rc;
}

static int qbg_get_fifo_count(struct qti_qbg *chip, u32 *fifo_count)
{
	int rc = 0;
	u8 val[2];

	rc = qbg_sdam_read(chip,
		QBG_SDAM_BASE(chip, SDAM_CTRL0) + QBG_SDAM_FIFO_COUNT_OFFSET,
		val, 2);
	if (rc < 0) {
		pr_err("Failed to read QBG SDAM, rc=%d\n", rc);
		return rc;
	}

	*fifo_count = (val[1] << 8) | val[0];
	qbg_dbg(chip, QBG_DEBUG_SDAM, "FIFO count=%d\n", *fifo_count);

	return rc;
}

static int qbg_hpm_fifo_depth_half(struct qti_qbg *chip, int current_fifo_count, bool enable)
{
	int rc = 0;
	u8 val = 0;

	if (enable)
		val = current_fifo_count / 2;
	else
		val = current_fifo_count * 2;

	if (val > chip->max_fifo_count)
		val = chip->max_fifo_count;

	rc = qbg_sdam_write(chip, QBG_SDAM_BASE(chip, SDAM_CTRL0) +
			QBG_SDAM_HPM_FIFO_COUNT_OFFSET, &val, 1);
	if (rc < 0)
		pr_err("Failed to write QBG SDAM, rc=%d\n", rc);

	return rc;
}

static void update_ocv_for_boost(struct qti_qbg *chip)
{
	int ocv_for_boost = 0x00;

	/*
	 * Every time when OCV gets updated, add 0.4V headroom compensation
	 * and round it to a value between 4.1V to 5.6V, then map it to a
	 * setting between [0x00 - 0x0F] and set it in SDAM to notify charger-boost.
	 */
	ocv_for_boost = chip->ocv_uv + BOOST_COMPENSATION_UV;
	if (ocv_for_boost < OCV_BOOST_MIN_UV)
		ocv_for_boost = OCV_BOOST_MIN_UV;
	else if (ocv_for_boost > OCV_BOOST_MAX_UV)
		ocv_for_boost = OCV_BOOST_MAX_UV;

	ocv_for_boost -= OCV_BOOST_MIN_UV;
	ocv_for_boost /= OCV_BOOST_STEP_UV;

	qbg_dbg(chip, QBG_DEBUG_STATUS, "Boost OCV value written =%d\n", ocv_for_boost);
	qbg_sdam_write(chip,
		QBG_SDAM_BASE(chip, SDAM_CTRL0) + QBG_SDAM_BHARGER_OCV_HDRM_OFFSET,
		(u8 *)&ocv_for_boost, 1);
}

static void process_udata_work(struct work_struct *work)
{
	struct qti_qbg *chip = container_of(work, struct qti_qbg, udata_work);
	int rc;

	if (chip->udata.param[QBG_PARAM_BATT_SOC].valid)
		chip->batt_soc = chip->udata.param[QBG_PARAM_BATT_SOC].data;

	if (chip->udata.param[QBG_PARAM_SOC].valid ||
			chip->udata.param[QBG_PARAM_SYS_SOC].valid) {

		qbg_dbg(chip, QBG_DEBUG_SOC, "udata update: QBG_SOC=%d QBG_SYS_SOC=%d\n",
				chip->udata.param[QBG_PARAM_SOC].valid ?
				chip->udata.param[QBG_PARAM_SOC].data : -EINVAL,
				chip->udata.param[QBG_PARAM_SYS_SOC].valid ?
				chip->udata.param[QBG_PARAM_SYS_SOC].data : -EINVAL);

		if (chip->udata.param[QBG_PARAM_SYS_SOC].valid) {
			chip->sys_soc = chip->udata.param[QBG_PARAM_SYS_SOC].data;

			rc = qbg_write_iio_chan(chip, SYS_SOC, chip->sys_soc);
			if (rc < 0)
				pr_err("Failed to write battery sys_soc, rc=%d\n", rc);
		}

		chip->soc = chip->udata.param[QBG_PARAM_SOC].data;
	}

	if (chip->udata.param[QBG_PARAM_ESR].valid) {
		chip->esr = chip->udata.param[QBG_PARAM_ESR].data;

		if (chip->qbg_psy)
			power_supply_changed(chip->qbg_psy);
	}

	if (chip->udata.param[QBG_PARAM_OCV_UV].valid) {
		chip->ocv_uv = chip->udata.param[QBG_PARAM_OCV_UV].data;
		update_ocv_for_boost(chip);
	}

	if (chip->udata.param[QBG_PARAM_CHARGE_CYCLE_COUNT].valid)
		chip->charge_cycle_count =
			chip->udata.param[QBG_PARAM_CHARGE_CYCLE_COUNT].data;

	if (chip->udata.param[QBG_PARAM_LEARNED_CAPACITY].valid)
		chip->learned_capacity =
			chip->udata.param[QBG_PARAM_LEARNED_CAPACITY].data;

	if (chip->udata.param[QBG_PARAM_TTF_100MS].valid)
		chip->ttf = chip->udata.param[QBG_PARAM_TTF_100MS].data;

	if (chip->udata.param[QBG_PARAM_TTE_100MS].valid)
		chip->tte = chip->udata.param[QBG_PARAM_TTE_100MS].data;

	if (chip->udata.param[QBG_PARAM_SOH].valid)
		chip->soh = chip->udata.param[QBG_PARAM_SOH].data;

	if (chip->udata.param[QBG_PARAM_TBAT].valid)
		chip->tbat = chip->udata.param[QBG_PARAM_TBAT].data;

	if (chip->udata.param[QBG_PARAM_ESSENTIAL_PARAM_REVID].valid) {
		chip->essential_param_revid =
			chip->udata.param[QBG_PARAM_ESSENTIAL_PARAM_REVID].data;

		rc = qbg_sdam_set_essential_param_revid(chip, chip->essential_param_revid);
		if (rc < 0)
			pr_err("Failed to set essential param revid in sdam, rc=%d\n",
				rc);
	}

	rc = qbg_sdam_set_battery_id(chip, chip->batt_id_ohm / 1000);
	if (rc < 0)
		pr_err("Failed to set battid in sdam, rc=%d\n", rc);

	__pm_relax(chip->qbg_ws);

	qbg_dbg(chip, QBG_DEBUG_STATUS, "udata update: batt_soc=%d sys_soc=%d soc=%d qbg_esr=%d\n",
		(chip->batt_soc != INT_MIN) ? chip->batt_soc : -EINVAL,
		(chip->sys_soc != INT_MIN) ? chip->sys_soc : -EINVAL,
		chip->soc, chip->esr);
}

static int qbg_decode_fifo_data(struct fifo_data fifo, unsigned int *vbat1,
				unsigned int *vbat2, int *ibat,
				unsigned int *tbat, unsigned int *ibat_t)
{
	unsigned short int acc0_data = 0, acc1_data = 0, acc2_data = 0,
			tbat_data = 0;
	unsigned int ibat_temp;

	if (!vbat1 || !vbat2 || !ibat || !tbat || !ibat_t)
		return -EINVAL;

	acc0_data = fifo.v1;
	acc1_data = fifo.v2;
	acc2_data = fifo.i;
	tbat_data = fifo.tbat;

	ibat_temp = acc2_data;

	*vbat1 = acc0_data * VBATT_1S_LSB;
	*ibat = ((int16_t)ibat_temp) * IBATT_10A_LSB * ICHG_FS_10A;
	*tbat = tbat_data;
	*ibat_t = fifo.ibat;

	return 0;
}

static int get_rtc_time(struct qti_qbg *chip, time64_t *rtc_time)
{
	struct rtc_time tm;
	int rc;

	if (!chip->rtc) {
		pr_err("Invalid RTC handle\n");
		return -EINVAL;
	}

	rc = rtc_read_time(chip->rtc, &tm);
	if (rc) {
		pr_err("Failed to read rtc time (%s) : %d\n",
				CONFIG_RTC_HCTOSYS_DEVICE, rc);
		goto close_time;
	}

	rc = rtc_valid_tm(&tm);
	if (rc) {
		pr_err("Invalid RTC time (%s): %d\n",
				CONFIG_RTC_HCTOSYS_DEVICE, rc);
		goto close_time;
	}
	*rtc_time = rtc_tm_to_time64(&tm);

	return rc;

close_time:
	rtc_class_close(chip->rtc);
	chip->rtc = NULL;

	return rc;
}

#define BAT_THERM_CAL_DENOM		2080
#define BAT_THERM_CAL_NUM		10000
#define BAT_THERM_SCALE			100
static int qbg_set_therm_trace_resistance(struct qti_qbg *chip, int vref_adc,
			int tbat_adc, int ibat_10na, int ibat_esr_10na, int tbat_esr_adc)
{
	int rc = 0;
	int64_t r_pack_nom = 0;
	int64_t r_pack = 0;
	int64_t r_pack_denom = 0;
	u8 reg_val = 0;

	int tbat_decidegree = div64_s64(((int64_t)tbat_adc * TBAT_LSB * BAT_THERM_SCALE),
					TBAT_DENOMINATOR);
	int tbat_esr_decidegree = div64_s64(((int64_t)tbat_esr_adc * TBAT_LSB * BAT_THERM_SCALE),
					TBAT_DENOMINATOR);
	int vref_tbat_decidegree = div64_s64(((int64_t)vref_adc * TBAT_LSB * BAT_THERM_SCALE),
					TBAT_DENOMINATOR);

	r_pack_nom = tbat_decidegree * (vref_tbat_decidegree - tbat_esr_decidegree)
			- tbat_esr_decidegree * (vref_tbat_decidegree - tbat_decidegree);

	r_pack_denom = DIV_ROUND_CLOSEST(ibat_esr_10na, MILLI_TO_10NANO)
				* (vref_tbat_decidegree - tbat_decidegree)
			- DIV_ROUND_CLOSEST(ibat_10na, MILLI_TO_10NANO)
				* (vref_tbat_decidegree - tbat_esr_decidegree);

	if (r_pack_denom != 0) {
		r_pack = div64_s64(r_pack_nom * BAT_THERM_CAL_NUM, r_pack_denom);
		r_pack = div64_s64(r_pack, BAT_THERM_CAL_DENOM);
		reg_val = (u8)r_pack;

		qbg_dbg(chip, QBG_DEBUG_SDAM, "tbat=%d, tbat_esr=%d vref=%d 10uv, therm trace resistance is %#x\n",
			tbat_decidegree, tbat_esr_decidegree, vref_tbat_decidegree, reg_val);

		rc = regmap_write(chip->regmap, chip->adc_cmn_wb_base + BAT_THERM_IBAT_CTL_2,
					reg_val);
		if (rc < 0)
			return rc;
	}

	return 0;
}

static int qbg_process_fifo(struct qti_qbg *chip, u32 fifo_count)
{
	struct fifo_data *fifo;
	int rc = 0, i = 0, ibat = 0, ibat_esr = 0;
	unsigned char data_tag;
	unsigned int vbat1 = 0, vbat2 = 0, tbat = 0, ibat_t = 0, esr = 0;
	unsigned int vbat1_esr = 0, vbat2_esr = 0, tbat_esr = 0, ibat_t_esr = 0;
	time64_t timestamp = 0;

	if (!fifo_count) {
		qbg_dbg(chip, QBG_DEBUG_SDAM, "No FIFO data\n");
		return -EINVAL;
	}

	fifo = kcalloc(fifo_count, sizeof(*fifo), GFP_KERNEL);
	if (!fifo)
		return -ENOMEM;

	rc = get_rtc_time(chip, &timestamp);
	if (rc < 0) {
		pr_err("Failed to read rtc time, rc=%d\n", rc);
		return rc;
	}

	chip->kdata.param[QBG_PARAM_FIFO_TIMESTAMP].valid = true;
	chip->kdata.param[QBG_PARAM_FIFO_TIMESTAMP].data = timestamp;

	/* Read raw FIFO data from SDAM */
	rc = qbg_sdam_get_fifo_data(chip, fifo, fifo_count);
	if (rc < 0) {
		pr_err("Failed to get QBG FIFO data, rc=%d\n", rc);
		goto ret;
	}

	data_tag = fifo[0].data_tag;
	if (fifo_count > QBG_SDAM_ESR_PULSE_FIFO_INDEX)
		data_tag = fifo[QBG_SDAM_ESR_PULSE_FIFO_INDEX].data_tag;

	data_tag = (data_tag & 0xC0) >> 6;

	if (data_tag == QBG_FAST_CHAR) {
		if (fifo_count <= QBG_SDAM_ESR_PULSE_FIFO_INDEX) {
			pr_err("Not enough FIFO samples in fast char mode\n");
			goto qbg_cal;
		}

		rc = qbg_decode_fifo_data(fifo[QBG_SDAM_ESR_PULSE_FIFO_INDEX - 1],
						&vbat1, &vbat2, &ibat, &tbat,
						&ibat_t);
		if (rc < 0) {
			pr_err("Couldn't decode FIFO before ESR pulse in fast char mode, rc=%d\n",
				rc);
			goto qbg_cal;
		}

		rc = qbg_decode_fifo_data(fifo[QBG_SDAM_ESR_PULSE_FIFO_INDEX],
						&vbat1_esr, &vbat2_esr,
						&ibat_esr, &tbat_esr,
						&ibat_t_esr);
		if (rc < 0) {
			pr_err("Couldn't decode FIFO before ESR pulse in fast char mode, rc=%d\n",
				rc);
			goto qbg_cal;
		}

		if ((ibat < ibat_esr)
			&& (ibat < (ESR_PULSE_10NA(ESR_FCC_150MA) + ESR_IBAT_QUALIFY_OFFSET_10NA))
			&& (vbat1 > vbat1_esr) && (tbat > tbat_esr)) {
			esr = (vbat1 - vbat1_esr) / ((ibat_esr - ibat) / 10000);
			esr *= 10000;
			chip->esr = esr;
			qbg_dbg(chip, QBG_DEBUG_SDAM, "ESR:%u, ibat=%d ibat_esr=%d vbat1:%u vbat1_esr:%u\n",
				esr, ibat, ibat_esr, vbat1, vbat1_esr);

			rc = qbg_set_therm_trace_resistance(chip,
				fifo[QBG_SDAM_ESR_PULSE_FIFO_INDEX - 1].vref,
				tbat, ibat, ibat_esr, tbat_esr);
			if (rc < 0) {
				pr_err("Failed to set therm trace resistance, rc=%d\n", rc);
				goto qbg_cal;
			}
		} else {
			pr_err("Couldn't calculate ESR, ibat=%d ibat_esr=%d vbat1:%u vbat1_esr:%u\n",
				ibat, ibat_esr, vbat1, vbat1_esr);
		}
	}

qbg_cal:
	for (i = 0; i < fifo_count; i++) {
		rc = qbg_decode_fifo_data(fifo[i], &vbat1, &vbat2, &ibat, &tbat,
						&ibat_t);
		if (rc < 0) {
			pr_err("Failed to decode fifo %d, rc=%d\n", i, rc);
			goto ret;
		}

		qbg_dbg(chip, QBG_DEBUG_SDAM, "vbat1:%u ibat:%d tbat:%u ibat_t:%u\n",
			vbat1, ibat, tbat, ibat_t);

		chip->kdata.fifo[i].v1 = vbat1;
		chip->kdata.fifo[i].v2 = vbat2;
		chip->kdata.fifo[i].i = ibat;
		chip->kdata.fifo[i].tbat = tbat;
		chip->kdata.fifo[i].ibat = ibat_t;
		chip->kdata.fifo[i].data_tag = fifo[i].data_tag;
		chip->kdata.fifo[i].qg_sts = fifo[i].qg_sts;
	}

	if (!chip->enable_fifo_depth_half && ((-1 * ibat) > chip->rated_capacity)) {
		chip->enable_fifo_depth_half = true;
		rc = qbg_hpm_fifo_depth_half(chip, fifo_count, chip->enable_fifo_depth_half);
		qbg_dbg(chip, QBG_DEBUG_SDAM, "HPM FIFO count reduced by half ibat = %d rated_capacity=%d\n",
				ibat, chip->rated_capacity);
	} else if (chip->enable_fifo_depth_half && ((-1 * ibat) < chip->rated_capacity)) {
		chip->enable_fifo_depth_half = false;
		rc = qbg_hpm_fifo_depth_half(chip, fifo_count, chip->enable_fifo_depth_half);
		qbg_dbg(chip, QBG_DEBUG_SDAM, "HPM FIFO count restored ibat = %d rated_capacity=%d\n",
				ibat, chip->rated_capacity);
	}

	if (rc < 0)
		pr_err("Failed to update fifo depth, rc=%d\n", rc);

ret:
	kfree(fifo);
	return rc;
}

static int qbg_clear_fifo_data(struct qti_qbg *chip)
{
	int rc = 0, i;
	u8 val[2] = {0, 0};

	rc = qbg_sdam_write(chip,
		QBG_SDAM_BASE(chip, SDAM_CTRL0) + QBG_SDAM_FIFO_COUNT_OFFSET,
		val, 2);
	if (rc < 0) {
		pr_err("Failed to clear QBG FIFO Count, rc=%d\n", rc);
		return rc;
	}

	for (i = 0; i < 10; i++) {
		val[0] = 0;
		rc = qbg_sdam_write(chip,
			QBG_SDAM_BASE(chip, SDAM_CTRL0) + QBG_SDAM_INT_TEST_VAL,
			val, 1);
		if (rc < 0) {
			pr_err("Failed to SDAM0 test val to 0, rc=%d\n", rc);
			return rc;
		}

		/* Handshake with PBS to access FIFO data */
		rc = qbg_sdam_read(chip,
			QBG_SDAM_BASE(chip, SDAM_CTRL0) + QBG_SDAM_INT_TEST_VAL,
			val, 1);
		if (rc < 0) {
			pr_err("Failed to read QBG SDAM, rc=%d\n", rc);
			return rc;
		}

		if (val[0] == 0)
			break;
	}
	if (i == 10)
		return -EINVAL;

	val[0] = QBG_SDAM_START_OFFSET;
	val[1] = 0x0;
	rc = qbg_sdam_write(chip,
		QBG_SDAM_BASE(chip, SDAM_DATA0) + QBG_SDAM_DATA_PUSH_COUNTER_OFFSET,
		val, 2);
	if (rc < 0) {
		pr_err("Failed to configure QBG data push counter, rc=%d\n", rc);
		return rc;
	}
	val[0] = 0x0;
	rc = qbg_sdam_write(chip,
		QBG_SDAM_BASE(chip, SDAM_CTRL0) + QBG_SDAM_PBS_STATUS_OFFSET,
		val, 1);
	if (rc < 0) {
		pr_err("Failed to set QBG PBS status, rc=%d\n", rc);
		return rc;
	}

	return 0;
}

static int qbg_enable_data_full_int(struct qti_qbg *chip, bool enable)
{
	/* Writing 0x80 while FIFO is full will raise the interrupt */
	u8 val = (enable ? 0x80 : 0x00);

	return qbg_sdam_write(chip,
			QBG_SDAM_BASE(chip, SDAM_CTRL0) + QBG_SDAM_INT_TEST1,
			&val, 1);
}

static int qbg_init_sdam(struct qti_qbg *chip)
{
	int rc = 0;
	u8 val[2] = {0};

	val[0] = 0x80;
	rc = qbg_sdam_write(chip,
		QBG_SDAM_BASE(chip, SDAM_CTRL0) + QBG_SDAM_INT_TEST1, val, 1);
	if (rc < 0) {
		pr_err("Failed to write QBG SDAM, rc=%d\n", rc);
		return rc;
	}

	val[0] = 0;
	rc = qbg_sdam_read(chip,
		QBG_SDAM_BASE(chip, SDAM_CTRL0) +  QBG_SDAM_INT_TEST_VAL,
		val, 1);
	if (rc < 0) {
		pr_err("Faiiled to read QBG SDAM, rc=%d\n", rc);
		return rc;
	}

	if (val[0] == QBG_SDAM_TEST_VAL_1_SET) {
		rc = qbg_clear_fifo_data(chip);
		if (rc < 0) {
			pr_err("Failed to clear QBG FIFO data, rc=%d\n", rc);
			return rc;
		}
	}

	return rc;
}

static int qbg_init_esr(struct qti_qbg *chip)
{
	int rc = 0;
	u8 val = ESR_FCC_150MA;

	rc = qbg_sdam_write(chip,
		QBG_SDAM_BASE(chip, SDAM_CTRL0) + QBG_ESR_PULSE_FCC_REDUCE_OFFSET, &val, 1);
	if (rc < 0)
		pr_err("Failed to write QBG ESR pulse FCC reduction offset, rc=%d\n", rc);

	return rc;
}

#define QBG_VBAT_EMPTY_STEP_THRES_UV 49920
static void qbg_init_vbatt_empty_threshold(struct qti_qbg *chip)
{
	int rc = 0;
	u8 val = 0;
	u32 vbatt_in_uv = 0;

	/* Check for Vbatt empty threshold limits -- Should be b/w 0 to 6V */
	if (chip->vbatt_empty_threshold_mv == 0 || chip->vbatt_empty_threshold_mv > 6000) {
		pr_info("Invalid Vbatt_empty threshold, value: %d\n",
				chip->vbatt_empty_threshold_mv);
		return;
	}

	vbatt_in_uv = chip->vbatt_empty_threshold_mv * 1000;

	val = (u8) (vbatt_in_uv / QBG_VBAT_EMPTY_STEP_THRES_UV);

	rc = qbg_write(chip, QBG_MAIN_VBAT_EMPTY_THRESH,
			&val, 1);
	if (rc < 0)
		pr_err("Failed to write QBG VBATT EMPTY THRESHOLD offset, rc=%d\n", rc);

}

static int qbg_force_fast_char(struct qti_qbg *chip, bool force)
{
	int rc = 0;
	/* The default ground used for BAT_THERM measurement is GND_XOADC*/
	u8 batt_therm_gnd_sel = 0x80;
	u8 val = force ? (1 << FORCE_FAST_CHAR_SHIFT) : 0;

	rc = qbg_write(chip, QBG_MAIN_QBG_STATE_FORCE_CMD, &val, 1);
	if (rc < 0) {
		pr_err("Failed to %s QBG fast char mode, rc=%d\n",
			force ? "enable" : "disable", rc);
		return rc;
	}

	/* Set the ground of BAT_THERM measurement to PACK_SNS_M */
	if (force)
		batt_therm_gnd_sel = 0x40;

	rc = regmap_write(chip->regmap, chip->adc_cmn_base + VADC_ANA_PARAM_4, batt_therm_gnd_sel);
	if (rc < 0)
		pr_err("Failed to set batt therm gnd sel rc=%d\n", rc);

	return rc;
}

static int qbg_handle_fast_char(struct qti_qbg *chip)
{
	int rc = 0;
	ktime_t now;
	u8 *data;
	ssize_t len = 0;

	if (chip->skip_esr_state) {
		data = nvmem_cell_read(chip->skip_esr_state, &len);
		if (IS_ERR(data)) {
			pr_err("Failed to read skip_esr_state from SDAM\n");
			return PTR_ERR(data);
		}

		if (*data) {
			qbg_dbg(chip, QBG_DEBUG_STATUS, "skip_esr=1 in_fast_char=%d\n",
				chip->in_fast_char);

			if (chip->in_fast_char) {
				rc = qbg_force_fast_char(chip, false);
				if (rc < 0) {
					pr_err("Failed to get out of fast char mode, rc=%d\n",
						rc);
					return rc;
				}
				chip->in_fast_char = false;
			}

			return 0;
		}
	}

	if (chip->in_fast_char) {
		qbg_dbg(chip, QBG_DEBUG_STATUS, "QBG ESR pulse disabled\n");
		rc = qbg_force_fast_char(chip, false);
		if (rc < 0) {
			pr_err("Failed to get out of fast char mode, rc=%d\n",
				rc);
			return rc;
		}
		chip->in_fast_char = false;
	} else if (chip->charge_type == POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE) {
		now = ktime_get();
		if ((ktime_ms_delta(now, chip->last_fast_char_time) >
			QBG_FAST_CHAR_DELTA_MS) && !chip->in_fast_char) {
			qbg_dbg(chip, QBG_DEBUG_STATUS, "QBG ESR pulse enabled\n");
			rc = qbg_force_fast_char(chip, true);
			if (rc < 0) {
				pr_err("Failed to put QBG to fast char mode, rc=%d\n",
					rc);
				return rc;
			}
			chip->in_fast_char = true;
			chip->last_fast_char_time = now;
		}
	}

	return rc;
}

static irqreturn_t qbg_data_full_irq_handler(int irq, void *_chip)
{
	struct qti_qbg *chip = _chip;
	int rc;
	u32 fifo_count = 0;

	qbg_dbg(chip, QBG_DEBUG_IRQ, "DATA FULL IRQ triggered\n");

	/* Disable fast char for PM5100 V1 */
	if (chip->rev4 != REVISION_V1) {
		rc = qbg_handle_fast_char(chip);
		if (rc < 0) {
			pr_err("Failed to handle QBG fast char, rc=%d\n", rc);
			return IRQ_HANDLED;
		}
	}

	mutex_lock(&chip->fifo_lock);
	memset(&chip->kdata.fifo, 0, sizeof(struct k_fifo_data));

	rc = qbg_get_fifo_count(chip, &fifo_count);
	if (rc < 0) {
		pr_err("Failed to get QBG FIFO length, rc=%d\n", rc);
		goto done;
	}

	rc = qbg_process_fifo(chip, fifo_count);
	if (rc < 0) {
		pr_err("Failed to process QBG FIFO, rc=%d\n", rc);
		goto done;
	}
	chip->kdata.fifo_count = fifo_count;

	rc = qbg_init_sdam(chip);
	if (rc < 0) {
		pr_err("Failed to initialize QBG sdam, rc=%d\n", rc);
		goto done;
	}

	chip->data_ready = true;
	/* Wakeup the read thread */
	wake_up_interruptible(&chip->qbg_wait_q);
done:
	mutex_unlock(&chip->fifo_lock);
	return IRQ_HANDLED;
}

static irqreturn_t qbg_vbatt_empty_irq_handler(int irq, void *_chip)
{
	struct qti_qbg *chip = _chip;

	qbg_dbg(chip, QBG_DEBUG_IRQ, "Vbatt empty IRQ Triggered\n");

	return IRQ_HANDLED;
}


static int qbg_notifier_cb(struct notifier_block *nb,
			unsigned long event, void *data)
{
	struct power_supply *psy = data;
	struct qti_qbg *chip = container_of(nb, struct qti_qbg, nb);

	if (event != PSY_EVENT_PROP_CHANGED)
		return NOTIFY_OK;

	if (work_pending(&chip->status_change_work))
		return NOTIFY_OK;

	if ((strcmp(psy->desc->name, "battery") == 0) ||
	    (strcmp(psy->desc->name, "usb") == 0) ||
	    (strcmp(psy->desc->name, "dc") == 0)) {
		schedule_work(&chip->status_change_work);
	}

	return NOTIFY_OK;
}

static int qbg_read_range_data_from_node(struct device_node *node,
		const char *prop_str, struct ranges *ranges,
		int max_threshold, u32 max_value)
{
	int rc = 0, i, length, per_tuple_length, tuples;

	if (!node || !prop_str || !ranges) {
		pr_err("Invalid parameters passed\n");
		return -EINVAL;
	}

	rc = of_property_count_elems_of_size(node, prop_str, sizeof(u32));
	if (rc < 0) {
		pr_err("Count %s failed, rc=%d\n", prop_str, rc);
		return rc;
	}

	length = rc;
	per_tuple_length = sizeof(struct range_data) / sizeof(u32);
	if (length % per_tuple_length) {
		pr_err("%s length (%d) should be multiple of %d\n",
				prop_str, length, per_tuple_length);
		return -EINVAL;
	}

	tuples = length / per_tuple_length;
	if (tuples > QBG_MAX_STEP_CHG_ENTRIES) {
		pr_err("too many entries(%d), only %d allowed\n",
				tuples, QBG_MAX_STEP_CHG_ENTRIES);
		return -EINVAL;
	}

	rc = of_property_read_u32_array(node, prop_str, (u32 *)ranges->data, length);
	if (rc < 0) {
		pr_err("Read %s failed, rc=%d\n", prop_str, rc);
		return rc;
	}

	for (i = 0; i < tuples; i++) {
		if (ranges->data[i].low_threshold >
				ranges->data[i].high_threshold) {
			pr_err("%s thresholds should be in ascendant ranges data\n",
						prop_str);
			rc = -EINVAL;
			goto clean;
		}

		if (i != 0) {
			if (ranges->data[i - 1].high_threshold >
					ranges->data[i].low_threshold) {
				pr_err("%s thresholds should be in ascendant ranges data\n",
							prop_str);
				rc = -EINVAL;
				goto clean;
			}
		}

		if (ranges->data[i].low_threshold > max_threshold)
			ranges->data[i].low_threshold = max_threshold;
		if (ranges->data[i].high_threshold > max_threshold)
			ranges->data[i].high_threshold = max_threshold;
		if (ranges->data[i].value > max_value)
			ranges->data[i].value = max_value;
	}

	ranges->range_count = tuples;
	ranges->valid = true;
	return rc;
clean:
	memset(ranges, 0, tuples * sizeof(struct range_data));
	return rc;
}

#define QBG_DEFAULT_JEITA_FULL_FV_10NV		420000000
#define QBG_DEFAULT_BATTERY_BETA		4250
#define QBG_DEFAULT_BATTERY_THERM_KOHM		100
#define QBG_DEFAULT_JEITA_WARM_ADC_DATA		0x25e3 /* WARM = 40 DegC */
#define QBG_DEFAULT_JEITA_COOL_ADC_DATA		0x5314 /* COOL = 5 DegC */
static int parse_step_chg_jeita_params(struct qti_qbg *chip, struct device_node *profile_node)
{
	struct qbg_step_chg_jeita_params *step_chg_jeita;
	bool soc_based_step_chg = false;
	bool ocv_based_step_chg = false;
	int rc;
	int i = 0;
	u32 temp[2];
	bool jeita_thresh_valid = true;

	step_chg_jeita = devm_kzalloc(chip->dev, sizeof(*step_chg_jeita),
				GFP_KERNEL);
	if (!step_chg_jeita)
		return -ENOMEM;

	/* choose the lower of the two soft threaholds as the jeita-full-fv */
	step_chg_jeita->jeita_full_fv_10nv = QBG_DEFAULT_JEITA_FULL_FV_10NV;
	rc = of_property_read_u32_array(profile_node, "qcom,jeita-soft-fv-uv",
				temp, 2);
	if (!rc)
		step_chg_jeita->jeita_full_fv_10nv = min(temp[0], temp[1]) * MICRO_TO_10NANO;
	else
		pr_debug("Failed to read jeita full fv from battery profile, rc=%d\n", rc);

	step_chg_jeita->jeita_full_iterm_10na = chip->iterm_ma * MILLI_TO_10NANO;

	soc_based_step_chg = of_property_read_bool(profile_node, "qcom,soc-based-step-chg");
	ocv_based_step_chg = of_property_read_bool(profile_node, "qcom,ocv-based-step-chg");
	if (soc_based_step_chg)
		step_chg_jeita->ttf_calc_mode = TTF_MODE_SOC_STEP_CHG;
	else if (ocv_based_step_chg)
		step_chg_jeita->ttf_calc_mode = TTF_MODE_OCV_STEP_CHG;

	step_chg_jeita->battery_beta = QBG_DEFAULT_BATTERY_BETA;
	rc = of_property_read_u32(profile_node, "qcom,battery-beta",
				&step_chg_jeita->battery_beta);
	if (rc < 0)
		pr_debug("Failed to read battery beta form battery profile, rc=%d\n", rc);

	step_chg_jeita->battery_therm_kohm = QBG_DEFAULT_BATTERY_THERM_KOHM;
	rc = of_property_read_u32(profile_node, "qcom,battery-therm-kohm",
				&step_chg_jeita->battery_therm_kohm);
	if (rc < 0)
		pr_debug("Failed to read battery therm from battery profile, rc=%d\n", rc);

	step_chg_jeita->jeita_cool_adc_value = QBG_DEFAULT_JEITA_COOL_ADC_DATA;
	step_chg_jeita->jeita_warm_adc_value = QBG_DEFAULT_JEITA_WARM_ADC_DATA;
	rc = of_property_read_u32_array(profile_node, "qcom,jeita-soft-thresholds",
				temp, 2);
	if (!rc) {
		step_chg_jeita->jeita_cool_adc_value = temp[0];
		step_chg_jeita->jeita_warm_adc_value = temp[1];
	} else {
		jeita_thresh_valid = false;
		pr_debug("Failed to read jeita cool and warm value from battery profile, rc=%d\n",
				 rc);
	}

	rc = qbg_read_range_data_from_node(profile_node,
			"qcom,jeita-fcc-ranges",
			&step_chg_jeita->jeita_fcc_cfg,
			BATT_HOT_DECIDEGREE_MAX,
			chip->fastchg_curr_ma * MILLI_TO_MICRO);
	if (rc < 0)
		pr_debug("Failed to read qcom,jeita-fcc-ranges from battery profile, rc=%d\n",
					rc);

	rc = qbg_read_range_data_from_node(profile_node,
			"qcom,jeita-fv-ranges",
			&step_chg_jeita->jeita_fv_cfg,
			BATT_HOT_DECIDEGREE_MAX, chip->float_volt_uv);
	if (rc < 0)
		pr_debug("Failed to read qcom,jeita-fv-ranges from battery profile, rc=%d\n",
					rc);

	if ((step_chg_jeita->jeita_fcc_cfg.valid != step_chg_jeita->jeita_fv_cfg.valid)
		|| (jeita_thresh_valid != step_chg_jeita->jeita_fcc_cfg.valid)) {
		pr_err("battery JEITA configuration is not valid\n");
		return -EINVAL;
	}

	rc = qbg_read_range_data_from_node(profile_node,
			"qcom,step-chg-ranges",
			&step_chg_jeita->step_fcc_cfg,
			soc_based_step_chg ? 100 : chip->float_volt_uv,
			chip->fastchg_curr_ma * MILLI_TO_MICRO);
	if (rc < 0)
		pr_debug("Failed to read qcom,step-chg-ranges from battery profile, rc=%d\n",
					rc);

	qbg_dbg(chip, QBG_DEBUG_PROFILE, "jeita_full_fv = %dnv, jeita_full_iterm = %dna, jeita_warm_adc = 0x%x, jeita_cool_adc = 0x%x, battery_beta = %d, battery_therm = %dkohm\n",
			step_chg_jeita->jeita_full_fv_10nv,
			step_chg_jeita->jeita_full_iterm_10na,
			step_chg_jeita->jeita_warm_adc_value,
			step_chg_jeita->jeita_cool_adc_value,
			step_chg_jeita->battery_beta,
			step_chg_jeita->battery_therm_kohm);

	pr_debug("step-chg-ranges:\n");
	for (i = 0; i < step_chg_jeita->step_fcc_cfg.range_count; i++) {
		pr_debug("%d %d %d\n",
			step_chg_jeita->step_fcc_cfg.data[i].low_threshold,
			step_chg_jeita->step_fcc_cfg.data[i].high_threshold,
			step_chg_jeita->step_fcc_cfg.data[i].value);
	}

	pr_debug("jeita-fcc-ranges:\n");
	for (i = 0; i < step_chg_jeita->jeita_fcc_cfg.range_count; i++) {
		pr_debug("%d %d %d\n",
			step_chg_jeita->jeita_fcc_cfg.data[i].low_threshold,
			step_chg_jeita->jeita_fcc_cfg.data[i].high_threshold,
			step_chg_jeita->jeita_fcc_cfg.data[i].value);
	}

	pr_debug("jeita-fv-ranges:\n");
	for (i = 0; i < step_chg_jeita->jeita_fv_cfg.range_count; i++) {
		pr_debug("%d %d %d\n",
			step_chg_jeita->jeita_fv_cfg.data[i].low_threshold,
			step_chg_jeita->jeita_fv_cfg.data[i].high_threshold,
			step_chg_jeita->jeita_fv_cfg.data[i].value);
	}

	chip->step_chg_jeita_params = step_chg_jeita;

	return 0;
}

#define QBG_DEFAULT_RECHARGE_ITERM_MA                   150
#define QBG_DEFAULT_RECHARGE_SOC_DELTA                  5
#define QBG_DEFAULT_RECHARGE_VFLT_DELTA                 100
static int qbg_load_battery_profile(struct qti_qbg *chip)
{
	struct device_node *node = chip->dev->of_node;
	struct device_node *profile_node;
	int rc;
	u32 temp[2];

	chip->batt_node = of_find_node_by_name(node, "qcom,battery-data");
	if (!chip->batt_node) {
		pr_err("Batterydata not available\n");
		return -ENXIO;
	}

	profile_node = of_batterydata_get_best_profile(chip->batt_node,
			chip->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 QBG battery profile, rc=%d\n",
			rc);
		goto out;
	}

	chip->battery = devm_kzalloc(chip->dev, sizeof(*chip->battery), GFP_KERNEL);
	if (!chip->battery) {
		rc = -ENOMEM;
		goto out;
	}

	rc = qbg_batterydata_init(profile_node, chip->battery);
	if (rc < 0) {
		pr_err("Failed to load batterydata, rc=%d\n", rc);
		chip->profile_loaded = false;
		goto out;
	}

	rc = of_property_read_string(profile_node, "qcom,battery-type",
					&chip->batt_type_str);
	if (rc < 0) {
		pr_err("Failed to get battery name, rc=%d\n", rc);
		goto out;
	}

	rc = of_property_read_u32(profile_node, "qcom,max-voltage-uv",
				&chip->float_volt_uv);
	if (rc < 0) {
		pr_err("Failed to read battery float-voltage, rc:%d\n", rc);
		chip->float_volt_uv = -EINVAL;
		goto out;
	}

	rc = of_property_read_u32(profile_node, "qcom,fastchg-current-ma",
				&chip->fastchg_curr_ma);
	if (rc < 0) {
		pr_err("Failed to read battery fastcharge current, rc:%d\n", rc);
		chip->fastchg_curr_ma = -EINVAL;
		goto out;
	}

	rc = of_property_read_u32(profile_node, "qcom,capacity", &chip->rated_capacity);
	if (rc < 0) {
		pr_err("Failed to read battery rated capacity, rc:%d\n", rc);
		chip->rated_capacity = -EINVAL;
		goto out;
	}
	chip->rated_capacity *= MILLI_TO_10NANO;

	rc = of_property_read_u32_array(profile_node, "qcom,battery-capacity", temp, 2);
	if (rc) {
		pr_err("Failed to read battery nominal capacity, rc=%d\n", rc);
		goto out;
	}
	chip->nominal_capacity = temp[0];

	chip->recharge_iterm_ma = QBG_DEFAULT_RECHARGE_ITERM_MA;
	rc = of_property_read_u32(profile_node, "qcom,recharge-iterm-ma", &temp[0]);
	if (!rc)
		chip->recharge_iterm_ma = temp[0];

	chip->recharge_soc = 100 - QBG_DEFAULT_RECHARGE_SOC_DELTA;
	rc = of_property_read_u32(profile_node, "qcom,recharge-soc-delta", &temp[0]);
	if (!rc)
		chip->recharge_soc = 100 - temp[0];

	chip->recharge_vflt_delta_mv = QBG_DEFAULT_RECHARGE_VFLT_DELTA;
	rc = of_property_read_u32(profile_node, "qcom,recharge-vflt-delta", &temp[0]);
	if (!rc)
		chip->recharge_vflt_delta_mv = temp[0];

	qbg_dbg(chip, QBG_DEBUG_SDAM, "Recharge SOC=% Recharge-Vflt=%duV Recharge-Ibat=%dmA\n",
		chip->recharge_soc, chip->recharge_vflt_delta_mv, chip->recharge_iterm_ma);

	rc = parse_step_chg_jeita_params(chip, profile_node);
out:
	of_node_put(chip->batt_node);
	return rc;
}


#define DEBUG_BATT_ID_LOW	6000
#define DEBUG_BATT_ID_HIGH	8500
static bool is_debug_batt_id(struct qti_qbg *chip)
{
	if (is_between(DEBUG_BATT_ID_LOW, DEBUG_BATT_ID_HIGH,
					chip->batt_id_ohm))
		return true;

	return false;
}

#define DEFAULT_BATT_TYPE	"Unknown Battery"
#define MISSING_BATT_TYPE	"Missing Battery"
#define DEBUG_BATT_TYPE		"Debug Board"
static const char *qbg_get_battery_type(struct qti_qbg *chip)
{
	if (chip->battery_missing)
		return MISSING_BATT_TYPE;

	if (is_debug_batt_id(chip))
		return DEBUG_BATT_TYPE;

	if (chip->batt_type_str && chip->profile_loaded)
		return chip->batt_type_str;

	return DEFAULT_BATT_TYPE;
}

#define DEBUG_BATT_SOC		67
#define BATT_MISSING_SOC	50
static int qbg_get_battery_capacity(struct qti_qbg *chip, int *soc)
{
	if (is_debug_batt_id(chip)) {
		*soc = DEBUG_BATT_SOC;
		return 0;
	}

	if (chip->battery_missing || !chip->profile_loaded) {
		*soc = BATT_MISSING_SOC;
		return 0;
	}

	*soc = chip->soc;

	return 0;
}

#define TEN_NANO_TO_MICRO	100
static int qbg_get_battery_voltage(struct qti_qbg *chip, int *vbat_uv)
{
	int rc;
	unsigned short acc0_data;
	u8 buf[2];

	if (chip->battery_missing) {
		*vbat_uv = 3700000;
		return 0;
	}

	rc = qbg_read(chip, QBG_MAIN_LAST_BURST_AVG_ACC0_DATA0, buf, 2);
	if (rc < 0) {
		pr_err("Failed to read average vbatt, rc=%d\n", rc);
		return rc;
	}

	acc0_data = buf[0] | (buf[1] << 8);
	*vbat_uv = acc0_data * VBATT_1S_LSB;
	*vbat_uv = *vbat_uv / TEN_NANO_TO_MICRO;

	return 0;
}

static int qbg_get_battery_current(struct qti_qbg *chip, int *ibat_ua)
{
	int rc;
	unsigned short acc2_data;
	u8 buf[2];

	if (chip->battery_missing) {
		*ibat_ua = 0;
		return 0;
	}

	rc = qbg_read(chip, QBG_MAIN_LAST_BURST_AVG_ACC2_DATA0, buf, 2);
	if (rc < 0) {
		pr_err("Failed to read average ibatt, rc=%d\n", rc);
		return rc;
	}

	acc2_data = buf[0] | (buf[1] << 8);
	*ibat_ua = ((int16_t)acc2_data) * IBATT_10A_LSB * ICHG_FS_10A;
	*ibat_ua = *ibat_ua / TEN_NANO_TO_MICRO;

	return 0;
}

static int qbg_get_battery_temp(struct qti_qbg *chip, int *temp)
{
	int rc;

	if (!chip->batt_temp_chan)
		return -EINVAL;

	rc = iio_read_channel_processed(chip->batt_temp_chan, temp);
	if (rc < 0) {
		pr_err("Failed to read BATT_TEMP over ADC, rc=%d\n", rc);
		return rc;
	}
	chip->tbat = *temp;

	return 0;
}

#define UAH_FACTOR	10
static int qbg_get_charge_counter(struct qti_qbg *chip, int *charge_count)
{

	if (is_debug_batt_id(chip) || chip->battery_missing || chip->battery_unknown) {
		*charge_count = -EINVAL;
		return 0;
	}

	*charge_count = chip->learned_capacity * chip->soc * UAH_FACTOR;

	return 0;
}

static bool is_battery_present(struct qti_qbg *chip)
{
	bool present = false;
	u8 reg;
	int rc;

	rc = qbg_read(chip, QBG_MAIN_STATUS2, &reg, 1);
	if (rc < 0)
		pr_err("Failed to read battery presence, rc=%d\n", rc);
	else
		present = !(reg & QBG_BATTERY_MISSING_BIT);

	return present;
}

static int get_batt_id_ohm(struct qti_qbg *chip, u32 *batt_id_ohm)
{
	int rc, batt_id;

	if (!chip->batt_id_chan)
		return -EINVAL;

	/* Read battery-id */
	rc = iio_read_channel_processed(chip->batt_id_chan, &batt_id);
	if (rc < 0) {
		pr_err("Failed to read BATT_ID over ADC, rc=%d\n", rc);
		return rc;
	}

	*batt_id_ohm = (u32)batt_id;

	qbg_dbg(chip, QBG_DEBUG_PROFILE, "batt_id_ohm=%u\n", *batt_id_ohm);

	return 0;
}

static int qbg_setup_battery(struct qti_qbg *chip)
{
	int rc = 0;
	u8 ocv_boost_val = 0x02;

	chip->profile_loaded = false;
	chip->battery_unknown = false;

	if (!is_battery_present(chip)) {
		qbg_dbg(chip, QBG_DEBUG_PROFILE, "Battery Missing!\n");
		chip->battery_missing = true;
	} else {
		/* battery present */
		rc = get_batt_id_ohm(chip, &chip->batt_id_ohm);
		if (rc < 0) {
			pr_err("Failed to detect batt_id rc=%d\n", rc);
		} else {
			rc = qbg_load_battery_profile(chip);
			if (rc < 0)
				pr_err("Failed to load battery-profile, rc=%d\n", rc);
			else
				chip->profile_loaded = true;
		}

		/* Update OCV boost headroom compensation value to 4.3V in debug battery case */
		if (is_debug_batt_id(chip)) {
			rc = qbg_sdam_write(chip,
				QBG_SDAM_BASE(chip, SDAM_CTRL0) +
				QBG_SDAM_BHARGER_OCV_HDRM_OFFSET, &ocv_boost_val, 1);
		}

		if (!strcmp(qbg_get_battery_type(chip), DEFAULT_BATT_TYPE)) {
			chip->batt_type_str = DEFAULT_BATT_TYPE;
			chip->battery_unknown = true;
			chip->soc = BATT_MISSING_SOC;
			rc = 0;
		}
	}

	qbg_dbg(chip, QBG_DEBUG_PROFILE, "battery_missing=%s batt_id=%d Ohm profile_loaded=%d battery_type=%s\n",
		chip->battery_missing ? "true" : "false", chip->batt_id_ohm,
		chip->profile_loaded, chip->batt_type_str);

	return rc;
}

static int qbg_get_pon_reading(struct qti_qbg *chip, unsigned int *ocv,
				unsigned int *ibat)
{
	int rc;
	unsigned short int ocv0_data, ibat_data;
	u8 acc_data[6];

	rc = qbg_read(chip, QBG_MAIN_PON_OCV_ACC0_DATA0, (u8 *)acc_data, 6);
	if (rc < 0) {
		pr_err("Failed to read PON accumulator data, rc=%d\n", rc);
		return rc;
	}

	ocv0_data = (acc_data[1] << 8) | acc_data[0];
	ibat_data = (acc_data[5] << 8) | acc_data[4];

	*ocv = ocv0_data * VBATT_1S_LSB;
	if (*ocv < VBAT_0PCT_OCV)
		*ocv = VBAT_0PCT_OCV;

	*ibat = ibat_data * IBATT_10A_LSB * ICHG_FS_10A;
	qbg_dbg(chip, QBG_DEBUG_PON, "ocv:%u ibat:%u\n", ocv, ibat);

	return 0;
}

static int qbg_determine_pon_soc(struct qti_qbg *chip)
{
	int rc, batt_temp;
	union power_supply_propval prop = {0, };
	unsigned long time_diff = 0;
	unsigned int pon_ocv;
	time64_t rtc_sec;

	/* Get RTC time here */
	rc = get_rtc_time(chip, &rtc_sec);
	if (rc < 0) {
		pr_err("Failed to read rtc time, rc=%d\n", rc);
		return rc;
	}

	if (!chip->profile_loaded) {
		qbg_dbg(chip, QBG_DEBUG_SOC, "No Profile, skipping PON soc\n");
		return 0;
	}

	rc = qbg_sdam_get_essential_params(chip, (u8 *)&chip->essential_params);
	if (rc < 0) {
		pr_err("Failed to read essential params, rc=%d\n", rc);
		return rc;
	}

	rc = qbg_get_pon_reading(chip, &chip->pon_ocv, &chip->pon_ibat);
	if (rc < 0) {
		pr_err("Failed to get QBG PON reading, rc=%d\n", rc);
		return rc;
	}

	/* Read battery-temp */
	rc = iio_read_channel_processed(chip->batt_temp_chan, &batt_temp);
	if (rc < 0) {
		pr_err("Failed to read BATT_TEMP over ADC, rc=%d\n", rc);
		return rc;
	}
	chip->pon_tbat = (unsigned int)batt_temp;

	if (is_batt_available(chip)) {
		rc = power_supply_get_property(chip->batt_psy,
				POWER_SUPPLY_PROP_STATUS, &prop);
		if (rc < 0)
			pr_err("Failed to get charger status, rc=%d\n", rc);
	}

	pon_ocv = chip->pon_ocv / 10000;
	rc = qbg_lookup_soc_ocv(chip->battery, &chip->pon_soc, pon_ocv,
				prop.intval == POWER_SUPPLY_STATUS_CHARGING);
	if (rc < 0) {
		pr_err("Failed to lookup PON SOC, rc=%d\n", rc);
		return rc;
	}
	chip->pon_soc = DIV_ROUND_CLOSEST(chip->pon_soc, 100);
	chip->soc = chip->pon_soc;

	qbg_dbg(chip, QBG_DEBUG_PON, "Shutdown: SOC=%d OCV=%duV Ibat:%duA timediff=%lusecs, time_now=%lusecs\n",
			chip->pon_soc, chip->pon_ocv, chip->pon_ibat, time_diff, rtc_sec);

	return 0;
}

static int qbg_psy_get_prop(struct power_supply *psy,
			enum power_supply_property psp,
			union power_supply_propval *pval)
{
	if (psp == POWER_SUPPLY_PROP_TYPE)
		pval->intval = POWER_SUPPLY_TYPE_MAINS;

	return 0;
}

static enum power_supply_property qbg_psy_props[] = {
	POWER_SUPPLY_PROP_TYPE,
};

static struct power_supply_desc qbg_psy_desc = {
	.name			= "bms",
	.type			= POWER_SUPPLY_TYPE_MAINS,
	.properties		= qbg_psy_props,
	.num_properties		= ARRAY_SIZE(qbg_psy_props),
	.get_property		= qbg_psy_get_prop,
};

static int qbg_init_psy(struct qti_qbg *chip)
{
	struct power_supply_config qbg_psy_cfg = {};
	int rc;

	qbg_psy_cfg.drv_data = chip;
	chip->qbg_psy = devm_power_supply_register(chip->dev, &qbg_psy_desc,
						&qbg_psy_cfg);
	if (IS_ERR_OR_NULL(chip->qbg_psy)) {
		pr_err("Failed to register qbg_psy, rc = %d\n",
				PTR_ERR(chip->qbg_psy));
		return PTR_ERR(chip->qbg_psy);
	}

	chip->nb.notifier_call = qbg_notifier_cb;
	rc = power_supply_reg_notifier(&chip->nb);
	if (rc < 0)
		pr_err("Failed to register psy notifier rc = %d\n", rc);

	return rc;
}

static int qbg_iio_write_raw(struct iio_dev *indio_dev,
		struct iio_chan_spec const *chan, int val1,
		int val2, long mask)
{
	return 0;
}

static int qbg_iio_read_raw(struct iio_dev *indio_dev,
		struct iio_chan_spec const *chan, int *val1,
		int *val2, long mask)
{
	struct qti_qbg *chip = iio_priv(indio_dev);
	int rc = 0;

	*val1 = 0;

	switch (chan->channel) {
	case PSY_IIO_DEBUG_BATTERY:
		*val1 = is_debug_batt_id(chip);
		break;
	case PSY_IIO_CAPACITY:
		rc = qbg_get_battery_capacity(chip, val1);
		break;
	case PSY_IIO_REAL_CAPACITY:
		rc = qbg_get_battery_capacity(chip, val1);
		break;
	case PSY_IIO_TEMP:
		rc = qbg_get_battery_temp(chip, val1);
		break;
	case PSY_IIO_VOLTAGE_MAX:
		*val1 = chip->float_volt_uv;
		break;
	case PSY_IIO_VOLTAGE_OCV:
		*val1 = chip->ocv_uv;
		break;
	case PSY_IIO_VOLTAGE_AVG:
		rc = qbg_get_battery_voltage(chip, val1);
		break;
	case PSY_IIO_VOLTAGE_NOW:
		rc = qbg_get_battery_voltage(chip, val1);
		break;
	case PSY_IIO_CURRENT_NOW:
		rc = qbg_get_battery_current(chip, val1);
		break;
	case PSY_IIO_RESISTANCE_ID:
		*val1 = chip->batt_id_ohm;
		break;
	case PSY_IIO_TIME_TO_FULL_AVG:
		*val1 = chip->ttf;
		break;
	case PSY_IIO_TIME_TO_FULL_NOW:
		*val1 = chip->ttf;
		break;
	case PSY_IIO_TIME_TO_EMPTY_AVG:
		*val1 = chip->tte;
		break;
	case PSY_IIO_ESR_ACTUAL:
		*val1 = chip->esr;
		break;
	case PSY_IIO_SOH:
		*val1 = chip->soh;
		break;
	case PSY_IIO_CHARGE_COUNTER:
		rc = qbg_get_charge_counter(chip, val1);
		break;
	case PSY_IIO_CYCLE_COUNT:
		*val1 = chip->charge_cycle_count;
		break;
	case PSY_IIO_CHARGE_FULL:
		*val1 = chip->learned_capacity * MILLI_TO_MICRO;
		break;
	case PSY_IIO_CHARGE_FULL_DESIGN:
		*val1 = chip->nominal_capacity * MILLI_TO_MICRO;
		break;
	default:
		pr_err_ratelimited("Unsupported property %d\n", chan->channel);
		rc = -EINVAL;
		break;
	}

	return (rc < 0) ? rc : IIO_VAL_INT;
}

static int qbg_iio_of_xlate(struct iio_dev *indio_dev,
				const struct of_phandle_args *iiospec)
{
	struct qti_qbg *chip = iio_priv(indio_dev);
	struct iio_chan_spec *iio_chan = chip->iio_chan;
	int i;

	for (i = 0; i < ARRAY_SIZE(qbg_iio_psy_channels); i++, iio_chan++) {
		if (iio_chan->channel == iiospec->args[0])
			return i;
	}

	return -EINVAL;
}

static const struct iio_info qbg_iio_info = {
	.read_raw	= qbg_iio_read_raw,
	.write_raw	= qbg_iio_write_raw,
	.of_xlate	= qbg_iio_of_xlate,
};

static int qbg_init_iio(struct qti_qbg *chip,
			struct platform_device *pdev)
{
	struct iio_dev *indio_dev = chip->indio_dev;
	struct iio_chan_spec *chan;
	int qbg_num_iio_channels = ARRAY_SIZE(qbg_iio_psy_channels);
	int rc, i;

	chip->iio_chan = devm_kcalloc(chip->dev, qbg_num_iio_channels,
				sizeof(*chip->iio_chan), GFP_KERNEL);
	if (!chip->iio_chan)
		return -ENOMEM;

	indio_dev->info = &qbg_iio_info;
	indio_dev->dev.parent = chip->dev;
	indio_dev->dev.of_node = chip->dev->of_node;
	indio_dev->name = pdev->name;
	indio_dev->modes = INDIO_DIRECT_MODE;
	indio_dev->channels = chip->iio_chan;
	indio_dev->num_channels = qbg_num_iio_channels;

	for (i = 0; i < qbg_num_iio_channels; i++) {
		chan = &chip->iio_chan[i];
		chan->address = i;
		chan->channel = qbg_iio_psy_channels[i].channel_num;
		chan->type = qbg_iio_psy_channels[i].type;
		chan->datasheet_name =
			qbg_iio_psy_channels[i].datasheet_name;
		chan->extend_name =
			qbg_iio_psy_channels[i].datasheet_name;
		chan->info_mask_separate =
			qbg_iio_psy_channels[i].info_mask;
	}

	rc = devm_iio_device_register(chip->dev, indio_dev);
	if (rc < 0)
		pr_err("Failed to register QBG IIO device, rc=%d\n", rc);

	return rc;
}

static int qbg_get_accumulator_properties(struct qti_qbg *chip,
				enum QBG_STATE state,
				enum QBG_SAMPLE_NUM_TYPE *num_of_accum,
				enum QBG_ACCUM_INTERVAL_TYPE *accum_interval)
{
	int rc = 0;
	unsigned int reg = QBG_MAIN_LPM_MEAS_CTL2;
	unsigned char data = 0;

	if (state >= QBG_STATE_MAX || !num_of_accum || !accum_interval)
		return -EINVAL;

	switch (state) {
	case QBG_LPM:
		reg = QBG_MAIN_LPM_MEAS_CTL2;
		break;
	case QBG_MPM:
		reg = QBG_MAIN_MPM_MEAS_CTL2;
		break;
	case QBG_HPM:
		reg = QBG_MAIN_HPM_MEAS_CTL2;
		break;
	case QBG_FAST_CHAR:
		reg = QBG_MAIN_FAST_CHAR_MEAS_CTL2;
		break;
	default:
		pr_err("Invalid QBG state requested for accumulator properties %u\n",
			state);
		return rc;
	}

	rc = qbg_read(chip, reg, &data, 1);
	if (rc < 0) {
		pr_err("Failed to MEAS_CTL2 %u, rc=%d\n", reg, rc);
		return rc;
	}

	if (state == QBG_FAST_CHAR)
		*num_of_accum = 0;
	else
		*num_of_accum = (data & QBG_NUM_OF_ACCUM_MASK) >> QBG_NUM_OF_ACCUM_SHIFT;

	*accum_interval = data & QBG_ACCUM_INTERVAL_MASK;

	qbg_dbg(chip, QBG_DEBUG_DEVICE, "state:%u num_of_accum:%u accum_interval:%u\n",
			state, *num_of_accum, *accum_interval);

	return rc;
}

static int qbg_get_sample_time_us(struct qti_qbg *chip)
{
	int rc = 0, index;
	unsigned int interval;
	enum QBG_ACCUM_INTERVAL_TYPE accum_interval = ACCUM_INTERVAL_100MS;
	enum QBG_SAMPLE_NUM_TYPE num_accum = SAMPLE_NUM_1;

	for (index = 0; index < QBG_STATE_MAX; index++) {
		if (index == QBG_PON_OCV)
			continue;

		rc = qbg_get_accumulator_properties(chip, index, &num_accum,
					&accum_interval);
		if (rc < 0)
			return rc;

		if (index == QBG_FAST_CHAR)
			interval = qbg_fast_char_avg_interval[accum_interval];
		else if (index == QBG_LPM)
			interval = qbg_lpm_accum_interval[accum_interval];
		else
			interval = qbg_accum_interval[accum_interval];

		chip->sample_time_us[index] = interval;
	}

	return rc;
}

static ssize_t qbg_context_show(struct class *c,
				struct class_attribute *attr, char *buf)
{
	struct qti_qbg *chip = container_of(c, struct qti_qbg, qbg_class);
	int count;

	if (!chip)
		return -ENODEV;

	if (!chip->context_count) {
		qbg_dbg(chip, QBG_DEBUG_DEVICE, "Empty context buffer, nothing to show\n");
		return 0;
	}

	mutex_lock(&chip->context_lock);
	memcpy(buf, chip->context, chip->context_count);
	count = chip->context_count;
	chip->context_count = 0;
	memset(chip->context, 0, QBG_CONTEXT_LOCAL_BUF_SIZE);
	mutex_unlock(&chip->context_lock);

	return count;
}

static ssize_t qbg_context_store(struct class *c,
					struct class_attribute *attr,
					const char *buf, size_t count)
{
	struct qti_qbg *chip = container_of(c, struct qti_qbg, qbg_class);

	if (!chip)
		return -ENODEV;

	if (count > QBG_CONTEXT_LOCAL_BUF_SIZE) {
		qbg_dbg(chip, QBG_DEBUG_DEVICE, "Context dump is greater than %d bytes\n",
				QBG_CONTEXT_LOCAL_BUF_SIZE);
		return -EINVAL;
	}

	if (!chip->context) {
		chip->context = devm_kcalloc(chip->dev, 1,
						QBG_CONTEXT_LOCAL_BUF_SIZE,
						GFP_KERNEL);
		if (!chip->context)
			return -ENOMEM;
	}

	mutex_lock(&chip->context_lock);
	memcpy(chip->context, buf, count);
	chip->context_count = count;
	mutex_unlock(&chip->context_lock);

	return count;
}
static CLASS_ATTR_RW(qbg_context);

static struct attribute *qbg_class_attrs[] = {
	&class_attr_qbg_context.attr,
	NULL,
};
ATTRIBUTE_GROUPS(qbg_class);

static ssize_t qbg_device_read(struct file *file, char __user *buf, size_t count,
			  loff_t *ppos)
{
	int rc;
	struct qti_qbg *chip = file->private_data;
	unsigned long data_size = sizeof(chip->kdata);

	if (count < data_size) {
		pr_err("Invalid datasize %lu, expected lesser then %zu\n",
							data_size, count);
		return -EINVAL;
	}

	/* non-blocking access, return */
	if (!chip->data_ready && (file->f_flags & O_NONBLOCK))
		return 0;

	/* blocking access wait on data_ready */
	if (!(file->f_flags & O_NONBLOCK)) {
		rc = wait_event_interruptible(chip->qbg_wait_q,
					chip->data_ready);
		if (rc < 0) {
			pr_debug("Failed wait! rc=%d\n", rc);
			return rc;
		}
	}

	mutex_lock(&chip->data_lock);
	if (!chip->data_ready) {
		pr_debug("No Data, false wakeup\n");
		rc = -ENODATA;
		goto fail_read;
	}

	if (copy_to_user(buf, &chip->kdata, data_size)) {
		pr_err("Failed to copy_to_user\n");
		rc = -EFAULT;
		goto fail_read;
	}
	chip->data_ready = false;

	qbg_dbg(chip, QBG_DEBUG_DEVICE, "QBG device read complete Size=%ld\n",
		data_size);

	/* clear data */
	memset(&chip->kdata, 0, sizeof(chip->kdata));

	rc = data_size;

fail_read:
	mutex_unlock(&chip->data_lock);
	return rc;
}

static ssize_t qbg_device_write(struct file *file, const char __user *buf,
			size_t count, loff_t *ppos)
{
	int rc = -EINVAL;
	struct qti_qbg *chip = file->private_data;
	unsigned long data_size = sizeof(chip->udata);

	mutex_lock(&chip->data_lock);
	if (count == 0) {
		pr_err("No data!\n");
		rc = -ENODATA;
		goto fail;
	}

	if (count < data_size) {
		pr_err("Invalid datasize %zu expected %lu\n", count, data_size);
		goto fail;
	}

	if (copy_from_user(&chip->udata, buf, data_size)) {
		pr_err("Failed to copy_from_user\n");
		rc = -EFAULT;
		goto fail;
	}

	__pm_stay_awake(chip->qbg_ws);
	rc = data_size;
	schedule_work(&chip->udata_work);

	qbg_dbg(chip, QBG_DEBUG_DEVICE, "QBG write complete size=%d\n", rc);
fail:
	mutex_unlock(&chip->data_lock);
	return rc;
}

static unsigned int qbg_device_poll(struct file *file, poll_table *wait)
{
	struct qti_qbg *chip = file->private_data;
	unsigned int mask = 0;

	poll_wait(file, &chip->qbg_wait_q, wait);

	if (chip->data_ready)
		mask = POLLIN | POLLRDNORM;

	return mask;
}

static int qbg_device_open(struct inode *inode, struct file *file)
{
	struct qti_qbg *chip = container_of(inode->i_cdev,
				struct qti_qbg, qbg_cdev);

	file->private_data = chip;

	qbg_dbg(chip, QBG_DEBUG_DEVICE, "QBG device opened!\n");

	return 0;
}

static int qbg_device_release(struct inode *inode, struct file *file)
{
	struct qti_qbg *chip = container_of(inode->i_cdev,
				struct qti_qbg, qbg_cdev);

	file->private_data = NULL;

	qbg_dbg(chip, QBG_DEBUG_DEVICE, "QBG device closed!\n");

	return 0;
}

static long qbg_device_ioctl(struct file *file, unsigned int cmd,
						unsigned long arg)
{
	struct qti_qbg *chip = file->private_data;
	struct qbg_config __user *config_user;
	struct qbg_config config;
	struct qbg_essential_params __user *params_user;
	struct qbg_step_chg_jeita_params __user *step_chg_params_user;
	time64_t rtc_sec;
	int rc = 0, i;

	if (!chip) {
		pr_err("Device private data not set!\n");
		return -EINVAL;
	}

	if (!arg) {
		pr_err("Invalid user pointer\n");
		return -EINVAL;
	}

	/* Get RTC time here */
	rc = get_rtc_time(chip, &rtc_sec);
	if (rc < 0) {
		pr_err("Failed to read rtc time, rc=%d\n", rc);
		return rc;
	}

	switch (cmd) {
	case QBGIOCXCFG:
	case QBG_QBGIOCXCFG:
		config_user = (struct qbg_config __user *)arg;

		rc = qbg_get_sample_time_us(chip);
		if (rc < 0) {
			pr_err("Failed to calculate sample time us, rc=%d\n", rc);
			return rc;
		}

		rc = qbg_sdam_get_battery_id(chip, &chip->sdam_batt_id);
		if (rc < 0) {
			pr_err("Failed to get battid from sdam, rc=%d\n", rc);
			return rc;
		}

		rc = qbg_sdam_get_essential_param_revid(chip,
					(u8 *)&chip->essential_param_revid);
		if (rc < 0) {
			pr_err("Failed to get essential param revid, rc=%d\n",
				rc);
			return rc;
		}

		config.current_time = rtc_sec;
		config.batt_id = chip->batt_id_ohm / 1000;
		config.pon_ocv = chip->pon_ocv;
		config.pon_ibat = chip->pon_ibat;
		config.pon_tbat = chip->pon_tbat;
		config.pon_soc = chip->pon_soc;
		config.float_volt_uv = chip->float_volt_uv;
		config.fastchg_curr_ma = chip->fastchg_curr_ma;
		config.vbat_cutoff_mv = chip->vbat_cutoff_mv;
		config.ibat_cutoff_ma = chip->ibat_cutoff_ma;
		config.vph_min_mv = chip->vph_min_mv;
		config.iterm_ma = chip->iterm_ma;
		config.rconn_mohm = chip->rconn_mohm;
		config.sdam_batt_id = chip->sdam_batt_id;
		config.essential_param_revid = chip->essential_param_revid;
		for (i = 0; i < QBG_STATE_MAX; i++) {
			config.sample_time_us[i] = chip->sample_time_us[i];
			qbg_dbg(chip, QBG_DEBUG_DEVICE, "QBGIOCXCFG: sample_time_us[%d]:%u\n",
					i, config.sample_time_us[i]);
		}

		if (copy_to_user(config_user, (void *)&config, sizeof(config))) {
			pr_err("Failed to copy QBG config to user\n");
			return -EFAULT;
		}

		qbg_dbg(chip, QBG_DEBUG_DEVICE, "QBGIOCXCFG: sdam_battid:%u essential param revid:%u battid:%u pon_ocv:%u pon_ibat:%u pon_soc:%u vbatt_cutoff_mv:%u iterm_ma:%u\n",
				config.sdam_batt_id, config.essential_param_revid,
				config.batt_id, config.pon_ocv,
				config.pon_ibat, config.pon_soc,
				config.vbat_cutoff_mv, config.iterm_ma);

		break;
	case QBGIOCXEPR:
	case QBG_QBGIOCXEPR:
		params_user = (struct qbg_essential_params __user *)arg;
		rc = qbg_sdam_get_essential_params(chip,
				(u8 *)&chip->essential_params);
		if (rc < 0) {
			pr_err("Failed to read essential params, rc=%d\n", rc);
			return -EFAULT;
		}

		if (copy_to_user(params_user, (void *)&chip->essential_params,
				sizeof(chip->essential_params))) {
			pr_err("Failed to copy QBG essential params to user\n");
			return -EFAULT;
		}

		break;
	case QBGIOCXEPW:
	case QBG_QBGIOCXEPW:
		params_user = (struct qbg_essential_params __user *)arg;
		if (copy_from_user((void *)&chip->essential_params, params_user,
					sizeof(chip->essential_params))) {
			pr_err("Failed to copy QBG essential params from user\n");
			return -EFAULT;
		}
		chip->previous_ep_time = rtc_sec;
		chip->essential_params.rtc_time = rtc_sec;

		rc = qbg_sdam_set_essential_params(chip,
					(u8 *)&chip->essential_params);
		if (rc < 0) {
			pr_err("Failed to write essential params, rc=%d\n", rc);
			return -EFAULT;
		}
		qbg_dbg(chip, QBG_DEBUG_SDAM, "Essential params written, time:%lu secs\n",
					rtc_sec);

		break;
	case QBGIOCXSTEPCHGCFG:
	case QBG_QBGIOCXSTEPCHGCFG:
		step_chg_params_user = (struct qbg_step_chg_jeita_params __user *)arg;

		if (copy_to_user(step_chg_params_user, (void *)chip->step_chg_jeita_params,
			sizeof(struct qbg_step_chg_jeita_params))) {
			pr_err("Failed to copy QBG step and jeita charge params to user\n");
			return -EFAULT;
		}
		qbg_dbg(chip, QBG_DEBUG_DEVICE, "QBGIOCXSTEPCHGCFG: jeita_full_fv_10nv:%d jeita_warm_adc_value:0x%x jeita_cool_adc_value:0x%x ttf_calc_mode:%u\n",
				chip->step_chg_jeita_params->jeita_full_fv_10nv,
				chip->step_chg_jeita_params->jeita_warm_adc_value,
				chip->step_chg_jeita_params->jeita_cool_adc_value,
				chip->step_chg_jeita_params->ttf_calc_mode);

		break;
	default:
		pr_err("IOCTL %d not supported\n", cmd);
		rc = -EINVAL;
	}

	return rc;
}

static long qbg_device_compat_ioctl(struct file *file, unsigned int cmd,
							unsigned long arg)
{
	return qbg_device_ioctl(file, _IOC_NR(cmd), arg);
}

static const struct file_operations qbg_fops = {
	.owner		= THIS_MODULE,
	.open		= qbg_device_open,
	.release	= qbg_device_release,
	.read		= qbg_device_read,
	.write		= qbg_device_write,
	.poll		= qbg_device_poll,
	.unlocked_ioctl	= qbg_device_ioctl,
	.compat_ioctl	= qbg_device_compat_ioctl,
};

static int qbg_register_device(struct qti_qbg *chip)
{
	int rc;

	rc = alloc_chrdev_region(&chip->dev_no, 0, 1, "qbg");
	if (rc < 0) {
		pr_err("Failed to allocate chardev rc=%d\n", rc);
		return rc;
	}

	cdev_init(&chip->qbg_cdev, &qbg_fops);
	rc = cdev_add(&chip->qbg_cdev, chip->dev_no, 1);
	if (rc < 0) {
		pr_err("Failed to cdev_add rc=%d\n", rc);
		goto unregister_chrdev;
	}

	chip->qbg_class.name = "qbg";
	chip->qbg_class.class_groups = qbg_class_groups;
	rc = class_register(&chip->qbg_class);
	if (rc < 0) {
		pr_err("Failed to create qbg_class rc=%d\n", rc);
		goto delete_cdev;
	}

	chip->qbg_device = device_create(&chip->qbg_class, NULL, chip->dev_no,
					NULL, "qbg");
	if (IS_ERR(chip->qbg_device)) {
		pr_err("Failed to create qbg_device\n");
		rc = -EINVAL;
		goto destroy_class;
	}

	qbg_dbg(chip, QBG_DEBUG_DEVICE, "'/dev/qbg' successfully created\n");

	return 0;

destroy_class:
	class_unregister(&chip->qbg_class);
delete_cdev:
	cdev_del(&chip->qbg_cdev);
unregister_chrdev:
	unregister_chrdev_region(chip->dev_no, 1);

	return rc;
}

static int qbg_register_interrupts(struct qti_qbg *chip)
{
	int rc = 0;

	/*
	 * Do not register for data-full to skip processing QBG
	 * data if a valid battery or debug battery is not detected
	 */
	if (chip->battery_unknown || is_debug_batt_id(chip))
		return rc;

	/*
	 * Turn off data full interrupt from PMIC side. After IRQ handler
	 * registration, we re-enable the interrupt which guarantees the IRQ
	 * handler will fire if the FIFO is already full.
	 */
	rc = qbg_enable_data_full_int(chip, false);
	if (rc < 0) {
		dev_err(chip->dev,
			"Failed to disable interrupt from PMIC side, rc=%d\n",
			rc);
	}

	rc = devm_request_threaded_irq(chip->dev, chip->irq, NULL,
			qbg_data_full_irq_handler, IRQF_ONESHOT,
			"qbg-sdam", chip);
	if (rc < 0) {
		dev_err(chip->dev, "Failed to request IRQ(qbg-sdam), rc=%d\n",
			rc);
		return rc;
	}

	/* Enable the interrupt, it will get raised if FIFO is already full */
	rc = qbg_enable_data_full_int(chip, true);
	if (rc < 0) {
		dev_err(chip->dev,
			"Failed to enable interrupt from PMIC side, rc=%d\n",
			rc);
	}

	rc = enable_irq_wake(chip->irq);
	if (rc < 0)
		dev_err(chip->dev, "Failed to set IRQ(qbg-sdam) wake-able, rc=%d\n",
			rc);

	/* Register for Vbatt_empty INT only if valid value is defined in DT */
	if (chip->vbatt_empty_threshold_mv != 0) {
		rc = devm_request_threaded_irq(chip->dev, chip->vbatt_empty_irq, NULL,
			qbg_vbatt_empty_irq_handler, IRQF_ONESHOT,
			"qbg-vbatt-empty", chip);
		if (rc < 0) {
			dev_err(chip->dev, "Failed to request IRQ(qbg-vbatt-empty), rc=%d\n",
				rc);
			return rc;
		}

		rc = enable_irq_wake(chip->vbatt_empty_irq);
		if (rc < 0)
			dev_err(chip->dev, "Failed to set IRQ(qbg-vbatt-empty) wake-able, rc=%d\n",
				rc);
	}

	return rc;
}

#ifdef CONFIG_DEBUG_FS
static ssize_t qbg_debug_mask_read(struct file *filp, char __user *buffer,
		size_t count, loff_t *ppos)
{
	char *buf;
	ssize_t len = 0;

	if (*ppos != 0)
		return 0;

	buf = kasprintf(GFP_KERNEL, "%d\n", qbg_debug_mask);
	if (!buf)
		return -ENOMEM;

	if (count < strlen(buf)) {
		kfree(buf);
		return -ENOSPC;
	}

	len = simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
	kfree(buf);

	return len;
}

static ssize_t qbg_debug_mask_write(struct file *filp, const char __user *buffer,
		size_t count, loff_t *ppos)
{
	struct qti_qbg *chip = filp->private_data;
	int rc = 0;
	char data[2];

	rc = kstrtou32_from_user(buffer, count, 10, &qbg_debug_mask);
	if (rc < 0)
		return rc;

	if (!chip->debug_mask_nvmem_low || !chip->debug_mask_nvmem_high)
		return count;

	data[0] = qbg_debug_mask & 0xff;
	data[1] = (qbg_debug_mask >> 8) & 0xff;

	rc = nvmem_cell_write(chip->debug_mask_nvmem_low, &data[0], sizeof(data[0]));
	if (rc < 0) {
		pr_err("Failed to write qbg debug mask low byte, rc = %d\n", rc);
		return rc;
	}

	rc = nvmem_cell_write(chip->debug_mask_nvmem_high, &data[1], sizeof(data[1]));
	if (rc < 0) {
		pr_err("Failed to write qbg debug mask high byte, rc = %d\n", rc);
		return rc;
	}

	return count;
}

static const struct file_operations qbg_debug_mask_fops = {
	.owner = THIS_MODULE,
	.open = simple_open,
	.read = qbg_debug_mask_read,
	.write = qbg_debug_mask_write,
};

static void qbg_create_debugfs(struct qti_qbg *chip)
{
	struct dentry *entry;

	pr_err("%s:%u\n", __func__, __LINE__);
	chip->dfs_root = debugfs_create_dir("qbg", NULL);
	if (IS_ERR_OR_NULL(chip->dfs_root)) {
		pr_err("Failed to create debugfs directory rc=%ld\n",
				(long)chip->dfs_root);
		return;
	}

	entry = debugfs_create_file("debug_mask", 0600, chip->dfs_root, chip,
			&qbg_debug_mask_fops);
	if (IS_ERR_OR_NULL(entry)) {
		pr_err("Failed to create debug_mask rc=%ld\n", (long)entry);
		debugfs_remove_recursive(chip->dfs_root);
	}
}
#else
static void qbg_create_debugfs(struct qti_qbg *chip)
{
}
#endif

static void get_qbg_debug_mask(struct qti_qbg *chip)
{
	ssize_t len;
	char *data[2];

	if (!chip->debug_mask_nvmem_low || !chip->debug_mask_nvmem_high)
		return;

	data[0] = nvmem_cell_read(chip->debug_mask_nvmem_low, &len);
	if (IS_ERR(data[0])) {
		pr_err("Failed to read qbg debug mask low byte from SDAM\n");
		return;
	}

	data[1] = nvmem_cell_read(chip->debug_mask_nvmem_high, &len);
	if (IS_ERR(data[1])) {
		pr_err("Failed to read qbg debug mask high byte from SDAM\n");
		return;
	}

	qbg_debug_mask = *data[1] & 0xff;
	qbg_debug_mask = (qbg_debug_mask << 8) | (*data[0]);

	kfree(data[0]);
	kfree(data[1]);
}

#define SOC_UPDATE_FREQUENCY_MS		60000
static void soc_update_work(struct work_struct *work)
{
	struct qti_qbg *chip = container_of(work, struct qti_qbg, soc_update_work.work);
	int rc = 0;

	if (chip->sys_soc != INT_MIN) {
		qbg_dbg(chip, QBG_DEBUG_STATUS, "write soc = %d to register\n",
			chip->sys_soc);
		rc = qbg_write_iio_chan(chip, SYS_SOC, chip->sys_soc);
		if (rc < 0) {
			pr_err("Failed to write battery sys_soc, rc=%d\n", rc);
			return;
		}
	}

	schedule_delayed_work(&chip->soc_update_work, msecs_to_jiffies(SOC_UPDATE_FREQUENCY_MS));
}

static int qbg_parse_sdam_dt(struct qti_qbg *chip, struct device_node *node)
{
	int rc;

	chip->irq = of_irq_get_byname(node, "qbg-sdam");
	if (chip->irq < 0) {
		pr_err("Failed to get irq for QBG, rc=%d\n", chip->irq);
		return -EINVAL;
	}

	rc = of_property_read_u32(node, "qcom,num-data-sdams",
				&chip->num_data_sdams);
	if (rc < 0) {
		pr_err("Failed to read number of DATA SDAMs for QBG, rc=%d\n", rc);
		return rc;
	}

	rc = of_property_read_u32(node, "qcom,sdam-base", &chip->sdam_base);
	if (rc < 0) {
		pr_err("Failed to read SDAM base address, rc=%d\n", rc);
		return rc;
	}

	return rc;
}

#define QBG_DEFAULT_VBAT_CUTOFF_MV			3100
#define QBG_DEFAULT_IBAT_CUTOFF_MA			150
#define QBG_DEFAULT_VPH_MIN_MV				2700
#define QBG_DEFAULT_ITERM_MA				100
#define QBG_DEFAULT_RCONN_MOHM				0
static int qbg_parse_dt(struct qti_qbg *chip)
{
	struct device_node *node = chip->dev->of_node;
	int rc = 0;
	u32 val;

	rc = qbg_parse_sdam_dt(chip, node);
	if (rc < 0) {
		pr_err("Failed to QBG SDAM DT, rc=%d\n", rc);
		return rc;
	}

	rc = of_property_read_u32(node, "qcom,adc-cmn-wb-base", &chip->adc_cmn_wb_base);
	if (rc) {
		pr_err("Failed to get adc cmn wb addr, rc=%d\n", rc);
		return rc;
	}

	rc = of_property_read_u32(node, "qcom,adc-cmn-base", &chip->adc_cmn_base);
	if (rc) {
		pr_err("Failed to get adc cmn addr, rc=%d\n", rc);
		return rc;
	}

	chip->vbat_cutoff_mv = QBG_DEFAULT_VBAT_CUTOFF_MV;
	rc = of_property_read_u32(node, "qcom,vbat-cutoff-mv", &val);
	if (!rc)
		chip->vbat_cutoff_mv = val;

	chip->ibat_cutoff_ma = QBG_DEFAULT_IBAT_CUTOFF_MA;
	rc = of_property_read_u32(node, "qcom,ibat-cutoff-ma", &val);
	if (!rc)
		chip->ibat_cutoff_ma = val;

	chip->vph_min_mv = QBG_DEFAULT_VPH_MIN_MV;
	rc = of_property_read_u32(node, "qcom,vph-min-mv", &val);
	if (!rc)
		chip->vph_min_mv = val;

	chip->iterm_ma = QBG_DEFAULT_ITERM_MA;
	rc = of_property_read_u32(node, "qcom,iterm-ma", &val);
	if (!rc)
		chip->iterm_ma = val;

	chip->rconn_mohm = QBG_DEFAULT_RCONN_MOHM;
	rc = of_property_read_u32(node, "qcom,rconn-mohm", &val);
	if (!rc)
		chip->rconn_mohm = val;

	chip->vbatt_empty_threshold_mv = 0;
	rc = of_property_read_u32(node, "qcom,vbatt-empty-threshold-mv",
					&val);

	if (!rc)
		chip->vbatt_empty_threshold_mv = val;

	chip->vbatt_empty_irq = of_irq_get_byname(node, "qbg-vbatt-empty");
	if (chip->vbatt_empty_irq < 0) {
		pr_err("Failed to get Vbatt_empty IRQ, rc=%d\n", chip->vbatt_empty_irq);
		return -EINVAL;
	}

	if (of_find_property(node, "nvmem-cells", NULL)) {
		chip->debug_mask_nvmem_low = devm_nvmem_cell_get(chip->dev, "qbg_debug_mask_low");
		if (IS_ERR(chip->debug_mask_nvmem_low)) {
			rc = PTR_ERR(chip->debug_mask_nvmem_low);
			if (rc != -EPROBE_DEFER)
				dev_err(chip->dev, "Failed to get nvmem-cells, rc=%d\n", rc);
			return rc;
		}
		chip->debug_mask_nvmem_high = devm_nvmem_cell_get(chip->dev, "qbg_debug_mask_high");
		if (IS_ERR(chip->debug_mask_nvmem_high)) {
			rc = PTR_ERR(chip->debug_mask_nvmem_high);
			if (rc != -EPROBE_DEFER)
				dev_err(chip->dev, "Failed to get nvmem-cells, rc=%d\n", rc);
			return rc;
		}

		chip->skip_esr_state = devm_nvmem_cell_get(chip->dev, "skip_esr_state");
		if (IS_ERR(chip->skip_esr_state)) {
			rc = PTR_ERR(chip->skip_esr_state);
			if (rc != -EPROBE_DEFER) {
				dev_dbg(chip->dev, "Failed to get skip_esr_state, rc=%d\n", rc);
				chip->skip_esr_state = NULL;
				goto exit;
			}
			return rc;
		}
	}

exit:
	return 0;
}

static int qti_qbg_probe(struct platform_device *pdev)
{
	struct qti_qbg *chip;
	struct iio_dev *indio_dev;
	int rc;
	u32 val;

	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*chip));
	if (!indio_dev)
		return -ENOMEM;

	chip = iio_priv(indio_dev);
	chip->dev = &pdev->dev;
	chip->indio_dev = indio_dev;

	chip->ext_iio_chans = devm_kcalloc(chip->dev,
				ARRAY_SIZE(qbg_ext_iio_chan_name),
				sizeof(*chip->ext_iio_chans),
				GFP_KERNEL);
	if (!chip->ext_iio_chans)
		return -ENOMEM;

	chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
	if (!chip->regmap) {
		dev_err(&pdev->dev, "Failed to get regmap\n");
		return -EINVAL;
	}

	rc = of_property_read_u32(pdev->dev.of_node, "reg", &val);
	if (rc < 0) {
		pr_err("Failed to get base address for QBG, rc = %d\n", rc);
		return rc;
	}
	chip->base = val;

	INIT_WORK(&chip->status_change_work, status_change_work);
	INIT_WORK(&chip->udata_work, process_udata_work);
	INIT_DELAYED_WORK(&chip->soc_update_work, soc_update_work);
	mutex_init(&chip->fifo_lock);
	mutex_init(&chip->data_lock);
	mutex_init(&chip->context_lock);
	dev_set_drvdata(chip->dev, chip);
	init_waitqueue_head(&chip->qbg_wait_q);

	chip->debug_mask = &qbg_debug_mask;

	chip->qbg_ws = wakeup_source_register(chip->dev, "qcom-qbg");
	if (!chip->qbg_ws)
		return -EINVAL;

	chip->default_iterm_ma = -EINVAL;
	chip->soc = INT_MIN;
	chip->batt_soc = INT_MIN;
	chip->sys_soc = INT_MIN;
	chip->esr = INT_MIN;
	chip->ocv_uv = INT_MIN;
	chip->pon_ocv = INT_MIN;
	chip->charge_cycle_count = INT_MIN;
	chip->nominal_capacity = INT_MIN;
	chip->learned_capacity = INT_MIN;
	chip->ttf = INT_MIN;
	chip->tte = INT_MIN;
	chip->soh = INT_MIN;
	chip->tbat = INT_MIN;

	rc = qbg_parse_dt(chip);
	if (rc < 0) {
		dev_err(&pdev->dev, "Failed to parse QBG devicetree, rc=%d\n", rc);
		return rc;
	}

	get_qbg_debug_mask(chip);

	/* ADC for Battery-ID */
	chip->batt_id_chan = devm_iio_channel_get(&pdev->dev, "batt-id");
	if (IS_ERR(chip->batt_id_chan)) {
		rc = PTR_ERR(chip->batt_id_chan);
		if (rc != -EPROBE_DEFER)
			dev_err(&pdev->dev, "batt-id channel unavailable, rc=%d\n", rc);
		chip->batt_id_chan = NULL;
		return rc;
	}

	/* ADC for Battery-Temp */
	chip->batt_temp_chan = devm_iio_channel_get(&pdev->dev, "batt-temp");
	if (IS_ERR(chip->batt_temp_chan)) {
		rc = PTR_ERR(chip->batt_temp_chan);
		if (rc != -EPROBE_DEFER)
			dev_err(&pdev->dev, "batt-temp channel unavailable, rc=%d\n", rc);
		chip->batt_temp_chan = NULL;
		return rc;
	}

	qbg_create_debugfs(chip);

	chip->rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
	if (chip->rtc == NULL)
		return -EPROBE_DEFER;

	rc = regmap_read(chip->regmap, REVID_REVISION4, &chip->rev4);
	if (rc < 0) {
		pr_err("Failed to read REVID_REVISION4, rc=%d\n", rc);
		return rc;
	}

	rc = qbg_init_sdam(chip);
	if (rc < 0) {
		dev_err(&pdev->dev, "Failed to initialize QBG sdam, rc=%d\n", rc);
		return rc;
	}

	rc = qbg_get_max_fifo_count(chip);
	if (rc < 0) {
		dev_err(&pdev->dev, "Failed to get fifo count, rc=%d\n", rc);
		return rc;
	}

	rc = qbg_init_esr(chip);
	if (rc < 0) {
		dev_err(&pdev->dev, "Failed to initialize QBG ESR, rc=%d\n", rc);
		return rc;
	}

	rc = qbg_setup_battery(chip);
	if (rc < 0) {
		dev_err(&pdev->dev, "Failed to setup battery for QBG, rc=%d\n", rc);
		return rc;
	}

	rc = qbg_determine_pon_soc(chip);
	if (rc < 0) {
		dev_err(&pdev->dev, "Failed to determine initial state, rc=%d\n", rc);
		return rc;
	}

	rc = qbg_register_device(chip);
	if (rc < 0) {
		dev_err(&pdev->dev, "Failed to register QBG device, rc=%d\n", rc);
		return rc;
	}

	rc = qbg_init_iio(chip, pdev);
	if (rc < 0) {
		dev_err(&pdev->dev, "Failed to initialize QBG IIO device, rc=%d\n", rc);
		return rc;
	}

	rc = qbg_init_psy(chip);
	if (rc < 0) {
		dev_err(&pdev->dev, "Failed to initialize QBG PSY, rc=%d\n", rc);
		return rc;
	}

	qbg_init_vbatt_empty_threshold(chip);

	rc = qbg_register_interrupts(chip);
	if (rc < 0) {
		dev_err(&pdev->dev, "Failed to register QBG interrupts, rc=%d\n", rc);
		return rc;
	}

	schedule_delayed_work(&chip->soc_update_work, msecs_to_jiffies(SOC_UPDATE_FREQUENCY_MS));

	dev_info(&pdev->dev, "QBG initialized! battery_profile=%s SOC=%d\n",
			qbg_get_battery_type(chip), chip->soc);

	return rc;
}

static int qti_qbg_remove(struct platform_device *pdev)
{
	struct qti_qbg *chip = platform_get_drvdata(pdev);

	if (chip->rtc)
		rtc_class_close(chip->rtc);
	cancel_work_sync(&chip->status_change_work);
	cancel_work_sync(&chip->udata_work);
	cancel_delayed_work_sync(&chip->soc_update_work);
	mutex_destroy(&chip->fifo_lock);
	mutex_destroy(&chip->data_lock);
	mutex_destroy(&chip->context_lock);
	cdev_del(&chip->qbg_cdev);
	unregister_chrdev_region(chip->dev_no, 1);
	class_unregister(&chip->qbg_class);

	return 0;
}

static int qbg_freeze(struct device *dev)
{
	struct qti_qbg *chip = dev_get_drvdata(dev);
	/*free irq*/
	if (chip->irq > 0)
		devm_free_irq(dev, chip->irq, chip);

	return 0;
}

static int qbg_restore(struct device *dev)
{
	int ret = 0;
	struct qti_qbg *chip = dev_get_drvdata(dev);

	/* Init & clear SDAM to kick-start QBG sampling */
	ret = qbg_init_sdam(chip);
	if (ret < 0) {
		dev_err(dev, "Failed to init qbg sdam rc = %d\n");
		return ret;
	}

	ret = qbg_register_interrupts(chip);
	if (ret < 0)
		dev_err(dev, "Failed to register qbg interrupt rc = %d\n");

	return ret;
}

static int qbg_suspend(struct device *dev)
{
#ifdef CONFIG_DEEPSLEEP
	if (pm_suspend_via_firmware())
		return qbg_freeze(dev);
#endif
	return 0;
}

static int qbg_resume(struct device *dev)
{
#ifdef CONFIG_DEEPSLEEP
	if (pm_suspend_via_firmware())
		return qbg_restore(dev);
#endif
	return 0;
}

static const struct dev_pm_ops qbg_pm_ops = {
	.freeze = qbg_freeze,
	.restore = qbg_restore,
	.suspend = qbg_suspend,
	.resume = qbg_resume,
};

static const struct of_device_id qbg_match_table[] = {
	{ .compatible = "qcom,qbg", },
	{ },
};

static struct platform_driver qti_qbg_driver = {
	.driver = {
		.name = "qti_qbg",
		.of_match_table = qbg_match_table,
		.pm = &qbg_pm_ops,
	},
	.probe = qti_qbg_probe,
	.remove = qti_qbg_remove,
};
module_platform_driver(qti_qbg_driver);

MODULE_DESCRIPTION("QTI QBG (Qualcomm Battery Gauging) driver");
MODULE_LICENSE("GPL v2");
