18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Battery driver for CPCAP PMIC
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2017 Tony Lindgren <tony@atomide.com>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Some parts of the code based on earlie Motorola mapphone Linux kernel
78c2ecf20Sopenharmony_ci * drivers:
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Copyright (C) 2009-2010 Motorola, Inc.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
128c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License version 2 as
138c2ecf20Sopenharmony_ci * published by the Free Software Foundation.
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any
168c2ecf20Sopenharmony_ci * kind, whether express or implied; without even the implied warranty
178c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
188c2ecf20Sopenharmony_ci * GNU General Public License for more details.
198c2ecf20Sopenharmony_ci */
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <linux/delay.h>
228c2ecf20Sopenharmony_ci#include <linux/err.h>
238c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
248c2ecf20Sopenharmony_ci#include <linux/kernel.h>
258c2ecf20Sopenharmony_ci#include <linux/module.h>
268c2ecf20Sopenharmony_ci#include <linux/of_device.h>
278c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
288c2ecf20Sopenharmony_ci#include <linux/power_supply.h>
298c2ecf20Sopenharmony_ci#include <linux/reboot.h>
308c2ecf20Sopenharmony_ci#include <linux/regmap.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#include <linux/iio/consumer.h>
338c2ecf20Sopenharmony_ci#include <linux/iio/types.h>
348c2ecf20Sopenharmony_ci#include <linux/mfd/motorola-cpcap.h>
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/*
378c2ecf20Sopenharmony_ci * Register bit defines for CPCAP_REG_BPEOL. Some of these seem to
388c2ecf20Sopenharmony_ci * map to MC13783UG.pdf "Table 5-19. Register 13, Power Control 0"
398c2ecf20Sopenharmony_ci * to enable BATTDETEN, LOBAT and EOL features. We currently use
408c2ecf20Sopenharmony_ci * LOBAT interrupts instead of EOL.
418c2ecf20Sopenharmony_ci */
428c2ecf20Sopenharmony_ci#define CPCAP_REG_BPEOL_BIT_EOL9	BIT(9)	/* Set for EOL irq */
438c2ecf20Sopenharmony_ci#define CPCAP_REG_BPEOL_BIT_EOL8	BIT(8)	/* Set for EOL irq */
448c2ecf20Sopenharmony_ci#define CPCAP_REG_BPEOL_BIT_UNKNOWN7	BIT(7)
458c2ecf20Sopenharmony_ci#define CPCAP_REG_BPEOL_BIT_UNKNOWN6	BIT(6)
468c2ecf20Sopenharmony_ci#define CPCAP_REG_BPEOL_BIT_UNKNOWN5	BIT(5)
478c2ecf20Sopenharmony_ci#define CPCAP_REG_BPEOL_BIT_EOL_MULTI	BIT(4)	/* Set for multiple EOL irqs */
488c2ecf20Sopenharmony_ci#define CPCAP_REG_BPEOL_BIT_UNKNOWN3	BIT(3)
498c2ecf20Sopenharmony_ci#define CPCAP_REG_BPEOL_BIT_UNKNOWN2	BIT(2)
508c2ecf20Sopenharmony_ci#define CPCAP_REG_BPEOL_BIT_BATTDETEN	BIT(1)	/* Enable battery detect */
518c2ecf20Sopenharmony_ci#define CPCAP_REG_BPEOL_BIT_EOLSEL	BIT(0)	/* BPDET = 0, EOL = 1 */
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/*
548c2ecf20Sopenharmony_ci * Register bit defines for CPCAP_REG_CCC1. These seem similar to the twl6030
558c2ecf20Sopenharmony_ci * coulomb counter registers rather than the mc13892 registers. Both twl6030
568c2ecf20Sopenharmony_ci * and mc13892 set bits 2 and 1 to reset and clear registers. But mc13892
578c2ecf20Sopenharmony_ci * sets bit 0 to start the coulomb counter while twl6030 sets bit 0 to stop
588c2ecf20Sopenharmony_ci * the coulomb counter like cpcap does. So for now, we use the twl6030 style
598c2ecf20Sopenharmony_ci * naming for the registers.
608c2ecf20Sopenharmony_ci */
618c2ecf20Sopenharmony_ci#define CPCAP_REG_CCC1_ACTIVE_MODE1	BIT(4)	/* Update rate */
628c2ecf20Sopenharmony_ci#define CPCAP_REG_CCC1_ACTIVE_MODE0	BIT(3)	/* Update rate */
638c2ecf20Sopenharmony_ci#define CPCAP_REG_CCC1_AUTOCLEAR	BIT(2)	/* Resets sample registers */
648c2ecf20Sopenharmony_ci#define CPCAP_REG_CCC1_CAL_EN		BIT(1)	/* Clears after write in 1s */
658c2ecf20Sopenharmony_ci#define CPCAP_REG_CCC1_PAUSE		BIT(0)	/* Stop counters, allow write */
668c2ecf20Sopenharmony_ci#define CPCAP_REG_CCC1_RESET_MASK	(CPCAP_REG_CCC1_AUTOCLEAR | \
678c2ecf20Sopenharmony_ci					 CPCAP_REG_CCC1_CAL_EN)
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci#define CPCAP_REG_CCCC2_RATE1		BIT(5)
708c2ecf20Sopenharmony_ci#define CPCAP_REG_CCCC2_RATE0		BIT(4)
718c2ecf20Sopenharmony_ci#define CPCAP_REG_CCCC2_ENABLE		BIT(3)
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci#define CPCAP_BATTERY_CC_SAMPLE_PERIOD_MS	250
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cienum {
768c2ecf20Sopenharmony_ci	CPCAP_BATTERY_IIO_BATTDET,
778c2ecf20Sopenharmony_ci	CPCAP_BATTERY_IIO_VOLTAGE,
788c2ecf20Sopenharmony_ci	CPCAP_BATTERY_IIO_CHRG_CURRENT,
798c2ecf20Sopenharmony_ci	CPCAP_BATTERY_IIO_BATT_CURRENT,
808c2ecf20Sopenharmony_ci	CPCAP_BATTERY_IIO_NR,
818c2ecf20Sopenharmony_ci};
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cienum cpcap_battery_irq_action {
848c2ecf20Sopenharmony_ci	CPCAP_BATTERY_IRQ_ACTION_NONE,
858c2ecf20Sopenharmony_ci	CPCAP_BATTERY_IRQ_ACTION_CC_CAL_DONE,
868c2ecf20Sopenharmony_ci	CPCAP_BATTERY_IRQ_ACTION_BATTERY_LOW,
878c2ecf20Sopenharmony_ci	CPCAP_BATTERY_IRQ_ACTION_POWEROFF,
888c2ecf20Sopenharmony_ci};
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistruct cpcap_interrupt_desc {
918c2ecf20Sopenharmony_ci	const char *name;
928c2ecf20Sopenharmony_ci	struct list_head node;
938c2ecf20Sopenharmony_ci	int irq;
948c2ecf20Sopenharmony_ci	enum cpcap_battery_irq_action action;
958c2ecf20Sopenharmony_ci};
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistruct cpcap_battery_config {
988c2ecf20Sopenharmony_ci	int cd_factor;
998c2ecf20Sopenharmony_ci	struct power_supply_info info;
1008c2ecf20Sopenharmony_ci	struct power_supply_battery_info bat;
1018c2ecf20Sopenharmony_ci};
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistruct cpcap_coulomb_counter_data {
1048c2ecf20Sopenharmony_ci	s32 sample;		/* 24 or 32 bits */
1058c2ecf20Sopenharmony_ci	s32 accumulator;
1068c2ecf20Sopenharmony_ci	s16 offset;		/* 9 bits */
1078c2ecf20Sopenharmony_ci	s16 integrator;		/* 13 or 16 bits */
1088c2ecf20Sopenharmony_ci};
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cienum cpcap_battery_state {
1118c2ecf20Sopenharmony_ci	CPCAP_BATTERY_STATE_PREVIOUS,
1128c2ecf20Sopenharmony_ci	CPCAP_BATTERY_STATE_LATEST,
1138c2ecf20Sopenharmony_ci	CPCAP_BATTERY_STATE_NR,
1148c2ecf20Sopenharmony_ci};
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistruct cpcap_battery_state_data {
1178c2ecf20Sopenharmony_ci	int voltage;
1188c2ecf20Sopenharmony_ci	int current_ua;
1198c2ecf20Sopenharmony_ci	int counter_uah;
1208c2ecf20Sopenharmony_ci	int temperature;
1218c2ecf20Sopenharmony_ci	ktime_t time;
1228c2ecf20Sopenharmony_ci	struct cpcap_coulomb_counter_data cc;
1238c2ecf20Sopenharmony_ci};
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistruct cpcap_battery_ddata {
1268c2ecf20Sopenharmony_ci	struct device *dev;
1278c2ecf20Sopenharmony_ci	struct regmap *reg;
1288c2ecf20Sopenharmony_ci	struct list_head irq_list;
1298c2ecf20Sopenharmony_ci	struct iio_channel *channels[CPCAP_BATTERY_IIO_NR];
1308c2ecf20Sopenharmony_ci	struct power_supply *psy;
1318c2ecf20Sopenharmony_ci	struct cpcap_battery_config config;
1328c2ecf20Sopenharmony_ci	struct cpcap_battery_state_data state[CPCAP_BATTERY_STATE_NR];
1338c2ecf20Sopenharmony_ci	u32 cc_lsb;		/* μAms per LSB */
1348c2ecf20Sopenharmony_ci	atomic_t active;
1358c2ecf20Sopenharmony_ci	int status;
1368c2ecf20Sopenharmony_ci	u16 vendor;
1378c2ecf20Sopenharmony_ci};
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci#define CPCAP_NO_BATTERY	-400
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic struct cpcap_battery_state_data *
1428c2ecf20Sopenharmony_cicpcap_battery_get_state(struct cpcap_battery_ddata *ddata,
1438c2ecf20Sopenharmony_ci			enum cpcap_battery_state state)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	if (state >= CPCAP_BATTERY_STATE_NR)
1468c2ecf20Sopenharmony_ci		return NULL;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	return &ddata->state[state];
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic struct cpcap_battery_state_data *
1528c2ecf20Sopenharmony_cicpcap_battery_latest(struct cpcap_battery_ddata *ddata)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	return cpcap_battery_get_state(ddata, CPCAP_BATTERY_STATE_LATEST);
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic struct cpcap_battery_state_data *
1588c2ecf20Sopenharmony_cicpcap_battery_previous(struct cpcap_battery_ddata *ddata)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	return cpcap_battery_get_state(ddata, CPCAP_BATTERY_STATE_PREVIOUS);
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic int cpcap_charger_battery_temperature(struct cpcap_battery_ddata *ddata,
1648c2ecf20Sopenharmony_ci					     int *value)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct iio_channel *channel;
1678c2ecf20Sopenharmony_ci	int error;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	channel = ddata->channels[CPCAP_BATTERY_IIO_BATTDET];
1708c2ecf20Sopenharmony_ci	error = iio_read_channel_processed(channel, value);
1718c2ecf20Sopenharmony_ci	if (error < 0) {
1728c2ecf20Sopenharmony_ci		dev_warn(ddata->dev, "%s failed: %i\n", __func__, error);
1738c2ecf20Sopenharmony_ci		*value = CPCAP_NO_BATTERY;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci		return error;
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	*value /= 100;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	return 0;
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic int cpcap_battery_get_voltage(struct cpcap_battery_ddata *ddata)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	struct iio_channel *channel;
1868c2ecf20Sopenharmony_ci	int error, value = 0;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	channel = ddata->channels[CPCAP_BATTERY_IIO_VOLTAGE];
1898c2ecf20Sopenharmony_ci	error = iio_read_channel_processed(channel, &value);
1908c2ecf20Sopenharmony_ci	if (error < 0) {
1918c2ecf20Sopenharmony_ci		dev_warn(ddata->dev, "%s failed: %i\n", __func__, error);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci		return 0;
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	return value * 1000;
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic int cpcap_battery_get_current(struct cpcap_battery_ddata *ddata)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	struct iio_channel *channel;
2028c2ecf20Sopenharmony_ci	int error, value = 0;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	channel = ddata->channels[CPCAP_BATTERY_IIO_BATT_CURRENT];
2058c2ecf20Sopenharmony_ci	error = iio_read_channel_processed(channel, &value);
2068c2ecf20Sopenharmony_ci	if (error < 0) {
2078c2ecf20Sopenharmony_ci		dev_warn(ddata->dev, "%s failed: %i\n", __func__, error);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci		return 0;
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	return value * 1000;
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci/**
2168c2ecf20Sopenharmony_ci * cpcap_battery_cc_raw_div - calculate and divide coulomb counter μAms values
2178c2ecf20Sopenharmony_ci * @ddata: device driver data
2188c2ecf20Sopenharmony_ci * @sample: coulomb counter sample value
2198c2ecf20Sopenharmony_ci * @accumulator: coulomb counter integrator value
2208c2ecf20Sopenharmony_ci * @offset: coulomb counter offset value
2218c2ecf20Sopenharmony_ci * @divider: conversion divider
2228c2ecf20Sopenharmony_ci *
2238c2ecf20Sopenharmony_ci * Note that cc_lsb and cc_dur values are from Motorola Linux kernel
2248c2ecf20Sopenharmony_ci * function data_get_avg_curr_ua() and seem to be based on measured test
2258c2ecf20Sopenharmony_ci * results. It also has the following comment:
2268c2ecf20Sopenharmony_ci *
2278c2ecf20Sopenharmony_ci * Adjustment factors are applied here as a temp solution per the test
2288c2ecf20Sopenharmony_ci * results. Need to work out a formal solution for this adjustment.
2298c2ecf20Sopenharmony_ci *
2308c2ecf20Sopenharmony_ci * A coulomb counter for similar hardware seems to be documented in
2318c2ecf20Sopenharmony_ci * "TWL6030 Gas Gauging Basics (Rev. A)" swca095a.pdf in chapter
2328c2ecf20Sopenharmony_ci * "10 Calculating Accumulated Current". We however follow what the
2338c2ecf20Sopenharmony_ci * Motorola mapphone Linux kernel is doing as there may be either a
2348c2ecf20Sopenharmony_ci * TI or ST coulomb counter in the PMIC.
2358c2ecf20Sopenharmony_ci */
2368c2ecf20Sopenharmony_cistatic int cpcap_battery_cc_raw_div(struct cpcap_battery_ddata *ddata,
2378c2ecf20Sopenharmony_ci				    s32 sample, s32 accumulator,
2388c2ecf20Sopenharmony_ci				    s16 offset, u32 divider)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	s64 acc;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	if (!divider)
2438c2ecf20Sopenharmony_ci		return 0;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	acc = accumulator;
2468c2ecf20Sopenharmony_ci	acc -= (s64)sample * offset;
2478c2ecf20Sopenharmony_ci	acc *= ddata->cc_lsb;
2488c2ecf20Sopenharmony_ci	acc *= -1;
2498c2ecf20Sopenharmony_ci	acc = div_s64(acc, divider);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	return acc;
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci/* 3600000μAms = 1μAh */
2558c2ecf20Sopenharmony_cistatic int cpcap_battery_cc_to_uah(struct cpcap_battery_ddata *ddata,
2568c2ecf20Sopenharmony_ci				   s32 sample, s32 accumulator,
2578c2ecf20Sopenharmony_ci				   s16 offset)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	return cpcap_battery_cc_raw_div(ddata, sample,
2608c2ecf20Sopenharmony_ci					accumulator, offset,
2618c2ecf20Sopenharmony_ci					3600000);
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic int cpcap_battery_cc_to_ua(struct cpcap_battery_ddata *ddata,
2658c2ecf20Sopenharmony_ci				  s32 sample, s32 accumulator,
2668c2ecf20Sopenharmony_ci				  s16 offset)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	return cpcap_battery_cc_raw_div(ddata, sample,
2698c2ecf20Sopenharmony_ci					accumulator, offset,
2708c2ecf20Sopenharmony_ci					sample *
2718c2ecf20Sopenharmony_ci					CPCAP_BATTERY_CC_SAMPLE_PERIOD_MS);
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci/**
2758c2ecf20Sopenharmony_ci * cpcap_battery_read_accumulated - reads cpcap coulomb counter
2768c2ecf20Sopenharmony_ci * @ddata: device driver data
2778c2ecf20Sopenharmony_ci * @ccd: coulomb counter values
2788c2ecf20Sopenharmony_ci *
2798c2ecf20Sopenharmony_ci * Based on Motorola mapphone kernel function data_read_regs().
2808c2ecf20Sopenharmony_ci * Looking at the registers, the coulomb counter seems similar to
2818c2ecf20Sopenharmony_ci * the coulomb counter in TWL6030. See "TWL6030 Gas Gauging Basics
2828c2ecf20Sopenharmony_ci * (Rev. A) swca095a.pdf for "10 Calculating Accumulated Current".
2838c2ecf20Sopenharmony_ci *
2848c2ecf20Sopenharmony_ci * Note that swca095a.pdf instructs to stop the coulomb counter
2858c2ecf20Sopenharmony_ci * before reading to avoid values changing. Motorola mapphone
2868c2ecf20Sopenharmony_ci * Linux kernel does not do it, so let's assume they've verified
2878c2ecf20Sopenharmony_ci * the data produced is correct.
2888c2ecf20Sopenharmony_ci */
2898c2ecf20Sopenharmony_cistatic int
2908c2ecf20Sopenharmony_cicpcap_battery_read_accumulated(struct cpcap_battery_ddata *ddata,
2918c2ecf20Sopenharmony_ci			       struct cpcap_coulomb_counter_data *ccd)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	u16 buf[7];	/* CPCAP_REG_CCS1 to CCI */
2948c2ecf20Sopenharmony_ci	int error;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	ccd->sample = 0;
2978c2ecf20Sopenharmony_ci	ccd->accumulator = 0;
2988c2ecf20Sopenharmony_ci	ccd->offset = 0;
2998c2ecf20Sopenharmony_ci	ccd->integrator = 0;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	/* Read coulomb counter register range */
3028c2ecf20Sopenharmony_ci	error = regmap_bulk_read(ddata->reg, CPCAP_REG_CCS1,
3038c2ecf20Sopenharmony_ci				 buf, ARRAY_SIZE(buf));
3048c2ecf20Sopenharmony_ci	if (error)
3058c2ecf20Sopenharmony_ci		return 0;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	/* Sample value CPCAP_REG_CCS1 & 2 */
3088c2ecf20Sopenharmony_ci	ccd->sample = (buf[1] & 0x0fff) << 16;
3098c2ecf20Sopenharmony_ci	ccd->sample |= buf[0];
3108c2ecf20Sopenharmony_ci	if (ddata->vendor == CPCAP_VENDOR_TI)
3118c2ecf20Sopenharmony_ci		ccd->sample = sign_extend32(24, ccd->sample);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	/* Accumulator value CPCAP_REG_CCA1 & 2 */
3148c2ecf20Sopenharmony_ci	ccd->accumulator = ((s16)buf[3]) << 16;
3158c2ecf20Sopenharmony_ci	ccd->accumulator |= buf[2];
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	/*
3188c2ecf20Sopenharmony_ci	 * Coulomb counter calibration offset is CPCAP_REG_CCM,
3198c2ecf20Sopenharmony_ci	 * REG_CCO seems unused
3208c2ecf20Sopenharmony_ci	 */
3218c2ecf20Sopenharmony_ci	ccd->offset = buf[4];
3228c2ecf20Sopenharmony_ci	ccd->offset = sign_extend32(ccd->offset, 9);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	/* Integrator register CPCAP_REG_CCI */
3258c2ecf20Sopenharmony_ci	if (ddata->vendor == CPCAP_VENDOR_TI)
3268c2ecf20Sopenharmony_ci		ccd->integrator = sign_extend32(buf[6], 13);
3278c2ecf20Sopenharmony_ci	else
3288c2ecf20Sopenharmony_ci		ccd->integrator = (s16)buf[6];
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	return cpcap_battery_cc_to_uah(ddata,
3318c2ecf20Sopenharmony_ci				       ccd->sample,
3328c2ecf20Sopenharmony_ci				       ccd->accumulator,
3338c2ecf20Sopenharmony_ci				       ccd->offset);
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci/**
3378c2ecf20Sopenharmony_ci * cpcap_battery_cc_get_avg_current - read cpcap coulumb counter
3388c2ecf20Sopenharmony_ci * @ddata: cpcap battery driver device data
3398c2ecf20Sopenharmony_ci */
3408c2ecf20Sopenharmony_cistatic int cpcap_battery_cc_get_avg_current(struct cpcap_battery_ddata *ddata)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	int value, acc, error;
3438c2ecf20Sopenharmony_ci	s32 sample;
3448c2ecf20Sopenharmony_ci	s16 offset;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	/* Coulomb counter integrator */
3478c2ecf20Sopenharmony_ci	error = regmap_read(ddata->reg, CPCAP_REG_CCI, &value);
3488c2ecf20Sopenharmony_ci	if (error)
3498c2ecf20Sopenharmony_ci		return error;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	if (ddata->vendor == CPCAP_VENDOR_TI) {
3528c2ecf20Sopenharmony_ci		acc = sign_extend32(value, 13);
3538c2ecf20Sopenharmony_ci		sample = 1;
3548c2ecf20Sopenharmony_ci	} else {
3558c2ecf20Sopenharmony_ci		acc = (s16)value;
3568c2ecf20Sopenharmony_ci		sample = 4;
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	/* Coulomb counter calibration offset  */
3608c2ecf20Sopenharmony_ci	error = regmap_read(ddata->reg, CPCAP_REG_CCM, &value);
3618c2ecf20Sopenharmony_ci	if (error)
3628c2ecf20Sopenharmony_ci		return error;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	offset = sign_extend32(value, 9);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	return cpcap_battery_cc_to_ua(ddata, sample, acc, offset);
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic bool cpcap_battery_full(struct cpcap_battery_ddata *ddata)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	struct cpcap_battery_state_data *state = cpcap_battery_latest(ddata);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	if (state->voltage >=
3748c2ecf20Sopenharmony_ci	    (ddata->config.bat.constant_charge_voltage_max_uv - 18000))
3758c2ecf20Sopenharmony_ci		return true;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	return false;
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic int cpcap_battery_update_status(struct cpcap_battery_ddata *ddata)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	struct cpcap_battery_state_data state, *latest, *previous;
3838c2ecf20Sopenharmony_ci	ktime_t now;
3848c2ecf20Sopenharmony_ci	int error;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	memset(&state, 0, sizeof(state));
3878c2ecf20Sopenharmony_ci	now = ktime_get();
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	latest = cpcap_battery_latest(ddata);
3908c2ecf20Sopenharmony_ci	if (latest) {
3918c2ecf20Sopenharmony_ci		s64 delta_ms = ktime_to_ms(ktime_sub(now, latest->time));
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci		if (delta_ms < CPCAP_BATTERY_CC_SAMPLE_PERIOD_MS)
3948c2ecf20Sopenharmony_ci			return delta_ms;
3958c2ecf20Sopenharmony_ci	}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	state.time = now;
3988c2ecf20Sopenharmony_ci	state.voltage = cpcap_battery_get_voltage(ddata);
3998c2ecf20Sopenharmony_ci	state.current_ua = cpcap_battery_get_current(ddata);
4008c2ecf20Sopenharmony_ci	state.counter_uah = cpcap_battery_read_accumulated(ddata, &state.cc);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	error = cpcap_charger_battery_temperature(ddata,
4038c2ecf20Sopenharmony_ci						  &state.temperature);
4048c2ecf20Sopenharmony_ci	if (error)
4058c2ecf20Sopenharmony_ci		return error;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	previous = cpcap_battery_previous(ddata);
4088c2ecf20Sopenharmony_ci	memcpy(previous, latest, sizeof(*previous));
4098c2ecf20Sopenharmony_ci	memcpy(latest, &state, sizeof(*latest));
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	return 0;
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cistatic enum power_supply_property cpcap_battery_props[] = {
4158c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_STATUS,
4168c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_PRESENT,
4178c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_TECHNOLOGY,
4188c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_NOW,
4198c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
4208c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
4218c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
4228c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CURRENT_AVG,
4238c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CURRENT_NOW,
4248c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
4258c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CHARGE_COUNTER,
4268c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_POWER_NOW,
4278c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_POWER_AVG,
4288c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
4298c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_SCOPE,
4308c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_TEMP,
4318c2ecf20Sopenharmony_ci};
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_cistatic int cpcap_battery_get_property(struct power_supply *psy,
4348c2ecf20Sopenharmony_ci				      enum power_supply_property psp,
4358c2ecf20Sopenharmony_ci				      union power_supply_propval *val)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	struct cpcap_battery_ddata *ddata = power_supply_get_drvdata(psy);
4388c2ecf20Sopenharmony_ci	struct cpcap_battery_state_data *latest, *previous;
4398c2ecf20Sopenharmony_ci	u32 sample;
4408c2ecf20Sopenharmony_ci	s32 accumulator;
4418c2ecf20Sopenharmony_ci	int cached;
4428c2ecf20Sopenharmony_ci	s64 tmp;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	cached = cpcap_battery_update_status(ddata);
4458c2ecf20Sopenharmony_ci	if (cached < 0)
4468c2ecf20Sopenharmony_ci		return cached;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	latest = cpcap_battery_latest(ddata);
4498c2ecf20Sopenharmony_ci	previous = cpcap_battery_previous(ddata);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	switch (psp) {
4528c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_PRESENT:
4538c2ecf20Sopenharmony_ci		if (latest->temperature > CPCAP_NO_BATTERY)
4548c2ecf20Sopenharmony_ci			val->intval = 1;
4558c2ecf20Sopenharmony_ci		else
4568c2ecf20Sopenharmony_ci			val->intval = 0;
4578c2ecf20Sopenharmony_ci		break;
4588c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_STATUS:
4598c2ecf20Sopenharmony_ci		if (cpcap_battery_full(ddata)) {
4608c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_STATUS_FULL;
4618c2ecf20Sopenharmony_ci			break;
4628c2ecf20Sopenharmony_ci		}
4638c2ecf20Sopenharmony_ci		if (cpcap_battery_cc_get_avg_current(ddata) < 0)
4648c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_STATUS_CHARGING;
4658c2ecf20Sopenharmony_ci		else
4668c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
4678c2ecf20Sopenharmony_ci		break;
4688c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_TECHNOLOGY:
4698c2ecf20Sopenharmony_ci		val->intval = ddata->config.info.technology;
4708c2ecf20Sopenharmony_ci		break;
4718c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
4728c2ecf20Sopenharmony_ci		val->intval = cpcap_battery_get_voltage(ddata);
4738c2ecf20Sopenharmony_ci		break;
4748c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
4758c2ecf20Sopenharmony_ci		val->intval = ddata->config.info.voltage_max_design;
4768c2ecf20Sopenharmony_ci		break;
4778c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
4788c2ecf20Sopenharmony_ci		val->intval = ddata->config.info.voltage_min_design;
4798c2ecf20Sopenharmony_ci		break;
4808c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
4818c2ecf20Sopenharmony_ci		val->intval = ddata->config.bat.constant_charge_voltage_max_uv;
4828c2ecf20Sopenharmony_ci		break;
4838c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CURRENT_AVG:
4848c2ecf20Sopenharmony_ci		sample = latest->cc.sample - previous->cc.sample;
4858c2ecf20Sopenharmony_ci		if (!sample) {
4868c2ecf20Sopenharmony_ci			val->intval = cpcap_battery_cc_get_avg_current(ddata);
4878c2ecf20Sopenharmony_ci			break;
4888c2ecf20Sopenharmony_ci		}
4898c2ecf20Sopenharmony_ci		accumulator = latest->cc.accumulator - previous->cc.accumulator;
4908c2ecf20Sopenharmony_ci		val->intval = cpcap_battery_cc_to_ua(ddata, sample,
4918c2ecf20Sopenharmony_ci						     accumulator,
4928c2ecf20Sopenharmony_ci						     latest->cc.offset);
4938c2ecf20Sopenharmony_ci		break;
4948c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CURRENT_NOW:
4958c2ecf20Sopenharmony_ci		val->intval = latest->current_ua;
4968c2ecf20Sopenharmony_ci		break;
4978c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CHARGE_COUNTER:
4988c2ecf20Sopenharmony_ci		val->intval = latest->counter_uah;
4998c2ecf20Sopenharmony_ci		break;
5008c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_POWER_NOW:
5018c2ecf20Sopenharmony_ci		tmp = (latest->voltage / 10000) * latest->current_ua;
5028c2ecf20Sopenharmony_ci		val->intval = div64_s64(tmp, 100);
5038c2ecf20Sopenharmony_ci		break;
5048c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_POWER_AVG:
5058c2ecf20Sopenharmony_ci		sample = latest->cc.sample - previous->cc.sample;
5068c2ecf20Sopenharmony_ci		if (!sample) {
5078c2ecf20Sopenharmony_ci			tmp = cpcap_battery_cc_get_avg_current(ddata);
5088c2ecf20Sopenharmony_ci			tmp *= (latest->voltage / 10000);
5098c2ecf20Sopenharmony_ci			val->intval = div64_s64(tmp, 100);
5108c2ecf20Sopenharmony_ci			break;
5118c2ecf20Sopenharmony_ci		}
5128c2ecf20Sopenharmony_ci		accumulator = latest->cc.accumulator - previous->cc.accumulator;
5138c2ecf20Sopenharmony_ci		tmp = cpcap_battery_cc_to_ua(ddata, sample, accumulator,
5148c2ecf20Sopenharmony_ci					     latest->cc.offset);
5158c2ecf20Sopenharmony_ci		tmp *= ((latest->voltage + previous->voltage) / 20000);
5168c2ecf20Sopenharmony_ci		val->intval = div64_s64(tmp, 100);
5178c2ecf20Sopenharmony_ci		break;
5188c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
5198c2ecf20Sopenharmony_ci		if (cpcap_battery_full(ddata))
5208c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
5218c2ecf20Sopenharmony_ci		else if (latest->voltage >= 3750000)
5228c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
5238c2ecf20Sopenharmony_ci		else if (latest->voltage >= 3300000)
5248c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
5258c2ecf20Sopenharmony_ci		else if (latest->voltage > 3100000)
5268c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
5278c2ecf20Sopenharmony_ci		else if (latest->voltage <= 3100000)
5288c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
5298c2ecf20Sopenharmony_ci		else
5308c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
5318c2ecf20Sopenharmony_ci		break;
5328c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
5338c2ecf20Sopenharmony_ci		val->intval = ddata->config.info.charge_full_design;
5348c2ecf20Sopenharmony_ci		break;
5358c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_SCOPE:
5368c2ecf20Sopenharmony_ci		val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
5378c2ecf20Sopenharmony_ci		break;
5388c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_TEMP:
5398c2ecf20Sopenharmony_ci		val->intval = latest->temperature;
5408c2ecf20Sopenharmony_ci		break;
5418c2ecf20Sopenharmony_ci	default:
5428c2ecf20Sopenharmony_ci		return -EINVAL;
5438c2ecf20Sopenharmony_ci	}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	return 0;
5468c2ecf20Sopenharmony_ci}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_cistatic int cpcap_battery_update_charger(struct cpcap_battery_ddata *ddata,
5498c2ecf20Sopenharmony_ci					int const_charge_voltage)
5508c2ecf20Sopenharmony_ci{
5518c2ecf20Sopenharmony_ci	union power_supply_propval prop;
5528c2ecf20Sopenharmony_ci	union power_supply_propval val;
5538c2ecf20Sopenharmony_ci	struct power_supply *charger;
5548c2ecf20Sopenharmony_ci	int error;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	charger = power_supply_get_by_name("usb");
5578c2ecf20Sopenharmony_ci	if (!charger)
5588c2ecf20Sopenharmony_ci		return -ENODEV;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	error = power_supply_get_property(charger,
5618c2ecf20Sopenharmony_ci				POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
5628c2ecf20Sopenharmony_ci				&prop);
5638c2ecf20Sopenharmony_ci	if (error)
5648c2ecf20Sopenharmony_ci		goto out_put;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	/* Allow charger const voltage lower than battery const voltage */
5678c2ecf20Sopenharmony_ci	if (const_charge_voltage > prop.intval)
5688c2ecf20Sopenharmony_ci		goto out_put;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	val.intval = const_charge_voltage;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	error = power_supply_set_property(charger,
5738c2ecf20Sopenharmony_ci			POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
5748c2ecf20Sopenharmony_ci			&val);
5758c2ecf20Sopenharmony_ciout_put:
5768c2ecf20Sopenharmony_ci	power_supply_put(charger);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	return error;
5798c2ecf20Sopenharmony_ci}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_cistatic int cpcap_battery_set_property(struct power_supply *psy,
5828c2ecf20Sopenharmony_ci				      enum power_supply_property psp,
5838c2ecf20Sopenharmony_ci				      const union power_supply_propval *val)
5848c2ecf20Sopenharmony_ci{
5858c2ecf20Sopenharmony_ci	struct cpcap_battery_ddata *ddata = power_supply_get_drvdata(psy);
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	switch (psp) {
5888c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
5898c2ecf20Sopenharmony_ci		if (val->intval < ddata->config.info.voltage_min_design)
5908c2ecf20Sopenharmony_ci			return -EINVAL;
5918c2ecf20Sopenharmony_ci		if (val->intval > ddata->config.info.voltage_max_design)
5928c2ecf20Sopenharmony_ci			return -EINVAL;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci		ddata->config.bat.constant_charge_voltage_max_uv = val->intval;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci		return cpcap_battery_update_charger(ddata, val->intval);
5978c2ecf20Sopenharmony_ci	default:
5988c2ecf20Sopenharmony_ci		return -EINVAL;
5998c2ecf20Sopenharmony_ci	}
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	return 0;
6028c2ecf20Sopenharmony_ci}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_cistatic int cpcap_battery_property_is_writeable(struct power_supply *psy,
6058c2ecf20Sopenharmony_ci					       enum power_supply_property psp)
6068c2ecf20Sopenharmony_ci{
6078c2ecf20Sopenharmony_ci	switch (psp) {
6088c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
6098c2ecf20Sopenharmony_ci		return 1;
6108c2ecf20Sopenharmony_ci	default:
6118c2ecf20Sopenharmony_ci		return 0;
6128c2ecf20Sopenharmony_ci	}
6138c2ecf20Sopenharmony_ci}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_cistatic irqreturn_t cpcap_battery_irq_thread(int irq, void *data)
6168c2ecf20Sopenharmony_ci{
6178c2ecf20Sopenharmony_ci	struct cpcap_battery_ddata *ddata = data;
6188c2ecf20Sopenharmony_ci	struct cpcap_battery_state_data *latest;
6198c2ecf20Sopenharmony_ci	struct cpcap_interrupt_desc *d;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	if (!atomic_read(&ddata->active))
6228c2ecf20Sopenharmony_ci		return IRQ_NONE;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	list_for_each_entry(d, &ddata->irq_list, node) {
6258c2ecf20Sopenharmony_ci		if (irq == d->irq)
6268c2ecf20Sopenharmony_ci			break;
6278c2ecf20Sopenharmony_ci	}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	if (list_entry_is_head(d, &ddata->irq_list, node))
6308c2ecf20Sopenharmony_ci		return IRQ_NONE;
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	latest = cpcap_battery_latest(ddata);
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	switch (d->action) {
6358c2ecf20Sopenharmony_ci	case CPCAP_BATTERY_IRQ_ACTION_CC_CAL_DONE:
6368c2ecf20Sopenharmony_ci		dev_info(ddata->dev, "Coulomb counter calibration done\n");
6378c2ecf20Sopenharmony_ci		break;
6388c2ecf20Sopenharmony_ci	case CPCAP_BATTERY_IRQ_ACTION_BATTERY_LOW:
6398c2ecf20Sopenharmony_ci		if (latest->current_ua >= 0)
6408c2ecf20Sopenharmony_ci			dev_warn(ddata->dev, "Battery low at %imV!\n",
6418c2ecf20Sopenharmony_ci				latest->voltage / 1000);
6428c2ecf20Sopenharmony_ci		break;
6438c2ecf20Sopenharmony_ci	case CPCAP_BATTERY_IRQ_ACTION_POWEROFF:
6448c2ecf20Sopenharmony_ci		if (latest->current_ua >= 0 && latest->voltage <= 3200000) {
6458c2ecf20Sopenharmony_ci			dev_emerg(ddata->dev,
6468c2ecf20Sopenharmony_ci				  "Battery empty at %imV, powering off\n",
6478c2ecf20Sopenharmony_ci				  latest->voltage / 1000);
6488c2ecf20Sopenharmony_ci			orderly_poweroff(true);
6498c2ecf20Sopenharmony_ci		}
6508c2ecf20Sopenharmony_ci		break;
6518c2ecf20Sopenharmony_ci	default:
6528c2ecf20Sopenharmony_ci		break;
6538c2ecf20Sopenharmony_ci	}
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	power_supply_changed(ddata->psy);
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
6588c2ecf20Sopenharmony_ci}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_cistatic int cpcap_battery_init_irq(struct platform_device *pdev,
6618c2ecf20Sopenharmony_ci				  struct cpcap_battery_ddata *ddata,
6628c2ecf20Sopenharmony_ci				  const char *name)
6638c2ecf20Sopenharmony_ci{
6648c2ecf20Sopenharmony_ci	struct cpcap_interrupt_desc *d;
6658c2ecf20Sopenharmony_ci	int irq, error;
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	irq = platform_get_irq_byname(pdev, name);
6688c2ecf20Sopenharmony_ci	if (irq < 0)
6698c2ecf20Sopenharmony_ci		return irq;
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	error = devm_request_threaded_irq(ddata->dev, irq, NULL,
6728c2ecf20Sopenharmony_ci					  cpcap_battery_irq_thread,
6738c2ecf20Sopenharmony_ci					  IRQF_SHARED | IRQF_ONESHOT,
6748c2ecf20Sopenharmony_ci					  name, ddata);
6758c2ecf20Sopenharmony_ci	if (error) {
6768c2ecf20Sopenharmony_ci		dev_err(ddata->dev, "could not get irq %s: %i\n",
6778c2ecf20Sopenharmony_ci			name, error);
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci		return error;
6808c2ecf20Sopenharmony_ci	}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	d = devm_kzalloc(ddata->dev, sizeof(*d), GFP_KERNEL);
6838c2ecf20Sopenharmony_ci	if (!d)
6848c2ecf20Sopenharmony_ci		return -ENOMEM;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	d->name = name;
6878c2ecf20Sopenharmony_ci	d->irq = irq;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	if (!strncmp(name, "cccal", 5))
6908c2ecf20Sopenharmony_ci		d->action = CPCAP_BATTERY_IRQ_ACTION_CC_CAL_DONE;
6918c2ecf20Sopenharmony_ci	else if (!strncmp(name, "lowbph", 6))
6928c2ecf20Sopenharmony_ci		d->action = CPCAP_BATTERY_IRQ_ACTION_BATTERY_LOW;
6938c2ecf20Sopenharmony_ci	else if (!strncmp(name, "lowbpl", 6))
6948c2ecf20Sopenharmony_ci		d->action = CPCAP_BATTERY_IRQ_ACTION_POWEROFF;
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	list_add(&d->node, &ddata->irq_list);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	return 0;
6998c2ecf20Sopenharmony_ci}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_cistatic int cpcap_battery_init_interrupts(struct platform_device *pdev,
7028c2ecf20Sopenharmony_ci					 struct cpcap_battery_ddata *ddata)
7038c2ecf20Sopenharmony_ci{
7048c2ecf20Sopenharmony_ci	static const char * const cpcap_battery_irqs[] = {
7058c2ecf20Sopenharmony_ci		"eol", "lowbph", "lowbpl",
7068c2ecf20Sopenharmony_ci		"chrgcurr1", "battdetb"
7078c2ecf20Sopenharmony_ci	};
7088c2ecf20Sopenharmony_ci	int i, error;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(cpcap_battery_irqs); i++) {
7118c2ecf20Sopenharmony_ci		error = cpcap_battery_init_irq(pdev, ddata,
7128c2ecf20Sopenharmony_ci					       cpcap_battery_irqs[i]);
7138c2ecf20Sopenharmony_ci		if (error)
7148c2ecf20Sopenharmony_ci			return error;
7158c2ecf20Sopenharmony_ci	}
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	/* Enable calibration interrupt if already available in dts */
7188c2ecf20Sopenharmony_ci	cpcap_battery_init_irq(pdev, ddata, "cccal");
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	/* Enable low battery interrupts for 3.3V high and 3.1V low */
7218c2ecf20Sopenharmony_ci	error = regmap_update_bits(ddata->reg, CPCAP_REG_BPEOL,
7228c2ecf20Sopenharmony_ci				   0xffff,
7238c2ecf20Sopenharmony_ci				   CPCAP_REG_BPEOL_BIT_BATTDETEN);
7248c2ecf20Sopenharmony_ci	if (error)
7258c2ecf20Sopenharmony_ci		return error;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	return 0;
7288c2ecf20Sopenharmony_ci}
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_cistatic int cpcap_battery_init_iio(struct cpcap_battery_ddata *ddata)
7318c2ecf20Sopenharmony_ci{
7328c2ecf20Sopenharmony_ci	const char * const names[CPCAP_BATTERY_IIO_NR] = {
7338c2ecf20Sopenharmony_ci		"battdetb", "battp", "chg_isense", "batti",
7348c2ecf20Sopenharmony_ci	};
7358c2ecf20Sopenharmony_ci	int error, i;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	for (i = 0; i < CPCAP_BATTERY_IIO_NR; i++) {
7388c2ecf20Sopenharmony_ci		ddata->channels[i] = devm_iio_channel_get(ddata->dev,
7398c2ecf20Sopenharmony_ci							  names[i]);
7408c2ecf20Sopenharmony_ci		if (IS_ERR(ddata->channels[i])) {
7418c2ecf20Sopenharmony_ci			error = PTR_ERR(ddata->channels[i]);
7428c2ecf20Sopenharmony_ci			goto out_err;
7438c2ecf20Sopenharmony_ci		}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci		if (!ddata->channels[i]->indio_dev) {
7468c2ecf20Sopenharmony_ci			error = -ENXIO;
7478c2ecf20Sopenharmony_ci			goto out_err;
7488c2ecf20Sopenharmony_ci		}
7498c2ecf20Sopenharmony_ci	}
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	return 0;
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ciout_err:
7548c2ecf20Sopenharmony_ci	return dev_err_probe(ddata->dev, error,
7558c2ecf20Sopenharmony_ci			     "could not initialize VBUS or ID IIO\n");
7568c2ecf20Sopenharmony_ci}
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci/* Calibrate coulomb counter */
7598c2ecf20Sopenharmony_cistatic int cpcap_battery_calibrate(struct cpcap_battery_ddata *ddata)
7608c2ecf20Sopenharmony_ci{
7618c2ecf20Sopenharmony_ci	int error, ccc1, value;
7628c2ecf20Sopenharmony_ci	unsigned long timeout;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	error = regmap_read(ddata->reg, CPCAP_REG_CCC1, &ccc1);
7658c2ecf20Sopenharmony_ci	if (error)
7668c2ecf20Sopenharmony_ci		return error;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(6000);
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	/* Start calibration */
7718c2ecf20Sopenharmony_ci	error = regmap_update_bits(ddata->reg, CPCAP_REG_CCC1,
7728c2ecf20Sopenharmony_ci				   0xffff,
7738c2ecf20Sopenharmony_ci				   CPCAP_REG_CCC1_CAL_EN);
7748c2ecf20Sopenharmony_ci	if (error)
7758c2ecf20Sopenharmony_ci		goto restore;
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	while (time_before(jiffies, timeout)) {
7788c2ecf20Sopenharmony_ci		error = regmap_read(ddata->reg, CPCAP_REG_CCC1, &value);
7798c2ecf20Sopenharmony_ci		if (error)
7808c2ecf20Sopenharmony_ci			goto restore;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci		if (!(value & CPCAP_REG_CCC1_CAL_EN))
7838c2ecf20Sopenharmony_ci			break;
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci		error = regmap_read(ddata->reg, CPCAP_REG_CCM, &value);
7868c2ecf20Sopenharmony_ci		if (error)
7878c2ecf20Sopenharmony_ci			goto restore;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci		msleep(300);
7908c2ecf20Sopenharmony_ci	}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	/* Read calibration offset from CCM */
7938c2ecf20Sopenharmony_ci	error = regmap_read(ddata->reg, CPCAP_REG_CCM, &value);
7948c2ecf20Sopenharmony_ci	if (error)
7958c2ecf20Sopenharmony_ci		goto restore;
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	dev_info(ddata->dev, "calibration done: 0x%04x\n", value);
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_cirestore:
8008c2ecf20Sopenharmony_ci	if (error)
8018c2ecf20Sopenharmony_ci		dev_err(ddata->dev, "%s: error %i\n", __func__, error);
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	error = regmap_update_bits(ddata->reg, CPCAP_REG_CCC1,
8048c2ecf20Sopenharmony_ci				   0xffff, ccc1);
8058c2ecf20Sopenharmony_ci	if (error)
8068c2ecf20Sopenharmony_ci		dev_err(ddata->dev, "%s: restore error %i\n",
8078c2ecf20Sopenharmony_ci			__func__, error);
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	return error;
8108c2ecf20Sopenharmony_ci}
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci/*
8138c2ecf20Sopenharmony_ci * Based on the values from Motorola mapphone Linux kernel. In the
8148c2ecf20Sopenharmony_ci * the Motorola mapphone Linux kernel tree the value for pm_cd_factor
8158c2ecf20Sopenharmony_ci * is passed to the kernel via device tree. If it turns out to be
8168c2ecf20Sopenharmony_ci * something device specific we can consider that too later.
8178c2ecf20Sopenharmony_ci *
8188c2ecf20Sopenharmony_ci * And looking at the battery full and shutdown values for the stock
8198c2ecf20Sopenharmony_ci * kernel on droid 4, full is 4351000 and software initiates shutdown
8208c2ecf20Sopenharmony_ci * at 3078000. The device will die around 2743000.
8218c2ecf20Sopenharmony_ci */
8228c2ecf20Sopenharmony_cistatic const struct cpcap_battery_config cpcap_battery_default_data = {
8238c2ecf20Sopenharmony_ci	.cd_factor = 0x3cc,
8248c2ecf20Sopenharmony_ci	.info.technology = POWER_SUPPLY_TECHNOLOGY_LION,
8258c2ecf20Sopenharmony_ci	.info.voltage_max_design = 4351000,
8268c2ecf20Sopenharmony_ci	.info.voltage_min_design = 3100000,
8278c2ecf20Sopenharmony_ci	.info.charge_full_design = 1740000,
8288c2ecf20Sopenharmony_ci	.bat.constant_charge_voltage_max_uv = 4200000,
8298c2ecf20Sopenharmony_ci};
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
8328c2ecf20Sopenharmony_cistatic const struct of_device_id cpcap_battery_id_table[] = {
8338c2ecf20Sopenharmony_ci	{
8348c2ecf20Sopenharmony_ci		.compatible = "motorola,cpcap-battery",
8358c2ecf20Sopenharmony_ci		.data = &cpcap_battery_default_data,
8368c2ecf20Sopenharmony_ci	},
8378c2ecf20Sopenharmony_ci	{},
8388c2ecf20Sopenharmony_ci};
8398c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, cpcap_battery_id_table);
8408c2ecf20Sopenharmony_ci#endif
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_cistatic int cpcap_battery_probe(struct platform_device *pdev)
8438c2ecf20Sopenharmony_ci{
8448c2ecf20Sopenharmony_ci	struct power_supply_desc *psy_desc;
8458c2ecf20Sopenharmony_ci	struct cpcap_battery_ddata *ddata;
8468c2ecf20Sopenharmony_ci	const struct of_device_id *match;
8478c2ecf20Sopenharmony_ci	struct power_supply_config psy_cfg = {};
8488c2ecf20Sopenharmony_ci	int error;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	match = of_match_device(of_match_ptr(cpcap_battery_id_table),
8518c2ecf20Sopenharmony_ci				&pdev->dev);
8528c2ecf20Sopenharmony_ci	if (!match)
8538c2ecf20Sopenharmony_ci		return -EINVAL;
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	if (!match->data) {
8568c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no configuration data found\n");
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci		return -ENODEV;
8598c2ecf20Sopenharmony_ci	}
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
8628c2ecf20Sopenharmony_ci	if (!ddata)
8638c2ecf20Sopenharmony_ci		return -ENOMEM;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ddata->irq_list);
8668c2ecf20Sopenharmony_ci	ddata->dev = &pdev->dev;
8678c2ecf20Sopenharmony_ci	memcpy(&ddata->config, match->data, sizeof(ddata->config));
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	ddata->reg = dev_get_regmap(ddata->dev->parent, NULL);
8708c2ecf20Sopenharmony_ci	if (!ddata->reg)
8718c2ecf20Sopenharmony_ci		return -ENODEV;
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	error = cpcap_get_vendor(ddata->dev, ddata->reg, &ddata->vendor);
8748c2ecf20Sopenharmony_ci	if (error)
8758c2ecf20Sopenharmony_ci		return error;
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	switch (ddata->vendor) {
8788c2ecf20Sopenharmony_ci	case CPCAP_VENDOR_ST:
8798c2ecf20Sopenharmony_ci		ddata->cc_lsb = 95374;	/* μAms per LSB */
8808c2ecf20Sopenharmony_ci		break;
8818c2ecf20Sopenharmony_ci	case CPCAP_VENDOR_TI:
8828c2ecf20Sopenharmony_ci		ddata->cc_lsb = 91501;	/* μAms per LSB */
8838c2ecf20Sopenharmony_ci		break;
8848c2ecf20Sopenharmony_ci	default:
8858c2ecf20Sopenharmony_ci		return -EINVAL;
8868c2ecf20Sopenharmony_ci	}
8878c2ecf20Sopenharmony_ci	ddata->cc_lsb = (ddata->cc_lsb * ddata->config.cd_factor) / 1000;
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, ddata);
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	error = cpcap_battery_init_interrupts(pdev, ddata);
8928c2ecf20Sopenharmony_ci	if (error)
8938c2ecf20Sopenharmony_ci		return error;
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	error = cpcap_battery_init_iio(ddata);
8968c2ecf20Sopenharmony_ci	if (error)
8978c2ecf20Sopenharmony_ci		return error;
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	psy_desc = devm_kzalloc(ddata->dev, sizeof(*psy_desc), GFP_KERNEL);
9008c2ecf20Sopenharmony_ci	if (!psy_desc)
9018c2ecf20Sopenharmony_ci		return -ENOMEM;
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	psy_desc->name = "battery";
9048c2ecf20Sopenharmony_ci	psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
9058c2ecf20Sopenharmony_ci	psy_desc->properties = cpcap_battery_props;
9068c2ecf20Sopenharmony_ci	psy_desc->num_properties = ARRAY_SIZE(cpcap_battery_props);
9078c2ecf20Sopenharmony_ci	psy_desc->get_property = cpcap_battery_get_property;
9088c2ecf20Sopenharmony_ci	psy_desc->set_property = cpcap_battery_set_property;
9098c2ecf20Sopenharmony_ci	psy_desc->property_is_writeable = cpcap_battery_property_is_writeable;
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	psy_cfg.of_node = pdev->dev.of_node;
9128c2ecf20Sopenharmony_ci	psy_cfg.drv_data = ddata;
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	ddata->psy = devm_power_supply_register(ddata->dev, psy_desc,
9158c2ecf20Sopenharmony_ci						&psy_cfg);
9168c2ecf20Sopenharmony_ci	error = PTR_ERR_OR_ZERO(ddata->psy);
9178c2ecf20Sopenharmony_ci	if (error) {
9188c2ecf20Sopenharmony_ci		dev_err(ddata->dev, "failed to register power supply\n");
9198c2ecf20Sopenharmony_ci		return error;
9208c2ecf20Sopenharmony_ci	}
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	atomic_set(&ddata->active, 1);
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	error = cpcap_battery_calibrate(ddata);
9258c2ecf20Sopenharmony_ci	if (error)
9268c2ecf20Sopenharmony_ci		return error;
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	return 0;
9298c2ecf20Sopenharmony_ci}
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_cistatic int cpcap_battery_remove(struct platform_device *pdev)
9328c2ecf20Sopenharmony_ci{
9338c2ecf20Sopenharmony_ci	struct cpcap_battery_ddata *ddata = platform_get_drvdata(pdev);
9348c2ecf20Sopenharmony_ci	int error;
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	atomic_set(&ddata->active, 0);
9378c2ecf20Sopenharmony_ci	error = regmap_update_bits(ddata->reg, CPCAP_REG_BPEOL,
9388c2ecf20Sopenharmony_ci				   0xffff, 0);
9398c2ecf20Sopenharmony_ci	if (error)
9408c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "could not disable: %i\n", error);
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	return 0;
9438c2ecf20Sopenharmony_ci}
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_cistatic struct platform_driver cpcap_battery_driver = {
9468c2ecf20Sopenharmony_ci	.driver	= {
9478c2ecf20Sopenharmony_ci		.name		= "cpcap_battery",
9488c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(cpcap_battery_id_table),
9498c2ecf20Sopenharmony_ci	},
9508c2ecf20Sopenharmony_ci	.probe	= cpcap_battery_probe,
9518c2ecf20Sopenharmony_ci	.remove = cpcap_battery_remove,
9528c2ecf20Sopenharmony_ci};
9538c2ecf20Sopenharmony_cimodule_platform_driver(cpcap_battery_driver);
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
9568c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
9578c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("CPCAP PMIC Battery Driver");
958