2019-05-28 20:10:04 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2015-06-05 16:24:52 +03:00
/*
* arch / arm / mach - socfpga / pm . c
*
* Copyright ( C ) 2014 - 2015 Altera Corporation . All rights reserved .
*
* with code from pm - imx6 . c
* Copyright 2011 - 2014 Freescale Semiconductor , Inc .
* Copyright 2011 Linaro Ltd .
*/
# include <linux/bitops.h>
# include <linux/genalloc.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/of_platform.h>
# include <linux/suspend.h>
# include <asm/suspend.h>
# include <asm/fncpy.h>
# include "core.h"
/* Pointer to function copied to ocram */
static u32 ( * socfpga_sdram_self_refresh_in_ocram ) ( u32 sdr_base ) ;
static int socfpga_setup_ocram_self_refresh ( void )
{
struct platform_device * pdev ;
phys_addr_t ocram_pbase ;
struct device_node * np ;
struct gen_pool * ocram_pool ;
unsigned long ocram_base ;
void __iomem * suspend_ocram_base ;
int ret = 0 ;
np = of_find_compatible_node ( NULL , NULL , " mmio-sram " ) ;
if ( ! np ) {
pr_err ( " %s: Unable to find mmio-sram in dtb \n " , __func__ ) ;
return - ENODEV ;
}
pdev = of_find_device_by_node ( np ) ;
if ( ! pdev ) {
pr_warn ( " %s: failed to find ocram device! \n " , __func__ ) ;
ret = - ENODEV ;
goto put_node ;
}
2015-09-05 01:47:43 +03:00
ocram_pool = gen_pool_get ( & pdev - > dev , NULL ) ;
2015-06-05 16:24:52 +03:00
if ( ! ocram_pool ) {
pr_warn ( " %s: ocram pool unavailable! \n " , __func__ ) ;
ret = - ENODEV ;
2020-07-21 16:45:51 +03:00
goto put_device ;
2015-06-05 16:24:52 +03:00
}
ocram_base = gen_pool_alloc ( ocram_pool , socfpga_sdram_self_refresh_sz ) ;
if ( ! ocram_base ) {
pr_warn ( " %s: unable to alloc ocram! \n " , __func__ ) ;
ret = - ENOMEM ;
2020-07-21 16:45:51 +03:00
goto put_device ;
2015-06-05 16:24:52 +03:00
}
ocram_pbase = gen_pool_virt_to_phys ( ocram_pool , ocram_base ) ;
suspend_ocram_base = __arm_ioremap_exec ( ocram_pbase ,
socfpga_sdram_self_refresh_sz ,
false ) ;
if ( ! suspend_ocram_base ) {
pr_warn ( " %s: __arm_ioremap_exec failed! \n " , __func__ ) ;
ret = - ENOMEM ;
2020-07-21 16:45:51 +03:00
goto put_device ;
2015-06-05 16:24:52 +03:00
}
/* Copy the code that puts DDR in self refresh to ocram */
socfpga_sdram_self_refresh_in_ocram =
( void * ) fncpy ( suspend_ocram_base ,
& socfpga_sdram_self_refresh ,
socfpga_sdram_self_refresh_sz ) ;
WARN ( ! socfpga_sdram_self_refresh_in_ocram ,
" could not copy function to ocram " ) ;
if ( ! socfpga_sdram_self_refresh_in_ocram )
ret = - EFAULT ;
2020-07-21 16:45:51 +03:00
put_device :
put_device ( & pdev - > dev ) ;
2015-06-05 16:24:52 +03:00
put_node :
of_node_put ( np ) ;
return ret ;
}
static int socfpga_pm_suspend ( unsigned long arg )
{
u32 ret ;
if ( ! sdr_ctl_base_addr )
return - EFAULT ;
ret = socfpga_sdram_self_refresh_in_ocram ( ( u32 ) sdr_ctl_base_addr ) ;
pr_debug ( " %s self-refresh loops request=%d exit=%d \n " , __func__ ,
ret & 0xffff , ( ret > > 16 ) & 0xffff ) ;
return 0 ;
}
static int socfpga_pm_enter ( suspend_state_t state )
{
switch ( state ) {
case PM_SUSPEND_MEM :
outer_disable ( ) ;
cpu_suspend ( 0 , socfpga_pm_suspend ) ;
outer_resume ( ) ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static const struct platform_suspend_ops socfpga_pm_ops = {
. valid = suspend_valid_only_mem ,
. enter = socfpga_pm_enter ,
} ;
static int __init socfpga_pm_init ( void )
{
int ret ;
ret = socfpga_setup_ocram_self_refresh ( ) ;
if ( ret )
return ret ;
suspend_set_ops ( & socfpga_pm_ops ) ;
pr_info ( " SoCFPGA initialized for DDR self-refresh during suspend. \n " ) ;
return 0 ;
}
arch_initcall ( socfpga_pm_init ) ;