2005-11-13 13:07:46 +03:00
/*
* Battery and Power Management code for the Sharp SL - C7xx
*
* Copyright ( c ) 2005 Richard Purdie
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
*/
# include <linux/module.h>
# include <linux/stat.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <linux/platform_device.h>
2007-02-27 15:10:07 +03:00
# include <linux/apm-emulation.h>
2005-11-13 13:07:46 +03:00
# include <asm/irq.h>
# include <asm/mach-types.h>
# include <asm/hardware.h>
# include <asm/hardware/scoop.h>
# include <asm/arch/sharpsl.h>
# include <asm/arch/corgi.h>
# include <asm/arch/pxa-regs.h>
# include "sharpsl.h"
2006-06-19 22:58:52 +04:00
# define SHARPSL_CHARGE_ON_VOLT 0x99 /* 2.9V */
# define SHARPSL_CHARGE_ON_TEMP 0xe0 /* 2.9V */
# define SHARPSL_CHARGE_ON_ACIN_HIGH 0x9b /* 6V */
# define SHARPSL_CHARGE_ON_ACIN_LOW 0x34 /* 2V */
# define SHARPSL_FATAL_ACIN_VOLT 182 /* 3.45V */
# define SHARPSL_FATAL_NOACIN_VOLT 170 /* 3.40V */
2005-11-13 13:07:46 +03:00
static void corgi_charger_init ( void )
{
pxa_gpio_mode ( CORGI_GPIO_ADC_TEMP_ON | GPIO_OUT ) ;
pxa_gpio_mode ( CORGI_GPIO_CHRG_ON | GPIO_OUT ) ;
pxa_gpio_mode ( CORGI_GPIO_CHRG_UKN | GPIO_OUT ) ;
pxa_gpio_mode ( CORGI_GPIO_KEY_INT | GPIO_IN ) ;
2006-01-05 23:44:55 +03:00
sharpsl_pm_pxa_init ( ) ;
2005-11-13 13:07:46 +03:00
}
static void corgi_measure_temp ( int on )
{
if ( on )
GPSR ( CORGI_GPIO_ADC_TEMP_ON ) = GPIO_bit ( CORGI_GPIO_ADC_TEMP_ON ) ;
else
GPCR ( CORGI_GPIO_ADC_TEMP_ON ) = GPIO_bit ( CORGI_GPIO_ADC_TEMP_ON ) ;
}
static void corgi_charge ( int on )
{
if ( on ) {
if ( machine_is_corgi ( ) & & ( sharpsl_pm . flags & SHARPSL_SUSPENDED ) ) {
GPCR ( CORGI_GPIO_CHRG_ON ) = GPIO_bit ( CORGI_GPIO_CHRG_ON ) ;
GPSR ( CORGI_GPIO_CHRG_UKN ) = GPIO_bit ( CORGI_GPIO_CHRG_UKN ) ;
} else {
GPSR ( CORGI_GPIO_CHRG_ON ) = GPIO_bit ( CORGI_GPIO_CHRG_ON ) ;
GPCR ( CORGI_GPIO_CHRG_UKN ) = GPIO_bit ( CORGI_GPIO_CHRG_UKN ) ;
}
} else {
GPCR ( CORGI_GPIO_CHRG_ON ) = GPIO_bit ( CORGI_GPIO_CHRG_ON ) ;
GPCR ( CORGI_GPIO_CHRG_UKN ) = GPIO_bit ( CORGI_GPIO_CHRG_UKN ) ;
}
}
static void corgi_discharge ( int on )
{
if ( on )
GPSR ( CORGI_GPIO_DISCHARGE_ON ) = GPIO_bit ( CORGI_GPIO_DISCHARGE_ON ) ;
else
GPCR ( CORGI_GPIO_DISCHARGE_ON ) = GPIO_bit ( CORGI_GPIO_DISCHARGE_ON ) ;
}
static void corgi_presuspend ( void )
{
int i ;
unsigned long wakeup_mask ;
/* charging , so CHARGE_ON bit is HIGH during OFF. */
if ( READ_GPIO_BIT ( CORGI_GPIO_CHRG_ON ) )
PGSR1 | = GPIO_bit ( CORGI_GPIO_CHRG_ON ) ;
else
PGSR1 & = ~ GPIO_bit ( CORGI_GPIO_CHRG_ON ) ;
if ( READ_GPIO_BIT ( CORGI_GPIO_LED_ORANGE ) )
PGSR0 | = GPIO_bit ( CORGI_GPIO_LED_ORANGE ) ;
else
PGSR0 & = ~ GPIO_bit ( CORGI_GPIO_LED_ORANGE ) ;
if ( READ_GPIO_BIT ( CORGI_GPIO_CHRG_UKN ) )
PGSR1 | = GPIO_bit ( CORGI_GPIO_CHRG_UKN ) ;
else
PGSR1 & = ~ GPIO_bit ( CORGI_GPIO_CHRG_UKN ) ;
/* Resume on keyboard power key */
PGSR2 = ( PGSR2 & ~ CORGI_GPIO_ALL_STROBE_BIT ) | CORGI_GPIO_STROBE_BIT ( 0 ) ;
wakeup_mask = GPIO_bit ( CORGI_GPIO_KEY_INT ) | GPIO_bit ( CORGI_GPIO_WAKEUP ) | GPIO_bit ( CORGI_GPIO_AC_IN ) | GPIO_bit ( CORGI_GPIO_CHRG_FULL ) ;
if ( ! machine_is_corgi ( ) )
wakeup_mask | = GPIO_bit ( CORGI_GPIO_MAIN_BAT_LOW ) ;
PWER = wakeup_mask | PWER_RTC ;
PRER = wakeup_mask ;
PFER = wakeup_mask ;
for ( i = 0 ; i < = 15 ; i + + ) {
if ( PRER & PFER & GPIO_bit ( i ) ) {
if ( GPLR0 & GPIO_bit ( i ) )
PRER & = ~ GPIO_bit ( i ) ;
else
PFER & = ~ GPIO_bit ( i ) ;
}
}
}
static void corgi_postsuspend ( void )
{
}
/*
* Check what brought us out of the suspend .
* Return : 0 to sleep , otherwise wake
*/
static int corgi_should_wakeup ( unsigned int resume_on_alarm )
{
int is_resume = 0 ;
dev_dbg ( sharpsl_pm . dev , " GPLR0 = %x,%x \n " , GPLR0 , PEDR ) ;
if ( ( PEDR & GPIO_bit ( CORGI_GPIO_AC_IN ) ) ) {
2006-01-05 23:44:55 +03:00
if ( sharpsl_pm . machinfo - > read_devdata ( SHARPSL_STATUS_ACIN ) ) {
2005-11-13 13:07:46 +03:00
/* charge on */
dev_dbg ( sharpsl_pm . dev , " ac insert \n " ) ;
sharpsl_pm . flags | = SHARPSL_DO_OFFLINE_CHRG ;
} else {
/* charge off */
dev_dbg ( sharpsl_pm . dev , " ac remove \n " ) ;
2006-01-05 23:44:55 +03:00
sharpsl_pm_led ( SHARPSL_LED_OFF ) ;
sharpsl_pm . machinfo - > charge ( 0 ) ;
2005-11-13 13:07:46 +03:00
sharpsl_pm . charge_mode = CHRG_OFF ;
}
}
if ( ( PEDR & GPIO_bit ( CORGI_GPIO_CHRG_FULL ) ) )
dev_dbg ( sharpsl_pm . dev , " Charge full interrupt \n " ) ;
if ( PEDR & GPIO_bit ( CORGI_GPIO_KEY_INT ) )
is_resume | = GPIO_bit ( CORGI_GPIO_KEY_INT ) ;
if ( PEDR & GPIO_bit ( CORGI_GPIO_WAKEUP ) )
is_resume | = GPIO_bit ( CORGI_GPIO_WAKEUP ) ;
if ( resume_on_alarm & & ( PEDR & PWER_RTC ) )
is_resume | = PWER_RTC ;
dev_dbg ( sharpsl_pm . dev , " is_resume: %x \n " , is_resume ) ;
return is_resume ;
}
static unsigned long corgi_charger_wakeup ( void )
{
return ~ GPLR0 & ( GPIO_bit ( CORGI_GPIO_AC_IN ) | GPIO_bit ( CORGI_GPIO_KEY_INT ) | GPIO_bit ( CORGI_GPIO_WAKEUP ) ) ;
}
2006-01-05 23:44:55 +03:00
unsigned long corgipm_read_devdata ( int type )
2005-11-13 13:07:46 +03:00
{
2006-01-05 23:44:55 +03:00
switch ( type ) {
case SHARPSL_STATUS_ACIN :
return ( ( GPLR ( CORGI_GPIO_AC_IN ) & GPIO_bit ( CORGI_GPIO_AC_IN ) ) ! = 0 ) ;
case SHARPSL_STATUS_LOCK :
return READ_GPIO_BIT ( sharpsl_pm . machinfo - > gpio_batlock ) ;
case SHARPSL_STATUS_CHRGFULL :
return READ_GPIO_BIT ( sharpsl_pm . machinfo - > gpio_batfull ) ;
case SHARPSL_STATUS_FATAL :
return READ_GPIO_BIT ( sharpsl_pm . machinfo - > gpio_fatal ) ;
case SHARPSL_ACIN_VOLT :
return sharpsl_pm_pxa_read_max1111 ( MAX1111_ACIN_VOLT ) ;
case SHARPSL_BATT_TEMP :
return sharpsl_pm_pxa_read_max1111 ( MAX1111_BATT_TEMP ) ;
case SHARPSL_BATT_VOLT :
default :
return sharpsl_pm_pxa_read_max1111 ( MAX1111_BATT_VOLT ) ;
}
2005-11-13 13:07:46 +03:00
}
static struct sharpsl_charger_machinfo corgi_pm_machinfo = {
. init = corgi_charger_init ,
2006-01-05 23:44:55 +03:00
. exit = sharpsl_pm_pxa_remove ,
2005-11-13 13:07:46 +03:00
. gpio_batlock = CORGI_GPIO_BAT_COVER ,
. gpio_acin = CORGI_GPIO_AC_IN ,
. gpio_batfull = CORGI_GPIO_CHRG_FULL ,
. discharge = corgi_discharge ,
. charge = corgi_charge ,
. measure_temp = corgi_measure_temp ,
. presuspend = corgi_presuspend ,
. postsuspend = corgi_postsuspend ,
2006-01-05 23:44:55 +03:00
. read_devdata = corgipm_read_devdata ,
2005-11-13 13:07:46 +03:00
. charger_wakeup = corgi_charger_wakeup ,
. should_wakeup = corgi_should_wakeup ,
2006-06-19 22:58:52 +04:00
. backlight_limit = corgibl_limit_intensity ,
. charge_on_volt = SHARPSL_CHARGE_ON_VOLT ,
. charge_on_temp = SHARPSL_CHARGE_ON_TEMP ,
. charge_acin_high = SHARPSL_CHARGE_ON_ACIN_HIGH ,
. charge_acin_low = SHARPSL_CHARGE_ON_ACIN_LOW ,
. fatal_acin_volt = SHARPSL_FATAL_ACIN_VOLT ,
. fatal_noacin_volt = SHARPSL_FATAL_NOACIN_VOLT ,
. bat_levels = 40 ,
. bat_levels_noac = spitz_battery_levels_noac ,
. bat_levels_acin = spitz_battery_levels_acin ,
2005-11-13 13:07:46 +03:00
. status_high_acin = 188 ,
. status_low_acin = 178 ,
. status_high_noac = 185 ,
. status_low_noac = 175 ,
} ;
static struct platform_device * corgipm_device ;
static int __devinit corgipm_init ( void )
{
int ret ;
corgipm_device = platform_device_alloc ( " sharpsl-pm " , - 1 ) ;
if ( ! corgipm_device )
return - ENOMEM ;
2006-06-19 22:58:52 +04:00
if ( ! machine_is_corgi ( ) )
corgi_pm_machinfo . batfull_irq = 1 ;
2005-11-13 13:07:46 +03:00
corgipm_device - > dev . platform_data = & corgi_pm_machinfo ;
ret = platform_device_add ( corgipm_device ) ;
if ( ret )
platform_device_put ( corgipm_device ) ;
return ret ;
}
static void corgipm_exit ( void )
{
platform_device_unregister ( corgipm_device ) ;
}
module_init ( corgipm_init ) ;
module_exit ( corgipm_exit ) ;