/**************************************************************************** * drivers/power/bq2429x.c * Lower half driver for BQ2429x battery charger * * Copyright (C) 2017 Neil Hancock. All rights reserved. * * Copyright (C) 2017 Haltian Ltd. All rights reserved. * Author: Juha Niskanen * * Copyright (C) 2016 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name NuttX nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ /* The BQ24296M/BQ24296 are Li-Ion Battery management * with Power-Path Management * and USB OTG +5V boost. * * BQ charger with OTG boost and not DSBGA listed below * BQ24190/24192 compared as similar * BQ24259 * BQ24295 * BQ24195 Boost2A/PMID Chg4A5 SolarPwr * BQ24195L Boost1A/PMID Chg2A5 SolarPwr * BQ24296 * BQ24296M Achg=0.1-3A, Vout=4.55 to 5.0V Current Limit 1.5A * BQ24297 * BQ24298 * BQ25601 I2C - to 13.5 Vin * BQ25606 No I2C control * BQ25890H * BQ25892 * BQ25895 * BQ25896 I2C 14V */ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* This driver requires: * * CONFIG_BATTERY_CHARGER - Upper half battery driver support * CONFIG_I2C - I2C support * CONFIG_I2C_BQ2429X - And the driver must be explicitly selected. */ #if defined(CONFIG_BATTERY_CHARGER) && defined(CONFIG_I2C) && \ defined(CONFIG_I2C_BQ2429X) /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /**************************************************************************** * Private ****************************************************************************/ struct bq2429x_dev_s { /* The common part of the battery driver visible to the upper-half driver */ struct battery_charger_dev_s dev; /* Battery charger device */ /* Data fields specific to the lower half BQ2429X driver follow */ FAR struct i2c_master_s *i2c; /* I2C interface */ uint8_t addr; /* I2C address */ uint32_t frequency; /* I2C frequency */ }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ /* I2C support */ static int bq2429x_getreg8(FAR struct bq2429x_dev_s *priv, uint8_t regaddr, FAR uint8_t *val, int num); static int bq2429x_putreg8(FAR struct bq2429x_dev_s *priv, uint8_t regaddr, uint8_t regval); static int bq2429x_reset(FAR struct bq2429x_dev_s *priv); static int bq2429x_watchdog(FAR struct bq2429x_dev_s *priv, bool enable); static int bq2429x_sysoff(FAR struct bq2429x_dev_s *priv); static int bq2429x_en_term(FAR struct bq2429x_dev_s *priv, bool state); static int bq2429x_en_hiz(FAR struct bq2429x_dev_s *priv, bool state); static int bq2429x_en_stat(FAR struct bq2429x_dev_s *priv, bool state); static int bq2429x_setboost_otg_config(FAR struct bq2429x_dev_s *priv, bool state); static int bq2429x_powersupply(FAR struct bq2429x_dev_s *priv, int current); static int bq2429x_setvolt(FAR struct bq2429x_dev_s *priv, int volts); static inline int bq2429x_setcurr(FAR struct bq2429x_dev_s *priv, int req_current); /* Battery driver lower half methods */ static int bq2429x_state(FAR struct battery_charger_dev_s *dev, FAR int *status); static int bq2429x_health(FAR struct battery_charger_dev_s *dev, FAR int *health); static int bq2429x_online(FAR struct battery_charger_dev_s *dev, FAR bool *status); static int bq2429x_voltage(FAR struct battery_charger_dev_s *dev, int value); static int bq2429x_current(FAR struct battery_charger_dev_s *dev, int value); static int bq2429x_input_current(FAR struct battery_charger_dev_s *dev, int value); static int bq2429x_operate(FAR struct battery_charger_dev_s *dev, uintptr_t param); /**************************************************************************** * Private Data ****************************************************************************/ static const struct battery_charger_operations_s g_bq2429xops = { bq2429x_state, bq2429x_health, bq2429x_online, bq2429x_voltage, bq2429x_current, bq2429x_input_current, bq2429x_operate, }; #ifdef CONFIG_DEBUG_BQ2429X static int bq2429x_dump_regs(FAR struct bq2429x_dev_s *priv); # define bq2429x_dump_regs(priv) bq2429x_dump_regs(priv) #else # define bq2429x_dump_regs(priv) #endif /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: bq2429x_getreg8 * * Description: * Read a 8-bit value from a BQ2429x register pair. * * START ACK ACK * REPEATED-START ACK Data0 ACK Data1 NO-ACK STOP * ****************************************************************************/ static int bq2429x_getreg8(FAR struct bq2429x_dev_s *priv, uint8_t regaddr, FAR uint8_t *regval, int num_char) { struct i2c_config_s config; int ret; /* Set up the I2C configuration */ config.frequency = priv->frequency; config.address = priv->addr; config.addrlen = 7; /* Write the register address */ ret = i2c_write(priv->i2c, &config, ®addr, 1); if (ret < 0) { baterr("ERROR: i2c_write failed: %d\n", ret); return ret; } /* Restart and read 8-bit values from the register */ ret = i2c_read(priv->i2c, &config, regval, num_char); if (ret < 0) { baterr("ERROR: i2c_read failed: %d\n", ret); return ret; } return OK; } /**************************************************************************** * Name: bq2429x_putreg8 * * Description: * Write a 8-bit value to a BQ2429x register pair. * * START ACK ACK Data0 ACK Data1 ACK STOP * ****************************************************************************/ static int bq2429x_putreg8(FAR struct bq2429x_dev_s *priv, uint8_t regaddr, uint8_t regval) { struct i2c_config_s config; uint8_t buffer[2]; /* Set up the I2C configuration */ config.frequency = priv->frequency; config.address = priv->addr; config.addrlen = 7; batinfo("addr: %02x regval: %08x\n", regaddr, regval); /* Set up a message to send */ buffer[0] = regaddr; buffer[1] = regval; /* Write the register address followed by the data (no RESTART) */ return i2c_write(priv->i2c, &config, buffer, 2); } #ifdef CONFIG_DEBUG_BQ2429X static int (bq2429x_dump_regs) (FAR struct bq2429x_dev_s * priv) { int ret; uint8_t value = 0; ret = bq2429x_getreg8(priv, BQ2429X_REG00, &value, 1); batinfo("REG#0: 0x%08X\n", value); ret |= bq2429x_getreg8(priv, BQ2429X_REG01, &value, 1); batinfo("REG#1: 0x%08X\n", value); ret |= bq2429x_getreg8(priv, BQ2429X_REG02, &value, 1); batinfo("REG#2: 0x%08X\n", value); ret |= bq2429x_getreg8(priv, BQ2429X_REG03, &value, 1); batinfo("REG#3: 0x%08X\n", value); ret |= bq2429x_getreg8(priv, BQ2429X_REG04, &value, 1); batinfo("REG#4: 0x%08X\n", value); ret |= bq2429x_getreg8(priv, BQ2429X_REG05, &value, 1); batinfo("REG#5: 0x%08X\n", value); ret |= bq2429x_getreg8(priv, BQ2429X_REG06, &value, 1); batinfo("REG#6: 0x%08X\n", value); ret |= bq2429x_getreg8(priv, BQ2429X_REG07, &value, 1); batinfo("REG#7: 0x%08X\n", value); ret |= bq2429x_getreg8(priv, BQ2429X_REG08, &value, 1); batinfo("REG#8: 0x%08X\n", value); /* Not reading fault register. */ ret |= bq2429x_getreg8(priv, BQ2429X_REG0A, &value, 1); batinfo("REG#10: 0x%08X\n", value); return ret; } #endif /**************************************************************************** * Name: bq2429x_reset * * Description: * Reset the BQ2429x * ****************************************************************************/ static int bq2429x_reset(FAR struct bq2429x_dev_s *priv) { int ret; uint8_t regval; /* Read current register value */ ret = bq2429x_getreg8(priv, BQ2429X_REG01, ®val, 1); if (ret < 0) { baterr("ERROR: Error reading from BQ2429X! Error = %d\n", ret); return ret; } /* Send reset command */ regval |= BQ2429XR1_REG_RESET; ret = bq2429x_putreg8(priv, BQ2429X_REG01, regval); if (ret < 0) { baterr("ERROR: Error writing to BQ2429X! Error = %d\n", ret); return ret; } /* Wait a little bit to clear registers */ nxsig_usleep(500); #if 0 /* There is a BUG in BQ2429X the RESET bit is always read as 1 */ regval &= ~(BQ2429X_RESET); ret = bq2429x_putreg8(priv, BQ2429X_REG01, regval); if (ret < 0) { baterr("ERROR: Error writing to BQ2429X! Error = %d\n", ret); return ret; } #endif return OK; } /**************************************************************************** * Name: bq2429x_watchdog * * Description: * Enable/Disable the BQ2429x watchdog * ****************************************************************************/ static int bq2429x_watchdog(FAR struct bq2429x_dev_s *priv, bool enable) { int ret; uint8_t regval; ret = bq2429x_getreg8(priv, BQ2429X_REG05, ®val, 1); if (ret < 0) { baterr("ERROR: Error reading from BQ2429X! Error = %d\n", ret); return ret; } if (enable) { /* Hw Default is 40Sec so use that for time being */ regval &= ~(BQ2429XR5_WATCHDOG_MASK); regval |= BQ2429XR5_WATCHDOG_040SEC; } else { /* 0 is disable */ regval &= ~(BQ2429XR5_WATCHDOG_MASK); } ret = bq2429x_putreg8(priv, BQ2429X_REG05, regval); if (ret < 0) { baterr("ERROR: Error writing to BQ2429X! Error = %d\n", ret); return ret; } return OK; } /**************************************************************************** * Name: bq2429x_sysoff * * Description: * Turn the internal battery FET off in order to reduce the leakage from * the BAT pin. Note that this disconnects the battery from the system. * ****************************************************************************/ static int bq2429x_sysoff(FAR struct bq2429x_dev_s *priv) { int ret; uint8_t value = 0; ret = bq2429x_getreg8(priv, BQ2429X_REG07, &value, 1); batinfo("REG7 read value: 0x%08X\n", value); value |= BQ2429XR7_BATFET_DISABLE; ret |= bq2429x_putreg8(priv, BQ2429X_REG07, value); return ret; } /**************************************************************************** * Name: bq2429x_syson * * Description: * Turn the internal battery FET on. * ****************************************************************************/ static int bq2429x_syson(FAR struct bq2429x_dev_s *priv) { int ret; uint8_t value = 0; ret = bq2429x_getreg8(priv, BQ2429X_REG07, &value, 1); batinfo("REG7 read value: 0x%08X\n", value); value &= ~BQ2429XR7_BATFET_DISABLE; ret |= bq2429x_putreg8(priv, BQ2429X_REG07, value); return ret; } /**************************************************************************** * Name: bq2429x_en_term * * Description: * Enable charger termination. When termination is disabled, there are no * indications of the charger terminating (i.e. STAT pin or registers). * ****************************************************************************/ static int bq2429x_en_term(FAR struct bq2429x_dev_s *priv, bool state) { uint8_t regval; int ret; ret = bq2429x_getreg8(priv, BQ2429X_REG05, ®val, 1); if (ret < 0) { baterr("ERROR: Error reading from BQ2429X REG5! Error = %d\n", ret); return ret; } batinfo("en_term: REG05 %02X EN_TERM=%d\n", regval, !!(regval & BQ2429XR5_EN_TERM)); /* Clear previous and set new value */ if (state) { regval |= BQ2429XR5_EN_TERM; } else { regval &= ~BQ2429XR5_EN_TERM; } ret = bq2429x_putreg8(priv, BQ2429X_REG05, regval); if (ret < 0) { baterr("ERROR: Error writing to BQ2429XR5! Error = %d\n", ret); return ret; } return OK; } /**************************************************************************** * Name: bq2429x_en_hiz * * Description: * Enable high-impedance mode. Sets the charger IC into low power standby * mode. * ****************************************************************************/ static int bq2429x_en_hiz(FAR struct bq2429x_dev_s *priv, bool state) { uint8_t regval; int ret; ret = bq2429x_getreg8(priv, BQ2429X_REG00, ®val, 1); if (ret < 0) { baterr("ERROR: Error reading from BQ2429X REG0! Error = %d\n", ret); return ret; } batinfo("en_hiz: REG00 %02X EN_HIZ=%d\n", regval, !!(regval & BQ2429XR1_EN_HIZ)); /* Clear previous and set new value */ if (state) { regval |= BQ2429XR1_EN_HIZ; } else { regval &= ~BQ2429XR1_EN_HIZ; } ret = bq2429x_putreg8(priv, BQ2429X_REG00, regval); if (ret < 0) { baterr("ERROR: Error writing to BQ2429XR0! Error = %d\n", ret); return ret; } return OK; } /**************************************************************************** * Name: bq2429x_en_stat * * Description: * Enable interrupts. * ****************************************************************************/ static int bq2429x_en_stat(FAR struct bq2429x_dev_s *priv, bool state) { uint8_t regval; int ret; ret = bq2429x_getreg8(priv, BQ2429X_REG07, ®val, 1); if (ret < 0) { baterr("ERROR: Error reading from BQ2429X REG7! Error = %d\n", ret); return ret; } batinfo("int stat: REG07 %02X INT_MASK1=%d INT_MASK0=%d\n", regval, !!(regval & BQ2429XR7_INT_MASK1), !!(regval & BQ2429XR7_INT_MASK0)); /* We always set or clear both interrupts together. */ if (state) { regval |= (BQ2429XR7_INT_MASK0 | BQ2429XR7_INT_MASK1); } else { regval &= ~(BQ2429XR7_INT_MASK0 | BQ2429XR7_INT_MASK1); } ret = bq2429x_putreg8(priv, BQ2429X_REG07, regval); if (ret < 0) { baterr("ERROR: Error writing to BQ2429XR7! Error = %d\n", ret); return ret; } return OK; } /**************************************************************************** * Name: bq2429x_setboost_otg_config * * Description: * Set the device boost mode. * ****************************************************************************/ static int bq2429x_setboost_otg_config(FAR struct bq2429x_dev_s *priv, bool state) { uint8_t regval; int ret; ret = bq2429x_getreg8(priv, BQ2429X_REG01, ®val, 1); if (ret < 0) { baterr("ERROR: Error reading from BQ2429X! Error = %d\n", ret); return ret; } /* Clear previous current and set new value */ if (state) { /* Set to Boost disable Charge */ regval = BQ2429XR1_OTG_CONFIG | (regval & ~BQ2429XR1_CHG_CONFIG); } else { /* Set to Charge disable Boost */ regval = BQ2429XR1_CHG_CONFIG | (regval & ~BQ2429XR1_OTG_CONFIG); } ret = bq2429x_putreg8(priv, BQ2429X_REG01, regval); if (ret < 0) { baterr("ERROR: Error writing to BQ2429X! Error = %d\n", ret); return ret; } #define BST_CONFIG_MASK (BQ2429XR1_CHG_CONFIG | BQ2429XR1_OTG_CONFIG) batinfo("otg_config: REG01 %02X Boost=%d\n", regval, ((BQ2429XR1_OTG_CONFIG == (regval & BST_CONFIG_MASK)) ? 1 : 0)); return OK; } /**************************************************************************** * Name: bq2429x_state * * Description: * Return the current battery management state * ****************************************************************************/ static int bq2429x_state(FAR struct battery_charger_dev_s *dev, FAR int *status) { FAR struct bq2429x_dev_s *priv = (FAR struct bq2429x_dev_s *)dev; uint8_t regval; bool isfault = false; int ret; ret = bq2429x_getreg8(priv, BQ2429X_REG08, ®val, 1); if (ret < 0) { *status = BATTERY_UNKNOWN; return ret; } if (regval & BQ2429XR8_DPM_STAT) { isfault = true; batinfo("DPM detected!\n"); } if (!(regval & BQ2429XR8_PG_STAT)) { isfault = true; batinfo("Power is not good!\n"); } if (regval & BQ2429XR8_THERM_STAT) { isfault = true; batinfo("Thermal regulation!\n"); } if (regval & BQ2429XR8_VSYS_STAT) { isfault = true; batinfo("VSYSMIN regulation! Battery is too low!\n"); } regval &= BQ2429XR8_CHRG_STAT_MASK; /* TODO: should we check REG09 faults here as well? */ if (isfault) { *status = BATTERY_FAULT; } /* Is the charging done? */ else if (regval == BQ2429XR8_CHRG_STAT_DONE) { *status = BATTERY_FULL; } /* Is the charging in progress? */ else if (regval == BQ2429XR8_CHRG_STAT_PRECHG || regval == BQ2429XR8_CHRG_STAT_FASTCHG) { *status = BATTERY_CHARGING; } /* Is the charging ready? */ else if (regval == BQ2429XR8_CHRG_STAT_NONE) { *status = BATTERY_IDLE; } return OK; } /**************************************************************************** * Name: bq2429x_health * * Description: * Return the current battery health state * * Note: if more than one fault happened the user needs to call this ioctl * again to read a new fault, repeat until receive a BATTERY_HEALTH_GOOD. * ****************************************************************************/ static int bq2429x_health(FAR struct battery_charger_dev_s *dev, FAR int *health) { FAR struct bq2429x_dev_s *priv = (FAR struct bq2429x_dev_s *)dev; uint8_t regval; int ret; ret = bq2429x_getreg8(priv, BQ2429X_REG09, ®val, 1); if (ret < 0) { *health = BATTERY_HEALTH_UNKNOWN; return ret; } switch (regval & BQ2429XR9_CHRG_FAULT_MASK) { case BQ2429XR9_CHRG_FAULT_TIMER: *health = BATTERY_HEALTH_SAFE_TMR_EXP; batinfo("battery safety timer expiration!\n"); return OK; case BQ2429XR9_CHRG_FAULT_INPUT: *health = BATTERY_HEALTH_DISCONNECTED; batinfo("input disconnect/fault!\n"); return OK; case BQ2429XR9_CHRG_FAULT_THERMAL: *health = BATTERY_HEALTH_OVERHEAT; batinfo("thermal shutdown!\n"); return OK; case BQ2429XR9_CHRG_FAULT_NORMAL: *health = BATTERY_HEALTH_GOOD; break; /* No return, check for other faults */ default: DEBUGPANIC(); *health = BATTERY_HEALTH_UNKNOWN; return OK; } if (regval & BQ2429XR9_BAT_FAULT) { *health = BATTERY_HEALTH_OVERVOLTAGE; batinfo("battery OVP!\n"); } else if (regval & BQ2429XR9_NTC_FAULT2_HOT) { *health = BATTERY_HEALTH_OVERHEAT; batinfo("NTC hot!\n"); } else if (regval & BQ2429XR9_NTC_FAULT1_COLD) { *health = BATTERY_HEALTH_COLD; batinfo("NTC cold!\n"); } else if (regval & BQ2429XR9_WATCHDOG_FAULT) { *health = BATTERY_HEALTH_WD_TMR_EXP; batinfo("watchdog expiration!\n"); } else if (regval & BQ2429XR9_OTG_FAULT) { *health = BATTERY_HEALTH_UNSPEC_FAIL; batinfo("VBUS overload or OVP!\n"); } return OK; } /**************************************************************************** * Name: bq2429x_online * * Description: * Return true if the battery is online * ****************************************************************************/ static int bq2429x_online(FAR struct battery_charger_dev_s *dev, FAR bool *status) { FAR struct bq2429x_dev_s *priv = (FAR struct bq2429x_dev_s *)dev; uint8_t regval; int ret; ret = bq2429x_getreg8(priv, BQ2429X_REG00, ®val, 1); if (ret < 0) { *status = false; return ret; } if (regval & BQ2429XR1_EN_HIZ) { /* Device is HIGH IMPEDANCE battery offline */ *status = false; } else { *status = true; } return OK; } /**************************************************************************** * Name: bq2429x_powersupply * * Description: * Set the Power Supply Current Limit. * ****************************************************************************/ static int bq2429x_powersupply(FAR struct bq2429x_dev_s *priv, int current) { uint8_t regval; uint8_t idx; int ret; switch (current) { case 100: idx = BQ2429XR0_INLIM_0100MA; break; case 150: idx = BQ2429XR0_INLIM_0150MA; break; case 500: idx = BQ2429XR0_INLIM_0500MA; break; case 900: idx = BQ2429XR0_INLIM_0900MA; break; case 1000: idx = BQ2429XR0_INLIM_1000MA; break; case 1500: idx = BQ2429XR0_INLIM_1500MA; break; case 2000: idx = BQ2429XR0_INLIM_2000MA; break; case 3000: idx = BQ2429XR0_INLIM_3000MA; break; default: baterr("ERROR: Current not supported, setting default to 100mA.!\n"); idx = BQ2429XR0_INLIM_0100MA; break; } /* Read current register */ ret = bq2429x_getreg8(priv, BQ2429X_REG00, ®val, 1); if (ret < 0) { baterr("ERROR: Error reading from BQ2429X! Error = %d\n", ret); return ret; } /* Clear previous current and set new value */ regval &= ~(BQ2429XR0_INLIM_MASK); regval |= idx; ret = bq2429x_putreg8(priv, BQ2429X_REG00, regval); if (ret < 0) { baterr("ERROR: Error writing to BQ2429X! Error = %d\n", ret); return ret; } return OK; } /**************************************************************************** * Name: bq2429x_setvolt * * Description: * Set the voltage level to charge the battery. Voltage value in mV. * ****************************************************************************/ static int bq2429x_setvolt(FAR struct bq2429x_dev_s *priv, int req_volts) { uint8_t regval; int idx; int ret; /* Verify if voltage is in the acceptable range */ if (req_volts < BQ2429X_VOLTCHG_MIN || req_volts > BQ2429X_VOLTCHG_MAX) { baterr("ERROR: Voltage %d mV is out of range.\n", req_volts); return -EINVAL; } ret = bq2429x_getreg8(priv, BQ2429X_REG04, ®val, 1); if (ret < 0) { baterr("ERROR: Error reading from BQ2429X! Error = %d\n", ret); return ret; } /* Charge Voltage starts at _MIN and increases in steps of 16mV */ idx = req_volts - BQ2429X_VOLTCHG_MIN; idx = idx / 16; /* Clear previous voltage */ regval &= ~(BQ2429XR4_VREG_MASK); regval |= (idx << BQ2429XR4_VREG_SHIFT); ret = bq2429x_putreg8(priv, BQ2429X_REG04, regval); if (ret < 0) { baterr("ERROR: Error writing to BQ2429X! Error = %d\n", ret); return ret; } return OK; } /**************************************************************************** * Name: bq2429x_setcurr * * Description: * Set the current to charge the battery. Current value in mA. * ****************************************************************************/ static inline int bq2429x_setcurr(FAR struct bq2429x_dev_s *priv, int req_current) { uint8_t regval; bool force_20pct = false; int idx; int ret; /* If requested current is below the minimum for fast charge, * configure for trickle charging. Trickle charging uses 20% * of current programmed to ICHG bits, so we multiply by five. */ if (req_current < BQ2429X_CURRCHG_MIN) { force_20pct = true; req_current *= 5; } /* Verify if current is in the acceptable range. */ if (req_current < BQ2429X_CURRCHG_MIN || req_current > BQ2429X_CURRCHG_MAX) { baterr("ERROR: Current %d mA is out of range.\n", force_20pct ? req_current / 5 : req_current); return -EINVAL; } /* According to the "Termination when REG02[0] = 1" section of * the bq24296M datasheet, the trickle charge could be less than * the termination current so it is recommended to turn off the * termination function. * * This means that the user will have to manually turn off the * charging when in 20% mode. Otherwise there could be battery * damage if we continuously trickle charge full battery until * safety timer finally stops it after 10 hours (timer is running * in half speed in 20% mode.) */ ret = bq2429x_en_term(priv, !force_20pct); if (ret < 0) { return ret; } /* Read previous current and charge type. */ ret = bq2429x_getreg8(priv, BQ2429X_REG02, ®val, 1); if (ret < 0) { baterr("ERROR: Error reading from BQ2429X! Error = %d\n", ret); return ret; } /* Current starts at _MIN mA and increases in steps of 64mA. */ idx = req_current - BQ2429X_CURRCHG_MIN; idx = idx / 64; /* Clear previous current and charge type and set new values. */ regval &= ~(BQ2429XR2_ICHG_MASK); regval |= (idx << BQ2429XR2_ICHG_SHIFT); if (force_20pct) { regval |= BQ2429XR2_FORCE_20PCT; } else { regval &= ~BQ2429XR2_FORCE_20PCT; } ret = bq2429x_putreg8(priv, BQ2429X_REG02, regval); if (ret < 0) { baterr("ERROR: Error writing to BQ2429X! Error = %d\n", ret); return ret; } return OK; } /**************************************************************************** * Name: bq2429x_voltage * * Description: * Set battery charger voltage * ****************************************************************************/ static int bq2429x_voltage(FAR struct battery_charger_dev_s *dev, int value) { FAR struct bq2429x_dev_s *priv = (FAR struct bq2429x_dev_s *)dev; int ret; /* Set voltage to battery charger */ ret = bq2429x_setvolt(priv, value); if (ret < 0) { baterr("ERROR: Error setting voltage to BQ2429X! Error = %d\n", ret); return ret; } return OK; } /**************************************************************************** * Name: bq2429x_current * * Description: * Set the battery charger current rate for charging ****************************************************************************/ static int bq2429x_current(FAR struct battery_charger_dev_s *dev, int value) { FAR struct bq2429x_dev_s *priv = (FAR struct bq2429x_dev_s *)dev; int ret; /* Set current to battery charger */ ret = bq2429x_setcurr(priv, value); if (ret < 0) { baterr("ERROR: Error setting current to BQ2429X! Error = %d\n", ret); return ret; } return OK; } /**************************************************************************** * Name: bq2429x_input_current * * Description: * Set the power-supply input current limit * ****************************************************************************/ static int bq2429x_input_current(FAR struct battery_charger_dev_s *dev, int value) { FAR struct bq2429x_dev_s *priv = (FAR struct bq2429x_dev_s *)dev; int ret; ret = bq2429x_powersupply(priv, value); if (ret < 0) { baterr("ERROR: Failed to set BQ2429x power supply input limit: %d\n", ret); return ret; } return OK; } /**************************************************************************** * Name: bq2429x_operate * * Description: * Do miscellaneous battery operation. There are numerous options that are * configurable on the bq2429x that go beyond what the NuttX battery * charger API provide access to. This operate() function allows changing * some of them. * * REG00 EN_HIZ * REG01[1] BOOST_LIM 1A/1.5A Default:1.5A * REG01 CHG_CONFIG * REG02[1] BCOLD * REG02[1] FORCE_20PCT * REG05[1] EN_TERM Charging Termination Enable * REG05[2] WATCHDOG I2C Watchdog Setting 5-20Hrs default 12Hrs * REG05[1] EN_TIMER Charging safety timer * REG05[2] CHG_TIMER Fast Charge Timer Setting * TREG[2] Thermal Regulation Threshold * REG07[1] DPDM_EN Force DPDM detection when VBUS poower is present * REG07[1] TMR2X_EN Safety Timer Slowed during DPM or thermal regulation * REG07[1] BATFET_Disable Turnon off BATFET (Q4) * REG07[1] INT_MASK1 - Allow INT on CHRG_FAULT Default: 1 Allow * REG07[1] INT_MASK0 - Allow INT on BAT_FAULT Default: 1 Allow * * Set by other battery charger methods: * REG00[3] InputCurrent Limit 100mA 3000mA with PSEL * REG02[1] ICHG Fast Charge Current Limit, 512-3008mA Default 2048mA * REG03[4] IPRECHG Pre-charge current Limit 128-2048mA Default: 128mA * REG03[3] ITERM Termination Current Limit 128-1024mA Default: 256mA * * System output voltage related: * REG00[4] VINDPM 3.88-5.08V Default:4.36V * REG01[3] Min Sys Voltage Range3.0-3.7V * REG04[6] Charge Voltage Limit 3504-4400mV Default: 4208mV * REG04[1] Battery Recharge Threshold 100/300mV Default 100mV * REG06[4] BOOSTV - Boost Voltage 4550-5510mV Default 4998mV * REG06[2] BHOT Boost Mode Temperature Monitor * ****************************************************************************/ static int bq2429x_operate(FAR struct battery_charger_dev_s *dev, uintptr_t param) { FAR struct bq2429x_dev_s *priv = (FAR struct bq2429x_dev_s *)dev; FAR struct batio_operate_msg_s *msg = (FAR struct batio_operate_msg_s *)param; int op; int value; int ret = OK; bq2429x_dump_regs(priv); op = msg->operate_type; value = (int)msg->u32; switch (op) { case BATIO_OPRTN_BOOST: ret = bq2429x_setboost_otg_config(priv, true); break; case BATIO_OPRTN_CHARGE: ret = bq2429x_setboost_otg_config(priv, false); break; case BATIO_OPRTN_EN_TERM: ret = bq2429x_en_term(priv, (bool)value); break; case BATIO_OPRTN_HIZ: ret = bq2429x_en_hiz(priv, (bool)value); /* Also need to set to 100mA USB host if the battery above Vbatgd? */ break; case BATIO_OPRTN_SYSOFF: ret = bq2429x_sysoff(priv); break; case BATIO_OPRTN_SYSON: ret = bq2429x_syson(priv); break; case BATIO_OPRTN_RESET: ret = bq2429x_reset(priv); break; case BATIO_OPRTN_WDOG: ret = bq2429x_watchdog(priv, (bool)value); break; default: baterr("Unsupported opt: 0x%X\n", op); ret = -EINVAL; break; } return ret; } /**************************************************************************** * Name: bq2429x_initialize * * Description: * Initialize the BQ2429x battery driver and return an instance of the * lower-half interface that may be used with battery_charger_register(). * * This driver requires: * * CONFIG_BATTERY_CHARGER - Upper half battery driver support * CONFIG_I2C - I2C support * CONFIG_I2C_BQ2429X - And the driver must be explicitly selected. * * Input Parameters: * i2c - An instance of the I2C interface to use to communicate with * the BQ2429x * addr - The I2C address of the BQ2429x (Better be 0x6B). * frequency - The I2C frequency * current - The input current our power-supply can offer to charger * * Returned Value: * A pointer to the initialized lower-half driver instance. A NULL pointer * is returned on a failure to initialize the BQ2429x lower half. * ****************************************************************************/ FAR struct battery_charger_dev_s * bq2429x_initialize(FAR struct i2c_master_s *i2c, uint8_t addr, uint32_t frequency, int current) { FAR struct bq2429x_dev_s *priv; int ret; /* Initialize the BQ2429x device structure */ priv = kmm_zalloc(sizeof(struct bq2429x_dev_s)); if (priv) { /* Initialize the BQ2429x device structure */ priv->dev.ops = &g_bq2429xops; priv->i2c = i2c; priv->addr = addr; priv->frequency = frequency; /* Reset the BQ2429x */ ret = bq2429x_reset(priv); if (ret < 0) { baterr("ERROR: Failed to reset the BQ2429x: %d\n", ret); kmm_free(priv); return NULL; } /* Disable watchdog otherwise BQ2429x returns to StandAlone mode */ ret = bq2429x_watchdog(priv, false); if (ret < 0) { baterr("ERROR: Failed to disable BQ2429x watchdog: %d\n", ret); kmm_free(priv); return NULL; } /* Define the current that our power supply can offer to the charger. */ ret = bq2429x_powersupply(priv, current); if (ret < 0) { baterr("ERROR: Failed to set BQ2429x power supply: %d\n", ret); kmm_free(priv); return NULL; } /* Disable all interrupts. */ ret = bq2429x_en_stat(priv, false); if (ret < 0) { baterr("ERROR: Failed to disable BQ2429x interrupts: %d\n", ret); kmm_free(priv); return NULL; } } return (FAR struct battery_charger_dev_s *)priv; } #endif /* CONFIG_BATTERY_CHARGER && CONFIG_I2C && CONFIG_I2C_BQ2429X */