M7350/kernel/drivers/power/bq25601-charger.c
2024-09-09 08:59:52 +00:00

1021 lines
23 KiB
C
Executable File

/*******************************************************************************
Copyright (C), 2020, TP-LINK TECHNOLOGIES CO., LTD.
File name : bq25601-charger.c
Description : Driver for charge ic bq25601.
Author : wuchao(w8189)
History:
------------------------------
V1.0, 2020-07-01, wuchao create file.
*******************************************************************************/
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/power_supply.h>
#include <linux/slab.h>
#include <linux/of_gpio.h>
#include <linux/qpnp/qpnp-adc.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#undef BQ25601_DEBUG
#undef dev_dbg
#ifdef BQ25601_DEBUG
#define dev_dbg(dev, format, arg...) dev_printk(KERN_INFO, dev, format, ##arg)
#else
#define dev_dbg(dev, format, arg...)
#endif
//define addrflag
int addrflag = 1;
int part_id;
// current is defined as a macro in current.h
/* Correspondence table of voltage and temperature */
static const int adc_map_temp[][2] =
{
{1800, -100},
{1712, -40},
{1654, -30},
{1570, -20},
{1517, -15},
{1457, -10},
{1390, -5},
{1316, 0},
{1238, 5},
{1155, 10},
{1070, 15},
{985, 20},
{900, 25},
{817, 30},
{738, 35},
{663, 40},
{593, 45},
{529, 50},
{470, 55},
{417, 60},
{370, 65},
{291, 75},
{160, 100},
{91, 125},
{0, 1000},
};
struct bq25601_charger {
struct i2c_client *client;
struct device *dev;
struct power_supply *usb_psy;
struct power_supply batt_psy;
struct qpnp_vadc_chip *vadc_dev;
struct work_struct usb_detect_work;
struct mutex lock;
struct mutex irq_lock;
struct mutex read_write_lock;
u32 irq_gpio;
u32 charge_gpio;
int usb_status;
int vbat_div;
};
static enum power_supply_property bq25601_power_properties[] = {
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_CURRENT_MAX,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
};
/* i2c read & write */
static int __bq25601_read_reg(struct bq25601_charger *chip, u8 reg, u8 *val)
{
s32 ret;
ret = i2c_smbus_read_byte_data(chip->client, reg);
if (ret < 0)
{
dev_err(chip->dev, "i2c read fail: can't read from %02x: %d\n", reg, ret);
return ret;
}
else
{
*val = ret;
}
dev_dbg(chip->dev, "%s: get reg(0x%02x) value 0x%02x\n", __func__, reg, *val);
return 0;
}
static int __bq25601_write_reg(struct bq25601_charger *chip, int reg, u8 val)
{
s32 ret;
ret = i2c_smbus_write_byte_data(chip->client, reg, val);
if (ret < 0)
{
dev_err(chip->dev, "i2c write fail: can't write %02x to %02x: %d\n", val, reg, ret);
return ret;
}
dev_dbg(chip->dev, "%s: set reg(0x%02x) value 0x%02x\n", __func__, reg, val);
return 0;
}
static int bq25601_read_reg(struct bq25601_charger *chip, int reg, u8 *val)
{
int rc;
mutex_lock(&chip->read_write_lock);
rc = __bq25601_read_reg(chip, reg, val);
mutex_unlock(&chip->read_write_lock);
return rc;
}
static int bq25601_masked_write(struct bq25601_charger *chip, int reg, u8 mask, u8 val, u8 shift)
{
s32 rc;
u8 temp;
mutex_lock(&chip->read_write_lock);
rc = __bq25601_read_reg(chip, reg, &temp);
if (rc)
{
goto out;
}
mask <<= shift;
val <<= shift;
temp &= ~mask;
temp |= val & mask;
rc = __bq25601_write_reg(chip, reg, temp);
out:
mutex_unlock(&chip->read_write_lock);
return rc;
}
/* set usb charging current */
#define USB_CHARGE_CURRENT_CTRL_REG 0x0
#define USB_CHARGE_CURRENT_MASK 0x1F
#define USB_CHARGE_CURRENT_SHIFT 0
// to do... we can use a better way
#define USB_CHARGE_CURRENT_VAL1000 0x9
static int bq25601_set_usb_charge_current(struct bq25601_charger *chip, unsigned int charge_current)
{
u8 val = USB_CHARGE_CURRENT_VAL1000;
dev_dbg(chip->dev, "%s: set usb charge current %dmA\n", __func__, charge_current);
switch (charge_current)
{
case 1000:
val = USB_CHARGE_CURRENT_VAL1000;
break;
default:
val = USB_CHARGE_CURRENT_VAL1000;
break;
}
return bq25601_masked_write(chip, USB_CHARGE_CURRENT_CTRL_REG,
USB_CHARGE_CURRENT_MASK, val, USB_CHARGE_CURRENT_SHIFT);
}
/* set & get battery charging current */
#define BATT_CHARGE_CURRENT_CTRL_REG 0x2
#define BATT_CHARGE_CURRENT_MASK 0x3F
#define BATT_CHARGE_CURRENT_SHIFT 0
// to do... we can use a better way
#define BATT_CHARGE_CURRENT_VAL0 0x0
#define BATT_CHARGE_CURRENT_VAL420 0x7
#define BATT_CHARGE_CURRENT_VAL960 0x10
#define BATT_CHARGE_CURRENT_VAL420_NEW 0x1C
#define BATT_CHARGE_CURRENT_VAL960_NEW 0x27
static int bq25601_set_batt_charge_current(struct bq25601_charger *chip, unsigned int charge_current, int addrflag)
{
u8 val = BATT_CHARGE_CURRENT_VAL0;
dev_dbg(chip->dev, "%s: set battery charge current %dmA\n", __func__, charge_current);
if ((addrflag == 2) && (part_id >= 0))
{
switch (charge_current)
{
case 0:
val = BATT_CHARGE_CURRENT_VAL0;
break;
case 400:
val = BATT_CHARGE_CURRENT_VAL420_NEW;
break;
case 1000:
val = BATT_CHARGE_CURRENT_VAL960_NEW;
break;
default:
val = BATT_CHARGE_CURRENT_VAL420_NEW;
break;
}
}
else
{
switch (charge_current)
{
case 0:
val = BATT_CHARGE_CURRENT_VAL0;
break;
case 400:
val = BATT_CHARGE_CURRENT_VAL420;
break;
case 1000:
val = BATT_CHARGE_CURRENT_VAL960;
break;
default:
val = BATT_CHARGE_CURRENT_VAL420;
break;
}
}
return bq25601_masked_write(chip, BATT_CHARGE_CURRENT_CTRL_REG,
BATT_CHARGE_CURRENT_MASK, val, BATT_CHARGE_CURRENT_SHIFT);
}
static int bq25601_get_batt_charge_current(struct bq25601_charger *chip, unsigned int *charge_current, int addrflag)
{
int rc;
u8 val;
rc = bq25601_read_reg(chip, BATT_CHARGE_CURRENT_CTRL_REG, &val);
if (rc < 0)
return -EAGAIN;
if ((addrflag == 2) && (part_id >= 0))
{
switch ((val >> BATT_CHARGE_CURRENT_SHIFT) & BATT_CHARGE_CURRENT_MASK)
{
case BATT_CHARGE_CURRENT_VAL0:
*charge_current = 0;
break;
case BATT_CHARGE_CURRENT_VAL420_NEW:
*charge_current = 400;
break;
case BATT_CHARGE_CURRENT_VAL960_NEW:
*charge_current = 1000;
break;
default:
*charge_current = 400;
break;
}
}
else
{
switch ((val >> BATT_CHARGE_CURRENT_SHIFT) & BATT_CHARGE_CURRENT_MASK)
{
case BATT_CHARGE_CURRENT_VAL0:
*charge_current = 0;
break;
case BATT_CHARGE_CURRENT_VAL420:
*charge_current = 400;
break;
case BATT_CHARGE_CURRENT_VAL960:
*charge_current = 1000;
break;
default:
*charge_current = 400;
break;
}
}
dev_dbg(chip->dev, "%s: get battery charge current(0x%02x) %dmA\n", __func__, val, *charge_current);
return 0;
}
/*set & get pre charge current */
#define BATT_CHARGE_PRE_CURRENT_REG 0x3
#define BATT_CHARGE_PRE_CURRENT_VAL180 0xDD
#define BATT_CHARGE_PRE_CURRENT_VAL40 0xD5
static int bq25601_set_batt_pre_charge_current(struct bq25601_charger *chip, unsigned int pre_current)
{
u8 val = BATT_CHARGE_PRE_CURRENT_VAL180;
dev_dbg(chip->dev, "%s: set batt pre charge current %dmA\n", __func__, val);
s32 ret;
switch (pre_current)
{
case 180:
val = BATT_CHARGE_PRE_CURRENT_VAL180;
break;
case 40:
val = BATT_CHARGE_PRE_CURRENT_VAL40;
break;
default:
val = BATT_CHARGE_PRE_CURRENT_VAL180;
break;
}
ret = i2c_smbus_write_byte_data(chip->client, BATT_CHARGE_PRE_CURRENT_REG, val);
if (ret < 0)
{
dev_err(chip->dev, "i2c write fail: can't write %02x to %02x: %d\n", val, BATT_CHARGE_PRE_CURRENT_REG, ret);
return ret;
}
return 0;
}
static int bq25601_get_batt_pre_charge_current(struct bq25601_charger *chip, unsigned int *pre_current)
{
int rc;
u8 val;
rc = bq25601_read_reg(chip, BATT_CHARGE_PRE_CURRENT_REG, &val);
if (rc < 0)
return -EAGAIN;
switch (val)
{
case BATT_CHARGE_PRE_CURRENT_VAL180:
*pre_current = 180;
break;
case BATT_CHARGE_PRE_CURRENT_VAL40:
*pre_current = 40;
break;
default:
*pre_current = 180;
break;
}
return 0;
}
/* set & get battery charging voltage */
#define BATT_CHARGE_VOLTAGE_CTRL_REG 0x4
#define BATT_CHARGE_VOLTAGE_MASK 0x1F
#define BATT_CHARGE_VOLTAGE_SHIFT 3
// to do... we can use a better way
#define BATT_CHARGE_VOLTAGE_VAL4080 0x7
#define BATT_CHARGE_VOLTAGE_VAL4208 0xB
static int bq25601_set_batt_charge_voltage(struct bq25601_charger *chip, unsigned int voltage)
{
u8 val = BATT_CHARGE_VOLTAGE_VAL4208;
dev_dbg(chip->dev, "%s: set battery charge voltage %dmV\n", __func__, voltage);
switch (voltage)
{
case 4100:
val = BATT_CHARGE_VOLTAGE_VAL4080;
break;
case 4200:
val = BATT_CHARGE_VOLTAGE_VAL4208;
break;
default:
val = BATT_CHARGE_VOLTAGE_VAL4080;
break;
}
return bq25601_masked_write(chip, BATT_CHARGE_VOLTAGE_CTRL_REG,
BATT_CHARGE_VOLTAGE_MASK, val, BATT_CHARGE_VOLTAGE_SHIFT);
}
static int bq25601_get_batt_charge_voltage(struct bq25601_charger *chip, unsigned int *voltage)
{
int rc;
u8 val;
rc = bq25601_read_reg(chip, BATT_CHARGE_VOLTAGE_CTRL_REG, &val);
if (rc < 0)
return -EAGAIN;
switch ((val >> BATT_CHARGE_VOLTAGE_SHIFT) & BATT_CHARGE_VOLTAGE_MASK)
{
case BATT_CHARGE_VOLTAGE_VAL4080:
*voltage = 4100;
break;
case BATT_CHARGE_VOLTAGE_VAL4208:
*voltage = 4200;
break;
default:
*voltage = 4100;
break;
}
dev_dbg(chip->dev, "%s: get battery charge voltage(0x%02x) %dmV\n", __func__, val, *voltage);
return 0;
}
/* disable watchdog */
#define WATCHDOG_CTRL_REG 0x5
#define WATCHDOG_MASK 0x3
#define WATCHDOG_SHIFT 4
// to do... we can use a better way
#define WATCHDOG_DISABLED 0x0
static int bq25601_disable_watchdog(struct bq25601_charger *chip)
{
u8 val = WATCHDOG_DISABLED;
dev_dbg(chip->dev, "%s: disable watchdog\n", __func__);
return bq25601_masked_write(chip, WATCHDOG_CTRL_REG, WATCHDOG_MASK, val, WATCHDOG_SHIFT);
}
/* set usb charging min voltage */
#define USB_CHARGE_VOLTAGE_CTRL_REG 0x6
#define USB_CHARGE_VOLTAGE_MASK 0xF
#define USB_CHARGE_VOLTAGE_SHIFT 0
// to do... we can use a better way
#define USB_CHARGE_VOLTAGE_VAL4600 0x7
static int bq25601_set_usb_charge_voltage(struct bq25601_charger *chip, unsigned int voltage)
{
u8 val = USB_CHARGE_VOLTAGE_VAL4600;
dev_dbg(chip->dev, "%s: set usb charge voltage %dmV\n", __func__, voltage);
switch (voltage)
{
case 4600:
val = USB_CHARGE_VOLTAGE_VAL4600;
break;
default:
val = USB_CHARGE_VOLTAGE_VAL4600;
break;
}
return bq25601_masked_write(chip, USB_CHARGE_VOLTAGE_CTRL_REG,
USB_CHARGE_VOLTAGE_MASK, val, USB_CHARGE_VOLTAGE_SHIFT);
}
/* get usb status */
#define USB_STATUS_CTRL_REG 0x8
#define USB_POWER_STATUS_MASK 0x1
#define USB_POWER_STATUS_SHIFT 2
static int bq25601_get_usb_status(struct bq25601_charger *chip, unsigned int *status)
{
int rc;
u8 val;
rc = bq25601_read_reg(chip, USB_STATUS_CTRL_REG, &val);
if (rc < 0)
return -EAGAIN;
// we can just use PG_STST
if ((val >> USB_POWER_STATUS_SHIFT) & USB_POWER_STATUS_MASK)
{
*status = 1;
}
else
{
*status = 0;
}
dev_dbg(chip->dev, "%s: get usb status(0x%02x) %d\n", __func__, val, *status);
return 0;
}
/* get battery voltage */
static int bq25601_get_property_battery_voltage(struct bq25601_charger *chip)
{
int rc = 0;
struct qpnp_vadc_result results;
int voltage = 0;
if (IS_ERR_OR_NULL(chip->vadc_dev))
{
chip->vadc_dev = qpnp_get_vadc(chip->dev, "chg");
if (IS_ERR(chip->vadc_dev))
{
pr_err("%s, Failed to get vadc\n", __func__);
return PTR_ERR(chip->vadc_dev);
}
}
rc = qpnp_vadc_read(chip->vadc_dev, P_MUX2_1_1, &results);
if (rc)
{
pr_err("Unable to read battery voltage, rc = %d\n", rc);
return rc;
}
voltage = (int)(results.physical * chip->vbat_div) / 1000;
dev_dbg(chip->dev, "%s: get battery voltage %d\n", __func__, voltage);
return voltage;
}
/* get part id */
#define BATT_PART_ID_REG 0xB
static int bq25601_get_batt_part_id(struct bq25601_charger *chip)
{
int rc;
u8 val;
rc = bq25601_read_reg(chip, BATT_PART_ID_REG, &val);
dev_dbg("get reg(0x%02x) value 0x%02x\n", BATT_PART_ID_REG, val);
if (rc < 0)
return -1;
return val;
}
/* get battery temperature */
static int em_batt_map_linear(const int (*table)[2], const u32 table_size, int in_num, int *out_num)
{
u32 i = 0;
if ((NULL == table) || (NULL == out_num))
{
pr_err("Invalid parameter\n");
return -EINVAL;
}
while (i < table_size)
{
if (table[i][0] < in_num)
{
break;
}
i++;
}
if (i == 0)
{
*out_num = table[0][1];
}
else if (i == table_size)
{
*out_num = table[table_size-1][1];
}
else
{
/* result is between search_index and search_index-1, interpolate linearly */
*out_num = (((table[i][1] - table[i-1][1]) * (in_num - table[i-1][0]))
/ (table[i][0] - table[i-1][0])) + table[i-1][1];
}
return 0;
}
static int bq25601_get_property_battery_id_therm(struct bq25601_charger *chip)
{
int rc = 0;
int temp_value = 0;
struct qpnp_vadc_result results;
if (IS_ERR_OR_NULL(chip->vadc_dev))
{
chip->vadc_dev = qpnp_get_vadc(chip->dev, "chg");
if (IS_ERR(chip->vadc_dev))
{
pr_err("%s, Failed to get vadc\n", __func__);
return PTR_ERR(chip->vadc_dev);
}
}
rc = qpnp_vadc_read(chip->vadc_dev, LR_MUX2_BAT_ID, &results);
if (rc)
{
pr_err("Unable to read battery temperature, rc = %d\n", rc);
return rc;
}
temp_value = ((int)results.physical) / 1000; /* convert to mV */
rc = em_batt_map_linear(adc_map_temp, sizeof(adc_map_temp) / sizeof(adc_map_temp[0]),
temp_value, &temp_value);
if (rc)
{
pr_err("Unable to calculate battery temperature, rc = %d\n", rc);
return rc;
}
return temp_value;
}
/* sysfs, write & read */
static int bq25601_property_is_writeable(struct power_supply *psy, enum power_supply_property psp)
{
switch (psp)
{
case POWER_SUPPLY_PROP_CURRENT_MAX:
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
case POWER_SUPPLY_PROP_CURRENT_NOW:
return 1;
default:
return 0;
}
}
static int bq25601_power_set_property(struct power_supply *psy, enum power_supply_property psp,
const union power_supply_propval *val)
{
struct bq25601_charger *chip = container_of(psy, struct bq25601_charger, batt_psy);
mutex_lock(&chip->lock);
switch (psp)
{
case POWER_SUPPLY_PROP_CURRENT_MAX:
bq25601_set_batt_charge_current(chip, val->intval, addrflag);
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
bq25601_set_batt_charge_voltage(chip, val->intval);
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
bq25601_set_batt_pre_charge_current(chip, val->intval);
break;
default:
mutex_unlock(&chip->lock);
return -EINVAL;
}
mutex_unlock(&chip->lock);
power_supply_changed(&chip->batt_psy);
return 0;
}
static int bq25601_power_get_property(struct power_supply *psy, enum power_supply_property psp,
union power_supply_propval *val)
{
struct bq25601_charger *chip = container_of(psy, struct bq25601_charger, batt_psy);
mutex_lock(&chip->lock);
switch (psp)
{
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
val->intval = bq25601_get_property_battery_voltage(chip);
break;
case POWER_SUPPLY_PROP_TEMP:
val->intval = bq25601_get_property_battery_id_therm(chip);
break;
case POWER_SUPPLY_PROP_CURRENT_MAX:
bq25601_get_batt_charge_current(chip, &(val->intval), addrflag);
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
bq25601_get_batt_charge_voltage(chip, &(val->intval));
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
bq25601_get_batt_pre_charge_current(chip, &(val->intval));
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
break;
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = "BQ25601";
break;
case POWER_SUPPLY_PROP_MANUFACTURER:
val->strval = "TI";
break;
default:
mutex_unlock(&chip->lock);
return -EINVAL;
}
mutex_unlock(&chip->lock);
return 0;
}
/* irq handler & usb detect */
static int bq25601_set_usb_default_status(struct bq25601_charger *chip)
{
int rc = 0;
// set usb charge limit voltage 4.6V
rc = bq25601_set_usb_charge_voltage(chip, 4600);
if (rc)
{
dev_err(chip->dev, "set usb charge voltage failed\n");
return rc;
}
// set usb charge current 1A
rc = bq25601_set_usb_charge_current(chip, 1000);
if (rc)
{
dev_err(chip->dev, "set usb charge current failed\n");
return rc;
}
// enable usb charging
rc = gpio_direction_output(chip->charge_gpio, 0);
if (rc)
{
dev_err(chip->dev, "set direction for charge gpio(%d) failed\n", chip->charge_gpio);
return rc;
}
// disable watchdog
rc = bq25601_disable_watchdog(chip);
if (rc)
{
dev_err(chip->dev, "disable watchdog failed\n");
return rc;
}
//set batt pre charge
if ((addrflag == 2) && (part_id >= 0))
{
rc = bq25601_set_batt_pre_charge_current(chip, 180);
}
if (rc)
{
dev_err(chip->dev, "set batt pre charge failed\n");
return rc;
}
return rc;
}
static void bq25601_usb_detect_work_func(struct work_struct *work)
{
struct bq25601_charger *chip = container_of(work, struct bq25601_charger, usb_detect_work);
int status = 0;
bq25601_get_usb_status(chip, &status);
if ((chip->usb_status != status) && (chip->usb_psy))
{
if (status)
{
dev_info(chip->dev, "usb insert, reset usb charge status\n");
bq25601_set_usb_default_status(chip);
}
power_supply_set_present(chip->usb_psy, status);
chip->usb_status = status;
}
else
{
dev_dbg(chip->dev, "usb status not changed\n");
}
}
static irqreturn_t bq25601_irq_handler(int irq, void *dev_id)
{
struct bq25601_charger *chip = dev_id;
mutex_lock(&chip->irq_lock);
dev_dbg(chip->dev, "IRQ triggered\n");
schedule_work(&chip->usb_detect_work);
mutex_unlock(&chip->irq_lock);
return IRQ_HANDLED;
}
/* module init & probe & remove */
static int bq25601_charger_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int rc, irq, value;
struct bq25601_charger *chip;
struct power_supply *usb_psy;
dev_info(&client->dev, "BQ25601 charger probe start...\n");
usb_psy = power_supply_get_by_name("usb");
if (!usb_psy)
{
dev_dbg(&client->dev, "USB psy not found, deferring probe\n");
return -EPROBE_DEFER;
}
// same init value as chip->usb_status
power_supply_set_present(usb_psy, 0);
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
{
dev_err(&client->dev, "Cannot allocate memory\n");
return -ENOMEM;
}
chip->client = client;
if (client->addr == 0x6b)
{
addrflag = 1;
}
else
{
addrflag = 2;
}
chip->dev = &client->dev;
chip->usb_psy = usb_psy;
chip->usb_status = 0;
mutex_init(&chip->lock);
mutex_init(&chip->irq_lock);
mutex_init(&chip->read_write_lock);
part_id = bq25601_get_batt_part_id(chip);
if (part_id < 0)
{
return -1;
}
if (of_find_property(chip->dev->of_node, "qcom,chg-vadc", NULL))
{
/* early for VADC get, defer probe if needed */
chip->vadc_dev = qpnp_get_vadc(chip->dev, "chg");
if (IS_ERR(chip->vadc_dev))
{
rc = PTR_ERR(chip->vadc_dev);
if (rc != -EPROBE_DEFER)
pr_err("vadc property configured incorrectly\n");
return rc;
}
}
chip->batt_psy.name = "battery";
chip->batt_psy.type = POWER_SUPPLY_TYPE_BATTERY;
chip->batt_psy.get_property = bq25601_power_get_property;
chip->batt_psy.set_property = bq25601_power_set_property;
chip->batt_psy.property_is_writeable = bq25601_property_is_writeable;
chip->batt_psy.properties = bq25601_power_properties;
chip->batt_psy.num_properties = ARRAY_SIZE(bq25601_power_properties);
rc = power_supply_register(chip->dev, &chip->batt_psy);
if (rc < 0)
{
dev_err(&client->dev, "Cannot register batt_psy, rc = %d\n", rc);
return rc;
}
INIT_WORK(&chip->usb_detect_work, bq25601_usb_detect_work_func);
rc = of_property_read_u32(chip->dev->of_node, "ti,chg-vbat-div", &value);
if (rc == 0)
{
chip->vbat_div = value;
}
else
{
chip->vbat_div = 1;
dev_err(&client->dev, "Failed to get battery voltage division, rc = %d\n", rc);
}
chip->charge_gpio = of_get_named_gpio(chip->dev->of_node, "ti,chg-en-gpio", 0);
dev_info(&client->dev, "charge gpio: %d\n", chip->charge_gpio);
if (gpio_is_valid(chip->charge_gpio))
{
rc = gpio_request(chip->charge_gpio, "ti,chg-en-gpio");
if (rc)
{
pr_err("charge enable gpio request failed, rc = %d\n", rc);
goto fail_charge_gpio_init;
}
}
else
{
goto fail_charge_gpio_init;
}
chip->irq_gpio = of_get_named_gpio_flags(chip->dev->of_node, "ti,irq-gpio", 0, NULL);
dev_info(&client->dev, "irq gpio: %d\n", chip->irq_gpio);
if (gpio_is_valid(chip->irq_gpio))
{
rc = gpio_request(chip->irq_gpio, "bq25601_irq");
if (rc)
{
dev_err(&client->dev, "irq gpio request failed, rc = %d", rc);
goto fail_irq_gpio_init;
}
rc = gpio_direction_input(chip->irq_gpio);
if (rc)
{
dev_err(&client->dev, "set direction for irq gpio(%d) failed\n", chip->irq_gpio);
goto fail_irq_gpio_init;
}
irq = gpio_to_irq(chip->irq_gpio);
if (irq < 0)
{
dev_err(&client->dev, "Invalid irq_gpio irq = %d\n", irq);
goto fail_irq_gpio_init;
}
rc = devm_request_threaded_irq(&client->dev, irq, NULL, bq25601_irq_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "bq25601_irq", chip);
if (rc)
{
dev_err(&client->dev, "Failed allocate irq = %d request, rc = %d\n", irq, rc);
goto fail_irq_gpio_init;
}
enable_irq_wake(irq);
dev_info(&client->dev, "set irq success\n");
}
else
{
goto fail_irq_gpio_init;
}
rc = bq25601_set_usb_default_status(chip);
if (rc)
{
goto fail_enable_charge;
}
// init usb status
schedule_work(&chip->usb_detect_work);
dev_info(&client->dev, "BQ25601 charger successfully probed\n");
return 0;
fail_enable_charge:
fail_irq_gpio_init:
if (gpio_is_valid(chip->irq_gpio))
gpio_free(chip->irq_gpio);
fail_charge_gpio_init:
if (gpio_is_valid(chip->charge_gpio))
gpio_free(chip->charge_gpio);
power_supply_unregister(&chip->batt_psy);
return rc;
}
static int bq25601_charger_remove(struct i2c_client *client)
{
struct bq25601_charger *chip = i2c_get_clientdata(client);
power_supply_unregister(&chip->batt_psy);
if (gpio_is_valid(chip->charge_gpio))
gpio_free(chip->charge_gpio);
if (gpio_is_valid(chip->irq_gpio))
gpio_free(chip->irq_gpio);
mutex_destroy(&chip->irq_lock);
return 0;
}
static int bq25601_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
dev_info(&client->dev, "BQ25601 charger suspend\n");
return 0;
}
static int bq25601_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
dev_info(&client->dev, "BQ25601 charger resume\n");
return 0;
}
static const struct dev_pm_ops bq25601_pm_ops = {
.suspend = bq25601_suspend,
.resume = bq25601_resume,
};
static struct of_device_id bq25601_match_table[] = {
{ .compatible = "ti,sgm41513-charger",},
{ .compatible = "ti,bq25601-charger",},
};
static const struct i2c_device_id bq25601_charger_id[] = {
{"sgm41513-charger", 0},
{"bq25601-charger", 1},
};
MODULE_DEVICE_TABLE(i2c, bq25601_charger_id);
static struct i2c_driver bq25601_charger_driver = {
.driver = {
.name = "bq25601-charger",
.owner = THIS_MODULE,
.of_match_table = bq25601_match_table,
.pm = &bq25601_pm_ops,
},
.probe = bq25601_charger_probe,
.remove = bq25601_charger_remove,
.id_table = bq25601_charger_id,
};
module_i2c_driver(bq25601_charger_driver);
MODULE_DESCRIPTION("BQ25601 Charger");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("i2c:bq25601-charger");