2019-01-29 10:38:19 +01:00
// SPDX-License-Identifier: GPL-2.0
// ChromeOS EC communication protocol helper functions
//
// Copyright (C) 2015 Google, Inc
2015-06-09 13:04:44 +02:00
# include <linux/delay.h>
# include <linux/device.h>
# include <linux/module.h>
2019-09-02 11:53:05 +02:00
# include <linux/platform_data/cros_ec_commands.h>
# include <linux/platform_data/cros_ec_proto.h>
2015-06-09 13:04:44 +02:00
# include <linux/slab.h>
2016-08-10 19:05:24 +02:00
# include <asm/unaligned.h>
2015-06-09 13:04:44 +02:00
2019-04-16 15:03:28 -06:00
# include "cros_ec_trace.h"
2015-06-09 13:04:44 +02:00
# define EC_COMMAND_RETRIES 50
2020-08-22 08:08:56 -07:00
static const int cros_ec_error_map [ ] = {
[ EC_RES_INVALID_COMMAND ] = - EOPNOTSUPP ,
[ EC_RES_ERROR ] = - EIO ,
[ EC_RES_INVALID_PARAM ] = - EINVAL ,
[ EC_RES_ACCESS_DENIED ] = - EACCES ,
[ EC_RES_INVALID_RESPONSE ] = - EPROTO ,
[ EC_RES_INVALID_VERSION ] = - ENOPROTOOPT ,
[ EC_RES_INVALID_CHECKSUM ] = - EBADMSG ,
[ EC_RES_IN_PROGRESS ] = - EINPROGRESS ,
[ EC_RES_UNAVAILABLE ] = - ENODATA ,
[ EC_RES_TIMEOUT ] = - ETIMEDOUT ,
[ EC_RES_OVERFLOW ] = - EOVERFLOW ,
[ EC_RES_INVALID_HEADER ] = - EBADR ,
[ EC_RES_REQUEST_TRUNCATED ] = - EBADR ,
[ EC_RES_RESPONSE_TOO_BIG ] = - EFBIG ,
[ EC_RES_BUS_ERROR ] = - EFAULT ,
[ EC_RES_BUSY ] = - EBUSY ,
[ EC_RES_INVALID_HEADER_VERSION ] = - EBADMSG ,
[ EC_RES_INVALID_HEADER_CRC ] = - EBADMSG ,
[ EC_RES_INVALID_DATA_CRC ] = - EBADMSG ,
[ EC_RES_DUP_UNAVAILABLE ] = - ENODATA ,
} ;
static int cros_ec_map_error ( uint32_t result )
{
int ret = 0 ;
if ( result ! = EC_RES_SUCCESS ) {
if ( result < ARRAY_SIZE ( cros_ec_error_map ) & & cros_ec_error_map [ result ] )
ret = cros_ec_error_map [ result ] ;
else
ret = - EPROTO ;
}
return ret ;
}
2022-05-18 17:18:12 +08:00
static int prepare_tx ( struct cros_ec_device * ec_dev ,
struct cros_ec_command * msg )
2015-06-09 13:04:45 +02:00
{
struct ec_host_request * request ;
u8 * out ;
int i ;
u8 csum = 0 ;
2022-05-13 12:41:39 +08:00
if ( msg - > outsize + sizeof ( * request ) > ec_dev - > dout_size )
return - EINVAL ;
2015-06-09 13:04:45 +02:00
out = ec_dev - > dout ;
request = ( struct ec_host_request * ) out ;
request - > struct_version = EC_HOST_REQUEST_VERSION ;
request - > checksum = 0 ;
request - > command = msg - > command ;
request - > command_version = msg - > version ;
request - > reserved = 0 ;
request - > data_len = msg - > outsize ;
for ( i = 0 ; i < sizeof ( * request ) ; i + + )
csum + = out [ i ] ;
/* Copy data and update checksum */
memcpy ( out + sizeof ( * request ) , msg - > data , msg - > outsize ) ;
for ( i = 0 ; i < msg - > outsize ; i + + )
csum + = msg - > data [ i ] ;
request - > checksum = - csum ;
return sizeof ( * request ) + msg - > outsize ;
}
2022-05-18 17:18:12 +08:00
static int prepare_tx_legacy ( struct cros_ec_device * ec_dev ,
struct cros_ec_command * msg )
{
u8 * out ;
u8 csum ;
int i ;
if ( msg - > outsize > EC_PROTO2_MAX_PARAM_SIZE )
return - EINVAL ;
out = ec_dev - > dout ;
out [ 0 ] = EC_CMD_VERSION0 + msg - > version ;
out [ 1 ] = msg - > command ;
out [ 2 ] = msg - > outsize ;
csum = out [ 0 ] + out [ 1 ] + out [ 2 ] ;
for ( i = 0 ; i < msg - > outsize ; i + + )
csum + = out [ EC_MSG_TX_HEADER_BYTES + i ] = msg - > data [ i ] ;
out [ EC_MSG_TX_HEADER_BYTES + msg - > outsize ] = csum ;
return EC_MSG_TX_PROTO_BYTES + msg - > outsize ;
}
2022-07-18 05:09:09 +00:00
static int cros_ec_xfer_command ( struct cros_ec_device * ec_dev , struct cros_ec_command * msg )
2015-06-09 13:04:45 +02:00
{
int ret ;
2017-09-08 13:50:11 -07:00
int ( * xfer_fxn ) ( struct cros_ec_device * ec , struct cros_ec_command * msg ) ;
2015-06-09 13:04:45 +02:00
if ( ec_dev - > proto_version > 2 )
2017-09-08 13:50:11 -07:00
xfer_fxn = ec_dev - > pkt_xfer ;
2015-06-09 13:04:45 +02:00
else
2017-09-08 13:50:11 -07:00
xfer_fxn = ec_dev - > cmd_xfer ;
2015-06-09 13:04:45 +02:00
2019-04-03 15:40:36 -07:00
if ( ! xfer_fxn ) {
/*
* This error can happen if a communication error happened and
* the EC is trying to use protocol v2 , on an underlying
* communication mechanism that does not support v2 .
*/
2022-07-18 05:09:09 +00:00
dev_err_once ( ec_dev - > dev , " missing EC transfer API, cannot send command \n " ) ;
2019-04-03 15:40:36 -07:00
return - EIO ;
}
2019-11-25 10:45:39 -07:00
trace_cros_ec_request_start ( msg ) ;
2017-09-08 13:50:11 -07:00
ret = ( * xfer_fxn ) ( ec_dev , msg ) ;
2019-11-25 10:45:39 -07:00
trace_cros_ec_request_done ( msg , ret ) ;
2022-07-18 05:09:09 +00:00
return ret ;
}
2022-07-18 05:09:10 +00:00
static int cros_ec_wait_until_complete ( struct cros_ec_device * ec_dev , uint32_t * result )
2022-07-18 05:09:09 +00:00
{
2022-07-18 05:09:10 +00:00
struct {
struct cros_ec_command msg ;
struct ec_response_get_comms_status status ;
} __packed buf ;
struct cros_ec_command * msg = & buf . msg ;
struct ec_response_get_comms_status * status = & buf . status ;
int ret = 0 , i ;
msg - > version = 0 ;
msg - > command = EC_CMD_GET_COMMS_STATUS ;
msg - > insize = sizeof ( * status ) ;
msg - > outsize = 0 ;
/* Query the EC's status until it's no longer busy or we encounter an error. */
for ( i = 0 ; i < EC_COMMAND_RETRIES ; + + i ) {
usleep_range ( 10000 , 11000 ) ;
ret = cros_ec_xfer_command ( ec_dev , msg ) ;
if ( ret = = - EAGAIN )
continue ;
if ( ret < 0 )
return ret ;
* result = msg - > result ;
if ( msg - > result ! = EC_RES_SUCCESS )
return ret ;
2022-07-18 05:09:14 +00:00
if ( ret = = 0 ) {
ret = - EPROTO ;
break ;
}
2022-07-18 05:09:10 +00:00
if ( ! ( status - > flags & EC_COMMS_STATUS_PROCESSING ) )
return ret ;
}
2015-06-09 13:04:45 +02:00
2022-07-18 05:09:12 +00:00
if ( i > = EC_COMMAND_RETRIES )
ret = - EAGAIN ;
2022-07-18 05:09:10 +00:00
return ret ;
}
2015-06-09 13:04:45 +02:00
2022-07-18 05:09:10 +00:00
static int cros_ec_send_command ( struct cros_ec_device * ec_dev , struct cros_ec_command * msg )
{
int ret = cros_ec_xfer_command ( ec_dev , msg ) ;
2015-06-09 13:04:45 +02:00
2022-07-18 05:09:10 +00:00
if ( msg - > result = = EC_RES_IN_PROGRESS )
ret = cros_ec_wait_until_complete ( ec_dev , & msg - > result ) ;
2015-06-09 13:04:45 +02:00
return ret ;
}
2019-11-19 13:45:45 +01:00
/**
* cros_ec_prepare_tx ( ) - Prepare an outgoing message in the output buffer .
* @ ec_dev : Device to register .
* @ msg : Message to write .
*
2022-05-18 17:18:12 +08:00
* This is used by all ChromeOS EC drivers to prepare the outgoing message
* according to different protocol versions .
2019-11-19 13:45:45 +01:00
*
2022-05-13 12:41:38 +08:00
* Return : number of prepared bytes on success or negative error code .
2019-11-19 13:45:45 +01:00
*/
2015-06-09 13:04:44 +02:00
int cros_ec_prepare_tx ( struct cros_ec_device * ec_dev ,
struct cros_ec_command * msg )
{
2015-06-09 13:04:45 +02:00
if ( ec_dev - > proto_version > 2 )
2022-05-18 17:18:12 +08:00
return prepare_tx ( ec_dev , msg ) ;
2015-06-09 13:04:44 +02:00
2022-05-18 17:18:12 +08:00
return prepare_tx_legacy ( ec_dev , msg ) ;
2015-06-09 13:04:44 +02:00
}
EXPORT_SYMBOL ( cros_ec_prepare_tx ) ;
2019-11-19 13:45:45 +01:00
/**
* cros_ec_check_result ( ) - Check ec_msg - > result .
* @ ec_dev : EC device .
* @ msg : Message to check .
*
* This is used by ChromeOS EC drivers to check the ec_msg - > result for
2022-05-18 17:18:13 +08:00
* EC_RES_IN_PROGRESS and to warn about them .
2019-11-19 13:45:45 +01:00
*
2022-05-18 17:18:13 +08:00
* The function should not check for furthermore error codes . Otherwise ,
* it would break the ABI .
*
* Return : - EAGAIN if ec_msg - > result = = EC_RES_IN_PROGRESS . Otherwise , 0.
2019-11-19 13:45:45 +01:00
*/
2015-06-09 13:04:44 +02:00
int cros_ec_check_result ( struct cros_ec_device * ec_dev ,
struct cros_ec_command * msg )
{
switch ( msg - > result ) {
case EC_RES_SUCCESS :
return 0 ;
case EC_RES_IN_PROGRESS :
dev_dbg ( ec_dev - > dev , " command 0x%02x in progress \n " ,
msg - > command ) ;
return - EAGAIN ;
default :
dev_dbg ( ec_dev - > dev , " command 0x%02x returned %d \n " ,
msg - > command , msg - > result ) ;
return 0 ;
}
}
EXPORT_SYMBOL ( cros_ec_check_result ) ;
2017-02-14 20:58:02 +01:00
/*
* cros_ec_get_host_event_wake_mask
*
* Get the mask of host events that cause wake from suspend .
*
* @ ec_dev : EC device to call
* @ msg : message structure to use
2022-06-09 08:49:55 +00:00
* @ mask : result when function returns 0.
2017-02-14 20:58:02 +01:00
*
* LOCKING :
* the caller has ec_dev - > lock mutex , or the caller knows there is
* no other command in progress .
*/
2022-06-09 08:49:43 +00:00
static int cros_ec_get_host_event_wake_mask ( struct cros_ec_device * ec_dev , uint32_t * mask )
2017-02-14 20:58:02 +01:00
{
2022-06-09 08:49:43 +00:00
struct cros_ec_command * msg ;
2017-02-14 20:58:02 +01:00
struct ec_response_host_event_mask * r ;
2022-06-09 08:49:42 +00:00
int ret , mapped ;
2017-02-14 20:58:02 +01:00
2022-06-09 08:49:43 +00:00
msg = kzalloc ( sizeof ( * msg ) + sizeof ( * r ) , GFP_KERNEL ) ;
if ( ! msg )
return - ENOMEM ;
2017-02-14 20:58:02 +01:00
msg - > command = EC_CMD_HOST_EVENT_GET_WAKE_MASK ;
msg - > insize = sizeof ( * r ) ;
2022-07-18 05:09:06 +00:00
ret = cros_ec_send_command ( ec_dev , msg ) ;
2022-06-09 08:49:57 +00:00
if ( ret < 0 )
goto exit ;
mapped = cros_ec_map_error ( msg - > result ) ;
if ( mapped ) {
ret = mapped ;
goto exit ;
platform/chrome: cros_ec_proto: check for missing EC_CMD_HOST_EVENT_GET_WAKE_MASK
As with cros_ec_cmd_xfer_status(), etc., it's not enough to simply check
for the return status of send_command() -- that only covers transport or
other similarly-fatal errors. One must also check the ->result field, to
see whether the command really succeeded. If not, we can't use the data
it returns.
The caller of cros_ec_get_host_event_wake_mask() ignores this, and so
for example, on EC's where the command is not implemented, we're using
junk (or in practice, all zeros) for our wake-mask. We should be using a
non-zero default (currently, it's supposed to be all-1's).
Fix this by checking the ->result field and returning -EPROTO for
errors.
I might label this as fixing commit 29d99b966d60 ("cros_ec: Don't signal
wake event for non-wake host events"), except that this fix alone
actually may make things worse, as it now allows for a lot more spurious
wakeups. The patch "platform/chrome: cros_ec_proto: ignore battery/AC
wakeups on old ECs" helps to mitigate this.
Signed-off-by: Brian Norris <briannorris@chromium.org>
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
2020-07-24 12:08:41 -07:00
}
2022-06-09 08:49:57 +00:00
if ( ret = = 0 ) {
ret = - EPROTO ;
goto exit ;
2017-02-14 20:58:02 +01:00
}
2022-06-09 08:49:57 +00:00
r = ( struct ec_response_host_event_mask * ) msg - > data ;
* mask = r - > mask ;
ret = 0 ;
2022-06-09 08:49:43 +00:00
exit :
kfree ( msg ) ;
2017-02-14 20:58:02 +01:00
return ret ;
}
2022-06-09 08:49:43 +00:00
static int cros_ec_get_proto_info ( struct cros_ec_device * ec_dev , int devidx )
2015-06-09 13:04:44 +02:00
{
2022-06-09 08:49:43 +00:00
struct cros_ec_command * msg ;
struct ec_response_get_protocol_info * info ;
int ret , mapped ;
ec_dev - > proto_version = 3 ;
if ( devidx > 0 )
ec_dev - > max_passthru = 0 ;
msg = kzalloc ( sizeof ( * msg ) + sizeof ( * info ) , GFP_KERNEL ) ;
if ( ! msg )
return - ENOMEM ;
2015-06-09 13:04:44 +02:00
2015-06-09 13:04:45 +02:00
msg - > command = EC_CMD_PASSTHRU_OFFSET ( devidx ) | EC_CMD_GET_PROTOCOL_INFO ;
2022-06-09 08:49:43 +00:00
msg - > insize = sizeof ( * info ) ;
2015-06-09 13:04:44 +02:00
2022-07-18 05:09:06 +00:00
ret = cros_ec_send_command ( ec_dev , msg ) ;
2021-05-18 16:07:58 +02:00
/*
* Send command once again when timeout occurred .
* Fingerprint MCU ( FPMCU ) is restarted during system boot which
* introduces small window in which FPMCU won ' t respond for any
* messages sent by kernel . There is no need to wait before next
* attempt because we waited at least EC_MSG_DEADLINE_MS .
*/
if ( ret = = - ETIMEDOUT )
2022-07-18 05:09:06 +00:00
ret = cros_ec_send_command ( ec_dev , msg ) ;
2015-06-09 13:04:45 +02:00
if ( ret < 0 ) {
dev_dbg ( ec_dev - > dev ,
" failed to check for EC[%d] protocol version: %d \n " ,
devidx , ret ) ;
2022-06-09 08:49:43 +00:00
goto exit ;
2015-06-09 13:04:45 +02:00
}
2022-06-09 08:49:43 +00:00
mapped = cros_ec_map_error ( msg - > result ) ;
if ( mapped ) {
ret = mapped ;
goto exit ;
}
2022-06-09 08:49:45 +00:00
if ( ret = = 0 ) {
ret = - EPROTO ;
goto exit ;
}
2015-06-09 13:04:45 +02:00
2022-06-09 08:49:43 +00:00
info = ( struct ec_response_get_protocol_info * ) msg - > data ;
switch ( devidx ) {
case CROS_EC_DEV_EC_INDEX :
ec_dev - > max_request = info - > max_request_packet_size -
sizeof ( struct ec_host_request ) ;
ec_dev - > max_response = info - > max_response_packet_size -
sizeof ( struct ec_host_response ) ;
ec_dev - > proto_version = min ( EC_HOST_REQUEST_VERSION ,
fls ( info - > protocol_versions ) - 1 ) ;
ec_dev - > din_size = info - > max_response_packet_size + EC_MAX_RESPONSE_OVERHEAD ;
ec_dev - > dout_size = info - > max_request_packet_size + EC_MAX_REQUEST_OVERHEAD ;
dev_dbg ( ec_dev - > dev , " using proto v%u \n " , ec_dev - > proto_version ) ;
break ;
case CROS_EC_DEV_PD_INDEX :
ec_dev - > max_passthru = info - > max_request_packet_size -
sizeof ( struct ec_host_request ) ;
dev_dbg ( ec_dev - > dev , " found PD chip \n " ) ;
break ;
default :
2022-06-14 07:49:09 +01:00
dev_dbg ( ec_dev - > dev , " unknown passthru index: %d \n " , devidx ) ;
2022-06-09 08:49:43 +00:00
break ;
}
ret = 0 ;
exit :
kfree ( msg ) ;
return ret ;
2015-06-09 13:04:45 +02:00
}
2022-06-09 08:49:46 +00:00
static int cros_ec_get_proto_info_legacy ( struct cros_ec_device * ec_dev )
2015-06-09 13:04:45 +02:00
{
struct cros_ec_command * msg ;
2022-06-09 08:49:46 +00:00
struct ec_params_hello * params ;
struct ec_response_hello * response ;
2022-06-09 08:49:48 +00:00
int ret , mapped ;
2015-06-09 13:04:45 +02:00
2022-06-09 08:49:46 +00:00
ec_dev - > proto_version = 2 ;
msg = kzalloc ( sizeof ( * msg ) + max ( sizeof ( * params ) , sizeof ( * response ) ) , GFP_KERNEL ) ;
2015-06-09 13:04:45 +02:00
if ( ! msg )
return - ENOMEM ;
msg - > command = EC_CMD_HELLO ;
2022-06-09 08:49:46 +00:00
msg - > insize = sizeof ( * response ) ;
msg - > outsize = sizeof ( * params ) ;
2015-06-09 13:04:45 +02:00
2022-06-09 08:49:46 +00:00
params = ( struct ec_params_hello * ) msg - > data ;
params - > in_data = 0xa0b0c0d0 ;
2015-06-09 13:04:45 +02:00
2022-07-18 05:09:06 +00:00
ret = cros_ec_send_command ( ec_dev , msg ) ;
2015-06-09 13:04:45 +02:00
if ( ret < 0 ) {
2022-06-09 08:49:46 +00:00
dev_dbg ( ec_dev - > dev , " EC failed to respond to v2 hello: %d \n " , ret ) ;
2015-06-09 13:04:45 +02:00
goto exit ;
2022-06-09 08:49:46 +00:00
}
2022-06-09 08:49:48 +00:00
mapped = cros_ec_map_error ( msg - > result ) ;
if ( mapped ) {
ret = mapped ;
2022-06-09 08:49:46 +00:00
dev_err ( ec_dev - > dev , " EC responded to v2 hello with error: %d \n " , msg - > result ) ;
2015-06-09 13:04:45 +02:00
goto exit ;
2022-06-09 08:49:46 +00:00
}
2022-06-09 08:49:48 +00:00
if ( ret = = 0 ) {
ret = - EPROTO ;
goto exit ;
}
2022-06-09 08:49:46 +00:00
response = ( struct ec_response_hello * ) msg - > data ;
if ( response - > out_data ! = 0xa1b2c3d4 ) {
2015-06-09 13:04:45 +02:00
dev_err ( ec_dev - > dev ,
" EC responded to v2 hello with bad result: %u \n " ,
2022-06-09 08:49:46 +00:00
response - > out_data ) ;
2015-06-09 13:04:45 +02:00
ret = - EBADMSG ;
goto exit ;
}
2022-06-09 08:49:46 +00:00
ec_dev - > max_request = EC_PROTO2_MAX_PARAM_SIZE ;
ec_dev - > max_response = EC_PROTO2_MAX_PARAM_SIZE ;
ec_dev - > max_passthru = 0 ;
ec_dev - > pkt_xfer = NULL ;
ec_dev - > din_size = EC_PROTO2_MSG_BYTES ;
ec_dev - > dout_size = EC_PROTO2_MSG_BYTES ;
2015-06-09 13:04:45 +02:00
2022-06-09 08:49:46 +00:00
dev_dbg ( ec_dev - > dev , " falling back to proto v2 \n " ) ;
ret = 0 ;
exit :
2015-06-09 13:04:45 +02:00
kfree ( msg ) ;
return ret ;
}
2017-02-14 20:58:01 +01:00
/*
* cros_ec_get_host_command_version_mask
*
* Get the version mask of a given command .
*
* @ ec_dev : EC device to call
* @ msg : message structure to use
* @ cmd : command to get the version of .
* @ mask : result when function returns 0.
*
* @ return 0 on success , error code otherwise
*
* LOCKING :
* the caller has ec_dev - > lock mutex or the caller knows there is
* no other command in progress .
*/
2022-06-09 08:49:52 +00:00
static int cros_ec_get_host_command_version_mask ( struct cros_ec_device * ec_dev , u16 cmd , u32 * mask )
2016-08-10 19:05:24 +02:00
{
struct ec_params_get_cmd_versions * pver ;
struct ec_response_get_cmd_versions * rver ;
struct cros_ec_command * msg ;
2022-06-09 08:49:52 +00:00
int ret , mapped ;
2016-08-10 19:05:24 +02:00
msg = kmalloc ( sizeof ( * msg ) + max ( sizeof ( * rver ) , sizeof ( * pver ) ) ,
GFP_KERNEL ) ;
if ( ! msg )
return - ENOMEM ;
msg - > version = 0 ;
msg - > command = EC_CMD_GET_CMD_VERSIONS ;
msg - > insize = sizeof ( * rver ) ;
msg - > outsize = sizeof ( * pver ) ;
pver = ( struct ec_params_get_cmd_versions * ) msg - > data ;
pver - > cmd = cmd ;
2022-07-18 05:09:06 +00:00
ret = cros_ec_send_command ( ec_dev , msg ) ;
2022-06-09 08:49:52 +00:00
if ( ret < 0 )
goto exit ;
mapped = cros_ec_map_error ( msg - > result ) ;
if ( mapped ) {
ret = mapped ;
goto exit ;
2016-08-10 19:05:24 +02:00
}
2022-06-09 08:49:54 +00:00
if ( ret = = 0 ) {
ret = - EPROTO ;
goto exit ;
}
2022-06-09 08:49:52 +00:00
rver = ( struct ec_response_get_cmd_versions * ) msg - > data ;
* mask = rver - > version_mask ;
ret = 0 ;
exit :
2016-08-10 19:05:24 +02:00
kfree ( msg ) ;
return ret ;
}
2019-11-19 13:45:45 +01:00
/**
* cros_ec_query_all ( ) - Query the protocol version supported by the
* ChromeOS EC .
* @ ec_dev : Device to register .
*
* Return : 0 on success or negative error code .
*/
2015-06-09 13:04:45 +02:00
int cros_ec_query_all ( struct cros_ec_device * ec_dev )
{
struct device * dev = ec_dev - > dev ;
2022-06-09 08:49:50 +00:00
u32 ver_mask ;
2015-06-09 13:04:45 +02:00
int ret ;
/* First try sending with proto v3. */
2022-06-09 08:49:43 +00:00
if ( ! cros_ec_get_proto_info ( ec_dev , CROS_EC_DEV_EC_INDEX ) ) {
/* Check for PD. */
cros_ec_get_proto_info ( ec_dev , CROS_EC_DEV_PD_INDEX ) ;
2015-06-09 13:04:45 +02:00
} else {
/* Try querying with a v2 hello message. */
2022-06-09 08:49:46 +00:00
ret = cros_ec_get_proto_info_legacy ( ec_dev ) ;
if ( ret ) {
2015-06-09 13:04:45 +02:00
/*
* It ' s possible for a test to occur too early when
* the EC isn ' t listening . If this happens , we ' ll
* test later when the first command is run .
*/
ec_dev - > proto_version = EC_PROTO_VERSION_UNKNOWN ;
dev_dbg ( ec_dev - > dev , " EC query failed: %d \n " , ret ) ;
2022-06-09 08:49:46 +00:00
return ret ;
2015-06-09 13:04:44 +02:00
}
2015-06-09 13:04:45 +02:00
}
2015-06-09 13:04:44 +02:00
2015-06-09 13:04:45 +02:00
devm_kfree ( dev , ec_dev - > din ) ;
devm_kfree ( dev , ec_dev - > dout ) ;
ec_dev - > din = devm_kzalloc ( dev , ec_dev - > din_size , GFP_KERNEL ) ;
if ( ! ec_dev - > din ) {
ret = - ENOMEM ;
goto exit ;
2015-06-09 13:04:44 +02:00
}
2015-06-09 13:04:45 +02:00
ec_dev - > dout = devm_kzalloc ( dev , ec_dev - > dout_size , GFP_KERNEL ) ;
if ( ! ec_dev - > dout ) {
devm_kfree ( dev , ec_dev - > din ) ;
ret = - ENOMEM ;
goto exit ;
}
2016-08-10 19:05:24 +02:00
/* Probe if MKBP event is supported */
2022-06-09 08:49:50 +00:00
ret = cros_ec_get_host_command_version_mask ( ec_dev , EC_CMD_GET_NEXT_EVENT , & ver_mask ) ;
2022-06-09 08:49:49 +00:00
if ( ret < 0 | | ver_mask = = 0 ) {
2016-08-10 19:05:24 +02:00
ec_dev - > mkbp_event_supported = 0 ;
2022-06-09 08:49:49 +00:00
} else {
2019-11-19 13:45:46 +01:00
ec_dev - > mkbp_event_supported = fls ( ver_mask ) ;
2022-06-09 08:49:49 +00:00
dev_dbg ( ec_dev - > dev , " MKBP support version %u \n " , ec_dev - > mkbp_event_supported - 1 ) ;
}
2016-08-10 19:05:24 +02:00
2019-04-03 14:34:28 -07:00
/* Probe if host sleep v1 is supported for S0ix failure detection. */
2022-06-09 08:49:50 +00:00
ret = cros_ec_get_host_command_version_mask ( ec_dev , EC_CMD_HOST_SLEEP_EVENT , & ver_mask ) ;
ec_dev - > host_sleep_v1 = ( ret = = 0 & & ( ver_mask & EC_VER_MASK ( 1 ) ) ) ;
2019-04-03 14:34:28 -07:00
platform/chrome: cros_ec_proto: ignore unnecessary wakeups on old ECs
ECs that don't implement EC_CMD_HOST_EVENT_GET_WAKE_MASK should still
have some reasonable default mask -- otherwise, they'll treat a variety
of EC signals as spurious wakeups. Battery and AC events can be
especially common, for devices that have been sitting at full charge
plugged into AC for a long time, as they may cycle their charging off
and on, or their battery may start reporting failures as it ages.
Treating these as wakeups does not serve a useful purpose, and is
instead often counterproductive. And indeed, later ECs (that implement
the mask) don't include these events in their wake-mask.
Note that this patch doesn't do anything without the subsequent patch
("platform/chrome: cros_ec_proto: check for missing
EC_CMD_HOST_EVENT_GET_WAKE_MASK"), because
cros_ec_get_host_event_wake_mask() currently does not return an error if
EC_CMD_HOST_EVENT_GET_WAKE_MASK is not implemented.
Some additional notes:
While the EC typically knows not to wake the CPU for these unimportant
events once the CPU reaches a sleep state, it doesn't really have a way
to know that the CPU is "almost" asleep, unless it has support for
EC_CMD_HOST_SLEEP_EVENT. Alas, these older ECs do not support that
command either, so this solution is not 100% complete.
Signed-off-by: Brian Norris <briannorris@chromium.org>
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
2020-07-24 12:08:40 -07:00
/* Get host event wake mask. */
2022-06-09 08:49:43 +00:00
ret = cros_ec_get_host_event_wake_mask ( ec_dev , & ec_dev - > host_event_wake_mask ) ;
platform/chrome: cros_ec_proto: ignore unnecessary wakeups on old ECs
ECs that don't implement EC_CMD_HOST_EVENT_GET_WAKE_MASK should still
have some reasonable default mask -- otherwise, they'll treat a variety
of EC signals as spurious wakeups. Battery and AC events can be
especially common, for devices that have been sitting at full charge
plugged into AC for a long time, as they may cycle their charging off
and on, or their battery may start reporting failures as it ages.
Treating these as wakeups does not serve a useful purpose, and is
instead often counterproductive. And indeed, later ECs (that implement
the mask) don't include these events in their wake-mask.
Note that this patch doesn't do anything without the subsequent patch
("platform/chrome: cros_ec_proto: check for missing
EC_CMD_HOST_EVENT_GET_WAKE_MASK"), because
cros_ec_get_host_event_wake_mask() currently does not return an error if
EC_CMD_HOST_EVENT_GET_WAKE_MASK is not implemented.
Some additional notes:
While the EC typically knows not to wake the CPU for these unimportant
events once the CPU reaches a sleep state, it doesn't really have a way
to know that the CPU is "almost" asleep, unless it has support for
EC_CMD_HOST_SLEEP_EVENT. Alas, these older ECs do not support that
command either, so this solution is not 100% complete.
Signed-off-by: Brian Norris <briannorris@chromium.org>
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
2020-07-24 12:08:40 -07:00
if ( ret < 0 ) {
/*
* If the EC doesn ' t support EC_CMD_HOST_EVENT_GET_WAKE_MASK ,
* use a reasonable default . Note that we ignore various
* battery , AC status , and power - state events , because ( a )
* those can be quite common ( e . g . , when sitting at full
* charge , on AC ) and ( b ) these are not actionable wake events ;
* if anything , we ' d like to continue suspending ( to save
* power ) , not wake up .
*/
ec_dev - > host_event_wake_mask = U32_MAX &
2020-12-09 22:03:55 +00:00
~ ( EC_HOST_EVENT_MASK ( EC_HOST_EVENT_LID_CLOSED ) |
EC_HOST_EVENT_MASK ( EC_HOST_EVENT_AC_DISCONNECTED ) |
2020-12-09 22:03:54 +00:00
EC_HOST_EVENT_MASK ( EC_HOST_EVENT_BATTERY_LOW ) |
EC_HOST_EVENT_MASK ( EC_HOST_EVENT_BATTERY_CRITICAL ) |
2020-12-09 22:03:55 +00:00
EC_HOST_EVENT_MASK ( EC_HOST_EVENT_BATTERY ) |
2020-12-09 22:03:54 +00:00
EC_HOST_EVENT_MASK ( EC_HOST_EVENT_PD_MCU ) |
EC_HOST_EVENT_MASK ( EC_HOST_EVENT_BATTERY_STATUS ) ) ;
platform/chrome: cros_ec_proto: check for missing EC_CMD_HOST_EVENT_GET_WAKE_MASK
As with cros_ec_cmd_xfer_status(), etc., it's not enough to simply check
for the return status of send_command() -- that only covers transport or
other similarly-fatal errors. One must also check the ->result field, to
see whether the command really succeeded. If not, we can't use the data
it returns.
The caller of cros_ec_get_host_event_wake_mask() ignores this, and so
for example, on EC's where the command is not implemented, we're using
junk (or in practice, all zeros) for our wake-mask. We should be using a
non-zero default (currently, it's supposed to be all-1's).
Fix this by checking the ->result field and returning -EPROTO for
errors.
I might label this as fixing commit 29d99b966d60 ("cros_ec: Don't signal
wake event for non-wake host events"), except that this fix alone
actually may make things worse, as it now allows for a lot more spurious
wakeups. The patch "platform/chrome: cros_ec_proto: ignore battery/AC
wakeups on old ECs" helps to mitigate this.
Signed-off-by: Brian Norris <briannorris@chromium.org>
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
2020-07-24 12:08:41 -07:00
/*
* Old ECs may not support this command . Complain about all
* other errors .
*/
if ( ret ! = - EOPNOTSUPP )
dev_err ( ec_dev - > dev ,
" failed to retrieve wake mask: %d \n " , ret ) ;
platform/chrome: cros_ec_proto: ignore unnecessary wakeups on old ECs
ECs that don't implement EC_CMD_HOST_EVENT_GET_WAKE_MASK should still
have some reasonable default mask -- otherwise, they'll treat a variety
of EC signals as spurious wakeups. Battery and AC events can be
especially common, for devices that have been sitting at full charge
plugged into AC for a long time, as they may cycle their charging off
and on, or their battery may start reporting failures as it ages.
Treating these as wakeups does not serve a useful purpose, and is
instead often counterproductive. And indeed, later ECs (that implement
the mask) don't include these events in their wake-mask.
Note that this patch doesn't do anything without the subsequent patch
("platform/chrome: cros_ec_proto: check for missing
EC_CMD_HOST_EVENT_GET_WAKE_MASK"), because
cros_ec_get_host_event_wake_mask() currently does not return an error if
EC_CMD_HOST_EVENT_GET_WAKE_MASK is not implemented.
Some additional notes:
While the EC typically knows not to wake the CPU for these unimportant
events once the CPU reaches a sleep state, it doesn't really have a way
to know that the CPU is "almost" asleep, unless it has support for
EC_CMD_HOST_SLEEP_EVENT. Alas, these older ECs do not support that
command either, so this solution is not 100% complete.
Signed-off-by: Brian Norris <briannorris@chromium.org>
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
2020-07-24 12:08:40 -07:00
}
2017-02-14 20:58:02 +01:00
2017-02-14 20:58:00 +01:00
ret = 0 ;
2015-06-09 13:04:44 +02:00
exit :
2015-06-09 13:04:45 +02:00
return ret ;
}
EXPORT_SYMBOL ( cros_ec_query_all ) ;
2019-11-19 13:45:45 +01:00
/**
2022-03-18 09:54:22 -07:00
* cros_ec_cmd_xfer ( ) - Send a command to the ChromeOS EC .
2019-11-19 13:45:45 +01:00
* @ ec_dev : EC device .
* @ msg : Message to write .
*
2022-03-18 09:54:22 -07:00
* Call this to send a command to the ChromeOS EC . This should be used instead
* of calling the EC ' s cmd_xfer ( ) callback directly . This function does not
* convert EC command execution error codes to Linux error codes . Most
* in - kernel users will want to use cros_ec_cmd_xfer_status ( ) instead since
* that function implements the conversion .
2019-11-19 13:45:45 +01:00
*
2020-09-21 10:51:58 +02:00
* Return :
2022-03-18 09:54:22 -07:00
* > 0 - EC command was executed successfully . The return value is the number
* of bytes returned by the EC ( excluding the header ) .
* = 0 - EC communication was successful . EC command execution results are
* reported in msg - > result . The result will be EC_RES_SUCCESS if the
* command was executed successfully or report an EC command execution
* error .
* < 0 - EC communication error . Return value is the Linux error code .
2019-11-19 13:45:45 +01:00
*/
2022-03-18 09:54:22 -07:00
int cros_ec_cmd_xfer ( struct cros_ec_device * ec_dev , struct cros_ec_command * msg )
2015-06-09 13:04:45 +02:00
{
2022-03-18 09:54:22 -07:00
int ret ;
2015-06-09 13:04:45 +02:00
mutex_lock ( & ec_dev - > lock ) ;
if ( ec_dev - > proto_version = = EC_PROTO_VERSION_UNKNOWN ) {
ret = cros_ec_query_all ( ec_dev ) ;
if ( ret ) {
dev_err ( ec_dev - > dev ,
" EC version unknown and query failed; aborting command \n " ) ;
mutex_unlock ( & ec_dev - > lock ) ;
return ret ;
}
}
if ( msg - > insize > ec_dev - > max_response ) {
dev_dbg ( ec_dev - > dev , " clamping message receive buffer \n " ) ;
msg - > insize = ec_dev - > max_response ;
}
2022-06-09 08:49:39 +00:00
if ( msg - > command < EC_CMD_PASSTHRU_OFFSET ( CROS_EC_DEV_PD_INDEX ) ) {
2015-06-09 13:04:45 +02:00
if ( msg - > outsize > ec_dev - > max_request ) {
dev_err ( ec_dev - > dev ,
" request of size %u is too big (max: %u) \n " ,
msg - > outsize ,
ec_dev - > max_request ) ;
mutex_unlock ( & ec_dev - > lock ) ;
return - EMSGSIZE ;
}
} else {
if ( msg - > outsize > ec_dev - > max_passthru ) {
dev_err ( ec_dev - > dev ,
" passthru rq of size %u is too big (max: %u) \n " ,
msg - > outsize ,
ec_dev - > max_passthru ) ;
mutex_unlock ( & ec_dev - > lock ) ;
return - EMSGSIZE ;
}
}
2020-09-21 10:51:58 +02:00
2022-07-18 05:09:06 +00:00
ret = cros_ec_send_command ( ec_dev , msg ) ;
2015-06-09 13:04:44 +02:00
mutex_unlock ( & ec_dev - > lock ) ;
2022-03-18 09:54:22 -07:00
return ret ;
}
EXPORT_SYMBOL ( cros_ec_cmd_xfer ) ;
/**
* cros_ec_cmd_xfer_status ( ) - Send a command to the ChromeOS EC .
* @ ec_dev : EC device .
* @ msg : Message to write .
*
* Call this to send a command to the ChromeOS EC . This should be used instead of calling the EC ' s
* cmd_xfer ( ) callback directly . It returns success status only if both the command was transmitted
* successfully and the EC replied with success status .
*
* Return :
* > = 0 - The number of bytes transferred .
* < 0 - Linux error code
*/
int cros_ec_cmd_xfer_status ( struct cros_ec_device * ec_dev ,
struct cros_ec_command * msg )
{
int ret , mapped ;
ret = cros_ec_cmd_xfer ( ec_dev , msg ) ;
if ( ret < 0 )
return ret ;
2020-08-22 08:08:56 -07:00
mapped = cros_ec_map_error ( msg - > result ) ;
if ( mapped ) {
dev_dbg ( ec_dev - > dev , " Command result (err: %d [%d]) \n " ,
msg - > result , mapped ) ;
ret = mapped ;
2016-07-15 16:28:41 -07:00
}
2020-09-21 10:51:58 +02:00
2016-07-15 16:28:41 -07:00
return ret ;
}
EXPORT_SYMBOL ( cros_ec_cmd_xfer_status ) ;
2016-08-10 19:05:24 +02:00
2018-07-04 17:08:18 +02:00
static int get_next_event_xfer ( struct cros_ec_device * ec_dev ,
struct cros_ec_command * msg ,
2019-11-19 13:45:46 +01:00
struct ec_response_get_next_event_v1 * event ,
2018-07-04 17:08:18 +02:00
int version , uint32_t size )
{
int ret ;
msg - > version = version ;
msg - > command = EC_CMD_GET_NEXT_EVENT ;
msg - > insize = size ;
msg - > outsize = 0 ;
2020-09-21 10:51:58 +02:00
ret = cros_ec_cmd_xfer_status ( ec_dev , msg ) ;
2018-07-04 17:08:18 +02:00
if ( ret > 0 ) {
ec_dev - > event_size = ret - 1 ;
2019-11-19 13:45:46 +01:00
ec_dev - > event_data = * event ;
2018-07-04 17:08:18 +02:00
}
return ret ;
}
2016-08-10 19:05:24 +02:00
static int get_next_event ( struct cros_ec_device * ec_dev )
{
2019-11-19 13:45:46 +01:00
struct {
struct cros_ec_command msg ;
struct ec_response_get_next_event_v1 event ;
} __packed buf ;
struct cros_ec_command * msg = & buf . msg ;
struct ec_response_get_next_event_v1 * event = & buf . event ;
const int cmd_version = ec_dev - > mkbp_event_supported - 1 ;
2016-08-10 19:05:24 +02:00
2019-11-19 13:45:46 +01:00
memset ( msg , 0 , sizeof ( * msg ) ) ;
2016-12-16 18:57:36 +01:00
if ( ec_dev - > suspended ) {
dev_dbg ( ec_dev - > dev , " Device suspended. \n " ) ;
return - EHOSTDOWN ;
}
2019-11-19 13:45:46 +01:00
if ( cmd_version = = 0 )
return get_next_event_xfer ( ec_dev , msg , event , 0 ,
2018-07-04 17:08:18 +02:00
sizeof ( struct ec_response_get_next_event ) ) ;
2019-11-19 13:45:46 +01:00
return get_next_event_xfer ( ec_dev , msg , event , cmd_version ,
sizeof ( struct ec_response_get_next_event_v1 ) ) ;
2016-08-10 19:05:24 +02:00
}
static int get_keyboard_state_event ( struct cros_ec_device * ec_dev )
{
u8 buffer [ sizeof ( struct cros_ec_command ) +
sizeof ( ec_dev - > event_data . data ) ] ;
struct cros_ec_command * msg = ( struct cros_ec_command * ) & buffer ;
msg - > version = 0 ;
msg - > command = EC_CMD_MKBP_STATE ;
msg - > insize = sizeof ( ec_dev - > event_data . data ) ;
msg - > outsize = 0 ;
2020-09-21 10:51:58 +02:00
ec_dev - > event_size = cros_ec_cmd_xfer_status ( ec_dev , msg ) ;
2016-08-10 19:05:24 +02:00
ec_dev - > event_data . event_type = EC_MKBP_EVENT_KEY_MATRIX ;
memcpy ( & ec_dev - > event_data . data , msg - > data ,
sizeof ( ec_dev - > event_data . data ) ) ;
return ec_dev - > event_size ;
}
2019-11-19 13:45:45 +01:00
/**
* cros_ec_get_next_event ( ) - Fetch next event from the ChromeOS EC .
* @ ec_dev : Device to fetch event from .
* @ wake_event : Pointer to a bool set to true upon return if the event might be
* treated as a wake event . Ignored if null .
2019-11-19 13:45:46 +01:00
* @ has_more_events : Pointer to bool set to true if more than one event is
* pending .
* Some EC will set this flag to indicate cros_ec_get_next_event ( )
* can be called multiple times in a row .
* It is an optimization to prevent issuing a EC command for
* nothing or wait for another interrupt from the EC to process
* the next message .
* Ignored if null .
2019-11-19 13:45:45 +01:00
*
* Return : negative error code on errors ; 0 for no data ; or else number of
* bytes received ( i . e . , an event was retrieved successfully ) . Event types are
* written out to @ ec_dev - > event_data . event_type on success .
*/
2019-11-19 13:45:46 +01:00
int cros_ec_get_next_event ( struct cros_ec_device * ec_dev ,
bool * wake_event ,
bool * has_more_events )
2016-08-10 19:05:24 +02:00
{
2018-11-07 18:49:39 -08:00
u8 event_type ;
2017-02-14 20:58:02 +01:00
u32 host_event ;
int ret ;
2022-08-02 17:41:28 +02:00
u32 ver_mask ;
2017-02-14 20:58:02 +01:00
2019-11-19 13:45:46 +01:00
/*
* Default value for wake_event .
* Wake up on keyboard event , wake up for spurious interrupt or link
* error to the EC .
*/
if ( wake_event )
* wake_event = true ;
2017-02-14 20:58:02 +01:00
2019-11-19 13:45:46 +01:00
/*
* Default value for has_more_events .
* EC will raise another interrupt if AP does not process all events
* anyway .
*/
if ( has_more_events )
* has_more_events = false ;
2017-02-14 20:58:02 +01:00
2019-11-19 13:45:46 +01:00
if ( ! ec_dev - > mkbp_event_supported )
return get_keyboard_state_event ( ec_dev ) ;
2017-02-14 20:58:02 +01:00
ret = get_next_event ( ec_dev ) ;
2022-08-02 17:41:28 +02:00
/*
* - ENOPROTOOPT is returned when EC returns EC_RES_INVALID_VERSION .
* This can occur when EC based device ( e . g . Fingerprint MCU ) jumps to
* the RO image which doesn ' t support newer version of the command . In
* this case we will attempt to update maximum supported version of the
* EC_CMD_GET_NEXT_EVENT .
*/
if ( ret = = - ENOPROTOOPT ) {
dev_dbg ( ec_dev - > dev ,
" GET_NEXT_EVENT returned invalid version error. \n " ) ;
ret = cros_ec_get_host_command_version_mask ( ec_dev ,
EC_CMD_GET_NEXT_EVENT ,
& ver_mask ) ;
if ( ret < 0 | | ver_mask = = 0 )
/*
* Do not change the MKBP supported version if we can ' t
* obtain supported version correctly . Please note that
* calling EC_CMD_GET_NEXT_EVENT returned
* EC_RES_INVALID_VERSION which means that the command
* is present .
*/
return - ENOPROTOOPT ;
ec_dev - > mkbp_event_supported = fls ( ver_mask ) ;
dev_dbg ( ec_dev - > dev , " MKBP support version changed to %u \n " ,
ec_dev - > mkbp_event_supported - 1 ) ;
/* Try to get next event with new MKBP support version set. */
ret = get_next_event ( ec_dev ) ;
}
2018-11-07 18:49:38 -08:00
if ( ret < = 0 )
2017-02-14 20:58:02 +01:00
return ret ;
2019-11-19 13:45:46 +01:00
if ( has_more_events )
* has_more_events = ec_dev - > event_data . event_type &
EC_MKBP_HAS_MORE_EVENTS ;
ec_dev - > event_data . event_type & = EC_MKBP_EVENT_TYPE_MASK ;
2017-02-14 20:58:02 +01:00
if ( wake_event ) {
2018-11-07 18:49:39 -08:00
event_type = ec_dev - > event_data . event_type ;
2017-02-14 20:58:02 +01:00
host_event = cros_ec_get_host_event ( ec_dev ) ;
2018-11-07 18:49:39 -08:00
/*
* Sensor events need to be parsed by the sensor sub - device .
* Defer them , and don ' t report the wakeup here .
*/
2020-10-30 16:25:23 -07:00
if ( event_type = = EC_MKBP_EVENT_SENSOR_FIFO ) {
2018-11-07 18:49:39 -08:00
* wake_event = false ;
2020-10-30 16:25:23 -07:00
} else if ( host_event ) {
/* rtc_update_irq() already handles wakeup events. */
if ( host_event & EC_HOST_EVENT_MASK ( EC_HOST_EVENT_RTC ) )
* wake_event = false ;
/* Masked host-events should not count as wake events. */
if ( ! ( host_event & ec_dev - > host_event_wake_mask ) )
* wake_event = false ;
}
2017-02-14 20:58:02 +01:00
}
return ret ;
2016-08-10 19:05:24 +02:00
}
EXPORT_SYMBOL ( cros_ec_get_next_event ) ;
2017-05-16 17:46:48 +02:00
2019-11-19 13:45:45 +01:00
/**
* cros_ec_get_host_event ( ) - Return a mask of event set by the ChromeOS EC .
* @ ec_dev : Device to fetch event from .
*
* When MKBP is supported , when the EC raises an interrupt , we collect the
* events raised and call the functions in the ec notifier . This function
* is a helper to know which events are raised .
*
* Return : 0 on error or non - zero bitmask of one or more EC_HOST_EVENT_ * .
*/
2017-05-16 17:46:48 +02:00
u32 cros_ec_get_host_event ( struct cros_ec_device * ec_dev )
{
u32 host_event ;
2022-05-13 12:41:40 +08:00
if ( ! ec_dev - > mkbp_event_supported )
return 0 ;
2017-05-16 17:46:48 +02:00
if ( ec_dev - > event_data . event_type ! = EC_MKBP_EVENT_HOST_EVENT )
return 0 ;
if ( ec_dev - > event_size ! = sizeof ( host_event ) ) {
dev_warn ( ec_dev - > dev , " Invalid host event size \n " ) ;
return 0 ;
}
host_event = get_unaligned_le32 ( & ec_dev - > event_data . data . host_event ) ;
return host_event ;
}
EXPORT_SYMBOL ( cros_ec_get_host_event ) ;
2019-11-19 13:45:45 +01:00
/**
* cros_ec_check_features ( ) - Test for the presence of EC features
*
* @ ec : EC device , does not have to be connected directly to the AP ,
* can be daisy chained through another device .
* @ feature : One of ec_feature_code bit .
*
* Call this function to test whether the ChromeOS EC supports a feature .
*
2021-09-15 18:46:27 -07:00
* Return : true if supported , false if not ( or if an error was encountered ) .
2019-11-19 13:45:45 +01:00
*/
2021-09-15 18:46:27 -07:00
bool cros_ec_check_features ( struct cros_ec_dev * ec , int feature )
2019-11-19 13:45:45 +01:00
{
2021-10-04 10:07:09 -07:00
struct ec_response_get_features * features = & ec - > features ;
2019-11-19 13:45:45 +01:00
int ret ;
2021-10-04 10:07:09 -07:00
if ( features - > flags [ 0 ] = = - 1U & & features - > flags [ 1 ] = = - 1U ) {
2019-11-19 13:45:45 +01:00
/* features bitmap not read yet */
2022-06-06 20:18:03 +00:00
ret = cros_ec_cmd ( ec - > ec_dev , 0 , EC_CMD_GET_FEATURES + ec - > cmd_offset ,
NULL , 0 , features , sizeof ( * features ) ) ;
2019-11-19 13:45:45 +01:00
if ( ret < 0 ) {
2021-10-04 10:07:12 -07:00
dev_warn ( ec - > dev , " cannot get EC features: %d \n " , ret ) ;
2021-10-04 10:07:09 -07:00
memset ( features , 0 , sizeof ( * features ) ) ;
2019-11-19 13:45:45 +01:00
}
dev_dbg ( ec - > dev , " EC features %08x %08x \n " ,
2021-10-04 10:07:09 -07:00
features - > flags [ 0 ] , features - > flags [ 1 ] ) ;
2019-11-19 13:45:45 +01:00
}
2021-10-04 10:07:09 -07:00
return ! ! ( features - > flags [ feature / 32 ] & EC_FEATURE_MASK_0 ( feature ) ) ;
2019-11-19 13:45:45 +01:00
}
EXPORT_SYMBOL_GPL ( cros_ec_check_features ) ;
/**
* cros_ec_get_sensor_count ( ) - Return the number of MEMS sensors supported .
*
* @ ec : EC device , does not have to be connected directly to the AP ,
* can be daisy chained through another device .
* Return : < 0 in case of error .
*/
int cros_ec_get_sensor_count ( struct cros_ec_dev * ec )
{
/*
* Issue a command to get the number of sensor reported .
* If not supported , check for legacy mode .
*/
int ret , sensor_count ;
struct ec_params_motion_sense * params ;
struct ec_response_motion_sense * resp ;
struct cros_ec_command * msg ;
struct cros_ec_device * ec_dev = ec - > ec_dev ;
u8 status ;
msg = kzalloc ( sizeof ( * msg ) + max ( sizeof ( * params ) , sizeof ( * resp ) ) ,
GFP_KERNEL ) ;
if ( ! msg )
return - ENOMEM ;
msg - > version = 1 ;
msg - > command = EC_CMD_MOTION_SENSE_CMD + ec - > cmd_offset ;
msg - > outsize = sizeof ( * params ) ;
msg - > insize = sizeof ( * resp ) ;
params = ( struct ec_params_motion_sense * ) msg - > data ;
params - > cmd = MOTIONSENSE_CMD_DUMP ;
2020-09-21 10:51:58 +02:00
ret = cros_ec_cmd_xfer_status ( ec - > ec_dev , msg ) ;
2019-11-19 13:45:45 +01:00
if ( ret < 0 ) {
sensor_count = ret ;
} else {
resp = ( struct ec_response_motion_sense * ) msg - > data ;
sensor_count = resp - > dump . sensor_count ;
}
kfree ( msg ) ;
/*
* Check legacy mode : Let ' s find out if sensors are accessible
* via LPC interface .
*/
2020-09-21 10:51:58 +02:00
if ( sensor_count < 0 & & ec - > cmd_offset = = 0 & & ec_dev - > cmd_readmem ) {
2019-11-19 13:45:45 +01:00
ret = ec_dev - > cmd_readmem ( ec_dev , EC_MEMMAP_ACC_STATUS ,
1 , & status ) ;
if ( ret > = 0 & &
( status & EC_MEMMAP_ACC_STATUS_PRESENCE_BIT ) ) {
/*
* We have 2 sensors , one in the lid , one in the base .
*/
sensor_count = 2 ;
} else {
/*
* EC uses LPC interface and no sensors are presented .
*/
sensor_count = 0 ;
}
}
return sensor_count ;
}
EXPORT_SYMBOL_GPL ( cros_ec_get_sensor_count ) ;
2021-09-29 19:23:50 -07:00
/**
2022-06-06 20:18:03 +00:00
* cros_ec_cmd - Send a command to the EC .
2021-09-29 19:23:50 -07:00
*
* @ ec_dev : EC device
2021-09-29 19:23:54 -07:00
* @ version : EC command version
2021-09-29 19:23:50 -07:00
* @ command : EC command
* @ outdata : EC command output data
* @ outsize : Size of outdata
* @ indata : EC command input data
* @ insize : Size of indata
*
* Return : > = 0 on success , negative error number on failure .
*/
2022-06-06 20:18:03 +00:00
int cros_ec_cmd ( struct cros_ec_device * ec_dev ,
unsigned int version ,
int command ,
void * outdata ,
2022-06-06 20:18:05 +00:00
size_t outsize ,
2022-06-06 20:18:03 +00:00
void * indata ,
2022-06-06 20:18:05 +00:00
size_t insize )
2021-09-29 19:23:50 -07:00
{
struct cros_ec_command * msg ;
int ret ;
msg = kzalloc ( sizeof ( * msg ) + max ( insize , outsize ) , GFP_KERNEL ) ;
if ( ! msg )
return - ENOMEM ;
2021-09-29 19:23:54 -07:00
msg - > version = version ;
2021-09-29 19:23:50 -07:00
msg - > command = command ;
msg - > outsize = outsize ;
msg - > insize = insize ;
if ( outsize )
memcpy ( msg - > data , outdata , outsize ) ;
ret = cros_ec_cmd_xfer_status ( ec_dev , msg ) ;
if ( ret < 0 )
goto error ;
if ( insize )
memcpy ( indata , msg - > data , insize ) ;
error :
kfree ( msg ) ;
return ret ;
}
2022-06-06 20:18:03 +00:00
EXPORT_SYMBOL_GPL ( cros_ec_cmd ) ;