// SPDX-License-Identifier: GPL-2.0-only
/*
* Battery driver for CPCAP PMIC
*
* Copyright (C) 2017 Tony Lindgren <tony@atomide.com>
*
* Some parts of the code based on earlier Motorola mapphone Linux kernel
* drivers:
*
* Copyright (C) 2009-2010 Motorola, Inc.
*/
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/reboot.h>
#include <linux/regmap.h>
#include <linux/nvmem-consumer.h>
#include <linux/moduleparam.h>
#include <linux/iio/consumer.h>
#include <linux/iio/types.h>
#include <linux/mfd/motorola-cpcap.h>
/*
* Register bit defines for CPCAP_REG_BPEOL. Some of these seem to
* map to MC13783UG.pdf "Table 5-19. Register 13, Power Control 0"
* to enable BATTDETEN, LOBAT and EOL features. We currently use
* LOBAT interrupts instead of EOL.
*/
#define CPCAP_REG_BPEOL_BIT_EOL9 BIT(9) /* Set for EOL irq */
#define CPCAP_REG_BPEOL_BIT_EOL8 BIT(8) /* Set for EOL irq */
#define CPCAP_REG_BPEOL_BIT_UNKNOWN7 BIT(7)
#define CPCAP_REG_BPEOL_BIT_UNKNOWN6 BIT(6)
#define CPCAP_REG_BPEOL_BIT_UNKNOWN5 BIT(5)
#define CPCAP_REG_BPEOL_BIT_EOL_MULTI BIT(4) /* Set for multiple EOL irqs */
#define CPCAP_REG_BPEOL_BIT_UNKNOWN3 BIT(3)
#define CPCAP_REG_BPEOL_BIT_UNKNOWN2 BIT(2)
#define CPCAP_REG_BPEOL_BIT_BATTDETEN BIT(1) /* Enable battery detect */
#define CPCAP_REG_BPEOL_BIT_EOLSEL BIT(0) /* BPDET = 0, EOL = 1 */
/*
* Register bit defines for CPCAP_REG_CCC1. These seem similar to the twl6030
* coulomb counter registers rather than the mc13892 registers. Both twl6030
* and mc13892 set bits 2 and 1 to reset and clear registers. But mc13892
* sets bit 0 to start the coulomb counter while twl6030 sets bit 0 to stop
* the coulomb counter like cpcap does. So for now, we use the twl6030 style
* naming for the registers.
*/
#define CPCAP_REG_CCC1_ACTIVE_MODE1 BIT(4) /* Update rate */
#define CPCAP_REG_CCC1_ACTIVE_MODE0 BIT(3) /* Update rate */
#define CPCAP_REG_CCC1_AUTOCLEAR BIT(2) /* Resets sample registers */
#define CPCAP_REG_CCC1_CAL_EN BIT(1) /* Clears after write in 1s */
#define CPCAP_REG_CCC1_PAUSE BIT(0) /* Stop counters, allow write */
#define CPCAP_REG_CCC1_RESET_MASK (CPCAP_REG_CCC1_AUTOCLEAR | \
CPCAP_REG_CCC1_CAL_EN)
#define CPCAP_REG_CCCC2_RATE1 BIT(5)
#define CPCAP_REG_CCCC2_RATE0 BIT(4)
#define CPCAP_REG_CCCC2_ENABLE BIT(3)
#define CPCAP_BATTERY_CC_SAMPLE_PERIOD_MS 250
#define CPCAP_BATTERY_EB41_HW4X_ID 0x9E
#define CPCAP_BATTERY_BW8X_ID 0x98
enum {
CPCAP_BATTERY_IIO_BATTDET,
CPCAP_BATTERY_IIO_VOLTAGE,
CPCAP_BATTERY_IIO_CHRG_CURRENT,
CPCAP_BATTERY_IIO_BATT_CURRENT,
CPCAP_BATTERY_IIO_NR,
};
enum cpcap_battery_irq_action {
CPCAP_BATTERY_IRQ_ACTION_NONE,
CPCAP_BATTERY_IRQ_ACTION_CC_CAL_DONE,
CPCAP_BATTERY_IRQ_ACTION_BATTERY_LOW,
CPCAP_BATTERY_IRQ_ACTION_POWEROFF,
};
struct cpcap_interrupt_desc {
const char *name;
struct list_head node;
int irq;
enum cpcap_battery_irq_action action;
};
struct cpcap_battery_config {
int cd_factor;
struct power_supply_info info;
struct power_supply_battery_info bat;
};
struct cpcap_coulomb_counter_data {
s32 sample; /* 24 or 32 bits */
s32 accumulator;
s16 offset; /* 9 bits */
s16 integrator; /* 13 or 16 bits */
};
enum cpcap_battery_state {
CPCAP_BATTERY_STATE_PREVIOUS,
CPCAP_BATTERY_STATE_LATEST,
CPCAP_BATTERY_STATE_EMPTY,
CPCAP_BATTERY_STATE_FULL,
CPCAP_BATTERY_STATE_NR,
};
struct cpcap_battery_state_data {
int voltage;
int current_ua;
int counter_uah;
int temperature;
ktime_t time;
struct cpcap_coulomb_counter_data cc;
};
struct cpcap_battery_ddata {
struct device *dev;
struct regmap *reg;
struct list_head irq_list;
struct iio_channel *channels[CPCAP_BATTERY_IIO_NR];
struct power_supply *psy;
struct cpcap_battery_config config;
struct cpcap_battery_state_data state[CPCAP_BATTERY_STATE_NR];
u32 cc_lsb; /* μAms per LSB */
atomic_t active;
int charge_full;
int status;
u16 vendor;
bool check_nvmem;
unsigned int is_full:1;
};
#define CPCAP_NO_BATTERY -400
static bool ignore_temperature_probe;
module_param(ignore_temperature_probe, bool, 0660);
static struct cpcap_battery_state_data *
cpcap_battery_get_state(struct cpcap_battery_ddata *ddata,
enum cpcap_battery_state state)
{
if (state >= CPCAP_BATTERY_STATE_NR)
return NULL;
return &ddata->state[state];
}
static struct cpcap_battery_state_data *
cpcap_battery_latest(struct cpcap_battery_ddata *ddata)
{
return cpcap_battery_get_state(ddata, CPCAP_BATTERY_STATE_LATEST);
}
static struct cpcap_battery_state_data *
cpcap_battery_previous(struct cpcap_battery_ddata *ddata)
{
return cpcap_battery_get_state(ddata, CPCAP_BATTERY_STATE_PREVIOUS);
}
static struct cpcap_battery_state_data *
cpcap_battery_get_empty(struct cpcap_battery_ddata *ddata)
{
return cpcap_battery_get_state(ddata, CPCAP_BATTERY_STATE_EMPTY);
}
static struct cpcap_battery_state_data *
cpcap_battery_get_full(struct cpcap_battery_ddata *ddata)
{
return cpcap_battery_get_state(ddata, CPCAP_BATTERY_STATE_FULL);
}
static int cpcap_charger_battery_temperature(struct cpcap_battery_ddata *ddata,
int *value)
{
struct iio_channel *channel;
int error;
channel = ddata->channels[CPCAP_BATTERY_IIO_BATTDET];
error = iio_read_channel_processed(channel, value);
if