2010-06-17 21:12:36 +04:00
/*
* intel_mid_battery . c - Intel MID 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 < nithish . mahalingam @ 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/param.h>
# include <linux/device.h>
# include <linux/spi/spi.h>
# include <linux/platform_device.h>
# include <linux/power_supply.h>
# include <asm/intel_scu_ipc.h>
# define DRIVER_NAME "pmic_battery"
/*********************************************************************
* Generic defines
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int debug ;
module_param ( debug , int , 0444 ) ;
MODULE_PARM_DESC ( debug , " Flag to enable PMIC Battery debug messages. " ) ;
# 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)
2011-08-26 15:22:00 +04:00
# define PMIC_BATT_CHR_EXCPT_MASK 0x86
2010-06-17 21:12:36 +04:00
# define PMIC_BATT_ADC_ACCCHRG_MASK (1 << 31)
# define PMIC_BATT_ADC_ACCCHRGVAL_MASK 0x7FFFFFFF
/* pmic ipc related */
# define PMIC_BATT_CHR_IPC_FCHRG_SUBID 0x4
# define PMIC_BATT_CHR_IPC_TCHRG_SUBID 0x6
/* 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 properties
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* pmic battery info
*/
struct pmic_power_module_info {
bool is_dev_info_updated ;
struct device * dev ;
/* 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 ,
} ;
/*
* Glue functions for talking to the IPC
*/
struct battery_property {
u32 capacity ; /* Charger capacity */
u8 crnt ; /* Quick charge current value*/
u8 volt ; /* Fine adjustment of constant charge voltage */
u8 prot ; /* CHRGPROT register value */
u8 prot2 ; /* CHRGPROT1 register value */
u8 timer ; /* Charging timer */
} ;
# define IPCMSG_BATTERY 0xEF
/* Battery coulomb counter accumulator commands */
# define IPC_CMD_CC_WR 0 /* Update coulomb counter value */
# define IPC_CMD_CC_RD 1 /* Read coulomb counter value */
# define IPC_CMD_BATTERY_PROPERTY 2 /* Read Battery property */
/**
* pmic_scu_ipc_battery_cc_read - read battery cc
* @ value : battery coulomb counter read
*
* Reads the battery couloumb counter value , returns 0 on success , or
* an error code
*
* This function may sleep . Locking for SCU accesses is handled for
* the caller .
*/
static int pmic_scu_ipc_battery_cc_read ( u32 * value )
{
2010-08-10 13:27:57 +04:00
return intel_scu_ipc_command ( IPCMSG_BATTERY , IPC_CMD_CC_RD ,
2010-06-17 21:12:36 +04:00
NULL , 0 , value , 1 ) ;
}
/**
* pmic_scu_ipc_battery_property_get - fetch properties
* @ prop : battery properties
*
* Retrieve the battery properties from the power management
*
* This function may sleep . Locking for SCU accesses is handled for
* the caller .
*/
static int pmic_scu_ipc_battery_property_get ( struct battery_property * prop )
{
u32 data [ 3 ] ;
u8 * p = ( u8 * ) & data [ 1 ] ;
2010-08-24 17:35:17 +04:00
int err = intel_scu_ipc_command ( IPCMSG_BATTERY ,
IPC_CMD_BATTERY_PROPERTY , NULL , 0 , data , 3 ) ;
2010-06-17 21:12:36 +04:00
prop - > capacity = data [ 0 ] ;
prop - > crnt = * p + + ;
prop - > volt = * p + + ;
prop - > prot = * p + + ;
prop - > prot2 = * p + + ;
prop - > timer = * p + + ;
return err ;
}
/**
* pmic_scu_ipc_set_charger - set charger
* @ charger : charger to select
*
* Switch the charging mode for the SCU
*/
static int pmic_scu_ipc_set_charger ( int charger )
{
2010-08-24 17:35:17 +04:00
return intel_scu_ipc_simple_command ( IPCMSG_BATTERY , charger ) ;
2010-06-17 21:12:36 +04:00
}
/**
* 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 )
{
printk ( KERN_WARNING " pmic-battery: " ) ;
switch ( event ) {
case BATT_EVENT_BATOVP_EXCPT :
printk ( KERN_CONT " battery overvoltage condition \n " ) ;
break ;
case BATT_EVENT_USBOVP_EXCPT :
printk ( KERN_CONT " usb charger overvoltage condition \n " ) ;
break ;
case BATT_EVENT_TEMP_EXCPT :
printk ( KERN_CONT " high battery temperature condition \n " ) ;
break ;
case BATT_EVENT_DCLMT_EXCPT :
printk ( KERN_CONT " over battery charge current condition \n " ) ;
break ;
default :
printk ( KERN_CONT " charger/battery exception %d \n " , event ) ;
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 ;
unsigned int chrg_val ;
u32 ccval ;
u8 r8 ;
struct battery_property batt_prop ;
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 */
if ( pmic_scu_ipc_battery_cc_read ( & ccval ) ) {
dev_warn ( pbi - > dev , " %s(): ipc config cmd failed \n " ,
__func__ ) ;
return ;
}
if ( intel_scu_ipc_ioread8 ( PMIC_BATT_CHR_SCHRGINT_ADDR , & r8 ) ) {
dev_warn ( pbi - > 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 ( r8 & 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 ( r8 & 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 ( r8 & 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 ;
2011-08-26 15:22:00 +04:00
if ( r8 & PMIC_BATT_CHR_SDCLMT_MASK ) {
/* PMIC will change charging current automatically */
pmic_battery_log_event ( BATT_EVENT_DCLMT_EXCPT ) ;
}
2010-06-17 21:12:36 +04:00
}
}
/* set usb_is_present */
if ( r8 & 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 ( r8 & 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 & PMIC_BATT_ADC_ACCCHRGVAL_MASK ;
/* set batt_prev_charge_full to battery capacity the first time */
if ( ! pbi - > is_dev_info_updated ) {
if ( pmic_scu_ipc_battery_property_get ( & batt_prop ) ) {
dev_warn ( pbi - > dev , " %s(): ipc config cmd failed \n " ,
__func__ ) ;
return ;
}
pbi - > batt_prev_charge_full = batt_prop . capacity ;
}
/* set batt_status */
if ( batt_present & & ! batt_exception ) {
if ( r8 & PMIC_BATT_CHR_SCOMP_MASK ) {
pbi - > batt_status = POWER_SUPPLY_STATUS_FULL ;
pbi - > batt_prev_charge_full = chrg_val ;
} else if ( ccval & 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 ;
}
static inline unsigned long mAStouAh ( unsigned long v )
{
/* seconds to hours, mA to µA */
2010-08-10 13:28:04 +04:00
return ( v * 1000 ) / 3600 ;
2010-06-17 21:12:36 +04:00
}
/**
* 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 = mAStouAh ( pbi - > batt_charge_now ) ;
break ;
case POWER_SUPPLY_PROP_CHARGE_FULL :
val - > intval = mAStouAh ( pbi - > batt_prev_charge_full ) ;
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 = pmic_scu_ipc_set_charger ( PMIC_BATT_CHR_IPC_FCHRG_SUBID ) ;
break ;
case BATT_USBOTG_TRICKLE_CHARGE :
retval = pmic_scu_ipc_set_charger ( PMIC_BATT_CHR_IPC_TCHRG_SUBID ) ;
break ;
default :
dev_warn ( pbi - > dev , " %s(): out of range usb charger "
" charge detected \n " , __func__ ) ;
return - EINVAL ;
}
if ( retval ) {
dev_warn ( pbi - > dev , " %s(): ipc pmic read failed \n " ,
__func__ ) ;
2011-04-09 06:49:08 +04:00
return retval ;
2010-06-17 21:12:36 +04:00
}
return 0 ;
}
/**
* 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 = 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 pmic_power_module_info * pbi = container_of ( work ,
struct pmic_power_module_info , handler ) ;
enum batt_charge_type chrg ;
u8 r8 ;
if ( intel_scu_ipc_ioread8 ( PMIC_BATT_CHR_SCHRGINT_ADDR , & r8 ) ) {
dev_warn ( pbi - > dev , " %s(): ipc pmic read failed \n " ,
__func__ ) ;
return ;
}
/* find the cause of the interrupt */
if ( r8 & 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 ( r8 & 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 ( r8 & PMIC_BATT_CHR_SCOMP_MASK ) {
u32 ccval ;
pbi - > batt_status = POWER_SUPPLY_STATUS_FULL ;
if ( pmic_scu_ipc_battery_cc_read ( & ccval ) ) {
dev_warn ( pbi - > dev , " %s(): ipc config cmd "
" failed \n " , __func__ ) ;
return ;
}
pbi - > batt_prev_charge_full = ccval &
PMIC_BATT_ADC_ACCCHRGVAL_MASK ;
return ;
}
if ( r8 & 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 */
#if 0
/* check usb otg power capability and set charger accordingly */
retval = langwell_udc_maxpower ( & power ) ;
if ( retval ) {
dev_warn ( pbi - > dev ,
" %s(): usb otg power query failed with error code %d \n " ,
__func__ , retval ) ;
return ;
}
if ( power > = 500 )
chrg = BATT_USBOTG_500MA_CHARGE ;
else
# endif
chrg = BATT_USBOTG_TRICKLE_CHARGE ;
/* enable battery charging */
if ( pmic_battery_set_charger ( pbi , chrg ) ) {
dev_warn ( pbi - > dev ,
" %s(): failed to set up battery charging \n " , __func__ ) ;
return ;
}
dev_dbg ( pbi - > dev ,
" pmic-battery: %s() - setting up battery charger successful \n " ,
__func__ ) ;
}
/**
* pmic_battery_probe - pmic battery initialize
* @ irq : pmic battery device irq
* @ dev : pmic battery device structure
* Context : can sleep
*
* PMIC battery initializes its internal data structue and other
* infrastructure components for it to work as expected .
*/
2012-11-19 22:22:23 +04:00
static int probe ( int irq , struct device * dev )
2010-06-17 21:12:36 +04:00
{
int retval = 0 ;
struct pmic_power_module_info * pbi ;
dev_dbg ( dev , " pmic-battery: found pmic battery device \n " ) ;
pbi = kzalloc ( sizeof ( * pbi ) , GFP_KERNEL ) ;
if ( ! pbi ) {
dev_err ( dev , " %s(): memory allocation failed \n " ,
__func__ ) ;
return - ENOMEM ;
}
pbi - > dev = dev ;
pbi - > irq = irq ;
dev_set_drvdata ( dev , pbi ) ;
/* initialize all required framework before enabling interrupts */
INIT_WORK ( & pbi - > handler , pmic_battery_handle_intrpt ) ;
INIT_DELAYED_WORK ( & pbi - > monitor_battery , pmic_battery_monitor ) ;
pbi - > monitor_wqueue =
create_singlethread_workqueue ( dev_name ( dev ) ) ;
if ( ! pbi - > monitor_wqueue ) {
dev_err ( 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 ( 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 ( dev , & pbi - > batt ) ;
if ( retval ) {
dev_err ( dev ,
" %s(): failed to register pmic battery device with power supply subsystem \n " ,
__func__ ) ;
goto power_reg_failed ;
}
dev_dbg ( dev , " 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 ( dev , & pbi - > usb ) ;
if ( retval ) {
dev_err ( dev ,
" %s(): failed to register pmic usb device with power supply subsystem \n " ,
__func__ ) ;
goto power_reg_failed_1 ;
}
if ( 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 :
2010-12-14 18:21:17 +03:00
cancel_delayed_work_sync ( & pbi - > monitor_battery ) ;
2010-06-17 21:12:36 +04:00
requestirq_failed :
destroy_workqueue ( pbi - > monitor_wqueue ) ;
wqueue_failed :
kfree ( pbi ) ;
return retval ;
}
2012-11-19 22:22:23 +04:00
static int platform_pmic_battery_probe ( struct platform_device * pdev )
2010-06-17 21:12:36 +04:00
{
return probe ( pdev - > id , & pdev - > dev ) ;
}
/**
* pmic_battery_remove - pmic battery finalize
* @ dev : pmic battery device structure
* Context : can sleep
*
* PMIC battery finalizes its internal data structue and other
* infrastructure components that it initialized in
* pmic_battery_probe .
*/
2012-11-19 22:26:07 +04:00
static int platform_pmic_battery_remove ( struct platform_device * pdev )
2010-06-17 21:12:36 +04:00
{
2013-05-23 14:34:39 +04:00
struct pmic_power_module_info * pbi = platform_get_drvdata ( pdev ) ;
2010-06-17 21:12:36 +04:00
free_irq ( pbi - > irq , pbi ) ;
2010-12-14 18:21:17 +03:00
cancel_delayed_work_sync ( & pbi - > monitor_battery ) ;
2010-06-17 21:12:36 +04:00
destroy_workqueue ( pbi - > monitor_wqueue ) ;
power_supply_unregister ( & pbi - > usb ) ;
power_supply_unregister ( & pbi - > batt ) ;
2010-12-11 19:51:45 +03:00
cancel_work_sync ( & pbi - > handler ) ;
2010-06-17 21:12:36 +04:00
kfree ( pbi ) ;
return 0 ;
}
static struct platform_driver platform_pmic_battery_driver = {
. driver = {
. name = DRIVER_NAME ,
. owner = THIS_MODULE ,
} ,
. probe = platform_pmic_battery_probe ,
2012-11-19 22:20:40 +04:00
. remove = platform_pmic_battery_remove ,
2010-06-17 21:12:36 +04:00
} ;
2011-11-26 08:01:10 +04:00
module_platform_driver ( platform_pmic_battery_driver ) ;
2010-06-17 21:12:36 +04:00
MODULE_AUTHOR ( " Nithish Mahalingam <nithish.mahalingam@intel.com> " ) ;
MODULE_DESCRIPTION ( " Intel Moorestown PMIC Battery Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;