From 5e8c7c54a9b297dae0081dd19a7bb94e23040a3d Mon Sep 17 00:00:00 2001 From: Joshua Lock Date: Tue, 18 May 2010 14:51:13 +0100 Subject: linux-moblin: add 2.6.33.2 kernel from MeeGo 1.0 Signed-off-by: Joshua Lock --- ...nux-2.6.34-moorestown-pmic-battery-driver.patch | 849 +++++++++++++++++++++ 1 file changed, 849 insertions(+) create mode 100644 meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-pmic-battery-driver.patch (limited to 'meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-pmic-battery-driver.patch') diff --git a/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-pmic-battery-driver.patch b/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-pmic-battery-driver.patch new file mode 100644 index 000000000..1e098a78e --- /dev/null +++ b/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-pmic-battery-driver.patch @@ -0,0 +1,849 @@ +From 10551a86fa76709587b48a644caeb78dd07690be Mon Sep 17 00:00:00 2001 +From: Nithish Mahalingam +Date: Tue, 29 Dec 2009 22:42:48 +0530 +Subject: [PATCH 068/104] Adding Intel Moorestown PMIC Battery Driver + +PMIC Battery driver provides battery charging and battery gauge functionality +on Intel Moorestown platform. + +Signed-off-by: Nithish Mahalingam +--- + drivers/power/Kconfig | 7 + + drivers/power/Makefile | 1 + + drivers/power/pmic_battery.c | 799 ++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 807 insertions(+), 0 deletions(-) + create mode 100644 drivers/power/pmic_battery.c + +diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig +index d4b3d67..6936bc8 100644 +--- a/drivers/power/Kconfig ++++ b/drivers/power/Kconfig +@@ -124,4 +124,11 @@ config CHARGER_PCF50633 + help + Say Y to include support for NXP PCF50633 Main Battery Charger. + ++config BATTERY_MRSTPMIC ++ tristate "PMIC battery driver for Intel Moorestown platform" ++ depends on SPI_MRST && LNW_IPC && USB_GADGET_LANGWELL ++ help ++ Say Y here to enable battery driver on Intel Moorestown ++ platform. ++ + endif # POWER_SUPPLY +diff --git a/drivers/power/Makefile b/drivers/power/Makefile +index 573597c..97af4b4 100644 +--- a/drivers/power/Makefile ++++ b/drivers/power/Makefile +@@ -31,3 +31,4 @@ obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o + obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o + obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o + obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o ++obj-$(CONFIG_BATTERY_MRSTPMIC) += pmic_battery.o +diff --git a/drivers/power/pmic_battery.c b/drivers/power/pmic_battery.c +new file mode 100644 +index 0000000..6e3c46a +--- /dev/null ++++ b/drivers/power/pmic_battery.c +@@ -0,0 +1,799 @@ ++/* ++ * pmic_battery.c - Intel Moorestown PMIC Battery Driver ++ * ++ * Copyright (C) 2009 Intel Corporation ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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; version 2 of the License. ++ * ++ * 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. ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * Author: Nithish Mahalingam ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++ ++MODULE_AUTHOR("Nithish Mahalingam "); ++MODULE_DESCRIPTION("Intel Moorestown PMIC Battery Driver"); ++MODULE_LICENSE("GPL"); ++ ++#define DRIVER_NAME "pmic_battery" ++ ++/********************************************************************* ++ * Generic defines ++ *********************************************************************/ ++ ++static int pmicbatteryDebug; ++module_param(pmicbatteryDebug, int, 0444); ++MODULE_PARM_DESC(pmicbatteryDebug, ++ "Flag to enable PMIC Battery debug messages."); ++ ++#define PMIC_BATT_DEBUG (pmicbatteryDebug) ++ ++#define PMIC_BATT_DRV_INFO_UPDATED 1 ++#define PMIC_BATT_PRESENT 1 ++#define PMIC_BATT_NOT_PRESENT 0 ++#define PMIC_USB_PRESENT PMIC_BATT_PRESENT ++#define PMIC_USB_NOT_PRESENT PMIC_BATT_NOT_PRESENT ++ ++/* pmic battery register related */ ++#define PMIC_BATT_CHR_SCHRGINT_ADDR 0xD2 ++#define PMIC_BATT_CHR_SBATOVP_MASK (1 << 1) ++#define PMIC_BATT_CHR_STEMP_MASK (1 << 2) ++#define PMIC_BATT_CHR_SCOMP_MASK (1 << 3) ++#define PMIC_BATT_CHR_SUSBDET_MASK (1 << 4) ++#define PMIC_BATT_CHR_SBATDET_MASK (1 << 5) ++#define PMIC_BATT_CHR_SDCLMT_MASK (1 << 6) ++#define PMIC_BATT_CHR_SUSBOVP_MASK (1 << 7) ++#define PMIC_BATT_CHR_EXCPT_MASK 0xC6 ++#define PMIC_BATT_ADC_ACCCHRG_MASK (1 << 31) ++#define PMIC_BATT_ADC_ACCCHRGVAL_MASK 0x7FFFFFFF ++ ++/* pmic ipc related */ ++#define PMIC_BATT_CHR_IPC_CMDID 0xEF ++#define PMIC_BATT_CHR_IPC_FCHRG_SUBID 0x4 ++#define PMIC_BATT_CHR_IPC_TCHRG_SUBID 0x6 ++ ++/* internal return values */ ++#define BATTSUCCESS 0 ++#define EBATTFAIL 1 ++#define EBATTERR 2 ++ ++/* types of battery charging */ ++enum batt_charge_type { ++ BATT_USBOTG_500MA_CHARGE, ++ BATT_USBOTG_TRICKLE_CHARGE, ++}; ++ ++/* valid battery events */ ++enum batt_event { ++ BATT_EVENT_BATOVP_EXCPT, ++ BATT_EVENT_USBOVP_EXCPT, ++ BATT_EVENT_TEMP_EXCPT, ++ BATT_EVENT_DCLMT_EXCPT, ++ BATT_EVENT_EXCPT ++}; ++ ++/* battery cca value */ ++struct batt_cca_data { ++ signed int cca_val; ++}; ++ ++/* battery property structure */ ++struct batt_prop_data { ++ unsigned int batt_capacity; ++ char batt_chrg_crnt; ++ char batt_chrg_volt; ++ char batt_chrg_prot; ++ char batt_chrg_prot2; ++ char batt_chrg_timer; ++} __attribute__((packed)); ++ ++ ++/********************************************************************* ++ * Battery properties ++ *********************************************************************/ ++ ++/* ++ * pmic battery info ++ */ ++struct pmic_power_module_info { ++ bool is_dev_info_updated; ++ struct spi_device *spi; ++ /* pmic battery data */ ++ unsigned long update_time; /* jiffies when data read */ ++ unsigned int usb_is_present; ++ unsigned int batt_is_present; ++ unsigned int batt_health; ++ unsigned int usb_health; ++ unsigned int batt_status; ++ unsigned int batt_charge_now; /* in mAS */ ++ unsigned int batt_prev_charge_full; /* in mAS */ ++ unsigned int batt_charge_rate; /* in units per second */ ++ ++ struct power_supply usb; ++ struct power_supply batt; ++ int irq; /* GPE_ID or IRQ# */ ++ struct workqueue_struct *monitor_wqueue; ++ struct delayed_work monitor_battery; ++ struct work_struct handler; ++}; ++ ++static unsigned int delay_time = 2000; /* in ms */ ++ ++/* ++ * pmic ac properties ++ */ ++static enum power_supply_property pmic_usb_props[] = { ++ POWER_SUPPLY_PROP_PRESENT, ++ POWER_SUPPLY_PROP_HEALTH, ++}; ++ ++/* ++ * pmic battery properties ++ */ ++static enum power_supply_property pmic_battery_props[] = { ++ POWER_SUPPLY_PROP_STATUS, ++ POWER_SUPPLY_PROP_HEALTH, ++ POWER_SUPPLY_PROP_PRESENT, ++ POWER_SUPPLY_PROP_CHARGE_NOW, ++ POWER_SUPPLY_PROP_CHARGE_FULL, ++ POWER_SUPPLY_PROP_CHARGE_AVG, ++}; ++ ++ ++/** ++ * pmic_battery_log_event - log battery events ++ * @event: battery event to be logged ++ * Context: can sleep ++ * ++ * There are multiple battery events which may be of interest to users; ++ * this battery function logs the different battery events onto the ++ * kernel log messages. ++ */ ++static void pmic_battery_log_event(enum batt_event event) ++{ ++ switch (event) { ++ case BATT_EVENT_BATOVP_EXCPT: ++ printk(KERN_WARNING "pmic-battery: battery overvoltage " ++ "condition detected\n"); ++ break; ++ case BATT_EVENT_USBOVP_EXCPT: ++ printk(KERN_WARNING "pmic-battery: usb charger overvoltage " ++ "condition detected\n"); ++ break; ++ case BATT_EVENT_TEMP_EXCPT: ++ printk(KERN_WARNING "pmic-battery: high battery temperature " ++ "condition detected\n"); ++ break; ++ case BATT_EVENT_DCLMT_EXCPT: ++ printk(KERN_WARNING "pmic-battery: over battery charge " ++ " current condition detected\n"); ++ break; ++ default: ++ printk(KERN_WARNING "pmic-battery: charger/battery " ++ " exception detected\n"); ++ break; ++ } ++} ++ ++/** ++ * pmic_battery_read_status - read battery status information ++ * @pbi: device info structure to update the read information ++ * Context: can sleep ++ * ++ * PMIC power source information need to be updated based on the data read ++ * from the PMIC battery registers. ++ * ++ */ ++static void pmic_battery_read_status(struct pmic_power_module_info *pbi) ++{ ++ unsigned int update_time_intrvl = 0; ++ unsigned int chrg_val = 0; ++ struct ipc_pmic_reg_data pmic_batt_reg = {0}; ++ struct ipc_cmd_type pmic_batt_cmd = {0}; ++ struct batt_cca_data ccval = {0}; ++ struct batt_prop_data batt_prop = {0}; ++ int batt_present = 0; ++ int usb_present = 0; ++ int batt_exception = 0; ++ ++ /* make sure the last batt_status read happened delay_time before */ ++ if (pbi->update_time && time_before(jiffies, pbi->update_time + ++ msecs_to_jiffies(delay_time))) ++ return; ++ ++ update_time_intrvl = jiffies_to_msecs(jiffies - pbi->update_time); ++ pbi->update_time = jiffies; ++ ++ /* read coulomb counter registers and schrgint register */ ++ ++ pmic_batt_cmd.ioc = TRUE; ++ pmic_batt_cmd.cmd = IPC_BATT_CCA_READ; ++ if (ipc_config_cmd(pmic_batt_cmd, sizeof(struct batt_cca_data), ++ &ccval)) { ++ dev_warn(&pbi->spi->dev, "%s(): ipc config cmd failed\n", ++ __func__); ++ return; ++ } ++ ++ pmic_batt_reg.ioc = TRUE; ++ pmic_batt_reg.pmic_reg_data[0].register_address = ++ PMIC_BATT_CHR_SCHRGINT_ADDR; ++ pmic_batt_reg.num_entries = 1; ++ ++ if (ipc_pmic_register_read(&pmic_batt_reg)) { ++ dev_warn(&pbi->spi->dev, "%s(): ipc pmic read failed\n", ++ __func__); ++ return; ++ } ++ ++ /* ++ * set pmic_power_module_info members based on pmic register values ++ * read. ++ */ ++ ++ /* set batt_is_present */ ++ if (pmic_batt_reg.pmic_reg_data[0].value & ++ PMIC_BATT_CHR_SBATDET_MASK) { ++ pbi->batt_is_present = PMIC_BATT_PRESENT; ++ batt_present = 1; ++ } else { ++ pbi->batt_is_present = PMIC_BATT_NOT_PRESENT; ++ pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN; ++ pbi->batt_status = POWER_SUPPLY_STATUS_UNKNOWN; ++ } ++ ++ /* set batt_health */ ++ if (batt_present) { ++ if (pmic_batt_reg.pmic_reg_data[0].value & ++ PMIC_BATT_CHR_SBATOVP_MASK) { ++ pbi->batt_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; ++ pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING; ++ pmic_battery_log_event(BATT_EVENT_BATOVP_EXCPT); ++ batt_exception = 1; ++ } else if (pmic_batt_reg.pmic_reg_data[0].value & ++ PMIC_BATT_CHR_SDCLMT_MASK) { ++ pbi->batt_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; ++ pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING; ++ pmic_battery_log_event(BATT_EVENT_DCLMT_EXCPT); ++ batt_exception = 1; ++ } else if (pmic_batt_reg.pmic_reg_data[0].value & ++ PMIC_BATT_CHR_STEMP_MASK) { ++ pbi->batt_health = POWER_SUPPLY_HEALTH_OVERHEAT; ++ pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING; ++ pmic_battery_log_event(BATT_EVENT_TEMP_EXCPT); ++ batt_exception = 1; ++ } else { ++ pbi->batt_health = POWER_SUPPLY_HEALTH_GOOD; ++ } ++ } ++ ++ /* set usb_is_present */ ++ if (pmic_batt_reg.pmic_reg_data[0].value & ++ PMIC_BATT_CHR_SUSBDET_MASK) { ++ pbi->usb_is_present = PMIC_USB_PRESENT; ++ usb_present = 1; ++ } else { ++ pbi->usb_is_present = PMIC_USB_NOT_PRESENT; ++ pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN; ++ } ++ ++ if (usb_present) { ++ if (pmic_batt_reg.pmic_reg_data[0].value & ++ PMIC_BATT_CHR_SUSBOVP_MASK) { ++ pbi->usb_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; ++ pmic_battery_log_event(BATT_EVENT_USBOVP_EXCPT); ++ } else { ++ pbi->usb_health = POWER_SUPPLY_HEALTH_GOOD; ++ } ++ } ++ ++ chrg_val = ccval.cca_val & PMIC_BATT_ADC_ACCCHRGVAL_MASK; ++ ++ /* set batt_prev_charge_full to battery capacity the first time */ ++ if (!pbi->is_dev_info_updated) { ++ pmic_batt_cmd.ioc = TRUE; ++ pmic_batt_cmd.cmd = IPC_BATT_GET_PROP; ++ if (ipc_config_cmd(pmic_batt_cmd, ++ sizeof(struct batt_prop_data), &batt_prop)) { ++ dev_warn(&pbi->spi->dev, "%s(): ipc config cmd " ++ "failed\n", __func__); ++ return; ++ } ++ pbi->batt_prev_charge_full = batt_prop.batt_capacity; ++ } ++ ++ /* set batt_status */ ++ if ((batt_present) && (!batt_exception)) { ++ if (pmic_batt_reg.pmic_reg_data[0].value & ++ PMIC_BATT_CHR_SCOMP_MASK) { ++ pbi->batt_status = POWER_SUPPLY_STATUS_FULL; ++ pbi->batt_prev_charge_full = chrg_val; ++ } else if (ccval.cca_val & PMIC_BATT_ADC_ACCCHRG_MASK) { ++ pbi->batt_status = POWER_SUPPLY_STATUS_DISCHARGING; ++ } else { ++ pbi->batt_status = POWER_SUPPLY_STATUS_CHARGING; ++ } ++ } ++ ++ /* set batt_charge_rate */ ++ if ((pbi->is_dev_info_updated) && (batt_present) && (!batt_exception)) { ++ if (pbi->batt_status == POWER_SUPPLY_STATUS_DISCHARGING) { ++ if (pbi->batt_charge_now - chrg_val) { ++ pbi->batt_charge_rate = ((pbi->batt_charge_now - ++ chrg_val) * 1000 * 60) / ++ update_time_intrvl; ++ } ++ } else if (pbi->batt_status == POWER_SUPPLY_STATUS_CHARGING) { ++ if (chrg_val - pbi->batt_charge_now) { ++ pbi->batt_charge_rate = ((chrg_val - ++ pbi->batt_charge_now) * 1000 * 60) / ++ update_time_intrvl; ++ } ++ } else ++ pbi->batt_charge_rate = 0; ++ } else { ++ pbi->batt_charge_rate = -1; ++ } ++ ++ /* batt_charge_now */ ++ if ((batt_present) && (!batt_exception)) ++ pbi->batt_charge_now = chrg_val; ++ else ++ pbi->batt_charge_now = -1; ++ ++ pbi->is_dev_info_updated = PMIC_BATT_DRV_INFO_UPDATED; ++} ++ ++/** ++ * pmic_usb_get_property - usb power source get property ++ * @psy: usb power supply context ++ * @psp: usb power source property ++ * @val: usb power source property value ++ * Context: can sleep ++ * ++ * PMIC usb power source property needs to be provided to power_supply ++ * subsytem for it to provide the information to users. ++ */ ++static int pmic_usb_get_property(struct power_supply *psy, ++ enum power_supply_property psp, ++ union power_supply_propval *val) ++{ ++ struct pmic_power_module_info *pbi = container_of(psy, ++ struct pmic_power_module_info, usb); ++ ++ /* update pmic_power_module_info members */ ++ pmic_battery_read_status(pbi); ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_PRESENT: ++ val->intval = pbi->usb_is_present; ++ break; ++ case POWER_SUPPLY_PROP_HEALTH: ++ val->intval = pbi->usb_health; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * pmic_battery_get_property - battery power source get property ++ * @psy: battery power supply context ++ * @psp: battery power source property ++ * @val: battery power source property value ++ * Context: can sleep ++ * ++ * PMIC battery power source property needs to be provided to power_supply ++ * subsytem for it to provide the information to users. ++ */ ++static int pmic_battery_get_property(struct power_supply *psy, ++ enum power_supply_property psp, ++ union power_supply_propval *val) ++{ ++ struct pmic_power_module_info *pbi = container_of(psy, ++ struct pmic_power_module_info, batt); ++ ++ /* update pmic_power_module_info members */ ++ pmic_battery_read_status(pbi); ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_STATUS: ++ val->intval = pbi->batt_status; ++ break; ++ case POWER_SUPPLY_PROP_HEALTH: ++ val->intval = pbi->batt_health; ++ break; ++ case POWER_SUPPLY_PROP_PRESENT: ++ val->intval = pbi->batt_is_present; ++ break; ++ case POWER_SUPPLY_PROP_CHARGE_NOW: ++ val->intval = pbi->batt_charge_now; ++ break; ++ case POWER_SUPPLY_PROP_CHARGE_FULL: ++ val->intval = pbi->batt_prev_charge_full; ++ break; ++ case POWER_SUPPLY_PROP_CHARGE_AVG: ++ val->intval = pbi->batt_charge_rate; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * pmic_battery_monitor - monitor battery status ++ * @work: work structure ++ * Context: can sleep ++ * ++ * PMIC battery status needs to be monitored for any change ++ * and information needs to be frequently updated. ++ */ ++static void pmic_battery_monitor(struct work_struct *work) ++{ ++ struct pmic_power_module_info *pbi = container_of(work, ++ struct pmic_power_module_info, monitor_battery.work); ++ ++ /* update pmic_power_module_info members */ ++ pmic_battery_read_status(pbi); ++ queue_delayed_work(pbi->monitor_wqueue, &pbi->monitor_battery, HZ * 10); ++} ++ ++/** ++ * pmic_battery_set_charger - set battery charger ++ * @pbi: device info structure ++ * @chrg: charge mode to set battery charger in ++ * Context: can sleep ++ * ++ * PMIC battery charger needs to be enabled based on the usb charge ++ * capabilities connected to the platform. ++ */ ++static int pmic_battery_set_charger(struct pmic_power_module_info *pbi, ++ enum batt_charge_type chrg) ++{ ++ int retval; ++ ++ /* set usblmt bits and chrgcntl register bits appropriately */ ++ switch (chrg) { ++ case BATT_USBOTG_500MA_CHARGE: ++ retval = lnw_ipc_single_cmd(PMIC_BATT_CHR_IPC_CMDID, ++ PMIC_BATT_CHR_IPC_FCHRG_SUBID, 0, 0); ++ break; ++ case BATT_USBOTG_TRICKLE_CHARGE: ++ retval = lnw_ipc_single_cmd(PMIC_BATT_CHR_IPC_CMDID, ++ PMIC_BATT_CHR_IPC_TCHRG_SUBID, 0, 0); ++ break; ++ default: ++ dev_warn(&pbi->spi->dev, "%s(): out of range usb charger " ++ "charge detected\n", __func__); ++ return -EBATTFAIL; ++ } ++ ++ if (retval) { ++ dev_warn(&pbi->spi->dev, "%s(): ipc pmic read failed\n", ++ __func__); ++ return -EBATTFAIL; ++ } ++ ++ return BATTSUCCESS; ++} ++ ++/** ++ * pmic_battery_interrupt_handler - pmic battery interrupt handler ++ * Context: interrupt context ++ * ++ * PMIC battery interrupt handler which will be called with either ++ * battery full condition occurs or usb otg & battery connect ++ * condition occurs. ++ */ ++static irqreturn_t pmic_battery_interrupt_handler(int id, void *dev) ++{ ++ struct pmic_power_module_info *pbi = ++ (struct pmic_power_module_info *)dev; ++ ++ schedule_work(&pbi->handler); ++ ++ return IRQ_HANDLED; ++} ++ ++/** ++ * pmic_battery_handle_intrpt - pmic battery service interrupt ++ * @work: work structure ++ * Context: can sleep ++ * ++ * PMIC battery needs to either update the battery status as full ++ * if it detects battery full condition caused the interrupt or needs ++ * to enable battery charger if it detects usb and battery detect ++ * caused the source of interrupt. ++ */ ++static void pmic_battery_handle_intrpt(struct work_struct *work) ++{ ++ struct ipc_pmic_reg_data pmic_batt_reg = {0}; ++ struct pmic_power_module_info *pbi = container_of(work, ++ struct pmic_power_module_info, handler); ++ int power = 0; ++ enum batt_charge_type chrg; ++ int retval = 0; ++ ++ /* check if pmic_power_module_info is initialized */ ++ if (!pbi) ++ return; ++ ++ /* read schrgint register to interpret cause of interrupt */ ++ pmic_batt_reg.ioc = TRUE; ++ pmic_batt_reg.pmic_reg_data[0].register_address = ++ PMIC_BATT_CHR_SCHRGINT_ADDR; ++ pmic_batt_reg.num_entries = 1; ++ ++ if (ipc_pmic_register_read(&pmic_batt_reg)) { ++ dev_warn(&pbi->spi->dev, "%s(): ipc pmic read failed\n", ++ __func__); ++ return; ++ } ++ ++ /* find the cause of the interrupt */ ++ ++ if (pmic_batt_reg.pmic_reg_data[0].value & PMIC_BATT_CHR_SBATDET_MASK) { ++ pbi->batt_is_present = PMIC_BATT_PRESENT; ++ } else { ++ pbi->batt_is_present = PMIC_BATT_NOT_PRESENT; ++ pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN; ++ pbi->batt_status = POWER_SUPPLY_STATUS_UNKNOWN; ++ return; ++ } ++ ++ if (pmic_batt_reg.pmic_reg_data[0].value & ++ PMIC_BATT_CHR_EXCPT_MASK) { ++ pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN; ++ pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING; ++ pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN; ++ pmic_battery_log_event(BATT_EVENT_EXCPT); ++ return; ++ } else { ++ pbi->batt_health = POWER_SUPPLY_HEALTH_GOOD; ++ pbi->usb_health = POWER_SUPPLY_HEALTH_GOOD; ++ } ++ ++ if (pmic_batt_reg.pmic_reg_data[0].value & PMIC_BATT_CHR_SCOMP_MASK) { ++ struct ipc_cmd_type pmic_batt_cmd = {0}; ++ struct batt_cca_data ccval = {0}; ++ ++ pbi->batt_status = POWER_SUPPLY_STATUS_FULL; ++ pmic_batt_cmd.ioc = TRUE; ++ pmic_batt_cmd.cmd = IPC_BATT_CCA_READ; ++ if (ipc_config_cmd(pmic_batt_cmd, ++ sizeof(struct batt_cca_data), &ccval)) { ++ dev_warn(&pbi->spi->dev, "%s(): ipc config cmd " ++ "failed\n", __func__); ++ return; ++ } ++ pbi->batt_prev_charge_full = ccval.cca_val & ++ PMIC_BATT_ADC_ACCCHRGVAL_MASK; ++ return; ++ } ++ ++ if (pmic_batt_reg.pmic_reg_data[0].value & PMIC_BATT_CHR_SUSBDET_MASK) { ++ pbi->usb_is_present = PMIC_USB_PRESENT; ++ } else { ++ pbi->usb_is_present = PMIC_USB_NOT_PRESENT; ++ pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN; ++ return; ++ } ++ ++ /* setup battery charging */ ++ ++ /* check usb otg power capability and set charger accordingly */ ++ retval = langwell_udc_maxpower(&power); ++ if (retval) { ++ dev_warn(&pbi->spi->dev, "%s(): usb otg power query failed " ++ "with error code %d\n", __func__, retval); ++ return; ++ } ++ ++ if (power >= 500) ++ chrg = BATT_USBOTG_500MA_CHARGE; ++ else ++ chrg = BATT_USBOTG_TRICKLE_CHARGE; ++ ++ /* enable battery charging */ ++ if (pmic_battery_set_charger(pbi, chrg)) { ++ dev_warn(&pbi->spi->dev, "%s(): failed to setup battery " ++ "charging\n", __func__); ++ return; ++ } ++ ++ if (PMIC_BATT_DEBUG) ++ printk(KERN_INFO "pmic-battery: %s() - setting up battery " ++ "charger successful\n", __func__); ++} ++ ++/** ++ * pmic_battery_probe - pmic battery initialize ++ * @spi: pmic battery spi structure ++ * Context: can sleep ++ * ++ * PMIC battery initializes its internal data structue and other ++ * infrastructure components for it to work as expected. ++ */ ++static int pmic_battery_probe(struct spi_device *spi) ++{ ++ int retval = 0; ++ struct pmic_power_module_info *pbi = 0; ++ ++ if (PMIC_BATT_DEBUG) ++ printk(KERN_INFO "pmic-battery: %s() - found pmic battery " ++ "device\n", __func__); ++ ++ pbi = kzalloc(sizeof(*pbi), GFP_KERNEL); ++ if (!pbi) { ++ dev_err(&spi->dev, "%s(): memory allocation failed\n", ++ __func__); ++ return -ENOMEM; ++ } ++ ++ pbi->spi = spi; ++ pbi->irq = spi->irq; ++ dev_set_drvdata(&spi->dev, pbi); ++ ++ /* initialize all required framework before enabling interrupts */ ++ INIT_WORK(&pbi->handler, (void *)pmic_battery_handle_intrpt); ++ INIT_DELAYED_WORK(&pbi->monitor_battery, pmic_battery_monitor); ++ pbi->monitor_wqueue = ++ create_singlethread_workqueue(dev_name(&spi->dev)); ++ if (!pbi->monitor_wqueue) { ++ dev_err(&spi->dev, "%s(): wqueue init failed\n", __func__); ++ retval = -ESRCH; ++ goto wqueue_failed; ++ } ++ ++ /* register interrupt */ ++ retval = request_irq(pbi->irq, pmic_battery_interrupt_handler, ++ 0, DRIVER_NAME, pbi); ++ if (retval) { ++ dev_err(&spi->dev, "%s(): cannot get IRQ\n", __func__); ++ goto requestirq_failed; ++ } ++ ++ /* register pmic-batt with power supply subsystem */ ++ pbi->batt.name = "pmic-batt"; ++ pbi->batt.type = POWER_SUPPLY_TYPE_BATTERY; ++ pbi->batt.properties = pmic_battery_props; ++ pbi->batt.num_properties = ARRAY_SIZE(pmic_battery_props); ++ pbi->batt.get_property = pmic_battery_get_property; ++ retval = power_supply_register(&spi->dev, &pbi->batt); ++ if (retval) { ++ dev_err(&spi->dev, "%s(): failed to register pmic battery " ++ "device with power supply subsystem\n", ++ __func__); ++ goto power_reg_failed; ++ } ++ ++ if (PMIC_BATT_DEBUG) ++ printk(KERN_INFO "pmic-battery: %s() - pmic battery device " ++ "registration with power supply subsystem " ++ "successful\n", __func__); ++ ++ queue_delayed_work(pbi->monitor_wqueue, &pbi->monitor_battery, HZ * 1); ++ ++ /* register pmic-usb with power supply subsystem */ ++ pbi->usb.name = "pmic-usb"; ++ pbi->usb.type = POWER_SUPPLY_TYPE_USB; ++ pbi->usb.properties = pmic_usb_props; ++ pbi->usb.num_properties = ARRAY_SIZE(pmic_usb_props); ++ pbi->usb.get_property = pmic_usb_get_property; ++ retval = power_supply_register(&spi->dev, &pbi->usb); ++ if (retval) { ++ dev_err(&spi->dev, "%s(): failed to register pmic usb " ++ "device with power supply subsystem\n", ++ __func__); ++ goto power_reg_failed_1; ++ } ++ ++ if (PMIC_BATT_DEBUG) ++ printk(KERN_INFO "pmic-battery: %s() - pmic usb device " ++ "registration with power supply subsystem successful\n", ++ __func__); ++ ++ return retval; ++ ++power_reg_failed_1: ++ power_supply_unregister(&pbi->batt); ++power_reg_failed: ++ cancel_rearming_delayed_workqueue(pbi->monitor_wqueue, ++ &pbi->monitor_battery); ++requestirq_failed: ++ destroy_workqueue(pbi->monitor_wqueue); ++wqueue_failed: ++ kfree(pbi); ++ ++ return retval; ++} ++ ++/** ++ * pmic_battery_remove - pmic battery finalize ++ * @spi: pmic battery spi device structure ++ * Context: can sleep ++ * ++ * PMIC battery finalizes its internal data structue and other ++ * infrastructure components that it initialized in ++ * pmic_battery_probe. ++ */ ++static int pmic_battery_remove(struct spi_device *spi) ++{ ++ struct pmic_power_module_info *pbi = dev_get_drvdata(&spi->dev); ++ ++ if (pbi) { ++ free_irq(pbi->irq, pbi); ++ ++ cancel_rearming_delayed_workqueue(pbi->monitor_wqueue, ++ &pbi->monitor_battery); ++ destroy_workqueue(pbi->monitor_wqueue); ++ ++ power_supply_unregister(&pbi->usb); ++ power_supply_unregister(&pbi->batt); ++ ++ flush_scheduled_work(); ++ ++ kfree(pbi); ++ } ++ ++ return 0; ++} ++ ++ ++/********************************************************************* ++ * Driver initialisation and finalization ++ *********************************************************************/ ++ ++static struct spi_driver pmic_battery_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .bus = &spi_bus_type, ++ .owner = THIS_MODULE, ++ }, ++ .probe = pmic_battery_probe, ++ .remove = __devexit_p(pmic_battery_remove), ++}; ++ ++ ++static int __init pmic_battery_module_init(void) ++{ ++ return spi_register_driver(&pmic_battery_driver); ++} ++ ++static void __exit pmic_battery_module_exit(void) ++{ ++ spi_unregister_driver(&pmic_battery_driver); ++} ++ ++module_init(pmic_battery_module_init); ++module_exit(pmic_battery_module_exit); +-- +1.6.2.5 + -- cgit v1.2.3