2017-04-01 01:51:01 +08:00
/*
* ARM Specific GTDT table Support
*
* Copyright ( C ) 2016 , Linaro Ltd .
* Author : Daniel Lezcano < daniel . lezcano @ linaro . org >
* Fu Wei < fu . wei @ linaro . org >
* Hanjun Guo < hanjun . guo @ linaro . org >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/acpi.h>
# include <linux/init.h>
2017-04-01 01:51:03 +08:00
# include <linux/irqdomain.h>
2017-04-01 01:51:01 +08:00
# include <linux/kernel.h>
2017-04-01 01:51:05 +08:00
# include <linux/platform_device.h>
2017-04-01 01:51:01 +08:00
# include <clocksource/arm_arch_timer.h>
# undef pr_fmt
# define pr_fmt(fmt) "ACPI GTDT: " fmt
/**
* struct acpi_gtdt_descriptor - Store the key info of GTDT for all functions
* @ gtdt : The pointer to the struct acpi_table_gtdt of GTDT table .
* @ gtdt_end : The pointer to the end of GTDT table .
* @ platform_timer : The pointer to the start of Platform Timer Structure
*
* The struct store the key info of GTDT table , it should be initialized by
* acpi_gtdt_init .
*/
struct acpi_gtdt_descriptor {
struct acpi_table_gtdt * gtdt ;
void * gtdt_end ;
void * platform_timer ;
} ;
static struct acpi_gtdt_descriptor acpi_gtdt_desc __initdata ;
2017-04-01 01:51:03 +08:00
static inline void * next_platform_timer ( void * platform_timer )
{
struct acpi_gtdt_header * gh = platform_timer ;
platform_timer + = gh - > length ;
if ( platform_timer < acpi_gtdt_desc . gtdt_end )
return platform_timer ;
return NULL ;
}
# define for_each_platform_timer(_g) \
for ( _g = acpi_gtdt_desc . platform_timer ; _g ; \
_g = next_platform_timer ( _g ) )
static inline bool is_timer_block ( void * platform_timer )
{
struct acpi_gtdt_header * gh = platform_timer ;
return gh - > type = = ACPI_GTDT_TYPE_TIMER_BLOCK ;
}
2017-04-01 01:51:05 +08:00
static inline bool is_non_secure_watchdog ( void * platform_timer )
{
struct acpi_gtdt_header * gh = platform_timer ;
struct acpi_gtdt_watchdog * wd = platform_timer ;
if ( gh - > type ! = ACPI_GTDT_TYPE_WATCHDOG )
return false ;
return ! ( wd - > timer_flags & ACPI_GTDT_WATCHDOG_SECURE ) ;
}
2017-04-01 01:51:01 +08:00
static int __init map_gt_gsi ( u32 interrupt , u32 flags )
{
int trigger , polarity ;
trigger = ( flags & ACPI_GTDT_INTERRUPT_MODE ) ? ACPI_EDGE_SENSITIVE
: ACPI_LEVEL_SENSITIVE ;
polarity = ( flags & ACPI_GTDT_INTERRUPT_POLARITY ) ? ACPI_ACTIVE_LOW
: ACPI_ACTIVE_HIGH ;
return acpi_register_gsi ( NULL , interrupt , trigger , polarity ) ;
}
/**
* acpi_gtdt_map_ppi ( ) - Map the PPIs of per - cpu arch_timer .
* @ type : the type of PPI .
*
* Note : Secure state is not managed by the kernel on ARM64 systems .
* So we only handle the non - secure timer PPIs ,
* ARCH_TIMER_PHYS_SECURE_PPI is treated as invalid type .
*
* Return : the mapped PPI value , 0 if error .
*/
int __init acpi_gtdt_map_ppi ( int type )
{
struct acpi_table_gtdt * gtdt = acpi_gtdt_desc . gtdt ;
switch ( type ) {
case ARCH_TIMER_PHYS_NONSECURE_PPI :
return map_gt_gsi ( gtdt - > non_secure_el1_interrupt ,
gtdt - > non_secure_el1_flags ) ;
case ARCH_TIMER_VIRT_PPI :
return map_gt_gsi ( gtdt - > virtual_timer_interrupt ,
gtdt - > virtual_timer_flags ) ;
case ARCH_TIMER_HYP_PPI :
return map_gt_gsi ( gtdt - > non_secure_el2_interrupt ,
gtdt - > non_secure_el2_flags ) ;
default :
pr_err ( " Failed to map timer interrupt: invalid type. \n " ) ;
}
return 0 ;
}
/**
* acpi_gtdt_c3stop ( ) - Got c3stop info from GTDT according to the type of PPI .
* @ type : the type of PPI .
*
* Return : true if the timer HW state is lost when a CPU enters an idle state ,
* false otherwise
*/
bool __init acpi_gtdt_c3stop ( int type )
{
struct acpi_table_gtdt * gtdt = acpi_gtdt_desc . gtdt ;
switch ( type ) {
case ARCH_TIMER_PHYS_NONSECURE_PPI :
return ! ( gtdt - > non_secure_el1_flags & ACPI_GTDT_ALWAYS_ON ) ;
case ARCH_TIMER_VIRT_PPI :
return ! ( gtdt - > virtual_timer_flags & ACPI_GTDT_ALWAYS_ON ) ;
case ARCH_TIMER_HYP_PPI :
return ! ( gtdt - > non_secure_el2_flags & ACPI_GTDT_ALWAYS_ON ) ;
default :
pr_err ( " Failed to get c3stop info: invalid type. \n " ) ;
}
return false ;
}
/**
* acpi_gtdt_init ( ) - Get the info of GTDT table to prepare for further init .
* @ table : The pointer to GTDT table .
* @ platform_timer_count : It points to a integer variable which is used
* for storing the number of platform timers .
* This pointer could be NULL , if the caller
* doesn ' t need this info .
*
* Return : 0 if success , - EINVAL if error .
*/
int __init acpi_gtdt_init ( struct acpi_table_header * table ,
int * platform_timer_count )
{
void * platform_timer ;
struct acpi_table_gtdt * gtdt ;
gtdt = container_of ( table , struct acpi_table_gtdt , header ) ;
acpi_gtdt_desc . gtdt = gtdt ;
acpi_gtdt_desc . gtdt_end = ( void * ) table + table - > length ;
acpi_gtdt_desc . platform_timer = NULL ;
if ( platform_timer_count )
* platform_timer_count = 0 ;
if ( table - > revision < 2 ) {
pr_warn ( " Revision:%d doesn't support Platform Timers. \n " ,
table - > revision ) ;
return 0 ;
}
if ( ! gtdt - > platform_timer_count ) {
pr_debug ( " No Platform Timer. \n " ) ;
return 0 ;
}
platform_timer = ( void * ) gtdt + gtdt - > platform_timer_offset ;
if ( platform_timer < ( void * ) table + sizeof ( struct acpi_table_gtdt ) ) {
pr_err ( FW_BUG " invalid timer data. \n " ) ;
return - EINVAL ;
}
acpi_gtdt_desc . platform_timer = platform_timer ;
if ( platform_timer_count )
* platform_timer_count = gtdt - > platform_timer_count ;
return 0 ;
}
2017-04-01 01:51:03 +08:00
static int __init gtdt_parse_timer_block ( struct acpi_gtdt_timer_block * block ,
struct arch_timer_mem * timer_mem )
{
int i ;
struct arch_timer_mem_frame * frame ;
struct acpi_gtdt_timer_entry * gtdt_frame ;
if ( ! block - > timer_count ) {
pr_err ( FW_BUG " GT block present, but frame count is zero. " ) ;
return - ENODEV ;
}
if ( block - > timer_count > ARCH_TIMER_MEM_MAX_FRAMES ) {
pr_err ( FW_BUG " GT block lists %d frames, ACPI spec only allows 8 \n " ,
block - > timer_count ) ;
return - EINVAL ;
}
timer_mem - > cntctlbase = ( phys_addr_t ) block - > block_address ;
/*
* The CNTCTLBase frame is 4 KB ( register offsets 0x000 - 0xFFC ) .
* See ARM DDI 04 87 A . k_iss10775 , page I1 - 5129 , Table I1 - 3
* " CNTCTLBase memory map " .
*/
timer_mem - > size = SZ_4K ;
gtdt_frame = ( void * ) block + block - > timer_offset ;
if ( gtdt_frame + block - > timer_count ! = ( void * ) block + block - > header . length )
return - EINVAL ;
/*
* Get the GT timer Frame data for every GT Block Timer
*/
for ( i = 0 ; i < block - > timer_count ; i + + , gtdt_frame + + ) {
if ( gtdt_frame - > common_flags & ACPI_GTDT_GT_IS_SECURE_TIMER )
continue ;
if ( gtdt_frame - > frame_number > = ARCH_TIMER_MEM_MAX_FRAMES | |
! gtdt_frame - > base_address | | ! gtdt_frame - > timer_interrupt )
goto error ;
frame = & timer_mem - > frame [ gtdt_frame - > frame_number ] ;
/* duplicate frame */
if ( frame - > valid )
goto error ;
frame - > phys_irq = map_gt_gsi ( gtdt_frame - > timer_interrupt ,
gtdt_frame - > timer_flags ) ;
if ( frame - > phys_irq < = 0 ) {
pr_warn ( " failed to map physical timer irq in frame %d. \n " ,
gtdt_frame - > frame_number ) ;
goto error ;
}
if ( gtdt_frame - > virtual_timer_interrupt ) {
frame - > virt_irq =
map_gt_gsi ( gtdt_frame - > virtual_timer_interrupt ,
gtdt_frame - > virtual_timer_flags ) ;
if ( frame - > virt_irq < = 0 ) {
pr_warn ( " failed to map virtual timer irq in frame %d. \n " ,
gtdt_frame - > frame_number ) ;
goto error ;
}
} else {
pr_debug ( " virtual timer in frame %d not implemented. \n " ,
gtdt_frame - > frame_number ) ;
}
frame - > cntbase = gtdt_frame - > base_address ;
/*
* The CNTBaseN frame is 4 KB ( register offsets 0x000 - 0xFFC ) .
* See ARM DDI 04 87 A . k_iss10775 , page I1 - 5130 , Table I1 - 4
* " CNTBaseN memory map " .
*/
frame - > size = SZ_4K ;
frame - > valid = true ;
}
return 0 ;
error :
do {
if ( gtdt_frame - > common_flags & ACPI_GTDT_GT_IS_SECURE_TIMER | |
gtdt_frame - > frame_number > = ARCH_TIMER_MEM_MAX_FRAMES )
continue ;
frame = & timer_mem - > frame [ gtdt_frame - > frame_number ] ;
if ( frame - > phys_irq > 0 )
acpi_unregister_gsi ( gtdt_frame - > timer_interrupt ) ;
frame - > phys_irq = 0 ;
if ( frame - > virt_irq > 0 )
acpi_unregister_gsi ( gtdt_frame - > virtual_timer_interrupt ) ;
frame - > virt_irq = 0 ;
} while ( i - - > = 0 & & gtdt_frame - - ) ;
return - EINVAL ;
}
/**
* acpi_arch_timer_mem_init ( ) - Get the info of all GT blocks in GTDT table .
* @ timer_mem : The pointer to the array of struct arch_timer_mem for returning
* the result of parsing . The element number of this array should
* be platform_timer_count ( the total number of platform timers ) .
* @ timer_count : It points to a integer variable which is used for storing the
* number of GT blocks we have parsed .
*
* Return : 0 if success , - EINVAL / - ENODEV if error .
*/
int __init acpi_arch_timer_mem_init ( struct arch_timer_mem * timer_mem ,
int * timer_count )
{
int ret ;
void * platform_timer ;
* timer_count = 0 ;
for_each_platform_timer ( platform_timer ) {
if ( is_timer_block ( platform_timer ) ) {
ret = gtdt_parse_timer_block ( platform_timer , timer_mem ) ;
if ( ret )
return ret ;
timer_mem + + ;
( * timer_count ) + + ;
}
}
if ( * timer_count )
pr_info ( " found %d memory-mapped timer block(s). \n " ,
* timer_count ) ;
return 0 ;
}
2017-04-01 01:51:05 +08:00
/*
* Initialize a SBSA generic Watchdog platform device info from GTDT
*/
static int __init gtdt_import_sbsa_gwdt ( struct acpi_gtdt_watchdog * wd ,
int index )
{
struct platform_device * pdev ;
int irq = map_gt_gsi ( wd - > timer_interrupt , wd - > timer_flags ) ;
/*
* According to SBSA specification the size of refresh and control
* frames of SBSA Generic Watchdog is SZ_4K ( Offset 0x000 – 0xFFF ) .
*/
struct resource res [ ] = {
DEFINE_RES_MEM ( wd - > control_frame_address , SZ_4K ) ,
DEFINE_RES_MEM ( wd - > refresh_frame_address , SZ_4K ) ,
DEFINE_RES_IRQ ( irq ) ,
} ;
int nr_res = ARRAY_SIZE ( res ) ;
pr_debug ( " found a Watchdog (0x%llx/0x%llx gsi:%u flags:0x%x). \n " ,
wd - > refresh_frame_address , wd - > control_frame_address ,
wd - > timer_interrupt , wd - > timer_flags ) ;
if ( ! ( wd - > refresh_frame_address & & wd - > control_frame_address ) ) {
pr_err ( FW_BUG " failed to get the Watchdog base address. \n " ) ;
acpi_unregister_gsi ( wd - > timer_interrupt ) ;
return - EINVAL ;
}
if ( irq < = 0 ) {
pr_warn ( " failed to map the Watchdog interrupt. \n " ) ;
nr_res - - ;
}
/*
* Add a platform device named " sbsa-gwdt " to match the platform driver .
* " sbsa-gwdt " : SBSA ( Server Base System Architecture ) Generic Watchdog
* The platform driver can get device info below by matching this name .
*/
pdev = platform_device_register_simple ( " sbsa-gwdt " , index , res , nr_res ) ;
if ( IS_ERR ( pdev ) ) {
acpi_unregister_gsi ( wd - > timer_interrupt ) ;
return PTR_ERR ( pdev ) ;
}
return 0 ;
}
static int __init gtdt_sbsa_gwdt_init ( void )
{
void * platform_timer ;
struct acpi_table_header * table ;
int ret , timer_count , gwdt_count = 0 ;
if ( acpi_disabled )
return 0 ;
if ( ACPI_FAILURE ( acpi_get_table ( ACPI_SIG_GTDT , 0 , & table ) ) )
return - EINVAL ;
/*
* Note : Even though the global variable acpi_gtdt_desc has been
* initialized by acpi_gtdt_init ( ) while initializing the arch timers ,
* when we call this function to get SBSA watchdogs info from GTDT , the
* pointers stashed in it are stale ( since they are early temporary
* mappings carried out before acpi_permanent_mmap is set ) and we need
* to re - initialize them with permanent mapped pointer values to let the
* GTDT parsing possible .
*/
ret = acpi_gtdt_init ( table , & timer_count ) ;
if ( ret | | ! timer_count )
return ret ;
for_each_platform_timer ( platform_timer ) {
if ( is_non_secure_watchdog ( platform_timer ) ) {
ret = gtdt_import_sbsa_gwdt ( platform_timer , gwdt_count ) ;
if ( ret )
break ;
gwdt_count + + ;
}
}
if ( gwdt_count )
pr_info ( " found %d SBSA generic Watchdog(s). \n " , gwdt_count ) ;
return ret ;
}
device_initcall ( gtdt_sbsa_gwdt_init ) ;