2019-07-08 09:41:06 +01:00
// SPDX-License-Identifier: GPL-2.0
/*
* System Control and Management Interface ( SCMI ) Reset Protocol
*
2022-03-30 16:05:38 +01:00
* Copyright ( C ) 2019 - 2022 ARM Ltd .
2019-07-08 09:41:06 +01:00
*/
2020-07-01 16:53:47 +01:00
# define pr_fmt(fmt) "SCMI Notifications RESET - " fmt
2021-03-16 12:49:02 +00:00
# include <linux/module.h>
2020-07-01 16:53:47 +01:00
# include <linux/scmi_protocol.h>
2022-03-30 16:05:38 +01:00
# include "protocols.h"
2020-07-01 16:53:47 +01:00
# include "notify.h"
2019-07-08 09:41:06 +01:00
2023-12-01 13:58:58 +00:00
/* Updated only after ALL the mandatory features for that version are merged */
# define SCMI_PROTOCOL_SUPPORTED_VERSION 0x30000
2019-07-08 09:41:06 +01:00
enum scmi_reset_protocol_cmd {
RESET_DOMAIN_ATTRIBUTES = 0x3 ,
RESET = 0x4 ,
RESET_NOTIFY = 0x5 ,
2022-03-30 16:05:40 +01:00
RESET_DOMAIN_NAME_GET = 0x6 ,
2019-07-08 09:41:06 +01:00
} ;
# define NUM_RESET_DOMAIN_MASK 0xffff
# define RESET_NOTIFY_ENABLE BIT(0)
struct scmi_msg_resp_reset_domain_attributes {
__le32 attributes ;
# define SUPPORTS_ASYNC_RESET(x) ((x) & BIT(31))
# define SUPPORTS_NOTIFY_RESET(x) ((x) & BIT(30))
2022-03-30 16:05:40 +01:00
# define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(29))
2019-07-08 09:41:06 +01:00
__le32 latency ;
2022-03-30 16:05:40 +01:00
u8 name [ SCMI_SHORT_NAME_MAX_SIZE ] ;
2019-07-08 09:41:06 +01:00
} ;
struct scmi_msg_reset_domain_reset {
__le32 domain_id ;
__le32 flags ;
# define AUTONOMOUS_RESET BIT(0)
# define EXPLICIT_RESET_ASSERT BIT(1)
# define ASYNCHRONOUS_RESET BIT(2)
__le32 reset_state ;
2020-10-08 16:37:22 +02:00
# define ARCH_COLD_RESET 0
2019-07-08 09:41:06 +01:00
} ;
2020-07-01 16:53:47 +01:00
struct scmi_msg_reset_notify {
__le32 id ;
__le32 event_control ;
# define RESET_TP_NOTIFY_ALL BIT(0)
} ;
struct scmi_reset_issued_notify_payld {
__le32 agent_id ;
__le32 domain_id ;
__le32 reset_state ;
} ;
2019-07-08 09:41:06 +01:00
struct reset_dom_info {
bool async_reset ;
bool reset_notify ;
u32 latency_us ;
char name [ SCMI_MAX_STR_SIZE ] ;
} ;
struct scmi_reset_info {
2019-11-22 14:48:40 +00:00
u32 version ;
2019-07-08 09:41:06 +01:00
int num_domains ;
struct reset_dom_info * dom_info ;
} ;
2021-03-16 12:48:45 +00:00
static int scmi_reset_attributes_get ( const struct scmi_protocol_handle * ph ,
2019-07-08 09:41:06 +01:00
struct scmi_reset_info * pi )
{
int ret ;
struct scmi_xfer * t ;
u32 attr ;
2021-03-16 12:48:45 +00:00
ret = ph - > xops - > xfer_get_init ( ph , PROTOCOL_ATTRIBUTES ,
0 , sizeof ( attr ) , & t ) ;
2019-07-08 09:41:06 +01:00
if ( ret )
return ret ;
2021-03-16 12:48:45 +00:00
ret = ph - > xops - > do_xfer ( ph , t ) ;
2019-07-08 09:41:06 +01:00
if ( ! ret ) {
attr = get_unaligned_le32 ( t - > rx . buf ) ;
pi - > num_domains = attr & NUM_RESET_DOMAIN_MASK ;
}
2021-03-16 12:48:45 +00:00
ph - > xops - > xfer_put ( ph , t ) ;
2019-07-08 09:41:06 +01:00
return ret ;
}
static int
2021-03-16 12:48:45 +00:00
scmi_reset_domain_attributes_get ( const struct scmi_protocol_handle * ph ,
2022-03-30 16:05:40 +01:00
u32 domain , struct reset_dom_info * dom_info ,
u32 version )
2019-07-08 09:41:06 +01:00
{
int ret ;
2022-03-30 16:05:40 +01:00
u32 attributes ;
2019-07-08 09:41:06 +01:00
struct scmi_xfer * t ;
struct scmi_msg_resp_reset_domain_attributes * attr ;
2021-03-16 12:48:45 +00:00
ret = ph - > xops - > xfer_get_init ( ph , RESET_DOMAIN_ATTRIBUTES ,
sizeof ( domain ) , sizeof ( * attr ) , & t ) ;
2019-07-08 09:41:06 +01:00
if ( ret )
return ret ;
put_unaligned_le32 ( domain , t - > tx . buf ) ;
attr = t - > rx . buf ;
2021-03-16 12:48:45 +00:00
ret = ph - > xops - > do_xfer ( ph , t ) ;
2019-07-08 09:41:06 +01:00
if ( ! ret ) {
2022-03-30 16:05:40 +01:00
attributes = le32_to_cpu ( attr - > attributes ) ;
2019-07-08 09:41:06 +01:00
dom_info - > async_reset = SUPPORTS_ASYNC_RESET ( attributes ) ;
dom_info - > reset_notify = SUPPORTS_NOTIFY_RESET ( attributes ) ;
dom_info - > latency_us = le32_to_cpu ( attr - > latency ) ;
if ( dom_info - > latency_us = = U32_MAX )
dom_info - > latency_us = 0 ;
2022-06-08 10:55:28 +01:00
strscpy ( dom_info - > name , attr - > name , SCMI_SHORT_NAME_MAX_SIZE ) ;
2019-07-08 09:41:06 +01:00
}
2021-03-16 12:48:45 +00:00
ph - > xops - > xfer_put ( ph , t ) ;
2022-03-30 16:05:40 +01:00
/*
* If supported overwrite short name with the extended one ;
* on error just carry on and use already provided short name .
*/
if ( ! ret & & PROTOCOL_REV_MAJOR ( version ) > = 0x3 & &
SUPPORTS_EXTENDED_NAMES ( attributes ) )
ph - > hops - > extended_name_get ( ph , RESET_DOMAIN_NAME_GET , domain ,
2023-11-14 14:54:49 +00:00
NULL , dom_info - > name ,
SCMI_MAX_STR_SIZE ) ;
2022-03-30 16:05:40 +01:00
2019-07-08 09:41:06 +01:00
return ret ;
}
2021-03-16 12:48:45 +00:00
static int scmi_reset_num_domains_get ( const struct scmi_protocol_handle * ph )
2019-07-08 09:41:06 +01:00
{
2021-03-16 12:48:45 +00:00
struct scmi_reset_info * pi = ph - > get_priv ( ph ) ;
2019-07-08 09:41:06 +01:00
return pi - > num_domains ;
}
2022-03-30 16:05:35 +01:00
static const char *
scmi_reset_name_get ( const struct scmi_protocol_handle * ph , u32 domain )
2021-03-16 12:48:45 +00:00
{
struct scmi_reset_info * pi = ph - > get_priv ( ph ) ;
2019-07-08 09:41:06 +01:00
struct reset_dom_info * dom = pi - > dom_info + domain ;
return dom - > name ;
}
2021-03-16 12:48:45 +00:00
static int scmi_reset_latency_get ( const struct scmi_protocol_handle * ph ,
u32 domain )
{
struct scmi_reset_info * pi = ph - > get_priv ( ph ) ;
2019-07-08 09:41:06 +01:00
struct reset_dom_info * dom = pi - > dom_info + domain ;
return dom - > latency_us ;
}
2021-03-16 12:48:45 +00:00
static int scmi_domain_reset ( const struct scmi_protocol_handle * ph , u32 domain ,
2019-07-08 09:41:06 +01:00
u32 flags , u32 state )
{
int ret ;
struct scmi_xfer * t ;
struct scmi_msg_reset_domain_reset * dom ;
2021-03-16 12:48:45 +00:00
struct scmi_reset_info * pi = ph - > get_priv ( ph ) ;
2022-08-17 18:27:29 +01:00
struct reset_dom_info * rdom ;
2019-07-08 09:41:06 +01:00
2022-08-17 18:27:29 +01:00
if ( domain > = pi - > num_domains )
return - EINVAL ;
rdom = pi - > dom_info + domain ;
2022-08-17 18:27:30 +01:00
if ( rdom - > async_reset & & flags & AUTONOMOUS_RESET )
2019-07-08 09:41:06 +01:00
flags | = ASYNCHRONOUS_RESET ;
2021-03-16 12:48:45 +00:00
ret = ph - > xops - > xfer_get_init ( ph , RESET , sizeof ( * dom ) , 0 , & t ) ;
2019-07-08 09:41:06 +01:00
if ( ret )
return ret ;
dom = t - > tx . buf ;
dom - > domain_id = cpu_to_le32 ( domain ) ;
dom - > flags = cpu_to_le32 ( flags ) ;
2019-09-09 16:21:30 +01:00
dom - > reset_state = cpu_to_le32 ( state ) ;
2019-07-08 09:41:06 +01:00
2022-08-17 18:27:30 +01:00
if ( flags & ASYNCHRONOUS_RESET )
2021-03-16 12:48:45 +00:00
ret = ph - > xops - > do_xfer_with_response ( ph , t ) ;
2019-07-08 09:41:06 +01:00
else
2021-03-16 12:48:45 +00:00
ret = ph - > xops - > do_xfer ( ph , t ) ;
2019-07-08 09:41:06 +01:00
2021-03-16 12:48:45 +00:00
ph - > xops - > xfer_put ( ph , t ) ;
2019-07-08 09:41:06 +01:00
return ret ;
}
2021-03-16 12:48:45 +00:00
static int scmi_reset_domain_reset ( const struct scmi_protocol_handle * ph ,
u32 domain )
2019-07-08 09:41:06 +01:00
{
2021-03-16 12:48:45 +00:00
return scmi_domain_reset ( ph , domain , AUTONOMOUS_RESET ,
2019-07-08 09:41:06 +01:00
ARCH_COLD_RESET ) ;
}
static int
2021-03-16 12:48:45 +00:00
scmi_reset_domain_assert ( const struct scmi_protocol_handle * ph , u32 domain )
2019-07-08 09:41:06 +01:00
{
2021-03-16 12:48:45 +00:00
return scmi_domain_reset ( ph , domain , EXPLICIT_RESET_ASSERT ,
2019-07-08 09:41:06 +01:00
ARCH_COLD_RESET ) ;
}
2021-03-16 12:48:45 +00:00
static int
scmi_reset_domain_deassert ( const struct scmi_protocol_handle * ph , u32 domain )
2019-07-08 09:41:06 +01:00
{
2021-03-16 12:48:45 +00:00
return scmi_domain_reset ( ph , domain , 0 , ARCH_COLD_RESET ) ;
}
static const struct scmi_reset_proto_ops reset_proto_ops = {
2019-07-08 09:41:06 +01:00
. num_domains_get = scmi_reset_num_domains_get ,
. name_get = scmi_reset_name_get ,
. latency_get = scmi_reset_latency_get ,
. reset = scmi_reset_domain_reset ,
. assert = scmi_reset_domain_assert ,
. deassert = scmi_reset_domain_deassert ,
} ;
2021-03-16 12:48:45 +00:00
static int scmi_reset_notify ( const struct scmi_protocol_handle * ph ,
u32 domain_id , bool enable )
2020-07-01 16:53:47 +01:00
{
int ret ;
u32 evt_cntl = enable ? RESET_TP_NOTIFY_ALL : 0 ;
struct scmi_xfer * t ;
struct scmi_msg_reset_notify * cfg ;
2021-03-16 12:48:45 +00:00
ret = ph - > xops - > xfer_get_init ( ph , RESET_NOTIFY , sizeof ( * cfg ) , 0 , & t ) ;
2020-07-01 16:53:47 +01:00
if ( ret )
return ret ;
cfg = t - > tx . buf ;
cfg - > id = cpu_to_le32 ( domain_id ) ;
cfg - > event_control = cpu_to_le32 ( evt_cntl ) ;
2021-03-16 12:48:45 +00:00
ret = ph - > xops - > do_xfer ( ph , t ) ;
2020-07-01 16:53:47 +01:00
2021-03-16 12:48:45 +00:00
ph - > xops - > xfer_put ( ph , t ) ;
2020-07-01 16:53:47 +01:00
return ret ;
}
2021-03-16 12:48:59 +00:00
static int scmi_reset_set_notify_enabled ( const struct scmi_protocol_handle * ph ,
2020-07-01 16:53:47 +01:00
u8 evt_id , u32 src_id , bool enable )
{
int ret ;
2021-03-16 12:48:45 +00:00
ret = scmi_reset_notify ( ph , src_id , enable ) ;
2020-07-01 16:53:47 +01:00
if ( ret )
pr_debug ( " FAIL_ENABLED - evt[%X] dom[%d] - ret:%d \n " ,
evt_id , src_id , ret ) ;
return ret ;
}
2021-03-16 12:48:59 +00:00
static void *
scmi_reset_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-07-01 16:53:47 +01:00
{
const struct scmi_reset_issued_notify_payld * p = payld ;
struct scmi_reset_issued_report * r = report ;
if ( evt_id ! = SCMI_EVENT_RESET_ISSUED | | sizeof ( * p ) ! = payld_sz )
return NULL ;
r - > timestamp = timestamp ;
r - > agent_id = le32_to_cpu ( p - > agent_id ) ;
r - > domain_id = le32_to_cpu ( p - > domain_id ) ;
r - > reset_state = le32_to_cpu ( p - > reset_state ) ;
* src_id = r - > domain_id ;
return r ;
}
2021-03-16 12:48:59 +00:00
static int scmi_reset_get_num_sources ( const struct scmi_protocol_handle * ph )
2021-03-16 12:48:31 +00:00
{
2021-03-16 12:48:59 +00:00
struct scmi_reset_info * pinfo = ph - > get_priv ( ph ) ;
2021-03-16 12:48:31 +00:00
if ( ! pinfo )
return - EINVAL ;
return pinfo - > num_domains ;
}
2020-07-01 16:53:47 +01:00
static const struct scmi_event reset_events [ ] = {
{
. id = SCMI_EVENT_RESET_ISSUED ,
. max_payld_sz = sizeof ( struct scmi_reset_issued_notify_payld ) ,
. max_report_sz = sizeof ( struct scmi_reset_issued_report ) ,
} ,
} ;
static const struct scmi_event_ops reset_event_ops = {
2021-03-16 12:48:31 +00:00
. get_num_sources = scmi_reset_get_num_sources ,
2020-07-01 16:53:47 +01:00
. set_notify_enabled = scmi_reset_set_notify_enabled ,
. fill_custom_report = scmi_reset_fill_custom_report ,
} ;
2021-03-16 12:48:31 +00:00
static const struct scmi_protocol_events reset_protocol_events = {
. queue_sz = SCMI_PROTO_QUEUE_SZ ,
. ops = & reset_event_ops ,
. evts = reset_events ,
. num_events = ARRAY_SIZE ( reset_events ) ,
} ;
2021-03-16 12:48:45 +00:00
static int scmi_reset_protocol_init ( const struct scmi_protocol_handle * ph )
2019-07-08 09:41:06 +01:00
{
2022-03-30 16:05:31 +01:00
int domain , ret ;
2019-07-08 09:41:06 +01:00
u32 version ;
struct scmi_reset_info * pinfo ;
2022-03-30 16:05:31 +01:00
ret = ph - > xops - > version_get ( ph , & version ) ;
if ( ret )
return ret ;
2019-07-08 09:41:06 +01:00
2021-03-16 12:48:45 +00:00
dev_dbg ( ph - > dev , " Reset Version %d.%d \n " ,
2019-07-08 09:41:06 +01:00
PROTOCOL_REV_MAJOR ( version ) , PROTOCOL_REV_MINOR ( version ) ) ;
2021-03-16 12:48:45 +00:00
pinfo = devm_kzalloc ( ph - > dev , sizeof ( * pinfo ) , GFP_KERNEL ) ;
2019-07-08 09:41:06 +01:00
if ( ! pinfo )
return - ENOMEM ;
2022-03-30 16:05:31 +01:00
ret = scmi_reset_attributes_get ( ph , pinfo ) ;
if ( ret )
return ret ;
2019-07-08 09:41:06 +01:00
2021-03-16 12:48:45 +00:00
pinfo - > dom_info = devm_kcalloc ( ph - > dev , pinfo - > num_domains ,
2019-07-08 09:41:06 +01:00
sizeof ( * pinfo - > dom_info ) , GFP_KERNEL ) ;
if ( ! pinfo - > dom_info )
return - ENOMEM ;
for ( domain = 0 ; domain < pinfo - > num_domains ; domain + + ) {
struct reset_dom_info * dom = pinfo - > dom_info + domain ;
2022-03-30 16:05:40 +01:00
scmi_reset_domain_attributes_get ( ph , domain , dom , version ) ;
2019-07-08 09:41:06 +01:00
}
2019-11-22 14:48:40 +00:00
pinfo - > version = version ;
2023-12-01 13:58:58 +00:00
return ph - > set_priv ( ph , pinfo , version ) ;
2019-07-08 09:41:06 +01:00
}
2021-03-16 12:48:26 +00:00
static const struct scmi_protocol scmi_reset = {
. id = SCMI_PROTOCOL_RESET ,
2021-03-16 12:49:02 +00:00
. owner = THIS_MODULE ,
2021-03-16 12:48:45 +00:00
. instance_init = & scmi_reset_protocol_init ,
. ops = & reset_proto_ops ,
2021-03-16 12:48:31 +00:00
. events = & reset_protocol_events ,
2023-12-01 13:58:58 +00:00
. supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION ,
2021-03-16 12:48:26 +00:00
} ;
DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER ( reset , scmi_reset )