nuttx/drivers/power/bq2425x.c
Gregory Nutt 936df1bcb5 Adds new OS internal functions nxsig_sleep() and nxsig_usleep. These differ from the standard sleep() and usleep() in that (1) they don't cause cancellation points, and (2) don't set the errno variable (if applicable). All calls to sleep() and usleep() changed to calls to nxsig_sleep() and nxsig_usleep().
Squashed commit of the following:

    Change all calls to usleep() in the OS proper to calls to nxsig_usleep()

    sched/signal:  Add a new OS internal function nxsig_usleep() that is functionally equivalent to usleep() but does not cause a cancellaption point and does not modify the errno variable.

    sched/signal:  Add a new OS internal function nxsig_sleep() that is functionally equivalent to sleep() but does not cause a cancellaption point.
2017-10-06 10:15:01 -06:00

832 lines
22 KiB
C

/****************************************************************************
* drivers/power/bq2425x.c
* Lower half driver for BQ2425x battery charger
*
* Copyright (C) 2015 Alan Carvalho de Assis. All rights reserved.
* Author: Alan Carvalho de Assis <acassis@gmail.com>
*
* Copyright (C) 2016 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* 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 BQ24250/BQ24251 are Li-Ion Battery Charger with Power-Path Management.
* It can be configured to Input Current Limit up to 2A.
*/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdint.h>
#include <stdbool.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/kmalloc.h>
#include <nuttx/signal.h>
#include <nuttx/i2c/i2c_master.h>
#include <nuttx/power/battery_charger.h>
#include <nuttx/power/battery_ioctl.h>
#include "bq2425x.h"
/* This driver requires:
*
* CONFIG_BATTERY_CHARGER - Upper half battery driver support
* CONFIG_I2C - I2C support
* CONFIG_I2C_BQ2425X - And the driver must be explictly selected.
*/
#if defined(CONFIG_BATTERY_CHARGER) && defined(CONFIG_I2C) && \
defined(CONFIG_I2C_BQ2425X)
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Debug ********************************************************************/
#ifdef CONFIG_DEBUG_BQ2425X
# define baterr _err
# define batreg _err
#else
# ifdef CONFIG_CPP_HAVE_VARARGS
# define baterr(x...)
# define batreg(x...)
# else
# define baterr (void)
# define batreg (void)
# endif
#endif
/****************************************************************************
* Private
****************************************************************************/
struct bq2425x_dev_s
{
/* The common part of the battery driver visible to the upper-half driver */
FAR const struct battery_charger_operations_s *ops; /* Battery operations */
sem_t batsem; /* Enforce mutually exclusive access */
/* Data fields specific to the lower half BQ2425x 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 bq2425x_getreg8(FAR struct bq2425x_dev_s *priv, uint8_t regaddr,
FAR uint8_t *regval);
static int bq2425x_putreg8(FAR struct bq2425x_dev_s *priv, uint8_t regaddr,
uint8_t regval);
static inline int bq2425x_getreport(FAR struct bq2425x_dev_s *priv,
uint8_t *report);
static inline int bq2425x_reset(FAR struct bq2425x_dev_s *priv);
static inline int bq2425x_watchdog(FAR struct bq2425x_dev_s *priv, bool enable);
static inline int bq2425x_powersupply(FAR struct bq2425x_dev_s *priv, int current);
static inline int bq2425x_setvolt(FAR struct bq2425x_dev_s *priv, int volts);
static inline int bq2425x_setcurr(FAR struct bq2425x_dev_s *priv, int current);
/* Battery driver lower half methods */
static int bq2425x_state(struct battery_charger_dev_s *dev, int *status);
static int bq2425x_health(struct battery_charger_dev_s *dev, int *health);
static int bq2425x_online(struct battery_charger_dev_s *dev, bool *status);
static int bq2425x_voltage(struct battery_charger_dev_s *dev, int value);
static int bq2425x_current(struct battery_charger_dev_s *dev, int value);
static int bq2425x_input_current(struct battery_charger_dev_s *dev, int value);
static int bq2425x_operate(struct battery_charger_dev_s *dev, uintptr_t param);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct battery_charger_operations_s g_bq2425xops =
{
bq2425x_state,
bq2425x_health,
bq2425x_online,
bq2425x_voltage,
bq2425x_current,
bq2425x_input_current,
bq2425x_operate
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: bq2425x_getreg8
*
* Description:
* Read a 8-bit value from a BQ2425x register pair.
*
* START <I2C write address> ACK <Reg address> ACK
* REPEATED-START <I2C read address> ACK Data0 ACK Data1 NO-ACK STOP
*
****************************************************************************/
static int bq2425x_getreg8(FAR struct bq2425x_dev_s *priv, uint8_t regaddr,
FAR uint8_t *regval)
{
struct i2c_config_s config;
uint8_t val;
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, &regaddr, 1);
if (ret < 0)
{
baterr("ERROR: i2c_write failed: %d\n", ret);
return ret;
}
/* Restart and read 8-bits from the register */
ret = i2c_read(priv->i2c, &config, &val, 1);
if (ret < 0)
{
baterr("ERROR: i2c_read failed: %d\n", ret);
return ret;
}
/* Copy 8-bit value to be returned */
*regval = val;
return OK;
}
/****************************************************************************
* Name: bq2425x_putreg8
*
* Description:
* Write a 8-bit value to a BQ2425x register pair.
*
* START <I2C write address> ACK <Reg address> ACK Data0 ACK Data1 ACK STOP
*
****************************************************************************/
static int bq2425x_putreg8(FAR struct bq2425x_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;
batreg("addr: %02x regval: %08x\n", regaddr, regval);
/* Set up a 3 byte 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);
}
/****************************************************************************
* Name: bq2425x_getreport
*
* Description:
* Read the BQ2425X Register #1 (status and fault)
*
****************************************************************************/
static inline int bq2425x_getreport(FAR struct bq2425x_dev_s *priv,
uint8_t *report)
{
uint8_t regval = 0;
int ret;
ret = bq2425x_getreg8(priv, BQ2425X_REG_1, &regval);
if (ret == OK)
{
*report = regval;
}
return ret;
}
/****************************************************************************
* Name: bq2425x_reset
*
* Description:
* Reset the BQ2425x
*
****************************************************************************/
static inline int bq2425x_reset(FAR struct bq2425x_dev_s *priv)
{
int ret;
uint8_t regval;
/* Read current register value */
ret = bq2425x_getreg8(priv, BQ2425X_REG_2, &regval);
if (ret < 0)
{
baterr("ERROR: Error reading from BQ2425X! Error = %d\n", ret);
return ret;
}
/* Send reset command */
regval |= BQ2425X_RESET;
ret = bq2425x_putreg8(priv, BQ2425X_REG_2, regval);
if (ret < 0)
{
baterr("ERROR: Error writing to BQ2425X! Error = %d\n", ret);
return ret;
}
/* Wait a little bit to clear registers */
nxsig_usleep(500);
/* There is a BUG in BQ2425X the RESET bit is always read as 1 */
regval &= ~(BQ2425X_RESET);
ret = bq2425x_putreg8(priv, BQ2425X_REG_2, regval);
if (ret < 0)
{
baterr("ERROR: Error writing to BQ2425X! Error = %d\n", ret);
return ret;
}
return OK;
}
/****************************************************************************
* Name: bq2425x_watchdog
*
* Description:
* Enable/Disable the BQ2425x watchdog
*
****************************************************************************/
static inline int bq2425x_watchdog(FAR struct bq2425x_dev_s *priv, bool enable)
{
int ret;
uint8_t regval;
ret = bq2425x_getreg8(priv, BQ2425X_REG_1, &regval);
if (ret < 0)
{
baterr("ERROR: Error reading from BQ2425X! Error = %d\n", ret);
return ret;
}
if (enable)
{
regval |= BQ2425X_WD_EN;
}
else
{
regval &= ~(BQ2425X_WD_EN);
}
ret = bq2425x_putreg8(priv, BQ2425X_REG_1, regval);
if (ret < 0)
{
baterr("ERROR: Error writing to BQ2425X! Error = %d\n", ret);
return ret;
}
return OK;
}
/****************************************************************************
* Name: bq2425x_state
*
* Description:
* Return the current battery state
*
****************************************************************************/
static int bq2425x_state(struct battery_charger_dev_s *dev, int *status)
{
FAR struct bq2425x_dev_s *priv = (FAR struct bq2425x_dev_s *)dev;
uint8_t regval = 0;
int ret;
ret = bq2425x_getreport(priv, &regval);
if (ret < 0)
{
*status = BATTERY_UNKNOWN;
return ret;
}
regval &= BQ2425X_STAT_MASK;
/* Is there some fault in the battery? */
if (regval == BQ2425X_STAT_FAULT)
{
*status = BATTERY_FAULT;
}
/* Is the charging done? */
else if (regval == BQ2425X_STAT_CHG_DONE)
{
*status = BATTERY_FULL;
}
/* Is the charging in progress? */
else if (regval == BQ2425X_STAT_CHG_PROGRESS)
{
*status = BATTERY_CHARGING;
}
/* Is the charging ready? */
else if (regval == BQ2425X_STAT_READY)
{
*status = BATTERY_IDLE;
}
return OK;
}
/****************************************************************************
* Name: bq2425x_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 bq2425x_health(struct battery_charger_dev_s *dev, int *health)
{
FAR struct bq2425x_dev_s *priv = (FAR struct bq2425x_dev_s *)dev;
uint8_t regval = 0;
int ret;
ret = bq2425x_getreport(priv, &regval);
if (ret < 0)
{
*health = BATTERY_HEALTH_UNKNOWN;
return ret;
}
regval &= BQ2425X_FAULT_MASK;
switch (regval)
{
case BQ2425X_FAULT_NORMAL:
*health = BATTERY_HEALTH_GOOD;
break;
case BQ2425X_FAULT_SLEEP:
case BQ2425X_FAULT_INP_OVP:
case BQ2425X_FAULT_INP_UVLO:
case BQ2425X_FAULT_ISET_SHORT:
case BQ2425X_FAULT_INP_LDO_LOW:
*health = BATTERY_HEALTH_UNSPEC_FAIL;
break;
case BQ2425X_FAULT_BAT_OVP:
*health = BATTERY_HEALTH_OVERVOLTAGE;
break;
case BQ2425X_FAULT_BAT_TEMP:
case BQ2425X_FAULT_THERM_SHUT:
*health = BATTERY_HEALTH_OVERHEAT;
break;
case BQ2425X_FAULT_TIMER:
*health = BATTERY_HEALTH_SAFE_TMR_EXP;
break;
case BQ2425X_FAULT_NO_BATTERY:
*health = BATTERY_HEALTH_DISCONNECTED;
break;
default:
*health = BATTERY_HEALTH_UNKNOWN;
break;
}
return OK;
}
/****************************************************************************
* Name: bq2425x_online
*
* Description:
* Return true if the battery is online
*
****************************************************************************/
static int bq2425x_online(struct battery_charger_dev_s *dev, bool *status)
{
/* There is no concept of online/offline in this driver */
*status = true;
return OK;
}
/****************************************************************************
* Name: bq2425x_powersupply
*
* Description:
* Set the Power Supply Current Limit.
*
****************************************************************************/
static inline int bq2425x_powersupply(FAR struct bq2425x_dev_s *priv, int current)
{
uint8_t regval;
int ret, idx;
switch (current)
{
case BATTERY_INPUT_CURRENT_EXT_LIM:
idx = BQ2425X_INP_CURR_EXT_ILIM;
break;
case 100:
idx = BQ2425X_INP_CURR_LIM_100MA;
break;
case 150:
idx = BQ2425X_INP_CURR_LIM_150MA;
break;
case 500:
idx = BQ2425X_INP_CURR_LIM_500MA;
break;
case 900:
idx = BQ2425X_INP_CURR_LIM_900MA;
break;
case 1500:
idx = BQ2425X_INP_CURR_LIM_1500MA;
break;
case 2000:
idx = BQ2425X_INP_CURR_LIM_2000MA;
break;
default:
baterr("ERROR: Current not supported, setting default to 100mA!\n");
idx = BQ2425X_INP_CURR_LIM_100MA;
break;
}
/* Read current register */
ret = bq2425x_getreg8(priv, BQ2425X_REG_2, &regval);
if (ret < 0)
{
baterr("ERROR: Error reading from BQ2425X! Error = %d\n", ret);
return ret;
}
/* Clear previous current and set new value */
regval &= ~(BQ2425X_INP_CURR_LIM_MASK);
regval |= idx;
/* Also clear Reset bit to avoid the resetting BUG */
regval &= ~(BQ2425X_RESET);
ret = bq2425x_putreg8(priv, BQ2425X_REG_2, regval);
if (ret < 0)
{
baterr("ERROR: Error writing to BQ2425X! Error = %d\n", ret);
return ret;
}
return OK;
}
/****************************************************************************
* Name: bq2425x_setvolt
*
* Description:
* Set the voltage level to charge the battery. Voltage value in mV.
*
****************************************************************************/
static inline int bq2425x_setvolt(FAR struct bq2425x_dev_s *priv, int volts)
{
uint8_t regval;
int ret, idx;
/* Verify if voltage is in the acceptable range */
if (volts < BQ2425X_VOLT_MIN || volts > BQ2425X_VOLT_MAX)
{
baterr("ERROR: Voltage %d mV is out of range.\n", volts);
return -EINVAL;
}
ret = bq2425x_getreg8(priv, BQ2425X_REG_3, &regval);
if (ret < 0)
{
baterr("ERROR: Error reading from BQ2425X! Error = %d\n", ret);
return ret;
}
/* Voltage starts at 3500mV and increases in steps of 20mV */
idx = volts - BQ2425X_VOLT_MIN;
idx = idx / 20;
/* Clear previous voltage */
regval &= ~(BQ2425X_BAT_VOLT_MASK);
regval |= (idx << BQ2425X_BAT_VOLT_SHIFT);
ret = bq2425x_putreg8(priv, BQ2425X_REG_3, regval);
if (ret < 0)
{
baterr("ERROR: Error writing to BQ2425X! Error = %d\n", ret);
return ret;
}
return OK;
}
/****************************************************************************
* Name: bq2425x_setcurr
*
* Description:
* Set the current to charge the battery. Current value in mA.
*
****************************************************************************/
static inline int bq2425x_setcurr(FAR struct bq2425x_dev_s *priv, int current)
{
uint8_t regval;
int ret, idx;
/* Verify if voltage is in the acceptable range */
if (current < BQ2425X_CURR_MIN || current > BQ2425X_CURR_MAX)
{
baterr("ERROR: Current %d mA is out of range.\n", volts);
return -EINVAL;
}
ret = bq2425x_getreg8(priv, BQ2425X_REG_4, &regval);
if (ret < 0)
{
baterr("ERROR: Error reading from BQ2425X! Error = %d\n", ret);
return ret;
}
/* Current starts at 500mV and increases in steps of 50mA */
idx = current - BQ2425X_CURR_MIN;
idx = idx / 50;
/* Clear previous current and set new value */
regval &= ~(BQ2425X_CHG_CURRENT_MASK);
regval |= (idx << BQ2425X_CHG_CURRENT_SHIFT);
ret = bq2425x_putreg8(priv, BQ2425X_REG_4, regval);
if (ret < 0)
{
baterr("ERROR: Error writing to BQ2425X! Error = %d\n", ret);
return ret;
}
return OK;
}
/****************************************************************************
* Name: bq2425x_voltage
*
* Description:
* Set battery charger voltage
*
****************************************************************************/
static int bq2425x_voltage(struct battery_charger_dev_s *dev, int value)
{
FAR struct bq2425x_dev_s *priv = (FAR struct bq2425x_dev_s *)dev;
int ret;
/* Set voltage to battery charger */
ret = bq2425x_setvolt(priv, value);
if (ret < 0)
{
baterr("ERROR: Error setting voltage to BQ2425X! Error = %d\n", ret);
return ret;
}
return OK;
}
/****************************************************************************
* Name: bq2425x_current
*
* Description:
* Set the battery charger current rate for charging
*
****************************************************************************/
static int bq2425x_current(struct battery_charger_dev_s *dev, int value)
{
FAR struct bq2425x_dev_s *priv = (FAR struct bq2425x_dev_s *)dev;
int ret;
/* Set current to battery charger */
ret = bq2425x_setcurr(priv, value);
if (ret < 0)
{
baterr("ERROR: Error setting current to BQ2425X! Error = %d\n", ret);
return ret;
}
return OK;
}
/****************************************************************************
* Name: bq2425x_input_current
*
* Description:
* Set the power-supply input current limit
*
****************************************************************************/
static int bq2425x_input_current(struct battery_charger_dev_s *dev, int value)
{
FAR struct bq2425x_dev_s *priv = (FAR struct bq2425x_dev_s *)dev;
int ret;
ret = bq2425x_powersupply(priv, value);
if (ret < 0)
{
baterr("ERROR: Failed to set BQ2425x power supply input limit: %d\n", ret);
return ret;
}
return OK;
}
/****************************************************************************
* Name: bq2425x_operate
*
* Description:
* Do miscellaneous battery ioctl()
*
****************************************************************************/
static int bq2425x_operate(struct battery_charger_dev_s *dev, uintptr_t param)
{
return -ENOSYS;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: bq2425x_initialize
*
* Description:
* Initialize the BQ2425x 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_BQ2425X - And the driver must be explictly selected.
*
* Input Parameters:
* i2c - An instance of the I2C interface to use to communicate with
* the BQ2425x
* addr - The I2C address of the BQ2425x (Better be 0x6a)
* 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 BQ2425x lower half.
*
****************************************************************************/
FAR struct battery_charger_dev_s *
bq2425x_initialize(FAR struct i2c_master_s *i2c, uint8_t addr,
uint32_t frequency, int current)
{
FAR struct bq2425x_dev_s *priv;
int ret;
/* Initialize the BQ2425x device structure */
priv = (FAR struct bq2425x_dev_s *)kmm_zalloc(sizeof(struct bq2425x_dev_s));
if (priv)
{
/* Initialize the BQ2425x device structure */
nxsem_init(&priv->batsem, 0, 1);
priv->ops = &g_bq2425xops;
priv->i2c = i2c;
priv->addr = addr;
priv->frequency = frequency;
/* Reset the BQ2425x */
ret = bq2425x_reset(priv);
if (ret < 0)
{
baterr("ERROR: Failed to reset the BQ2425x: %d\n", ret);
kmm_free(priv);
return NULL;
}
/* Disable watchdog otherwise BQ2425x returns to StandAlone mode */
ret = bq2425x_watchdog(priv, false);
if (ret < 0)
{
baterr("ERROR: Failed to disable BQ2425x watchdog: %d\n", ret);
kmm_free(priv);
return NULL;
}
/* Define the current that our power supply can offer to the charger. */
ret = bq2425x_powersupply(priv, current);
if (ret < 0)
{
baterr("ERROR: Failed to set BQ2425x power supply input limit: %d\n", ret);
kmm_free(priv);
return NULL;
}
}
return (FAR struct battery_charger_dev_s *)priv;
}
#endif /* CONFIG_BATTERY_CHARGER && CONFIG_I2C && CONFIG_I2C_BQ2425X */