diff options
author | Alex Gonzalez <alex.gonzalez@digi.com> | 2011-12-27 12:18:55 +0100 |
---|---|---|
committer | Alex Gonzalez <alex.gonzalez@digi.com> | 2011-12-27 13:51:41 +0100 |
commit | 38a9afd942404e75eca2646ce987809caae1536e (patch) | |
tree | 0e82f49c9853a222d2fdbb7d5e88a8a8a5bb26b4 | |
parent | 28d105e116892376e0409e2ba637fbe61c64303a (diff) |
ccxmx53: Add custom battery driver
This battery driver is based on the da9052-battery.c driver by Dialog, and on the DVT battery scripts specific to the CCXMX53 JSK.
The following limitations apply:
- No battery temperature measurements are possible on the CCXMX53 JSK.
- No events notifications are implemented.
- No junction temperature or battery temperature monitoring.
- VBUS charging not supported by JSK.
- The only battery technology supported is Lithium-Ion.
- Only standard working temperatures are supported.
Signed-off-by: Alex Gonzalez <alex.gonzalez@digi.com>
-rw-r--r-- | arch/arm/mach-mx5/mx53_ccwmx53js_pmic_da9053.c | 17 | ||||
-rw-r--r-- | drivers/power/Kconfig | 9 | ||||
-rw-r--r-- | drivers/power/Makefile | 1 | ||||
-rw-r--r-- | drivers/power/da9052-ccxmx53js-battery.c | 822 | ||||
-rw-r--r-- | include/linux/mfd/da9052/bat_ccxmx53js.h | 334 |
5 files changed, 1168 insertions, 15 deletions
diff --git a/arch/arm/mach-mx5/mx53_ccwmx53js_pmic_da9053.c b/arch/arm/mach-mx5/mx53_ccwmx53js_pmic_da9053.c index ac034a1c3252..5bba3c53ae0b 100644 --- a/arch/arm/mach-mx5/mx53_ccwmx53js_pmic_da9053.c +++ b/arch/arm/mach-mx5/mx53_ccwmx53js_pmic_da9053.c @@ -125,22 +125,9 @@ static struct da9052_leds_platform_data da9052_gpio_leds = { static struct da9052_bat_platform_data da9052_bat = { - .sw_temp_control_en = 0, - .monitoring_interval = 500, - .sw_bat_temp_threshold = 60, - .sw_junc_temp_threshold = 120, - .hysteresis_window_size = 1, - .current_monitoring_window = 10, - .bat_with_no_resistor = 62, - .bat_capacity_limit_low = 4, - .bat_capacity_full = 100, - .bat_capacity_limit_high = 70, - .chg_hysteresis_const = 89, - .hysteresis_reading_interval = 1000, - .hysteresis_no_of_reading = 10, - .filter_size = 4, + .monitoring_interval = 5000, .bat_volt_cutoff = 2800, - .vbat_first_valid_detect_iteration = 3, + .filter_size = 4, }; static void da9052_init_ssc_cache(struct da9052 *da9052) diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 5e578eb44b5b..5e35a2f6c84c 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -131,6 +131,15 @@ config BATTERY_DA9052 To compile this driver as a module, choose M here. +config BATTERY_JSCCXMX53_DA9052 + tristate "Dialog DA9052 Battery driver for the CCXMX53 JSK." + depends on PMIC_DA9052 + help + Say Y here to enable support for batteries charger integrated into + DA9052 PMIC on Digi's ConnectCore (w)i.MX53 JumpStart Kit. + + To compile this driver as a module, choose M here. + config BATTERY_MAX17085 tristate "Maxim MAX17085 charger" depends on PMIC_DA9052 && SENSORS_DA9052 diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 0d0ddd90cedd..fbdf510cf07b 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_TEST_POWER) += test_power.o obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o obj-$(CONFIG_BATTERY_DS2782) += ds2782_battery.o obj-$(CONFIG_BATTERY_DA9052) += da9052-battery.o +obj-$(CONFIG_BATTERY_JSCCXMX53_DA9052) += da9052-ccxmx53js-battery.o obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o diff --git a/drivers/power/da9052-ccxmx53js-battery.c b/drivers/power/da9052-ccxmx53js-battery.c new file mode 100644 index 000000000000..17b9f53afd1a --- /dev/null +++ b/drivers/power/da9052-ccxmx53js-battery.c @@ -0,0 +1,822 @@ +/* + * da9052-battery.c -- Batttery Driver for Dialog DA9052 on a CCXMX53 JSK. + * + * Copyright(c) 2009 Dialog Semiconductor Ltd. + * Copyright(c) 2011 Digi International Ltd. + * + * 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. + * + */ + +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/delay.h> +#include <linux/timer.h> +#include <linux/uaccess.h> +#include <linux/jiffies.h> +#include <linux/power_supply.h> +#include <linux/platform_device.h> +#include <linux/freezer.h> + +#include <linux/mfd/da9052/da9052.h> +#include <linux/mfd/da9052/reg.h> +#include <linux/mfd/da9052/bat_ccxmx53js.h> +#include <linux/mfd/da9052/adc.h> + +#define DA9052_BAT_DEVICE_NAME "da9052-bat" + +static const char __initdata banner[] = KERN_INFO "DA9052 battery driver on "\ + "a CCXMX53 JSK."; + +static struct da9052_bat_device bat_info; +static struct da9052_bat_status bat_status; +static struct monitoring_state monitoring_status; +struct power_supply_info battery_info; + +struct da9052_charger_device charger; + +static u16 bat_target_voltage; +static u16 filter_size = FILTER_SIZE; + +static int da9052_bat_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val); + +s32 monitoring_thread_pid; +u8 monitoring_thread_state = ACTIVE; +struct completion monitoring_thread_notifier; + +static enum power_supply_property da902_bat_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_AVG, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, +}; + +static int da9052_read(struct da9052 *da9052, u8 reg_address, u8 *reg_data) +{ + struct da9052_ssc_msg msg; + int ret; + + msg.addr = reg_address; + msg.data = 0; + + da9052_lock(da9052); + ret = da9052->read(da9052, &msg); + if (ret) + goto ssc_comm_err; + da9052_unlock(da9052); + + *reg_data = msg.data; + return 0; +ssc_comm_err: + da9052_unlock(da9052); + return ret; +} + +static int da9052_write(struct da9052 *da9052, u8 reg_address, u8 reg_data) +{ + struct da9052_ssc_msg msg; + int ret; + + msg.addr = reg_address; + msg.data = reg_data; + + da9052_lock(da9052); + ret = da9052->write(da9052, &msg); + if (ret) + goto ssc_comm_err; + da9052_unlock(da9052); + + return 0; +ssc_comm_err: + da9052_unlock(da9052); + return ret; +} + +static int pmic_write( struct da9052 *da9052, u8 reg_address, u8 reg_mask , u8 reg_data ) +{ + u8 value; + int ret = 0; + + if( (ret = da9052_read( da9052 , reg_address , &value )) == 0 ){ + value = (value & ~reg_mask) | reg_data; + if( (ret = da9052_write( da9052 , reg_address , value )) ) + pr_err("Error writing %08x into %d\n",value,reg_address); + } + else + pr_err("Error reading from %d\n",reg_address); + + return ret;; +} + +static s32 da9052_adc_read_ich(struct da9052 *da9052, u16 *data) +{ + struct da9052_ssc_msg msg; + da9052_lock(da9052); + /* Read charging conversion register */ + msg.addr = DA9052_ICHGAV_REG; + msg.data = 0; + if (da9052->read(da9052, &msg)) { + da9052_unlock(da9052); + return DA9052_SSC_FAIL; + } + da9052_unlock(da9052); + + *data = (u16)msg.data; + DA9052_DEBUG("Read 0x%08x\n", msg.data); + return SUCCESS; +} + +#if 0 +static s32 da9052_adc_read_vddout(struct da9052 *da9052, u16 *data) +{ + u8 reg_data; + s32 ret; + + ret = da9052_read(da9052, DA9052_ADCCONT_REG, ®_data); + if (ret) + return ret; + + if (!(reg_data & DA9052_ADCCONT_AUTOVDDEN)) { + reg_data = (reg_data | DA9052_ADCCONT_AUTOVDDEN); + + ret = da9052_write(da9052, DA9052_INPUTCONT_REG, reg_data); + if (ret) + return ret; + reg_data = 0x0; + + ret = da9052_read(da9052, DA9052_ADCCONT_REG, ®_data); + if (ret) + return ret; + + if (reg_data & DA9052_ADCCONT_ADCMODE) + msleep(1); + else + msleep(10); + + ret = da9052_read(da9052, DA9052_VDDRES_REG, ®_data); + if (ret) + return ret; + + *data = (u16)reg_data; + + ret = da9052_read(da9052, DA9052_ADCCONT_REG, ®_data); + if (ret) + return ret; + + reg_data = reg_data & ~(DA9052_ADCCONT_AUTOVDDEN); + ret = da9052_write(da9052, DA9052_ADCCONT_REG, reg_data); + if (ret) + return ret; + } else { + ret = da9052_read(da9052, DA9052_VDDRES_REG, ®_data); + if (ret) + return ret; + + *data = (u16)reg_data; + } + return 0; +} + +s32 da9052_adc_read_tjunc(struct da9052 *da9052, u16 *data) +{ + struct da9052_ssc_msg msg; + u16 temp; + + da9052_lock(da9052); + + /* Read TJunction conversion result */ + msg.addr = DA9052_TJUNCRES_REG; + if (da9052->read(da9052, &msg)) { + da9052_unlock(da9052); + return DA9052_SSC_FAIL; + } + temp = msg.data; + /* Read calibration for junction temperature */ + msg.addr = DA9052_TOFFSET_REG; + if (da9052->read(da9052, &msg)) { + da9052_unlock(da9052); + return DA9052_SSC_FAIL; + } + + da9052_unlock(da9052); + /* Calculate Junction temperature */ + *data = temp - (u16)msg.data; + DA9052_DEBUG("Read 0x%08x\n", *data); + return 0; +} +#endif + +s32 da9052_adc_read_vbat(struct da9052 *da9052, u16 *data) +{ + s32 ret; + + ret = da9052_manual_read(da9052, DA9052_ADC_VBAT); + if (ret == -EIO) { + *data = 0; + return ret; + } else { + *data = ret; + } + DA9052_DEBUG("Read 0x%08x\n", *data); + return 0; +} + + +static u16 filter_sample(u16 *buffer) +{ + u8 count; + u16 tempvalue = 0; + u16 ret; + + if (buffer == NULL) + return -EINVAL; + + for (count = 0; count < filter_size; count++) + tempvalue = tempvalue + *(buffer + count); + + ret = tempvalue/filter_size; + return ret; +} + + +#if 0 +static s32 da9052_bat_get_charger_vddout(struct da9052 *da9052, u16 *buffer) +{ + + u8 count; + u16 filterqueue[filter_size]; + + if (bat_status.status != DA9052_CHARGING_CC || + bat_status.status != DA9052_CHARGING_CV) + return -EIO; + + /* Measure the charger voltage using ADC function. Number + of read equal to average filter size*/ + for (count = 0; count < filter_size; count++) + if (da9052_adc_read_vddout(da9052, &filterqueue[count])) + return -EIO; + + /*Apply average filter */ + filterqueue[0] = filter_sample(filterqueue); + /* Convert the charger voltage in terms of mV */ + bat_info.vddout = vddout_reg_to_mV(filterqueue[0]); + *buffer = bat_info.vddout; + + return SUCCESS; +} +#endif + +static s32 da9052_bat_get_chg_current(struct da9052 *da9052, u16 *buffer) +{ + + if (bat_status.status == DA9052_DISCHARGING_WITHOUT_CHARGER) + return -EIO; + + /* Measure the Charger current using ADC function */ + if (da9052_adc_read_ich(da9052, buffer)){ + *buffer=0; + return -EIO; + } + + /* Convert the raw value in terms of mA */ + bat_info.chg_current = ichg_reg_to_mA(*buffer); + *buffer = bat_info.chg_current; + + DA9052_DEBUG("bat_info.chg_current %08x\n",bat_info.chg_current); + return 0; +} + +s32 da9052_bat_get_battery_voltage(struct da9052 *da9052, u16 *buffer) +{ + + u8 count; + u16 filterqueue[filter_size]; + + /* Measure the battery voltage using ADC function. + Number of read equal to average filter size*/ + for (count = 0; count < filter_size; count++) + if (da9052_adc_read_vbat(da9052, &filterqueue[count])) + return -EIO; + + /* Apply average filter */ + filterqueue[0] = filter_sample(filterqueue); + + /* Convert battery voltage raw value in terms of mV */ + bat_info.bat_voltage = volt_reg_to_mV(filterqueue[0]); + *buffer = bat_info.bat_voltage; + DA9052_DEBUG("bat_info.bat_voltage %08x\n",bat_info.bat_voltage); + return 0; +} + +#if DA9052_BAT_DEBUG +void get_bat_mode(u8 mode_num, char *temp_name) +{ + if (mode_num == DA9052_NONE) + sprintf(temp_name, "NONE"); + else if (mode_num == DA9052_CHARGING_CC || mode_num == DA9052_CHARGING_CV) + sprintf(temp_name, "CHARGING"); + else if (mode_num == DA9052_DISCHARGING_WITH_CHARGER) + sprintf(temp_name, "DISCHARGING_WITH_CHARGER"); + else if (mode_num == DA9052_DISCHARGING_WITHOUT_CHARGER) + sprintf(temp_name, "DISCHARGING_WITHOUT_CHARGER"); +} +#endif + +#if 0 +void get_charging_mode(u8 mode, char *temp_name) +{ + if (mode == DA9052_NONE) + sprintf(temp_name, "NONE"); + else if (mode == DA9052_PRECHARGING) + sprintf(temp_name, "PRECHARGING"); + else if (mode == DA9052_LINEARCHARGING) + sprintf(temp_name, "LINEARCHARGING"); + else if (mode == DA9052_CHARGEEND) + sprintf(temp_name, "CHARGEEND"); +} + +void get_charger_type(u8 type, char *temp_name) +{ + if (type == DA9052_USB_HUB) + sprintf(temp_name, "USB_HUB"); + else if (type == DA9052_NOCHARGER) + sprintf(temp_name, "NOCHARGER"); + else if (type == DA9052_USB_CHARGER) + sprintf(temp_name, "USB_CHARGER"); + else if (type == DA9052_WALL_CHARGER) + sprintf(temp_name, "WALL_CHARGER"); +} +#endif + +static void da9052_charger_status_update(struct da9052_charger_device + *chg_device) +{ + u8 regvalue = 0; + u16 ichgm = 0; + u16 vbat = 0; + + // Will return error and ichgm=0 if no charger present + da9052_bat_get_chg_current(chg_device->da9052,&ichgm); + if( da9052_bat_get_battery_voltage(chg_device->da9052, &vbat) ) + goto read_err; + + if( da9052_read(chg_device->da9052, DA9052_STATUSA_REG , ®value )) + goto read_err; + + if( (regvalue & DA9052_STATUSA_DCINSEL) == 0 ){ + bat_status.charger_type = DA9052_NOCHARGER; + bat_status.status = DA9052_DISCHARGING_WITHOUT_CHARGER; + } + else if ( vbat > VCHARGE_MAX && ichgm < ICHARGE_END ){ + bat_status.charger_type = DA9052_WALL_CHARGER; + bat_status.status = DA9052_CHARGEEND; + } + else if ( vbat > VCHARGE_MAX && ichgm > ICHARGE_END ){ + bat_status.charger_type = DA9052_WALL_CHARGER; + bat_status.status = DA9052_CHARGING_CV; + } + else if ( vbat > VDISCHG_MIN && ichgm > ICHARGE_CC ){ + bat_status.charger_type = DA9052_WALL_CHARGER; + bat_status.status = DA9052_CHARGING_CC; + } + else if ( vbat > VDISCHG_MIN ){ + bat_status.charger_type = DA9052_WALL_CHARGER; + bat_status.status = DA9052_CHARGING_CC; + } + else if ( vbat <= VDISCHG_MIN ){ + pmic_write( chg_device->da9052 , DA9052_BATCHG_REG , DA9052_BATCHG_ICHGBAT , ICHG_BAT_0); + pmic_write( chg_device->da9052 , DA9052_BATCHG_REG , DA9052_BATCHG_ICHGPRE , ICHG_PRE_60); + + // Will return error and ichgm=0 if no charger present + da9052_bat_get_chg_current(chg_device->da9052,&ichgm); + + if (ichgm > ICHARGE_PRECHG) { + bat_status.charger_type = DA9052_WALL_CHARGER; + bat_status.status = DA9052_PRECHARGING; + } + else{ + pmic_write( chg_device->da9052 , DA9052_BATCHG_REG , DA9052_BATCHG_ICHGBAT , ICHG_BAT_0); + pmic_write( chg_device->da9052 , DA9052_BATCHG_REG , DA9052_BATCHG_ICHGPRE , ICHG_PRE_0); + pmic_write( chg_device->da9052 , DA9052_BATCHG_REG , DA9052_BATCHG_ICHGBAT , ICHG_BAT_1890); + bat_status.charger_type = DA9052_WALL_CHARGER; + bat_status.status = DA9052_NONE; + } + } + DA9052_DEBUG("Status %s\n",get_bat_mode(bat_status.status)); + return; + +read_err: + pr_err("da9052: Error on read\n"); +return; + +} + +s32 da9052_get_bat_level(struct da9052_charger_device *chg_device) +{ + u16 vbat = 0; + u16 ichgm = 0; + + da9052_charger_status_update(chg_device); + + if( da9052_bat_get_battery_voltage(chg_device->da9052, &vbat) ) + goto read_err; + + // Will return error and ichgm=0 if no charger present + da9052_bat_get_chg_current(chg_device->da9052,&ichgm); + + switch(bat_status.status){ + case DA9052_DISCHARGING_WITHOUT_CHARGER: + case DA9052_DISCHARGING_WITH_CHARGER: + if( vbat > VDISCHARGE_100 ) + bat_status.cal_capacity = 100; + else if(vbat > VDISCHARGE_95) + bat_status.cal_capacity = 95; + else if(vbat > VDISCHARGE_90) + bat_status.cal_capacity = 90; + else if(vbat > VDISCHARGE_85) + bat_status.cal_capacity = 85; + else if(vbat > VDISCHARGE_80) + bat_status.cal_capacity = 80; + else if(vbat > VDISCHARGE_75) + bat_status.cal_capacity = 75; + else if(vbat > VDISCHARGE_65) + bat_status.cal_capacity = 65; + else if(vbat > VDISCHARGE_55) + bat_status.cal_capacity = 55; + else if(vbat > VDISCHARGE_50) + bat_status.cal_capacity = 50; + else if(vbat > VDISCHARGE_45) + bat_status.cal_capacity = 45; + else if(vbat > VDISCHARGE_40) + bat_status.cal_capacity = 40; + else if(vbat > VDISCHARGE_35) + bat_status.cal_capacity = 35; + else if(vbat > VDISCHARGE_25) + bat_status.cal_capacity = 25; + else if(vbat > VDISCHARGE_20) + bat_status.cal_capacity = 20; + else if(vbat > VDISCHARGE_15) + bat_status.cal_capacity = 15; + else if(vbat > VDISCHARGE_10) + bat_status.cal_capacity = 10; + else if(vbat > VDISCHARGE_5) + bat_status.cal_capacity = 5; + else + bat_status.cal_capacity = 2; + break; + case DA9052_CHARGING_CC: + if(vbat<VCHARGE_5) + bat_status.cal_capacity = 5; + else if(vbat<VCHARGE_10) + bat_status.cal_capacity = 10; + else if(vbat<VCHARGE_15) + bat_status.cal_capacity = 15; + else if(vbat<VCHARGE_20) + bat_status.cal_capacity = 20; + else if(vbat<VCHARGE_25) + bat_status.cal_capacity = 25; + else if(vbat<VCHARGE_30) + bat_status.cal_capacity = 30; + else if(vbat<VCHARGE_35) + bat_status.cal_capacity = 35; + else if(vbat<VCHARGE_40) + bat_status.cal_capacity = 40; + else if(vbat<VCHARGE_45) + bat_status.cal_capacity = 45; + else if(vbat<VCHARGE_50) + bat_status.cal_capacity = 50; + else if(vbat<VCHARGE_55) + bat_status.cal_capacity = 55; + else if(vbat<VCHARGE_60) + bat_status.cal_capacity = 60; + else if(vbat<VCHARGE_65) + bat_status.cal_capacity = 65; + else if(vbat<VCHARGE_70) + bat_status.cal_capacity = 70; + else if(vbat<VCHARGE_75) + bat_status.cal_capacity = 75; + else if(vbat<VCHARGE_80) + bat_status.cal_capacity = 80; + else + bat_status.cal_capacity = 90; + break; + case DA9052_CHARGING_CV: + if( ichgm > ICHARGE_85) + bat_status.cal_capacity = 85; + else if( ichgm > ICHARGE_90) + bat_status.cal_capacity = 90; + else if( ichgm > ICHARGE_95) + bat_status.cal_capacity = 95; + else + bat_status.cal_capacity = 100; + break; + case DA9052_CHARGEEND: + bat_status.cal_capacity = 100; + break; + case DA9052_PRECHARGING: + bat_status.cal_capacity = 0; + break; + default: + break; + } + + DA9052_DEBUG("bat_status.cal_capacity %d\n",bat_status.cal_capacity); + return 0; + +read_err: + pr_err("da9052: Error on read\n"); + return 0; +} + +static s32 monitoring_thread(void *data) +{ + s32 ret = 0; + + struct da9052_charger_device *chg_device = + (struct da9052_charger_device *)data; + + set_freezable(); + + while (monitoring_thread_state == ACTIVE) { + + /* Make this thread friendly to system suspend and resume */ + try_to_freeze(); + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(chg_device->monitoring_interval); + + da9052_charger_status_update(chg_device); + + ret = da9052_get_bat_level(chg_device); + if (!ret) { + /* BAT Capacity is low then + update the monitoring status*/ + if (bat_status.cal_capacity < + chg_device->bat_pdata->bat_capacity_limit_low) { + monitoring_status.bat_level_status = + TRUE; + monitoring_status.bat_level = + bat_status.cal_capacity; + } else { + monitoring_status.bat_level_status = 0; + monitoring_status.bat_level = + bat_status.cal_capacity; + } + } else + DA9052_DEBUG("Battery Measurement Fails = %d\n", ret); + } + + complete_and_exit(&monitoring_thread_notifier, 0); + + return 0; +} + +static int da9052_bat_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + s32 ret = 0; + + struct da9052_charger_device *chg_device = + container_of(psy, struct da9052_charger_device, psy); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (bat_status.status == DA9052_CHARGING_CC || + bat_status.status == DA9052_CHARGING_CV) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + + else if (bat_status.status == + DA9052_DISCHARGING_WITH_CHARGER) + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + + else if (bat_status.status == + DA9052_DISCHARGING_WITHOUT_CHARGER) + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + + else if (bat_status.status == DA9052_CHARGEEND) + val->intval = POWER_SUPPLY_STATUS_FULL; + + break; + case POWER_SUPPLY_PROP_ONLINE: + if (bat_status.charger_type == DA9052_NOCHARGER) + val->intval = 0; + else + val->intval = 1; + break; + case POWER_SUPPLY_PROP_PRESENT: + if (bat_status.status == DA9052_NONE ) + val->intval = 0; + else + val->intval = 1; + break; + case POWER_SUPPLY_PROP_HEALTH: + if (bat_status.health != POWER_SUPPLY_HEALTH_OVERHEAT) { + if (bat_status.cal_capacity < + chg_device->bat_pdata->bat_capacity_limit_low) + bat_status.health = POWER_SUPPLY_HEALTH_DEAD; + else + bat_status.health = POWER_SUPPLY_HEALTH_GOOD; + } + val->intval = bat_status.health; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = (bat_target_voltage * 1000); + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + val->intval = (chg_device->bat_pdata->bat_volt_cutoff * 1000); + break; + case POWER_SUPPLY_PROP_VOLTAGE_AVG: + val->intval = (bat_info.bat_voltage * 1000); + break; + case POWER_SUPPLY_PROP_CURRENT_AVG: + val->intval = (bat_info.chg_current * 1000); + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = bat_status.cal_capacity; + break; + case POWER_SUPPLY_PROP_CAPACITY_LEVEL: + if (bat_status.cal_capacity < + chg_device->bat_pdata->bat_capacity_limit_low) + val->intval = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; + + else if (bat_status.cal_capacity < + chg_device->bat_pdata->bat_capacity_limit_high) + val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; + + else if (bat_status.cal_capacity == + chg_device->bat_pdata->bat_capacity_full) + val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL; + + else if (bat_status.cal_capacity > + chg_device->bat_pdata->bat_capacity_limit_high) + val->intval = POWER_SUPPLY_CAPACITY_LEVEL_HIGH; + + else + val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = BAT_TYPE; + break; + default: + ret = -EINVAL; + break; + } + return 0; +} + +static void da9052_battery_setup_psy(struct da9052_charger_device *chg_device) +{ + battery_info.name = DA9052_BAT_DEVICE_NAME; + battery_info.technology = BAT_TYPE; + battery_info.voltage_max_design = + (chg_device->bat_target_voltage*1000); + battery_info.voltage_min_design = + (chg_device->bat_pdata->bat_volt_cutoff*1000); + battery_info.energy_full_design = + chg_device->bat_pdata->bat_capacity_full; + battery_info.energy_empty_design = + chg_device->bat_pdata->bat_capacity_limit_low; + battery_info.use_for_apm = 1; + + chg_device->psy.name = DA9052_BAT_DEVICE_NAME; + chg_device->psy.use_for_apm = 1; + chg_device->psy.type = POWER_SUPPLY_TYPE_BATTERY; + chg_device->psy.get_property = da9052_bat_get_property; + + chg_device->psy.properties = da902_bat_props; + chg_device->psy.num_properties = ARRAY_SIZE(da902_bat_props); + +}; + +static void da9052_ccxmx53_init( struct da9052 *da9052 ) +{ + // ISET_BUCK to maximum + pmic_write(da9052,DA9052_CHGBUCK_REG,DA9052_CHGBUCK_ISETBUCK,ISET_BUCK_1800); + // ISET_USB and ISET_DCIN to maximum + pmic_write(da9052,DA9052_ISET_REG,DA9052_ISET_ISETDCIN,ISET_DCIN_1800); + pmic_write(da9052,DA9052_ISET_REG,DA9052_ISET_ISETVBUS,ISET_VBUS_1800); + // ICHG_BAT to maximum + pmic_write(da9052,DA9052_BATCHG_REG,DA9052_BATCHG_ICHGBAT,ICHG_BAT_1890); + // ICHG_PRE to disable + pmic_write(da9052,DA9052_BATCHG_REG,DA9052_BATCHG_ICHGPRE,ICHG_PRE_0); + // VCHG_THR to 3.7V + pmic_write(da9052,DA9052_CHGCONT_REG,DA9052_CHGCONT_VCHTHR,VCH_THR_38); + // VCHG_BAT to IonLi voltage (4.2V) + pmic_write(da9052,DA9052_CHGCONT_REG,DA9052_CHGCONT_VCHGBAT,VCHG_BAT_LIION); + // Charge Time out disabled + pmic_write(da9052,DA9052_INPUTCONT_REG,DA9053_INPUTCONT_TCTR,TCTR_DIS); +} + +static s32 __devinit da9052_bat_probe(struct platform_device *pdev) +{ + struct da9052_bat_platform_data *pdata = pdev->dev.platform_data; + struct da9052_charger_device *chg_device; + u8 reg_data; + int ret; + + chg_device = kzalloc(sizeof(*chg_device), GFP_KERNEL); + if (!chg_device) + return -ENOMEM; + + chg_device->da9052 = dev_get_drvdata(pdev->dev.parent); + + chg_device->bat_pdata = pdata; + + platform_set_drvdata(pdev, chg_device); + + chg_device->monitoring_interval = + msecs_to_jiffies(chg_device->bat_pdata->monitoring_interval); + + if(chg_device->bat_pdata->filter_size) + filter_size = chg_device->bat_pdata->filter_size; + + ret = da9052_read(chg_device->da9052, DA9052_CHGCONT_REG, ®_data); + if (ret) + goto err_charger_init; + chg_device->charger_voltage_drop = bat_drop_reg_to_mV(reg_data && + DA9052_CHGCONT_TCTR); + chg_device->bat_target_voltage = + bat_reg_to_mV(reg_data && DA9052_CHGCONT_VCHGBAT); + bat_target_voltage = chg_device->bat_target_voltage; + + reg_data = 0; + ret = da9052_read(chg_device->da9052, DA9052_ICHGEND_REG, ®_data); + if (ret) + goto err_charger_init; + + chg_device->chg_end_current = ichg_reg_to_mA(reg_data); + + bat_status.charger_type = DA9052_NOCHARGER; + bat_status.status = DA9052_CHARGING_CC; + bat_status.charging_mode = DA9052_NONE; + + da9052_ccxmx53_init(chg_device->da9052); + + da9052_battery_setup_psy(chg_device); + + ret = power_supply_register(&pdev->dev, &chg_device->psy); + if (ret) + goto err_charger_init; + + monitoring_thread_state = ACTIVE; + init_completion(&monitoring_thread_notifier); + monitoring_thread_pid = kernel_thread(monitoring_thread, chg_device, + CLONE_KERNEL | SIGCHLD); + if (monitoring_thread_pid > 0) { + printk(KERN_ERR "Monitoring thread is successfully started,\ + pid = %d\n", monitoring_thread_pid); + } + + printk(KERN_INFO "Exiting DA9052 battery probe \n"); + return 0; + +err_charger_init: + platform_set_drvdata(pdev, NULL); + kfree(chg_device); + return ret; +} +static int __devexit da9052_bat_remove(struct platform_device *dev) +{ + monitoring_thread_state = INACTIVE; + wait_for_completion(&monitoring_thread_notifier); + return 0; +} + +static struct platform_driver da9052_bat_driver = { + .probe = da9052_bat_probe, + .remove = __devexit_p(da9052_bat_remove), + .driver = { + .name = DA9052_BAT_DEVICE_NAME, + .owner = THIS_MODULE, + }, +}; + +static s32 __init da9052_bat_init(void) +{ + printk(banner); + return platform_driver_register(&da9052_bat_driver); +} +module_init(da9052_bat_init); + +static void __exit da9052_bat_exit(void) +{ + printk("DA9052: Unregistering BAT device.\n"); + platform_driver_unregister(&da9052_bat_driver); +} +module_exit(da9052_bat_exit); + +MODULE_AUTHOR("Digi International."); +MODULE_DESCRIPTION("DA9052 BAT Device Driver on a CCXMX53 JSK."); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/da9052/bat_ccxmx53js.h b/include/linux/mfd/da9052/bat_ccxmx53js.h new file mode 100644 index 000000000000..adca16549439 --- /dev/null +++ b/include/linux/mfd/da9052/bat_ccxmx53js.h @@ -0,0 +1,334 @@ +/* + * da9052 BAT module declarations. + * + * Copyright(c) 2009 Dialog Semiconductor Ltd. + * Copyright(c) 2011 Digi International Ltd. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __LINUX_MFD_DA9052_BAT_CCXMX53JS_H +#define __LINUX_MFD_DA9052_BAT_CCXMX53JS_H + +#include <linux/power_supply.h> + +/* STATIC CONFIGURATION */ +#define BAT_TYPE POWER_SUPPLY_TECHNOLOGY_LION +#define DA9052_NUMBER_OF_STORE_CURENT_READING 4 +#define CURRENT_MONITORING_WINDOW 10 +#define FILTER_SIZE 4 + +enum charge_status_enum { + DA9052_NONE = 1, + DA9052_CHARGING_CC, + DA9052_CHARGING_CV, + DA9052_DISCHARGING_WITH_CHARGER, + DA9052_DISCHARGING_WITHOUT_CHARGER, + DA9052_PRECHARGING, + DA9052_LINEARCHARGING, + DA9052_CHARGEEND +}; + +enum charger_type_enum { + DA9052_NOCHARGER = 1, + DA9052_USB_HUB, + DA9052_USB_CHARGER, + DA9052_WALL_CHARGER +}; + +enum precharge_enum { + PRE_CHARGE_0MA = 0, + PRE_CHARGE_20MA = 20, + PRE_CHARGE_40MA = 40, + PRE_CHARGE_60MA = 60 +}; + +struct da9052_bat_threshold { + u16 vddout_mon; + u16 ichg_thr; + u16 tbat_thr_min; + u16 tbat_thr_max; + u16 tbat_thr_highns; + u16 tbat_thr_limit; + u16 tjunc_thr_limit; + u16 ichg_av_thr_min; + u16 ichg_av_thr_max; +}; + +struct da9052_bat_status { + u8 cal_capacity; + u8 charging_mode; + u8 charger_type; + u8 health; + u8 status; + u8 illegalbattery; +}; + +struct monitoring_state { + u16 vddout_value; + u16 current_value; + u8 bat_level; + u8 vddout_status:1; + u8 current_status:1; + u8 bat_level_status:1; +}; + +struct da9052_bat_device { + u16 chg_current_raw[DA9052_NUMBER_OF_STORE_CURENT_READING]; + u16 chg_current; + u16 bat_voltage; + u16 backup_bat_voltage; + u16 vddout; +}; + + +struct da9052_charger_device { + struct da9052_bat_threshold threshold; + struct da9052 *da9052; + struct da9052_bat_platform_data *bat_pdata; + struct delayed_work work; + struct power_supply psy; + u16 monitoring_interval; + u16 charger_voltage_drop; + u16 bat_target_voltage; + u16 voltage_threshold; + u16 dcin_current; + u16 vbus_current; + u16 usb_charger_current;; + u16 chg_end_current; + u16 precharging_current; + u16 charging_time; + u8 timer_mode:1; + u8 charger_buck_lp:1; + u8 usb_charger_det:1; + u8 ichg_low_cntr:1; + u8 sw_temp_cntr:1; + u8 auto_temp_cntr:1; +}; + +static inline u8 bat_temp_reg_to_C(u16 value) { return (55 - value); } +static inline u8 bat_mV_to_reg(u16 value) { return (((value-4100)/100)<<4); } +static inline u8 bat_drop_mV_to_reg(u16 value) + { return (((value-100)/100)<<6); } +static inline u16 bat_reg_to_mV(u8 value) { return ((value*100) + 4100); } +static inline u16 bat_drop_reg_to_mV(u8 value) { return ((value*100)+100); } +static inline u8 vch_thr_mV_to_reg(u16 value) { return ((value-3700)/100); } +static inline u8 precharge_mA_to_reg(u8 value) { return ((value/20)<<6); } +static inline u8 vddout_mon_mV_to_reg(u16 value) + { return (((value-2500)*128)/1000); } +static inline u16 vddout_reg_to_mV(u8 value) + { return ((value*1000)/128)+2500; } +static inline u16 volt_reg_to_mV(u16 value) + { return ((value*1000)/512)+2500; } +static inline u8 ichg_mA_to_reg(u16 value) { return (value/4); } +static inline u16 ichg_reg_to_mA(u8 value) { return ((value*3900)/1000); } +static inline u8 iset_mA_to_reg(u16 iset_value) + {\ + if ((70 <= iset_value) && (iset_value <= 120)) \ + return (iset_value-70)/10; \ + else if ((400 <= iset_value) && (iset_value <= 700)) \ + return ((iset_value-400)/50)+6; \ + else if ((900 <= iset_value) && (iset_value <= 1300)) \ + return ((iset_value-900)/200)+13; else return 0; + } + +#define DA9052_BAT_DEBUG 0 + +#define SUCCESS 0 +#define FAILURE 1 + +#define TRUE 1 +#define FALSE 0 + +#define set_bits(value, mask) (value | mask) +#define clear_bits(value, mask) (value & ~(mask)) + +#undef DA9052_DEBUG +#if DA9052_BAT_DEBUG +#define DA9052_DEBUG(fmt, args...) printk(KERN_CRIT "[%s:%d]" fmt, __FUNCTION__\ + ,__LINE__, ##args) +#else +#define DA9052_DEBUG(fmt, args...) +#endif + +/* SSC Read or Write Error */ +#define DA9052_SSC_FAIL 150 + +/* Battery PCB resistance (mR) */ +#define DISCHARGE_VDROP 200 +#define CHARGE_CC_VDROP 150 + +/* Battery thesholds(mV, mA) */ +#define VCHARGE_MAX 4150 +#define VDISCHG_MIN 3100 +#define ICHARGE_CC 990 +#define ICHARGE_END 100 +#define ICHARGE_PRECHG 10 + +/* CHARGER BUCK REGISTER */ +#define DA9052_CHGBUCK_CHGTEMP (1<<7) +#define DA9052_CHGBUCK_CHGUSBILIM (1<<6) +#define DA9052_CHGBUCK_CHGBUCKLP (1<<5) +#define DA9052_CHGBUCK_CHGBUCKEN (1<<4) +#define DA9052_CHGBUCK_ISETBUCK (15<<0) +#define ISET_BUCK_80 (0<<0) +#define ISET_BUCK_90 (1<<0) +#define ISET_BUCK_100 (2<<0) +#define ISET_BUCK_110 (3<<0) +#define ISET_BUCK_120 (4<<0) +#define ISET_BUCK_400 (5<<0) +#define ISET_BUCK_450 (6<<0) +#define ISET_BUCK_500 (7<<0) +#define ISET_BUCK_550 (8<<0) +#define ISET_BUCK_600 (9<<0) +#define ISET_BUCK_800 (10<<0) +#define ISET_BUCK_1000 (11<<0) +#define ISET_BUCK_1200 (12<<0) +#define ISET_BUCK_1400 (13<<0) +#define ISET_BUCK_1600 (14<<0) +#define ISET_BUCK_1800 (15<<0) + +/* ISET CONTROL REGISTER */ +#define DA9052_ISET_ISETDCIN (15<<4) +#define ISET_DCIN_80 (0<<4) +#define ISET_DCIN_90 (1<<4) +#define ISET_DCIN_100 (2<<4) +#define ISET_DCIN_110 (3<<4) +#define ISET_DCIN_120 (4<<4) +#define ISET_DCIN_400 (5<<4) +#define ISET_DCIN_450 (6<<4) +#define ISET_DCIN_500 (7<<4) +#define ISET_DCIN_550 (8<<4) +#define ISET_DCIN_600 (9<<4) +#define ISET_DCIN_800 (10<<4) +#define ISET_DCIN_1000 (11<<4) +#define ISET_DCIN_1200 (12<<4) +#define ISET_DCIN_1400 (13<<4) +#define ISET_DCIN_1600 (14<<4) +#define ISET_DCIN_1800 (15<<4) + +#define DA9052_ISET_ISETVBUS (15<<0) +#define ISET_VBUS_80 (0<<0) +#define ISET_VBUS_90 (1<<0) +#define ISET_VBUS_100 (2<<0) +#define ISET_VBUS_110 (3<<0) +#define ISET_VBUS_120 (4<<0) +#define ISET_VBUS_400 (5<<0) +#define ISET_VBUS_450 (6<<0) +#define ISET_VBUS_500 (7<<0) +#define ISET_VBUS_550 (8<<0) +#define ISET_VBUS_600 (9<<0) +#define ISET_VBUS_800 (10<<0) +#define ISET_VBUS_1000 (11<<0) +#define ISET_VBUS_1200 (12<<0) +#define ISET_VBUS_1400 (13<<0) +#define ISET_VBUS_1600 (14<<0) +#define ISET_VBUS_1800 (15<<0) + +/* BATTERY CHARGER CONTROL REGISTER */ +#define DA9052_BATCHG_ICHGPRE (3<<6) +#define ICHG_PRE_0 (0<<6) +#define ICHG_PRE_20 (1<<6) +#define ICHG_PRE_40 (2<<6) +#define ICHG_PRE_60 (3<<6) + +#define DA9052_BATCHG_ICHGBAT (63<<0) +#define ICHG_BAT_0 (0<<0) +#define ICHG_BAT_120 (4<<0) +#define ICHG_BAT_240 (8<<0) +#define ICHG_BAT_360 (12<<0) +#define ICHG_BAT_480 (16<<0) +#define ICHG_BAT_600 (20<<0) +#define ICHG_BAT_720 (24<<0) +#define ICHG_BAT_840 (28<<0) +#define ICHG_BAT_960 (32<<0) +#define ICHG_BAT_1080 (36<<0) +#define ICHG_BAT_1200 (40<<0) +#define ICHG_BAT_1320 (44<<0) +#define ICHG_BAT_1440 (48<<0) +#define ICHG_BAT_1560 (52<<0) +#define ICHG_BAT_1680 (56<<0) +#define ICHG_BAT_1800 (60<<0) +#define ICHG_BAT_1890 (63<<0) + +/* CHARGER COUNTER REGISTER */ +#define DA9052_CHGCONT_VCHGBAT (31<<3) +#define VCHG_BAT_LIION (22<<3) + +#define DA9052_CHGCONT_VCHTHR (7<<0) +#define VCH_THR_38 (0<<0) +#define VCH_THR_40 (1<<0) +#define VCH_THR_41 (2<<0) +#define VCH_THR_42 (3<<0) +#define VCH_THR_43 (4<<0) +#define VCH_THR_44 (5<<0) +#define VCH_THR_46 (6<<0) +#define VCH_THR_48 (7<<0) + +/* INPUT CONTROL REGISTER */ +#define DA9053_INPUTCONT_TCTRMODE (1<<7) +#define DA9053_INPUTCONT_VCHG_DROP (1<<6) +#define DA9053_INPUTCONT_VBUSSUSP (1<<4) +#define DA9053_INPUTCONT_DCINSUSP (1<<5) +#define DA9053_INPUTCONT_TCTR (15<<0) +#define TCTR_DIS (0<<0) + +/* Battery discharge voltage level (mV) */ +#define VDISCHARGE_5 3500-DISCHARGE_VDROP +#define VDISCHARGE_10 3600-DISCHARGE_VDROP +#define VDISCHARGE_15 3630-DISCHARGE_VDROP +#define VDISCHARGE_20 3660-DISCHARGE_VDROP +#define VDISCHARGE_25 3690-DISCHARGE_VDROP +#define VDISCHARGE_30 3700-DISCHARGE_VDROP +#define VDISCHARGE_35 3720-DISCHARGE_VDROP +#define VDISCHARGE_40 3740-DISCHARGE_VDROP +#define VDISCHARGE_45 3760-DISCHARGE_VDROP +#define VDISCHARGE_50 3780-DISCHARGE_VDROP +#define VDISCHARGE_55 3810-DISCHARGE_VDROP +#define VDISCHARGE_60 3830-DISCHARGE_VDROP +#define VDISCHARGE_65 3850-DISCHARGE_VDROP +#define VDISCHARGE_70 3870-DISCHARGE_VDROP +#define VDISCHARGE_75 3890-DISCHARGE_VDROP +#define VDISCHARGE_80 3920-DISCHARGE_VDROP +#define VDISCHARGE_85 3960-DISCHARGE_VDROP +#define VDISCHARGE_90 4010-DISCHARGE_VDROP +#define VDISCHARGE_95 4050-DISCHARGE_VDROP +#define VDISCHARGE_100 4100-DISCHARGE_VDROP + +/* Battery charge voltage level (mV) */ +#define VCHARGE_5 3600+CHARGE_CC_VDROP +#define VCHARGE_10 3700+CHARGE_CC_VDROP +#define VCHARGE_15 3750+CHARGE_CC_VDROP +#define VCHARGE_20 3780+CHARGE_CC_VDROP +#define VCHARGE_25 3790+CHARGE_CC_VDROP +#define VCHARGE_30 3800+CHARGE_CC_VDROP +#define VCHARGE_35 3810+CHARGE_CC_VDROP +#define VCHARGE_40 3820+CHARGE_CC_VDROP +#define VCHARGE_45 3840+CHARGE_CC_VDROP +#define VCHARGE_50 3860+CHARGE_CC_VDROP +#define VCHARGE_55 3880+CHARGE_CC_VDROP +#define VCHARGE_60 3910+CHARGE_CC_VDROP +#define VCHARGE_65 3940+CHARGE_CC_VDROP +#define VCHARGE_70 3970+CHARGE_CC_VDROP +#define VCHARGE_75 4010+CHARGE_CC_VDROP +#define VCHARGE_80 4050+CHARGE_CC_VDROP + +/* Battery charge current level (mA) */ +#define ICHARGE_85 800 +#define ICHARGE_90 600 +#define ICHARGE_95 300 + +#endif /* __LINUX_MFD_DA9052_BAT_CCXMX53JS_H */ |