[Meego-kernel] [PATCH] Intel Medfield MSIC Battery Driver

Pallala, Ramakrishna ramakrishna.pallala
Wed Sep 29 22:33:42 PDT 2010


Hi All, I am submitting the Initial patch for Intel Medfield MSIC Battery Driver.



This patch has the following features enabled.

Basic and Temperature based charging

USB OTG callback support

Maintenance Charging

Runtime PM support

Along with Resolving HSD 3576341, 3576366



kernel config option to build this driver

        CONFIG_BATTERY_INTEL_MDF=y



integration dependencies

        PMIC Battery Driver depends on -

                * Medfield IPC driver

                * Penwell USB OTG driver

        To enable these drivers the following config options need to be enabled:

                - CONFIG_INTEL_SCU_IPC=y
            -  CONFIG_USB_PENWELL_OTG =y

The patch is created against 2.6.35 Kernel.

Signed-off-by: Ramakrishna Pallala <ramakrishna.pallala at intel.com<mailto:ramakrishna.pallala at intel.com>>

diff -Naur kernel_orig/drivers/power/intel_mdf_battery.c kernel_msic/drivers/power/intel_mdf_battery.c
--- kernel_orig/drivers/power/intel_mdf_battery.c    1970-01-01 05:30:00.000000000 +0530
+++ kernel_msic/drivers/power/intel_mdf_battery.c    2010-09-30 22:17:14.421839232 +0530
@@ -0,0 +1,2032 @@
+/*
+ * msic_battery.c - Intel Medfield MSIC Internal charger and Battery Driver
+ *
+ * Copyright (C) 2010 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: Ananth Krishna <ananth.krishna.r at intel.com>,
+ *         Anantha Narayanan <anantha.narayanan at intel.com>
+ *         Ramakrishna Pallala <ramakrishna.pallala at intel.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/param.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/pm_runtime.h>
+
+#include <asm/intel_scu_ipc.h>
+#include <linux/usb/penwell_otg.h>
+
+#define DRIVER_NAME "msic_battery"
+
+/*********************************************************************
+ *        Generic defines
+ *********************************************************************/
+
+#define MSIC_BATT_PRESENT           1
+#define MSIC_BATT_NOT_PRESENT             0
+#define MSIC_USB_CHARGER_PRESENT          MSIC_BATT_PRESENT
+#define MSIC_USB_CHARGER_NOT_PRESENT      MSIC_BATT_NOT_PRESENT
+
+/* Interrupt registers*/
+#define MSIC_BATT_CHR_PWRSRCINT_ADDR 0x005
+#define MSIC_BATT_CHR_BATTDET_MASK  (1 << 0)
+#define MSIC_BATT_CHR_USBDET_MASK   (1 << 1)
+#define MSIC_BATT_CHR_ADPLVDET_MASK (1 << 3)
+#define MSIC_BATT_CHR_ADPHVDET_MASK (1 << 4)
+
+#define MSIC_BATT_PWRSRC_MASK       0x1A
+
+#define MSIC_BATT_CHR_PWRSRCINT1_ADDR     0x006
+#define MSIC_BATT_CHR_USBDCDET_MASK (1 << 2)
+#define MSIC_BATT_CHR_USBCHPDET_MASK (1 << 6)
+
+#define MSIC_BATT_CHR_CHRINT_ADDR   0x007
+#define MSIC_BATT_CHR_BATTOCP_MASK  (1 << 1)
+#define MSIC_BATT_CHR_BATTOTP_MASK  (1 << 2)
+#define MSIC_BATT_CHR_LOWBATT_MASK  (1 << 3)
+#define MSIC_BATT_CHR_WDTIMEEXP_MASK (1 << 5)
+#define MSIC_BATT_CHR_ADPOVP_MASK   (1 << 6)
+#define MSIC_BATT_CHR_TIMEEXP_MASK  (1 << 7)
+
+#define MSIC_BATT_CHRINT_EXCP_MASK  0x5E
+#define MSIC_BATT_CHR_CHRINT1_ADDR  0x008
+#define MSIC_BATT_CHR_WKVINDET_MASK (1 << 2)
+#define MSIC_BATT_CHR_CHROTP_MASK   (1 << 3)
+#define MSIC_BATT_CHR_BATTOVP_MASK  (1 << 5)
+#define MSIC_BATT_CHR_USBOVP_MASK   (1 << 6)
+#define MSIC_BATT_CHR_CHRCMPLT_MASK (1 << 7)
+#define MSIC_BATT_CHRINT1_EXCP_MASK 0x68
+
+/* Interrupt Mask registers */
+#define MSIC_BATT_CHR_MPWRSRCINT_ADDR     0x014
+#define MSIC_BATT_CHR_MPWRSRCINT1_ADDR    0x015
+#define MSIC_BATT_CHR_MCHRINT_ADDR  0x016
+#define MSIC_BATT_CHR_MCHRINT1_ADDR 0x017
+
+/* Internal charger control registers */
+#define MSIC_BATT_CHR_CHRCTRL_ADDR  0x188
+#define CHRCNTL_CHRG_DISABLE        (1 << 2)
+
+#define MSIC_BATT_CHR_CHRCVOLTAGE_ADDR    0x189
+/* Set Charger Voltage to 4140 mV */
+#define CHR_CHRVOLTAGE_SET_DEF      4140
+
+#define MSIC_BATT_CHR_CHRCCURRENT_ADDR    0x18A
+
+#define MSIC_BATT_CHR_SPCHARGER_ADDR 0x18B
+#define CHR_SPCHRGER_LOWCHR_ENABLE  (1 << 5)
+#define CHR_SPCHRGER_WEAKVIN        0x04
+
+#define MSIC_BATT_CHR_CHRTTIME_ADDR 0x18C
+#define CHR_CHRTIME_SET_12HRS       0x0E
+
+#define MSIC_BATT_CHR_CHRCTRL1_ADDR 0x18D
+#define MSIC_BATT_CHR_EXTCHRDIS_MASK (1 << 5)
+
+/* Safe limit registers */
+#define MSIC_BATT_CHR_PWRSRCLMT_ADDR 0x18E  /*Temperature limits*/
+#define CHR_PWRSRCLMT_SET_RANGE           0xC0
+
+#define MSIC_BATT_CHR_CHRSTWDT_ADDR 0x18F  /*Watch dog timer*/
+#define CHR_WDT_DISABLE             0x0
+#define CHR_WDT_SET_60SEC      0x10
+
+#define MSIC_BATT_CHR_WDTWRITE_ADDR 0x190
+#define WDTWRITE_UNLOCK_VALUE       0x01
+
+#define MSIC_BATT_CHR_CHRSAFELMT_ADDR     0x191  /*Maximum safe charging
+                               voltage and current*/
+
+/* Status registers */
+#define MSIC_BATT_CHR_SPWRSRCINT_ADDR     0x192
+#define MSIC_BATT_CHR_SPWRSRCINT1_ADDR    0x193
+#define MSIC_BATT_CHR_USBSLOWBATT_MASK    (1 << 0)
+
+/* ADC1 - registers */
+#define MSIC_BATT_SENSORS 3
+#define ADC_CHNL_START_ADDR    0x1C5 /* increments by 1 */
+#define ADC_DATA_START_ADDR     0x1D4   /* increments by 2 */
+
+#define  MSIC_ADC1CNTL1_ADDR   0x1C0
+#define  MSIC_CNTL1_ADC_ENBL   0x10
+#define  MSIC_CNTL1_RR_ENBL    0x08
+
+#define  MSIC_ADC1CNTL2_ADDR        0x1C1
+#define  MSIC_ADC1CNTL3_ADDR        0x1C2
+#define  MSIC_CNTL3_ADCTHERM_ENBL   0x04
+#define  MSIC_CNTL3_ADCRRDATA_ENBL  0x05
+#define  MSIC_CHANL_MASK_VAL        0x0F
+
+#define  MSIC_STOPBIT_MASK     0x10
+#define  MSIC_ADCTHERM_MASK    4
+#define  ADC_CHANLS_MAX        15 /*no of adc channels*/
+#define  ADC_LOOP_MAX          (ADC_CHANLS_MAX - MSIC_BATT_SENSORS)
+
+#define MSIC_BATT_PACK_VOL     0x0
+#define MSIC_BATT_PACK_CUR     0x1
+#define MSIC_BATT_PACK_TEMP    0x7
+#define MSIC_ADC_VOL_IDX  0
+#define MSIC_ADC_CUR_IDX  1
+#define MSIC_ADC_TEMP_IDX 2
+
+
+#define MSIC_VAUDA        0x0DB
+#define MSIC_VAUDA_VAL         0xFF
+
+/*MSIC battery temparature  attributes*/
+#define MSIC_BTP_ADC_MIN       107
+#define MSIC_BTP_ADC_MAX       977
+
+
+/*convert adc_val to voltage mV */
+#define MSIC_MAX_VOL_DEV       ((5 * 4692) / 1000)
+#define MSIC_ADC_TO_VOL(adc_val)    ((4692 * (adc_val)) / 1000)
+
+/*convert adc_val to current mA */
+#define MSIC_ADC_MAX_CUR       4000 /* In milli Amph */
+#define MSIC_ADC_TO_CUR(adc_val)    ((78125 * (adc_val)) / 10000)
+
+
+/* ADC2 - Coulomb Counter registers */
+#define MSIC_BATT_ADC_CCADCHA_ADDR  0x205
+#define MSIC_BATT_ADC_CCADCLA_ADDR  0x206
+
+#define MSIC_BATT_ADC_CHRGNG_MASK   (1 << 31)
+#define MSIC_BATT_ADC_ACCCHRGVAL_MASK   0x7FFFFFFF
+
+/*
+ * Convert the voltage form decimal to
+ * Register writable format
+ */
+#define CONV_VOL_DEC_MSICREG(a)     (((a - 3500) / 20) << 2)
+
+/* internal return values */
+#define TRUE   1
+#define FALSE  0
+#define BATTSUCCESS 0
+#define EBATTFAIL   1
+#define EBATTERR    2
+#define NR_ARR_ELM_MAX    5
+
+/* IPC defines */
+#define IPCMSG_BATTERY         0xEF
+
+#define TEMP_CHARGE_DELAY_JIFFIES   (HZ * 30)  /*30 sec */
+
+#define IRQ_Q_MAX 8
+#define THERM_CURVE_MAX_SAMPLES 7
+#define THERM_CURVE_MAX_VALUES 4
+#define BATT_STRING_MAX   8
+
+
+/* Valid msic exceptional events */
+enum msic_event {
+    MSIC_EVENT_BATTOCP_EXCPT,
+    MSIC_EVENT_BATTOTP_EXCPT,
+    MSIC_EVENT_LOWBATT_EXCPT,
+    MSIC_EVENT_BATTOVP_EXCPT,
+    MSIC_EVENT_ADPOVP_EXCPT,
+    MSIC_EVENT_CHROTP_EXCPT,
+    MSIC_EVENT_USBOVP_EXCPT,
+};
+
+/* Valid Charging modes */
+enum {
+    BATT_CHARGING_MODE_NONE = 0,
+    BATT_CHARGING_MODE_NORMAL,
+    BATT_CHARGING_MODE_MAINTAINENCE,
+};
+
+
+static int charging_complete;
+static void *otg_handle;
+static struct device *msic_dev;
+
+/*
+ * This array represents the Battery Pack thermistor
+ * temarature and corresponding ADC value limits
+ */
+static int therm_curve_data[THERM_CURVE_MAX_SAMPLES][THERM_CURVE_MAX_VALUES] = {
+                    /* {temp_max, temp_min, adc_max, adc_min} */
+                               {-10, -20, 977, 941},
+                               {0, -10, 941, 887},
+                               {10, 0, 887, 769},
+                               {50, 10, 769, 357},
+                               {75, 50, 357, 186},
+                               {100, 75, 186, 107},
+};
+
+
+/*********************************************************************
+ *        SFI table entries Structures
+ *********************************************************************/
+
+/* Battery Identifier */
+struct battery_id {
+    unsigned char manufac[3];
+    unsigned char model[5];
+    unsigned char sub_ver[3];
+};
+
+/* Parameters defining the range */
+struct temperature_monitoring_range {
+    unsigned char range_number;
+    char temp_low_lim;
+    char temp_up_lim;
+    short int full_chrg_cur;
+    short int full_chrg_vol;
+    short int maint_chrg_cur;
+    short int maint_chrg_vol_ll;
+    short int maint_chrg_vol_ul;
+};
+
+/* SFI table entries */
+struct msic_batt_sfi_prop {
+    unsigned char sign[5];
+    unsigned int length;
+    unsigned char revision;
+    unsigned char checksum;
+    unsigned char oem_id[7];
+    unsigned char oem_tid[9];
+    struct battery_id batt_id;
+    unsigned short int voltage_max;
+    unsigned int capacity;
+    unsigned char battery_type;
+    char safe_temp_low_lim;
+    char safe_temp_up_lim;
+    unsigned short int safe_vol_low_lim;
+    unsigned short int safe_vol_up_lim;
+    unsigned short int chrg_cur_lim;
+    char chrg_term_lim;
+    unsigned short int term_cur;
+    char temp_mon_ranges;
+    struct temperature_monitoring_range temp_mon_range[4];
+    unsigned int sram_addr;
+};
+
+static struct msic_batt_sfi_prop *sfi_table;
+
+/*********************************************************************
+ *        Battery properties
+ *********************************************************************/
+struct charge_params {
+    short int cvol;
+    short int ccur;
+    short int vinilmt;
+    enum usb_charger_type chrg_type;
+};
+
+struct msic_batt_props {
+    unsigned int status;
+    unsigned int health;
+    unsigned int present;
+    unsigned int technology;
+    unsigned int vol_max_des;
+    unsigned int vol_now;
+    unsigned int cur_now;
+    unsigned int charge_full;  /* in mAS */
+    unsigned int charge_now;   /* in mAS */
+    unsigned int charge_avg;   /* in units per second */
+    unsigned int capacity;     /* in units persentage */
+    unsigned int temparature; /* in milli Centigrade*/
+    char model[BATT_STRING_MAX];
+    char vender[BATT_STRING_MAX];
+};
+
+struct msic_charg_props {
+    unsigned int charger_type;
+    unsigned int charger_present;
+    unsigned int charger_health;
+    char charger_model[BATT_STRING_MAX];
+    char charger_vender[BATT_STRING_MAX];
+};
+
+/* All Interrupt request are queued from Interrupt
+ * handler and processed in the bottem half or
+ * scheduled work.
+ */
+struct irq_list {
+    struct list_head list;
+    unsigned char regs[4];
+};
+struct irq_queue {
+    int idx;
+    struct irq_list qlist[IRQ_Q_MAX];
+};
+static struct irq_queue irq_qinfo;
+/*
+ * msic battery info
+ */
+struct msic_power_module_info {
+
+    struct platform_device *pdev;
+
+    /* msic charger data */
+    struct msic_charg_props usb_chrg_props;
+    struct power_supply usb;
+
+    /* msic battery data */
+    struct msic_batt_props batt_props;
+    struct power_supply batt;
+
+    uint16_t adc_index;        /* ADC Channel Index */
+    int irq;                   /* GPE_ID or IRQ# */
+    struct work_struct handler;
+
+    spinlock_t irq_lock;
+    struct list_head irq_head;
+
+    struct delayed_work connect_handler;
+    struct delayed_work disconn_handler;
+    struct charge_params ch_params; /* holds the charge parameters */
+
+    unsigned long update_time;      /* jiffies when data read */
+
+    void __iomem *msic_regs_iomap;
+    int batt_event;
+    int charging_mode;
+};
+
+/*
+ * msic usb properties
+ */
+static enum power_supply_property msic_usb_props[] = {
+    POWER_SUPPLY_PROP_CHARGE_TYPE,
+    POWER_SUPPLY_PROP_PRESENT,
+    POWER_SUPPLY_PROP_HEALTH,
+    POWER_SUPPLY_PROP_MODEL_NAME,
+    POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+/*
+ * msic battery properties
+ */
+static enum power_supply_property msic_battery_props[] = {
+    POWER_SUPPLY_PROP_STATUS,
+    POWER_SUPPLY_PROP_HEALTH,
+    POWER_SUPPLY_PROP_PRESENT,
+    POWER_SUPPLY_PROP_TECHNOLOGY,
+    POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+    POWER_SUPPLY_PROP_VOLTAGE_NOW,
+    POWER_SUPPLY_PROP_CURRENT_NOW,
+    POWER_SUPPLY_PROP_CHARGE_NOW,
+    POWER_SUPPLY_PROP_CHARGE_FULL,
+    POWER_SUPPLY_PROP_CHARGE_AVG,
+    POWER_SUPPLY_PROP_CAPACITY, /* in percents! */
+    POWER_SUPPLY_PROP_TEMP,
+    POWER_SUPPLY_PROP_MODEL_NAME,
+    POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static int enable_adc(struct msic_power_module_info *mbi)
+{
+    int ret;
+    uint8_t data;
+    /* enabling the VAUDA line
+    * this is a temporary workaround for MSIC issue
+    */
+    ret = intel_scu_ipc_iowrite8(MSIC_VAUDA, MSIC_VAUDA_VAL);
+    if (ret) {
+          dev_warn(&mbi->pdev->dev,
+               "%s:VAUDA:ipc write failed\n", __func__);
+          return ret;
+    }
+
+    ret = intel_scu_ipc_ioread8(MSIC_ADC1CNTL1_ADDR, &data);
+    if (ret) {
+          dev_warn(&mbi->pdev->dev, "%s:ipc read failed\n", __func__);
+          return ret;
+    }
+
+    data |= MSIC_CNTL1_ADC_ENBL;    /*enable ADC */
+    data |= MSIC_CNTL1_RR_ENBL;     /*Start Convertion */
+    ret = intel_scu_ipc_iowrite8(MSIC_ADC1CNTL1_ADDR, data);
+    if (ret)
+          dev_warn(&mbi->pdev->dev,
+               "%s:ADC CNTRL1 enabling failed\n", __func__);
+    return ret;
+}
+
+/**
+ * set_up_batt_pack_chnl - to set thermal for conversion
+ * @base_addr: index of free msic adc channel
+ * @therm: struct thermal module info
+ * Context: can sleep
+ *
+ * To set up the adc for reading thermistor
+ * and converting the same into actual temp value
+ * on the platform
+ */
+static int set_up_batt_pack_chnl(u16 ch_index,
+                    struct msic_power_module_info *mbi)
+{
+    int ret;
+    u16 base_addr;
+
+    base_addr = ADC_CHNL_START_ADDR + ch_index;
+
+    /* enabling the MSIC_BATT_PACK_VOL channel */
+    ret = intel_scu_ipc_iowrite8(base_addr, MSIC_BATT_PACK_VOL);
+    if (ret) {
+          dev_warn(&mbi->pdev->dev,
+               "%s:enabling skin therm sensor0 failed\n", __func__);
+          goto fail;
+    }
+
+    base_addr++;
+    ret = intel_scu_ipc_iowrite8(base_addr,    MSIC_BATT_PACK_CUR);
+    if (ret) {
+          dev_warn(&mbi->pdev->dev,
+               "%s:enabling skin therm sensor1 failed\n", __func__);
+          goto fail;
+    }
+
+    /*
+    * enabling the MSIC_BATT_PACK_TEMP channel
+    * emabling stop bit for the last channel
+    */
+    base_addr++;
+    ret = intel_scu_ipc_iowrite8(base_addr,
+                    MSIC_BATT_PACK_TEMP | MSIC_STOPBIT_MASK);
+    if (ret) {
+         dev_warn(&mbi->pdev->dev,
+               "%s:enabling sys therm sensor failed\n", __func__);
+          goto fail;
+    }
+fail:
+    enable_adc(mbi);
+    return ret;
+}
+
+
+/*
+ * reset_stopbit - sets the stop bit to 0 on the given channel
+ * @addr: address of the channel
+ */
+static int reset_stopbit(uint16_t addr)
+{
+    int ret;
+    uint8_t data;
+    ret = intel_scu_ipc_ioread8(addr, &data);
+    if (ret)
+          return ret;
+    data &= ~MSIC_STOPBIT_MASK;     /*setting the stop bit to zero*/
+    ret = intel_scu_ipc_iowrite8(addr, data);
+    return ret;
+}
+
+
+/*
+ * find_free_channel - finds an empty channel for conversion
+ * @mbi: struct msic power module info
+ * Context: can sleep
+ *
+ * If adc is not enabled then start using 0th channel
+ * itself. Otherwise find an empty channel by looking for
+ * one in which the stopbit is set to 1.
+ * returns the base address if succeeds,-EINVAL otherwise
+ */
+static int find_free_channel(struct msic_power_module_info *mbi)
+{
+    int ret;
+    int i;
+    uint8_t data;
+
+    /* Looping for empty channel */
+    for (i = 0; i < ADC_CHANLS_MAX; i++) {
+          ret = intel_scu_ipc_ioread8(ADC_CHNL_START_ADDR + i, &data);
+          if (ret) {
+               dev_warn(&mbi->pdev->dev, "%s:ipc read failed\n",
+                    __func__);
+               return ret;
+          }
+          if (data & MSIC_STOPBIT_MASK)
+               break;
+    }
+
+    if (i >= ADC_CHANLS_MAX-1) {
+          /* No STOP bit found, Retrun channel number as zero */
+          return 0;
+    }
+
+    /* Free Channels should be more than 3(VOL,CUR,TEMP) */
+    if (i > ADC_LOOP_MAX) {
+          dev_warn(&mbi->pdev->dev,
+               "%s:Cannot set up adc, no channels free : %d\n",
+                          __func__, i);
+          return -EINVAL;
+    }
+
+    /*
+    *  Reset STOP bit of the current channel
+    *  No need to reset if the current channel index is 12
+    */
+    if (i != ADC_LOOP_MAX) {
+          ret = reset_stopbit(ADC_CHNL_START_ADDR + i);
+          if (ret) {
+               dev_err(&mbi->pdev->dev,
+               "%s:intel_mdf_battery:ipc r/w failed", __func__);
+               return ret;
+          }
+    }
+
+    /*
+    *  i points to the current channel
+    *  so retrun the next free channel
+    */
+    return i + 1;
+}
+
+/*
+ * mid_initialize_adc - initializing the adc
+ * @therm: struct thermal module info
+ * Context: can sleep
+ *
+ * To initialize the adc for reading thermistor
+ * and converting the same into actual temp value
+ * on the platform
+ */
+static int mdf_initialize_adc(struct msic_power_module_info *mbi)
+{
+    int ret;
+    int channel_index = -1;
+
+    channel_index = find_free_channel(mbi);
+    if (channel_index == -EINVAL)
+          return ret;
+
+    /* Program the free ADC channel */
+    ret = set_up_batt_pack_chnl(channel_index, mbi);
+    if (ret)
+          return ret;
+
+    dev_info(&mbi->pdev->dev,
+          "intel_mdf_battery:adc initialization successful\n");
+
+    return channel_index;
+}
+
+/* Check for valid Temp ADC range */
+static int is_valid_temp_adc(int adc_val)
+{
+    if (adc_val >= MSIC_BTP_ADC_MIN && adc_val <= MSIC_BTP_ADC_MAX)
+          return TRUE;
+    else
+          return FALSE;
+}
+
+/* Temparature conversion Macros */
+static int conv_adc_temp(int adc_val, int adc_max, int adc_diff, int temp_diff)
+{
+    int ret = 0;
+
+    ret = (adc_max - adc_val) * temp_diff;
+    return ret/adc_diff;
+}
+
+/* Check if the adc value is in the curve sample range */
+static int is_valid_temp_adc_range(int val, int min, int max)
+{
+    if (val > min && val <= max)
+          return TRUE;
+    else
+          return FALSE;
+}
+
+static int adc_to_temp(uint16_t adc_val)
+{
+    int temp;
+    int i = 0;
+
+    if (!is_valid_temp_adc(adc_val))
+          return -ERANGE;
+
+    for (i = 0; i < THERM_CURVE_MAX_SAMPLES; i++) {
+
+          /* linear approximation for battery pack temperature*/
+          if (is_valid_temp_adc_range(adc_val, therm_curve_data[i][3],
+                               therm_curve_data[i][2])) {
+
+               temp = conv_adc_temp(adc_val, therm_curve_data[i][2],
+                    therm_curve_data[i][2]-therm_curve_data[i][3],
+                    therm_curve_data[i][0]-therm_curve_data[i][1]);
+
+               temp += therm_curve_data[i][1];
+               break;
+          }
+    }
+
+    if (i >= THERM_CURVE_MAX_SAMPLES)
+          dev_info(msic_dev, "Invalid temp adc range\n");
+
+    /*convert tempertaure in celsius to milli degree celsius*/
+    return temp * 1000;
+}
+
+static int mdf_read_adc_regs(int sensor,
+               struct msic_power_module_info *mbi)
+{
+    uint16_t adc_val, addr;
+    uint8_t data = 0;
+    int ret;
+
+    ret = intel_scu_ipc_ioread8(MSIC_ADC1CNTL3_ADDR, &data);
+    if (ret) {
+          dev_warn(&mbi->pdev->dev, "%s:ipc write failed\n",
+                                    __func__);
+          return ret;
+    }
+
+    /* enable the msic for conversion before reading */
+    ret = intel_scu_ipc_iowrite8(MSIC_ADC1CNTL3_ADDR,
+                          data | MSIC_CNTL3_ADCRRDATA_ENBL);
+    if (ret) {
+          dev_warn(&mbi->pdev->dev, "%s:ipc write failed\n",
+                                    __func__);
+          return ret;
+    }
+    /* re-toggle the RRDATARD bit
+    * temporary workaround */
+    ret = intel_scu_ipc_iowrite8(MSIC_ADC1CNTL3_ADDR,
+                          data | MSIC_CNTL3_ADCTHERM_ENBL);
+    if (ret) {
+          dev_warn(&mbi->pdev->dev, "%s:ipc write failed",
+                                    __func__);
+          return ret;
+    }
+    /* reading the higher bits of data */
+    addr = ADC_DATA_START_ADDR+2*(mbi->adc_index + sensor);
+    ret = intel_scu_ipc_ioread8(addr, &data);
+    if (ret) {
+          dev_warn(&mbi->pdev->dev, "%s:ipc read failed", __func__);
+          return ret;
+    }
+    /* shifting bits to accomodate the lower two data bits */
+    adc_val = (data << 2);
+    addr++;
+    ret = intel_scu_ipc_ioread8(addr, &data);/* reading lower bits */
+    if (ret) {
+          dev_warn(&mbi->pdev->dev, "%s:ipc read failed", __func__);
+          return ret;
+    }
+    /*adding lower two bits to the higher bits*/
+    data &= 0x3;
+    adc_val += data;
+
+    switch (sensor) {
+    case MSIC_ADC_VOL_IDX:
+                    ret = MSIC_ADC_TO_VOL(adc_val);
+                    dev_info(msic_dev, "vol_now: %x\n",
+                                          adc_val);
+                    break;
+    case MSIC_ADC_CUR_IDX:
+                    ret = MSIC_ADC_TO_CUR(adc_val & 0x1FF);
+                    /* if D9 bit is set battery is discharging */
+                    if (adc_val & 0x200)
+                          ret = -(MSIC_ADC_MAX_CUR - ret);
+                     dev_info(msic_dev, "cur_now: %x\n",
+                                          adc_val);
+                    break;
+    case MSIC_ADC_TEMP_IDX:
+                    ret = adc_to_temp(adc_val);
+                    dev_info(msic_dev, "temparature: %x\n",
+                                          adc_val);
+                    break;
+    default:
+          dev_err(&mbi->pdev->dev,
+               "intel_mdf_battery:invalid adc_code:%d", adc_val);
+          ret = -EINVAL;
+    }
+
+    return ret;
+}
+
+/**
+ * msic_battery_read_status - read battery status information
+ * @mbi: device info structure to update the read information
+ * Context: can sleep
+ *
+ * MSIC power source information need to be updated based on the data read
+ * from the MSIC registers.
+ *
+ */
+static int msic_battery_read_status(struct msic_power_module_info *mbi)
+{
+    int err, retval = 0;
+    uint16_t address = 0;
+    unsigned char data = 0;
+    uint32_t cvalue = 0;
+
+    address = MSIC_BATT_CHR_SPWRSRCINT_ADDR;
+
+    /* read specific to determine the status */
+    retval = intel_scu_ipc_ioread8(address, &data);
+
+    if (retval) {
+          dev_warn(&mbi->pdev->dev, "%s(): ipc msic charge control"
+                               " read failed\n", __func__);
+          return -EBATTFAIL;
+    }
+
+    /* determine battery status */
+    if (data & MSIC_BATT_CHR_BATTDET_MASK)
+          mbi->batt_props.present = MSIC_BATT_PRESENT;
+    else
+          mbi->batt_props.present = MSIC_BATT_NOT_PRESENT;
+
+    /* determine other parameters */
+    err = intel_scu_ipc_command(IPCMSG_BATTERY, 0x01, NULL, 0, &cvalue, 1);
+    if (!err) {
+          mbi->batt_props.charge_now = cvalue;
+          if (cvalue & MSIC_BATT_ADC_CHRGNG_MASK)
+               mbi->batt_props.status =
+                          POWER_SUPPLY_STATUS_NOT_CHARGING;
+          else
+               mbi->batt_props.status = POWER_SUPPLY_STATUS_CHARGING;
+
+    } else {
+          mbi->batt_props.status = POWER_SUPPLY_STATUS_UNKNOWN;
+    }
+
+
+    return 0;
+}
+
+
+/**
+ * msic_usb_read_status - read usb charger status information
+ * @mbi: device info structure to update the read information
+ * Context: can sleep
+ *
+ * MSIC power source information need to be updated based on the data read
+ * from the MSIC registers.
+ *
+ */
+static int msic_usb_read_status(struct msic_power_module_info *mbi)
+{
+    int retval = 0;
+    uint16_t address = 0;
+    unsigned char data = 0;
+
+    if (mbi->batt_event == USBCHRG_EVENT_CONNECT ||
+          mbi->batt_event == USBCHRG_EVENT_RESUME ||
+          mbi->batt_event == USBCHRG_EVENT_UPDATE) {
+
+          mbi->usb_chrg_props.charger_present = MSIC_USB_CHARGER_PRESENT;
+
+          address = MSIC_BATT_CHR_SPWRSRCINT1_ADDR;
+
+          /* read specific to determine the status */
+          retval = intel_scu_ipc_ioread8(address, &data);
+          if (retval) {
+               dev_warn(&mbi->pdev->dev, "%s(): ipc msic status power "
+                    "src 1 read failed\n", __func__);
+               return -EBATTFAIL;
+          }
+
+          if (data & MSIC_BATT_CHR_USBSLOWBATT_MASK)
+               mbi->usb_chrg_props.charger_health =
+                               POWER_SUPPLY_HEALTH_DEAD;
+          else
+               mbi->usb_chrg_props.charger_health =
+                               POWER_SUPPLY_HEALTH_GOOD;
+    } else {
+          mbi->usb_chrg_props.charger_present =
+                               MSIC_USB_CHARGER_NOT_PRESENT;
+          mbi->usb_chrg_props.charger_health =
+                               POWER_SUPPLY_HEALTH_UNKNOWN;
+    }
+
+    return 0;
+}
+
+/**
+ * msic_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
+ *
+ * MSIC usb power source property needs to be provided to power_supply
+ * subsytem for it to provide the information to users.
+ */
+static int msic_usb_get_property(struct power_supply *psy,
+                    enum power_supply_property psp,
+                    union power_supply_propval *val)
+{
+    struct msic_power_module_info *mbi = container_of(psy,
+                    struct msic_power_module_info, usb);
+
+    /* update msic_power_module_info members */
+    msic_usb_read_status(mbi);
+
+    switch (psp) {
+    case POWER_SUPPLY_PROP_PRESENT:
+          val->intval = mbi->usb_chrg_props.charger_present;
+          break;
+    case POWER_SUPPLY_PROP_HEALTH:
+          val->intval = mbi->usb_chrg_props.charger_health;
+          break;
+    case POWER_SUPPLY_PROP_CHARGE_TYPE:
+          val->intval = mbi->usb_chrg_props.charger_type;
+          break;
+    case POWER_SUPPLY_PROP_MODEL_NAME:
+          val->strval = mbi->usb_chrg_props.charger_model;
+          break;
+    case POWER_SUPPLY_PROP_MANUFACTURER:
+          val->strval = mbi->usb_chrg_props.charger_vender;
+          break;
+    default:
+          return -EINVAL;
+    }
+
+    return 0;
+}
+
+static unsigned int mdf_cal_avg(struct msic_power_module_info *mbi)
+{
+    unsigned long avg = 0;
+    avg = (mbi->batt_props.charge_avg + (mbi->batt_props.charge_now &
+                          MSIC_BATT_ADC_ACCCHRGVAL_MASK));
+
+    return avg / 2;
+}
+
+/**
+ * msic_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
+ *
+ * MSIC battery power source property needs to be provided to power_supply
+ * subsytem for it to provide the information to users.
+ */
+static int msic_battery_get_property(struct power_supply *psy,
+                    enum power_supply_property psp,
+                    union power_supply_propval *val)
+{
+    int ret;
+    struct msic_power_module_info *mbi = container_of(psy,
+                    struct msic_power_module_info, batt);
+    /* update msic_power_module_info members */
+    ret = msic_battery_read_status(mbi);
+    if (ret) {
+          dev_info(msic_dev, "msic_battery_read_status-FAILED\n");
+          return -EINVAL;
+    }
+
+    /*
+    * All voltages, currents, charges, energies, time and temperatures
+    * in uV, ??A, ??Ah, ??Wh, seconds and tenths of degree Celsius un
+    * less otherwise stated. It's driver's job to convert its raw values
+    * to units in which this class operates.
+    */
+
+    switch (psp) {
+    case POWER_SUPPLY_PROP_STATUS:
+          val->intval = mbi->batt_props.status;
+          break;
+    case POWER_SUPPLY_PROP_HEALTH:
+          val->intval = mbi->batt_props.health;
+          break;
+    case POWER_SUPPLY_PROP_PRESENT:
+          val->intval = mbi->batt_props.present;
+          break;
+    case POWER_SUPPLY_PROP_TECHNOLOGY:
+          val->intval = mbi->batt_props.technology;
+          break;
+    case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+          val->intval = mbi->batt_props.vol_max_des * 1000;
+          break;
+    case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+          mbi->batt_props.vol_now =
+                    mdf_read_adc_regs(MSIC_ADC_VOL_IDX, mbi);
+          val->intval = mbi->batt_props.vol_now * 1000;
+          break;
+    case POWER_SUPPLY_PROP_CURRENT_NOW:
+          mbi->batt_props.cur_now =
+                    mdf_read_adc_regs(MSIC_ADC_CUR_IDX, mbi);
+          val->intval = mbi->batt_props.cur_now * 1000;
+          break;
+    case POWER_SUPPLY_PROP_CHARGE_NOW:
+          /*val->intval = MSIC_BATT_ADC_ACCCHRGVAL_MASK &
+                          mbi->batt_props.charge_now;*/
+          val->intval = mbi->batt_props.charge_now;
+          break;
+    case POWER_SUPPLY_PROP_CHARGE_FULL:
+          val->intval = mbi->batt_props.charge_full;
+          break;
+    case POWER_SUPPLY_PROP_CHARGE_AVG:
+          mbi->batt_props.charge_avg = mdf_cal_avg(mbi);
+          val->intval = mbi->batt_props.charge_avg;
+          break;
+    case POWER_SUPPLY_PROP_CAPACITY:
+          val->intval = mbi->batt_props.capacity;
+          break;
+    case POWER_SUPPLY_PROP_TEMP:
+          mbi->batt_props.temparature =
+                    mdf_read_adc_regs(MSIC_ADC_TEMP_IDX, mbi);
+          val->intval = mbi->batt_props.temparature * 1000;
+          break;
+    case POWER_SUPPLY_PROP_MODEL_NAME:
+          val->strval = mbi->batt_props.model;
+          break;
+    case POWER_SUPPLY_PROP_MANUFACTURER:
+          val->strval = mbi->batt_props.vender;
+          break;
+    default:
+          return -EINVAL;
+    }
+
+    return 0;
+}
+
+/**
+ * msic_log_exception_event - log battery events
+ * @event: msic event to be logged
+ * Context: can sleep
+ *
+ * There are multiple battery and internal charger events
+ * which may be of interest to users.
+ * this battery function logs the different events onto the
+ * kernel log messages.
+ */
+static void msic_log_exception_event(enum msic_event event)
+{
+    switch (event) {
+    case MSIC_EVENT_BATTOCP_EXCPT:
+          printk(KERN_WARNING "msic-battery: over battery charge "
+                          " current condition detected\n");
+          break;
+    case MSIC_EVENT_BATTOTP_EXCPT:
+          printk(KERN_WARNING "msic-battery: high battery temperature "
+                               "condition detected\n");
+          break;
+    case MSIC_EVENT_LOWBATT_EXCPT:
+          printk(KERN_WARNING "msic-battery: Low battery voltage "
+                               "condition detected\n");
+          break;
+    case MSIC_EVENT_BATTOVP_EXCPT:
+          printk(KERN_WARNING "msic-battery: battery overvoltage "
+                               "condition detected\n");
+          break;
+    case MSIC_EVENT_CHROTP_EXCPT:
+          printk(KERN_WARNING "msic-charger: charger high temperature "
+                                    "condition detected\n");
+          break;
+    case MSIC_EVENT_USBOVP_EXCPT:
+          printk(KERN_WARNING "USB over voltage "
+                               "condition detected\n");
+          break;
+    default:
+          dev_info(msic_dev, "unknown error %u detected\n",
+                                          -EBATTERR);
+          break;
+    }
+}
+
+/**
+ * msic_handle_exception - handle any exception scenario
+ * @mbi: device info structure to update the information
+ * Context: can sleep
+ *
+ */
+
+static void msic_handle_exception(struct msic_power_module_info *mbi,
+          uint8_t CHRINT_reg_value, uint8_t CHRINT1_reg_value)
+{
+    enum msic_event exception;
+    uint8_t chrint_reg_value, chrint1_reg_value;
+
+    chrint_reg_value = CHRINT_reg_value;
+    chrint1_reg_value = CHRINT1_reg_value;
+
+
+    if (chrint_reg_value & MSIC_BATT_CHR_BATTOCP_MASK) {
+          exception = MSIC_EVENT_BATTOCP_EXCPT;
+          msic_log_exception_event(exception);
+    }
+
+    if (chrint1_reg_value & MSIC_BATT_CHR_BATTOTP_MASK) {
+          mbi->batt_props.status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+          mbi->batt_props.health = POWER_SUPPLY_HEALTH_OVERHEAT;
+          exception = MSIC_EVENT_BATTOTP_EXCPT;
+          msic_log_exception_event(exception);
+    }
+
+    if (CHRINT_reg_value & MSIC_BATT_CHR_LOWBATT_MASK) {
+          exception = MSIC_EVENT_LOWBATT_EXCPT;
+          msic_log_exception_event(exception);
+    }
+
+    if (CHRINT1_reg_value & MSIC_BATT_CHR_BATTOVP_MASK) {
+          mbi->batt_props.status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+          mbi->batt_props.health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+          exception = MSIC_EVENT_BATTOVP_EXCPT;
+          msic_log_exception_event(exception);
+    }
+
+    if (CHRINT1_reg_value & MSIC_BATT_CHR_CHROTP_MASK) {
+          mbi->batt_props.status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+          exception = MSIC_EVENT_CHROTP_EXCPT;
+          msic_log_exception_event(exception);
+    }
+
+    if (CHRINT1_reg_value & MSIC_BATT_CHR_USBOVP_MASK) {
+          mbi->batt_props.status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+          mbi->usb_chrg_props.charger_health =
+                               POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+          exception = MSIC_EVENT_USBOVP_EXCPT;
+          msic_log_exception_event(exception);
+    }
+
+}
+
+
+static int msic_batt_stop_charging(struct msic_power_module_info *mbi)
+{
+    int retval, i;
+    uint16_t address[3] = {0};
+    unsigned char data[3] = {0};
+
+    address[0] = MSIC_BATT_CHR_WDTWRITE_ADDR;
+    address[1] = MSIC_BATT_CHR_CHRCTRL_ADDR;
+    address[2] = MSIC_BATT_CHR_CHRSTWDT_ADDR;
+
+    data[0] = WDTWRITE_UNLOCK_VALUE; /* unlock WDT write */
+    data[1] = CHRCNTL_CHRG_DISABLE; /* Disable Charging */
+    data[2] = CHR_WDT_DISABLE; /* Disable WDT Timer */
+
+    for (i = 1; i < 3; i++) {
+          retval = intel_scu_ipc_iowrite8(address[0], data[0]);
+          if (retval) {
+               dev_warn(&mbi->pdev->dev, "%s(): ipc msic "
+                          "write failed\n", __func__);
+               return -EBATTFAIL;
+          }
+          retval = intel_scu_ipc_iowrite8(address[i], data[i]);
+          if (retval) {
+               dev_warn(&mbi->pdev->dev, "%s(): ipc msic "
+                          "write failed\n", __func__);
+               return -EBATTFAIL;
+          }
+    }
+
+    return 0;
+}
+/**
+ * msic_batt_do_charging - set battery charger
+ * @mbi: device info structure
+ * @chrg: charge mode to set battery charger in
+ * Context: can sleep
+ *
+ * MsIC battery charger needs to be enabled based on the charger
+ * capabilities connected to the platform.
+ */
+static int msic_batt_do_charging(struct msic_power_module_info *mbi,
+                               struct charge_params *params)
+{
+    int retval, i;
+    uint16_t wdtwrite_addr;
+    uint8_t wdtwrite = WDTWRITE_UNLOCK_VALUE;
+    uint16_t address[4] = {0};
+    unsigned char data[4] = {0};
+
+    if (!params) {
+          dev_warn(&mbi->pdev->dev, "%s(): ipc msic "
+                    "write failed\n", __func__);
+          return -EBATTFAIL;
+    }
+
+    address[0] = MSIC_BATT_CHR_CHRCCURRENT_ADDR;
+    address[1] = MSIC_BATT_CHR_CHRCVOLTAGE_ADDR;
+    address[2] = MSIC_BATT_CHR_CHRCTRL_ADDR;
+    address[3] = MSIC_BATT_CHR_CHRSTWDT_ADDR;
+
+    data[0] = params->ccur;
+    data[1] = params->cvol; /* charge voltage 4.14V */
+    data[2] = params->vinilmt;
+    data[3] = CHR_WDT_SET_60SEC;  /* WTD Timer set to 60 Sec */
+
+    /* to unlock charger control regs */
+    wdtwrite_addr = MSIC_BATT_CHR_WDTWRITE_ADDR;
+    for (i = 0; i < 4; i++) {
+          retval = intel_scu_ipc_iowrite8(wdtwrite_addr, wdtwrite);
+          if (retval) {
+               dev_warn(&mbi->pdev->dev, "%s(): ipc msic "
+                          "write failed\n", __func__);
+               return -EBATTFAIL;
+          }
+          retval = intel_scu_ipc_iowrite8(address[i], data[i]);
+          if (retval) {
+               dev_warn(&mbi->pdev->dev, "%s(): ipc msic "
+                          "write failed\n", __func__);
+               return -EBATTFAIL;
+          }
+    }
+    dev_info(msic_dev, "Charger Enabled\n");
+
+    mbi->batt_props.status = POWER_SUPPLY_STATUS_CHARGING;
+    mbi->batt_props.health = POWER_SUPPLY_HEALTH_GOOD;
+
+    mbi->usb_chrg_props.charger_health = POWER_SUPPLY_HEALTH_GOOD;
+    mbi->usb_chrg_props.charger_present = MSIC_USB_CHARGER_PRESENT;
+    memcpy(mbi->usb_chrg_props.charger_model, "msic", sizeof("msic"));
+    memcpy(mbi->usb_chrg_props.charger_vender, "Intel", sizeof("Intel"));
+
+    if (mbi->ch_params.vinilmt == CHRG_CURR_SDP_LOW) {
+          mbi->usb_chrg_props.charger_type =
+                          POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+    } else {
+          mbi->usb_chrg_props.charger_type =
+                          POWER_SUPPLY_CHARGE_TYPE_FAST;
+    }
+
+    return BATTSUCCESS;
+}
+
+static void reset_wdt_timer(struct msic_power_module_info *mbi)
+{
+    uint16_t wdtwrite_addr;
+    uint16_t address;
+    unsigned char data;
+    int retval;
+
+    address = MSIC_BATT_CHR_CHRSTWDT_ADDR;
+    data = CHR_WDT_SET_60SEC;  /* WDT timer set to 60 Sec */
+
+    wdtwrite_addr = MSIC_BATT_CHR_WDTWRITE_ADDR;
+
+    retval = intel_scu_ipc_iowrite8(wdtwrite_addr, WDTWRITE_UNLOCK_VALUE);
+    if (retval) {
+          dev_warn(&mbi->pdev->dev, "%s(): ipc msic "
+                    "write failed\n", __func__);
+    }
+    retval = intel_scu_ipc_iowrite8(address, data);
+    if (retval) {
+          dev_warn(&mbi->pdev->dev, "%s(): ipc msic "
+                    "write failed\n", __func__);
+    }
+}
+
+static void msic_update_disconn_status(struct msic_power_module_info *mbi)
+{
+    dev_info(msic_dev, "in msic_update_disconn_status\n");
+    mbi->usb_chrg_props.charger_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+    memcpy(mbi->usb_chrg_props.charger_model, "Unknown", sizeof("Unknown"));
+    memcpy(mbi->usb_chrg_props.charger_vender, "Unknown",
+                                    sizeof("Unknown"));
+
+    if (mbi->batt_event == USBCHRG_EVENT_SUSPEND) {
+          mbi->usb_chrg_props.charger_type =
+                          POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+          mbi->batt_props.status =
+                          POWER_SUPPLY_STATUS_NOT_CHARGING;
+          mbi->usb_chrg_props.charger_present =
+                          MSIC_USB_CHARGER_PRESENT;
+    } else {
+          mbi->usb_chrg_props.charger_type =
+                          POWER_SUPPLY_CHARGE_TYPE_NONE;
+          mbi->batt_props.status =
+                          POWER_SUPPLY_STATUS_DISCHARGING;
+          mbi->usb_chrg_props.charger_present =
+                          MSIC_USB_CHARGER_NOT_PRESENT;
+    }
+
+    power_supply_changed(&mbi->batt);
+}
+
+/**
+* msic_batt_temp_charging - manages the charging based on temperature
+* @charge_param: charging parameter
+* @sfi_table: SFI table structure
+*
+* To manage the charging based on the
+* temperature of the battery
+*/
+static void msic_batt_temp_charging(struct work_struct *work)
+{
+    int ret;
+    int i = 0;
+    static int iprev = -1;
+    short int cv = 0, cc = 0, vinlimit = 0;
+    int adc_temp = 0, adc_vol = 0;
+    struct charge_params charge_param;
+    struct msic_power_module_info *mbi = container_of(work,
+               struct msic_power_module_info, connect_handler.work);
+    struct temperature_monitoring_range *temp_mon = NULL;
+
+    dev_info(msic_dev, "Start of %s\n", __func__);
+
+    memset(&charge_param, 0x0, sizeof(struct charge_params));
+    charge_param.vinilmt = mbi->ch_params.vinilmt;
+    charge_param.chrg_type = mbi->ch_params.chrg_type;
+
+    if (mbi->usb_chrg_props.charger_type  ==
+                          POWER_SUPPLY_CHARGE_TYPE_UNKNOWN ||
+          mbi->usb_chrg_props.charger_type ==
+                               POWER_SUPPLY_CHARGE_TYPE_NONE) {
+
+          /*
+          * If the the charger type is unknown or None
+          * better start the charging again and compute
+          * the properties again.
+          */
+          iprev = -1;
+    }
+
+    ret = mdf_read_adc_regs(MSIC_ADC_TEMP_IDX, mbi);
+    /* mdf_read_adc_regs returns in milli Centigrade */
+    adc_temp = ret / 1000;
+
+    for (i = 0; i < sfi_table->temp_mon_ranges; i++) {
+          if ((adc_temp >=  sfi_table->temp_mon_range[i].temp_low_lim) &&
+          (adc_temp < sfi_table->temp_mon_range[i].temp_up_lim)) {
+
+               cv = sfi_table->temp_mon_range[i].full_chrg_vol;
+               cc = sfi_table->temp_mon_range[i].full_chrg_cur;
+
+               /* D7,D6 bits of CHRCNTL will set the VINILMT */
+               if (charge_param.vinilmt > 950)
+                    vinlimit = 0xC0; /* VINILMT set to No Limit */
+               else if (charge_param.vinilmt > 500)
+                    vinlimit = 0x80; /* VINILMT set to 950mA */
+               else if (charge_param.vinilmt > 100)
+                    vinlimit = 0x40; /* VINILMT set to 500mA */
+               else
+                    vinlimit = 0x00; /* VINILMT set to 100mA */
+
+               dev_info(msic_dev, "Range Temp ID %d\n", i);
+               break;
+          }
+    }
+
+    if (i >= sfi_table->temp_mon_ranges) {
+          dev_info(msic_dev, "TEMP RANGE NOT EXIST\n");
+          return ;
+    }
+
+    /*
+    * If we are in same Temparature range check for check for the
+    * maintainence charging mode and enable the charging depending
+    * on the adc voltage and lower threshold.
+    * If Temparature range is changed then anyways we need to set
+    * charging parameters and enable charging, in that case no need
+    * to check for Maintainence mode.
+    */
+    if (i == iprev) {
+          if (mbi->charging_mode == BATT_CHARGING_MODE_MAINTAINENCE) {
+               dev_info(msic_dev, "Mantainence Charging Enabled\n");
+               temp_mon = &sfi_table->temp_mon_range[i];
+               /* Read ADC value for battery Voltage */
+               adc_vol = mdf_read_adc_regs(MSIC_ADC_VOL_IDX, mbi);
+
+               /*
+               * Check if the voltage falls below lower threshold
+               * set charge voltage to maintainence voltage upper
+               * limit and enable charging otherwise just
+               * schedule the work
+               */
+               if (adc_vol <= temp_mon->maint_chrg_vol_ll)
+                    cv = temp_mon->maint_chrg_vol_ul;
+               else
+                    goto lbl_sched_work;
+
+          } else {
+               /*
+               * We are not in normal Mode and no change in temp
+               * range so just write the WDT timer and
+               * schedule the wirk again.
+               */
+               dev_info(msic_dev, "NoChange in Temp Range\n");
+               /* Reset WDT Timer Register for 60 Sec */
+               reset_wdt_timer(mbi);
+               goto lbl_sched_work;
+          }
+    }
+
+    iprev = i;
+    charge_param.cvol = CONV_VOL_DEC_MSICREG(cv);
+
+    cc = cc - 550;
+    if (cc <= 0)
+          cc = 0;
+    else
+          cc = cc / 100;
+    cc = cc << 3;
+
+    charge_param.ccur = cc;
+
+    /* Enable Charge Termination */
+    vinlimit |= 0x08;
+    charge_param.vinilmt = vinlimit;
+
+    dev_info(msic_dev, "params  vol: %x  cur:%x vinilmt:%x\n",
+          charge_param.cvol, charge_param.ccur, charge_param.vinilmt);
+
+    /* enable charging here */
+    ret = msic_batt_do_charging(mbi, &charge_param);
+    if (ret) {
+          dev_info(msic_dev, "msic_batt_do_charging failed\n");
+          goto lbl_sched_work;
+    }
+    power_supply_changed(&mbi->usb);
+
+lbl_sched_work:
+    /* Schedule teh work after 30 Seconds */
+    schedule_delayed_work(&mbi->connect_handler, TEMP_CHARGE_DELAY_JIFFIES);
+    dev_info(msic_dev, "End of %s\n", __func__);
+}
+
+
+static void msic_batt_disconn(struct work_struct *work)
+{
+    int ret = 0;
+    struct msic_power_module_info *mbi = container_of(work,
+               struct msic_power_module_info, disconn_handler.work);
+
+    ret = msic_batt_stop_charging(mbi);
+    if (ret) {
+          dev_info(msic_dev, "%s: failed\n", __func__);
+          return ;
+    }
+    msic_update_disconn_status(mbi);
+    power_supply_changed(&mbi->batt);
+}
+
+static int msic_charger_callback(void *arg, int event, struct otg_bc_cap *cap)
+{
+    struct msic_power_module_info *mbi =
+                          (struct msic_power_module_info *)arg;
+    int ret = 0;
+
+    dev_info(msic_dev, "%s : %d\n", __func__,  cap->mA);
+
+    mbi->batt_event = event;
+
+    switch (event) {
+    case USBCHRG_EVENT_CONNECT:
+    case USBCHRG_EVENT_RESUME:
+    case USBCHRG_EVENT_UPDATE:
+               pm_runtime_get_sync(&mbi->pdev->dev);
+               /* minimum charge current is 550 mA */
+               mbi->ch_params.vinilmt = cap->mA;
+               mbi->ch_params.chrg_type = cap->chrg_type;
+               dev_info(msic_dev, "CHRG TYPE:%d\n",
+                                    cap->chrg_type);
+
+               schedule_delayed_work(&mbi->connect_handler, 0);
+               mbi->charging_mode = BATT_CHARGING_MODE_NORMAL;
+               break;
+    case USBCHRG_EVENT_DISCONN:
+    case USBCHRG_EVENT_SUSPEND:
+               dev_info(msic_dev, "USB DISCONN or SUSPEND\n");
+               cancel_delayed_work(&mbi->connect_handler);
+               schedule_delayed_work(&mbi->disconn_handler, 0);
+               mbi->charging_mode = BATT_CHARGING_MODE_NONE;
+               pm_runtime_put_sync(&mbi->pdev->dev);
+               break;
+    default:
+          dev_info(msic_dev, "Invalid OTG Event:%s\n", __func__);
+    }
+
+    return ret;
+}
+
+/**
+ * msic_battery_interrupt_handler - msic battery interrupt handler
+ * Context: interrupt context
+ *
+ * MSIC battery interrupt handler which will be called on insertion
+ * of valid power source to charge the battery or an exception
+ * condition occurs.
+ */
+static irqreturn_t msic_battery_interrupt_handler(int id, void *dev)
+{
+    struct msic_power_module_info *mbi =
+                    (struct msic_power_module_info *)dev;
+    int idx = 0;
+
+    spin_lock_irq(&mbi->irq_lock);
+    if (irq_qinfo.idx >= IRQ_Q_MAX) {
+          dev_warn(&mbi->pdev->dev, "IRQ Overflow\n");
+          spin_unlock_irq(&mbi->irq_lock);
+          return IRQ_HANDLED;
+    }
+
+    idx = irq_qinfo.idx;
+    /* Copy Interrupt registers locally */
+    irq_qinfo.qlist[idx].regs[0] = readb(mbi->msic_regs_iomap);
+    irq_qinfo.qlist[idx].regs[1] = readb(mbi->msic_regs_iomap + 1);
+    irq_qinfo.qlist[idx].regs[2] = readb(mbi->msic_regs_iomap + 2);
+    irq_qinfo.qlist[idx].regs[3] = readb(mbi->msic_regs_iomap + 3);
+
+    /* Add the Interrupt regs to  Queue */
+    list_add_tail(&irq_qinfo.qlist[idx].list, &mbi->irq_head);
+    irq_qinfo.idx++;
+    spin_unlock_irq(&mbi->irq_lock);
+
+    schedule_work(&mbi->handler);
+
+    return IRQ_HANDLED;
+}
+
+/**
+ * msic_battery_handle_intrpt - msic battery service interrupt
+ * @work: work structure
+ * Context: can sleep
+ *
+ * MSIC 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 msic_battery_handle_intrpt(struct work_struct *work)
+{
+    int err;
+    uint32_t batt_charge_val = 0;
+    unsigned char data[2] = {0};
+    struct msic_power_module_info *mbi = container_of(work,
+                    struct msic_power_module_info, handler);
+    struct irq_list *tmp;
+
+    /* check if msic_power_module_info is initialized */
+    if (!mbi) {
+          printk(KERN_ERR "battery: mbi struct NULL !!\n");
+          return;
+    }
+
+    /* Get the Interrupt regs satte from Queue */
+    spin_lock(&mbi->irq_lock);
+    if (unlikely(list_empty(&mbi->irq_head))) {
+          dev_warn(&mbi->pdev->dev, "List Empty\n");
+          spin_unlock(&mbi->irq_lock);
+          return ;
+    }
+    tmp = list_first_entry(&mbi->irq_head, struct irq_list, list);
+    /* find the cause of the interrupt */
+    data[0] = tmp->regs[2];
+    data[1] = tmp->regs[3];
+    list_del(&tmp->list);
+    irq_qinfo.idx--;
+    spin_unlock(&mbi->irq_lock);
+
+    dev_info(msic_dev, "CHR Int %x %x\n", data[0], data[1]);
+
+    /* Check if charge complete */
+    if (data[1] & MSIC_BATT_CHR_CHRCMPLT_MASK) {
+          dev_info(msic_dev, "CHRG COMPLT\n");
+          charging_complete  = 1;
+          /* Disable Charging */
+          msic_batt_stop_charging(mbi);
+          mbi->charging_mode = BATT_CHARGING_MODE_MAINTAINENCE;
+
+          mbi->batt_props.status = POWER_SUPPLY_STATUS_FULL;
+          err = intel_scu_ipc_command(IPCMSG_BATTERY, 0x01, NULL,
+                               0, &batt_charge_val, 1);
+          if (!err)
+               mbi->batt_props.charge_full = batt_charge_val;
+
+    }
+
+    if ((data[1] & MSIC_BATT_CHR_WKVINDET_MASK)) {
+          dev_info(msic_dev, "WEAK VIN\n");
+          mbi->batt_props.status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+    }
+
+    /* Check if total charge time expired */
+    if ((data[0] & MSIC_BATT_CHR_TIMEEXP_MASK) & (!charging_complete)) {
+          dev_info(msic_dev, "TIMER EXP\n");
+          mbi->batt_props.status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+    }
+
+    /* Check if an exception occured */
+    if ((data[0] & ~MSIC_BATT_CHR_TIMEEXP_MASK) ||
+          (data[1] & ~(MSIC_BATT_CHR_CHRCMPLT_MASK |
+               MSIC_BATT_CHR_WKVINDET_MASK))) {
+          msic_handle_exception(mbi, data[0], data[1]);
+          return;
+    }
+
+}
+
+static int check_charger_conn(struct msic_power_module_info *mbi)
+{
+    int retval = 0;
+    struct otg_bc_cap cap;
+    unsigned char data = 0;
+
+    retval = intel_scu_ipc_ioread8(MSIC_BATT_CHR_SPWRSRCINT_ADDR, &data);
+    if (retval) {
+          dev_warn(&mbi->pdev->dev, "%s(): ipc msic read failed\n",
+                                          __func__);
+          return -EBATTFAIL;
+    }
+
+    if (data & MSIC_BATT_CHR_USBDET_MASK) {
+          retval = penwell_otg_query_charging_cap(&cap);
+          if (retval) {
+               dev_warn(&mbi->pdev->dev, "%s(): usb otg power query "
+               "failed with error code %d\n", __func__, retval);
+               return -EBATTFAIL;
+          }
+          /* Enable charging only if vinilmt is >= 100mA */
+          if (cap.mA >= 100 && (mbi->batt_props.status ==
+                    POWER_SUPPLY_STATUS_NOT_CHARGING))
+               msic_charger_callback(mbi, USBCHRG_EVENT_CONNECT, &cap);
+    }
+
+    /* read the battery and charger status */
+    msic_battery_read_status(mbi);
+    msic_usb_read_status(mbi);
+
+    return retval;
+}
+
+/**
+ * sfi_table_populate - Simple Firmware Interface table Populate
+ * @sfi_table: Simple Firmware Interface table structure
+ *
+ * SFI table has entries for the temperature limits
+ * which is populated in a local structure
+ */
+void sfi_table_populate(struct msic_batt_sfi_prop *sfi_table)
+{
+    memcpy(sfi_table->sign, "BATT", sizeof("BATT"));
+    sfi_table->length = 183;
+    sfi_table->revision = 1;
+    sfi_table->checksum = 15;
+    memcpy(sfi_table->oem_id, "INTEL", sizeof("INTEL"));
+    memcpy(sfi_table->oem_tid, "OEMTID", sizeof("OEMTID"));
+    memcpy(sfi_table->batt_id.manufac, "NK", sizeof("NK"));
+    memcpy(sfi_table->batt_id.model, "BP4L", sizeof("BP4L"));
+    memcpy(sfi_table->batt_id.sub_ver, "00", sizeof("00"));
+    sfi_table->voltage_max = 4200;
+    sfi_table->capacity = 1500;
+    sfi_table->battery_type = 3;    /* POWER_SUPPLY_TECHNOLOGY_LIPO */
+    sfi_table->safe_temp_low_lim = 0;
+    sfi_table->safe_temp_up_lim = 60;
+    sfi_table->safe_vol_low_lim = 3700;
+    sfi_table->safe_vol_up_lim = 4200;
+    sfi_table->chrg_cur_lim = 1000;
+    sfi_table->chrg_term_lim = 1;
+    sfi_table->term_cur = 50;
+    sfi_table->temp_mon_ranges = 4;
+
+    sfi_table->temp_mon_range[0].range_number = 0;
+    sfi_table->temp_mon_range[0].temp_low_lim = 45;
+    sfi_table->temp_mon_range[0].temp_up_lim = 60;
+    sfi_table->temp_mon_range[0].full_chrg_cur = 950;
+    sfi_table->temp_mon_range[0].full_chrg_vol = 4100;
+    sfi_table->temp_mon_range[0].maint_chrg_cur = 950;
+    sfi_table->temp_mon_range[0].maint_chrg_vol_ll = 4000;
+    sfi_table->temp_mon_range[0].maint_chrg_vol_ul = 4050;
+    sfi_table->temp_mon_range[1].range_number = 1;
+    sfi_table->temp_mon_range[1].temp_low_lim = 10;
+    sfi_table->temp_mon_range[1].temp_up_lim = 45;
+    sfi_table->temp_mon_range[1].full_chrg_cur = 950;
+    sfi_table->temp_mon_range[1].full_chrg_vol = 4200;
+    sfi_table->temp_mon_range[1].maint_chrg_cur = 950;
+    sfi_table->temp_mon_range[1].maint_chrg_vol_ll = 4100;
+    sfi_table->temp_mon_range[1].maint_chrg_vol_ul = 4150;
+    sfi_table->temp_mon_range[2].range_number = 2;
+    sfi_table->temp_mon_range[2].temp_low_lim = 0;
+    sfi_table->temp_mon_range[2].temp_up_lim = 10;
+    sfi_table->temp_mon_range[2].full_chrg_cur = 950;
+    sfi_table->temp_mon_range[2].full_chrg_vol = 4100;
+    sfi_table->temp_mon_range[2].maint_chrg_cur = 950;
+    sfi_table->temp_mon_range[2].maint_chrg_vol_ll = 4000;
+    sfi_table->temp_mon_range[2].maint_chrg_vol_ul = 4050;
+    sfi_table->temp_mon_range[3].range_number = 3;
+    sfi_table->temp_mon_range[3].temp_low_lim = -10;
+    sfi_table->temp_mon_range[3].temp_up_lim = 0;
+    sfi_table->temp_mon_range[3].full_chrg_cur = 350;
+    sfi_table->temp_mon_range[3].full_chrg_vol = 3900;
+    sfi_table->temp_mon_range[3].maint_chrg_cur = 350;
+    sfi_table->temp_mon_range[3].maint_chrg_vol_ll = 3950;
+    sfi_table->temp_mon_range[3].maint_chrg_vol_ul = 3950;;
+
+    sfi_table->sram_addr = 0xFFFF7FC3;
+}
+
+/**
+ * init_batt_props - initialize battery properties
+ * @mbi: msic module device structure
+ * Context: can sleep
+ *
+ * init_batt_props function initializes the
+ * MSIC battery properties.
+ */
+static void init_batt_props(struct msic_power_module_info *mbi)
+{
+    mbi->batt_event = 0;
+    mbi->charging_mode = BATT_CHARGING_MODE_NONE;
+
+    mbi->batt_props.status = POWER_SUPPLY_STATUS_UNKNOWN;
+    mbi->batt_props.health = POWER_SUPPLY_HEALTH_GOOD;
+    mbi->batt_props.present = MSIC_BATT_NOT_PRESENT;
+    mbi->batt_props.technology = sfi_table->battery_type;
+    mbi->batt_props.vol_max_des =  sfi_table->voltage_max;
+    mbi->batt_props.vol_now = 0x0;
+    mbi->batt_props.cur_now = 0x0;
+    mbi->batt_props.charge_full = 5400000; /* 5400 milii coulombs */
+    mbi->batt_props.charge_now = 0x0;
+    mbi->batt_props.charge_avg = 0x0;
+    mbi->batt_props.capacity = sfi_table->capacity;
+    mbi->batt_props.temparature = 0;
+
+    memcpy(mbi->batt_props.vender, sfi_table->batt_id.manufac,
+                    sizeof(sfi_table->batt_id.manufac));
+    memcpy(mbi->batt_props.model, sfi_table->batt_id.model,
+                          sizeof(sfi_table->batt_id.model));
+
+}
+
+/**
+ * init_charger_props - initialize charger properties
+ * @mbi: msic module device structure
+ * Context: can sleep
+ *
+ * init_charger_props function initializes the
+ * MSIC usb charger properties.
+ */
+static void init_charger_props(struct msic_power_module_info *mbi)
+{
+    dev_info(msic_dev, "init charg props\n");
+    mbi->usb_chrg_props.charger_type = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+    mbi->usb_chrg_props.charger_present = MSIC_USB_CHARGER_NOT_PRESENT;
+    mbi->usb_chrg_props.charger_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+    memcpy(mbi->usb_chrg_props.charger_model, "Unknown", sizeof("Unknown"));
+    memcpy(mbi->usb_chrg_props.charger_vender, "Unknown",
+                                    sizeof("Unknown"));
+}
+
+/**
+ * init_msic_regs - initialize msic registers
+ * @mbi: msic module device structure
+ * Context: can sleep
+ *
+ * init_msic_regs function initializes the
+ * MSIC registers like CV,Power Source LMT,etc..
+ */
+static void init_msic_regs(struct msic_power_module_info *mbi)
+{
+    uint16_t wdtwrite_addr;
+    int retval, i;
+    uint16_t address[NR_ARR_ELM_MAX] = {0};
+    unsigned char data[NR_ARR_ELM_MAX] = {0};
+
+    address[0] = MSIC_BATT_CHR_PWRSRCLMT_ADDR;
+    address[1] = MSIC_BATT_CHR_CHRCVOLTAGE_ADDR;
+    address[2] = MSIC_BATT_CHR_CHRTTIME_ADDR;
+    address[3] = MSIC_BATT_CHR_SPCHARGER_ADDR;
+    address[4] = MSIC_BATT_CHR_CHRSTWDT_ADDR;
+
+    wdtwrite_addr = MSIC_BATT_CHR_WDTWRITE_ADDR;
+    /* Safe Temp window set from 0 to 60 Degree Centigrades */
+    data[0] = CHR_PWRSRCLMT_SET_RANGE;
+    /* Setting CV to 4.14V */
+    data[1] = CONV_VOL_DEC_MSICREG(CHR_CHRVOLTAGE_SET_DEF);
+    data[2] = CHR_CHRTIME_SET_12HRS; /* set charge timer to 12 hrs */
+    /* Disable LOWCHR and Set WeakVin to 4.52V */
+    data[3] = (~CHR_SPCHRGER_LOWCHR_ENABLE &
+               CHR_SPCHRGER_WEAKVIN);
+    data[4] = CHR_WDT_DISABLE;  /* disable WDT timer */
+
+    for (i = 0; i < NR_ARR_ELM_MAX; i++) {
+          retval = intel_scu_ipc_iowrite8(wdtwrite_addr,
+                               WDTWRITE_UNLOCK_VALUE);
+          if (retval) {
+               dev_warn(&mbi->pdev->dev, "%s(): ipc msic "
+                          "write failed\n", __func__);
+          }
+          retval = intel_scu_ipc_iowrite8(address[i], data[i]);
+          if (retval) {
+               dev_warn(&mbi->pdev->dev, "%s(): ipc msic "
+                          "write failed\n", __func__);
+          }
+    }
+}
+
+
+/**
+ * msic_battery_probe - msic battery initialize
+ * @pdev: msic battery platform device structure
+ * Context: can sleep
+ *
+ * MSIC battery initializes its internal data structue and other
+ * infrastructure components for it to work as expected.
+ */
+static int msic_battery_probe(struct platform_device *pdev)
+{
+    int retval = 0;
+    struct msic_power_module_info *mbi = NULL;
+
+    mbi = kzalloc(sizeof(struct msic_power_module_info), GFP_KERNEL);
+    if (!mbi) {
+          dev_err(&pdev->dev, "%s(): memory allocation failed\n",
+                                          __func__);
+          return -ENOMEM;
+    }
+
+    sfi_table = kzalloc(sizeof(struct msic_batt_sfi_prop), GFP_KERNEL);
+    if (!sfi_table) {
+          dev_err(&pdev->dev, "%s(): memory allocation failed\n",
+                                          __func__);
+          kfree(mbi);
+          return -ENOMEM;
+    }
+
+    mbi->pdev = pdev;
+    mbi->irq =  platform_get_irq(pdev, 0);
+    platform_set_drvdata(pdev, mbi);
+    msic_dev = &pdev->dev;
+
+    /* initialize all required framework before enabling interrupts */
+
+    INIT_WORK(&mbi->handler, (void *)msic_battery_handle_intrpt);
+
+    /*
+    * OTG Disconnect is being called from IRQ context
+    * so calling ipc function is not approprite from otg callback
+    */
+    INIT_DELAYED_WORK(&mbi->disconn_handler, msic_batt_disconn);
+    INIT_DELAYED_WORK(&mbi->connect_handler, msic_batt_temp_charging);
+
+    spin_lock_init(&mbi->irq_lock);
+    /* Initialize the list */
+    INIT_LIST_HEAD(&mbi->irq_head);
+
+    /* Populate data from SFI Table */
+    sfi_table_populate(sfi_table);
+
+    /* Initialize battery and charger Properties*/
+    init_batt_props(mbi);
+    init_charger_props(mbi);
+
+    /* Re Map Phy address space for MSIC regs */
+    mbi->msic_regs_iomap = ioremap_nocache(sfi_table->sram_addr, 8);
+    if (!mbi->msic_regs_iomap) {
+          dev_err(&pdev->dev, "battery: ioremap Failed\n");
+          goto ioremap_failed;
+    }
+
+    /* Init MSIC Registers */
+    init_msic_regs(mbi);
+
+    /* Initialize ADC Channels */
+    mbi->adc_index = mdf_initialize_adc(mbi);
+
+    /* register msic-batt with power supply subsystem */
+    mbi->batt.name = "msic-battery";
+    mbi->batt.type = POWER_SUPPLY_TYPE_BATTERY;
+    mbi->batt.properties = msic_battery_props;
+    mbi->batt.num_properties = ARRAY_SIZE(msic_battery_props);
+    mbi->batt.get_property = msic_battery_get_property;
+    retval = power_supply_register(&pdev->dev, &mbi->batt);
+    if (retval) {
+          dev_err(&pdev->dev, "%s(): failed to register msic battery "
+                         "device with power supply subsystem\n",
+                          __func__);
+          goto power_reg_failed_batt;
+    }
+
+    /* register msic-usb with power supply subsystem */
+    mbi->usb.name = "msic-charger";
+    mbi->usb.type = POWER_SUPPLY_TYPE_USB;
+    mbi->usb.properties = msic_usb_props;
+    mbi->usb.num_properties = ARRAY_SIZE(msic_usb_props);
+    mbi->usb.get_property = msic_usb_get_property;
+    retval = power_supply_register(&pdev->dev, &mbi->usb);
+    if (retval) {
+          dev_err(&pdev->dev, "%s(): failed to register msic usb "
+                          "device with power supply subsystem\n",
+                          __func__);
+          goto power_reg_failed_usb;
+    }
+
+    /* Register with OTG */
+    otg_handle = penwell_otg_register_bc_callback(msic_charger_callback,
+                                          (void *)mbi);
+    if (!otg_handle) {
+          dev_err(&pdev->dev, "battery: OTG Registration failed\n");
+          goto otg_failed;
+    }
+
+    /* Init Runtime PM State */
+    pm_runtime_set_active(&mbi->pdev->dev);
+    pm_runtime_enable(&mbi->pdev->dev);
+    pm_schedule_suspend(&mbi->pdev->dev, MSEC_PER_SEC);
+
+    /* Check if already exist a Charger connection */
+    retval = check_charger_conn(mbi);
+    if (retval)
+          goto ipc_read_failed;
+
+    /* register interrupt */
+    retval = request_irq(mbi->irq, msic_battery_interrupt_handler,
+                               0, DRIVER_NAME, mbi);
+    if (retval) {
+          dev_err(&pdev->dev, "%s(): cannot get IRQ\n", __func__);
+          goto requestirq_failed;
+    }
+
+
+    return retval;
+
+requestirq_failed:
+    penwell_otg_unregister_bc_callback(otg_handle);
+ipc_read_failed:
+otg_failed:
+    power_supply_unregister(&mbi->usb);
+power_reg_failed_usb:
+    power_supply_unregister(&mbi->batt);
+power_reg_failed_batt:
+    iounmap(mbi->msic_regs_iomap);
+ioremap_failed:
+    kfree(sfi_table);
+    kfree(mbi);
+
+    return retval;
+}
+
+/**
+ * msic_battery_remove - msic battery finalize
+ * @pdev: msic battery platform  device structure
+ * Context: can sleep
+ *
+ * MSIC battery finalizes its internal data structue and other
+ * infrastructure components that it initialized in
+ * msic_battery_probe.
+ */
+static int msic_battery_remove(struct platform_device *pdev)
+{
+    struct msic_power_module_info *mbi = platform_get_drvdata(pdev);
+
+    if (mbi) {
+          penwell_otg_unregister_bc_callback(otg_handle);
+          flush_scheduled_work();
+          free_irq(mbi->irq, mbi);
+          if (mbi->msic_regs_iomap != NULL)
+               iounmap(mbi->msic_regs_iomap);
+          power_supply_unregister(&mbi->usb);
+          power_supply_unregister(&mbi->batt);
+
+          kfree(sfi_table);
+          kfree(mbi);
+    }
+
+    return 0;
+}
+
+#ifdef CONFIG_PM
+static int msic_battery_suspend(struct platform_device *pdev,
+                               pm_message_t state)
+{
+    struct msic_power_module_info *mbi = platform_get_drvdata(pdev);
+
+    if (mbi->usb_chrg_props.charger_present &&
+          (mbi->batt_props.status == POWER_SUPPLY_STATUS_CHARGING)) {
+          cancel_delayed_work(&mbi->connect_handler);
+          msic_batt_disconn(&mbi->disconn_handler.work);
+          mbi->charging_mode = BATT_CHARGING_MODE_NONE;
+          pm_runtime_put_noidle(&mbi->pdev->dev);
+          dev_info(&mbi->pdev->dev, "Forced  suspend\n");
+    }
+
+    return BATTSUCCESS;
+}
+
+static int msic_battery_resume(struct platform_device *pdev)
+{
+    int retval = 0;
+    struct msic_power_module_info *mbi = platform_get_drvdata(pdev);
+
+    /* Check if already exist a Charger connection */
+    retval = check_charger_conn(mbi);
+    if (retval)
+          dev_info(&mbi->pdev->dev, "check_charger_conn failed\n");
+
+    return BATTSUCCESS;
+}
+#else
+#define msic_battery_suspend    NULL
+#define msic_battery_resume     NULL
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int msic_runtime_suspend(struct device *dev)
+{
+
+    /* ToDo: Check for MSIC Power rails */
+    dev_info(dev, "%s called\n", __func__);
+    return BATTSUCCESS;
+}
+
+static int msic_runtime_resume(struct device *dev)
+{
+    /* ToDo: Check for MSIC Power rails */
+    dev_info(dev, "%s called\n", __func__);
+
+    return BATTSUCCESS;
+}
+
+static int msic_runtime_idle(struct device *dev)
+{
+    struct platform_device *pdev  = container_of(dev,
+                    struct platform_device, dev);
+    struct msic_power_module_info *mbi = platform_get_drvdata(pdev);
+
+    dev_info(dev, "%s called\n", __func__);
+
+    if (mbi->usb_chrg_props.charger_present &&
+          (mbi->batt_props.status == POWER_SUPPLY_STATUS_CHARGING ||
+          mbi->batt_props.status == POWER_SUPPLY_STATUS_FULL)) {
+          dev_warn(&mbi->pdev->dev, "%s(): failed\n", __func__);
+          return -EBUSY;
+    }
+
+    return BATTSUCCESS;
+}
+#else
+#define msic_runtime_suspend   NULL
+#define msic_runtime_resume    NULL
+#define msic_runtime_idle NULL
+#endif
+/*********************************************************************
+ *        Driver initialisation and finalization
+ *********************************************************************/
+
+static const struct platform_device_id battery_id_table[] = {
+    { "msic_battery", 1 },
+};
+
+static const struct dev_pm_ops msic_batt_pm_ops = {
+    .runtime_suspend =   msic_runtime_suspend,
+    .runtime_resume =    msic_runtime_resume,
+    .runtime_idle =      msic_runtime_idle,
+};
+
+static struct platform_driver msic_battery_driver = {
+    .driver = {
+          .name = DRIVER_NAME,
+          .owner = THIS_MODULE,
+          .pm = &msic_batt_pm_ops,
+    },
+    .probe = msic_battery_probe,
+    .remove = __devexit_p(msic_battery_remove),
+    .suspend = msic_battery_suspend,
+    .resume = msic_battery_resume,
+    .id_table = battery_id_table,
+};
+static int __init msic_battery_module_init(void)
+{
+    int ret;
+
+    ret = platform_driver_register(&msic_battery_driver);
+    if (ret)
+          dev_info(msic_dev, "driver_register failed");
+
+    return ret;
+}
+
+static void __exit msic_battery_module_exit(void)
+{
+    platform_driver_unregister(&msic_battery_driver);
+}
+module_init(msic_battery_module_init);
+module_exit(msic_battery_module_exit);
+
+MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala at intel.com>");
+MODULE_AUTHOR("Anantha Narayanan <anantha.narayanan at intel.com>");
+MODULE_AUTHOR("Ananth Krishna <ananth.krishna.r at intel.com>");
+MODULE_DESCRIPTION("Intel Medfield MSIC Battery Driver");
+MODULE_LICENSE("GPL");
diff -Naur kernel_orig/drivers/power/Kconfig kernel_msic/drivers/power/Kconfig
--- kernel_orig/drivers/power/Kconfig 2010-08-13 08:58:10.131281501 +0530
+++ kernel_msic/drivers/power/Kconfig 2010-08-13 08:58:50.080281339 +0530
@@ -149,4 +149,12 @@
       Say Y here to enable the battery driver on Intel MID
       platforms.

+config BATTERY_INTEL_MDF
+        tristate "Battery driver for Intel MDFLD platforms"
+        depends on INTEL_SCU_IPC && SPI
+        help
+          Say Y here to enable the battery driver on Intel MID
+          platforms.
+
+
 endif # POWER_SUPPLY
diff -Naur kernel_orig/drivers/power/Makefile kernel_msic/drivers/power/Makefile
--- kernel_orig/drivers/power/Makefile     2010-08-13 08:58:10.131281501 +0530
+++ kernel_msic/drivers/power/Makefile     2010-08-13 08:58:50.078708080 +0530
@@ -35,3 +35,4 @@
 obj-$(CONFIG_BATTERY_Z2)  += z2_battery.o
 obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
 obj-$(CONFIG_BATTERY_INTEL_MID)     += intel_mid_battery.o
+obj-$(CONFIG_BATTERY_INTEL_MDF) += intel_mdf_battery.o


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.meego.com/pipermail/meego-kernel/attachments/20100930/df8851a9/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Intel-MFLD-Battery-Driver-001-0930.patch
Type: application/octet-stream
Size: 60312 bytes
Desc: Intel-MFLD-Battery-Driver-001-0930.patch
URL: <http://lists.meego.com/pipermail/meego-kernel/attachments/20100930/df8851a9/attachment-0001.obj>




More information about the Meego-kernel mailing list