Highlights:
- A new driver for TI BQ24735 Battery Chargers, courtesy of NVidia. - Device tree bindings for TWL4030 chips. - Random fixes and cleanups. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.14 (GNU/Linux) iQIcBAABAgAGBQJSiXSwAAoJEGgI9fZJve1bJBgQAJKXIYXqZG5AwCqtFXltN0S5 qYmxiaxUekQyesBxRNNb6LwgWEBUF+lQtmfhDpSaHwi6aRc7pmSQaVw6tfD1dpBe bAEHrWu1YD8v6rwhrhB91kv9F81ea06pfUtyUAwDpbchGq/VaN/tsUT4uO5E/AIT K7tWZzjmGyQ79186HUnDxmgb0lA1PPDypsuv/pP1KgrioKNPHskDqhLeS2YuCe6J WUHiGb3cDB8Tu2+Af5OIxXio5J74486KvjlH1K9+ziixUrU0P380D26JshklgIsG qWc1hu9DSO+r76N7HV0EzbpJNqZNEFJiXaXSozgA/yRtJy+ISEIVXJE9oDwW7SxH thLveZTck6VQ6CyggAh2nn9KPVrpFYUcy+HFshpM7V4xugjcAe9Bq9i5cWwJHvn9 Xy4G2s2MS81PFdKEGc9JjSwvXULTRTMAe5gMMfwAqnn+Xuf2+KSCtTlOnPkON/sa cNraBFTSVi33yCEHS9BO7BKbb5ikPNtAaAB+4VszwMQbUysPld7WvEpEOIUOaoJO EYgsM1VOLV0cYO5Qwu9Yxplrjmz/l+kNxAgtCW21SV14DjUbnV9ZuhL5Gx4jbLyY T1uiREtzpTUGoKhZHqWHKXtoEUW+d48nkDXbO+IbKmKPTF6bn0TuZmW0sNU/ZHb8 yNJPZKinHfUE6heJCX2X =rT+j -----END PGP SIGNATURE----- Merge tag 'for-v3.13' of git://git.infradead.org/battery-2.6 Pull battery updates from Anton Vorontsov: "Highlights: - A new driver for TI BQ24735 Battery Chargers, courtesy of NVidia. - Device tree bindings for TWL4030 chips. - Random fixes and cleanups" * tag 'for-v3.13' of git://git.infradead.org/battery-2.6: pm2301-charger: Remove unneeded NULL checks twl4030_charger: Add devicetree support power_supply: Fix documentation for TEMP_*ALERT* properties max17042_battery: Support regmap to access device's registers max17042_battery: Use SIMPLE_DEV_PM_OPS charger-manager : Replace kzalloc to devm_kzalloc and remove uneccessary code bq2415x_charger: Fix max battery regulation voltage tps65090-charger: Use "IS_ENABLED(CONFIG_OF)" for DT code tps65090-charger: Drop devm_free_irq of devm_ allocated irq power_supply: Add support for bq24735 charger pm2301-charger: Staticize pm2xxx_charger_die_therm_mngt pm2301-charger: Check return value of regulator_enable ab8500-charger: Remove redundant break ab8500-charger: Check return value of regulator_enable isp1704_charger: Fix driver to work with changes introduced in v3.5
This commit is contained in:
commit
a709bd585f
20
Documentation/devicetree/bindings/power/twl-charger.txt
Normal file
20
Documentation/devicetree/bindings/power/twl-charger.txt
Normal file
|
@ -0,0 +1,20 @@
|
|||
TWL BCI (Battery Charger Interface)
|
||||
|
||||
Required properties:
|
||||
- compatible:
|
||||
- "ti,twl4030-bci"
|
||||
- interrupts: two interrupt lines from the TWL SIH (secondary
|
||||
interrupt handler) - interrupts 9 and 2.
|
||||
|
||||
Optional properties:
|
||||
- ti,bb-uvolt: microvolts for charging the backup battery.
|
||||
- ti,bb-uamp: microamps for charging the backup battery.
|
||||
|
||||
Examples:
|
||||
|
||||
bci {
|
||||
compatible = "ti,twl4030-bci";
|
||||
interrupts = <9>, <2>;
|
||||
ti,bb-uvolt = <3200000>;
|
||||
ti,bb-uamp = <150>;
|
||||
};
|
|
@ -0,0 +1,32 @@
|
|||
TI BQ24735 Charge Controller
|
||||
~~~~~~~~~~
|
||||
|
||||
Required properties :
|
||||
- compatible : "ti,bq24735"
|
||||
|
||||
Optional properties :
|
||||
- interrupts : Specify the interrupt to be used to trigger when the AC
|
||||
adapter is either plugged in or removed.
|
||||
- ti,ac-detect-gpios : This GPIO is optionally used to read the AC adapter
|
||||
presence. This is a Host GPIO that is configured as an input and
|
||||
connected to the bq24735.
|
||||
- ti,charge-current : Used to control and set the charging current. This value
|
||||
must be between 128mA and 8.128A with a 64mA step resolution. The POR value
|
||||
is 0x0000h. This number is in mA (e.g. 8192), see spec for more information
|
||||
about the ChargeCurrent (0x14h) register.
|
||||
- ti,charge-voltage : Used to control and set the charging voltage. This value
|
||||
must be between 1.024V and 19.2V with a 16mV step resolution. The POR value
|
||||
is 0x0000h. This number is in mV (e.g. 19200), see spec for more information
|
||||
about the ChargeVoltage (0x15h) register.
|
||||
- ti,input-current : Used to control and set the charger input current. This
|
||||
value must be between 128mA and 8.064A with a 128mA step resolution. The
|
||||
POR value is 0x1000h. This number is in mA (e.g. 8064), see the spec for
|
||||
more information about the InputCurrent (0x3fh) register.
|
||||
|
||||
Example:
|
||||
|
||||
bq24735@9 {
|
||||
compatible = "ti,bq24735";
|
||||
reg = <0x9>;
|
||||
ti,ac-detect-gpios = <&gpio 72 0x1>;
|
||||
}
|
|
@ -135,11 +135,11 @@ CAPACITY_LEVEL - capacity level. This corresponds to
|
|||
POWER_SUPPLY_CAPACITY_LEVEL_*.
|
||||
|
||||
TEMP - temperature of the power supply.
|
||||
TEMP_ALERT_MIN - minimum battery temperature alert value in milli centigrade.
|
||||
TEMP_ALERT_MAX - maximum battery temperature alert value in milli centigrade.
|
||||
TEMP_ALERT_MIN - minimum battery temperature alert.
|
||||
TEMP_ALERT_MAX - maximum battery temperature alert.
|
||||
TEMP_AMBIENT - ambient temperature.
|
||||
TEMP_AMBIENT_ALERT_MIN - minimum ambient temperature alert value in milli centigrade.
|
||||
TEMP_AMBIENT_ALERT_MAX - maximum ambient temperature alert value in milli centigrade.
|
||||
TEMP_AMBIENT_ALERT_MIN - minimum ambient temperature alert.
|
||||
TEMP_AMBIENT_ALERT_MAX - maximum ambient temperature alert.
|
||||
|
||||
TIME_TO_EMPTY - seconds left for battery to be considered empty (i.e.
|
||||
while battery powers a load)
|
||||
|
|
|
@ -19,6 +19,12 @@ rtc {
|
|||
interrupts = <11>;
|
||||
};
|
||||
|
||||
charger: bci {
|
||||
compatible = "ti,twl4030-bci";
|
||||
interrupts = <9>, <2>;
|
||||
bci3v1-supply = <&vusb3v1>;
|
||||
};
|
||||
|
||||
watchdog {
|
||||
compatible = "ti,twl4030-wdt";
|
||||
};
|
||||
|
|
|
@ -346,6 +346,12 @@ config CHARGER_BQ24190
|
|||
help
|
||||
Say Y to enable support for the TI BQ24190 battery charger.
|
||||
|
||||
config CHARGER_BQ24735
|
||||
tristate "TI BQ24735 battery charger support"
|
||||
depends on I2C && GPIOLIB
|
||||
help
|
||||
Say Y to enable support for the TI BQ24735 battery charger.
|
||||
|
||||
config CHARGER_SMB347
|
||||
tristate "Summit Microelectronics SMB347 Battery Charger"
|
||||
depends on I2C
|
||||
|
|
|
@ -52,6 +52,7 @@ obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
|
|||
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
|
||||
obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o
|
||||
obj-$(CONFIG_CHARGER_BQ24190) += bq24190_charger.o
|
||||
obj-$(CONFIG_CHARGER_BQ24735) += bq24735-charger.o
|
||||
obj-$(CONFIG_POWER_AVS) += avs/
|
||||
obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o
|
||||
obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o
|
||||
|
|
|
@ -766,7 +766,6 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
|
|||
ret = -ENXIO;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case USB_STAT_CARKIT_1:
|
||||
case USB_STAT_CARKIT_2:
|
||||
case USB_STAT_ACA_DOCK_CHARGER:
|
||||
|
@ -1387,8 +1386,12 @@ static int ab8500_charger_ac_en(struct ux500_charger *charger,
|
|||
* the GPADC module independant of the AB8500 chargers
|
||||
*/
|
||||
if (!di->vddadc_en_ac) {
|
||||
regulator_enable(di->regu);
|
||||
di->vddadc_en_ac = true;
|
||||
ret = regulator_enable(di->regu);
|
||||
if (ret)
|
||||
dev_warn(di->dev,
|
||||
"Failed to enable regulator\n");
|
||||
else
|
||||
di->vddadc_en_ac = true;
|
||||
}
|
||||
|
||||
/* Check if the requested voltage or current is valid */
|
||||
|
@ -1556,8 +1559,12 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
|
|||
* the GPADC module independant of the AB8500 chargers
|
||||
*/
|
||||
if (!di->vddadc_en_usb) {
|
||||
regulator_enable(di->regu);
|
||||
di->vddadc_en_usb = true;
|
||||
ret = regulator_enable(di->regu);
|
||||
if (ret)
|
||||
dev_warn(di->dev,
|
||||
"Failed to enable regulator\n");
|
||||
else
|
||||
di->vddadc_en_usb = true;
|
||||
}
|
||||
|
||||
/* Enable USB charging */
|
||||
|
|
|
@ -605,9 +605,13 @@ static int bq2415x_set_battery_regulation_voltage(struct bq2415x_device *bq,
|
|||
{
|
||||
int val = (mV/10 - 350) / 2;
|
||||
|
||||
/*
|
||||
* According to datasheet, maximum battery regulation voltage is
|
||||
* 4440mV which is b101111 = 47.
|
||||
*/
|
||||
if (val < 0)
|
||||
val = 0;
|
||||
else if (val > 94) /* FIXME: Max is 94 or 122 ? Set max value ? */
|
||||
else if (val > 47)
|
||||
return -EINVAL;
|
||||
|
||||
return bq2415x_i2c_write_mask(bq, BQ2415X_REG_VOLTAGE, val,
|
||||
|
|
419
drivers/power/bq24735-charger.c
Normal file
419
drivers/power/bq24735-charger.c
Normal file
|
@ -0,0 +1,419 @@
|
|||
/*
|
||||
* Battery charger driver for TI BQ24735
|
||||
*
|
||||
* Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation;
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/power/bq24735-charger.h>
|
||||
|
||||
#define BQ24735_CHG_OPT 0x12
|
||||
#define BQ24735_CHG_OPT_CHARGE_DISABLE (1 << 0)
|
||||
#define BQ24735_CHG_OPT_AC_PRESENT (1 << 4)
|
||||
#define BQ24735_CHARGE_CURRENT 0x14
|
||||
#define BQ24735_CHARGE_CURRENT_MASK 0x1fc0
|
||||
#define BQ24735_CHARGE_VOLTAGE 0x15
|
||||
#define BQ24735_CHARGE_VOLTAGE_MASK 0x7ff0
|
||||
#define BQ24735_INPUT_CURRENT 0x3f
|
||||
#define BQ24735_INPUT_CURRENT_MASK 0x1f80
|
||||
#define BQ24735_MANUFACTURER_ID 0xfe
|
||||
#define BQ24735_DEVICE_ID 0xff
|
||||
|
||||
struct bq24735 {
|
||||
struct power_supply charger;
|
||||
struct i2c_client *client;
|
||||
struct bq24735_platform *pdata;
|
||||
};
|
||||
|
||||
static inline struct bq24735 *to_bq24735(struct power_supply *psy)
|
||||
{
|
||||
return container_of(psy, struct bq24735, charger);
|
||||
}
|
||||
|
||||
static enum power_supply_property bq24735_charger_properties[] = {
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
};
|
||||
|
||||
static inline int bq24735_write_word(struct i2c_client *client, u8 reg,
|
||||
u16 value)
|
||||
{
|
||||
return i2c_smbus_write_word_data(client, reg, le16_to_cpu(value));
|
||||
}
|
||||
|
||||
static inline int bq24735_read_word(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
s32 ret = i2c_smbus_read_word_data(client, reg);
|
||||
|
||||
return ret < 0 ? ret : le16_to_cpu(ret);
|
||||
}
|
||||
|
||||
static int bq24735_update_word(struct i2c_client *client, u8 reg,
|
||||
u16 mask, u16 value)
|
||||
{
|
||||
unsigned int tmp;
|
||||
int ret;
|
||||
|
||||
ret = bq24735_read_word(client, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
tmp = ret & ~mask;
|
||||
tmp |= value & mask;
|
||||
|
||||
return bq24735_write_word(client, reg, tmp);
|
||||
}
|
||||
|
||||
static inline int bq24735_enable_charging(struct bq24735 *charger)
|
||||
{
|
||||
return bq24735_update_word(charger->client, BQ24735_CHG_OPT,
|
||||
BQ24735_CHG_OPT_CHARGE_DISABLE,
|
||||
~BQ24735_CHG_OPT_CHARGE_DISABLE);
|
||||
}
|
||||
|
||||
static inline int bq24735_disable_charging(struct bq24735 *charger)
|
||||
{
|
||||
return bq24735_update_word(charger->client, BQ24735_CHG_OPT,
|
||||
BQ24735_CHG_OPT_CHARGE_DISABLE,
|
||||
BQ24735_CHG_OPT_CHARGE_DISABLE);
|
||||
}
|
||||
|
||||
static int bq24735_config_charger(struct bq24735 *charger)
|
||||
{
|
||||
struct bq24735_platform *pdata = charger->pdata;
|
||||
int ret;
|
||||
u16 value;
|
||||
|
||||
if (pdata->charge_current) {
|
||||
value = pdata->charge_current & BQ24735_CHARGE_CURRENT_MASK;
|
||||
|
||||
ret = bq24735_write_word(charger->client,
|
||||
BQ24735_CHARGE_CURRENT, value);
|
||||
if (ret < 0) {
|
||||
dev_err(&charger->client->dev,
|
||||
"Failed to write charger current : %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (pdata->charge_voltage) {
|
||||
value = pdata->charge_voltage & BQ24735_CHARGE_VOLTAGE_MASK;
|
||||
|
||||
ret = bq24735_write_word(charger->client,
|
||||
BQ24735_CHARGE_VOLTAGE, value);
|
||||
if (ret < 0) {
|
||||
dev_err(&charger->client->dev,
|
||||
"Failed to write charger voltage : %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (pdata->input_current) {
|
||||
value = pdata->input_current & BQ24735_INPUT_CURRENT_MASK;
|
||||
|
||||
ret = bq24735_write_word(charger->client,
|
||||
BQ24735_INPUT_CURRENT, value);
|
||||
if (ret < 0) {
|
||||
dev_err(&charger->client->dev,
|
||||
"Failed to write input current : %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool bq24735_charger_is_present(struct bq24735 *charger)
|
||||
{
|
||||
struct bq24735_platform *pdata = charger->pdata;
|
||||
int ret;
|
||||
|
||||
if (pdata->status_gpio_valid) {
|
||||
ret = gpio_get_value_cansleep(pdata->status_gpio);
|
||||
return ret ^= pdata->status_gpio_active_low == 0;
|
||||
} else {
|
||||
int ac = 0;
|
||||
|
||||
ac = bq24735_read_word(charger->client, BQ24735_CHG_OPT);
|
||||
if (ac < 0) {
|
||||
dev_err(&charger->client->dev,
|
||||
"Failed to read charger options : %d\n",
|
||||
ac);
|
||||
return false;
|
||||
}
|
||||
return (ac & BQ24735_CHG_OPT_AC_PRESENT) ? true : false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static irqreturn_t bq24735_charger_isr(int irq, void *devid)
|
||||
{
|
||||
struct power_supply *psy = devid;
|
||||
struct bq24735 *charger = to_bq24735(psy);
|
||||
|
||||
if (bq24735_charger_is_present(charger))
|
||||
bq24735_enable_charging(charger);
|
||||
else
|
||||
bq24735_disable_charging(charger);
|
||||
|
||||
power_supply_changed(psy);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int bq24735_charger_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct bq24735 *charger;
|
||||
|
||||
charger = container_of(psy, struct bq24735, charger);
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
val->intval = bq24735_charger_is_present(charger) ? 1 : 0;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct bq24735_platform *bq24735_parse_dt_data(struct i2c_client *client)
|
||||
{
|
||||
struct bq24735_platform *pdata;
|
||||
struct device_node *np = client->dev.of_node;
|
||||
u32 val;
|
||||
int ret;
|
||||
enum of_gpio_flags flags;
|
||||
|
||||
pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata) {
|
||||
dev_err(&client->dev,
|
||||
"Memory alloc for bq24735 pdata failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pdata->status_gpio = of_get_named_gpio_flags(np, "ti,ac-detect-gpios",
|
||||
0, &flags);
|
||||
|
||||
if (flags & OF_GPIO_ACTIVE_LOW)
|
||||
pdata->status_gpio_active_low = 1;
|
||||
|
||||
ret = of_property_read_u32(np, "ti,charge-current", &val);
|
||||
if (!ret)
|
||||
pdata->charge_current = val;
|
||||
|
||||
ret = of_property_read_u32(np, "ti,charge-voltage", &val);
|
||||
if (!ret)
|
||||
pdata->charge_voltage = val;
|
||||
|
||||
ret = of_property_read_u32(np, "ti,input-current", &val);
|
||||
if (!ret)
|
||||
pdata->input_current = val;
|
||||
|
||||
return pdata;
|
||||
}
|
||||
|
||||
static int bq24735_charger_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct bq24735 *charger;
|
||||
struct power_supply *supply;
|
||||
char *name;
|
||||
|
||||
charger = devm_kzalloc(&client->dev, sizeof(*charger), GFP_KERNEL);
|
||||
if (!charger)
|
||||
return -ENOMEM;
|
||||
|
||||
charger->pdata = client->dev.platform_data;
|
||||
|
||||
if (IS_ENABLED(CONFIG_OF) && !charger->pdata && client->dev.of_node)
|
||||
charger->pdata = bq24735_parse_dt_data(client);
|
||||
|
||||
if (!charger->pdata) {
|
||||
dev_err(&client->dev, "no platform data provided\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
name = (char *)charger->pdata->name;
|
||||
if (!name) {
|
||||
name = kasprintf(GFP_KERNEL, "bq24735@%s",
|
||||
dev_name(&client->dev));
|
||||
if (!name) {
|
||||
dev_err(&client->dev, "Failed to alloc device name\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
charger->client = client;
|
||||
|
||||
supply = &charger->charger;
|
||||
|
||||
supply->name = name;
|
||||
supply->type = POWER_SUPPLY_TYPE_MAINS;
|
||||
supply->properties = bq24735_charger_properties;
|
||||
supply->num_properties = ARRAY_SIZE(bq24735_charger_properties);
|
||||
supply->get_property = bq24735_charger_get_property;
|
||||
supply->supplied_to = charger->pdata->supplied_to;
|
||||
supply->num_supplicants = charger->pdata->num_supplicants;
|
||||
supply->of_node = client->dev.of_node;
|
||||
|
||||
i2c_set_clientdata(client, charger);
|
||||
|
||||
ret = bq24735_read_word(client, BQ24735_MANUFACTURER_ID);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed to read manufacturer id : %d\n",
|
||||
ret);
|
||||
goto err_free_name;
|
||||
} else if (ret != 0x0040) {
|
||||
dev_err(&client->dev,
|
||||
"manufacturer id mismatch. 0x0040 != 0x%04x\n", ret);
|
||||
ret = -ENODEV;
|
||||
goto err_free_name;
|
||||
}
|
||||
|
||||
ret = bq24735_read_word(client, BQ24735_DEVICE_ID);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed to read device id : %d\n", ret);
|
||||
goto err_free_name;
|
||||
} else if (ret != 0x000B) {
|
||||
dev_err(&client->dev,
|
||||
"device id mismatch. 0x000b != 0x%04x\n", ret);
|
||||
ret = -ENODEV;
|
||||
goto err_free_name;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(charger->pdata->status_gpio)) {
|
||||
ret = devm_gpio_request(&client->dev,
|
||||
charger->pdata->status_gpio,
|
||||
name);
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"Failed GPIO request for GPIO %d: %d\n",
|
||||
charger->pdata->status_gpio, ret);
|
||||
}
|
||||
|
||||
charger->pdata->status_gpio_valid = !ret;
|
||||
}
|
||||
|
||||
ret = bq24735_config_charger(charger);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed in configuring charger");
|
||||
goto err_free_name;
|
||||
}
|
||||
|
||||
/* check for AC adapter presence */
|
||||
if (bq24735_charger_is_present(charger)) {
|
||||
ret = bq24735_enable_charging(charger);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed to enable charging\n");
|
||||
goto err_free_name;
|
||||
}
|
||||
}
|
||||
|
||||
ret = power_supply_register(&client->dev, supply);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed to register power supply: %d\n",
|
||||
ret);
|
||||
goto err_free_name;
|
||||
}
|
||||
|
||||
if (client->irq) {
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, bq24735_charger_isr,
|
||||
IRQF_TRIGGER_RISING |
|
||||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_ONESHOT,
|
||||
supply->name, supply);
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"Unable to register IRQ %d err %d\n",
|
||||
client->irq, ret);
|
||||
goto err_unregister_supply;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_unregister_supply:
|
||||
power_supply_unregister(supply);
|
||||
err_free_name:
|
||||
if (name != charger->pdata->name)
|
||||
kfree(name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bq24735_charger_remove(struct i2c_client *client)
|
||||
{
|
||||
struct bq24735 *charger = i2c_get_clientdata(client);
|
||||
|
||||
if (charger->client->irq)
|
||||
devm_free_irq(&charger->client->dev, charger->client->irq,
|
||||
&charger->charger);
|
||||
|
||||
power_supply_unregister(&charger->charger);
|
||||
|
||||
if (charger->charger.name != charger->pdata->name)
|
||||
kfree(charger->charger.name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id bq24735_charger_id[] = {
|
||||
{ "bq24735-charger", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, bq24735_charger_id);
|
||||
|
||||
static const struct of_device_id bq24735_match_ids[] = {
|
||||
{ .compatible = "ti,bq24735", },
|
||||
{ /* end */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bq24735_match_ids);
|
||||
|
||||
static struct i2c_driver bq24735_charger_driver = {
|
||||
.driver = {
|
||||
.name = "bq24735-charger",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = bq24735_match_ids,
|
||||
},
|
||||
.probe = bq24735_charger_probe,
|
||||
.remove = bq24735_charger_remove,
|
||||
.id_table = bq24735_charger_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(bq24735_charger_driver);
|
||||
|
||||
MODULE_DESCRIPTION("bq24735 battery charging driver");
|
||||
MODULE_AUTHOR("Darbha Sriharsha <dsriharsha@nvidia.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -1378,7 +1378,8 @@ static int charger_manager_register_sysfs(struct charger_manager *cm)
|
|||
charger = &desc->charger_regulators[i];
|
||||
|
||||
snprintf(buf, 10, "charger.%d", i);
|
||||
str = kzalloc(sizeof(char) * (strlen(buf) + 1), GFP_KERNEL);
|
||||
str = devm_kzalloc(cm->dev,
|
||||
sizeof(char) * (strlen(buf) + 1), GFP_KERNEL);
|
||||
if (!str) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
|
@ -1452,30 +1453,23 @@ static int charger_manager_probe(struct platform_device *pdev)
|
|||
rtc_dev = NULL;
|
||||
dev_err(&pdev->dev, "Cannot get RTC %s\n",
|
||||
g_desc->rtc_name);
|
||||
ret = -ENODEV;
|
||||
goto err_alloc;
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
if (!desc) {
|
||||
dev_err(&pdev->dev, "No platform data (desc) found\n");
|
||||
ret = -ENODEV;
|
||||
goto err_alloc;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
cm = kzalloc(sizeof(struct charger_manager), GFP_KERNEL);
|
||||
if (!cm) {
|
||||
ret = -ENOMEM;
|
||||
goto err_alloc;
|
||||
}
|
||||
cm = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct charger_manager), GFP_KERNEL);
|
||||
if (!cm)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Basic Values. Unspecified are Null or 0 */
|
||||
cm->dev = &pdev->dev;
|
||||
cm->desc = kmemdup(desc, sizeof(struct charger_desc), GFP_KERNEL);
|
||||
if (!cm->desc) {
|
||||
ret = -ENOMEM;
|
||||
goto err_alloc_desc;
|
||||
}
|
||||
cm->desc = desc;
|
||||
cm->last_temp_mC = INT_MIN; /* denotes "unmeasured, yet" */
|
||||
|
||||
/*
|
||||
|
@ -1498,27 +1492,23 @@ static int charger_manager_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
if (!desc->charger_regulators || desc->num_charger_regulators < 1) {
|
||||
ret = -EINVAL;
|
||||
dev_err(&pdev->dev, "charger_regulators undefined\n");
|
||||
goto err_no_charger;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!desc->psy_charger_stat || !desc->psy_charger_stat[0]) {
|
||||
dev_err(&pdev->dev, "No power supply defined\n");
|
||||
ret = -EINVAL;
|
||||
goto err_no_charger_stat;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Counting index only */
|
||||
while (desc->psy_charger_stat[i])
|
||||
i++;
|
||||
|
||||
cm->charger_stat = kzalloc(sizeof(struct power_supply *) * (i + 1),
|
||||
GFP_KERNEL);
|
||||
if (!cm->charger_stat) {
|
||||
ret = -ENOMEM;
|
||||
goto err_no_charger_stat;
|
||||
}
|
||||
cm->charger_stat = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct power_supply *) * i, GFP_KERNEL);
|
||||
if (!cm->charger_stat)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; desc->psy_charger_stat[i]; i++) {
|
||||
cm->charger_stat[i] = power_supply_get_by_name(
|
||||
|
@ -1526,8 +1516,7 @@ static int charger_manager_probe(struct platform_device *pdev)
|
|||
if (!cm->charger_stat[i]) {
|
||||
dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n",
|
||||
desc->psy_charger_stat[i]);
|
||||
ret = -ENODEV;
|
||||
goto err_chg_stat;
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1535,21 +1524,18 @@ static int charger_manager_probe(struct platform_device *pdev)
|
|||
if (!cm->fuel_gauge) {
|
||||
dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n",
|
||||
desc->psy_fuel_gauge);
|
||||
ret = -ENODEV;
|
||||
goto err_chg_stat;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (desc->polling_interval_ms == 0 ||
|
||||
msecs_to_jiffies(desc->polling_interval_ms) <= CM_JIFFIES_SMALL) {
|
||||
dev_err(&pdev->dev, "polling_interval_ms is too small\n");
|
||||
ret = -EINVAL;
|
||||
goto err_chg_stat;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!desc->temperature_out_of_range) {
|
||||
dev_err(&pdev->dev, "there is no temperature_out_of_range\n");
|
||||
ret = -EINVAL;
|
||||
goto err_chg_stat;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!desc->charging_max_duration_ms ||
|
||||
|
@ -1570,14 +1556,13 @@ static int charger_manager_probe(struct platform_device *pdev)
|
|||
cm->charger_psy.name = cm->psy_name_buf;
|
||||
|
||||
/* Allocate for psy properties because they may vary */
|
||||
cm->charger_psy.properties = kzalloc(sizeof(enum power_supply_property)
|
||||
cm->charger_psy.properties = devm_kzalloc(&pdev->dev,
|
||||
sizeof(enum power_supply_property)
|
||||
* (ARRAY_SIZE(default_charger_props) +
|
||||
NUM_CHARGER_PSY_OPTIONAL),
|
||||
GFP_KERNEL);
|
||||
if (!cm->charger_psy.properties) {
|
||||
ret = -ENOMEM;
|
||||
goto err_chg_stat;
|
||||
}
|
||||
NUM_CHARGER_PSY_OPTIONAL), GFP_KERNEL);
|
||||
if (!cm->charger_psy.properties)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(cm->charger_psy.properties, default_charger_props,
|
||||
sizeof(enum power_supply_property) *
|
||||
ARRAY_SIZE(default_charger_props));
|
||||
|
@ -1614,7 +1599,7 @@ static int charger_manager_probe(struct platform_device *pdev)
|
|||
if (ret) {
|
||||
dev_err(&pdev->dev, "Cannot register charger-manager with name \"%s\"\n",
|
||||
cm->charger_psy.name);
|
||||
goto err_register;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Register extcon device for charger cable */
|
||||
|
@ -1655,8 +1640,6 @@ static int charger_manager_probe(struct platform_device *pdev)
|
|||
charger = &desc->charger_regulators[i];
|
||||
sysfs_remove_group(&cm->charger_psy.dev->kobj,
|
||||
&charger->attr_g);
|
||||
|
||||
kfree(charger->attr_g.name);
|
||||
}
|
||||
err_reg_extcon:
|
||||
for (i = 0; i < desc->num_charger_regulators; i++) {
|
||||
|
@ -1674,16 +1657,7 @@ static int charger_manager_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
power_supply_unregister(&cm->charger_psy);
|
||||
err_register:
|
||||
kfree(cm->charger_psy.properties);
|
||||
err_chg_stat:
|
||||
kfree(cm->charger_stat);
|
||||
err_no_charger_stat:
|
||||
err_no_charger:
|
||||
kfree(cm->desc);
|
||||
err_alloc_desc:
|
||||
kfree(cm);
|
||||
err_alloc:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1718,11 +1692,6 @@ static int charger_manager_remove(struct platform_device *pdev)
|
|||
|
||||
try_charger_enable(cm, false);
|
||||
|
||||
kfree(cm->charger_psy.properties);
|
||||
kfree(cm->charger_stat);
|
||||
kfree(cm->desc);
|
||||
kfree(cm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* ISP1704 USB Charger Detection driver
|
||||
*
|
||||
* Copyright (C) 2010 Nokia Corporation
|
||||
* Copyright (C) 2012 - 2013 Pali Rohár <pali.rohar@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -65,10 +66,6 @@ struct isp1704_charger {
|
|||
unsigned present:1;
|
||||
unsigned online:1;
|
||||
unsigned current_max;
|
||||
|
||||
/* temp storage variables */
|
||||
unsigned long event;
|
||||
unsigned max_power;
|
||||
};
|
||||
|
||||
static inline int isp1704_read(struct isp1704_charger *isp, u32 reg)
|
||||
|
@ -231,56 +228,59 @@ static inline int isp1704_charger_detect(struct isp1704_charger *isp)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static inline int isp1704_charger_detect_dcp(struct isp1704_charger *isp)
|
||||
{
|
||||
if (isp1704_charger_detect(isp) &&
|
||||
isp1704_charger_type(isp) == POWER_SUPPLY_TYPE_USB_DCP)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static void isp1704_charger_work(struct work_struct *data)
|
||||
{
|
||||
int detect;
|
||||
unsigned long event;
|
||||
unsigned power;
|
||||
struct isp1704_charger *isp =
|
||||
container_of(data, struct isp1704_charger, work);
|
||||
static DEFINE_MUTEX(lock);
|
||||
|
||||
event = isp->event;
|
||||
power = isp->max_power;
|
||||
|
||||
mutex_lock(&lock);
|
||||
|
||||
if (event != USB_EVENT_NONE)
|
||||
isp1704_charger_set_power(isp, 1);
|
||||
|
||||
switch (event) {
|
||||
switch (isp->phy->last_event) {
|
||||
case USB_EVENT_VBUS:
|
||||
isp->online = true;
|
||||
/* do not call wall charger detection more times */
|
||||
if (!isp->present) {
|
||||
isp->online = true;
|
||||
isp->present = 1;
|
||||
isp1704_charger_set_power(isp, 1);
|
||||
|
||||
/* detect charger */
|
||||
detect = isp1704_charger_detect(isp);
|
||||
/* detect wall charger */
|
||||
if (isp1704_charger_detect_dcp(isp)) {
|
||||
isp->psy.type = POWER_SUPPLY_TYPE_USB_DCP;
|
||||
isp->current_max = 1800;
|
||||
} else {
|
||||
isp->psy.type = POWER_SUPPLY_TYPE_USB;
|
||||
isp->current_max = 500;
|
||||
}
|
||||
|
||||
if (detect) {
|
||||
isp->present = detect;
|
||||
isp->psy.type = isp1704_charger_type(isp);
|
||||
}
|
||||
|
||||
switch (isp->psy.type) {
|
||||
case POWER_SUPPLY_TYPE_USB_DCP:
|
||||
isp->current_max = 1800;
|
||||
break;
|
||||
case POWER_SUPPLY_TYPE_USB_CDP:
|
||||
/*
|
||||
* Only 500mA here or high speed chirp
|
||||
* handshaking may break
|
||||
*/
|
||||
isp->current_max = 500;
|
||||
/* FALLTHROUGH */
|
||||
case POWER_SUPPLY_TYPE_USB:
|
||||
default:
|
||||
/* enable data pullups */
|
||||
if (isp->phy->otg->gadget)
|
||||
usb_gadget_connect(isp->phy->otg->gadget);
|
||||
}
|
||||
|
||||
if (isp->psy.type != POWER_SUPPLY_TYPE_USB_DCP) {
|
||||
/*
|
||||
* Only 500mA here or high speed chirp
|
||||
* handshaking may break
|
||||
*/
|
||||
if (isp->current_max > 500)
|
||||
isp->current_max = 500;
|
||||
|
||||
if (isp->current_max > 100)
|
||||
isp->psy.type = POWER_SUPPLY_TYPE_USB_CDP;
|
||||
}
|
||||
break;
|
||||
case USB_EVENT_NONE:
|
||||
isp->online = false;
|
||||
isp->current_max = 0;
|
||||
isp->present = 0;
|
||||
isp->current_max = 0;
|
||||
isp->psy.type = POWER_SUPPLY_TYPE_USB;
|
||||
|
@ -298,12 +298,6 @@ static void isp1704_charger_work(struct work_struct *data)
|
|||
|
||||
isp1704_charger_set_power(isp, 0);
|
||||
break;
|
||||
case USB_EVENT_ENUMERATED:
|
||||
if (isp->present)
|
||||
isp->current_max = 1800;
|
||||
else
|
||||
isp->current_max = power;
|
||||
break;
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
|
@ -314,16 +308,11 @@ static void isp1704_charger_work(struct work_struct *data)
|
|||
}
|
||||
|
||||
static int isp1704_notifier_call(struct notifier_block *nb,
|
||||
unsigned long event, void *power)
|
||||
unsigned long val, void *v)
|
||||
{
|
||||
struct isp1704_charger *isp =
|
||||
container_of(nb, struct isp1704_charger, nb);
|
||||
|
||||
isp->event = event;
|
||||
|
||||
if (power)
|
||||
isp->max_power = *((unsigned *)power);
|
||||
|
||||
schedule_work(&isp->work);
|
||||
|
||||
return NOTIFY_OK;
|
||||
|
@ -462,13 +451,13 @@ static int isp1704_charger_probe(struct platform_device *pdev)
|
|||
if (isp->phy->otg->gadget)
|
||||
usb_gadget_disconnect(isp->phy->otg->gadget);
|
||||
|
||||
if (isp->phy->last_event == USB_EVENT_NONE)
|
||||
isp1704_charger_set_power(isp, 0);
|
||||
|
||||
/* Detect charger if VBUS is valid (the cable was already plugged). */
|
||||
ret = isp1704_read(isp, ULPI_USB_INT_STS);
|
||||
isp1704_charger_set_power(isp, 0);
|
||||
if ((ret & ULPI_INT_VBUS_VALID) && !isp->phy->otg->default_a) {
|
||||
isp->event = USB_EVENT_VBUS;
|
||||
if (isp->phy->last_event == USB_EVENT_VBUS &&
|
||||
!isp->phy->otg->default_a)
|
||||
schedule_work(&isp->work);
|
||||
}
|
||||
|
||||
return 0;
|
||||
fail2:
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <linux/power_supply.h>
|
||||
#include <linux/power/max17042_battery.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* Status register bits */
|
||||
#define STATUS_POR_BIT (1 << 1)
|
||||
|
@ -67,6 +68,7 @@
|
|||
|
||||
struct max17042_chip {
|
||||
struct i2c_client *client;
|
||||
struct regmap *regmap;
|
||||
struct power_supply battery;
|
||||
enum max170xx_chip_type chip_type;
|
||||
struct max17042_platform_data *pdata;
|
||||
|
@ -74,35 +76,6 @@ struct max17042_chip {
|
|||
int init_complete;
|
||||
};
|
||||
|
||||
static int max17042_write_reg(struct i2c_client *client, u8 reg, u16 value)
|
||||
{
|
||||
int ret = i2c_smbus_write_word_data(client, reg, value);
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "%s: err %d\n", __func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max17042_read_reg(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
int ret = i2c_smbus_read_word_data(client, reg);
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "%s: err %d\n", __func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void max17042_set_reg(struct i2c_client *client,
|
||||
struct max17042_reg_data *data, int size)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
max17042_write_reg(client, data[i].addr, data[i].data);
|
||||
}
|
||||
|
||||
static enum power_supply_property max17042_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_CYCLE_COUNT,
|
||||
|
@ -125,96 +98,98 @@ static int max17042_get_property(struct power_supply *psy,
|
|||
{
|
||||
struct max17042_chip *chip = container_of(psy,
|
||||
struct max17042_chip, battery);
|
||||
struct regmap *map = chip->regmap;
|
||||
int ret;
|
||||
u32 data;
|
||||
|
||||
if (!chip->init_complete)
|
||||
return -EAGAIN;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
ret = max17042_read_reg(chip->client, MAX17042_STATUS);
|
||||
ret = regmap_read(map, MAX17042_STATUS, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret & MAX17042_STATUS_BattAbsent)
|
||||
if (data & MAX17042_STATUS_BattAbsent)
|
||||
val->intval = 0;
|
||||
else
|
||||
val->intval = 1;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CYCLE_COUNT:
|
||||
ret = max17042_read_reg(chip->client, MAX17042_Cycles);
|
||||
ret = regmap_read(map, MAX17042_Cycles, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = ret;
|
||||
val->intval = data;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
|
||||
ret = max17042_read_reg(chip->client, MAX17042_MinMaxVolt);
|
||||
ret = regmap_read(map, MAX17042_MinMaxVolt, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = ret >> 8;
|
||||
val->intval = data >> 8;
|
||||
val->intval *= 20000; /* Units of LSB = 20mV */
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
|
||||
if (chip->chip_type == MAX17042)
|
||||
ret = max17042_read_reg(chip->client, MAX17042_V_empty);
|
||||
ret = regmap_read(map, MAX17042_V_empty, &data);
|
||||
else
|
||||
ret = max17042_read_reg(chip->client, MAX17047_V_empty);
|
||||
ret = regmap_read(map, MAX17047_V_empty, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = ret >> 7;
|
||||
val->intval = data >> 7;
|
||||
val->intval *= 10000; /* Units of LSB = 10mV */
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
ret = max17042_read_reg(chip->client, MAX17042_VCELL);
|
||||
ret = regmap_read(map, MAX17042_VCELL, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = ret * 625 / 8;
|
||||
val->intval = data * 625 / 8;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_AVG:
|
||||
ret = max17042_read_reg(chip->client, MAX17042_AvgVCELL);
|
||||
ret = regmap_read(map, MAX17042_AvgVCELL, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = ret * 625 / 8;
|
||||
val->intval = data * 625 / 8;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_OCV:
|
||||
ret = max17042_read_reg(chip->client, MAX17042_OCVInternal);
|
||||
ret = regmap_read(map, MAX17042_OCVInternal, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = ret * 625 / 8;
|
||||
val->intval = data * 625 / 8;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
ret = max17042_read_reg(chip->client, MAX17042_RepSOC);
|
||||
ret = regmap_read(map, MAX17042_RepSOC, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = ret >> 8;
|
||||
val->intval = data >> 8;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_FULL:
|
||||
ret = max17042_read_reg(chip->client, MAX17042_FullCAP);
|
||||
ret = regmap_read(map, MAX17042_FullCAP, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = ret * 1000 / 2;
|
||||
val->intval = data * 1000 / 2;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
|
||||
ret = max17042_read_reg(chip->client, MAX17042_QH);
|
||||
ret = regmap_read(map, MAX17042_QH, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = ret * 1000 / 2;
|
||||
val->intval = data * 1000 / 2;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TEMP:
|
||||
ret = max17042_read_reg(chip->client, MAX17042_TEMP);
|
||||
ret = regmap_read(map, MAX17042_TEMP, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = ret;
|
||||
val->intval = data;
|
||||
/* The value is signed. */
|
||||
if (val->intval & 0x8000) {
|
||||
val->intval = (0x7fff & ~val->intval) + 1;
|
||||
|
@ -226,11 +201,11 @@ static int max17042_get_property(struct power_supply *psy,
|
|||
break;
|
||||
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
||||
if (chip->pdata->enable_current_sense) {
|
||||
ret = max17042_read_reg(chip->client, MAX17042_Current);
|
||||
ret = regmap_read(map, MAX17042_Current, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = ret;
|
||||
val->intval = data;
|
||||
if (val->intval & 0x8000) {
|
||||
/* Negative */
|
||||
val->intval = ~val->intval & 0x7fff;
|
||||
|
@ -244,12 +219,11 @@ static int max17042_get_property(struct power_supply *psy,
|
|||
break;
|
||||
case POWER_SUPPLY_PROP_CURRENT_AVG:
|
||||
if (chip->pdata->enable_current_sense) {
|
||||
ret = max17042_read_reg(chip->client,
|
||||
MAX17042_AvgCurrent);
|
||||
ret = regmap_read(map, MAX17042_AvgCurrent, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = ret;
|
||||
val->intval = data;
|
||||
if (val->intval & 0x8000) {
|
||||
/* Negative */
|
||||
val->intval = ~val->intval & 0x7fff;
|
||||
|
@ -267,16 +241,15 @@ static int max17042_get_property(struct power_supply *psy,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int max17042_write_verify_reg(struct i2c_client *client,
|
||||
u8 reg, u16 value)
|
||||
static int max17042_write_verify_reg(struct regmap *map, u8 reg, u32 value)
|
||||
{
|
||||
int retries = 8;
|
||||
int ret;
|
||||
u16 read_value;
|
||||
u32 read_value;
|
||||
|
||||
do {
|
||||
ret = i2c_smbus_write_word_data(client, reg, value);
|
||||
read_value = max17042_read_reg(client, reg);
|
||||
ret = regmap_write(map, reg, value);
|
||||
regmap_read(map, reg, &read_value);
|
||||
if (read_value != value) {
|
||||
ret = -EIO;
|
||||
retries--;
|
||||
|
@ -284,50 +257,51 @@ static int max17042_write_verify_reg(struct i2c_client *client,
|
|||
} while (retries && read_value != value);
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "%s: err %d\n", __func__, ret);
|
||||
pr_err("%s: err %d\n", __func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void max17042_override_por(
|
||||
struct i2c_client *client, u8 reg, u16 value)
|
||||
static inline void max17042_override_por(struct regmap *map,
|
||||
u8 reg, u16 value)
|
||||
{
|
||||
if (value)
|
||||
max17042_write_reg(client, reg, value);
|
||||
regmap_write(map, reg, value);
|
||||
}
|
||||
|
||||
static inline void max10742_unlock_model(struct max17042_chip *chip)
|
||||
{
|
||||
struct i2c_client *client = chip->client;
|
||||
max17042_write_reg(client, MAX17042_MLOCKReg1, MODEL_UNLOCK1);
|
||||
max17042_write_reg(client, MAX17042_MLOCKReg2, MODEL_UNLOCK2);
|
||||
struct regmap *map = chip->regmap;
|
||||
regmap_write(map, MAX17042_MLOCKReg1, MODEL_UNLOCK1);
|
||||
regmap_write(map, MAX17042_MLOCKReg2, MODEL_UNLOCK2);
|
||||
}
|
||||
|
||||
static inline void max10742_lock_model(struct max17042_chip *chip)
|
||||
{
|
||||
struct i2c_client *client = chip->client;
|
||||
max17042_write_reg(client, MAX17042_MLOCKReg1, MODEL_LOCK1);
|
||||
max17042_write_reg(client, MAX17042_MLOCKReg2, MODEL_LOCK2);
|
||||
struct regmap *map = chip->regmap;
|
||||
|
||||
regmap_write(map, MAX17042_MLOCKReg1, MODEL_LOCK1);
|
||||
regmap_write(map, MAX17042_MLOCKReg2, MODEL_LOCK2);
|
||||
}
|
||||
|
||||
static inline void max17042_write_model_data(struct max17042_chip *chip,
|
||||
u8 addr, int size)
|
||||
{
|
||||
struct i2c_client *client = chip->client;
|
||||
struct regmap *map = chip->regmap;
|
||||
int i;
|
||||
for (i = 0; i < size; i++)
|
||||
max17042_write_reg(client, addr + i,
|
||||
chip->pdata->config_data->cell_char_tbl[i]);
|
||||
regmap_write(map, addr + i,
|
||||
chip->pdata->config_data->cell_char_tbl[i]);
|
||||
}
|
||||
|
||||
static inline void max17042_read_model_data(struct max17042_chip *chip,
|
||||
u8 addr, u16 *data, int size)
|
||||
u8 addr, u32 *data, int size)
|
||||
{
|
||||
struct i2c_client *client = chip->client;
|
||||
struct regmap *map = chip->regmap;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
data[i] = max17042_read_reg(client, addr + i);
|
||||
regmap_read(map, addr + i, &data[i]);
|
||||
}
|
||||
|
||||
static inline int max17042_model_data_compare(struct max17042_chip *chip,
|
||||
|
@ -350,7 +324,7 @@ static int max17042_init_model(struct max17042_chip *chip)
|
|||
{
|
||||
int ret;
|
||||
int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl);
|
||||
u16 *temp_data;
|
||||
u32 *temp_data;
|
||||
|
||||
temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL);
|
||||
if (!temp_data)
|
||||
|
@ -365,7 +339,7 @@ static int max17042_init_model(struct max17042_chip *chip)
|
|||
ret = max17042_model_data_compare(
|
||||
chip,
|
||||
chip->pdata->config_data->cell_char_tbl,
|
||||
temp_data,
|
||||
(u16 *)temp_data,
|
||||
table_size);
|
||||
|
||||
max10742_lock_model(chip);
|
||||
|
@ -378,7 +352,7 @@ static int max17042_verify_model_lock(struct max17042_chip *chip)
|
|||
{
|
||||
int i;
|
||||
int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl);
|
||||
u16 *temp_data;
|
||||
u32 *temp_data;
|
||||
int ret = 0;
|
||||
|
||||
temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL);
|
||||
|
@ -398,40 +372,38 @@ static int max17042_verify_model_lock(struct max17042_chip *chip)
|
|||
static void max17042_write_config_regs(struct max17042_chip *chip)
|
||||
{
|
||||
struct max17042_config_data *config = chip->pdata->config_data;
|
||||
struct regmap *map = chip->regmap;
|
||||
|
||||
max17042_write_reg(chip->client, MAX17042_CONFIG, config->config);
|
||||
max17042_write_reg(chip->client, MAX17042_LearnCFG, config->learn_cfg);
|
||||
max17042_write_reg(chip->client, MAX17042_FilterCFG,
|
||||
regmap_write(map, MAX17042_CONFIG, config->config);
|
||||
regmap_write(map, MAX17042_LearnCFG, config->learn_cfg);
|
||||
regmap_write(map, MAX17042_FilterCFG,
|
||||
config->filter_cfg);
|
||||
max17042_write_reg(chip->client, MAX17042_RelaxCFG, config->relax_cfg);
|
||||
regmap_write(map, MAX17042_RelaxCFG, config->relax_cfg);
|
||||
if (chip->chip_type == MAX17047)
|
||||
max17042_write_reg(chip->client, MAX17047_FullSOCThr,
|
||||
regmap_write(map, MAX17047_FullSOCThr,
|
||||
config->full_soc_thresh);
|
||||
}
|
||||
|
||||
static void max17042_write_custom_regs(struct max17042_chip *chip)
|
||||
{
|
||||
struct max17042_config_data *config = chip->pdata->config_data;
|
||||
struct regmap *map = chip->regmap;
|
||||
|
||||
max17042_write_verify_reg(chip->client, MAX17042_RCOMP0,
|
||||
config->rcomp0);
|
||||
max17042_write_verify_reg(chip->client, MAX17042_TempCo,
|
||||
config->tcompc0);
|
||||
max17042_write_verify_reg(chip->client, MAX17042_ICHGTerm,
|
||||
config->ichgt_term);
|
||||
max17042_write_verify_reg(map, MAX17042_RCOMP0, config->rcomp0);
|
||||
max17042_write_verify_reg(map, MAX17042_TempCo, config->tcompc0);
|
||||
max17042_write_verify_reg(map, MAX17042_ICHGTerm, config->ichgt_term);
|
||||
if (chip->chip_type == MAX17042) {
|
||||
max17042_write_reg(chip->client, MAX17042_EmptyTempCo,
|
||||
config->empty_tempco);
|
||||
max17042_write_verify_reg(chip->client, MAX17042_K_empty0,
|
||||
regmap_write(map, MAX17042_EmptyTempCo, config->empty_tempco);
|
||||
max17042_write_verify_reg(map, MAX17042_K_empty0,
|
||||
config->kempty0);
|
||||
} else {
|
||||
max17042_write_verify_reg(chip->client, MAX17047_QRTbl00,
|
||||
max17042_write_verify_reg(map, MAX17047_QRTbl00,
|
||||
config->qrtbl00);
|
||||
max17042_write_verify_reg(chip->client, MAX17047_QRTbl10,
|
||||
max17042_write_verify_reg(map, MAX17047_QRTbl10,
|
||||
config->qrtbl10);
|
||||
max17042_write_verify_reg(chip->client, MAX17047_QRTbl20,
|
||||
max17042_write_verify_reg(map, MAX17047_QRTbl20,
|
||||
config->qrtbl20);
|
||||
max17042_write_verify_reg(chip->client, MAX17047_QRTbl30,
|
||||
max17042_write_verify_reg(map, MAX17047_QRTbl30,
|
||||
config->qrtbl30);
|
||||
}
|
||||
}
|
||||
|
@ -439,58 +411,60 @@ static void max17042_write_custom_regs(struct max17042_chip *chip)
|
|||
static void max17042_update_capacity_regs(struct max17042_chip *chip)
|
||||
{
|
||||
struct max17042_config_data *config = chip->pdata->config_data;
|
||||
struct regmap *map = chip->regmap;
|
||||
|
||||
max17042_write_verify_reg(chip->client, MAX17042_FullCAP,
|
||||
max17042_write_verify_reg(map, MAX17042_FullCAP,
|
||||
config->fullcap);
|
||||
max17042_write_reg(chip->client, MAX17042_DesignCap,
|
||||
config->design_cap);
|
||||
max17042_write_verify_reg(chip->client, MAX17042_FullCAPNom,
|
||||
regmap_write(map, MAX17042_DesignCap, config->design_cap);
|
||||
max17042_write_verify_reg(map, MAX17042_FullCAPNom,
|
||||
config->fullcapnom);
|
||||
}
|
||||
|
||||
static void max17042_reset_vfsoc0_reg(struct max17042_chip *chip)
|
||||
{
|
||||
u16 vfSoc;
|
||||
unsigned int vfSoc;
|
||||
struct regmap *map = chip->regmap;
|
||||
|
||||
vfSoc = max17042_read_reg(chip->client, MAX17042_VFSOC);
|
||||
max17042_write_reg(chip->client, MAX17042_VFSOC0Enable, VFSOC0_UNLOCK);
|
||||
max17042_write_verify_reg(chip->client, MAX17042_VFSOC0, vfSoc);
|
||||
max17042_write_reg(chip->client, MAX17042_VFSOC0Enable, VFSOC0_LOCK);
|
||||
regmap_read(map, MAX17042_VFSOC, &vfSoc);
|
||||
regmap_write(map, MAX17042_VFSOC0Enable, VFSOC0_UNLOCK);
|
||||
max17042_write_verify_reg(map, MAX17042_VFSOC0, vfSoc);
|
||||
regmap_write(map, MAX17042_VFSOC0Enable, VFSOC0_LOCK);
|
||||
}
|
||||
|
||||
static void max17042_load_new_capacity_params(struct max17042_chip *chip)
|
||||
{
|
||||
u16 full_cap0, rep_cap, dq_acc, vfSoc;
|
||||
u32 full_cap0, rep_cap, dq_acc, vfSoc;
|
||||
u32 rem_cap;
|
||||
|
||||
struct max17042_config_data *config = chip->pdata->config_data;
|
||||
struct regmap *map = chip->regmap;
|
||||
|
||||
full_cap0 = max17042_read_reg(chip->client, MAX17042_FullCAP0);
|
||||
vfSoc = max17042_read_reg(chip->client, MAX17042_VFSOC);
|
||||
regmap_read(map, MAX17042_FullCAP0, &full_cap0);
|
||||
regmap_read(map, MAX17042_VFSOC, &vfSoc);
|
||||
|
||||
/* fg_vfSoc needs to shifted by 8 bits to get the
|
||||
* perc in 1% accuracy, to get the right rem_cap multiply
|
||||
* full_cap0, fg_vfSoc and devide by 100
|
||||
*/
|
||||
rem_cap = ((vfSoc >> 8) * full_cap0) / 100;
|
||||
max17042_write_verify_reg(chip->client, MAX17042_RemCap, (u16)rem_cap);
|
||||
max17042_write_verify_reg(map, MAX17042_RemCap, rem_cap);
|
||||
|
||||
rep_cap = (u16)rem_cap;
|
||||
max17042_write_verify_reg(chip->client, MAX17042_RepCap, rep_cap);
|
||||
rep_cap = rem_cap;
|
||||
max17042_write_verify_reg(map, MAX17042_RepCap, rep_cap);
|
||||
|
||||
/* Write dQ_acc to 200% of Capacity and dP_acc to 200% */
|
||||
dq_acc = config->fullcap / dQ_ACC_DIV;
|
||||
max17042_write_verify_reg(chip->client, MAX17042_dQacc, dq_acc);
|
||||
max17042_write_verify_reg(chip->client, MAX17042_dPacc, dP_ACC_200);
|
||||
max17042_write_verify_reg(map, MAX17042_dQacc, dq_acc);
|
||||
max17042_write_verify_reg(map, MAX17042_dPacc, dP_ACC_200);
|
||||
|
||||
max17042_write_verify_reg(chip->client, MAX17042_FullCAP,
|
||||
max17042_write_verify_reg(map, MAX17042_FullCAP,
|
||||
config->fullcap);
|
||||
max17042_write_reg(chip->client, MAX17042_DesignCap,
|
||||
regmap_write(map, MAX17042_DesignCap,
|
||||
config->design_cap);
|
||||
max17042_write_verify_reg(chip->client, MAX17042_FullCAPNom,
|
||||
max17042_write_verify_reg(map, MAX17042_FullCAPNom,
|
||||
config->fullcapnom);
|
||||
/* Update SOC register with new SOC */
|
||||
max17042_write_reg(chip->client, MAX17042_RepSOC, vfSoc);
|
||||
regmap_write(map, MAX17042_RepSOC, vfSoc);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -500,59 +474,60 @@ static void max17042_load_new_capacity_params(struct max17042_chip *chip)
|
|||
*/
|
||||
static inline void max17042_override_por_values(struct max17042_chip *chip)
|
||||
{
|
||||
struct i2c_client *client = chip->client;
|
||||
struct regmap *map = chip->regmap;
|
||||
struct max17042_config_data *config = chip->pdata->config_data;
|
||||
|
||||
max17042_override_por(client, MAX17042_TGAIN, config->tgain);
|
||||
max17042_override_por(client, MAx17042_TOFF, config->toff);
|
||||
max17042_override_por(client, MAX17042_CGAIN, config->cgain);
|
||||
max17042_override_por(client, MAX17042_COFF, config->coff);
|
||||
max17042_override_por(map, MAX17042_TGAIN, config->tgain);
|
||||
max17042_override_por(map, MAx17042_TOFF, config->toff);
|
||||
max17042_override_por(map, MAX17042_CGAIN, config->cgain);
|
||||
max17042_override_por(map, MAX17042_COFF, config->coff);
|
||||
|
||||
max17042_override_por(client, MAX17042_VALRT_Th, config->valrt_thresh);
|
||||
max17042_override_por(client, MAX17042_TALRT_Th, config->talrt_thresh);
|
||||
max17042_override_por(client, MAX17042_SALRT_Th,
|
||||
config->soc_alrt_thresh);
|
||||
max17042_override_por(client, MAX17042_CONFIG, config->config);
|
||||
max17042_override_por(client, MAX17042_SHDNTIMER, config->shdntimer);
|
||||
max17042_override_por(map, MAX17042_VALRT_Th, config->valrt_thresh);
|
||||
max17042_override_por(map, MAX17042_TALRT_Th, config->talrt_thresh);
|
||||
max17042_override_por(map, MAX17042_SALRT_Th,
|
||||
config->soc_alrt_thresh);
|
||||
max17042_override_por(map, MAX17042_CONFIG, config->config);
|
||||
max17042_override_por(map, MAX17042_SHDNTIMER, config->shdntimer);
|
||||
|
||||
max17042_override_por(client, MAX17042_DesignCap, config->design_cap);
|
||||
max17042_override_por(client, MAX17042_ICHGTerm, config->ichgt_term);
|
||||
max17042_override_por(map, MAX17042_DesignCap, config->design_cap);
|
||||
max17042_override_por(map, MAX17042_ICHGTerm, config->ichgt_term);
|
||||
|
||||
max17042_override_por(client, MAX17042_AtRate, config->at_rate);
|
||||
max17042_override_por(client, MAX17042_LearnCFG, config->learn_cfg);
|
||||
max17042_override_por(client, MAX17042_FilterCFG, config->filter_cfg);
|
||||
max17042_override_por(client, MAX17042_RelaxCFG, config->relax_cfg);
|
||||
max17042_override_por(client, MAX17042_MiscCFG, config->misc_cfg);
|
||||
max17042_override_por(client, MAX17042_MaskSOC, config->masksoc);
|
||||
max17042_override_por(map, MAX17042_AtRate, config->at_rate);
|
||||
max17042_override_por(map, MAX17042_LearnCFG, config->learn_cfg);
|
||||
max17042_override_por(map, MAX17042_FilterCFG, config->filter_cfg);
|
||||
max17042_override_por(map, MAX17042_RelaxCFG, config->relax_cfg);
|
||||
max17042_override_por(map, MAX17042_MiscCFG, config->misc_cfg);
|
||||
max17042_override_por(map, MAX17042_MaskSOC, config->masksoc);
|
||||
|
||||
max17042_override_por(client, MAX17042_FullCAP, config->fullcap);
|
||||
max17042_override_por(client, MAX17042_FullCAPNom, config->fullcapnom);
|
||||
max17042_override_por(map, MAX17042_FullCAP, config->fullcap);
|
||||
max17042_override_por(map, MAX17042_FullCAPNom, config->fullcapnom);
|
||||
if (chip->chip_type == MAX17042)
|
||||
max17042_override_por(client, MAX17042_SOC_empty,
|
||||
max17042_override_por(map, MAX17042_SOC_empty,
|
||||
config->socempty);
|
||||
max17042_override_por(client, MAX17042_LAvg_empty, config->lavg_empty);
|
||||
max17042_override_por(client, MAX17042_dQacc, config->dqacc);
|
||||
max17042_override_por(client, MAX17042_dPacc, config->dpacc);
|
||||
max17042_override_por(map, MAX17042_LAvg_empty, config->lavg_empty);
|
||||
max17042_override_por(map, MAX17042_dQacc, config->dqacc);
|
||||
max17042_override_por(map, MAX17042_dPacc, config->dpacc);
|
||||
|
||||
if (chip->chip_type == MAX17042)
|
||||
max17042_override_por(client, MAX17042_V_empty, config->vempty);
|
||||
max17042_override_por(map, MAX17042_V_empty, config->vempty);
|
||||
else
|
||||
max17042_override_por(client, MAX17047_V_empty, config->vempty);
|
||||
max17042_override_por(client, MAX17042_TempNom, config->temp_nom);
|
||||
max17042_override_por(client, MAX17042_TempLim, config->temp_lim);
|
||||
max17042_override_por(client, MAX17042_FCTC, config->fctc);
|
||||
max17042_override_por(client, MAX17042_RCOMP0, config->rcomp0);
|
||||
max17042_override_por(client, MAX17042_TempCo, config->tcompc0);
|
||||
max17042_override_por(map, MAX17047_V_empty, config->vempty);
|
||||
max17042_override_por(map, MAX17042_TempNom, config->temp_nom);
|
||||
max17042_override_por(map, MAX17042_TempLim, config->temp_lim);
|
||||
max17042_override_por(map, MAX17042_FCTC, config->fctc);
|
||||
max17042_override_por(map, MAX17042_RCOMP0, config->rcomp0);
|
||||
max17042_override_por(map, MAX17042_TempCo, config->tcompc0);
|
||||
if (chip->chip_type) {
|
||||
max17042_override_por(client, MAX17042_EmptyTempCo,
|
||||
config->empty_tempco);
|
||||
max17042_override_por(client, MAX17042_K_empty0,
|
||||
config->kempty0);
|
||||
max17042_override_por(map, MAX17042_EmptyTempCo,
|
||||
config->empty_tempco);
|
||||
max17042_override_por(map, MAX17042_K_empty0,
|
||||
config->kempty0);
|
||||
}
|
||||
}
|
||||
|
||||
static int max17042_init_chip(struct max17042_chip *chip)
|
||||
{
|
||||
struct regmap *map = chip->regmap;
|
||||
int ret;
|
||||
int val;
|
||||
|
||||
|
@ -597,31 +572,32 @@ static int max17042_init_chip(struct max17042_chip *chip)
|
|||
max17042_load_new_capacity_params(chip);
|
||||
|
||||
/* Init complete, Clear the POR bit */
|
||||
val = max17042_read_reg(chip->client, MAX17042_STATUS);
|
||||
max17042_write_reg(chip->client, MAX17042_STATUS,
|
||||
val & (~STATUS_POR_BIT));
|
||||
regmap_read(map, MAX17042_STATUS, &val);
|
||||
regmap_write(map, MAX17042_STATUS, val & (~STATUS_POR_BIT));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void max17042_set_soc_threshold(struct max17042_chip *chip, u16 off)
|
||||
{
|
||||
u16 soc, soc_tr;
|
||||
struct regmap *map = chip->regmap;
|
||||
u32 soc, soc_tr;
|
||||
|
||||
/* program interrupt thesholds such that we should
|
||||
* get interrupt for every 'off' perc change in the soc
|
||||
*/
|
||||
soc = max17042_read_reg(chip->client, MAX17042_RepSOC) >> 8;
|
||||
regmap_read(map, MAX17042_RepSOC, &soc);
|
||||
soc >>= 8;
|
||||
soc_tr = (soc + off) << 8;
|
||||
soc_tr |= (soc - off);
|
||||
max17042_write_reg(chip->client, MAX17042_SALRT_Th, soc_tr);
|
||||
regmap_write(map, MAX17042_SALRT_Th, soc_tr);
|
||||
}
|
||||
|
||||
static irqreturn_t max17042_thread_handler(int id, void *dev)
|
||||
{
|
||||
struct max17042_chip *chip = dev;
|
||||
u16 val;
|
||||
u32 val;
|
||||
|
||||
val = max17042_read_reg(chip->client, MAX17042_STATUS);
|
||||
regmap_read(chip->regmap, MAX17042_STATUS, &val);
|
||||
if ((val & STATUS_INTR_SOCMIN_BIT) ||
|
||||
(val & STATUS_INTR_SOCMAX_BIT)) {
|
||||
dev_info(&chip->client->dev, "SOC threshold INTR\n");
|
||||
|
@ -682,13 +658,20 @@ max17042_get_pdata(struct device *dev)
|
|||
}
|
||||
#endif
|
||||
|
||||
static struct regmap_config max17042_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 16,
|
||||
.val_format_endian = REGMAP_ENDIAN_NATIVE,
|
||||
};
|
||||
|
||||
static int max17042_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
struct max17042_chip *chip;
|
||||
int ret;
|
||||
int reg;
|
||||
int i;
|
||||
u32 val;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
|
||||
return -EIO;
|
||||
|
@ -698,6 +681,12 @@ static int max17042_probe(struct i2c_client *client,
|
|||
return -ENOMEM;
|
||||
|
||||
chip->client = client;
|
||||
chip->regmap = devm_regmap_init_i2c(client, &max17042_regmap_config);
|
||||
if (IS_ERR(chip->regmap)) {
|
||||
dev_err(&client->dev, "Failed to initialize regmap\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chip->pdata = max17042_get_pdata(&client->dev);
|
||||
if (!chip->pdata) {
|
||||
dev_err(&client->dev, "no platform data provided\n");
|
||||
|
@ -706,15 +695,15 @@ static int max17042_probe(struct i2c_client *client,
|
|||
|
||||
i2c_set_clientdata(client, chip);
|
||||
|
||||
ret = max17042_read_reg(chip->client, MAX17042_DevName);
|
||||
if (ret == MAX17042_IC_VERSION) {
|
||||
regmap_read(chip->regmap, MAX17042_DevName, &val);
|
||||
if (val == MAX17042_IC_VERSION) {
|
||||
dev_dbg(&client->dev, "chip type max17042 detected\n");
|
||||
chip->chip_type = MAX17042;
|
||||
} else if (ret == MAX17047_IC_VERSION) {
|
||||
} else if (val == MAX17047_IC_VERSION) {
|
||||
dev_dbg(&client->dev, "chip type max17047/50 detected\n");
|
||||
chip->chip_type = MAX17047;
|
||||
} else {
|
||||
dev_err(&client->dev, "device version mismatch: %x\n", ret);
|
||||
dev_err(&client->dev, "device version mismatch: %x\n", val);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
@ -733,13 +722,15 @@ static int max17042_probe(struct i2c_client *client,
|
|||
chip->pdata->r_sns = MAX17042_DEFAULT_SNS_RESISTOR;
|
||||
|
||||
if (chip->pdata->init_data)
|
||||
max17042_set_reg(client, chip->pdata->init_data,
|
||||
chip->pdata->num_init_data);
|
||||
for (i = 0; i < chip->pdata->num_init_data; i++)
|
||||
regmap_write(chip->regmap,
|
||||
chip->pdata->init_data[i].addr,
|
||||
chip->pdata->init_data[i].data);
|
||||
|
||||
if (!chip->pdata->enable_current_sense) {
|
||||
max17042_write_reg(client, MAX17042_CGAIN, 0x0000);
|
||||
max17042_write_reg(client, MAX17042_MiscCFG, 0x0003);
|
||||
max17042_write_reg(client, MAX17042_LearnCFG, 0x0007);
|
||||
regmap_write(chip->regmap, MAX17042_CGAIN, 0x0000);
|
||||
regmap_write(chip->regmap, MAX17042_MiscCFG, 0x0003);
|
||||
regmap_write(chip->regmap, MAX17042_LearnCFG, 0x0007);
|
||||
}
|
||||
|
||||
ret = power_supply_register(&client->dev, &chip->battery);
|
||||
|
@ -754,9 +745,9 @@ static int max17042_probe(struct i2c_client *client,
|
|||
IRQF_TRIGGER_FALLING,
|
||||
chip->battery.name, chip);
|
||||
if (!ret) {
|
||||
reg = max17042_read_reg(client, MAX17042_CONFIG);
|
||||
reg |= CONFIG_ALRT_BIT_ENBL;
|
||||
max17042_write_reg(client, MAX17042_CONFIG, reg);
|
||||
regmap_read(chip->regmap, MAX17042_CONFIG, &val);
|
||||
val |= CONFIG_ALRT_BIT_ENBL;
|
||||
regmap_write(chip->regmap, MAX17042_CONFIG, val);
|
||||
max17042_set_soc_threshold(chip, 1);
|
||||
} else {
|
||||
client->irq = 0;
|
||||
|
@ -765,8 +756,8 @@ static int max17042_probe(struct i2c_client *client,
|
|||
}
|
||||
}
|
||||
|
||||
reg = max17042_read_reg(chip->client, MAX17042_STATUS);
|
||||
if (reg & STATUS_POR_BIT) {
|
||||
regmap_read(chip->regmap, MAX17042_STATUS, &val);
|
||||
if (val & STATUS_POR_BIT) {
|
||||
INIT_WORK(&chip->work, max17042_init_worker);
|
||||
schedule_work(&chip->work);
|
||||
} else {
|
||||
|
@ -786,7 +777,7 @@ static int max17042_remove(struct i2c_client *client)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int max17042_suspend(struct device *dev)
|
||||
{
|
||||
struct max17042_chip *chip = dev_get_drvdata(dev);
|
||||
|
@ -816,17 +807,11 @@ static int max17042_resume(struct device *dev)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops max17042_pm_ops = {
|
||||
.suspend = max17042_suspend,
|
||||
.resume = max17042_resume,
|
||||
};
|
||||
|
||||
#define MAX17042_PM_OPS (&max17042_pm_ops)
|
||||
#else
|
||||
#define MAX17042_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(max17042_pm_ops, max17042_suspend,
|
||||
max17042_resume);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id max17042_dt_match[] = {
|
||||
{ .compatible = "maxim,max17042" },
|
||||
|
@ -849,7 +834,7 @@ static struct i2c_driver max17042_i2c_driver = {
|
|||
.driver = {
|
||||
.name = "max17042",
|
||||
.of_match_table = of_match_ptr(max17042_dt_match),
|
||||
.pm = MAX17042_PM_OPS,
|
||||
.pm = &max17042_pm_ops,
|
||||
},
|
||||
.probe = max17042_probe,
|
||||
.remove = max17042_remove,
|
||||
|
|
|
@ -205,7 +205,7 @@ static int pm2xxx_charger_batt_therm_mngt(struct pm2xxx_charger *pm2, int val)
|
|||
}
|
||||
|
||||
|
||||
int pm2xxx_charger_die_therm_mngt(struct pm2xxx_charger *pm2, int val)
|
||||
static int pm2xxx_charger_die_therm_mngt(struct pm2xxx_charger *pm2, int val)
|
||||
{
|
||||
queue_work(pm2->charger_wq, &pm2->check_main_thermal_prot_work);
|
||||
|
||||
|
@ -722,8 +722,12 @@ static int pm2xxx_charger_ac_en(struct ux500_charger *charger,
|
|||
|
||||
dev_dbg(pm2->dev, "Enable AC: %dmV %dmA\n", vset, iset);
|
||||
if (!pm2->vddadc_en_ac) {
|
||||
regulator_enable(pm2->regu);
|
||||
pm2->vddadc_en_ac = true;
|
||||
ret = regulator_enable(pm2->regu);
|
||||
if (ret)
|
||||
dev_warn(pm2->dev,
|
||||
"Failed to enable vddadc regulator\n");
|
||||
else
|
||||
pm2->vddadc_en_ac = true;
|
||||
}
|
||||
|
||||
ret = pm2xxx_charging_init(pm2);
|
||||
|
@ -953,37 +957,24 @@ static int pm2xxx_runtime_suspend(struct device *dev)
|
|||
{
|
||||
struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev);
|
||||
struct pm2xxx_charger *pm2;
|
||||
int ret = 0;
|
||||
|
||||
pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(pm2xxx_i2c_client);
|
||||
if (!pm2) {
|
||||
dev_err(pm2->dev, "no pm2xxx_charger data supplied\n");
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
clear_lpn_pin(pm2);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm2xxx_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev);
|
||||
struct pm2xxx_charger *pm2;
|
||||
int ret = 0;
|
||||
|
||||
pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(pm2xxx_i2c_client);
|
||||
if (!pm2) {
|
||||
dev_err(pm2->dev, "no pm2xxx_charger data supplied\n");
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(pm2->lpn_pin) && gpio_get_value(pm2->lpn_pin) == 0)
|
||||
set_lpn_pin(pm2);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -15,15 +15,17 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/mfd/tps65090.h>
|
||||
|
||||
#define TPS65090_REG_INTR_STS 0x00
|
||||
|
@ -185,10 +187,6 @@ static irqreturn_t tps65090_charger_isr(int irq, void *dev_id)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
|
||||
#include <linux/of_device.h>
|
||||
|
||||
static struct tps65090_platform_data *
|
||||
tps65090_parse_dt_charger_data(struct platform_device *pdev)
|
||||
{
|
||||
|
@ -210,13 +208,6 @@ static struct tps65090_platform_data *
|
|||
return pdata;
|
||||
|
||||
}
|
||||
#else
|
||||
static struct tps65090_platform_data *
|
||||
tps65090_parse_dt_charger_data(struct platform_device *pdev)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int tps65090_charger_probe(struct platform_device *pdev)
|
||||
{
|
||||
|
@ -228,7 +219,7 @@ static int tps65090_charger_probe(struct platform_device *pdev)
|
|||
|
||||
pdata = dev_get_platdata(pdev->dev.parent);
|
||||
|
||||
if (!pdata && pdev->dev.of_node)
|
||||
if (IS_ENABLED(CONFIG_OF) && !pdata && pdev->dev.of_node)
|
||||
pdata = tps65090_parse_dt_charger_data(pdev);
|
||||
|
||||
if (!pdata) {
|
||||
|
@ -277,13 +268,13 @@ static int tps65090_charger_probe(struct platform_device *pdev)
|
|||
if (ret) {
|
||||
dev_err(cdata->dev, "Unable to register irq %d err %d\n", irq,
|
||||
ret);
|
||||
goto fail_free_irq;
|
||||
goto fail_unregister_supply;
|
||||
}
|
||||
|
||||
ret = tps65090_config_charger(cdata);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "charger config failed, err %d\n", ret);
|
||||
goto fail_free_irq;
|
||||
goto fail_unregister_supply;
|
||||
}
|
||||
|
||||
/* Check for charger presence */
|
||||
|
@ -292,14 +283,14 @@ static int tps65090_charger_probe(struct platform_device *pdev)
|
|||
if (ret < 0) {
|
||||
dev_err(cdata->dev, "%s(): Error in reading reg 0x%x", __func__,
|
||||
TPS65090_REG_CG_STATUS1);
|
||||
goto fail_free_irq;
|
||||
goto fail_unregister_supply;
|
||||
}
|
||||
|
||||
if (status1 != 0) {
|
||||
ret = tps65090_enable_charging(cdata);
|
||||
if (ret < 0) {
|
||||
dev_err(cdata->dev, "error enabling charger\n");
|
||||
goto fail_free_irq;
|
||||
goto fail_unregister_supply;
|
||||
}
|
||||
cdata->ac_online = 1;
|
||||
power_supply_changed(&cdata->ac);
|
||||
|
@ -307,8 +298,6 @@ static int tps65090_charger_probe(struct platform_device *pdev)
|
|||
|
||||
return 0;
|
||||
|
||||
fail_free_irq:
|
||||
devm_free_irq(cdata->dev, irq, cdata);
|
||||
fail_unregister_supply:
|
||||
power_supply_unregister(&cdata->ac);
|
||||
|
||||
|
@ -319,7 +308,6 @@ static int tps65090_charger_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct tps65090_charger *cdata = platform_get_drvdata(pdev);
|
||||
|
||||
devm_free_irq(cdata->dev, cdata->irq, cdata);
|
||||
power_supply_unregister(&cdata->ac);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -495,10 +495,38 @@ static enum power_supply_property twl4030_charger_props[] = {
|
|||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct twl4030_bci_platform_data *
|
||||
twl4030_bci_parse_dt(struct device *dev)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
struct twl4030_bci_platform_data *pdata;
|
||||
u32 num;
|
||||
|
||||
if (!np)
|
||||
return NULL;
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return pdata;
|
||||
|
||||
if (of_property_read_u32(np, "ti,bb-uvolt", &num) == 0)
|
||||
pdata->bb_uvolt = num;
|
||||
if (of_property_read_u32(np, "ti,bb-uamp", &num) == 0)
|
||||
pdata->bb_uamp = num;
|
||||
return pdata;
|
||||
}
|
||||
#else
|
||||
static inline const struct twl4030_bci_platform_data *
|
||||
twl4030_bci_parse_dt(struct device *dev)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __init twl4030_bci_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct twl4030_bci *bci;
|
||||
struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data;
|
||||
const struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data;
|
||||
int ret;
|
||||
u32 reg;
|
||||
|
||||
|
@ -506,6 +534,9 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
|
|||
if (bci == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!pdata)
|
||||
pdata = twl4030_bci_parse_dt(&pdev->dev);
|
||||
|
||||
bci->dev = &pdev->dev;
|
||||
bci->irq_chg = platform_get_irq(pdev, 0);
|
||||
bci->irq_bci = platform_get_irq(pdev, 1);
|
||||
|
@ -581,8 +612,11 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
|
|||
|
||||
twl4030_charger_enable_ac(true);
|
||||
twl4030_charger_enable_usb(bci, true);
|
||||
twl4030_charger_enable_backup(pdata->bb_uvolt,
|
||||
pdata->bb_uamp);
|
||||
if (pdata)
|
||||
twl4030_charger_enable_backup(pdata->bb_uvolt,
|
||||
pdata->bb_uamp);
|
||||
else
|
||||
twl4030_charger_enable_backup(0, 0);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -631,10 +665,17 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id twl_bci_of_match[] = {
|
||||
{.compatible = "ti,twl4030-bci", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, twl_bci_of_match);
|
||||
|
||||
static struct platform_driver twl4030_bci_driver = {
|
||||
.driver = {
|
||||
.name = "twl4030_bci",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(twl_bci_of_match),
|
||||
},
|
||||
.remove = __exit_p(twl4030_bci_remove),
|
||||
};
|
||||
|
|
39
include/linux/power/bq24735-charger.h
Normal file
39
include/linux/power/bq24735-charger.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __CHARGER_BQ24735_H_
|
||||
#define __CHARGER_BQ24735_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/power_supply.h>
|
||||
|
||||
struct bq24735_platform {
|
||||
uint32_t charge_current;
|
||||
uint32_t charge_voltage;
|
||||
uint32_t input_current;
|
||||
|
||||
const char *name;
|
||||
|
||||
int status_gpio;
|
||||
int status_gpio_active_low;
|
||||
bool status_gpio_valid;
|
||||
|
||||
char **supplied_to;
|
||||
size_t num_supplicants;
|
||||
};
|
||||
|
||||
#endif /* __CHARGER_BQ24735_H_ */
|
Loading…
Reference in New Issue
Block a user