diff --git a/drivers/mfd/max8998.c b/drivers/mfd/max8998.c index 9ec7570f5b8..de4096aee24 100644 --- a/drivers/mfd/max8998.c +++ b/drivers/mfd/max8998.c @@ -39,6 +39,8 @@ static struct mfd_cell max8998_devs[] = { .name = "max8998-pmic", }, { .name = "max8998-rtc", + }, { + .name = "max8998-battery", }, }; diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index e57b50b3856..57de051a74b 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -235,4 +235,18 @@ config CHARGER_GPIO This driver can be build as a module. If so, the module will be called gpio-charger. +config CHARGER_MAX8997 + tristate "Maxim MAX8997/MAX8966 PMIC battery charger driver" + depends on MFD_MAX8997 && REGULATOR_MAX8997 + help + Say Y to enable support for the battery charger control sysfs and + platform data of MAX8997/LP3974 PMICs. + +config CHARGER_MAX8998 + tristate "Maxim MAX8998/LP3974 PMIC battery charger driver" + depends on MFD_MAX8998 && REGULATOR_MAX8998 + help + Say Y to enable support for the battery charger control sysfs and + platform data of MAX8998/LP3974 PMICs. + endif # POWER_SUPPLY diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 009a90fa8ac..b4af13dd8b6 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -36,3 +36,5 @@ obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o +obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o +obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o diff --git a/drivers/power/apm_power.c b/drivers/power/apm_power.c index dc628cb2e76..8a612dec913 100644 --- a/drivers/power/apm_power.c +++ b/drivers/power/apm_power.c @@ -14,11 +14,11 @@ #include -#define PSY_PROP(psy, prop, val) psy->get_property(psy, \ - POWER_SUPPLY_PROP_##prop, val) +#define PSY_PROP(psy, prop, val) (psy->get_property(psy, \ + POWER_SUPPLY_PROP_##prop, val)) -#define _MPSY_PROP(prop, val) main_battery->get_property(main_battery, \ - prop, val) +#define _MPSY_PROP(prop, val) (main_battery->get_property(main_battery, \ + prop, val)) #define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val) diff --git a/drivers/power/bq20z75.c b/drivers/power/bq20z75.c index 506585e31a5..9c5e5beda3a 100644 --- a/drivers/power/bq20z75.c +++ b/drivers/power/bq20z75.c @@ -152,6 +152,10 @@ struct bq20z75_info { bool gpio_detect; bool enable_detection; int irq; + int last_state; + int poll_time; + struct delayed_work work; + int ignore_changes; }; static int bq20z75_read_word_data(struct i2c_client *client, u8 address) @@ -279,6 +283,7 @@ static int bq20z75_get_battery_property(struct i2c_client *client, int reg_offset, enum power_supply_property psp, union power_supply_propval *val) { + struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); s32 ret; ret = bq20z75_read_word_data(client, @@ -293,15 +298,24 @@ static int bq20z75_get_battery_property(struct i2c_client *client, if (ret >= bq20z75_data[reg_offset].min_value && ret <= bq20z75_data[reg_offset].max_value) { val->intval = ret; - if (psp == POWER_SUPPLY_PROP_STATUS) { - if (ret & BATTERY_FULL_CHARGED) - val->intval = POWER_SUPPLY_STATUS_FULL; - else if (ret & BATTERY_FULL_DISCHARGED) - val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; - else if (ret & BATTERY_DISCHARGING) - val->intval = POWER_SUPPLY_STATUS_DISCHARGING; - else - val->intval = POWER_SUPPLY_STATUS_CHARGING; + if (psp != POWER_SUPPLY_PROP_STATUS) + return 0; + + if (ret & BATTERY_FULL_CHARGED) + val->intval = POWER_SUPPLY_STATUS_FULL; + else if (ret & BATTERY_FULL_DISCHARGED) + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + else if (ret & BATTERY_DISCHARGING) + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + else + val->intval = POWER_SUPPLY_STATUS_CHARGING; + + if (bq20z75_device->poll_time == 0) + bq20z75_device->last_state = val->intval; + else if (bq20z75_device->last_state != val->intval) { + cancel_delayed_work_sync(&bq20z75_device->work); + power_supply_changed(&bq20z75_device->power_supply); + bq20z75_device->poll_time = 0; } } else { if (psp == POWER_SUPPLY_PROP_STATUS) @@ -545,6 +559,60 @@ static irqreturn_t bq20z75_irq(int irq, void *devid) return IRQ_HANDLED; } +static void bq20z75_external_power_changed(struct power_supply *psy) +{ + struct bq20z75_info *bq20z75_device; + + bq20z75_device = container_of(psy, struct bq20z75_info, power_supply); + + if (bq20z75_device->ignore_changes > 0) { + bq20z75_device->ignore_changes--; + return; + } + + /* cancel outstanding work */ + cancel_delayed_work_sync(&bq20z75_device->work); + + schedule_delayed_work(&bq20z75_device->work, HZ); + bq20z75_device->poll_time = bq20z75_device->pdata->poll_retry_count; +} + +static void bq20z75_delayed_work(struct work_struct *work) +{ + struct bq20z75_info *bq20z75_device; + s32 ret; + + bq20z75_device = container_of(work, struct bq20z75_info, work.work); + + ret = bq20z75_read_word_data(bq20z75_device->client, + bq20z75_data[REG_STATUS].addr); + /* if the read failed, give up on this work */ + if (ret < 0) { + bq20z75_device->poll_time = 0; + return; + } + + if (ret & BATTERY_FULL_CHARGED) + ret = POWER_SUPPLY_STATUS_FULL; + else if (ret & BATTERY_FULL_DISCHARGED) + ret = POWER_SUPPLY_STATUS_NOT_CHARGING; + else if (ret & BATTERY_DISCHARGING) + ret = POWER_SUPPLY_STATUS_DISCHARGING; + else + ret = POWER_SUPPLY_STATUS_CHARGING; + + if (bq20z75_device->last_state != ret) { + bq20z75_device->poll_time = 0; + power_supply_changed(&bq20z75_device->power_supply); + return; + } + if (bq20z75_device->poll_time > 0) { + schedule_delayed_work(&bq20z75_device->work, HZ); + bq20z75_device->poll_time--; + return; + } +} + static int __devinit bq20z75_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -566,6 +634,13 @@ static int __devinit bq20z75_probe(struct i2c_client *client, bq20z75_device->power_supply.num_properties = ARRAY_SIZE(bq20z75_properties); bq20z75_device->power_supply.get_property = bq20z75_get_property; + /* ignore first notification of external change, it is generated + * from the power_supply_register call back + */ + bq20z75_device->ignore_changes = 1; + bq20z75_device->last_state = POWER_SUPPLY_STATUS_UNKNOWN; + bq20z75_device->power_supply.external_power_changed = + bq20z75_external_power_changed; if (pdata) { bq20z75_device->gpio_detect = @@ -625,6 +700,10 @@ skip_gpio: dev_info(&client->dev, "%s: battery gas gauge device registered\n", client->name); + INIT_DELAYED_WORK(&bq20z75_device->work, bq20z75_delayed_work); + + bq20z75_device->enable_detection = true; + return 0; exit_psupply: @@ -648,6 +727,9 @@ static int __devexit bq20z75_remove(struct i2c_client *client) gpio_free(bq20z75_device->pdata->battery_detect); power_supply_unregister(&bq20z75_device->power_supply); + + cancel_delayed_work_sync(&bq20z75_device->work); + kfree(bq20z75_device); bq20z75_device = NULL; @@ -661,6 +743,9 @@ static int bq20z75_suspend(struct i2c_client *client, struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); s32 ret; + if (bq20z75_device->poll_time > 0) + cancel_delayed_work_sync(&bq20z75_device->work); + /* write to manufacturer access with sleep command */ ret = bq20z75_write_word_data(client, bq20z75_data[REG_MANUFACTURER_DATA].addr, diff --git a/drivers/power/gpio-charger.c b/drivers/power/gpio-charger.c index 718f2c53782..a64b8854cfd 100644 --- a/drivers/power/gpio-charger.c +++ b/drivers/power/gpio-charger.c @@ -127,7 +127,7 @@ static int __devinit gpio_charger_probe(struct platform_device *pdev) ret = request_any_context_irq(irq, gpio_charger_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, dev_name(&pdev->dev), charger); - if (ret) + if (ret < 0) dev_warn(&pdev->dev, "Failed to request irq: %d\n", ret); else gpio_charger->irq = irq; diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index c5c8805156c..98bfab35b8e 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c @@ -29,74 +29,6 @@ #include #include -enum max17042_register { - MAX17042_STATUS = 0x00, - MAX17042_VALRT_Th = 0x01, - MAX17042_TALRT_Th = 0x02, - MAX17042_SALRT_Th = 0x03, - MAX17042_AtRate = 0x04, - MAX17042_RepCap = 0x05, - MAX17042_RepSOC = 0x06, - MAX17042_Age = 0x07, - MAX17042_TEMP = 0x08, - MAX17042_VCELL = 0x09, - MAX17042_Current = 0x0A, - MAX17042_AvgCurrent = 0x0B, - MAX17042_Qresidual = 0x0C, - MAX17042_SOC = 0x0D, - MAX17042_AvSOC = 0x0E, - MAX17042_RemCap = 0x0F, - MAX17402_FullCAP = 0x10, - MAX17042_TTE = 0x11, - MAX17042_V_empty = 0x12, - - MAX17042_RSLOW = 0x14, - - MAX17042_AvgTA = 0x16, - MAX17042_Cycles = 0x17, - MAX17042_DesignCap = 0x18, - MAX17042_AvgVCELL = 0x19, - MAX17042_MinMaxTemp = 0x1A, - MAX17042_MinMaxVolt = 0x1B, - MAX17042_MinMaxCurr = 0x1C, - MAX17042_CONFIG = 0x1D, - MAX17042_ICHGTerm = 0x1E, - MAX17042_AvCap = 0x1F, - MAX17042_ManName = 0x20, - MAX17042_DevName = 0x21, - MAX17042_DevChem = 0x22, - - MAX17042_TempNom = 0x24, - MAX17042_TempCold = 0x25, - MAX17042_TempHot = 0x26, - MAX17042_AIN = 0x27, - MAX17042_LearnCFG = 0x28, - MAX17042_SHFTCFG = 0x29, - MAX17042_RelaxCFG = 0x2A, - MAX17042_MiscCFG = 0x2B, - MAX17042_TGAIN = 0x2C, - MAx17042_TOFF = 0x2D, - MAX17042_CGAIN = 0x2E, - MAX17042_COFF = 0x2F, - - MAX17042_Q_empty = 0x33, - MAX17042_T_empty = 0x34, - - MAX17042_RCOMP0 = 0x38, - MAX17042_TempCo = 0x39, - MAX17042_Rx = 0x3A, - MAX17042_T_empty0 = 0x3B, - MAX17042_TaskPeriod = 0x3C, - MAX17042_FSTAT = 0x3D, - - MAX17042_SHDNTIMER = 0x3F, - - MAX17042_VFRemCap = 0x4A, - - MAX17042_QH = 0x4D, - MAX17042_QL = 0x4E, -}; - struct max17042_chip { struct i2c_client *client; struct power_supply battery; @@ -123,10 +55,27 @@ static int max17042_read_reg(struct i2c_client *client, u8 reg) 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, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_VOLTAGE_AVG, POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_AVG, }; static int max17042_get_property(struct power_supply *psy, @@ -137,6 +86,30 @@ static int max17042_get_property(struct power_supply *psy, struct max17042_chip, battery); switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + val->intval = max17042_read_reg(chip->client, + MAX17042_STATUS); + if (val->intval & MAX17042_STATUS_BattAbsent) + val->intval = 0; + else + val->intval = 1; + break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + val->intval = max17042_read_reg(chip->client, + MAX17042_Cycles); + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + val->intval = max17042_read_reg(chip->client, + MAX17042_MinMaxVolt); + val->intval >>= 8; + val->intval *= 20000; /* Units of LSB = 20mV */ + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + val->intval = max17042_read_reg(chip->client, + MAX17042_V_empty); + val->intval >>= 7; + val->intval *= 10000; /* Units of LSB = 10mV */ + break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: val->intval = max17042_read_reg(chip->client, MAX17042_VCELL) * 83; /* 1000 / 12 = 83 */ @@ -149,6 +122,57 @@ static int max17042_get_property(struct power_supply *psy, val->intval = max17042_read_reg(chip->client, MAX17042_SOC) / 256; break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + val->intval = max17042_read_reg(chip->client, + MAX17042_RepSOC); + if ((val->intval / 256) >= MAX17042_BATTERY_FULL) + val->intval = 1; + else if (val->intval >= 0) + val->intval = 0; + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = max17042_read_reg(chip->client, + MAX17042_TEMP); + /* The value is signed. */ + if (val->intval & 0x8000) { + val->intval = (0x7fff & ~val->intval) + 1; + val->intval *= -1; + } + /* The value is converted into deci-centigrade scale */ + /* Units of LSB = 1 / 256 degree Celsius */ + val->intval = val->intval * 10 / 256; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + if (chip->pdata->enable_current_sense) { + val->intval = max17042_read_reg(chip->client, + MAX17042_Current); + if (val->intval & 0x8000) { + /* Negative */ + val->intval = ~val->intval & 0x7fff; + val->intval++; + val->intval *= -1; + } + val->intval >>= 4; + val->intval *= 1000000 * 25 / chip->pdata->r_sns; + } else { + return -EINVAL; + } + break; + case POWER_SUPPLY_PROP_CURRENT_AVG: + if (chip->pdata->enable_current_sense) { + val->intval = max17042_read_reg(chip->client, + MAX17042_AvgCurrent); + if (val->intval & 0x8000) { + /* Negative */ + val->intval = ~val->intval & 0x7fff; + val->intval++; + val->intval *= -1; + } + val->intval *= 1562500 / chip->pdata->r_sns; + } else { + return -EINVAL; + } + break; default: return -EINVAL; } @@ -180,18 +204,30 @@ static int __devinit max17042_probe(struct i2c_client *client, chip->battery.properties = max17042_battery_props; chip->battery.num_properties = ARRAY_SIZE(max17042_battery_props); + /* When current is not measured, + * CURRENT_NOW and CURRENT_AVG properties should be invisible. */ + if (!chip->pdata->enable_current_sense) + chip->battery.num_properties -= 2; + ret = power_supply_register(&client->dev, &chip->battery); if (ret) { dev_err(&client->dev, "failed: power supply register\n"); - i2c_set_clientdata(client, NULL); kfree(chip); return ret; } + /* Initialize registers according to values from the platform data */ + if (chip->pdata->init_data) + max17042_set_reg(client, chip->pdata->init_data, + chip->pdata->num_init_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); + } else { + if (chip->pdata->r_sns == 0) + chip->pdata->r_sns = MAX17042_DEFAULT_SNS_RESISTOR; } return 0; @@ -202,7 +238,6 @@ static int __devexit max17042_remove(struct i2c_client *client) struct max17042_chip *chip = i2c_get_clientdata(client); power_supply_unregister(&chip->battery); - i2c_set_clientdata(client, NULL); kfree(chip); return 0; } diff --git a/drivers/power/max8903_charger.c b/drivers/power/max8903_charger.c index 33ff0e37809..a9b0209a2f5 100644 --- a/drivers/power/max8903_charger.c +++ b/drivers/power/max8903_charger.c @@ -28,7 +28,7 @@ #include struct max8903_data { - struct max8903_pdata *pdata; + struct max8903_pdata pdata; struct device *dev; struct power_supply psy; bool fault; @@ -52,8 +52,8 @@ static int max8903_get_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_STATUS: val->intval = POWER_SUPPLY_STATUS_UNKNOWN; - if (data->pdata->chg) { - if (gpio_get_value(data->pdata->chg) == 0) + if (data->pdata.chg) { + if (gpio_get_value(data->pdata.chg) == 0) val->intval = POWER_SUPPLY_STATUS_CHARGING; else if (data->usb_in || data->ta_in) val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; @@ -80,7 +80,7 @@ static int max8903_get_property(struct power_supply *psy, static irqreturn_t max8903_dcin(int irq, void *_data) { struct max8903_data *data = _data; - struct max8903_pdata *pdata = data->pdata; + struct max8903_pdata *pdata = &data->pdata; bool ta_in; enum power_supply_type old_type; @@ -121,7 +121,7 @@ static irqreturn_t max8903_dcin(int irq, void *_data) static irqreturn_t max8903_usbin(int irq, void *_data) { struct max8903_data *data = _data; - struct max8903_pdata *pdata = data->pdata; + struct max8903_pdata *pdata = &data->pdata; bool usb_in; enum power_supply_type old_type; @@ -160,7 +160,7 @@ static irqreturn_t max8903_usbin(int irq, void *_data) static irqreturn_t max8903_fault(int irq, void *_data) { struct max8903_data *data = _data; - struct max8903_pdata *pdata = data->pdata; + struct max8903_pdata *pdata = &data->pdata; bool fault; fault = gpio_get_value(pdata->flt) ? false : true; @@ -193,7 +193,7 @@ static __devinit int max8903_probe(struct platform_device *pdev) dev_err(dev, "Cannot allocate memory.\n"); return -ENOMEM; } - data->pdata = pdata; + memcpy(&data->pdata, pdata, sizeof(struct max8903_pdata)); data->dev = dev; platform_set_drvdata(pdev, data); @@ -349,7 +349,7 @@ static __devexit int max8903_remove(struct platform_device *pdev) struct max8903_data *data = platform_get_drvdata(pdev); if (data) { - struct max8903_pdata *pdata = data->pdata; + struct max8903_pdata *pdata = &data->pdata; if (pdata->flt) free_irq(gpio_to_irq(pdata->flt), data); diff --git a/drivers/power/max8997_charger.c b/drivers/power/max8997_charger.c new file mode 100644 index 00000000000..7106b49b26e --- /dev/null +++ b/drivers/power/max8997_charger.c @@ -0,0 +1,206 @@ +/* + * max8997_charger.c - Power supply consumer driver for the Maxim 8997/8966 + * + * Copyright (C) 2011 Samsung Electronics + * MyungJoo Ham + * + * 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 + */ + +#include +#include +#include +#include +#include +#include + +struct charger_data { + struct device *dev; + struct max8997_dev *iodev; + struct power_supply battery; +}; + +static enum power_supply_property max8997_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, /* "FULL" or "NOT FULL" only. */ + POWER_SUPPLY_PROP_PRESENT, /* the presence of battery */ + POWER_SUPPLY_PROP_ONLINE, /* charger is active or not */ +}; + +/* Note that the charger control is done by a current regulator "CHARGER" */ +static int max8997_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct charger_data *charger = container_of(psy, + struct charger_data, battery); + struct i2c_client *i2c = charger->iodev->i2c; + int ret; + u8 reg; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = 0; + ret = max8997_read_reg(i2c, MAX8997_REG_STATUS4, ®); + if (ret) + return ret; + if ((reg & (1 << 0)) == 0x1) + val->intval = POWER_SUPPLY_STATUS_FULL; + + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 0; + ret = max8997_read_reg(i2c, MAX8997_REG_STATUS4, ®); + if (ret) + return ret; + if ((reg & (1 << 2)) == 0x0) + val->intval = 1; + + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = 0; + ret = max8997_read_reg(i2c, MAX8997_REG_STATUS4, ®); + if (ret) + return ret; + /* DCINOK */ + if (reg & (1 << 1)) + val->intval = 1; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static __devinit int max8997_battery_probe(struct platform_device *pdev) +{ + int ret = 0; + struct charger_data *charger; + struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); + struct max8997_platform_data *pdata = dev_get_platdata(iodev->dev); + + if (!pdata) + return -EINVAL; + + if (pdata->eoc_mA) { + u8 val = (pdata->eoc_mA - 50) / 10; + if (val < 0) + val = 0; + if (val > 0xf) + val = 0xf; + + ret = max8997_update_reg(iodev->i2c, + MAX8997_REG_MBCCTRL5, val, 0xf); + if (ret < 0) { + dev_err(&pdev->dev, "Cannot use i2c bus.\n"); + return ret; + } + } + + switch (pdata->timeout) { + case 5: + ret = max8997_update_reg(iodev->i2c, MAX8997_REG_MBCCTRL1, + 0x2 << 4, 0x7 << 4); + break; + case 6: + ret = max8997_update_reg(iodev->i2c, MAX8997_REG_MBCCTRL1, + 0x3 << 4, 0x7 << 4); + break; + case 7: + ret = max8997_update_reg(iodev->i2c, MAX8997_REG_MBCCTRL1, + 0x4 << 4, 0x7 << 4); + break; + case 0: + ret = max8997_update_reg(iodev->i2c, MAX8997_REG_MBCCTRL1, + 0x7 << 4, 0x7 << 4); + break; + default: + dev_err(&pdev->dev, "incorrect timeout value (%d)\n", + pdata->timeout); + return -EINVAL; + } + if (ret < 0) { + dev_err(&pdev->dev, "Cannot use i2c bus.\n"); + return ret; + } + + charger = kzalloc(sizeof(struct charger_data), GFP_KERNEL); + if (charger == NULL) { + dev_err(&pdev->dev, "Cannot allocate memory.\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, charger); + + charger->battery.name = "max8997_pmic"; + charger->battery.type = POWER_SUPPLY_TYPE_BATTERY; + charger->battery.get_property = max8997_battery_get_property; + charger->battery.properties = max8997_battery_props; + charger->battery.num_properties = ARRAY_SIZE(max8997_battery_props); + + charger->dev = &pdev->dev; + charger->iodev = iodev; + + ret = power_supply_register(&pdev->dev, &charger->battery); + if (ret) { + dev_err(&pdev->dev, "failed: power supply register\n"); + goto err; + } + + return 0; +err: + kfree(charger); + return ret; +} + +static int __devexit max8997_battery_remove(struct platform_device *pdev) +{ + struct charger_data *charger = platform_get_drvdata(pdev); + + power_supply_unregister(&charger->battery); + kfree(charger); + return 0; +} + +static const struct platform_device_id max8997_battery_id[] = { + { "max8997-battery", 0 }, +}; + +static struct platform_driver max8997_battery_driver = { + .driver = { + .name = "max8997-battery", + .owner = THIS_MODULE, + }, + .probe = max8997_battery_probe, + .remove = __devexit_p(max8997_battery_remove), + .id_table = max8997_battery_id, +}; + +static int __init max8997_battery_init(void) +{ + return platform_driver_register(&max8997_battery_driver); +} +subsys_initcall(max8997_battery_init); + +static void __exit max8997_battery_cleanup(void) +{ + platform_driver_unregister(&max8997_battery_driver); +} +module_exit(max8997_battery_cleanup); + +MODULE_DESCRIPTION("MAXIM 8997/8966 battery control driver"); +MODULE_AUTHOR("MyungJoo Ham "); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/max8998_charger.c b/drivers/power/max8998_charger.c new file mode 100644 index 00000000000..cc21fa2120b --- /dev/null +++ b/drivers/power/max8998_charger.c @@ -0,0 +1,218 @@ +/* + * max8998_charger.c - Power supply consumer driver for the Maxim 8998/LP3974 + * + * Copyright (C) 2009-2010 Samsung Electronics + * MyungJoo Ham + * + * 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 + */ + +#include +#include +#include +#include +#include +#include + +struct max8998_battery_data { + struct device *dev; + struct max8998_dev *iodev; + struct power_supply battery; +}; + +static enum power_supply_property max8998_battery_props[] = { + POWER_SUPPLY_PROP_PRESENT, /* the presence of battery */ + POWER_SUPPLY_PROP_ONLINE, /* charger is active or not */ +}; + +/* Note that the charger control is done by a current regulator "CHARGER" */ +static int max8998_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct max8998_battery_data *max8998 = container_of(psy, + struct max8998_battery_data, battery); + struct i2c_client *i2c = max8998->iodev->i2c; + int ret; + u8 reg; + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + ret = max8998_read_reg(i2c, MAX8998_REG_STATUS2, ®); + if (ret) + return ret; + if (reg & (1 << 4)) + val->intval = 0; + else + val->intval = 1; + break; + case POWER_SUPPLY_PROP_ONLINE: + ret = max8998_read_reg(i2c, MAX8998_REG_STATUS2, ®); + if (ret) + return ret; + if (reg & (1 << 3)) + val->intval = 0; + else + val->intval = 1; + break; + default: + return -EINVAL; + } + + return 0; +} + +static __devinit int max8998_battery_probe(struct platform_device *pdev) +{ + struct max8998_dev *iodev = dev_get_drvdata(pdev->dev.parent); + struct max8998_platform_data *pdata = dev_get_platdata(iodev->dev); + struct max8998_battery_data *max8998; + struct i2c_client *i2c; + int ret = 0; + + if (!pdata) { + dev_err(pdev->dev.parent, "No platform init data supplied\n"); + return -ENODEV; + } + + max8998 = kzalloc(sizeof(struct max8998_battery_data), GFP_KERNEL); + if (!max8998) + return -ENOMEM; + + max8998->dev = &pdev->dev; + max8998->iodev = iodev; + platform_set_drvdata(pdev, max8998); + i2c = max8998->iodev->i2c; + + /* Setup "End of Charge" */ + /* If EOC value equals 0, + * remain value set from bootloader or default value */ + if (pdata->eoc >= 10 && pdata->eoc <= 45) { + max8998_update_reg(i2c, MAX8998_REG_CHGR1, + (pdata->eoc / 5 - 2) << 5, 0x7 << 5); + } else if (pdata->eoc == 0) { + dev_dbg(max8998->dev, + "EOC value not set: leave it unchanged.\n"); + } else { + dev_err(max8998->dev, "Invalid EOC value\n"); + ret = -EINVAL; + goto err; + } + + /* Setup Charge Restart Level */ + switch (pdata->restart) { + case 100: + max8998_update_reg(i2c, MAX8998_REG_CHGR1, 0x1 << 3, 0x3 << 3); + break; + case 150: + max8998_update_reg(i2c, MAX8998_REG_CHGR1, 0x0 << 3, 0x3 << 3); + break; + case 200: + max8998_update_reg(i2c, MAX8998_REG_CHGR1, 0x2 << 3, 0x3 << 3); + break; + case -1: + max8998_update_reg(i2c, MAX8998_REG_CHGR1, 0x3 << 3, 0x3 << 3); + break; + case 0: + dev_dbg(max8998->dev, + "Restart Level not set: leave it unchanged.\n"); + break; + default: + dev_err(max8998->dev, "Invalid Restart Level\n"); + ret = -EINVAL; + goto err; + } + + /* Setup Charge Full Timeout */ + switch (pdata->timeout) { + case 5: + max8998_update_reg(i2c, MAX8998_REG_CHGR2, 0x0 << 4, 0x3 << 4); + break; + case 6: + max8998_update_reg(i2c, MAX8998_REG_CHGR2, 0x1 << 4, 0x3 << 4); + break; + case 7: + max8998_update_reg(i2c, MAX8998_REG_CHGR2, 0x2 << 4, 0x3 << 4); + break; + case -1: + max8998_update_reg(i2c, MAX8998_REG_CHGR2, 0x3 << 4, 0x3 << 4); + break; + case 0: + dev_dbg(max8998->dev, + "Full Timeout not set: leave it unchanged.\n"); + default: + dev_err(max8998->dev, "Invalid Full Timeout value\n"); + ret = -EINVAL; + goto err; + } + + max8998->battery.name = "max8998_pmic"; + max8998->battery.type = POWER_SUPPLY_TYPE_BATTERY; + max8998->battery.get_property = max8998_battery_get_property; + max8998->battery.properties = max8998_battery_props; + max8998->battery.num_properties = ARRAY_SIZE(max8998_battery_props); + + ret = power_supply_register(max8998->dev, &max8998->battery); + if (ret) { + dev_err(max8998->dev, "failed: power supply register\n"); + goto err; + } + + return 0; +err: + kfree(max8998); + return ret; +} + +static int __devexit max8998_battery_remove(struct platform_device *pdev) +{ + struct max8998_battery_data *max8998 = platform_get_drvdata(pdev); + + power_supply_unregister(&max8998->battery); + kfree(max8998); + + return 0; +} + +static const struct platform_device_id max8998_battery_id[] = { + { "max8998-battery", TYPE_MAX8998 }, +}; + +static struct platform_driver max8998_battery_driver = { + .driver = { + .name = "max8998-battery", + .owner = THIS_MODULE, + }, + .probe = max8998_battery_probe, + .remove = __devexit_p(max8998_battery_remove), + .id_table = max8998_battery_id, +}; + +static int __init max8998_battery_init(void) +{ + return platform_driver_register(&max8998_battery_driver); +} +module_init(max8998_battery_init); + +static void __exit max8998_battery_cleanup(void) +{ + platform_driver_unregister(&max8998_battery_driver); +} +module_exit(max8998_battery_cleanup); + +MODULE_DESCRIPTION("MAXIM 8998 battery control driver"); +MODULE_AUTHOR("MyungJoo Ham "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:max8998-battery"); diff --git a/drivers/power/s3c_adc_battery.c b/drivers/power/s3c_adc_battery.c index d36c289aaef..a675e31b4f1 100644 --- a/drivers/power/s3c_adc_battery.c +++ b/drivers/power/s3c_adc_battery.c @@ -266,7 +266,7 @@ static irqreturn_t s3c_adc_bat_charged(int irq, void *dev_id) return IRQ_HANDLED; } -static int __init s3c_adc_bat_probe(struct platform_device *pdev) +static int __devinit s3c_adc_bat_probe(struct platform_device *pdev) { struct s3c_adc_client *client; struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data; diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 92c16e1677b..54b9198fa57 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -62,7 +62,7 @@ #define TWL4030_MSTATEC_COMPLETE4 0x0e static bool allow_usb; -module_param(allow_usb, bool, 1); +module_param(allow_usb, bool, 0644); MODULE_PARM_DESC(allow_usb, "Allow USB charge drawing default current"); struct twl4030_bci { @@ -425,7 +425,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) { struct twl4030_bci *bci; int ret; - int reg; + u32 reg; bci = kzalloc(sizeof(*bci), GFP_KERNEL); if (bci == NULL) @@ -486,7 +486,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) } /* Enable interrupts now. */ - reg = ~(TWL4030_ICHGLOW | TWL4030_ICHGEOC | TWL4030_TBATOR2 | + reg = ~(u32)(TWL4030_ICHGLOW | TWL4030_ICHGEOC | TWL4030_TBATOR2 | TWL4030_TBATOR1 | TWL4030_BATSTS); ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg, TWL4030_INTERRUPTS_BCIIMR1A); @@ -495,7 +495,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) goto fail_unmask_interrupts; } - reg = ~(TWL4030_VBATOV | TWL4030_VBUSOV | TWL4030_ACCHGOV); + reg = ~(u32)(TWL4030_VBATOV | TWL4030_VBUSOV | TWL4030_ACCHGOV); ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg, TWL4030_INTERRUPTS_BCIIMR2A); if (ret < 0) @@ -572,7 +572,7 @@ static void __exit twl4030_bci_exit(void) } module_exit(twl4030_bci_exit); -MODULE_AUTHOR("Gražydas Ignotas"); +MODULE_AUTHOR("Gražvydas Ignotas"); MODULE_DESCRIPTION("TWL4030 Battery Charger Interface driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:twl4030_bci"); diff --git a/drivers/power/wm831x_backup.c b/drivers/power/wm831x_backup.c index 0fd130d80f5..e648cbea1e6 100644 --- a/drivers/power/wm831x_backup.c +++ b/drivers/power/wm831x_backup.c @@ -22,6 +22,7 @@ struct wm831x_backup { struct wm831x *wm831x; struct power_supply backup; + char name[20]; }; static int wm831x_backup_read_voltage(struct wm831x *wm831x, @@ -163,6 +164,7 @@ static enum power_supply_property wm831x_backup_props[] = { static __devinit int wm831x_backup_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); + struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data; struct wm831x_backup *devdata; struct power_supply *backup; int ret; @@ -182,7 +184,14 @@ static __devinit int wm831x_backup_probe(struct platform_device *pdev) */ wm831x_config_backup(wm831x); - backup->name = "wm831x-backup"; + if (wm831x_pdata && wm831x_pdata->wm831x_num) + snprintf(devdata->name, sizeof(devdata->name), + "wm831x-backup.%d", wm831x_pdata->wm831x_num); + else + snprintf(devdata->name, sizeof(devdata->name), + "wm831x-backup"); + + backup->name = devdata->name; backup->type = POWER_SUPPLY_TYPE_BATTERY; backup->properties = wm831x_backup_props; backup->num_properties = ARRAY_SIZE(wm831x_backup_props); @@ -203,6 +212,7 @@ static __devexit int wm831x_backup_remove(struct platform_device *pdev) struct wm831x_backup *devdata = platform_get_drvdata(pdev); power_supply_unregister(&devdata->backup); + kfree(devdata->backup.name); kfree(devdata); return 0; diff --git a/drivers/power/wm831x_power.c b/drivers/power/wm831x_power.c index ddf8cf5f320..6cc2ca6427f 100644 --- a/drivers/power/wm831x_power.c +++ b/drivers/power/wm831x_power.c @@ -24,6 +24,9 @@ struct wm831x_power { struct power_supply wall; struct power_supply usb; struct power_supply battery; + char wall_name[20]; + char usb_name[20]; + char battery_name[20]; }; static int wm831x_power_check_online(struct wm831x *wm831x, int supply, @@ -486,6 +489,7 @@ static irqreturn_t wm831x_pwr_src_irq(int irq, void *data) static __devinit int wm831x_power_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); + struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data; struct wm831x_power *power; struct power_supply *usb; struct power_supply *battery; @@ -503,12 +507,28 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev) battery = &power->battery; wall = &power->wall; + if (wm831x_pdata && wm831x_pdata->wm831x_num) { + snprintf(power->wall_name, sizeof(power->wall_name), + "wm831x-wall.%d", wm831x_pdata->wm831x_num); + snprintf(power->battery_name, sizeof(power->wall_name), + "wm831x-battery.%d", wm831x_pdata->wm831x_num); + snprintf(power->usb_name, sizeof(power->wall_name), + "wm831x-usb.%d", wm831x_pdata->wm831x_num); + } else { + snprintf(power->wall_name, sizeof(power->wall_name), + "wm831x-wall"); + snprintf(power->battery_name, sizeof(power->wall_name), + "wm831x-battery"); + snprintf(power->usb_name, sizeof(power->wall_name), + "wm831x-usb"); + } + /* We ignore configuration failures since we can still read back * the status without enabling the charger. */ wm831x_config_battery(wm831x); - wall->name = "wm831x-wall"; + wall->name = power->wall_name; wall->type = POWER_SUPPLY_TYPE_MAINS; wall->properties = wm831x_wall_props; wall->num_properties = ARRAY_SIZE(wm831x_wall_props); @@ -517,7 +537,7 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev) if (ret) goto err_kmalloc; - battery->name = "wm831x-battery"; + battery->name = power->battery_name; battery->properties = wm831x_bat_props; battery->num_properties = ARRAY_SIZE(wm831x_bat_props); battery->get_property = wm831x_bat_get_prop; @@ -526,7 +546,7 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev) if (ret) goto err_wall; - usb->name = "wm831x-usb", + usb->name = power->usb_name, usb->type = POWER_SUPPLY_TYPE_USB; usb->properties = wm831x_usb_props; usb->num_properties = ARRAY_SIZE(wm831x_usb_props); diff --git a/include/linux/mfd/max8997.h b/include/linux/mfd/max8997.h index 60931d08942..0bbd13dbe33 100644 --- a/include/linux/mfd/max8997.h +++ b/include/linux/mfd/max8997.h @@ -107,11 +107,16 @@ struct max8997_platform_data { unsigned int buck5_voltage[8]; bool buck5_gpiodvs; + /* ---- Charger control ---- */ + /* eoc stands for 'end of charge' */ + int eoc_mA; /* 50 ~ 200mA by 10mA step */ + /* charge Full Timeout */ + int timeout; /* 0 (no timeout), 5, 6, 7 hours */ + /* MUIC: Not implemented */ /* HAPTIC: Not implemented */ /* RTC: Not implemented */ /* Flash: Not implemented */ - /* Charger control: Not implemented */ }; #endif /* __LINUX_MFD_MAX8998_H */ diff --git a/include/linux/mfd/max8998.h b/include/linux/mfd/max8998.h index 61daa167b57..f4f0dfa4698 100644 --- a/include/linux/mfd/max8998.h +++ b/include/linux/mfd/max8998.h @@ -87,6 +87,15 @@ struct max8998_regulator_data { * @wakeup: Allow to wake up from suspend * @rtc_delay: LP3974 RTC chip bug that requires delay after a register * write before reading it. + * @eoc: End of Charge Level in percent: 10% ~ 45% by 5% step + * If it equals 0, leave it unchanged. + * Otherwise, it is a invalid value. + * @restart: Restart Level in mV: 100, 150, 200, and -1 for disable. + * If it equals 0, leave it unchanged. + * Otherwise, it is a invalid value. + * @timeout: Full Timeout in hours: 5, 6, 7, and -1 for disable. + * If it equals 0, leave it unchanged. + * Otherwise, leave it unchanged. */ struct max8998_platform_data { struct max8998_regulator_data *regulators; @@ -107,6 +116,9 @@ struct max8998_platform_data { int buck2_default_idx; bool wakeup; bool rtc_delay; + int eoc; + int restart; + int timeout; }; #endif /* __LINUX_MFD_MAX8998_H */ diff --git a/include/linux/power/bq20z75.h b/include/linux/power/bq20z75.h index b0843b68af9..1398eb004e8 100644 --- a/include/linux/power/bq20z75.h +++ b/include/linux/power/bq20z75.h @@ -29,11 +29,14 @@ * @battery_detect: GPIO which is used to detect battery presence * @battery_detect_present: gpio state when battery is present (0 / 1) * @i2c_retry_count: # of times to retry on i2c IO failure + * @poll_retry_count: # of times to retry looking for new status after + * external change notification */ struct bq20z75_platform_data { int battery_detect; int battery_detect_present; int i2c_retry_count; + int poll_retry_count; }; #endif diff --git a/include/linux/power/max17042_battery.h b/include/linux/power/max17042_battery.h index 7995deb8bfc..fe99211fb2b 100644 --- a/include/linux/power/max17042_battery.h +++ b/include/linux/power/max17042_battery.h @@ -23,8 +23,99 @@ #ifndef __MAX17042_BATTERY_H_ #define __MAX17042_BATTERY_H_ +#define MAX17042_STATUS_BattAbsent (1 << 3) +#define MAX17042_BATTERY_FULL (100) +#define MAX17042_DEFAULT_SNS_RESISTOR (10000) + +enum max17042_register { + MAX17042_STATUS = 0x00, + MAX17042_VALRT_Th = 0x01, + MAX17042_TALRT_Th = 0x02, + MAX17042_SALRT_Th = 0x03, + MAX17042_AtRate = 0x04, + MAX17042_RepCap = 0x05, + MAX17042_RepSOC = 0x06, + MAX17042_Age = 0x07, + MAX17042_TEMP = 0x08, + MAX17042_VCELL = 0x09, + MAX17042_Current = 0x0A, + MAX17042_AvgCurrent = 0x0B, + MAX17042_Qresidual = 0x0C, + MAX17042_SOC = 0x0D, + MAX17042_AvSOC = 0x0E, + MAX17042_RemCap = 0x0F, + MAX17402_FullCAP = 0x10, + MAX17042_TTE = 0x11, + MAX17042_V_empty = 0x12, + + MAX17042_RSLOW = 0x14, + + MAX17042_AvgTA = 0x16, + MAX17042_Cycles = 0x17, + MAX17042_DesignCap = 0x18, + MAX17042_AvgVCELL = 0x19, + MAX17042_MinMaxTemp = 0x1A, + MAX17042_MinMaxVolt = 0x1B, + MAX17042_MinMaxCurr = 0x1C, + MAX17042_CONFIG = 0x1D, + MAX17042_ICHGTerm = 0x1E, + MAX17042_AvCap = 0x1F, + MAX17042_ManName = 0x20, + MAX17042_DevName = 0x21, + MAX17042_DevChem = 0x22, + + MAX17042_TempNom = 0x24, + MAX17042_TempCold = 0x25, + MAX17042_TempHot = 0x26, + MAX17042_AIN = 0x27, + MAX17042_LearnCFG = 0x28, + MAX17042_SHFTCFG = 0x29, + MAX17042_RelaxCFG = 0x2A, + MAX17042_MiscCFG = 0x2B, + MAX17042_TGAIN = 0x2C, + MAx17042_TOFF = 0x2D, + MAX17042_CGAIN = 0x2E, + MAX17042_COFF = 0x2F, + + MAX17042_Q_empty = 0x33, + MAX17042_T_empty = 0x34, + + MAX17042_RCOMP0 = 0x38, + MAX17042_TempCo = 0x39, + MAX17042_Rx = 0x3A, + MAX17042_T_empty0 = 0x3B, + MAX17042_TaskPeriod = 0x3C, + MAX17042_FSTAT = 0x3D, + + MAX17042_SHDNTIMER = 0x3F, + + MAX17042_VFRemCap = 0x4A, + + MAX17042_QH = 0x4D, + MAX17042_QL = 0x4E, +}; + +/* + * used for setting a register to a desired value + * addr : address for a register + * data : setting value for the register + */ +struct max17042_reg_data { + u8 addr; + u16 data; +}; + struct max17042_platform_data { + struct max17042_reg_data *init_data; + int num_init_data; /* Number of enties in init_data array */ bool enable_current_sense; + + /* + * R_sns in micro-ohms. + * default 10000 (if r_sns = 0) as it is the recommended value by + * the datasheet although it can be changed by board designers. + */ + unsigned int r_sns; }; #endif /* __MAX17042_BATTERY_H_ */