Dear TI expert;
Do you have the BQ25121 sample code which can use I2C to control the the BQ25121 charging process? Thank you very much.
This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
FanYang,
Here is a sample code for accessing the BQ25120.
/* * BQ25120 Battery Charger Driver * * Copyright (C) 2015, Texas Instruments Corporation * * License TBD from SRAS */ #include <linux/err.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/i2c.h> #include <linux/power_supply.h> #include <linux/regmap.h> /* Registers */ #define BQ25120_STATUS_SHIPMODE_REG 0x00 #define BQ25120_FAULTS_FAULTMASKS_REG 0x01 #define BQ25120_TSCONTROL_STATUS_REG 0x02 #define BQ25120_FASTCHARGE_CTL_REG 0x03 #define BQ25120_CHARGETERM_I2CADDR_REG 0x04 #define BQ25120_BATT_VOLTAGE_CTL_REG 0x05 #define BQ25120_SYSTEM_VOUT_CTL_REG 0x06 #define BQ25120_LOADSW_LDO_CTL_REG 0x07 #define BQ25120_PUSH_BTN_CTL_REG 0x08 #define BQ25120_ILIMIT_UVLO_CTL_REG 0x09 #define BQ25120_BATT_VOLT_MONITOR_REG 0x0A #define BQ25120_VIN_DPM_TIMER_REG 0x0B /* Status and ShipMode Control Register (0x0) */ #define BQ25120_STAT (BIT(7) | BIT(6)) #define BQ25120_STAT_SHIFT 0x06 #define BQ25120_STAT_MASK 0x03 #define BQ25120_EN_SHIPMODE BIT(5) #define BQ25120_RESET BIT(4) #define BQ25120_TIMER BIT(3) #define BQ25120_VINDPM_STAT BIT(2) #define BQ25120_NOT_CD_STAT BIT(1) #define BQ25120_SYS_EN_STAT BIT(0) /* Faults and Fault Masks Register (Ox1) */ #define BQ25120_VIN_OV BIT(7) #define BQ25120_VIN_UV BIT(6) #define BQ25120_BAT_UVLO BIT(5) #define BQ25120_BAT_OCP BIT(4) #define BQ25120_FAULTS (BIT(7) | BIT(6) | BIT(5) | BIT(4)) #define BQ25120_FAULTS_SHIFT 0x04 #define BQ25120_FAULTS_MASK 0x0F #define BQ25120_VIN_OV_M BIT(3) #define BQ25120_VIN_OV_M_SHIFT 0x03 #define BQ25120_VIN_OV_M_MASK 0x01 #define BQ25120_VIN_UV_M BIT(2) #define BQ25120_VIN_UV_M_SHIFT 0x02 #define BQ25120_VIN_UV_M_MASK 0x01 #define BQ25120_BAT_UVLO_M BIT(1) #define BQ25120_BAT_UVLO_M_SHIFT 0x01 #define BQ25120_BAT_UVLO_M_MASK 0x01 #define BQ25120_BAT_OCP_M BIT(0) #define BQ25120_BAT_OCP_M_SHIFT 0x0 #define BQ25120_BAT_OCP_M_MASK 0x01 /* TS Control and Status Mask Register (0x2) */ #define BQ25120_TS_EN BIT(7) #define BQ25120_TS_FAULT (BIT(6) | BIT(5)) #define BQ25120_TS_FAULT_SHIFT 0x05 #define BQ25120_TS_FAULT_MASK 0X03 #define BQ25120_TS_FAULT_OPEN BIT(4) #define BQ25120_EN_INT BIT(3) #define BQ25120_WAKE_M BIT(2) #define BQ25120_WAKE_M_SHIFT 0x02 #define BQ25120_WAKE_M_MASK 0x01 #define BQ25120_RESET_M BIT(1) #define BQ25120_RESET_SHIFT 0x01 #define BQ25120_RESET_MASK 0x01 #define BQ25120_TIMER_M BIT(0) #define BQ25120_TIMER_M_SHIFT 0 #define BQ25120_TIMER_M_MASK 0x01 /* Fast Charge Control Register (0x03) */ #define BQ25120_ICHRG_RANGE BIT(7) #define BQ25120_ICHARG (BIT(6) | BIT(5) | BIT(4) | BIT(3) | BIT(2)) #define BQ25120_ICHARG_SHIFT 0x02 #define BQ25120_ICHARG_MASK 0x1F #define BQ25120_CE BIT(1) #define BQ25120_HZ_MODE BIT(0) /* Termination/Pre-Charge and I2C Address Register (0x4) */ #define BQ25120_IPRETERM_RANGE BIT(7) #define BQ25120_IPRETERM (BIT(6) | BIT(5) | BIT(4) | BIT(3) | BIT(2)) #define BQ25120_IPRETERM_SHIFT 0x02 #define BQ25120_IPRETERM_MASK 0x1F #define BQ25120_TE BIT(1) // Bit 0 Reserved /* Battery Voltage Control Register (0x05) */ #define BQ25120_VBREG (BIT(7) | BIT(6) | BIT(5) | BIT(4) | BIT(3) | BIT(2) | BIT(1)) #define BQ25120_VBREG_SHIFT 0x1 #define BQ25120_VBREG_MASK 0x7F // Bit 0 Reserved /* SYS VOUT Control Register (0x06) */ #define BQ25120_EN_SYS_OUT BIT(7) #define BQ25120_SYS_SEL (BIT(6) | BIT(5)) #define BQ25120_SYS_SEL_SHIFT 0x4 #define BQ25120_SYS_MASK 0x3 #define BQ25120_SYS_VOUT (BIT(4) | BIT(3) | BIT(2) | BIT(1)) #define BQ25120_SYS_VOUT_SHIFT 0x01 #define BQ25120_SYS_VOUT_MASK 0x0F //Bit 0 Reserved /* Load Switch and LDO Control Register (0x07) */ #define BQ25120_EN_LS_LDO BIT(7) #define BQ25120_LS_LDO (BIT(6) | BIT(5) | BIT(4) | BIT(3) | BIT(2)) #define BQ25120_LS_LDO_SHIFT 0x2 #define BQ25120_LS_LDO_MASK 0x1F //Bit 1 Reserved #define BQ25120_MRRESET_VIN BIT(0) /* Pushbutton Control Register (0x08) */ #define BQ25120_MRWAKE1 BIT(7) #define BQ25120_MRWAKE2 BIT(6) #define BQ25120_MRREC BIT(5) #define BQ25120_MRRESET (BIT(4) | BIT(3)) #define BQ25120_MRRESET_SHIFT 0x03 #define BQ25120_MRRESET_MASK 0x03 #define BQ25120_PGB_MRS BIT(2) #define BQ25120_WAKE1 BIT(1) #define BQ25120_WAKE2 BIT(0) /* ILIM and Battery UVLO Control Register (0x09) */ #define BQ25120_RESET_REG BIT(7) //Bit 6 Reserved #define BQ25120_INLIM (BIT(5) | BIT(4) | BIT(3)) #define BQ25120_INLIM_SHIFT 0x03 #define BQ25120_INLIM_MASK 0x07 #define BQ25120_BUVLO (BIT(2) | BIT(1) | BIT(0)) #define BQ25120_BUVLO_SHIFT 0x0 #define BQ25120_BUVLO_MASK 0x7 /* Voltage Based Battery Monitor Register (0x0A) */ #define BQ25120_VBMON_READ BIT(7) #define BQ25120_VBMON_RANGE (BIT(6) | BIT(5)) #define BQ25120_VBMON_RANGE_SHIFT 0x05 #define BQ25120_VBMON_RANGE_MASK 0x03 #define BQ25120_VBMON_TH (BIT(4) | BIT(3) | BIT(2)) #define BQ25120_VBMON_TH_SHIFT 0x02 #define BQ25120_VBMON_TH_MASK 0x07 //Bit 1 and 0 Reserved /* VIN_DPM and Timers Register (0x0B) */ #define BQ25120_VINDPM_OFF BIT(7) #define BQ25120_VINDPM (BIT(6) | BIT(5) | BIT(4)) #define BQ25120_VINDPM_SHIFT 0x04 #define BQ25120_VINDPM_MASK 0x07 #define BQ25120_2XTMR_EN BIT(3) #define BQ25120_TMR (BIT(2) | BIT(1)) #define BQ25120_TMR_SHIFT 0x01 #define BQ25120_TMR_MASK 0x03 #define BQ25120_TMR_DISABLE 0x03 //Bit 0 Reserved #define BQ25120_MAX_CHG_FAULT 8 static struct reg_default BQ25120_reg_defs[] = { {BQ25120_STATUS_SHIPMODE_REG, 0x01}, {BQ25120_FAULTS_FAULTMASKS_REG, 0x00}, {BQ25120_TSCONTROL_STATUS_REG, 0x88}, {BQ25120_FASTCHARGE_CTL_REG, 0x14}, {BQ25120_CHARGETERM_I2CADDR_REG, 0x0E}, {BQ25120_BATT_VOLTAGE_CTL_REG, 0x78}, {BQ25120_SYSTEM_VOUT_CTL_REG, 0xAA}, {BQ25120_LOADSW_LDO_CTL_REG, 0x7C}, {BQ25120_PUSH_BTN_CTL_REG, 0x68}, {BQ25120_ILIMIT_UVLO_CTL_REG, 0x0A}, {BQ25120_BATT_VOLT_MONITOR_REG, 0x00}, {BQ25120_VIN_DPM_TIMER_REG, 0x4A}, }; enum BQ25120_charge_fault { BQ25120_NORMAL = 0, BQ25120_CH_VIN_OV, BQ25120_CH_VIN_UV, BQ25120_BATTERY_UVLO, BQ25120_BATTERY_OCP, }; enum BQ25120_temp_status { BQ25120_TEMP_NORMAL = 0, BQ25120_TEMP_TNTC, BQ25120_TEMP_TCOOL, BQ25120_TEMP_TWARM, }; //Not used. static const char *fault_desc[BQ25120_MAX_CHG_FAULT] = { [BQ25120_NORMAL] = "Normal", [BQ25120_VIN] = "Vin > VovpINPUT", [BQ25120_LOW_SUPPLY_CONNECTED] = "Low Power Supply Connected or Boost Mode Overcurrent", [BQ25120_THERMAL_SHUTDOWN] = "Thermal Shutdown", [BQ25120_BATTERY_TS_FAULT] = "Battery Temperature Fault", [BQ25120_BATTERY_OVP] = "Battery OVP", [BQ25120_NO_BATTERY_CONNECTED] = "No Battery Connected", }; static const char * temp_status_desc[BQ25120_MAX_TEMP_STATUS] = { [BQ25120_NORMAL] = "Normal", [BQ25120_TEMP_TNTC] = "TS temp < Tcold or TS temp > Thot", [BQ25120_TEMP_TCOOL] = "Tcool > TS temp or TS temp > Tcold", [BQ25120_TEMP_TWARM] = "Twarm < TS temp or TS temp < Tcold", }; /** * struct bq25120_charger - bq25120 charger instance * @dev: pointer to device * @regmap: pointer to driver regmap * @mains: power_supply instance for AC/DC power * @battery: power_supply instance for battery * @mains_online: is AC/DC input connected * @charging_enabled: is charging enabled * @pdata: pointer to platform data * @irq: fault irq number */ struct bq25120_charger { struct device *dev; struct regmap *regmap; struct power_supply mains; struct power_supply battery; bool mains_online; bool usb_online; bool charging_enabled; bool vsysoff_enabled; int irq; /* IRQ for INT pin */ const struct bq25120_charger_platform_data *pdata; }; /* Charge current in mA */ static const unsigned int cc_tbl[] = { 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220, 230, 240, 250, 260, 270, 280, 290, 300 }; /* Termination current in mA */ static const unsigned int tc_tbl[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37 }; /* Input current limit in mA */ static const unsigned int icl_tbl[] = { 50, 100, 150, 200, 250, 300, 350, 400 }; /* Safety timer time limit in minutes */ static const unsigned int stl_tbl[] = { 30, 180, 540, 0, /* Disable */ }; /* Convert current to register value using lookup table */ static int current_to_hw(const unsigned int *tbl, size_t size, unsigned int val) { size_t i; for (i = 0; i < size; i++) if (val < tbl[i]) break; return i > 0 ? i - 1 : -EINVAL; } /* * bq25120_is_ps_online - returns whether input power source is connected * @bq25120: pointer to bq25120 charger instance * * Returns %true if input power source is connected. Note that this is * dependent on what platform has configured for usable power sources. */ static bool bq25120_is_ps_online(struct bq25120_charger *bq25120) { return (bq25120->mains_online); } /** * bq25120_update_ps_status - refreshes the power source status * @bq25120: pointer to bq25120 charger instance * * Function checks whether any power source is connected to the charger and * updates internal state accordingly. If there is a change to previous state * function returns %1, otherwise %0 and negative errno in case of errror. */ static int bq25120_update_ps_status(struct bq25120_charger *bq25120) { bool usb = false; bool dc = false; unsigned int val; int ret; int vinDpm; ret = regmap_read(bq25120->regmap, BQ25120_BATT_VOLT_MONITOR_REG , &val); if (ret < 0) return ret; /* * platform data _and_ whether corresponding undervoltage is set. */ if (bq25120->pdata->use_mains) dc = !( (val >> 7) BQ25120_VINDPM_OFF) ); ret = bq25120->mains_online != dc; bq25120->mains_online = dc; return ret; } enum power_supply_property bq25120_charger_properties[] = { POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, }; /** * bq25120_charging_status - returns status of charging * @bq25120: pointer to bq25120 charger instance * * Function returns charging status. * %0 means ready, %1 charging is in progress, * %2charging done and %3 fault. */ static int bq25120_charging_status(struct bq25120_charger *bq25120) { unsigned int val; int ret; if (!bq25120_is_ps_online(bq25120)) return 0; ret = regmap_read(bq25120->regmap, BQ25120_STATUS_SHIPMODE_REG, &val); if (ret < 0) return 0; return ((val & BQ25120_STAT_MASK) >> BQ25120_STAT_SHIFT); } static int bq25120_charging_type(struct bq25120_charger *bq25120) { unsigned int val; int ret; if (!bq25120_is_ps_online(bq25120)) return 0; ret = regmap_read(bq25120->regmap, BQ25120_STATUS_SHIPMODE_REG, &val); if (ret < 0) return 0; return ((val >> BQ25120_STAT_SHIFT) & BQ25120_STAT_MASK); } static int bq25120_timer_suspend(struct bq25120_charger *bq25120) { int ret = 0; if (bq25120->pdata->enable_control != BQ25120_CE) { dev_dbg(bq25120->dev, "vsys enable/disable in SW disabled\n"); return 0; } ret = regmap_update_bits(bq25120->regmap, BQ25120_VIN_DPM_TIMER_REG, BQ25120_TMR, BQ25120_TMR_DISABLE); if (!ret) dev_err(bq25120->dev, "failed to suspend timer\n"); return ret; } static int bq25120_timer_reset(struct bq25120_charger *bq25120) { int ret; unsigned int tmr; /* Reset with previous time value, when fault occur */ ret = regmap_read(bq25120->regmap, BQ25120_VIN_DPM_TIMER_REG, &tmr); tmr = (tmr & BQ25120_TMR) >> BQ25120_TMR_SHIFT; ret = regmap_update_bits(bq25120->regmap, BQ25120_VIN_DPM_TIMER_REG, BQ25120_TMR, tmr); if (!ret) dev_err(bq25120->dev, "failed to reset timer\n"); return ret; } static int bq25120_charging_set(struct bq25120_charger *bq25120, bool enable) { int ret = 0; if (bq25120->charging_enabled != enable) { ret = regmap_update_bits(bq25120->regmap, BQ25120_FASTCHARGE_CTL_REG, BQ25120_CE, enable ? 0 : 1); if (!ret) bq25120->charging_enabled = enable; } return ret; } static inline int bq25120_charging_enable(struct bq25120_charger *bq25120) { return bq25120_charging_set(bq25120, true); } static inline int bq25120_charging_disable(struct bq25120_charger *bq25120) { return bq25120_charging_set(bq25120, false); } static int bq25120_start_stop_charging(struct bq25120_charger *bq25120) { int ret; /* * Depending on whether valid power source is connected or not, we * disable or enable the charging. We do it manually because it * depends on how the platform has configured the valid inputs. */ if (bq25120_is_ps_online(bq25120)) { ret = bq25120_charging_enable(bq25120); if (ret < 0) dev_err(bq25120->dev, "failed to enable charging\n"); } else { ret = bq25120_charging_disable(bq25120); if (ret < 0) dev_err(bq25120->dev, "failed to disable charging\n"); } return ret; } static int bq25120_set_charge_current(struct bq25120_charger *bq25120) { int ret; unsigned int max_chrg_curr = bq25120->pdata->max_charge_current; unsigned int term_curr = bq25120->pdata->termination_current; if (max_chrg_curr) { if (max_chrg_curr > 300) { dev_warn(bq25120->dev, "Platform max charge current %d. \ Set to 300mA for bq25120\n", max_chrg_curr); } ret = current_to_hw(cc_tbl, ARRAY_SIZE(cc_tbl), max_chrg_curr); if (ret < 0) return ret; ret = regmap_update_bits(bq25120->regmap, BQ25120_CHARGETERM_I2CADDR_REG, BQ25120_IPRETERM, ret); if (ret < 0) return ret; } if (term_curr) { if (term_curr < 5) { dev_warn(bq25120->dev, "Platform termination current %d. \ Set to 5mA for bq25120\n", term_curr); } ret = current_to_hw(tc_tbl, ARRAY_SIZE(tc_tbl), term_curr); if (ret < 0) return ret; ret = regmap_update_bits(bq25120->regmap, BQ25120_CHARGETERM_I2CADDR_REG, BQ25120_IPRETERM, ret); if (ret < 0) return ret; } return 0; } static int bq25120_set_current_limits(struct bq25120_charger *bq25120) { int ret; if (bq25120->pdata->mains_current_limit) { ret = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl), bq25120->pdata->mains_current_limit); if (ret < 0) return ret; if (ret > 400) dev_warn(bq25120->dev, "Invalid mains input current limit\n"); else { ret = regmap_update_bits(bq25120->regmap, BQ25120_ILIMIT_UVLO_CTL_REG, BQ25120_INLIM, ret); if (ret < 0) return ret; } } return 0; } static int bq25120_set_voltage_limits(struct bq25120_charger *bq25120) { int ret; if (bq25120->pdata->max_charge_voltage) { ret = bq25120->pdata->max_charge_voltage; if (ret < 0) return ret; ret = regmap_update_bits(bq25120->regmap, BQ25120_BATT_VOLTAGE_CTL_REG, BQ25120_VBREG, ret); if (ret < 0) return ret; } return 0; } static int bq25120_hw_init(struct bq25120_charger *bq25120) { int ret; /* * Program the platform specific configuration values to the device * first. */ ret = bq25120_set_charge_current(bq25120); if (ret < 0) goto fail; ret = bq25120_set_current_limits(bq25120); if (ret < 0) goto fail; ret = bq25120_set_voltage_limits(bq25120); if (ret < 0) goto fail; ret = bq25120_update_ps_status(bq25120); if (ret < 0) goto fail; ret = bq25120_start_stop_charging(bq25120); fail: return ret; } static irqreturn_t bq25120_interrupt(int irq, void *data) { struct bq25120_charger *bq25120 = data; unsigned int irqstat; bool handled = false; int ret; ret = regmap_read(bq25120->regmap, BQ25120_FAULTS_FAULTMASKS_REG, &irqstat); if (ret < 0) { dev_warn(bq25120->dev, "reading BQ25120_FAULTS_FAULTMASKS_REG failed\n"); return IRQ_NONE; } irqstat &= 0xF0; //Mask mask bits /* * If we get a fault report the error. */ switch (irqstat) { case BQ25120_NORMAL: /* Normal - No fault */ handled = true; break; case BQ25120_VIN_OV: dev_info(bq25120->dev,"Charger Fault: Vin Over Voltage fault.\n"); handled = false; break; case BQ25120_VIN_UV: dev_info(bq25120->dev,"Charger Fault: VIN Under Voltage fault.\n"); handled = false; break; case BQ25120_BAT_UVLO: dev_info(bq25120->dev, "Battery Fault: BAT_UVLO fault.\n"); handled = false; break; case BQ25120_BAT_OCP: dev_info(bq25120->dev, "Battery Fault: BAT_OCP fault.\n"); handled = false; break; default: dev_err(bq25120->dev, "Fault: unknown fault# %d - not handled. \n", irqstat); handled = false; break; } return handled ? IRQ_HANDLED : IRQ_NONE; } static int bq25120_irq_set(struct bq25120_charger *bq25120, bool enable) { int ret; /* * Enable/disable interrupts for: */ ret = regmap_update_bits(bq25120->regmap, BQ25120_TSCONTROL_STATUS_REG, BQ25120_EN_INT, enable ? 1 : 0); if (ret < 0) return ret; return 0; } static inline int bq25120_irq_enable(struct bq25120_charger *bq25120) { return bq25120_irq_set(bq25120, true); } static inline int bq25120_irq_disable(struct bq25120_charger *bq25120) { return bq25120_irq_set(bq25120, false); } static int bq25120_irq_init(struct bq25120_charger *bq25120, int irq) { int ret; if (irq <= 0) { dev_info(bq25120->dev, "invalid irq number: %d\n", irq); goto out; } regmap_update_bits(bq25120->regmap, BQ25120_TSCONTROL_STATUS_REG, BQ25120_EN_INT, 1); ret = request_threaded_irq(irq, NULL, bq25120_interrupt, IRQF_TRIGGER_RISING | IRQF_ONESHOT, "bq25120_irq", bq25120); if (ret) return ret; bq25120->irq = irq; bq25120_irq_enable(bq25120); out: return 0; } static ssize_t bq25120_show_charger_fault(struct device *dev, struct device_attribute *attr, char *buf) { unsigned int val; int ret; struct bq25120_charger *bq25120 = dev_get_drvdata(dev); ret = regmap_read(bq25120->regmap, BQ25120_FAULTS_FAULTMASKS_REG, &val); if (ret < 0) return 0; val &= 0xC0; switch (val) { case BQ25120_VIN_OV: return scnprintf(buf, PAGE_SIZE, "%s\n", "Charge VIN overvoltage fault."); break; case BQ25120_VIN_UV: return scnprintf(buf, PAGE_SIZE, "%s\n", "Charge VIN undervoltage fault."); break; } return 0; } static ssize_t bq25120_show_temp_fault(struct device *dev, struct device_attribute *attr, char *buf) { unsigned int val; int ret; struct bq25120_charger *bq25120 = dev_get_drvdata(dev); ret = regmap_read(bq25120->regmap, BQ25120_TSCONTROL_STATUS_REG, &val); if (ret < 0) return 0; val = (val >> BQ25120_TS_FAULT_SHIFT) & BQ25120_TS_FAULT_MASK; return scnprintf(buf, PAGE_SIZE, "%s\n", temp_status_desc[val]); } static DEVICE_ATTR(charger_fault, S_IRUSR, bq25120_show_charger_fault, NULL); static DEVICE_ATTR(temp_fault, S_IRUSR, bq25120_show_temp_fault, NULL); static struct attribute *bq25120_charger_attr[] = { &dev_attr_charger_fault.attr, &dev_attr_temp_fault.attr, NULL, }; static const struct attribute_group bq25120_attr_group = { .attrs = bq25120_charger_attr, }; /* * Returns the constant charge current programmed * into the charger in mA. */ static int get_const_charge_current(struct bq25120_charger *bq25120) { int ret; unsigned int val, intval; if (!bq25120_is_ps_online(bq25120)) return -ENODATA; ret = regmap_read(bq25120->regmap, bq25120_CHARGE_CURR_TERM_REG, &val); if (ret < 0) return ret; val = (val & bq25120_CHARGE_CURR) >> bq25120_CHARGE_CURR_SHIFT; intval = 500 + val * 100; if (intval >= 3000) intval = 3000; /* set Max as 3A */ dev_info(bq25120->dev, "const charge current is %d mA\n", intval); return intval; } /* * Returns the constant charge voltage programmed * into the charger in mV. */ static int get_const_charge_voltage(struct bq25120_charger *bq25120) { int ret; unsigned int val; int intval; if (!bq25120_is_ps_online(bq25120)) return -ENODATA; ret = regmap_read(bq25120->regmap, bq25120_BATTERY_VOLTAGE_REG, &val); if (ret < 0) return ret; val = (val & bq25120_BATTERY_VOLTAGE) >> bq25120_BATTERY_VOLTAGE_SHIFT; intval = 3500 + val * 20; if (intval >= 4440) /* set MAX as 4.4V */ intval = 4440; dev_info(bq25120->dev, "const charge voltage is=%d mV\n", intval); return intval; } static int bq25120_mains_get_property(struct power_supply *psy, enum power_supply_property prop, union power_supply_propval *val) { struct bq25120_charger *bq25120 = container_of(psy, struct bq25120_charger, mains); int ret; switch (prop) { case POWER_SUPPLY_PROP_ONLINE: val->intval = bq25120->mains_online; break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: ret = get_const_charge_voltage(bq25120); if (ret < 0) return ret; else val->intval = ret; break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: ret = get_const_charge_current(bq25120); if (ret < 0) return ret; else val->intval = ret; break; default: return -EINVAL; } return 0; } static int bq25120_battery_get_property(struct power_supply *psy, enum power_supply_property prop, union power_supply_propval *val) { struct bq25120_charger *bq25120 = container_of(psy, struct bq25120_charger, battery); const struct bq25120_charger_platform_data *pdata = bq25120->pdata; int ret; ret = bq25120_update_ps_status(bq25120); if (ret < 0) return ret; switch (prop) { case POWER_SUPPLY_PROP_STATUS: if (!bq25120_is_ps_online(bq25120)) { val->intval = POWER_SUPPLY_STATUS_DISCHARGING; break; } switch (bq25120_charging_status(bq25120)) { case 0: val->intval = POWER_SUPPLY_STATUS_DISCHARGING; break; case 1: val->intval = POWER_SUPPLY_STATUS_CHARGING; break; case 2: val->intval = POWER_SUPPLY_STATUS_FULL; break; case 3: val->intval = POWER_SUPPLY_STATUS_UNKNOWN; /* FAULT */ break; } break; case POWER_SUPPLY_PROP_CHARGE_TYPE: if (!bq25120_is_ps_online(bq25120)) return -ENODATA; /* We handle charger mode and boost mode. */ switch (bq25120_charging_type(bq25120)) { case 0: val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; break; case 1: val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; break; default: val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; break; } break; case POWER_SUPPLY_PROP_TECHNOLOGY: val->intval = pdata->battery_info.technology; break; case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: val->intval = pdata->battery_info.voltage_min_design; break; case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: val->intval = pdata->battery_info.voltage_max_design; break; case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: val->intval = pdata->battery_info.charge_full_design; break; case POWER_SUPPLY_PROP_MODEL_NAME: val->strval = pdata->battery_info.name; break; default: return -EINVAL; } return 0; } static enum power_supply_property bq25120_battery_properties[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_CHARGE_TYPE, POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_MODEL_NAME, }; static int bq25120_power_supply_register(struct bq25120_charger *bq25120) { static char *battery[] = { "bq25120-battery" }; int ret; if (bq25120->pdata->use_mains) { bq25120->mains.name = "bq25120-mains"; bq25120->mains.type = POWER_SUPPLY_TYPE_MAINS; bq25120->mains.get_property = bq25120_mains_get_property; bq25120->mains.properties = bq25120_charger_properties; bq25120->mains.num_properties = ARRAY_SIZE(bq25120_charger_properties); bq25120->mains.supplied_to = battery; bq25120->mains.num_supplicants = ARRAY_SIZE(battery); ret = power_supply_register(bq25120->dev, &bq25120->mains); if (ret < 0) return ret; } bq25120->battery.name = "bq25120-battery"; bq25120->battery.type = POWER_SUPPLY_TYPE_BATTERY; bq25120->battery.get_property = bq25120_battery_get_property; bq25120->battery.properties = bq25120_battery_properties; bq25120->battery.num_properties = ARRAY_SIZE(bq25120_battery_properties); ret = power_supply_register(bq25120->dev, &bq25120->battery); if (ret < 0) { if (bq25120->pdata->use_mains) power_supply_unregister(&bq25120->mains); return ret; } return 0; } static void bq25120_power_supply_unregister(struct bq25120_charger *bq25120) { power_supply_unregister(&bq25120->battery); if (bq25120->pdata->use_usb) power_supply_unregister(&bq25120->usb); if (bq25120->pdata->use_mains) power_supply_unregister(&bq25120->mains); } static const struct regmap_config bq25120_regmap = { .reg_bits = 8, .val_bits = 8, .max_register = bq25120_MAX_REGISTER, .reg_defaults = bq25120_reg_defs, .num_reg_defaults = ARRAY_SIZE(bq25120_reg_defs), .cache_type = REGCACHE_RBTREE, }; static int bq25120_probe(struct i2c_client *client, const struct i2c_device_id *id) { const struct bq25120_charger_platform_data *pdata; struct device *dev = &client->dev; struct bq25120_charger *bq25120; int ret; pdata = dev->platform_data; if (!pdata) { printk(KERN_NOTICE "In probe - failed with NO PDATA\n"); return -EINVAL; } bq25120 = devm_kzalloc(dev, sizeof(*bq25120), GFP_KERNEL); if (!bq25120) return -ENOMEM; bq25120->dev = &client->dev; bq25120->pdata = client->dev.platform_data; i2c_set_clientdata(client, bq25120); bq25120->regmap = devm_regmap_init_i2c(client, &bq25120_regmap); if (IS_ERR(bq25120->regmap)) return PTR_ERR(bq25120->regmap); ret = bq25120_hw_init(bq25120); if (ret < 0) { dev_err(dev, "failed to initialize bq25120 device: %d\n", ret); goto err_dev; } ret = bq25120_power_supply_register(bq25120); if (ret < 0) { dev_err(dev, "failed to register power supply: %d\n", ret); goto err_dev; } ret = sysfs_create_group(&dev->kobj, &bq25120_attr_group); if (ret < 0) { dev_err(dev, "failed to add charge sysfs: %d\n", ret); goto err_sysfs; } /* * Interrupt pin is optional. If it is connected, we setup the * interrupt support here. */ ret = bq25120_irq_init(bq25120, client->irq); if (ret < 0) { dev_warn(dev, "failed to initialize IRQ: %d\n", ret); dev_warn(dev, "disabling IRQ support\n"); } return 0; err_sysfs: bq25120_power_supply_unregister(bq25120); err_dev: return ret; } static int bq25120_remove(struct i2c_client *client) { struct bq25120_charger *bq25120 = i2c_get_clientdata(client); if (bq25120->irq) { bq25120_irq_disable(bq25120); free_irq(bq25120->irq, bq25120); } sysfs_remove_group(&bq25120->dev->kobj, &bq25120_attr_group); bq25120_power_supply_unregister(bq25120); return 0; } static const struct i2c_device_id bq25120_id[] = { { "bq25120", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, bq25120_id); static struct i2c_driver bq25120_driver = { .driver = { .name = "bq25120", .owner = THIS_MODULE, }, .probe = bq25120_probe, .remove = bq25120_remove, .id_table = bq25120_id, }; module_i2c_driver(bq25120_driver); MODULE_DESCRIPTION("bq25120 battery charger driver"); MODULE_AUTHOR("TI BMS software tools team"); MODULE_LICENSE("TI BSD");