diff options
Diffstat (limited to 'drivers/mxc/pmic/mc34708/mc34708_battery.c')
-rw-r--r-- | drivers/mxc/pmic/mc34708/mc34708_battery.c | 950 |
1 files changed, 950 insertions, 0 deletions
diff --git a/drivers/mxc/pmic/mc34708/mc34708_battery.c b/drivers/mxc/pmic/mc34708/mc34708_battery.c new file mode 100644 index 000000000000..55dd7d058a1a --- /dev/null +++ b/drivers/mxc/pmic/mc34708/mc34708_battery.c @@ -0,0 +1,950 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, Inc. 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; 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 + */ + +/* + * Includes + */ +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <asm/mach-types.h> +#include <linux/mfd/mc34708/mc34708_battery.h> +#include <linux/mfd/mc34708/mc34708_adc.h> +#include <linux/pmic_status.h> +#include <linux/pmic_external.h> +#include <linux/mfd/mc34708/mc34708.h> + +#define CHRCV_LSH 6 +#define CHRCV_WID 6 + +#define CHRCC_LSH 12 +#define CHRCC_WID 4 + +#define CHRITERM_LSH 16 +#define CHRITERM_WID 3 + +#define USBDETS_LSH 3 +#define USBDETS_WID 1 + +#define AUXDETS_LSH 4 +#define AUXDETS_WID 1 + +#define VBAT_TRKL_LSH 0 +#define VBAT_TRKL_WID 2 + +#define LOWBATT_LSH 4 +#define LOWBATT_WID 2 + +#define BATTEMPH_LSH 21 +#define BATTEMPH_WID 2 + +#define EOCBUCKEN_LSH 23 +#define EOCBUCKEN_WID 1 + +#define BATTEMPL_LSH 19 +#define BATTEMPL_WID 2 + +#define CHRITERMEN_LSH 2 +#define CHRITERMEN_WID 1 + +#define CHREN_LSH 3 +#define CHREN_WID 1 + +#define MUSBCHRG_LSH 13 +#define MUSBCHRG_WID 2 + +#define MSW_LSH 1 +#define MSW_WID 1 + +#define AUXWEAKEN_LSH 22 +#define AUXWEAKEN_WID 1 + +#define VBUSWEAKEN_LSH 23 +#define VBUSWEAKEN_WID 1 + +#define AUXILIM_LSH 11 +#define AUXILIM_WID 3 + +#define ILIM1P5_LSH 20 +#define ILIM1P5_WID 1 + +#define ACC_STARTCC_LSH 0 +#define ACC_STARTCC_WID 1 +#define ACC_RSTCC_LSH 1 +#define ACC_RSTCC_WID 1 +#define ACC_CCFAULT_LSH 7 +#define ACC_CCFAULT_WID 7 +#define ACC_CCOUT_LSH 8 +#define ACC_CCOUT_WID 16 +#define ACC1_ONEC_LSH 0 +#define ACC1_ONEC_WID 15 + +#define ACC_CALIBRATION 0x17 +#define ACC_START_COUNTER 0x07 +#define ACC_STOP_COUNTER 0x2 +#define ACC_CONTROL_BIT_MASK 0x1f +#define ACC_ONEC_VALUE 2621 +#define ACC_COULOMB_PER_LSB 1 +#define ACC_CALIBRATION_DURATION_MSECS 20 + +#define BAT_VOLTAGE_UNIT_UV 4692 +#define BAT_CURRENT_UNIT_UA 7813 +#define CHG_VOLTAGE_UINT_UV 23474 +#define CHG_MIN_CURRENT_UA 3500 + +#define COULOMB_TO_UAH(c) (10000 * c / 36) + +#define BATTOVP_LSH 9 +#define BATTOVP_WID 1 + +#define USBDETM_LSH 3 +#define USBDETM_WID 1 +#define AUXDETM_LSH 4 +#define AUXDETM_WID 1 +#define ATTACHM_LSH 15 +#define ATTACHM_WID 1 +#define DETACHM_LSH 16 +#define DETACHM_WID 1 +#define ADCCHANGEM_LSH 21 +#define ADCCHANGEM_WID 1 + +#define BATTISOEN_LSH 23 +#define BATTISOEN_WID 1 + +#define USBHOST 0x4 +#define USBCHARGER 0x20 +#define DEDICATEDCHARGER 0x40 + +static int suspend_flag; +static int charging_flag; +static int power_change_flag; + +/* +usb_type = 0x4; USB host; +usb_type = 0x20; USB charger; +usb_type = 0x40; Dedicated charger; +*/ +static int usb_type; +static struct mc34708_charger_setting_point ripley_charger_setting_point[] = { + { + .microVolt = 4200000, + .microAmp = 1150000, + }, +}; + +static struct mc34708_charger_config ripley_charge_config = { + .batteryTempLow = 0, + .batteryTempHigh = 0, + .hasTempSensor = 0, + .trickleThreshold = 3000000, + .vbusThresholdLow = 4600000, + .vbusThresholdWeak = 4800000, + .vbusThresholdHigh = 5000000, + .vauxThresholdLow = 4600000, + .vauxThresholdWeak = 4800000, + .vauxThresholdHigh = 5000000, + .lowBattThreshold = 3100000, + .toppingOffMicroAmp = 50000, /* 50mA */ + .chargingPoints = ripley_charger_setting_point, + .pointsNumber = 3, +}; + +static int dump_ripley_register(int reg); + +static int enable_charger(int enable) +{ + charging_flag = enable ? 1 : 0; + CHECK_ERROR(pmic_write_reg(MC34708_REG_BATTERY_PROFILE, + BITFVAL(CHREN, enable ? 1 : 0), + BITFMASK(CHREN))); + return 0; +} + +static int ripley_get_batt_voltage(unsigned short *voltage) +{ + t_channel channel; + unsigned short result[8]; + + channel = BATTERY_VOLTAGE; + CHECK_ERROR(mc34708_pmic_adc_convert(channel, result)); + *voltage = result[0]; + + return 0; +} + +static int ripley_get_batt_current(unsigned short *curr) +{ + t_channel channel; + unsigned short result[8]; + + channel = BATTERY_CURRENT; + CHECK_ERROR(mc34708_pmic_adc_convert(channel, result)); + *curr = result[0]; + + return 0; +} + +static int coulomb_counter_calibration; +static unsigned int coulomb_counter_start_time_msecs; + +static int ripley_start_coulomb_counter(void) +{ + /* set scaler */ + CHECK_ERROR(pmic_write_reg(REG_ACC1, + ACC_COULOMB_PER_LSB * ACC_ONEC_VALUE, + BITFMASK(ACC1_ONEC))); + + CHECK_ERROR(pmic_write_reg + (REG_ACC0, ACC_START_COUNTER, ACC_CONTROL_BIT_MASK)); + coulomb_counter_start_time_msecs = jiffies_to_msecs(jiffies); + pr_debug("coulomb counter start time %u\n", + coulomb_counter_start_time_msecs); + return 0; +} + +static int ripley_stop_coulomb_counter(void) +{ + CHECK_ERROR(pmic_write_reg + (REG_ACC0, ACC_STOP_COUNTER, ACC_CONTROL_BIT_MASK)); + return 0; +} + +static int ripley_calibrate_coulomb_counter(void) +{ + int ret; + unsigned int value; + + /* set scaler */ + CHECK_ERROR(pmic_write_reg(REG_ACC1, 0x1, BITFMASK(ACC1_ONEC))); + + CHECK_ERROR(pmic_write_reg + (REG_ACC0, ACC_CALIBRATION, ACC_CONTROL_BIT_MASK)); + msleep(ACC_CALIBRATION_DURATION_MSECS); + + ret = pmic_read_reg(REG_ACC0, &value, BITFMASK(ACC_CCOUT)); + if (ret != 0) + return -1; + value = BITFEXT(value, ACC_CCOUT); + pr_debug("calibrate value = %x\n", value); + coulomb_counter_calibration = (int)((s16) ((u16) value)); + pr_debug("coulomb_counter_calibration = %d\n", + coulomb_counter_calibration); + + return 0; + +} + +static int ripley_get_charger_coulomb(int *coulomb) +{ + int ret; + unsigned int value; + int calibration; + unsigned int time_diff_msec; + + ret = pmic_read_reg(REG_ACC0, &value, BITFMASK(ACC_CCOUT)); + if (ret != 0) + return -1; + value = BITFEXT(value, ACC_CCOUT); + pr_debug("counter value = %x\n", value); + *coulomb = ((s16) ((u16) value)) * ACC_COULOMB_PER_LSB; + + if (abs(*coulomb) >= ACC_COULOMB_PER_LSB) { + /* calibrate */ + time_diff_msec = jiffies_to_msecs(jiffies); + time_diff_msec = + (time_diff_msec > coulomb_counter_start_time_msecs) ? + (time_diff_msec - coulomb_counter_start_time_msecs) : + (0xffffffff - coulomb_counter_start_time_msecs + + time_diff_msec); + calibration = coulomb_counter_calibration * (int)time_diff_msec + / (ACC_ONEC_VALUE * ACC_CALIBRATION_DURATION_MSECS); + *coulomb -= calibration; + } + + return 0; +} + +struct ripley_dev_info { + struct device *dev; + + unsigned short voltage_raw; + int voltage_uV; + unsigned short current_raw; + int current_uA; + int battery_status; + int full_counter; + int chargeTimeSeconds; + int usb_charger_online; + int aux_charger_online; + int charger_voltage_uV; + int accum_current_uAh; + + int currChargePoint; + + struct power_supply bat; + struct power_supply usb_charger; + struct power_supply aux_charger; + + struct workqueue_struct *monitor_wqueue; + struct delayed_work monitor_work; + struct mc34708_charger_config *chargeConfig; +}; + +#define ripley_SENSER 25 +#define to_ripley_dev_info(x) container_of((x), struct ripley_dev_info, \ + bat); + +static enum power_supply_property ripley_battery_props[] = { + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_STATUS, +}; + +static enum power_supply_property ripley_aux_charger_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static enum power_supply_property ripley_usb_charger_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +#define BATTTEMPH_TO_BITS(temp) ((temp - 45) / 5) +#define BATTTEMPL_TO_BITS(temp) (temp / 5) +#define VBAT_TRKL_UV_TO_BITS(uv) ((uv-2800000) / 100000) +#define LOWBATT_UV_TO_BITS(uv) ((uv - 3100000) / 100000) +#define CHRITEM_UV_TO_BITS(uv) (((uv / 1000) - 50) / 50) + +static int dump_ripley_register(int reg) +{ + unsigned int value; + pmic_read_reg(reg, &value, PMIC_ALL_BITS); + pr_info("ripley reg %d = 0x%x\n", reg, value); + return 0; +} + +static int init_charger(struct mc34708_charger_config *config) +{ + /* set charger current termination threshold */ + CHECK_ERROR(pmic_write_reg(MC34708_REG_BATTERY_PROFILE, + BITFVAL(CHRITERM, + CHRITEM_UV_TO_BITS + (config->toppingOffMicroAmp)), + BITFMASK(CHRITERM))); + /* enable charger current termination */ + CHECK_ERROR(pmic_write_reg(MC34708_REG_BATTERY_PROFILE, + BITFVAL(CHRITERMEN, 1), + BITFMASK(CHRITERMEN))); + + /* enable EOC buck */ + CHECK_ERROR(pmic_write_reg(MC34708_REG_BATTERY_PROFILE, + BITFVAL(EOCBUCKEN, 1), BITFMASK(EOCBUCKEN))); + /* disable manual switch */ + CHECK_ERROR(pmic_write_reg(MC34708_REG_USB_CTL, + BITFVAL(MSW, 0), BITFMASK(MSW))); + /* enable 1P5 large current */ + CHECK_ERROR(pmic_write_reg(MC34708_REG_CHARGER_DEBOUNCE, + BITFVAL(ILIM1P5, 1), BITFMASK(ILIM1P5))); + + /* enable ISO */ + CHECK_ERROR(pmic_write_reg(MC34708_REG_CHARGER_DEBOUNCE, + BITFVAL(BATTISOEN, 1), BITFMASK(BATTISOEN))); + + CHECK_ERROR(pmic_write_reg(MC34708_REG_CHARGER_SOURCE, + BITFVAL(AUXWEAKEN, 1), BITFMASK(AUXWEAKEN))); + + CHECK_ERROR(pmic_write_reg(MC34708_REG_CHARGER_SOURCE, + BITFVAL(VBUSWEAKEN, 1), + BITFMASK(VBUSWEAKEN))); + + CHECK_ERROR(pmic_write_reg(MC34708_REG_BATTERY_PROFILE, + BITFVAL(VBAT_TRKL, + VBAT_TRKL_UV_TO_BITS + (config->trickleThreshold)), + BITFMASK(VBAT_TRKL))); + CHECK_ERROR(pmic_write_reg + (MC34708_REG_BATTERY_PROFILE, + BITFVAL(LOWBATT, + LOWBATT_UV_TO_BITS(config->lowBattThreshold)), + BITFMASK(LOWBATT))); + + if (config->hasTempSensor) { + CHECK_ERROR(pmic_write_reg(MC34708_REG_BATTERY_PROFILE, + BITFVAL(BATTEMPH, + BATTTEMPH_TO_BITS + (config->batteryTempHigh)), + BITFMASK(BATTEMPH))); + CHECK_ERROR(pmic_write_reg + (MC34708_REG_BATTERY_PROFILE, + BITFVAL(BATTEMPL, + BATTTEMPL_TO_BITS(config->batteryTempLow)), + BITFMASK(BATTEMPL))); + } + + return 0; +} + +#define CHRCV_UV_TO_BITS(uv) ((uv - 3500000) / 20000) +#define CHRCC_UA_TO_BITS(ua) ((ua - 250000) / 100000) + +static int set_charging_point(struct ripley_dev_info *di, int point) +{ + unsigned int val, mask; + if (point >= 0 && point < di->chargeConfig->pointsNumber) { + + val = + BITFVAL(CHRCV, + CHRCV_UV_TO_BITS(di-> + chargeConfig->chargingPoints + [point].microVolt)); + switch (usb_type) { + case USBHOST: + /* set current limit to 500mA */ + CHECK_ERROR(pmic_write_reg(MC34708_REG_USB_CTL, + BITFVAL(MUSBCHRG, 1), + BITFMASK(MUSBCHRG))); + val |= BITFVAL(CHRCC, CHRCC_UA_TO_BITS(250000)); + break; + case USBCHARGER: + /* set current limit to 950mA */ + CHECK_ERROR(pmic_write_reg(MC34708_REG_USB_CTL, + BITFVAL(MUSBCHRG, 3), + BITFMASK(MUSBCHRG))); + val |= BITFVAL(CHRCC, CHRCC_UA_TO_BITS(350000)); + break; + case DEDICATEDCHARGER: + /* set current limit to 950mA */ + CHECK_ERROR(pmic_write_reg(MC34708_REG_USB_CTL, + BITFVAL(MUSBCHRG, 3), + BITFMASK(MUSBCHRG))); + val |= BITFVAL(CHRCC, CHRCC_UA_TO_BITS(350000)); + break; + default: + val |= + BITFVAL(CHRCC, + CHRCC_UA_TO_BITS(di-> + chargeConfig->chargingPoints + [point].microAmp)); + break; + } + + mask = BITFMASK(CHRCV) | BITFMASK(CHRCC); + CHECK_ERROR(pmic_write_reg(MC34708_REG_BATTERY_PROFILE, + val, mask)); + + di->currChargePoint = point; + return 0; + } + return -EINVAL; +} + +#define FULL_DEBOUNCE_TIME 3 +static int adjust_charging_parameter(struct mc34708_charger_config **config) +{ + struct ripley_dev_info *di = + container_of((config), struct ripley_dev_info, chargeConfig); + + if (di->voltage_uV >= + (*config)->chargingPoints[di->currChargePoint].microVolt) { + + /* for NOT the final charge point */ + if (di->currChargePoint < (*config)->pointsNumber - 1) { + di->full_counter++; + if (di->full_counter > FULL_DEBOUNCE_TIME) { + set_charging_point(di, di->currChargePoint + 1); + pr_info + ("shift to charge point %d, volt=%d uV, curr=%d uA\n", + di->currChargePoint, + (*config)-> + chargingPoints + [di->currChargePoint].microVolt, + (*config)-> + chargingPoints + [di->currChargePoint].microAmp); + } + } else { + if (di->current_uA <= (*config)->toppingOffMicroAmp) + di->full_counter++; + if (di->full_counter > FULL_DEBOUNCE_TIME) + di->battery_status = + POWER_SUPPLY_STATUS_NOT_CHARGING; + } + } + + return 0; +} + +static int ripley_charger_update_status(struct ripley_dev_info *di) +{ + int ret; + unsigned int value; + unsigned int reg_usb_type; + int usbOnline, auxOnline; + int restartCharging = 0; + int stopCharging = 0; + + ret = pmic_read_reg(MC34708_REG_INT_SENSE0, &value, PMIC_ALL_BITS); + + if (ret == 0) { + usbOnline = BITFEXT(value, USBDETS); + auxOnline = BITFEXT(value, AUXDETS); + if (!(di->aux_charger_online || di->usb_charger_online) && + (usbOnline || auxOnline)) + restartCharging = 1; + if ((di->aux_charger_online || di->usb_charger_online) && + !(usbOnline || auxOnline)) + stopCharging = 1; + + if (auxOnline != di->aux_charger_online) { + msleep(500); + di->aux_charger_online = auxOnline; + dev_info(di->aux_charger.dev, + "aux charger status: %s\n", + auxOnline ? "online" : "offline"); + power_supply_changed(&di->aux_charger); + } + if (usbOnline != di->usb_charger_online) { + /* need some delay to know the usb type */ + msleep(800); + ret = pmic_read_reg(MC34708_REG_USB_DEVICE_TYPE, + ®_usb_type, PMIC_ALL_BITS); + usb_type = 0; + if ((reg_usb_type & USBHOST) != 0) { + usb_type = USBHOST; + pr_info("USB host attached!!!\n"); + } + if ((reg_usb_type & USBCHARGER) != 0) { + usb_type = USBCHARGER; + pr_info("USB charger attached!!!\n"); + } + if ((reg_usb_type & DEDICATEDCHARGER) != 0) { + usb_type = DEDICATEDCHARGER; + pr_info("Dedicated charger attached!!!\n"); + } + di->usb_charger_online = usbOnline; + dev_info(di->usb_charger.dev, "usb cable status: %s\n", + usbOnline ? "online" : "offline"); + power_supply_changed(&di->usb_charger); + } + + if (restartCharging) { + ripley_start_coulomb_counter(); + enable_charger(1); + cancel_delayed_work(&di->monitor_work); + queue_delayed_work(di->monitor_wqueue, + &di->monitor_work, HZ / 10); + } else if (stopCharging) { + ripley_stop_coulomb_counter(); + cancel_delayed_work(&di->monitor_work); + queue_delayed_work(di->monitor_wqueue, + &di->monitor_work, HZ / 10); + } + } + power_change_flag = 1; + + return ret; +} + +static int ripley_aux_charger_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct ripley_dev_info *di = + container_of((psy), struct ripley_dev_info, aux_charger); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = di->aux_charger_online; + return 0; + default: + break; + } + + return -EINVAL; +} + +static int ripley_usb_charger_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct ripley_dev_info *di = + container_of((psy), struct ripley_dev_info, usb_charger); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = di->usb_charger_online; + return 0; + default: + break; + } + + return -EINVAL; +} + +static int ripley_battery_read_status(struct ripley_dev_info *di) +{ + int retval; + int coulomb; + retval = ripley_get_batt_voltage(&(di->voltage_raw)); + if (retval == 0) + di->voltage_uV = di->voltage_raw * BAT_VOLTAGE_UNIT_UV; + + retval = ripley_get_batt_current(&(di->current_raw)); + if (retval == 0) { + if (di->current_raw & 0x200) + di->current_uA = + (0x1FF - (di->current_raw & 0x1FF)) * + BAT_CURRENT_UNIT_UA * (-1); + else + di->current_uA = + (di->current_raw & 0x1FF) * BAT_CURRENT_UNIT_UA; + } + retval = ripley_get_charger_coulomb(&coulomb); + if (retval == 0) + di->accum_current_uAh = COULOMB_TO_UAH(coulomb); + return retval; +} + +static void ripley_battery_update_status(struct ripley_dev_info *di) +{ + unsigned int point = 0; + struct mc34708_charger_config *config = di->chargeConfig; + int old_battery_status = di->battery_status; + + if (di->battery_status == POWER_SUPPLY_STATUS_UNKNOWN) + di->full_counter = 0; + + ripley_battery_read_status(di); + + if (di->usb_charger_online || di->aux_charger_online) { + if (di->battery_status == POWER_SUPPLY_STATUS_UNKNOWN || + di->battery_status == POWER_SUPPLY_STATUS_DISCHARGING) { + point = 0; + init_charger(config); + set_charging_point(di, point); + enable_charger(1); + } else if (di->battery_status == POWER_SUPPLY_STATUS_CHARGING) + adjust_charging_parameter(&(di->chargeConfig)); + if (di->full_counter > FULL_DEBOUNCE_TIME && + di->currChargePoint >= di->chargeConfig->pointsNumber) { + di->battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING; + } else + di->battery_status = POWER_SUPPLY_STATUS_CHARGING; + } else { + di->battery_status = POWER_SUPPLY_STATUS_DISCHARGING; + di->full_counter = 0; + } + + if (power_change_flag) { + pr_info("battery status: %d, old status: %d, point: %d\n", + di->battery_status, old_battery_status, + di->currChargePoint); + power_change_flag = 0; + } + dev_dbg(di->bat.dev, "bat status: %d\n", di->battery_status); + if (old_battery_status != POWER_SUPPLY_STATUS_UNKNOWN && + di->battery_status != old_battery_status) + power_supply_changed(&di->bat); +} + +static void ripley_battery_work(struct work_struct *work) +{ + struct ripley_dev_info *di = container_of(work, + struct ripley_dev_info, + monitor_work.work); + const int interval = HZ * 15; + + dev_dbg(di->dev, "%s\n", __func__); + + ripley_battery_update_status(di); + queue_delayed_work(di->monitor_wqueue, &di->monitor_work, interval); +} + +static void usb_charger_online_event_callback(void *para) +{ + struct ripley_dev_info *di = (struct ripley_dev_info *)para; + ripley_charger_update_status(di); +} + +static void aux_charger_online_event_callback(void *para) +{ + struct ripley_dev_info *di = (struct ripley_dev_info *)para; + ripley_charger_update_status(di); +} + +static void usb_over_voltage_event_callback(void *para) +{ + struct ripley_dev_info *di = (struct ripley_dev_info *)para; + ripley_charger_update_status(di); +} + +static void aux_over_voltage_event_callback(void *para) +{ + struct ripley_dev_info *di = (struct ripley_dev_info *)para; + ripley_charger_update_status(di); +} + +static void battery_over_voltage_event_callback(void *para) +{ + struct ripley_dev_info *di = (struct ripley_dev_info *)para; + ripley_charger_update_status(di); +} + +static void battery_over_temp_event_callback(void *para) +{ + struct ripley_dev_info *di = (struct ripley_dev_info *)para; + ripley_charger_update_status(di); +} + +static void battery_charge_complete_event_callback(void *para) +{ + struct ripley_dev_info *di = (struct ripley_dev_info *)para; + pr_info("\n\n battery charge complete event, disable charging\n"); +} + +static int ripley_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct ripley_dev_info *di = to_ripley_dev_info(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (di->battery_status == POWER_SUPPLY_STATUS_UNKNOWN) { + ripley_charger_update_status(di); + ripley_battery_update_status(di); + } + val->intval = di->battery_status; + return 0; + default: + break; + } + + ripley_battery_read_status(di); + + switch (psp) { + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = di->voltage_uV; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = di->current_uA; + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + val->intval = di->accum_current_uAh; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = 3800000; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + val->intval = 3300000; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ripley_battery_remove(struct platform_device *pdev) +{ + pmic_event_callback_t bat_event_callback; + struct ripley_dev_info *di = platform_get_drvdata(pdev); + + bat_event_callback.func = usb_charger_online_event_callback; + bat_event_callback.param = (void *)di; + pmic_event_unsubscribe(MC34708_EVENT_USBDET, bat_event_callback); + bat_event_callback.func = aux_charger_online_event_callback; + bat_event_callback.param = (void *)di; + pmic_event_unsubscribe(MC34708_EVENT_AUXDET, bat_event_callback); + + cancel_rearming_delayed_workqueue(di->monitor_wqueue, + &di->monitor_work); + destroy_workqueue(di->monitor_wqueue); + power_supply_unregister(&di->bat); + power_supply_unregister(&di->usb_charger); + power_supply_unregister(&di->aux_charger); + + kfree(di); + + return 0; +} + +static int ripley_battery_probe(struct platform_device *pdev) +{ + int retval = 0; + struct ripley_dev_info *di; + pmic_event_callback_t bat_event_callback; + di = kzalloc(sizeof(*di), GFP_KERNEL); + if (!di) { + retval = -ENOMEM; + goto di_alloc_failed; + } + + platform_set_drvdata(pdev, di); + + di->aux_charger.name = "ripley_aux_charger"; + di->aux_charger.type = POWER_SUPPLY_TYPE_MAINS; + di->aux_charger.properties = ripley_aux_charger_props; + di->aux_charger.num_properties = ARRAY_SIZE(ripley_aux_charger_props); + di->aux_charger.get_property = ripley_aux_charger_get_property; + retval = power_supply_register(&pdev->dev, &di->aux_charger); + if (retval) { + dev_err(di->dev, "failed to register charger\n"); + goto charger_failed; + } + + di->usb_charger.name = "ripley_usb_charger"; + di->usb_charger.type = POWER_SUPPLY_TYPE_USB; + di->usb_charger.properties = ripley_usb_charger_props; + di->usb_charger.num_properties = ARRAY_SIZE(ripley_usb_charger_props); + di->usb_charger.get_property = ripley_usb_charger_get_property; + retval = power_supply_register(&pdev->dev, &di->usb_charger); + if (retval) { + dev_err(di->dev, "failed to register charger\n"); + goto charger_failed; + } + + INIT_DELAYED_WORK(&di->monitor_work, ripley_battery_work); + di->monitor_wqueue = + create_singlethread_workqueue(dev_name(&pdev->dev)); + if (!di->monitor_wqueue) { + retval = -ESRCH; + goto workqueue_failed; + } + + queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ * 10); + + di->dev = &pdev->dev; + di->chargeConfig = &ripley_charge_config; + di->bat.name = "ripley_bat"; + di->bat.type = POWER_SUPPLY_TYPE_BATTERY; + di->bat.properties = ripley_battery_props; + di->bat.num_properties = ARRAY_SIZE(ripley_battery_props); + di->bat.get_property = ripley_battery_get_property; + di->bat.use_for_apm = 1; + + di->battery_status = POWER_SUPPLY_STATUS_UNKNOWN; + + retval = power_supply_register(&pdev->dev, &di->bat); + if (retval) { + dev_err(di->dev, "failed to register battery\n"); + goto batt_failed; + } + + bat_event_callback.func = usb_over_voltage_event_callback; + bat_event_callback.param = (void *)di; + pmic_event_subscribe(MC34708_EVENT_USBOVP, bat_event_callback); + + bat_event_callback.func = aux_over_voltage_event_callback; + bat_event_callback.param = (void *)di; + pmic_event_subscribe(MC34708_EVENT_AUXOVP, bat_event_callback); + + bat_event_callback.func = usb_charger_online_event_callback; + bat_event_callback.param = (void *)di; + pmic_event_subscribe(MC34708_EVENT_USBDET, bat_event_callback); + + bat_event_callback.func = aux_charger_online_event_callback; + bat_event_callback.param = (void *)di; + pmic_event_subscribe(MC34708_EVENT_AUXDET, bat_event_callback); + + bat_event_callback.func = battery_over_voltage_event_callback; + bat_event_callback.param = (void *)di; + pmic_event_subscribe(MC34708_EVENT_BATTOVP, bat_event_callback); + + bat_event_callback.func = battery_over_temp_event_callback; + bat_event_callback.param = (void *)di; + pmic_event_subscribe(MC34708_EVENT_BATTOTP, bat_event_callback); + + bat_event_callback.func = battery_charge_complete_event_callback; + bat_event_callback.param = (void *)di; + pmic_event_subscribe(MC34708_EVENT_CHRCMPL, bat_event_callback); + + ripley_stop_coulomb_counter(); + ripley_calibrate_coulomb_counter(); + + goto success; + +workqueue_failed: + power_supply_unregister(&di->aux_charger); + power_supply_unregister(&di->usb_charger); +charger_failed: + power_supply_unregister(&di->bat); +batt_failed: + kfree(di); +di_alloc_failed: +success: + dev_dbg(di->dev, "%s battery probed!\n", __func__); + return retval; + + return 0; +} + +static int ripley_battery_suspend(struct platform_device *pdev, + pm_message_t state) +{ + suspend_flag = 1; + CHECK_ERROR(pmic_write_reg + (MC34708_REG_INT_STATUS0, BITFVAL(BATTOVP, 0), + BITFMASK(BATTOVP))); + CHECK_ERROR(pmic_write_reg + (MC34708_REG_INT_MASK0, BITFVAL(BATTOVP, 1), + BITFMASK(BATTOVP))); + + return 0; +}; + +static int ripley_battery_resume(struct platform_device *pdev) +{ + suspend_flag = 0; + CHECK_ERROR(pmic_write_reg + (MC34708_REG_INT_MASK0, BITFVAL(BATTOVP, 0), + BITFMASK(BATTOVP))); + + return 0; +}; + +static struct platform_driver ripley_battery_driver_ldm = { + .driver = { + .name = "mc34708_battery", + .bus = &platform_bus_type, + }, + .probe = ripley_battery_probe, + .remove = ripley_battery_remove, + .suspend = ripley_battery_suspend, + .resume = ripley_battery_resume, +}; + +static int __init ripley_battery_init(void) +{ + pr_debug("Ripley Battery driver loading...\n"); + return platform_driver_register(&ripley_battery_driver_ldm); +} + +static void __exit ripley_battery_exit(void) +{ + platform_driver_unregister(&ripley_battery_driver_ldm); + pr_debug("Ripley Battery driver successfully unloaded\n"); +} + +module_init(ripley_battery_init); +module_exit(ripley_battery_exit); + +MODULE_DESCRIPTION("ripley_battery driver"); +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_LICENSE("GPL"); |