blob: 953c217c192eb55a64a90853f9c95039de0f0729 [file] [log] [blame]
/*
* Copyright (C) 2014 Nest Labs
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/iio/consumer.h>
#include <linux/iio/types.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/power_supply.h>
#include <linux/printk.h>
#include <linux/workqueue.h>
#define WORK_DELAY_SECS (10 * 60)
#define DCP_CURRENT_LIMIT 1500
#define REG1_READ_COUNT_MAX 10 /* Max loops reading reg1 */
#define REG1_ADDR 0x00
#define REG1_WD_EN 0x40
#define REG1_STATUS_MASK 0x30
#define REG1_STATUS_READY 0x00
#define REG1_STATUS_CHARGE_IN_PROGRESS 0x10
#define REG1_STATUS_CHARGE_DONE 0x20
#define REG1_STATUS_FAULT 0x30
#define REG1_FAULT_MASK 0x0f
#define REG1_FAULT_NORMAL 0x00
#define REG1_FAULT_INPUT_OVP 0x01
#define REG1_FAULT_INPUT_UVLO 0x02
#define REG1_FAULT_SLEEP 0x03
#define REG1_FAULT_BATTERY_TEMPERATURE_FAULT 0x04
#define REG1_FAULT_BATTERY_OVP 0x05
#define REG1_FAULT_THERMAL_SHUTDOWN 0x06
#define REG1_FAULT_TIMER_FAULT 0x07
#define REG1_FAULT_NO_BATTERY_CONNECTED 0x08
#define REG1_FAULT_ISET_SHORT 0x09
#define REG1_FAULT_INPUT_FAULT_AND_LDO_LOW 0x0a
#define REG2_ADDR 0x01
#define REG2_RESET_MASK 0x80
#define REG2_IIN_LIMIT_MASK 0x70
#define REG2_IIN_LIMIT_100 0x00
#define REG2_IIN_LIMIT_150 0x10
#define REG2_IIN_LIMIT_500 0x20
#define REG2_IIN_LIMIT_900 0x30
#define REG2_IIN_LIMIT_1500 0x40
#define REG2_IIN_LIMIT_2000 0x50
#define REG2_IIN_LIMIT_EXT 0x60
#define REG2_CE_MASK 0x02
#define REG2_HZ_MODE_MASK 0x01
#define REG3_ADDR 0x02
#define REG3_VBATREG_MASK 0xfc
#define REG3_VBATREG_MIN 3500000
#define REG3_VBATREG_MAX 4440000
#define REG3_VBATREG_STEP 20000
#define REG3_VBATREG_SHIFT 2
#define REG3_USB_DET_MASK 0x03
#define REG3_USB_DET_DCD 0x00
#define REG3_USB_DET_CDP 0x01
#define REG3_USB_DET_SDP 0x02
#define REG3_USB_DET_NON_STANDARD 0x03
#define REG4_ADDR 0x03
#define REG4_ICHG_MASK 0xf8
#define REG4_ICHG_MIN 500000
#define REG4_ICHG_MAX 2000000
#define REG4_ICHG_STEP 50000
#define REG4_ICHG_SHIFT 3
#define REG4_ITERM_MASK 0x07
#define REG4_ITERM_MIN 50000
#define REG4_ITERM_MAX 225000
#define REG4_ITERM_STEP 25000
#define REG4_ITERM_SHIFT 0
#define REG5_ADDR 0x04
#define REG5_LOW_CHG 0x20
#define REG5_VINDPM_MASK 0x07
#define REG5_VINDPM_MIN 4200000
#define REG5_VINDPM_MAX 4760000
#define REG5_VINDPM_STEP 80000
#define REG5_VINDPM_SHIFT 0
#define REG6_ADDR 0x05
#define REG6_SYSOFF_MASK 0x10
#define REG6_TMR_MASK 0x60
#define REG6_TMR_MIN 0
#define REG6_TMR_MAX 3
#define REG6_TMR_STEP 1
#define REG6_TMR_SHIFT 5
#define REG6_TS_EN_MASK 0x08
#define REG6_TS_STAT_MASK 0x07
#define REG6_TS_STAT_NORMAL 0x00
#define REG6_TS_STAT_TEMP_GT_THOT 0x01
#define REG6_TS_STAT_TWARM_LT_TEMP 0x02
#define REG6_TS_STAT_TCOLD_LT_TEMP 0x03
#define REG6_TS_STAT_TEMP_LT_TCOLD 0x04
#define REG6_TS_STAT_TFREEZE_LT_TEMP_LT_TCOLD 0x05
#define REG6_TS_STAT_TEMP_LT_TFREEZE 0x06
#define REG6_TS_STAT_TS_OPEN 0x07
#define REG7_ADDR 0x06
#define REG7_VOVP_MASK 0xe0
#define REG7_VOVP_VAL0 6000000
#define REG7_VOVP_VAL1 6500000
#define REG7_VOVP_VAL2 7000000
#define REG7_VOVP_VAL3 8000000
#define REG7_VOVP_VAL4 9000000
#define REG7_VOVP_VAL5 9500000
#define REG7_VOVP_VAL6 10000000
#define REG7_VOVP_VAL7 10500000
#define REG7_VOVP_SHIFT 5
#define LENGTH(a) (sizeof(a) / sizeof((a)[0]))
// Q macros where QsN (signed) and QuN (unsigned) have N fractional bits
#define Q(type, p, x) (type)((x) * ((type)1 << (p)))
#define Qs16(x) Q(int32_t, 16, (x))
// Convert down
#define Qdown(from, to, x) (((x) + (1 << (((from) - (to)) - 1))) >> ((from) - (to)))
#define Qint(from, x) Qdown((from), 0, (x))
// Convert up
#define Qup(from, to, x) ((x) << ((to) - (from)))
#define APPLY_LINEAR_CONFIG(client, string, reg, field) \
bq24251_apply_linear_config( \
client, \
string, \
reg##_ADDR, \
reg##_##field##_MASK, \
reg##_##field##_MIN, \
reg##_##field##_MAX, \
reg##_##field##_STEP, \
reg##_##field##_SHIFT)
struct bq24251_device {
struct mutex thread_mutex;
struct power_supply_desc bat_psd;
struct power_supply_config bat_psc;
struct power_supply *bat_ps;
struct i2c_client *client;
struct delayed_work work;
int stat_gpio;
int status;
int health;
bool online;
struct iio_channel *chan;
unsigned int r1_ohm;
unsigned int r2_ohm;
};
static enum power_supply_property bq24251_bat_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
POWER_SUPPLY_PROP_VOLTAGE_NOW, /* Keep this one last */
};
static const int vovp_vals[] = {
REG7_VOVP_VAL0,
REG7_VOVP_VAL1,
REG7_VOVP_VAL2,
REG7_VOVP_VAL3,
REG7_VOVP_VAL4,
REG7_VOVP_VAL5,
REG7_VOVP_VAL6,
REG7_VOVP_VAL7,
};
static struct workqueue_struct *bq24251_wq;
static int force_input_limit = 0;
module_param(force_input_limit, int, 0444);
MODULE_PARM_DESC(force_input_limit, "Force 2 A input current limit");
static int maximum_battery_regulation_mv = 4400;
static int bq24251_detect_battery(struct bq24251_device *bq);
static int bq24251_read_status(struct bq24251_device *bq);
static int bq24251_update_status(struct bq24251_device *bq);
static void bq24251_wq_function(struct work_struct *work)
{
struct bq24251_device *bq = container_of(work, struct bq24251_device, work.work);
mutex_lock(&bq->thread_mutex);
bq24251_detect_battery(bq);
mutex_unlock(&bq->thread_mutex);
power_supply_changed(bq->bat_ps);
return;
}
static ssize_t bq24251_show_bit(struct device *dev,
struct device_attribute *attr, char *buf,
uint8_t addr, uint8_t mask,
const char *err)
{
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
int reg_value;
reg_value = i2c_smbus_read_byte_data(client, addr);
if (reg_value < 0) {
dev_err(dev, err);
return -EIO;
}
return sprintf(buf, "%u\n", !(~reg_value & mask));
}
static ssize_t bq24251_store_bit(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count,
uint8_t addr, uint8_t mask,
const char *err)
{
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
struct bq24251_device *bq = i2c_get_clientdata(client);
int reg_value;
unsigned int value;
int ret;
ret = kstrtouint(buf, 10, &value);
if (ret)
return ret;
mutex_lock(&bq->thread_mutex);
reg_value = i2c_smbus_read_byte_data(client, addr);
if (reg_value < 0) {
dev_err(dev, err);
ret = reg_value;
} else {
/* Clear out the reset bit */
if (addr == REG2_ADDR)
reg_value &= ~REG2_RESET_MASK;
if (value)
reg_value |= mask;
else
reg_value &= ~mask;
ret = i2c_smbus_write_byte_data(client, addr, reg_value);
if (ret == 0)
ret = count;
}
mutex_unlock(&bq->thread_mutex);
return ret;
}
static ssize_t bq24251_sysoff_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return bq24251_show_bit(dev, attr, buf, REG6_ADDR, REG6_SYSOFF_MASK, "Couldn't read Register 6\n");
}
static ssize_t bq24251_sysoff_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
return bq24251_store_bit(dev, attr, buf, count, REG6_ADDR, REG6_SYSOFF_MASK, "Couldn't read Register 6\n");
}
static ssize_t bq24251_ce_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return bq24251_show_bit(dev, attr, buf, REG2_ADDR, REG2_CE_MASK, "Couldn't read Register 2\n");
}
static ssize_t bq24251_ce_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
return bq24251_store_bit(dev, attr, buf, count, REG2_ADDR, REG2_CE_MASK, "Couldn't read Register 2\n");
}
static ssize_t bq24251_hz_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return bq24251_show_bit(dev, attr, buf, REG2_ADDR, REG2_HZ_MODE_MASK, "Couldn't read Register 2\n");
}
static ssize_t bq24251_hz_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
return bq24251_store_bit(dev, attr, buf, count, REG2_ADDR, REG2_HZ_MODE_MASK, "Couldn't read Register 2\n");
}
static ssize_t bq24251_show_num(struct device *dev,
struct device_attribute *attr, char *buf,
uint8_t addr, uint8_t mask, unsigned int shift,
int32_t scale, /* Qs16 fixed point */
int32_t offset, /* in scaled units */
const char *err)
{
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
int reg_value;
int32_t scaled_value, raw_value;
reg_value = i2c_smbus_read_byte_data(client, addr);
if (reg_value < 0) {
dev_err(dev, err);
return -EIO;
}
raw_value = (reg_value & mask) >> shift;
scaled_value = Qint(16, (int64_t)raw_value * scale) + offset;
return sprintf(buf, "%d (%.2x)\n", scaled_value, raw_value);
}
static ssize_t bq24251_store_num(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count,
uint8_t addr, uint8_t mask, unsigned int shift,
int32_t recip_scale, /* Qs16 fixed point */
int32_t offset, int32_t min, int32_t max, /* in scaled units */
const char *err)
{
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
struct bq24251_device *bq = i2c_get_clientdata(client);
int reg_value;
int32_t scaled_value, raw_value;
int ret;
ret = kstrtos32(buf, 10, &scaled_value);
if (ret) {
return ret;
}
mutex_lock(&bq->thread_mutex);
reg_value = i2c_smbus_read_byte_data(client, addr);
if (reg_value < 0) {
dev_err(dev, err);
ret = reg_value;
} else {
reg_value &= ~mask;
scaled_value = clamp(scaled_value, min, max) - offset;
raw_value = Qint(16, (int64_t)scaled_value * recip_scale);
reg_value |= (raw_value << shift) & mask;
ret = i2c_smbus_write_byte_data(client, addr, reg_value);
if (ret == 0)
ret = count;
}
mutex_unlock(&bq->thread_mutex);
return ret;
}
static ssize_t bq24251_vbatreg_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return bq24251_show_num(dev, attr, buf,
REG3_ADDR, REG3_VBATREG_MASK, REG3_VBATREG_SHIFT,
Qs16(20) /* 20 mV/LSB */,
REG3_VBATREG_MIN / 1000,
"Couldn't read Register 3\n");
}
static ssize_t bq24251_vbatreg_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
return bq24251_store_num(dev, attr, buf, count,
REG3_ADDR, REG3_VBATREG_MASK, REG3_VBATREG_SHIFT,
Qs16(0.05) /* 0.05 LSB/mV */,
REG3_VBATREG_MIN / 1000, REG3_VBATREG_MIN / 1000, maximum_battery_regulation_mv,
"Couldn't read Register 3\n");
}
static ssize_t bq24251_r1_ohms_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
struct bq24251_device *bq = i2c_get_clientdata(client);
return snprintf(buf, PAGE_SIZE, "%u\n", bq->r1_ohm);
}
static ssize_t bq24251_r2_ohms_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
struct bq24251_device *bq = i2c_get_clientdata(client);
return snprintf(buf, PAGE_SIZE, "%u\n", bq->r2_ohm);
}
/*
* Battery Detection Dance Steps AKA the BQ Twist
*
* +-----------------------------+
* |Is CE disabled? If so, enable|
* +-----------------------------+
* |
* +--------------------------------------------------------+
* |Is there a temp fault that masks a disconnected battery?|
* +--------------------------------------------------------+
* | |
* | YES | NO
* | |
* +------------------------+ +-----------+
* |Disable TS and check for| |Some other |
* |disconnected fault | |temp fault?|
* +------------------------+ +-----------+
* | | | \
* Disconnected | | Connected | \ NO
* +--------------+ | | \
* | Disable CE to| | | +------+
* | stop cycling | | | | Exit |
* +--------------+ | | YES +------+
* \ | |
* \ | |
* \ +------------+ |
* \|Re-enable TS|\ |
* +------------+ \ |
* \ |
* \ |
* +-----------------------------------+
* |Re-schedule work, as a change in |
* |battery connection will not trigger|
* |an interrupt |
* +-----------------------------------+
*/
/* Call while holding thread_mutex */
static int bq24251_detect_battery(struct bq24251_device *bq)
{
int ret;
int reg1_value;
int reg2_value;
int reg6_value;
reg2_value = i2c_smbus_read_byte_data(bq->client, REG2_ADDR);
if (reg2_value < 0) {
dev_err(&bq->client->dev, "Couldn't read Register 2\n");
return reg2_value;
}
if (reg2_value & REG2_CE_MASK) {
/* Charging disabled, re-enable */
reg2_value &= ~(REG2_CE_MASK | REG2_RESET_MASK);
ret = i2c_smbus_write_byte_data(bq->client, REG2_ADDR, reg2_value);
if (ret < 0) {
dev_err(&bq->client->dev, "Couldn't write Register 2\n");
return ret;
}
msleep(32); /* deglitch */
}
/* If temperature sensing is enabled, a disconnected battery
* will appear as fault 100 - TS temp < T_COLD, so clear TS_EN
* to check for the disconnected fault */
reg6_value = i2c_smbus_read_byte_data(bq->client, REG6_ADDR);
if ((reg6_value & REG6_TS_STAT_MASK) == REG6_TS_STAT_TEMP_LT_TCOLD) {
reg6_value &= ~REG6_TS_EN_MASK;
ret = i2c_smbus_write_byte_data(bq->client, REG6_ADDR, reg6_value);
if (ret < 0) {
dev_err(&bq->client->dev, "Couldn't write Register 6\n");
return ret;
}
msleep(250); /* deglitch + battery detection times */
/* Check for disconnected fault */
reg1_value = bq24251_read_status(bq);
if ((reg1_value & REG1_FAULT_MASK) == REG1_FAULT_NO_BATTERY_CONNECTED) {
/* A battery disconnected fault was masked. Disable charging to
* stop SYS cycling. */
reg2_value = i2c_smbus_read_byte_data(bq->client, REG2_ADDR);
if (reg2_value < 0) {
dev_err(&bq->client->dev, "Couldn't read Register 2\n");
return reg2_value;
}
reg2_value |= REG2_CE_MASK;
reg2_value &= ~REG2_RESET_MASK;
ret = i2c_smbus_write_byte_data(bq->client, REG2_ADDR, reg2_value);
if (ret < 0) {
dev_err(&bq->client->dev, "Couldn't write Register 2\n");
return ret;
}
bq->online = false;
} else {
/* No battery disconnected fault detected - a true temp fault */
bq->health = POWER_SUPPLY_HEALTH_OVERHEAT;
bq->online = true;
}
/* Re-enable temperature sensing */
reg6_value = i2c_smbus_read_byte_data(bq->client, REG6_ADDR);
reg6_value |= REG6_TS_EN_MASK;
ret = i2c_smbus_write_byte_data(bq->client, REG6_ADDR, reg6_value);
if (ret < 0) {
dev_err(&bq->client->dev, "Couldn't write Register 6\n");
return ret;
}
msleep(32); /* deglitch */
queue_delayed_work(bq24251_wq, &bq->work, msecs_to_jiffies(WORK_DELAY_SECS * MSEC_PER_SEC));
} else if (reg6_value & REG6_TS_STAT_MASK) {
bq->health = POWER_SUPPLY_HEALTH_OVERHEAT;
bq->online = true;
queue_delayed_work(bq24251_wq, &bq->work, msecs_to_jiffies(WORK_DELAY_SECS * MSEC_PER_SEC));
} else {
bq->online = true;
bq24251_update_status(bq);
}
return 0;
}
static int bq24251_bat_get_property(struct power_supply *bat_ps,
enum power_supply_property prop,
union power_supply_propval *val)
{
struct bq24251_device *bq = container_of(bat_ps->desc, struct
bq24251_device, bat_psd);
int ret = 0;
switch (prop) {
case POWER_SUPPLY_PROP_STATUS:
val->intval = bq->status;
break;
case POWER_SUPPLY_PROP_HEALTH:
val->intval = bq->health;
break;
case POWER_SUPPLY_PROP_PRESENT:
/* Always present */
val->intval = 1;
break;
case POWER_SUPPLY_PROP_ONLINE:
val->intval = bq->online;
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
/* Only support for Lithium Ion */
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
BUG_ON(!bq->chan);
mutex_lock(&bq->thread_mutex);
if (ret >= 0) {
/* Read ADC voltage and compensate for the divider */
ret = iio_read_channel_processed(bq->chan, &val->intval);
val->intval *= bq->r1_ohm + bq->r2_ohm;
val->intval /= bq->r2_ohm;
/* Processed IIO returns mV but power supply returns uV */
val->intval *= 1000;
}
mutex_unlock(&bq->thread_mutex);
break;
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = "bq24251";
break;
case POWER_SUPPLY_PROP_MANUFACTURER:
val->strval = "TI";
break;
default:
ret = -EINVAL;
}
return ret;
}
static int bq24251_read_status(struct bq24251_device *bq)
{
int reg1_read_count = 0;
int reg1_value = -1;
int reg1_value_old;
/* Read Register 1 until status is stable */
do {
reg1_value_old = reg1_value;
reg1_value = i2c_smbus_read_byte_data(bq->client, REG1_ADDR);
if (reg1_value < 0) {
dev_err(&bq->client->dev, "Couldn't read Register 1\n");
return reg1_value;
}
dev_info(&bq->client->dev, "Register 1: 0x%02X\n", reg1_value);
reg1_read_count++;
} while (reg1_value != reg1_value_old &&
reg1_read_count < REG1_READ_COUNT_MAX);
return reg1_value;
}
/* Call while holding thread_mutex */
static int bq24251_update_status(struct bq24251_device *bq)
{
int reg1_value;
reg1_value = bq24251_read_status(bq);
if (reg1_value < 0)
return reg1_value;
/* Set Battery Status */
switch (reg1_value & REG1_STATUS_MASK) {
case REG1_STATUS_READY:
bq->status = POWER_SUPPLY_STATUS_DISCHARGING;
break;
case REG1_STATUS_CHARGE_IN_PROGRESS:
bq->status = POWER_SUPPLY_STATUS_CHARGING;
break;
case REG1_STATUS_CHARGE_DONE:
bq->status = POWER_SUPPLY_STATUS_FULL;
break;
default:
bq->status = POWER_SUPPLY_STATUS_DISCHARGING;
break;
}
/* Set Battery Health */
switch (reg1_value & REG1_FAULT_MASK) {
case REG1_FAULT_NORMAL:
case REG1_FAULT_INPUT_UVLO:
case REG1_FAULT_INPUT_FAULT_AND_LDO_LOW:
bq->health = POWER_SUPPLY_HEALTH_GOOD;
break;
case REG1_FAULT_INPUT_OVP:
case REG1_FAULT_BATTERY_OVP:
bq->health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
break;
case REG1_FAULT_BATTERY_TEMPERATURE_FAULT:
#ifdef CONFIG_CHARGER_BQ24251_BATTERY_DISCONNECT_DETECTION
/* Distinguishing temperature faults from the disconnected
* battery fault requires a particular set of dance steps that
* triggers activity on the STAT pin, leading to more
* interrupts. Schedule the dance one second in the future to
* avoid an interrupt loop. If necessary the scheduled work
* will reschedule itself with a longer delay. */
if (!delayed_work_pending(&bq->work))
queue_delayed_work(bq24251_wq, &bq->work, msecs_to_jiffies(1 * MSEC_PER_SEC));
#endif
bq->health = POWER_SUPPLY_HEALTH_OVERHEAT;
break;
case REG1_FAULT_THERMAL_SHUTDOWN:
bq->health = POWER_SUPPLY_HEALTH_OVERHEAT;
break;
default:
bq->health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
break;
}
#ifdef CONFIG_CHARGER_BQ24251_BATTERY_DISCONNECT_DETECTION
if (!delayed_work_pending(&bq->work))
bq->online = true;
#endif
return 0;
}
static irqreturn_t bq24251_irq_handler(int irq, void *devid)
{
struct i2c_client *client = devid;
struct bq24251_device *bq = i2c_get_clientdata(client);
mutex_lock(&bq->thread_mutex);
bq24251_update_status(bq);
mutex_unlock(&bq->thread_mutex);
pm_wakeup_event(&client->dev, 0);
power_supply_changed(bq->bat_ps);
return IRQ_HANDLED;
}
static int bq24251_apply_linear_config(struct i2c_client *client,
const char *string,
unsigned int reg,
unsigned int mask,
unsigned int min,
unsigned int max,
unsigned int step,
unsigned int shift)
{
struct device_node *np = client->dev.of_node;
unsigned int prop;
int reg_value;
int ret;
ret = of_property_read_u32(np, string, &prop);
if (ret == 0) {
if ((prop < min) | (prop > max)) {
dev_err(&client->dev, "Invalid %s property\n", string);
return -EINVAL;
}
reg_value = i2c_smbus_read_byte_data(client, reg);
if (reg_value < 0)
return reg_value;
reg_value &= ~mask;
reg_value |= (((prop - min) / step) << shift) & mask;
ret = i2c_smbus_write_byte_data(client, reg, reg_value);
return ret;
}
return 0;
}
static int bq24251_parse_configuration(struct i2c_client *client)
{
struct device_node *np = client->dev.of_node;
unsigned int prop;
int reg_value;
int ret;
int i;
/* Disable watchdog so software written parameters take effect */
reg_value = i2c_smbus_read_byte_data(client, REG1_ADDR);
if (reg_value < 0)
return reg_value;
reg_value &= ~REG1_WD_EN;
ret = i2c_smbus_write_byte_data(client, REG1_ADDR, reg_value);
ret = APPLY_LINEAR_CONFIG(client, "battery-regulation-voltage", REG3, VBATREG);
if (ret)
return ret;
ret = of_property_read_u32(np, "max-battery-regulation-voltage", &prop);
if (ret)
return ret;
maximum_battery_regulation_mv = (int)clamp_t(unsigned int, prop, REG3_VBATREG_MIN, REG3_VBATREG_MAX) / 1000;
ret = APPLY_LINEAR_CONFIG(client, "term-current-sense-threshold", REG4, ITERM);
if (ret)
return ret;
ret = APPLY_LINEAR_CONFIG(client, "charge-current", REG4, ICHG);
if (ret)
return ret;
ret = APPLY_LINEAR_CONFIG(client, "vindpm-threshold", REG5, VINDPM);
if (ret)
return ret;
ret = APPLY_LINEAR_CONFIG(client, "safety-timer-time-limit", REG6, TMR);
if (ret)
return ret;
ret = of_property_read_u32(np, "ovp-voltage", &prop);
if (ret == 0) {
for (i = 0; i < ARRAY_SIZE(vovp_vals); i++)
if (prop == vovp_vals[i]) {
reg_value = i2c_smbus_read_byte_data(client, REG7_ADDR);
if (reg_value < 0)
return reg_value;
reg_value &= ~REG7_VOVP_MASK;
reg_value |= i << REG7_VOVP_SHIFT;
ret = i2c_smbus_write_byte_data(client, REG7_ADDR, reg_value);
return ret;
}
dev_err(&client->dev, "Invalid ovp-voltage property\n");
return -EINVAL;
}
return 0;
}
static int bq24251_bat_get_ext_psy_data(struct device *dev, void *data)
{
struct power_supply *ext_ps = dev_get_drvdata(dev);
struct bq24251_device *bq = (struct bq24251_device *) data;
union power_supply_propval val;
int i, found = 0;
int reg2_value;
int ret;
/* See if bq->bat_psd is a supplicant of ext_ps */
if (!ext_ps->desc->name)
return 0;
for (i = 0; i < bq->bat_ps->num_supplies; i++)
if (!strcmp(ext_ps->desc->name, bq->bat_ps->supplied_from[i]))
found = 1;
if (!found)
return 0;
/* Get the latest max current property */
if (ext_ps->desc->get_property(ext_ps, POWER_SUPPLY_PROP_CURRENT_MAX, &val))
return 0;
dev_info(&bq->client->dev, "Supply limits current to %d mA\n", val.intval);
mutex_lock(&bq->thread_mutex);
/* Read Register 2 */
reg2_value = i2c_smbus_read_byte_data(bq->client, REG2_ADDR);
if (reg2_value < 0) {
dev_err(&bq->client->dev, "Couldn't read Register 2\n");
mutex_unlock(&bq->thread_mutex);
return reg2_value;
}
/* Apply current limit based on what type of charger */
reg2_value &= ~(REG2_RESET_MASK | REG2_IIN_LIMIT_MASK);
#ifdef CONFIG_CHARGER_BQ24251_FORCE_2A
reg2_value |= REG2_IIN_LIMIT_2000;
#else
if (val.intval == DCP_CURRENT_LIMIT || force_input_limit)
reg2_value |= REG2_IIN_LIMIT_2000; /* Our DCP charger will supply 2A */
else
reg2_value |= REG2_IIN_LIMIT_500; /* Otherwise use post-enumeration SDP limit */
#endif
ret = i2c_smbus_write_byte_data(bq->client, REG2_ADDR, reg2_value);
if (ret < 0)
dev_err(&bq->client->dev, "Couldn't write Register 2\n");
mutex_unlock(&bq->thread_mutex);
return ret;
}
static void bq24251_bat_ext_changed(struct power_supply *bat_ps)
{
struct bq24251_device *bq = container_of(bat_ps->desc, struct
bq24251_device, bat_psd);
class_for_each_device(power_supply_class, NULL, bq,
bq24251_bat_get_ext_psy_data);
}
static DEVICE_ATTR(sysoff, S_IRUSR | S_IWUSR, bq24251_sysoff_show, bq24251_sysoff_store);
static DEVICE_ATTR(charging_enabled, S_IRUSR | S_IWUSR, bq24251_ce_show, bq24251_ce_store);
static DEVICE_ATTR(high_impedance_mode_enabled, S_IRUSR | S_IWUSR, bq24251_hz_mode_show, bq24251_hz_mode_store);
static DEVICE_ATTR(battery_regulation_voltage, S_IRUSR | S_IWUSR, bq24251_vbatreg_show, bq24251_vbatreg_store);
static DEVICE_ATTR(r1_ohms, S_IRUSR, bq24251_r1_ohms_show, NULL);
static DEVICE_ATTR(r2_ohms, S_IRUSR, bq24251_r2_ohms_show, NULL);
static struct attribute *bq24251_attrs[] = {
&dev_attr_sysoff.attr,
&dev_attr_charging_enabled.attr,
&dev_attr_high_impedance_mode_enabled.attr,
&dev_attr_battery_regulation_voltage.attr,
&dev_attr_r1_ohms.attr,
&dev_attr_r2_ohms.attr,
NULL,
};
static const struct attribute_group bq24251_attr_group = {
.attrs = bq24251_attrs,
};
#ifdef CONFIG_CHARGER_BQ24251_POWER_OFF
struct device *power_off_dev;
static void bq24251_power_off(void)
{
bq24251_sysoff_store(power_off_dev, NULL, "1", 1);
mdelay(100);
bq24251_sysoff_store(power_off_dev, NULL, "0", 1);
}
#endif
static int bq24251_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct bq24251_device *bq;
struct device_node *np;
int ret;
/* Check for OF node */
np = client->dev.of_node;
if (!np) {
dev_err(&client->dev, "Missing DT node\n");
return -ENODEV;
}
/* Claim memory */
bq = devm_kzalloc(&client->dev, sizeof(*bq), GFP_KERNEL);
if (!bq)
return -ENOMEM;
i2c_set_clientdata(client, bq);
bq->client = client;
/* Parse configuration constraints */
ret = bq24251_parse_configuration(client);
if (ret)
return ret;
bq->bat_psd.name = "bq24251-battery";
bq->bat_psd.type = POWER_SUPPLY_TYPE_BATTERY;
bq->bat_psd.get_property = bq24251_bat_get_property;
bq->bat_psd.properties = bq24251_bat_props;
bq->bat_psd.num_properties = ARRAY_SIZE(bq24251_bat_props);
bq->bat_psd.external_power_changed = bq24251_bat_ext_changed;
bq->bat_psc.of_node = client->dev.of_node;
mutex_init(&bq->thread_mutex);
device_init_wakeup(&client->dev, 1);
/* Get STAT gpio and interrupt */
bq->stat_gpio = of_get_named_gpio(np, "stat-gpio", 0);
if (!gpio_is_valid(bq->stat_gpio)) {
dev_err(&client->dev, "DT missing STAT gpio property\n");
return -ENODEV;
}
ret = devm_gpio_request(&client->dev, bq->stat_gpio, "bq24251-stat");
if (ret) {
dev_err(&client->dev, "Couldn't request STAT gpio\n");
return ret;
}
ret = gpio_direction_input(bq->stat_gpio);
if (ret) {
dev_err(&client->dev, "Couldn't set STAT gpio direction\n");
return ret;
}
mutex_lock(&bq->thread_mutex);
ret = devm_request_threaded_irq(&client->dev,
gpio_to_irq(bq->stat_gpio), NULL,
bq24251_irq_handler,
IRQF_ONESHOT | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
"bq24251-stat", client);
if (ret) {
dev_err(&client->dev, "Couldn't request irq for STAT gpio\n");
goto probe_err_mutex_unlock;
}
/* Get optional ADC and voltage divider resistor values */
bq->chan = iio_channel_get(&client->dev, NULL);
if (!IS_ERR(bq->chan)) {
ret = of_property_read_u32(np, "divider-r1-ohm", &bq->r1_ohm);
if (ret) {
dev_err(&client->dev, "Couldn't get r1 property from DT\n");
goto probe_err_iio_release;
}
ret = of_property_read_u32(np, "divider-r2-ohm", &bq->r2_ohm);
if (ret) {
dev_err(&client->dev, "Couldn't get r2 property from DT\n");
goto probe_err_iio_release;
}
if (!bq->r1_ohm || !bq->r2_ohm) {
dev_err(&client->dev, "Divider values must be non-zero\n");
ret = -EINVAL;
goto probe_err_iio_release;
}
} else if (PTR_ERR(bq->chan) == -EPROBE_DEFER) {
/* If ADC is specified but not available, defer */
return -EPROBE_DEFER;
} else {
dev_info(&client->dev, "No associated ADC\n");
/* Omit support for POWER_SUPPLY_PROP_VOLTAGE_NOW if no ADC */
bq->bat_psd.num_properties--;
bq->chan = 0;
}
/* Add sysfs files */
ret = sysfs_create_group(&client->dev.kobj, &bq24251_attr_group);
if (ret) {
dev_err(&client->dev, "Failed to create sysfs files\n");
goto probe_err_mutex_unlock;
}
#ifdef CONFIG_CHARGER_BQ24251_BATTERY_DISCONNECT_DETECTION
bq24251_wq = create_workqueue("bq24251_wq");
if (bq24251_wq) {
INIT_DELAYED_WORK(&bq->work, bq24251_wq_function);
} else {
dev_err(&client->dev, "Failed to create workqueue\n");
goto probe_err_sysfs_destroy;
}
#else
bq->online = true;
#endif
bq24251_update_status(bq);
/* Register as power supply */
bq->bat_ps = power_supply_register(&client->dev, &bq->bat_psd,
&bq->bat_psc);
if (IS_ERR(bq->bat_ps)) {
ret = PTR_ERR(bq->bat_ps);
bq->bat_ps = NULL;
goto probe_err_workqueue_destroy;
}
mutex_unlock(&bq->thread_mutex);
/* Simulate external PS change to set input current */
bq24251_bat_ext_changed(bq->bat_ps);
/* Allow the device to wake us up */
device_init_wakeup(&client->dev, 1);
#ifdef CONFIG_CHARGER_BQ24251_POWER_OFF
/* Register a power off hook */
if (pm_power_off != bq24251_power_off) {
power_off_dev = &client->dev;
pm_power_off = bq24251_power_off;
}
#endif
return 0;
probe_err_workqueue_destroy:
#ifdef CONFIG_CHARGER_BQ24251_BATTERY_DISCONNECT_DETECTION
cancel_delayed_work_sync(&bq->work);
destroy_workqueue(bq24251_wq);
probe_err_sysfs_destroy:
#endif
sysfs_remove_group(&client->dev.kobj, &bq24251_attr_group);
probe_err_mutex_unlock:
mutex_unlock(&bq->thread_mutex);
probe_err_iio_release:
iio_channel_release(bq->chan);
return ret;
}
static int bq24251_remove(struct i2c_client *client)
{
struct bq24251_device *bq = i2c_get_clientdata(client);
#ifdef CONFIG_CHARGER_BQ24251_BATTERY_DISCONNECT_DETECTION
cancel_delayed_work_sync(&bq->work);
destroy_workqueue(bq24251_wq);
#endif
sysfs_remove_group(&client->dev.kobj, &bq24251_attr_group);
power_supply_unregister(bq->bat_ps);
if (bq->chan)
iio_channel_release(bq->chan);
device_init_wakeup(&client->dev, 0);
return 0;
}
static int bq24251_suspend(struct device *dev)
{
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
struct bq24251_device *bq = i2c_get_clientdata(client);
if (device_may_wakeup(&client->dev))
enable_irq_wake(gpio_to_irq(bq->stat_gpio));
return 0;
}
static int bq24251_resume(struct device *dev)
{
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
struct bq24251_device *bq = i2c_get_clientdata(client);
if (device_may_wakeup(&client->dev))
disable_irq_wake(gpio_to_irq(bq->stat_gpio));
return 0;
}
static const struct i2c_device_id bq24251_id[] = {
{ "bq24251-charger", 0 },
{ }
};
static const struct of_device_id bq24251_match[] = {
{ .compatible = "ti,bq24251-charger", },
{ },
};
static SIMPLE_DEV_PM_OPS(bq24251_pm_ops, bq24251_suspend, bq24251_resume);
static struct i2c_driver bq24251_driver = {
.probe = bq24251_probe,
.remove = bq24251_remove,
.id_table = bq24251_id,
.driver = {
.name = "bq24251-charger",
.of_match_table = bq24251_match,
.pm = &bq24251_pm_ops,
},
};
module_i2c_driver(bq24251_driver);
MODULE_AUTHOR("Tim Kryger <tkryger@nestlabs.com>");
MODULE_DESCRIPTION("bq24251 charger driver");
MODULE_LICENSE("GPL v2");