2014-11-21 17:00:08 +01:00
/*
* Board - level suspend / resume support .
*
2015-07-08 17:02:33 +02:00
* Copyright ( C ) 2014 - 2015 Marvell
2014-11-21 17:00:08 +01:00
*
* Thomas Petazzoni < thomas . petazzoni @ free - electrons . com >
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*/
# include <linux/delay.h>
# include <linux/gpio.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_gpio.h>
# include <linux/slab.h>
# include "common.h"
2015-07-08 17:02:33 +02:00
# define ARMADA_PIC_NR_GPIOS 3
2014-11-21 17:00:08 +01:00
static void __iomem * gpio_ctrl ;
2015-07-08 17:02:33 +02:00
static int pic_gpios [ ARMADA_PIC_NR_GPIOS ] ;
static int pic_raw_gpios [ ARMADA_PIC_NR_GPIOS ] ;
2014-11-21 17:00:08 +01:00
2015-07-08 17:02:33 +02:00
static void mvebu_armada_pm_enter ( void __iomem * sdram_reg , u32 srcmd )
2014-11-21 17:00:08 +01:00
{
u32 reg , ackcmd ;
int i ;
/* Put 001 as value on the GPIOs */
reg = readl ( gpio_ctrl ) ;
2015-07-08 17:02:33 +02:00
for ( i = 0 ; i < ARMADA_PIC_NR_GPIOS ; i + + )
2014-11-21 17:00:08 +01:00
reg & = ~ BIT ( pic_raw_gpios [ i ] ) ;
reg | = BIT ( pic_raw_gpios [ 0 ] ) ;
writel ( reg , gpio_ctrl ) ;
/* Prepare writing 111 to the GPIOs */
ackcmd = readl ( gpio_ctrl ) ;
2015-07-08 17:02:33 +02:00
for ( i = 0 ; i < ARMADA_PIC_NR_GPIOS ; i + + )
2014-11-21 17:00:08 +01:00
ackcmd | = BIT ( pic_raw_gpios [ i ] ) ;
2015-06-16 14:12:57 +02:00
srcmd = cpu_to_le32 ( srcmd ) ;
ackcmd = cpu_to_le32 ( ackcmd ) ;
2014-11-21 17:00:08 +01:00
/*
* Wait a while , the PIC needs quite a bit of time between the
* two GPIO commands .
*/
mdelay ( 3000 ) ;
asm volatile (
/* Align to a cache line */
" .balign 32 \n \t "
/* Enter self refresh */
" str %[srcmd], [%[sdram_reg]] \n \t "
/*
* Wait 100 cycles for DDR to enter self refresh , by
* doing 50 times two instructions .
*/
" mov r1, #50 \n \t "
" 1: subs r1, r1, #1 \n \t "
" bne 1b \n \t "
/* Issue the command ACK */
" str %[ackcmd], [%[gpio_ctrl]] \n \t "
/* Trap the processor */
" b . \n \t "
: : [ srcmd ] " r " ( srcmd ) , [ sdram_reg ] " r " ( sdram_reg ) ,
[ ackcmd ] " r " ( ackcmd ) , [ gpio_ctrl ] " r " ( gpio_ctrl ) : " r1 " ) ;
}
2015-07-03 13:55:50 +02:00
static int __init mvebu_armada_pm_init ( void )
2014-11-21 17:00:08 +01:00
{
struct device_node * np ;
struct device_node * gpio_ctrl_np ;
int ret = 0 , i ;
if ( ! of_machine_is_compatible ( " marvell,axp-gp " ) )
return - ENODEV ;
np = of_find_node_by_name ( NULL , " pm_pic " ) ;
if ( ! np )
return - ENODEV ;
2015-07-08 17:02:33 +02:00
for ( i = 0 ; i < ARMADA_PIC_NR_GPIOS ; i + + ) {
2014-11-21 17:00:08 +01:00
char * name ;
struct of_phandle_args args ;
pic_gpios [ i ] = of_get_named_gpio ( np , " ctrl-gpios " , i ) ;
if ( pic_gpios [ i ] < 0 ) {
ret = - ENODEV ;
goto out ;
}
name = kasprintf ( GFP_KERNEL , " pic-pin%d " , i ) ;
if ( ! name ) {
ret = - ENOMEM ;
goto out ;
}
ret = gpio_request ( pic_gpios [ i ] , name ) ;
if ( ret < 0 ) {
kfree ( name ) ;
goto out ;
}
ret = gpio_direction_output ( pic_gpios [ i ] , 0 ) ;
if ( ret < 0 ) {
gpio_free ( pic_gpios [ i ] ) ;
kfree ( name ) ;
goto out ;
}
ret = of_parse_phandle_with_fixed_args ( np , " ctrl-gpios " , 2 ,
i , & args ) ;
if ( ret < 0 ) {
gpio_free ( pic_gpios [ i ] ) ;
kfree ( name ) ;
goto out ;
}
gpio_ctrl_np = args . np ;
pic_raw_gpios [ i ] = args . args [ 0 ] ;
}
gpio_ctrl = of_iomap ( gpio_ctrl_np , 0 ) ;
if ( ! gpio_ctrl )
return - ENOMEM ;
2015-07-03 13:55:51 +02:00
mvebu_pm_suspend_init ( mvebu_armada_pm_enter ) ;
2014-11-21 17:00:08 +01:00
out :
of_node_put ( np ) ;
return ret ;
}
2015-07-03 13:55:51 +02:00
/*
* Registering the mvebu_board_pm_enter callback must be done before
* the platform_suspend_ops will be registered . In the same time we
* also need to have the gpio devices registered . That ' s why we use a
* device_initcall_sync which is called after all the device_initcall
* ( used by the gpio device ) but before the late_initcall ( used to
* register the platform_suspend_ops )
*/
device_initcall_sync ( mvebu_armada_pm_init ) ;