2020-09-07 18:46:55 +01:00
// SPDX-License-Identifier: GPL-2.0
/*
* System Control and Management Interface ( SCMI ) System Power Protocol
*
2022-03-30 16:05:38 +01:00
* Copyright ( C ) 2020 - 2022 ARM Ltd .
2020-09-07 18:46:55 +01:00
*/
# define pr_fmt(fmt) "SCMI Notifications SYSTEM - " fmt
2021-03-16 12:49:02 +00:00
# include <linux/module.h>
2020-09-07 18:46:55 +01:00
# include <linux/scmi_protocol.h>
2022-03-30 16:05:38 +01:00
# include "protocols.h"
2020-09-07 18:46:55 +01:00
# include "notify.h"
# define SCMI_SYSTEM_NUM_SOURCES 1
enum scmi_system_protocol_cmd {
SYSTEM_POWER_STATE_NOTIFY = 0x5 ,
} ;
struct scmi_system_power_state_notify {
__le32 notify_enable ;
} ;
struct scmi_system_power_state_notifier_payld {
__le32 agent_id ;
__le32 flags ;
__le32 system_state ;
2022-07-04 11:19:31 +01:00
__le32 timeout ;
2020-09-07 18:46:55 +01:00
} ;
struct scmi_system_info {
u32 version ;
2022-07-04 11:19:31 +01:00
bool graceful_timeout_supported ;
2020-09-07 18:46:55 +01:00
} ;
2021-03-16 12:48:52 +00:00
static int scmi_system_request_notify ( const struct scmi_protocol_handle * ph ,
2020-09-07 18:46:55 +01:00
bool enable )
{
int ret ;
struct scmi_xfer * t ;
struct scmi_system_power_state_notify * notify ;
2021-03-16 12:48:52 +00:00
ret = ph - > xops - > xfer_get_init ( ph , SYSTEM_POWER_STATE_NOTIFY ,
sizeof ( * notify ) , 0 , & t ) ;
2020-09-07 18:46:55 +01:00
if ( ret )
return ret ;
notify = t - > tx . buf ;
notify - > notify_enable = enable ? cpu_to_le32 ( BIT ( 0 ) ) : 0 ;
2021-03-16 12:48:52 +00:00
ret = ph - > xops - > do_xfer ( ph , t ) ;
2020-09-07 18:46:55 +01:00
2021-03-16 12:48:52 +00:00
ph - > xops - > xfer_put ( ph , t ) ;
2020-09-07 18:46:55 +01:00
return ret ;
}
2021-03-16 12:48:59 +00:00
static int scmi_system_set_notify_enabled ( const struct scmi_protocol_handle * ph ,
2020-09-07 18:46:55 +01:00
u8 evt_id , u32 src_id , bool enable )
{
int ret ;
2021-03-16 12:48:52 +00:00
ret = scmi_system_request_notify ( ph , enable ) ;
2020-09-07 18:46:55 +01:00
if ( ret )
pr_debug ( " FAIL_ENABLE - evt[%X] - ret:%d \n " , evt_id , ret ) ;
return ret ;
}
2021-03-16 12:48:59 +00:00
static void *
scmi_system_fill_custom_report ( const struct scmi_protocol_handle * ph ,
u8 evt_id , ktime_t timestamp ,
const void * payld , size_t payld_sz ,
void * report , u32 * src_id )
2020-09-07 18:46:55 +01:00
{
2022-07-04 11:19:31 +01:00
size_t expected_sz ;
2020-09-07 18:46:55 +01:00
const struct scmi_system_power_state_notifier_payld * p = payld ;
struct scmi_system_power_state_notifier_report * r = report ;
2022-07-04 11:19:31 +01:00
struct scmi_system_info * pinfo = ph - > get_priv ( ph ) ;
2020-09-07 18:46:55 +01:00
2022-07-04 11:19:31 +01:00
expected_sz = pinfo - > graceful_timeout_supported ?
sizeof ( * p ) : sizeof ( * p ) - sizeof ( __le32 ) ;
2020-09-07 18:46:55 +01:00
if ( evt_id ! = SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER | |
2022-07-04 11:19:31 +01:00
payld_sz ! = expected_sz )
2020-09-07 18:46:55 +01:00
return NULL ;
r - > timestamp = timestamp ;
r - > agent_id = le32_to_cpu ( p - > agent_id ) ;
r - > flags = le32_to_cpu ( p - > flags ) ;
r - > system_state = le32_to_cpu ( p - > system_state ) ;
2022-07-04 11:19:31 +01:00
if ( pinfo - > graceful_timeout_supported & &
r - > system_state = = SCMI_SYSTEM_SHUTDOWN & &
SCMI_SYSPOWER_IS_REQUEST_GRACEFUL ( r - > flags ) )
r - > timeout = le32_to_cpu ( p - > timeout ) ;
else
r - > timeout = 0x00 ;
2020-09-07 18:46:55 +01:00
* src_id = 0 ;
return r ;
}
static const struct scmi_event system_events [ ] = {
{
. id = SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER ,
. max_payld_sz =
sizeof ( struct scmi_system_power_state_notifier_payld ) ,
. max_report_sz =
sizeof ( struct scmi_system_power_state_notifier_report ) ,
} ,
} ;
static const struct scmi_event_ops system_event_ops = {
. set_notify_enabled = scmi_system_set_notify_enabled ,
. fill_custom_report = scmi_system_fill_custom_report ,
} ;
2021-03-16 12:48:31 +00:00
static const struct scmi_protocol_events system_protocol_events = {
. queue_sz = SCMI_PROTO_QUEUE_SZ ,
. ops = & system_event_ops ,
. evts = system_events ,
. num_events = ARRAY_SIZE ( system_events ) ,
. num_sources = SCMI_SYSTEM_NUM_SOURCES ,
} ;
2021-03-16 12:48:52 +00:00
static int scmi_system_protocol_init ( const struct scmi_protocol_handle * ph )
2020-09-07 18:46:55 +01:00
{
2022-03-30 16:05:31 +01:00
int ret ;
2020-09-07 18:46:55 +01:00
u32 version ;
struct scmi_system_info * pinfo ;
2022-03-30 16:05:31 +01:00
ret = ph - > xops - > version_get ( ph , & version ) ;
if ( ret )
return ret ;
2020-09-07 18:46:55 +01:00
2021-03-16 12:48:52 +00:00
dev_dbg ( ph - > dev , " System Power Version %d.%d \n " ,
2020-09-07 18:46:55 +01:00
PROTOCOL_REV_MAJOR ( version ) , PROTOCOL_REV_MINOR ( version ) ) ;
2021-03-16 12:48:52 +00:00
pinfo = devm_kzalloc ( ph - > dev , sizeof ( * pinfo ) , GFP_KERNEL ) ;
2020-09-07 18:46:55 +01:00
if ( ! pinfo )
return - ENOMEM ;
pinfo - > version = version ;
2022-07-04 11:19:31 +01:00
if ( PROTOCOL_REV_MAJOR ( pinfo - > version ) > = 0x2 )
pinfo - > graceful_timeout_supported = true ;
2021-03-16 12:48:52 +00:00
return ph - > set_priv ( ph , pinfo ) ;
2020-09-07 18:46:55 +01:00
}
2021-03-16 12:48:26 +00:00
static const struct scmi_protocol scmi_system = {
. id = SCMI_PROTOCOL_SYSTEM ,
2021-03-16 12:49:02 +00:00
. owner = THIS_MODULE ,
2021-03-16 12:48:52 +00:00
. instance_init = & scmi_system_protocol_init ,
2021-03-16 12:48:26 +00:00
. ops = NULL ,
2021-03-16 12:48:31 +00:00
. events = & system_protocol_events ,
2021-03-16 12:48:26 +00:00
} ;
DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER ( system , scmi_system )