2019-05-27 08:55:08 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2011-09-06 14:39:44 +08:00
/*
* Copyright 2011 Freescale Semiconductor , Inc .
* Copyright 2011 Linaro Ltd .
*/
# include <linux/init.h>
# include <linux/io.h>
2021-05-25 21:14:16 -03:00
# include <linux/iopoll.h>
2011-09-06 14:39:44 +08:00
# include <linux/of.h>
# include <linux/of_address.h>
2021-10-05 12:06:18 +02:00
# include <linux/platform_device.h>
2013-03-28 17:35:19 +01:00
# include <linux/reset-controller.h>
2011-08-09 12:24:07 +01:00
# include <linux/smp.h>
2012-01-20 12:01:12 +01:00
# include <asm/smp_plat.h>
2013-03-25 09:20:43 -03:00
# include "common.h"
2021-05-25 21:14:16 -03:00
# include "hardware.h"
2011-09-06 14:39:44 +08:00
# define SRC_SCR 0x000
2021-05-25 21:14:16 -03:00
# define SRC_GPR1_V1 0x020
# define SRC_GPR1_V2 0x074
# define SRC_GPR1(gpr_v2) ((gpr_v2) ? SRC_GPR1_V2 : SRC_GPR1_V1)
2011-12-09 00:51:26 +01:00
# define BP_SRC_SCR_WARM_RESET_ENABLE 0
2013-03-28 17:35:19 +01:00
# define BP_SRC_SCR_SW_GPU_RST 1
# define BP_SRC_SCR_SW_VPU_RST 2
# define BP_SRC_SCR_SW_IPU1_RST 3
# define BP_SRC_SCR_SW_OPEN_VG_RST 4
# define BP_SRC_SCR_SW_IPU2_RST 12
2011-09-06 14:39:44 +08:00
# define BP_SRC_SCR_CORE1_RST 14
# define BP_SRC_SCR_CORE1_ENABLE 22
2021-05-25 21:14:16 -03:00
/* below is for i.MX7D */
# define SRC_A7RCR1 0x008
# define BP_SRC_A7RCR1_A7_CORE1_ENABLE 1
# define GPC_CPU_PGC_SW_PUP_REQ 0xf0
# define GPC_CPU_PGC_SW_PDN_REQ 0xfc
# define GPC_PGC_C1 0x840
# define BM_CPU_PGC_SW_PDN_PUP_REQ_CORE1_A7 0x2
2011-09-06 14:39:44 +08:00
static void __iomem * src_base ;
2013-03-28 17:35:19 +01:00
static DEFINE_SPINLOCK ( scr_lock ) ;
2021-05-25 21:14:16 -03:00
static bool gpr_v2 ;
static void __iomem * gpc_base ;
2013-03-28 17:35:19 +01:00
static const int sw_reset_bits [ 5 ] = {
BP_SRC_SCR_SW_GPU_RST ,
BP_SRC_SCR_SW_VPU_RST ,
BP_SRC_SCR_SW_IPU1_RST ,
BP_SRC_SCR_SW_OPEN_VG_RST ,
BP_SRC_SCR_SW_IPU2_RST
} ;
static int imx_src_reset_module ( struct reset_controller_dev * rcdev ,
unsigned long sw_reset_idx )
{
unsigned long timeout ;
unsigned long flags ;
int bit ;
u32 val ;
if ( sw_reset_idx > = ARRAY_SIZE ( sw_reset_bits ) )
return - EINVAL ;
bit = 1 < < sw_reset_bits [ sw_reset_idx ] ;
spin_lock_irqsave ( & scr_lock , flags ) ;
val = readl_relaxed ( src_base + SRC_SCR ) ;
val | = bit ;
writel_relaxed ( val , src_base + SRC_SCR ) ;
spin_unlock_irqrestore ( & scr_lock , flags ) ;
timeout = jiffies + msecs_to_jiffies ( 1000 ) ;
while ( readl ( src_base + SRC_SCR ) & bit ) {
if ( time_after ( jiffies , timeout ) )
return - ETIME ;
cpu_relax ( ) ;
}
return 0 ;
}
2016-02-25 10:44:41 +01:00
static const struct reset_control_ops imx_src_ops = {
2013-03-28 17:35:19 +01:00
. reset = imx_src_reset_module ,
} ;
2021-05-25 21:14:16 -03:00
static void imx_gpcv2_set_m_core_pgc ( bool enable , u32 offset )
{
writel_relaxed ( enable , gpc_base + offset ) ;
}
/*
* The motivation for bringing up the second i . MX7D core inside the kernel
* is that legacy vendor bootloaders usually do not implement PSCI support .
* This is a significant blocker for systems in the field that are running old
* bootloader versions to upgrade to a modern mainline kernel version , as only
* one CPU of the i . MX7D would be brought up .
* Bring up the second i . MX7D core inside the kernel to make the migration
* path to mainline kernel easier for the existing iMX7D users .
*/
void imx_gpcv2_set_core1_pdn_pup_by_software ( bool pdn )
{
u32 reg = pdn ? GPC_CPU_PGC_SW_PDN_REQ : GPC_CPU_PGC_SW_PUP_REQ ;
u32 val , pup ;
int ret ;
imx_gpcv2_set_m_core_pgc ( true , GPC_PGC_C1 ) ;
val = readl_relaxed ( gpc_base + reg ) ;
val | = BM_CPU_PGC_SW_PDN_PUP_REQ_CORE1_A7 ;
writel_relaxed ( val , gpc_base + reg ) ;
ret = readl_relaxed_poll_timeout_atomic ( gpc_base + reg , pup ,
! ( pup & BM_CPU_PGC_SW_PDN_PUP_REQ_CORE1_A7 ) ,
5 , 1000000 ) ;
if ( ret < 0 ) {
pr_err ( " i.MX7D: CORE1_A7 power up timeout \n " ) ;
val & = ~ BM_CPU_PGC_SW_PDN_PUP_REQ_CORE1_A7 ;
writel_relaxed ( val , gpc_base + reg ) ;
}
imx_gpcv2_set_m_core_pgc ( false , GPC_PGC_C1 ) ;
}
2011-09-06 14:39:44 +08:00
void imx_enable_cpu ( int cpu , bool enable )
{
u32 mask , val ;
2011-08-09 12:24:07 +01:00
cpu = cpu_logical_map ( cpu ) ;
2013-03-28 17:35:19 +01:00
spin_lock ( & scr_lock ) ;
2021-05-25 21:14:16 -03:00
if ( gpr_v2 ) {
if ( enable )
imx_gpcv2_set_core1_pdn_pup_by_software ( false ) ;
mask = 1 < < ( BP_SRC_A7RCR1_A7_CORE1_ENABLE + cpu - 1 ) ;
val = readl_relaxed ( src_base + SRC_A7RCR1 ) ;
val = enable ? val | mask : val & ~ mask ;
writel_relaxed ( val , src_base + SRC_A7RCR1 ) ;
} else {
mask = 1 < < ( BP_SRC_SCR_CORE1_ENABLE + cpu - 1 ) ;
val = readl_relaxed ( src_base + SRC_SCR ) ;
val = enable ? val | mask : val & ~ mask ;
val | = 1 < < ( BP_SRC_SCR_CORE1_RST + cpu - 1 ) ;
writel_relaxed ( val , src_base + SRC_SCR ) ;
}
2013-03-28 17:35:19 +01:00
spin_unlock ( & scr_lock ) ;
2011-09-06 14:39:44 +08:00
}
void imx_set_cpu_jump ( int cpu , void * jump_addr )
{
2011-08-09 12:24:07 +01:00
cpu = cpu_logical_map ( cpu ) ;
2017-01-15 03:59:29 +01:00
writel_relaxed ( __pa_symbol ( jump_addr ) ,
2021-05-25 21:14:16 -03:00
src_base + SRC_GPR1 ( gpr_v2 ) + cpu * 8 ) ;
2011-09-06 14:39:44 +08:00
}
2013-03-26 16:46:07 +08:00
u32 imx_get_cpu_arg ( int cpu )
{
cpu = cpu_logical_map ( cpu ) ;
2021-05-25 21:14:16 -03:00
return readl_relaxed ( src_base + SRC_GPR1 ( gpr_v2 ) + cpu * 8 + 4 ) ;
2013-03-26 16:46:07 +08:00
}
void imx_set_cpu_arg ( int cpu , u32 arg )
{
cpu = cpu_logical_map ( cpu ) ;
2021-05-25 21:14:16 -03:00
writel_relaxed ( arg , src_base + SRC_GPR1 ( gpr_v2 ) + cpu * 8 + 4 ) ;
2013-03-26 16:46:07 +08:00
}
2011-09-06 14:39:44 +08:00
void __init imx_src_init ( void )
{
struct device_node * np ;
2011-12-09 00:51:26 +01:00
u32 val ;
2011-09-06 14:39:44 +08:00
2013-03-28 17:35:22 +01:00
np = of_find_compatible_node ( NULL , NULL , " fsl,imx51-src " ) ;
if ( ! np )
return ;
2011-09-06 14:39:44 +08:00
src_base = of_iomap ( np , 0 ) ;
WARN_ON ( ! src_base ) ;
2011-12-09 00:51:26 +01:00
/*
* force warm reset sources to generate cold reset
* for a more reliable restart
*/
2013-03-28 17:35:19 +01:00
spin_lock ( & scr_lock ) ;
2011-12-09 00:51:26 +01:00
val = readl_relaxed ( src_base + SRC_SCR ) ;
val & = ~ ( 1 < < BP_SRC_SCR_WARM_RESET_ENABLE ) ;
writel_relaxed ( val , src_base + SRC_SCR ) ;
2013-03-28 17:35:19 +01:00
spin_unlock ( & scr_lock ) ;
2011-09-06 14:39:44 +08:00
}
2021-05-25 21:14:16 -03:00
void __init imx7_src_init ( void )
{
struct device_node * np ;
gpr_v2 = true ;
np = of_find_compatible_node ( NULL , NULL , " fsl,imx7d-src " ) ;
if ( ! np )
return ;
src_base = of_iomap ( np , 0 ) ;
if ( ! src_base )
return ;
np = of_find_compatible_node ( NULL , NULL , " fsl,imx7d-gpc " ) ;
if ( ! np )
return ;
gpc_base = of_iomap ( np , 0 ) ;
if ( ! gpc_base )
return ;
}
2021-10-05 12:06:18 +02:00
static const struct of_device_id imx_src_dt_ids [ ] = {
{ . compatible = " fsl,imx51-src " } ,
{ /* sentinel */ }
} ;
static int imx_src_probe ( struct platform_device * pdev )
{
struct reset_controller_dev * rcdev ;
rcdev = devm_kzalloc ( & pdev - > dev , sizeof ( * rcdev ) , GFP_KERNEL ) ;
if ( ! rcdev )
return - ENOMEM ;
rcdev - > ops = & imx_src_ops ;
rcdev - > dev = & pdev - > dev ;
rcdev - > of_node = pdev - > dev . of_node ;
rcdev - > nr_resets = ARRAY_SIZE ( sw_reset_bits ) ;
return devm_reset_controller_register ( & pdev - > dev , rcdev ) ;
}
static struct platform_driver imx_src_driver = {
. driver = {
. name = " imx-src " ,
. of_match_table = imx_src_dt_ids ,
} ,
. probe = imx_src_probe ,
} ;
builtin_platform_driver ( imx_src_driver ) ;