2009-08-31 20:32:18 +04:00
/*
* linux / drivers / i2c / chips / twl4030 - power . c
*
* Handle TWL4030 Power initialization
*
* Copyright ( C ) 2008 Nokia Corporation
* Copyright ( C ) 2006 Texas Instruments , Inc
*
* Written by Kalle Jokiniemi
* Peter De Schrijver < peter . de - schrijver @ nokia . com >
* Several fixes by Amit Kucheria < amit . kucheria @ verdurent . com >
*
* This file is subject to the terms and conditions of the GNU General
* Public License . See the file " COPYING " in the main directory of this
* archive for more details .
*
* 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
*/
# include <linux/module.h>
# include <linux/pm.h>
2009-12-13 22:05:51 +03:00
# include <linux/i2c/twl.h>
2009-08-31 20:32:18 +04:00
# include <linux/platform_device.h>
2013-06-18 17:17:58 +04:00
# include <linux/of.h>
2009-08-31 20:32:18 +04:00
# include <asm/mach-types.h>
static u8 twl4030_start_script_address = 0x2b ;
2014-05-20 22:17:53 +04:00
/* Register bits for P1, P2 and P3_SW_EVENTS */
# define PWR_STOPON_PRWON BIT(6)
# define PWR_STOPON_SYSEN BIT(5)
# define PWR_ENABLE_WARMRESET BIT(4)
# define PWR_LVL_WAKEUP BIT(3)
# define PWR_DEVACT BIT(2)
# define PWR_DEVSLP BIT(1)
# define PWR_DEVOFF BIT(0)
2011-11-13 13:49:50 +04:00
# define SEQ_OFFSYNC (1 << 0)
2009-08-31 20:32:18 +04:00
# define PHY_TO_OFF_PM_MASTER(p) (p - 0x36)
# define PHY_TO_OFF_PM_RECEIVER(p) (p - 0x5b)
/* resource - hfclk */
# define R_HFCLKOUT_DEV_GRP PHY_TO_OFF_PM_RECEIVER(0xe6)
/* PM events */
# define R_P1_SW_EVENTS PHY_TO_OFF_PM_MASTER(0x46)
# define R_P2_SW_EVENTS PHY_TO_OFF_PM_MASTER(0x47)
# define R_P3_SW_EVENTS PHY_TO_OFF_PM_MASTER(0x48)
# define R_CFG_P1_TRANSITION PHY_TO_OFF_PM_MASTER(0x36)
# define R_CFG_P2_TRANSITION PHY_TO_OFF_PM_MASTER(0x37)
# define R_CFG_P3_TRANSITION PHY_TO_OFF_PM_MASTER(0x38)
# define END_OF_SCRIPT 0x3f
# define R_SEQ_ADD_A2S PHY_TO_OFF_PM_MASTER(0x55)
# define R_SEQ_ADD_S2A12 PHY_TO_OFF_PM_MASTER(0x56)
# define R_SEQ_ADD_S2A3 PHY_TO_OFF_PM_MASTER(0x57)
# define R_SEQ_ADD_WARM PHY_TO_OFF_PM_MASTER(0x58)
# define R_MEMORY_ADDRESS PHY_TO_OFF_PM_MASTER(0x59)
# define R_MEMORY_DATA PHY_TO_OFF_PM_MASTER(0x5a)
2009-10-19 16:10:48 +04:00
/* resource configuration registers
< RESOURCE > _DEV_GRP at address ' n + 0 '
< RESOURCE > _TYPE at address ' n + 1 '
< RESOURCE > _REMAP at address ' n + 2 '
< RESOURCE > _DEDICATED at address ' n + 3 '
*/
2009-10-19 16:10:44 +04:00
# define DEV_GRP_OFFSET 0
2009-08-31 20:32:18 +04:00
# define TYPE_OFFSET 1
2009-10-19 16:11:00 +04:00
# define REMAP_OFFSET 2
# define DEDICATED_OFFSET 3
2009-08-31 20:32:18 +04:00
2009-10-19 16:10:44 +04:00
/* Bit positions in the registers */
2009-10-19 16:10:48 +04:00
/* <RESOURCE>_DEV_GRP */
2009-10-19 16:10:44 +04:00
# define DEV_GRP_SHIFT 5
# define DEV_GRP_MASK (7 << DEV_GRP_SHIFT)
2009-10-19 16:10:48 +04:00
/* <RESOURCE>_TYPE */
2009-08-31 20:32:18 +04:00
# define TYPE_SHIFT 0
# define TYPE_MASK (7 << TYPE_SHIFT)
# define TYPE2_SHIFT 3
# define TYPE2_MASK (3 << TYPE2_SHIFT)
2009-10-19 16:11:00 +04:00
/* <RESOURCE>_REMAP */
# define SLEEP_STATE_SHIFT 0
# define SLEEP_STATE_MASK (0xf << SLEEP_STATE_SHIFT)
# define OFF_STATE_SHIFT 4
# define OFF_STATE_MASK (0xf << OFF_STATE_SHIFT)
2009-08-31 20:32:18 +04:00
static u8 res_config_addrs [ ] = {
[ RES_VAUX1 ] = 0x17 ,
[ RES_VAUX2 ] = 0x1b ,
[ RES_VAUX3 ] = 0x1f ,
[ RES_VAUX4 ] = 0x23 ,
[ RES_VMMC1 ] = 0x27 ,
[ RES_VMMC2 ] = 0x2b ,
[ RES_VPLL1 ] = 0x2f ,
[ RES_VPLL2 ] = 0x33 ,
[ RES_VSIM ] = 0x37 ,
[ RES_VDAC ] = 0x3b ,
[ RES_VINTANA1 ] = 0x3f ,
[ RES_VINTANA2 ] = 0x43 ,
[ RES_VINTDIG ] = 0x47 ,
[ RES_VIO ] = 0x4b ,
[ RES_VDD1 ] = 0x55 ,
[ RES_VDD2 ] = 0x63 ,
[ RES_VUSB_1V5 ] = 0x71 ,
[ RES_VUSB_1V8 ] = 0x74 ,
[ RES_VUSB_3V1 ] = 0x77 ,
[ RES_VUSBCP ] = 0x7a ,
[ RES_REGEN ] = 0x7f ,
[ RES_NRES_PWRON ] = 0x82 ,
[ RES_CLKEN ] = 0x85 ,
[ RES_SYSEN ] = 0x88 ,
[ RES_HFCLKOUT ] = 0x8b ,
[ RES_32KCLKOUT ] = 0x8e ,
[ RES_RESET ] = 0x91 ,
2011-04-14 16:27:51 +04:00
[ RES_MAIN_REF ] = 0x94 ,
2009-08-31 20:32:18 +04:00
} ;
2012-11-19 22:23:04 +04:00
static int twl4030_write_script_byte ( u8 address , u8 byte )
2009-08-31 20:32:18 +04:00
{
int err ;
2012-11-13 12:28:52 +04:00
err = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , address , R_MEMORY_ADDRESS ) ;
2009-08-31 20:32:18 +04:00
if ( err )
goto out ;
2012-11-13 12:28:52 +04:00
err = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , byte , R_MEMORY_DATA ) ;
2009-08-31 20:32:18 +04:00
out :
return err ;
}
2012-11-19 22:23:04 +04:00
static int twl4030_write_script_ins ( u8 address , u16 pmb_message ,
2009-08-31 20:32:18 +04:00
u8 delay , u8 next )
{
int err ;
address * = 4 ;
err = twl4030_write_script_byte ( address + + , pmb_message > > 8 ) ;
if ( err )
goto out ;
err = twl4030_write_script_byte ( address + + , pmb_message & 0xff ) ;
if ( err )
goto out ;
err = twl4030_write_script_byte ( address + + , delay ) ;
if ( err )
goto out ;
err = twl4030_write_script_byte ( address + + , next ) ;
out :
return err ;
}
2012-11-19 22:23:04 +04:00
static int twl4030_write_script ( u8 address , struct twl4030_ins * script ,
2009-08-31 20:32:18 +04:00
int len )
{
2013-01-25 18:14:26 +04:00
int err = - EINVAL ;
2009-08-31 20:32:18 +04:00
for ( ; len ; len - - , address + + , script + + ) {
if ( len = = 1 ) {
err = twl4030_write_script_ins ( address ,
script - > pmb_message ,
script - > delay ,
END_OF_SCRIPT ) ;
if ( err )
break ;
} else {
err = twl4030_write_script_ins ( address ,
script - > pmb_message ,
script - > delay ,
address + 1 ) ;
if ( err )
break ;
}
}
return err ;
}
2012-11-19 22:23:04 +04:00
static int twl4030_config_wakeup3_sequence ( u8 address )
2009-08-31 20:32:18 +04:00
{
int err ;
u8 data ;
/* Set SLEEP to ACTIVE SEQ address for P3 */
2012-11-13 12:28:52 +04:00
err = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , address , R_SEQ_ADD_S2A3 ) ;
2009-08-31 20:32:18 +04:00
if ( err )
goto out ;
/* P3 LVL_WAKEUP should be on LEVEL */
2012-11-13 12:28:52 +04:00
err = twl_i2c_read_u8 ( TWL_MODULE_PM_MASTER , & data , R_P3_SW_EVENTS ) ;
2009-08-31 20:32:18 +04:00
if ( err )
goto out ;
2014-05-20 22:17:53 +04:00
data | = PWR_LVL_WAKEUP ;
2012-11-13 12:28:52 +04:00
err = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , data , R_P3_SW_EVENTS ) ;
2009-08-31 20:32:18 +04:00
out :
if ( err )
pr_err ( " TWL4030 wakeup sequence for P3 config error \n " ) ;
return err ;
}
2012-11-19 22:23:04 +04:00
static int twl4030_config_wakeup12_sequence ( u8 address )
2009-08-31 20:32:18 +04:00
{
int err = 0 ;
u8 data ;
/* Set SLEEP to ACTIVE SEQ address for P1 and P2 */
2012-11-13 12:28:52 +04:00
err = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , address , R_SEQ_ADD_S2A12 ) ;
2009-08-31 20:32:18 +04:00
if ( err )
goto out ;
/* P1/P2 LVL_WAKEUP should be on LEVEL */
2012-11-13 12:28:52 +04:00
err = twl_i2c_read_u8 ( TWL_MODULE_PM_MASTER , & data , R_P1_SW_EVENTS ) ;
2009-08-31 20:32:18 +04:00
if ( err )
goto out ;
2014-05-20 22:17:53 +04:00
data | = PWR_LVL_WAKEUP ;
2012-11-13 12:28:52 +04:00
err = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , data , R_P1_SW_EVENTS ) ;
2009-08-31 20:32:18 +04:00
if ( err )
goto out ;
2012-11-13 12:28:52 +04:00
err = twl_i2c_read_u8 ( TWL_MODULE_PM_MASTER , & data , R_P2_SW_EVENTS ) ;
2009-08-31 20:32:18 +04:00
if ( err )
goto out ;
2014-05-20 22:17:53 +04:00
data | = PWR_LVL_WAKEUP ;
2012-11-13 12:28:52 +04:00
err = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , data , R_P2_SW_EVENTS ) ;
2009-08-31 20:32:18 +04:00
if ( err )
goto out ;
if ( machine_is_omap_3430sdp ( ) | | machine_is_omap_ldp ( ) ) {
/* Disabling AC charger effect on sleep-active transitions */
2012-11-13 12:28:52 +04:00
err = twl_i2c_read_u8 ( TWL_MODULE_PM_MASTER , & data ,
R_CFG_P1_TRANSITION ) ;
2009-08-31 20:32:18 +04:00
if ( err )
goto out ;
data & = ~ ( 1 < < 1 ) ;
2012-11-13 12:28:52 +04:00
err = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , data ,
R_CFG_P1_TRANSITION ) ;
2009-08-31 20:32:18 +04:00
if ( err )
goto out ;
}
out :
if ( err )
pr_err ( " TWL4030 wakeup sequence for P1 and P2 " \
" config error \n " ) ;
return err ;
}
2012-11-19 22:23:04 +04:00
static int twl4030_config_sleep_sequence ( u8 address )
2009-08-31 20:32:18 +04:00
{
int err ;
/* Set ACTIVE to SLEEP SEQ address in T2 memory*/
2012-11-13 12:28:52 +04:00
err = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , address , R_SEQ_ADD_A2S ) ;
2009-08-31 20:32:18 +04:00
if ( err )
pr_err ( " TWL4030 sleep sequence config error \n " ) ;
return err ;
}
2012-11-19 22:23:04 +04:00
static int twl4030_config_warmreset_sequence ( u8 address )
2009-08-31 20:32:18 +04:00
{
int err ;
u8 rd_data ;
/* Set WARM RESET SEQ address for P1 */
2012-11-13 12:28:52 +04:00
err = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , address , R_SEQ_ADD_WARM ) ;
2009-08-31 20:32:18 +04:00
if ( err )
goto out ;
/* P1/P2/P3 enable WARMRESET */
2012-11-13 12:28:52 +04:00
err = twl_i2c_read_u8 ( TWL_MODULE_PM_MASTER , & rd_data , R_P1_SW_EVENTS ) ;
2009-08-31 20:32:18 +04:00
if ( err )
goto out ;
2014-05-20 22:17:53 +04:00
rd_data | = PWR_ENABLE_WARMRESET ;
2012-11-13 12:28:52 +04:00
err = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , rd_data , R_P1_SW_EVENTS ) ;
2009-08-31 20:32:18 +04:00
if ( err )
goto out ;
2012-11-13 12:28:52 +04:00
err = twl_i2c_read_u8 ( TWL_MODULE_PM_MASTER , & rd_data , R_P2_SW_EVENTS ) ;
2009-08-31 20:32:18 +04:00
if ( err )
goto out ;
2014-05-20 22:17:53 +04:00
rd_data | = PWR_ENABLE_WARMRESET ;
2012-11-13 12:28:52 +04:00
err = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , rd_data , R_P2_SW_EVENTS ) ;
2009-08-31 20:32:18 +04:00
if ( err )
goto out ;
2012-11-13 12:28:52 +04:00
err = twl_i2c_read_u8 ( TWL_MODULE_PM_MASTER , & rd_data , R_P3_SW_EVENTS ) ;
2009-08-31 20:32:18 +04:00
if ( err )
goto out ;
2014-05-20 22:17:53 +04:00
rd_data | = PWR_ENABLE_WARMRESET ;
2012-11-13 12:28:52 +04:00
err = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , rd_data , R_P3_SW_EVENTS ) ;
2009-08-31 20:32:18 +04:00
out :
if ( err )
pr_err ( " TWL4030 warmreset seq config error \n " ) ;
return err ;
}
2012-11-19 22:23:04 +04:00
static int twl4030_configure_resource ( struct twl4030_resconfig * rconfig )
2009-08-31 20:32:18 +04:00
{
int rconfig_addr ;
int err ;
u8 type ;
u8 grp ;
2009-10-19 16:11:00 +04:00
u8 remap ;
2009-08-31 20:32:18 +04:00
if ( rconfig - > resource > TOTAL_RESOURCES ) {
pr_err ( " TWL4030 Resource %d does not exist \n " ,
rconfig - > resource ) ;
return - EINVAL ;
}
rconfig_addr = res_config_addrs [ rconfig - > resource ] ;
/* Set resource group */
2012-11-13 12:28:52 +04:00
err = twl_i2c_read_u8 ( TWL_MODULE_PM_RECEIVER , & grp ,
2009-12-13 23:23:33 +03:00
rconfig_addr + DEV_GRP_OFFSET ) ;
2009-08-31 20:32:18 +04:00
if ( err ) {
pr_err ( " TWL4030 Resource %d group could not be read \n " ,
rconfig - > resource ) ;
return err ;
}
2009-10-19 23:24:02 +04:00
if ( rconfig - > devgroup ! = TWL4030_RESCONFIG_UNDEF ) {
2009-10-19 16:10:44 +04:00
grp & = ~ DEV_GRP_MASK ;
grp | = rconfig - > devgroup < < DEV_GRP_SHIFT ;
2012-11-13 12:28:52 +04:00
err = twl_i2c_write_u8 ( TWL_MODULE_PM_RECEIVER ,
2009-12-13 23:23:33 +03:00
grp , rconfig_addr + DEV_GRP_OFFSET ) ;
2009-08-31 20:32:18 +04:00
if ( err < 0 ) {
pr_err ( " TWL4030 failed to program devgroup \n " ) ;
return err ;
}
}
/* Set resource types */
2012-11-13 12:28:52 +04:00
err = twl_i2c_read_u8 ( TWL_MODULE_PM_RECEIVER , & type ,
2009-08-31 20:32:18 +04:00
rconfig_addr + TYPE_OFFSET ) ;
if ( err < 0 ) {
pr_err ( " TWL4030 Resource %d type could not be read \n " ,
rconfig - > resource ) ;
return err ;
}
2009-10-19 23:24:02 +04:00
if ( rconfig - > type ! = TWL4030_RESCONFIG_UNDEF ) {
2009-08-31 20:32:18 +04:00
type & = ~ TYPE_MASK ;
type | = rconfig - > type < < TYPE_SHIFT ;
}
2009-10-19 23:24:02 +04:00
if ( rconfig - > type2 ! = TWL4030_RESCONFIG_UNDEF ) {
2009-08-31 20:32:18 +04:00
type & = ~ TYPE2_MASK ;
type | = rconfig - > type2 < < TYPE2_SHIFT ;
}
2012-11-13 12:28:52 +04:00
err = twl_i2c_write_u8 ( TWL_MODULE_PM_RECEIVER ,
2009-08-31 20:32:18 +04:00
type , rconfig_addr + TYPE_OFFSET ) ;
if ( err < 0 ) {
pr_err ( " TWL4030 failed to program resource type \n " ) ;
return err ;
}
2009-10-19 16:11:00 +04:00
/* Set remap states */
2012-11-13 12:28:52 +04:00
err = twl_i2c_read_u8 ( TWL_MODULE_PM_RECEIVER , & remap ,
2009-12-13 23:23:33 +03:00
rconfig_addr + REMAP_OFFSET ) ;
2009-10-19 16:11:00 +04:00
if ( err < 0 ) {
pr_err ( " TWL4030 Resource %d remap could not be read \n " ,
rconfig - > resource ) ;
return err ;
}
2009-10-21 15:49:22 +04:00
if ( rconfig - > remap_off ! = TWL4030_RESCONFIG_UNDEF ) {
2009-10-19 16:11:00 +04:00
remap & = ~ OFF_STATE_MASK ;
remap | = rconfig - > remap_off < < OFF_STATE_SHIFT ;
}
2009-10-21 15:49:22 +04:00
if ( rconfig - > remap_sleep ! = TWL4030_RESCONFIG_UNDEF ) {
2009-10-19 16:11:00 +04:00
remap & = ~ SLEEP_STATE_MASK ;
2010-02-05 11:51:37 +03:00
remap | = rconfig - > remap_sleep < < SLEEP_STATE_SHIFT ;
2009-10-19 16:11:00 +04:00
}
2012-11-13 12:28:52 +04:00
err = twl_i2c_write_u8 ( TWL_MODULE_PM_RECEIVER ,
2009-12-13 23:23:33 +03:00
remap ,
rconfig_addr + REMAP_OFFSET ) ;
2009-10-19 16:11:00 +04:00
if ( err < 0 ) {
pr_err ( " TWL4030 failed to program remap \n " ) ;
return err ;
}
2009-08-31 20:32:18 +04:00
return 0 ;
}
2012-11-19 22:23:04 +04:00
static int load_twl4030_script ( struct twl4030_script * tscript ,
2009-08-31 20:32:18 +04:00
u8 address )
{
int err ;
2009-08-17 18:01:56 +04:00
static int order ;
2009-08-31 20:32:18 +04:00
/* Make sure the script isn't going beyond last valid address (0x3f) */
if ( ( address + tscript - > size ) > END_OF_SCRIPT ) {
pr_err ( " TWL4030 scripts too big error \n " ) ;
return - EINVAL ;
}
err = twl4030_write_script ( address , tscript - > script , tscript - > size ) ;
if ( err )
goto out ;
if ( tscript - > flags & TWL4030_WRST_SCRIPT ) {
err = twl4030_config_warmreset_sequence ( address ) ;
if ( err )
goto out ;
}
if ( tscript - > flags & TWL4030_WAKEUP12_SCRIPT ) {
2014-05-14 05:34:04 +04:00
/* Reset any existing sleep script to avoid hangs on reboot */
err = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , END_OF_SCRIPT ,
R_SEQ_ADD_A2S ) ;
if ( err )
goto out ;
2009-08-31 20:32:18 +04:00
err = twl4030_config_wakeup12_sequence ( address ) ;
if ( err )
goto out ;
2009-08-17 18:01:56 +04:00
order = 1 ;
2009-08-31 20:32:18 +04:00
}
if ( tscript - > flags & TWL4030_WAKEUP3_SCRIPT ) {
err = twl4030_config_wakeup3_sequence ( address ) ;
if ( err )
goto out ;
}
2011-04-14 16:27:49 +04:00
if ( tscript - > flags & TWL4030_SLEEP_SCRIPT ) {
2011-04-14 16:27:50 +04:00
if ( ! order )
2009-08-17 18:01:56 +04:00
pr_warning ( " TWL4030: Bad order of scripts (sleep " \
" script before wakeup) Leads to boot " \
" failure on some boards \n " ) ;
2009-08-31 20:32:18 +04:00
err = twl4030_config_sleep_sequence ( address ) ;
2011-04-14 16:27:49 +04:00
}
2009-08-31 20:32:18 +04:00
out :
return err ;
}
2010-02-22 20:16:30 +03:00
int twl4030_remove_script ( u8 flags )
{
int err = 0 ;
2012-11-13 12:28:52 +04:00
err = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , TWL4030_PM_MASTER_KEY_CFG1 ,
TWL4030_PM_MASTER_PROTECT_KEY ) ;
2010-02-22 20:16:30 +03:00
if ( err ) {
pr_err ( " twl4030: unable to unlock PROTECT_KEY \n " ) ;
return err ;
}
2012-11-13 12:28:52 +04:00
err = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , TWL4030_PM_MASTER_KEY_CFG2 ,
TWL4030_PM_MASTER_PROTECT_KEY ) ;
2010-02-22 20:16:30 +03:00
if ( err ) {
pr_err ( " twl4030: unable to unlock PROTECT_KEY \n " ) ;
return err ;
}
if ( flags & TWL4030_WRST_SCRIPT ) {
2012-11-13 12:28:52 +04:00
err = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , END_OF_SCRIPT ,
R_SEQ_ADD_WARM ) ;
2010-02-22 20:16:30 +03:00
if ( err )
return err ;
}
if ( flags & TWL4030_WAKEUP12_SCRIPT ) {
2012-11-13 12:28:52 +04:00
err = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , END_OF_SCRIPT ,
R_SEQ_ADD_S2A12 ) ;
2011-05-04 16:08:53 +04:00
if ( err )
2010-02-22 20:16:30 +03:00
return err ;
}
if ( flags & TWL4030_WAKEUP3_SCRIPT ) {
2012-11-13 12:28:52 +04:00
err = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , END_OF_SCRIPT ,
R_SEQ_ADD_S2A3 ) ;
2010-02-22 20:16:30 +03:00
if ( err )
return err ;
}
if ( flags & TWL4030_SLEEP_SCRIPT ) {
2012-11-13 12:28:52 +04:00
err = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , END_OF_SCRIPT ,
R_SEQ_ADD_A2S ) ;
2010-02-22 20:16:30 +03:00
if ( err )
return err ;
}
2012-11-13 12:28:52 +04:00
err = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , 0 ,
TWL4030_PM_MASTER_PROTECT_KEY ) ;
2010-02-22 20:16:30 +03:00
if ( err )
pr_err ( " TWL4030 Unable to relock registers \n " ) ;
return err ;
}
2013-08-01 05:52:55 +04:00
static int twl4030_power_configure_scripts ( struct twl4030_power_data * pdata )
2013-06-18 17:17:57 +04:00
{
int err ;
int i ;
u8 address = twl4030_start_script_address ;
for ( i = 0 ; i < pdata - > num ; i + + ) {
err = load_twl4030_script ( pdata - > scripts [ i ] , address ) ;
if ( err )
return err ;
address + = pdata - > scripts [ i ] - > size ;
}
return 0 ;
}
2013-08-01 05:52:55 +04:00
static int twl4030_power_configure_resources ( struct twl4030_power_data * pdata )
2013-06-18 17:17:57 +04:00
{
struct twl4030_resconfig * resconfig = pdata - > resource_config ;
int err ;
if ( resconfig ) {
while ( resconfig - > resource ) {
err = twl4030_configure_resource ( resconfig ) ;
if ( err )
return err ;
resconfig + + ;
}
}
return 0 ;
}
2011-11-13 13:49:50 +04:00
/*
* In master mode , start the power off sequence .
* After a successful execution , TWL shuts down the power to the SoC
* and all peripherals connected to it .
*/
void twl4030_power_off ( void )
{
int err ;
2012-11-13 12:28:52 +04:00
err = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , PWR_DEVOFF ,
2011-11-13 13:49:50 +04:00
TWL4030_PM_MASTER_P1_SW_EVENTS ) ;
if ( err )
pr_err ( " TWL4030 Unable to power off \n " ) ;
}
2013-06-18 17:17:58 +04:00
static bool twl4030_power_use_poweroff ( struct twl4030_power_data * pdata ,
struct device_node * node )
{
if ( pdata & & pdata - > use_poweroff )
return true ;
if ( of_property_read_bool ( node , " ti,use_poweroff " ) )
return true ;
return false ;
}
2013-08-01 05:52:55 +04:00
static int twl4030_power_probe ( struct platform_device * pdev )
2009-08-31 20:32:18 +04:00
{
2013-07-30 12:10:05 +04:00
struct twl4030_power_data * pdata = dev_get_platdata ( & pdev - > dev ) ;
2013-06-18 17:17:58 +04:00
struct device_node * node = pdev - > dev . of_node ;
2009-08-31 20:32:18 +04:00
int err = 0 ;
2013-06-18 17:18:00 +04:00
int err2 = 0 ;
2013-06-18 17:17:57 +04:00
u8 val ;
2009-08-31 20:32:18 +04:00
2013-06-18 17:17:58 +04:00
if ( ! pdata & & ! node ) {
dev_err ( & pdev - > dev , " Platform data is missing \n " ) ;
return - EINVAL ;
}
2012-11-13 12:28:52 +04:00
err = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , TWL4030_PM_MASTER_KEY_CFG1 ,
TWL4030_PM_MASTER_PROTECT_KEY ) ;
2013-06-18 17:17:59 +04:00
err | = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER ,
TWL4030_PM_MASTER_KEY_CFG2 ,
2012-11-13 12:28:52 +04:00
TWL4030_PM_MASTER_PROTECT_KEY ) ;
2013-06-18 17:17:59 +04:00
if ( err ) {
pr_err ( " TWL4030 Unable to unlock registers \n " ) ;
return err ;
}
2009-08-31 20:32:18 +04:00
2013-06-18 17:17:58 +04:00
if ( pdata ) {
/* TODO: convert to device tree */
err = twl4030_power_configure_scripts ( pdata ) ;
2013-06-18 17:17:59 +04:00
if ( err ) {
pr_err ( " TWL4030 failed to load scripts \n " ) ;
2013-06-18 17:18:00 +04:00
goto relock ;
2013-06-18 17:17:59 +04:00
}
2013-06-18 17:17:58 +04:00
err = twl4030_power_configure_resources ( pdata ) ;
2013-06-18 17:17:59 +04:00
if ( err ) {
pr_err ( " TWL4030 failed to configure resource \n " ) ;
2013-06-18 17:18:00 +04:00
goto relock ;
2013-06-18 17:17:59 +04:00
}
2013-06-18 17:17:58 +04:00
}
2009-08-31 20:32:18 +04:00
2011-11-13 13:49:50 +04:00
/* Board has to be wired properly to use this feature */
2013-06-18 17:17:58 +04:00
if ( twl4030_power_use_poweroff ( pdata , node ) & & ! pm_power_off ) {
2011-11-13 13:49:50 +04:00
/* Default for SEQ_OFFSYNC is set, lets ensure this */
2012-11-13 12:28:52 +04:00
err = twl_i2c_read_u8 ( TWL_MODULE_PM_MASTER , & val ,
2011-11-13 13:49:50 +04:00
TWL4030_PM_MASTER_CFG_P123_TRANSITION ) ;
if ( err ) {
pr_warning ( " TWL4030 Unable to read registers \n " ) ;
} else if ( ! ( val & SEQ_OFFSYNC ) ) {
val | = SEQ_OFFSYNC ;
2012-11-13 12:28:52 +04:00
err = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , val ,
2011-11-13 13:49:50 +04:00
TWL4030_PM_MASTER_CFG_P123_TRANSITION ) ;
if ( err ) {
pr_err ( " TWL4030 Unable to setup SEQ_OFFSYNC \n " ) ;
goto relock ;
}
}
pm_power_off = twl4030_power_off ;
}
relock :
2013-06-18 17:18:00 +04:00
err2 = twl_i2c_write_u8 ( TWL_MODULE_PM_MASTER , 0 ,
2012-11-13 12:28:52 +04:00
TWL4030_PM_MASTER_PROTECT_KEY ) ;
2013-06-18 17:18:00 +04:00
if ( err2 ) {
2009-08-31 20:32:18 +04:00
pr_err ( " TWL4030 Unable to relock registers \n " ) ;
2013-06-18 17:18:00 +04:00
return err2 ;
}
2013-06-18 17:17:56 +04:00
return err ;
}
static int twl4030_power_remove ( struct platform_device * pdev )
{
return 0 ;
2009-08-31 20:32:18 +04:00
}
2013-06-18 17:17:56 +04:00
2013-06-18 17:17:58 +04:00
# ifdef CONFIG_OF
static const struct of_device_id twl4030_power_of_match [ ] = {
{ . compatible = " ti,twl4030-power " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , twl4030_power_of_match ) ;
# endif
2013-06-18 17:17:56 +04:00
static struct platform_driver twl4030_power_driver = {
. driver = {
. name = " twl4030_power " ,
. owner = THIS_MODULE ,
2013-06-18 17:17:58 +04:00
. of_match_table = of_match_ptr ( twl4030_power_of_match ) ,
2013-06-18 17:17:56 +04:00
} ,
. probe = twl4030_power_probe ,
. remove = twl4030_power_remove ,
} ;
module_platform_driver ( twl4030_power_driver ) ;
MODULE_AUTHOR ( " Nokia Corporation " ) ;
MODULE_AUTHOR ( " Texas Instruments, Inc. " ) ;
MODULE_DESCRIPTION ( " Power management for TWL4030 " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:twl4030_power " ) ;