blob: 5de8364be9059bf92e7ffce65957a355febc1204 [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>
#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 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 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 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 bat_ps;
struct i2c_client *client;
int stat_gpio;
int status;
int health;
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 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 ssize_t bq24251_sysoff_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
unsigned long sysoff;
int reg6_value;
reg6_value = i2c_smbus_read_byte_data(client, REG6_ADDR);
if (reg6_value < 0) {
dev_err(dev, "Couldn't read Register 6\n");
return -EIO;
}
sysoff = (reg6_value & REG6_SYSOFF_MASK) ? 1 : 0;
return sprintf(buf, "%lu\n", sysoff);
}
static ssize_t bq24251_sysoff_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
struct bq24251_device *bq = i2c_get_clientdata(client);
unsigned long sysoff;
int reg6_value;
int ret;
ret = strict_strtoul(buf, 10, &sysoff);
if (ret)
return ret;
mutex_lock(&bq->thread_mutex);
reg6_value = i2c_smbus_read_byte_data(client, REG6_ADDR);
if (reg6_value < 0) {
dev_err(dev, "Couldn't read Register 6\n");
ret = reg6_value;
} else {
if (sysoff)
reg6_value |= REG6_SYSOFF_MASK;
else
reg6_value &= ~REG6_SYSOFF_MASK;
ret = i2c_smbus_write_byte_data(client, REG6_ADDR, reg6_value);
if (ret == 0)
ret = count;
}
mutex_unlock(&bq->thread_mutex);
return ret;
}
static ssize_t bq24251_ce_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
unsigned long ce;
int reg2_value;
reg2_value = i2c_smbus_read_byte_data(client, REG2_ADDR);
if (reg2_value < 0) {
dev_err(dev, "Couldn't read Register 2\n");
return -EIO;
}
ce = (reg2_value & REG2_CE_MASK) ? 0 : 1;
return sprintf(buf, "%lu\n", ce);
}
static ssize_t bq24251_ce_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
struct bq24251_device *bq = i2c_get_clientdata(client);
unsigned long ce;
int reg2_value;
int ret;
ret = strict_strtoul(buf, 10, &ce);
if (ret)
return ret;
mutex_lock(&bq->thread_mutex);
reg2_value = i2c_smbus_read_byte_data(client, REG2_ADDR);
if (reg2_value < 0) {
dev_err(dev, "Couldn't read Register 2\n");
ret = reg2_value;
} else {
/* Clear out the reset bit */
reg2_value &= ~REG2_RESET_MASK;
if (ce)
reg2_value &= ~REG2_CE_MASK;
else
reg2_value |= REG2_CE_MASK;
ret = i2c_smbus_write_byte_data(client, REG2_ADDR, reg2_value);
if (ret == 0)
ret = count;
}
mutex_unlock(&bq->thread_mutex);
return ret;
}
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, struct bq24251_device, bat_ps);
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:
/* Always online */
val->intval = 1;
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_update_status(struct bq24251_device *bq)
{
int reg1_value;
int reg1_read_count = 0;
/* Read Register 1 until fault bits match status bits */
do {
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_dbg(&bq->client->dev, "Register 1: 0x%02X\n", reg1_value);
reg1_read_count++;
} while ((reg1_value & REG1_STATUS_MASK) != REG1_STATUS_FAULT &&
(reg1_value & REG1_FAULT_MASK) != REG1_FAULT_NORMAL &&
reg1_read_count < REG1_READ_COUNT_MAX);
/* 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:
case REG1_FAULT_THERMAL_SHUTDOWN:
bq->health = POWER_SUPPLY_HEALTH_OVERHEAT;
break;
default:
bq->health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
break;
}
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 = 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 = 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_ps is a supplicant of ext_ps */
if (!ext_ps->name)
return 0;
for (i = 0; i < bq->bat_ps.num_supplies; i++)
if (!strcmp(ext_ps->name, bq->bat_ps.supplied_from[i]))
found = 1;
if (!found)
return 0;
/* Get the latest max current property */
if (ext_ps->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, struct bq24251_device, bat_ps);
class_for_each_device(power_supply_class, NULL, bq, bq24251_bat_get_ext_psy_data);
}
static struct device_attribute bq24251_sysoff_attr = {
.attr = {
.name = "sysoff",
.mode = S_IRUSR | S_IWUSR,
},
.show = bq24251_sysoff_show,
.store = bq24251_sysoff_store,
};
static struct device_attribute bq24251_ce_attr = {
.attr = {
.name = "charging_enabled",
.mode = S_IRUSR | S_IWUSR,
},
.show = bq24251_ce_show,
.store = bq24251_ce_store,
};
#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_ps.name = "bq24251-battery";
bq->bat_ps.type = POWER_SUPPLY_TYPE_BATTERY;
bq->bat_ps.get_property = bq24251_bat_get_property;
bq->bat_ps.properties = bq24251_bat_props;
bq->bat_ps.num_properties = ARRAY_SIZE(bq24251_bat_props);
bq->bat_ps.external_power_changed = bq24251_bat_ext_changed;
bq->bat_ps.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_ps.num_properties--;
bq->chan = 0;
}
/* Add sysfs files */
ret = device_create_file(&client->dev, &bq24251_sysoff_attr);
if (ret) {
dev_err(&client->dev, "Failed to create sysoff sysfs file\n");
goto probe_err_mutex_unlock;
}
ret = device_create_file(&client->dev, &bq24251_ce_attr);
if (ret) {
dev_err(&client->dev, "Failed to create ce sysfs file\n");
goto probe_err_sysfs_destroy;
}
bq24251_update_status(bq);
mutex_unlock(&bq->thread_mutex);
/* Register as power supply */
power_supply_register(&client->dev, &bq->bat_ps);
/* 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_sysfs_destroy:
device_remove_file(&client->dev, &bq24251_sysoff_attr);
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);
device_remove_file(&client->dev, &bq24251_ce_attr);
device_remove_file(&client->dev, &bq24251_sysoff_attr);
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");