diff options
author | Jason Chen <b02280@freescale.com> | 2011-02-21 11:29:19 +0800 |
---|---|---|
committer | Jason Chen <b02280@freescale.com> | 2011-02-21 12:49:45 +0800 |
commit | 1431a283524c2e87970b3a13ff28ff0b38cb5f53 (patch) | |
tree | cad77546fd856595a8339e9eadf75fe6edfcb835 /drivers | |
parent | 11e8de6cd235b45076001f127a63a30d9dafc78a (diff) |
ENGR00139380-2 battery charger: add max17085 battery charger support
Add max17085 battery charger support
Signed-off-by: Jason Chen <b02280@freescale.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/hwmon/da9052-adc.c | 10 | ||||
-rw-r--r-- | drivers/power/Kconfig | 6 | ||||
-rw-r--r-- | drivers/power/Makefile | 1 | ||||
-rw-r--r-- | drivers/power/max17085_battery.c | 371 |
4 files changed, 388 insertions, 0 deletions
diff --git a/drivers/hwmon/da9052-adc.c b/drivers/hwmon/da9052-adc.c index 647e580e2131..b8b6e88f1d2d 100644 --- a/drivers/hwmon/da9052-adc.c +++ b/drivers/hwmon/da9052-adc.c @@ -39,6 +39,15 @@ static const char *input_names[] = { [DA9052_ADC_VBBAT] = "BACK-UP BATTERY TEMP", }; +struct da9052 *da9052_local; + +int da9052_adc_read(unsigned char channel) +{ + if (da9052_local != NULL) + return da9052_manual_read(da9052_local, channel); + return -1; +} +EXPORT_SYMBOL(da9052_adc_read); int da9052_manual_read(struct da9052 *da9052, unsigned char channel) @@ -591,6 +600,7 @@ static int __init da9052_adc_probe(struct platform_device *pdev) /* Initialize mutex required for ADC Manual read */ mutex_init(&priv->da9052->manconv_lock); + da9052_local = priv->da9052; return 0; out_err_create2: diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 20d0cd22f606..5e578eb44b5b 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -131,6 +131,12 @@ config BATTERY_DA9052 To compile this driver as a module, choose M here. +config BATTERY_MAX17085 + tristate "Maxim MAX17085 charger" + depends on PMIC_DA9052 && SENSORS_DA9052 + help + Say Y to include support for the battery on the MAX17085. + config BATTERY_MAX17040 tristate "Maxim MAX17040 Fuel Gauge" depends on I2C diff --git a/drivers/power/Makefile b/drivers/power/Makefile index d20c49bd29ff..0d0ddd90cedd 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -37,3 +37,4 @@ obj-$(CONFIG_BATTERY_Z2) += z2_battery.o obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o obj-$(CONFIG_BATTERY_MXS) += mxs/ obj-$(CONFIG_BATTERY_DS2438) += ds2438_battery.o +obj-$(CONFIG_BATTERY_MAX17085) += max17085_battery.o diff --git a/drivers/power/max17085_battery.c b/drivers/power/max17085_battery.c new file mode 100644 index 000000000000..5f88cc4b119d --- /dev/null +++ b/drivers/power/max17085_battery.c @@ -0,0 +1,371 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/* + * MAX17085 Battery driver + */ + +#include <linux/module.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/jiffies.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <mach/gpio.h> + +struct max17085_chip { + struct device *dev; + + /*gpio*/ + int pwr_good; + int ac_in; + int charge_now; + int charge_done; + + struct power_supply ac; + struct power_supply bat; + struct delayed_work work; + + int online; + int health; + int status; + int volt; + int cap; +}; + +#define MAX17085_DELAY 1000 + +static int max17085_bat_get_mfr(struct power_supply *psy, + union power_supply_propval *val) +{ + val->strval = "unknow"; + return 0; +} + +static int max17085_bat_get_tech(struct power_supply *psy, + union power_supply_propval *val) +{ + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + return 0; +} + +static int max17085_bat_get_cap(struct power_supply *psy, + union power_supply_propval *val) +{ + struct max17085_chip *chip = container_of(psy, + struct max17085_chip, bat); + + val->intval = chip->cap; + return 0; +} + +static int max17085_bat_get_volt(struct power_supply *psy, + union power_supply_propval *val) +{ + struct max17085_chip *chip = container_of(psy, + struct max17085_chip, bat); + + val->intval = chip->volt; + return 0; +} + +static int max17085_bat_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret = 0; + struct max17085_chip *chip = container_of(psy, + struct max17085_chip, bat); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = chip->status; + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = chip->health; + break; + case POWER_SUPPLY_PROP_CAPACITY: + ret = max17085_bat_get_cap(psy, val); + if (ret) + return ret; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = max17085_bat_get_volt(psy, val); + if (ret) + return ret; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + ret = max17085_bat_get_mfr(psy, val); + if (ret) + return ret; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + ret = max17085_bat_get_tech(psy, val); + if (ret) + return ret; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_TEMP: + case POWER_SUPPLY_PROP_VOLTAGE_AVG: + case POWER_SUPPLY_PROP_CURRENT_AVG: + case POWER_SUPPLY_PROP_TEMP_AMBIENT: + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + case POWER_SUPPLY_PROP_SERIAL_NUMBER: + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int max17085_ac_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct max17085_chip *chip = container_of(psy, + struct max17085_chip, ac); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = chip->online; + break; + default: + break; + } + + return 0; +} + +static void max17085_get_online(struct max17085_chip *chip) +{ + int level = gpio_get_value(chip->ac_in); + + chip->online = !level; +} + +static void max17085_get_health(struct max17085_chip *chip) +{ + int level = gpio_get_value(chip->pwr_good); + + if (level && (chip->volt >= 0)) + chip->health = POWER_SUPPLY_HEALTH_GOOD; + else + chip->health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; +} + +extern int da9052_adc_read(unsigned char channel); +#define VOLT_REG_TO_MV(val) ((val * 2500) / 1024) +static void max17085_get_volt(struct max17085_chip *chip) +{ + int val; + val = da9052_adc_read(6); + if (val > 0) + chip->volt = VOLT_REG_TO_MV(val); + else + chip->volt = -1; +} + +#define BATT_EMPTY_MV 9000 +#define BATT_FULL_MV 12600 +#define BATT_TO_ADC_SCALE 11 +static void max17085_get_cap(struct max17085_chip *chip) +{ + if (chip->volt >= 0) { + int voltage_uV; + voltage_uV = chip->volt * 1000 * BATT_TO_ADC_SCALE; + chip->cap = (voltage_uV/1000 - BATT_EMPTY_MV) * 100/ + (BATT_FULL_MV - BATT_EMPTY_MV); + } else + chip->cap = 0; +} + +static void max17085_update_status(struct max17085_chip *chip) +{ + if (chip->online) { + if (chip->volt < BATT_FULL_MV) + chip->status = + POWER_SUPPLY_STATUS_CHARGING; + else + chip->status = + POWER_SUPPLY_STATUS_NOT_CHARGING; + } else + chip->status = POWER_SUPPLY_STATUS_DISCHARGING; + + if (chip->cap < 20) { + gpio_set_value(chip->charge_now, 1); + gpio_set_value(chip->charge_done, 0); + } else if (chip->cap < 80) { + gpio_set_value(chip->charge_now, 1); + gpio_set_value(chip->charge_done, 1); + } else { + gpio_set_value(chip->charge_now, 0); + gpio_set_value(chip->charge_done, 1); + } +} + +static void max17085_work(struct work_struct *work) +{ + struct max17085_chip *chip = container_of(work, + struct max17085_chip, work.work); + + max17085_get_online(chip); + max17085_get_volt(chip); + max17085_get_health(chip); + max17085_get_cap(chip); + max17085_update_status(chip); + + schedule_delayed_work(&chip->work, MAX17085_DELAY); +} + +static enum power_supply_property max17085_bat_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_AVG, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TEMP_AMBIENT, + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_SERIAL_NUMBER, + POWER_SUPPLY_PROP_CHARGE_COUNTER, +}; + +static enum power_supply_property max17085_ac_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static int max17085_bat_probe(struct platform_device *pdev) +{ + int ret = 0; + struct max17085_chip *chip; + struct resource *res; + + chip = kzalloc(sizeof(struct max17085_chip), GFP_KERNEL); + if (!chip) { + ret = -ENOMEM; + goto chip_alloc_failed; + } + + res = platform_get_resource_byname(pdev, + IORESOURCE_IO, "pwr-good"); + if (res == NULL) { + ret = -EINVAL; + goto resource_failed; + } + chip->pwr_good = res->start; + res = platform_get_resource_byname(pdev, + IORESOURCE_IO, "ac-in"); + if (res == NULL) { + ret = -EINVAL; + goto resource_failed; + } + chip->ac_in = res->start; + res = platform_get_resource_byname(pdev, + IORESOURCE_IO, "charge-now"); + if (res == NULL) { + ret = -EINVAL; + goto resource_failed; + } + chip->charge_now = res->start; + res = platform_get_resource_byname(pdev, + IORESOURCE_IO, "charge-done"); + if (res == NULL) { + ret = -EINVAL; + goto resource_failed; + } + chip->charge_done = res->start; + + chip->dev = &pdev->dev; + chip->bat.name = "battery"; + chip->bat.type = POWER_SUPPLY_TYPE_BATTERY; + chip->bat.properties = max17085_bat_props; + chip->bat.num_properties = ARRAY_SIZE(max17085_bat_props); + chip->bat.get_property = max17085_bat_get_property; + chip->bat.use_for_apm = 1; + + chip->ac.name = "ac"; + chip->ac.type = POWER_SUPPLY_TYPE_MAINS; + chip->ac.properties = max17085_ac_props; + chip->ac.num_properties = ARRAY_SIZE(max17085_ac_props); + chip->ac.get_property = max17085_ac_get_property; + + platform_set_drvdata(pdev, chip); + + ret = power_supply_register(&pdev->dev, &chip->ac); + if (ret) { + dev_err(chip->dev, "failed to register ac\n"); + goto register_ac_failed; + } + + ret = power_supply_register(&pdev->dev, &chip->bat); + if (ret) { + dev_err(chip->dev, "failed to register battery\n"); + goto register_batt_failed; + } + + INIT_DELAYED_WORK_DEFERRABLE(&chip->work, max17085_work); + schedule_delayed_work(&chip->work, MAX17085_DELAY); + + return ret; + +register_batt_failed: + power_supply_unregister(&chip->ac); +register_ac_failed: +resource_failed: + kfree(chip); +chip_alloc_failed: + return ret; +} + +static int max17085_bat_remove(struct platform_device *pdev) +{ + struct max17085_chip *chip = platform_get_drvdata(pdev); + + cancel_delayed_work(&chip->work); + power_supply_unregister(&chip->bat); + power_supply_unregister(&chip->ac); + kfree(chip); + + return 0; +} + +static struct platform_driver max17085_bat_driver = { + .driver = { + .name = "max17085_bat", + }, + .probe = max17085_bat_probe, + .remove = max17085_bat_remove, +}; + +static int __devinit max17085_bat_init(void) +{ + return platform_driver_register(&max17085_bat_driver); +} + +static void __devexit max17085_bat_exit(void) +{ + platform_driver_unregister(&max17085_bat_driver); +} + +module_init(max17085_bat_init); +module_exit(max17085_bat_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MAX17085 battery driver"); +MODULE_LICENSE("GPL"); |