2013-03-27 12:37:53 +01:00
/*
* Xilinx SLCR driver
*
* Copyright ( c ) 2011 - 2013 Xilinx Inc .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*
* 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 . , 675 Mass Ave , Cambridge , MA
* 0213 9 , USA .
*/
# include <linux/io.h>
2015-03-19 09:24:38 -05:00
# include <linux/reboot.h>
2013-11-26 15:41:31 +01:00
# include <linux/mfd/syscon.h>
2013-03-27 12:37:53 +01:00
# include <linux/of_address.h>
2013-11-26 15:41:31 +01:00
# include <linux/regmap.h>
2013-03-27 12:37:53 +01:00
# include <linux/clk/zynq.h>
# include "common.h"
2013-07-17 10:10:14 -07:00
/* register offsets */
# define SLCR_UNLOCK_OFFSET 0x8 /* SCLR unlock register */
2013-03-20 11:42:15 +01:00
# define SLCR_PS_RST_CTRL_OFFSET 0x200 /* PS Software Reset Control */
2013-07-17 10:10:14 -07:00
# define SLCR_A9_CPU_RST_CTRL_OFFSET 0x244 /* CPU Software Reset Control */
# define SLCR_REBOOT_STATUS_OFFSET 0x258 /* PS Reboot Status */
2013-07-31 09:19:59 +02:00
# define SLCR_PSS_IDCODE 0x530 /* PS IDCODE */
2016-02-02 20:30:49 -06:00
# define SLCR_L2C_RAM 0xA1C /* L2C_RAM in AR#54190 */
2013-03-20 13:50:12 +01:00
2013-07-17 10:10:14 -07:00
# define SLCR_UNLOCK_MAGIC 0xDF0D
2013-03-20 13:50:12 +01:00
# define SLCR_A9_CPU_CLKSTOP 0x10
# define SLCR_A9_CPU_RST 0x1
2013-07-31 09:19:59 +02:00
# define SLCR_PSS_IDCODE_DEVICE_SHIFT 12
# define SLCR_PSS_IDCODE_DEVICE_MASK 0x1F
2013-03-20 13:50:12 +01:00
2013-06-29 09:20:17 +02:00
static void __iomem * zynq_slcr_base ;
2013-11-26 15:41:31 +01:00
static struct regmap * zynq_slcr_regmap ;
2013-03-27 12:37:53 +01:00
2014-01-06 14:52:02 +01:00
/**
* zynq_slcr_write - Write to a register in SLCR block
*
* @ val : Value to write to the register
* @ offset : Register offset in SLCR block
*
* Return : a negative value on error , 0 on success
*/
static int zynq_slcr_write ( u32 val , u32 offset )
{
return regmap_write ( zynq_slcr_regmap , offset , val ) ;
}
/**
* zynq_slcr_read - Read a register in SLCR block
*
* @ val : Pointer to value to be read from SLCR
* @ offset : Register offset in SLCR block
*
* Return : a negative value on error , 0 on success
*/
static int zynq_slcr_read ( u32 * val , u32 offset )
{
2014-12-11 11:31:30 +01:00
return regmap_read ( zynq_slcr_regmap , offset , val ) ;
2014-01-06 14:52:02 +01:00
}
2013-11-26 14:46:58 +01:00
/**
* zynq_slcr_unlock - Unlock SLCR registers
*
* Return : a negative value on error , 0 on success
*/
static inline int zynq_slcr_unlock ( void )
{
zynq_slcr_write ( SLCR_UNLOCK_MAGIC , SLCR_UNLOCK_OFFSET ) ;
return 0 ;
}
2013-07-31 09:19:59 +02:00
/**
* zynq_slcr_get_device_id - Read device code id
*
* Return : Device code id
*/
u32 zynq_slcr_get_device_id ( void )
{
u32 val ;
zynq_slcr_read ( & val , SLCR_PSS_IDCODE ) ;
val > > = SLCR_PSS_IDCODE_DEVICE_SHIFT ;
val & = SLCR_PSS_IDCODE_DEVICE_MASK ;
return val ;
}
2013-03-20 11:42:15 +01:00
/**
2015-03-19 09:24:38 -05:00
* zynq_slcr_system_restart - Restart the entire system .
*
* @ nb : Pointer to restart notifier block ( unused )
* @ action : Reboot mode ( unused )
* @ data : Restart handler private data ( unused )
*
* Return : 0 always
2013-03-20 11:42:15 +01:00
*/
2015-03-19 09:24:38 -05:00
static
int zynq_slcr_system_restart ( struct notifier_block * nb ,
unsigned long action , void * data )
2013-03-20 11:42:15 +01:00
{
u32 reboot ;
/*
* Clear 0x0F000000 bits of reboot status register to workaround
* the FSBL not loading the bitstream after soft - reboot
* This is a temporary solution until we know more .
*/
2014-01-06 14:52:02 +01:00
zynq_slcr_read ( & reboot , SLCR_REBOOT_STATUS_OFFSET ) ;
zynq_slcr_write ( reboot & 0xF0FFFFFF , SLCR_REBOOT_STATUS_OFFSET ) ;
zynq_slcr_write ( 1 , SLCR_PS_RST_CTRL_OFFSET ) ;
2015-03-19 09:24:38 -05:00
return 0 ;
2013-03-20 11:42:15 +01:00
}
2015-03-19 09:24:38 -05:00
static struct notifier_block zynq_slcr_restart_nb = {
. notifier_call = zynq_slcr_system_restart ,
. priority = 192 ,
} ;
2013-03-20 13:50:12 +01:00
/**
* zynq_slcr_cpu_start - Start cpu
* @ cpu : cpu number
*/
void zynq_slcr_cpu_start ( int cpu )
{
2014-01-06 14:52:02 +01:00
u32 reg ;
zynq_slcr_read ( & reg , SLCR_A9_CPU_RST_CTRL_OFFSET ) ;
2013-07-17 10:10:15 -07:00
reg & = ~ ( SLCR_A9_CPU_RST < < cpu ) ;
2014-01-06 14:52:02 +01:00
zynq_slcr_write ( reg , SLCR_A9_CPU_RST_CTRL_OFFSET ) ;
2013-07-17 10:10:15 -07:00
reg & = ~ ( SLCR_A9_CPU_CLKSTOP < < cpu ) ;
2014-01-06 14:52:02 +01:00
zynq_slcr_write ( reg , SLCR_A9_CPU_RST_CTRL_OFFSET ) ;
2014-09-02 14:19:12 -07:00
zynq_slcr_cpu_state_write ( cpu , false ) ;
2013-03-20 13:50:12 +01:00
}
/**
* zynq_slcr_cpu_stop - Stop cpu
* @ cpu : cpu number
*/
void zynq_slcr_cpu_stop ( int cpu )
{
2014-01-06 14:52:02 +01:00
u32 reg ;
zynq_slcr_read ( & reg , SLCR_A9_CPU_RST_CTRL_OFFSET ) ;
2013-07-17 10:10:15 -07:00
reg | = ( SLCR_A9_CPU_CLKSTOP | SLCR_A9_CPU_RST ) < < cpu ;
2014-01-06 14:52:02 +01:00
zynq_slcr_write ( reg , SLCR_A9_CPU_RST_CTRL_OFFSET ) ;
2013-03-20 13:50:12 +01:00
}
2013-03-27 12:37:53 +01:00
/**
2014-09-02 14:19:12 -07:00
* zynq_slcr_cpu_state - Read / write cpu state
* @ cpu : cpu number
2013-11-26 15:41:31 +01:00
*
2014-09-02 14:19:12 -07:00
* SLCR_REBOOT_STATUS save upper 2 bits ( 31 / 30 cpu states for cpu0 and cpu1 )
* 0 means cpu is running , 1 cpu is going to die .
*
* Return : true if cpu is running , false if cpu is going to die
*/
bool zynq_slcr_cpu_state_read ( int cpu )
{
u32 state ;
state = readl ( zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET ) ;
state & = 1 < < ( 31 - cpu ) ;
return ! state ;
}
/**
* zynq_slcr_cpu_state - Read / write cpu state
* @ cpu : cpu number
* @ die : cpu state - true if cpu is going to die
*
* SLCR_REBOOT_STATUS save upper 2 bits ( 31 / 30 cpu states for cpu0 and cpu1 )
* 0 means cpu is running , 1 cpu is going to die .
*/
void zynq_slcr_cpu_state_write ( int cpu , bool die )
{
u32 state , mask ;
state = readl ( zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET ) ;
mask = 1 < < ( 31 - cpu ) ;
if ( die )
state | = mask ;
else
state & = ~ mask ;
writel ( state , zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET ) ;
}
2013-11-26 15:41:31 +01:00
/**
* zynq_early_slcr_init - Early slcr init function
*
* Return : 0 on success , negative errno otherwise .
*
* Called very early during boot from platform code to unlock SLCR .
*/
int __init zynq_early_slcr_init ( void )
2013-03-27 12:37:53 +01:00
{
struct device_node * np ;
np = of_find_compatible_node ( NULL , NULL , " xlnx,zynq-slcr " ) ;
if ( ! np ) {
pr_err ( " %s: no slcr node found \n " , __func__ ) ;
BUG ( ) ;
}
zynq_slcr_base = of_iomap ( np , 0 ) ;
if ( ! zynq_slcr_base ) {
pr_err ( " %s: Unable to map I/O memory \n " , __func__ ) ;
BUG ( ) ;
}
2013-11-26 14:02:44 +01:00
np - > data = ( __force void * ) zynq_slcr_base ;
2014-12-11 11:31:30 +01:00
zynq_slcr_regmap = syscon_regmap_lookup_by_compatible ( " xlnx,zynq-slcr " ) ;
if ( IS_ERR ( zynq_slcr_regmap ) ) {
pr_err ( " %s: failed to find zynq-slcr \n " , __func__ ) ;
return - ENODEV ;
}
2013-03-27 12:37:53 +01:00
/* unlock the SLCR so that registers can be changed */
2013-11-26 14:46:58 +01:00
zynq_slcr_unlock ( ) ;
2013-03-27 12:37:53 +01:00
2016-02-02 20:30:49 -06:00
/* See AR#54190 design advisory */
regmap_update_bits ( zynq_slcr_regmap , SLCR_L2C_RAM , 0x70707 , 0x20202 ) ;
2015-03-19 09:24:38 -05:00
register_restart_handler ( & zynq_slcr_restart_nb ) ;
2013-03-27 12:37:53 +01:00
pr_info ( " %s mapped to %p \n " , np - > name , zynq_slcr_base ) ;
of_node_put ( np ) ;
return 0 ;
}