2012-12-25 21:06:07 +04:00
/*
*
* Intel Management Engine Interface ( Intel MEI ) Linux driver
* Copyright ( c ) 2003 - 2012 , Intel Corporation .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
*/
2014-03-19 00:51:55 +04:00
# include <linux/export.h>
2012-12-25 21:06:07 +04:00
# include <linux/sched.h>
# include <linux/wait.h>
2014-03-19 00:52:02 +04:00
# include <linux/pm_runtime.h>
2014-09-29 17:31:46 +04:00
# include <linux/slab.h>
# include <linux/mei.h>
2012-12-25 21:06:07 +04:00
# include "mei_dev.h"
2013-01-09 01:07:12 +04:00
# include "hbm.h"
2014-02-17 17:13:23 +04:00
# include "client.h"
2012-12-25 21:06:07 +04:00
2014-08-21 15:29:16 +04:00
static const char * mei_hbm_status_str ( enum mei_hbm_status status )
{
# define MEI_HBM_STATUS(status) case MEI_HBMS_##status: return #status
switch ( status ) {
MEI_HBM_STATUS ( SUCCESS ) ;
MEI_HBM_STATUS ( CLIENT_NOT_FOUND ) ;
MEI_HBM_STATUS ( ALREADY_EXISTS ) ;
MEI_HBM_STATUS ( REJECTED ) ;
MEI_HBM_STATUS ( INVALID_PARAMETER ) ;
MEI_HBM_STATUS ( NOT_ALLOWED ) ;
MEI_HBM_STATUS ( ALREADY_STARTED ) ;
MEI_HBM_STATUS ( NOT_STARTED ) ;
default : return " unknown " ;
}
# undef MEI_HBM_STATUS
} ;
2014-02-17 17:13:20 +04:00
static const char * mei_cl_conn_status_str ( enum mei_cl_connect_status status )
{
# define MEI_CL_CS(status) case MEI_CL_CONN_##status: return #status
switch ( status ) {
MEI_CL_CS ( SUCCESS ) ;
MEI_CL_CS ( NOT_FOUND ) ;
MEI_CL_CS ( ALREADY_STARTED ) ;
MEI_CL_CS ( OUT_OF_RESOURCES ) ;
MEI_CL_CS ( MESSAGE_SMALL ) ;
2015-07-29 14:59:33 +03:00
MEI_CL_CS ( NOT_ALLOWED ) ;
2014-02-17 17:13:20 +04:00
default : return " unknown " ;
}
# undef MEI_CL_CCS
}
2014-09-29 17:31:33 +04:00
const char * mei_hbm_state_str ( enum mei_hbm_state state )
{
# define MEI_HBM_STATE(state) case MEI_HBM_##state: return #state
switch ( state ) {
MEI_HBM_STATE ( IDLE ) ;
MEI_HBM_STATE ( STARTING ) ;
MEI_HBM_STATE ( STARTED ) ;
MEI_HBM_STATE ( ENUM_CLIENTS ) ;
MEI_HBM_STATE ( CLIENT_PROPERTIES ) ;
MEI_HBM_STATE ( STOPPED ) ;
default :
return " unknown " ;
}
# undef MEI_HBM_STATE
}
2014-02-17 17:13:20 +04:00
/**
* mei_cl_conn_status_to_errno - convert client connect response
* status to error code
*
* @ status : client connect response status
*
2014-09-29 17:31:49 +04:00
* Return : corresponding error code
2014-02-17 17:13:20 +04:00
*/
static int mei_cl_conn_status_to_errno ( enum mei_cl_connect_status status )
{
switch ( status ) {
case MEI_CL_CONN_SUCCESS : return 0 ;
case MEI_CL_CONN_NOT_FOUND : return - ENOTTY ;
case MEI_CL_CONN_ALREADY_STARTED : return - EBUSY ;
case MEI_CL_CONN_OUT_OF_RESOURCES : return - EBUSY ;
case MEI_CL_CONN_MESSAGE_SMALL : return - EINVAL ;
2015-07-29 14:59:33 +03:00
case MEI_CL_CONN_NOT_ALLOWED : return - EBUSY ;
2014-02-17 17:13:20 +04:00
default : return - EINVAL ;
}
}
2014-05-07 17:51:28 +04:00
/**
* mei_hbm_idle - set hbm to idle state
*
* @ dev : the device structure
*/
void mei_hbm_idle ( struct mei_device * dev )
{
dev - > init_clients_timer = 0 ;
dev - > hbm_state = MEI_HBM_IDLE ;
}
2014-08-21 15:29:14 +04:00
/**
* mei_hbm_reset - reset hbm counters and book keeping data structurs
*
* @ dev : the device structure
*/
void mei_hbm_reset ( struct mei_device * dev )
{
2015-01-11 01:07:16 +03:00
mei_me_cl_rm_all ( dev ) ;
2014-05-07 17:51:28 +04:00
mei_hbm_idle ( dev ) ;
}
2014-09-29 17:31:40 +04:00
/**
* mei_hbm_hdr - construct hbm header
*
* @ hdr : hbm header
* @ length : payload length
*/
static inline void mei_hbm_hdr ( struct mei_msg_hdr * hdr , size_t length )
{
hdr - > host_addr = 0 ;
hdr - > me_addr = 0 ;
hdr - > length = length ;
hdr - > msg_complete = 1 ;
hdr - > reserved = 0 ;
2016-05-09 07:07:47 +03:00
hdr - > internal = 0 ;
2014-09-29 17:31:40 +04:00
}
2012-12-25 21:06:09 +04:00
/**
* mei_hbm_cl_hdr - construct client hbm header
2013-04-04 20:05:05 +04:00
*
2014-08-21 15:29:11 +04:00
* @ cl : client
2012-12-25 21:06:09 +04:00
* @ hbm_cmd : host bus message command
* @ buf : buffer for cl header
* @ len : buffer length
*/
static inline
void mei_hbm_cl_hdr ( struct mei_cl * cl , u8 hbm_cmd , void * buf , size_t len )
{
struct mei_hbm_cl_cmd * cmd = buf ;
memset ( cmd , 0 , len ) ;
cmd - > hbm_cmd = hbm_cmd ;
2015-05-04 09:43:56 +03:00
cmd - > host_addr = mei_cl_host_addr ( cl ) ;
2015-05-04 09:43:54 +03:00
cmd - > me_addr = mei_cl_me_id ( cl ) ;
2012-12-25 21:06:09 +04:00
}
2014-08-21 15:29:11 +04:00
/**
* mei_hbm_cl_write - write simple hbm client message
*
* @ dev : the device structure
* @ cl : client
* @ hbm_cmd : host bus message command
2016-06-16 17:58:53 +03:00
* @ buf : message buffer
2014-08-21 15:29:11 +04:00
* @ len : buffer length
2014-09-29 17:31:50 +04:00
*
* Return : 0 on success , < 0 on failure .
2014-08-21 15:29:11 +04:00
*/
2017-03-20 16:04:04 +03:00
static inline int mei_hbm_cl_write ( struct mei_device * dev , struct mei_cl * cl ,
u8 hbm_cmd , void * buf , size_t len )
2014-08-21 15:29:11 +04:00
{
2016-05-09 07:07:47 +03:00
struct mei_msg_hdr mei_hdr ;
2014-08-21 15:29:11 +04:00
2016-05-09 07:07:47 +03:00
mei_hbm_hdr ( & mei_hdr , len ) ;
mei_hbm_cl_hdr ( cl , hbm_cmd , buf , len ) ;
2014-08-21 15:29:11 +04:00
2016-05-09 07:07:47 +03:00
return mei_write_message ( dev , & mei_hdr , buf ) ;
2014-08-21 15:29:11 +04:00
}
2012-12-25 21:06:09 +04:00
/**
2014-08-21 15:29:20 +04:00
* mei_hbm_cl_addr_equal - check if the client ' s and
* the message address match
2012-12-25 21:06:09 +04:00
*
2014-08-21 15:29:20 +04:00
* @ cl : client
* @ cmd : hbm client message
2012-12-25 21:06:09 +04:00
*
2014-09-29 17:31:49 +04:00
* Return : true if addresses are the same
2012-12-25 21:06:09 +04:00
*/
static inline
2014-08-21 15:29:20 +04:00
bool mei_hbm_cl_addr_equal ( struct mei_cl * cl , struct mei_hbm_cl_cmd * cmd )
2012-12-25 21:06:09 +04:00
{
2015-05-04 09:43:56 +03:00
return mei_cl_host_addr ( cl ) = = cmd - > host_addr & &
2015-05-04 09:43:54 +03:00
mei_cl_me_id ( cl ) = = cmd - > me_addr ;
2012-12-25 21:06:09 +04:00
}
2014-08-21 15:29:20 +04:00
/**
* mei_hbm_cl_find_by_cmd - find recipient client
*
* @ dev : the device structure
* @ buf : a buffer with hbm cl command
*
2014-09-29 17:31:49 +04:00
* Return : the recipient client or NULL if not found
2014-08-21 15:29:20 +04:00
*/
static inline
struct mei_cl * mei_hbm_cl_find_by_cmd ( struct mei_device * dev , void * buf )
{
struct mei_hbm_cl_cmd * cmd = ( struct mei_hbm_cl_cmd * ) buf ;
struct mei_cl * cl ;
list_for_each_entry ( cl , & dev - > file_list , link )
if ( mei_hbm_cl_addr_equal ( cl , cmd ) )
return cl ;
return NULL ;
}
2014-08-21 15:29:19 +04:00
/**
* mei_hbm_start_wait - wait for start response message .
*
* @ dev : the device structure
*
2014-09-29 17:31:49 +04:00
* Return : 0 on success and < 0 on failure
2014-08-21 15:29:19 +04:00
*/
2013-04-19 00:03:48 +04:00
int mei_hbm_start_wait ( struct mei_device * dev )
{
int ret ;
2014-08-21 15:29:19 +04:00
if ( dev - > hbm_state > MEI_HBM_STARTING )
2013-04-19 00:03:48 +04:00
return 0 ;
mutex_unlock ( & dev - > device_lock ) ;
2014-08-21 15:29:19 +04:00
ret = wait_event_timeout ( dev - > wait_hbm_start ,
dev - > hbm_state ! = MEI_HBM_STARTING ,
2014-01-15 01:10:10 +04:00
mei_secs_to_jiffies ( MEI_HBM_TIMEOUT ) ) ;
2013-04-19 00:03:48 +04:00
mutex_lock ( & dev - > device_lock ) ;
2014-08-21 15:29:19 +04:00
if ( ret = = 0 & & ( dev - > hbm_state < = MEI_HBM_STARTING ) ) {
2013-04-19 00:03:48 +04:00
dev - > hbm_state = MEI_HBM_IDLE ;
2014-09-29 17:31:42 +04:00
dev_err ( dev - > dev , " waiting for mei start failed \n " ) ;
2014-02-19 19:35:49 +04:00
return - ETIME ;
2013-04-19 00:03:48 +04:00
}
return 0 ;
}
2012-12-25 21:06:07 +04:00
/**
2012-12-25 21:06:11 +04:00
* mei_hbm_start_req - sends start request message .
2012-12-25 21:06:07 +04:00
*
* @ dev : the device structure
2014-01-08 22:19:21 +04:00
*
2014-09-29 17:31:49 +04:00
* Return : 0 on success and < 0 on failure
2012-12-25 21:06:07 +04:00
*/
2013-04-19 00:03:48 +04:00
int mei_hbm_start_req ( struct mei_device * dev )
2012-12-25 21:06:07 +04:00
{
2016-05-09 07:07:47 +03:00
struct mei_msg_hdr mei_hdr ;
struct hbm_host_version_request start_req ;
2012-12-25 21:06:07 +04:00
const size_t len = sizeof ( struct hbm_host_version_request ) ;
2014-01-08 22:19:21 +04:00
int ret ;
2012-12-25 21:06:07 +04:00
2014-08-21 15:29:13 +04:00
mei_hbm_reset ( dev ) ;
2016-05-09 07:07:47 +03:00
mei_hbm_hdr ( & mei_hdr , len ) ;
2012-12-25 21:06:07 +04:00
/* host start message */
2016-05-09 07:07:47 +03:00
memset ( & start_req , 0 , len ) ;
start_req . hbm_cmd = HOST_START_REQ_CMD ;
start_req . host_version . major_version = HBM_MAJOR_VERSION ;
start_req . host_version . minor_version = HBM_MINOR_VERSION ;
2012-12-25 21:06:07 +04:00
2013-04-19 00:03:48 +04:00
dev - > hbm_state = MEI_HBM_IDLE ;
2016-05-09 07:07:47 +03:00
ret = mei_write_message ( dev , & mei_hdr , & start_req ) ;
2014-01-08 22:19:21 +04:00
if ( ret ) {
2014-09-29 17:31:42 +04:00
dev_err ( dev - > dev , " version message write failed: ret = %d \n " ,
2014-01-08 22:19:21 +04:00
ret ) ;
return ret ;
2012-12-25 21:06:07 +04:00
}
2014-01-08 22:19:21 +04:00
2014-08-21 15:29:19 +04:00
dev - > hbm_state = MEI_HBM_STARTING ;
2012-12-25 21:06:07 +04:00
dev - > init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT ;
2016-09-25 13:25:31 +03:00
mei_schedule_stall_timer ( dev ) ;
2013-04-19 00:03:48 +04:00
return 0 ;
2012-12-25 21:06:07 +04:00
}
2015-10-13 15:02:39 +03:00
/**
2012-12-25 21:06:11 +04:00
* mei_hbm_enum_clients_req - sends enumeration client request message .
2012-12-25 21:06:07 +04:00
*
* @ dev : the device structure
*
2014-09-29 17:31:49 +04:00
* Return : 0 on success and < 0 on failure
2012-12-25 21:06:07 +04:00
*/
2014-01-08 22:19:21 +04:00
static int mei_hbm_enum_clients_req ( struct mei_device * dev )
2012-12-25 21:06:07 +04:00
{
2016-05-09 07:07:47 +03:00
struct mei_msg_hdr mei_hdr ;
struct hbm_host_enum_request enum_req ;
2012-12-25 21:06:07 +04:00
const size_t len = sizeof ( struct hbm_host_enum_request ) ;
2014-01-08 22:19:21 +04:00
int ret ;
2012-12-25 21:06:07 +04:00
/* enumerate clients */
2016-05-09 07:07:47 +03:00
mei_hbm_hdr ( & mei_hdr , len ) ;
2012-12-25 21:06:07 +04:00
2016-05-09 07:07:47 +03:00
memset ( & enum_req , 0 , len ) ;
enum_req . hbm_cmd = HOST_ENUM_REQ_CMD ;
enum_req . flags | = dev - > hbm_f_dc_supported ?
MEI_HBM_ENUM_F_ALLOW_ADD : 0 ;
enum_req . flags | = dev - > hbm_f_ie_supported ?
MEI_HBM_ENUM_F_IMMEDIATE_ENUM : 0 ;
2012-12-25 21:06:07 +04:00
2016-05-09 07:07:47 +03:00
ret = mei_write_message ( dev , & mei_hdr , & enum_req ) ;
2014-01-08 22:19:21 +04:00
if ( ret ) {
2014-09-29 17:31:42 +04:00
dev_err ( dev - > dev , " enumeration request write failed: ret = %d. \n " ,
2014-01-08 22:19:21 +04:00
ret ) ;
return ret ;
2012-12-25 21:06:07 +04:00
}
2013-04-19 00:03:48 +04:00
dev - > hbm_state = MEI_HBM_ENUM_CLIENTS ;
2012-12-25 21:06:07 +04:00
dev - > init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT ;
2016-09-25 13:25:31 +03:00
mei_schedule_stall_timer ( dev ) ;
2014-01-08 22:19:21 +04:00
return 0 ;
2012-12-25 21:06:07 +04:00
}
2015-10-13 15:02:39 +03:00
/**
2014-08-21 15:29:13 +04:00
* mei_hbm_me_cl_add - add new me client to the list
*
* @ dev : the device structure
* @ res : hbm property response
*
2014-09-29 17:31:49 +04:00
* Return : 0 on success and - ENOMEM on allocation failure
2014-08-21 15:29:13 +04:00
*/
static int mei_hbm_me_cl_add ( struct mei_device * dev ,
struct hbm_props_response * res )
{
struct mei_me_client * me_cl ;
2015-01-11 01:07:16 +03:00
const uuid_le * uuid = & res - > client_properties . protocol_name ;
mei_me_cl_rm_by_uuid ( dev , uuid ) ;
2014-08-21 15:29:13 +04:00
me_cl = kzalloc ( sizeof ( struct mei_me_client ) , GFP_KERNEL ) ;
if ( ! me_cl )
return - ENOMEM ;
2015-01-11 01:07:16 +03:00
mei_me_cl_init ( me_cl ) ;
2014-08-21 15:29:13 +04:00
me_cl - > props = res - > client_properties ;
me_cl - > client_id = res - > me_addr ;
2016-07-26 01:06:04 +03:00
me_cl - > tx_flow_ctrl_creds = 0 ;
2014-08-21 15:29:13 +04:00
2015-02-10 11:39:31 +03:00
mei_me_cl_add ( dev , me_cl ) ;
2014-08-21 15:29:13 +04:00
return 0 ;
}
2015-07-23 21:37:12 +03:00
/**
* mei_hbm_add_cl_resp - send response to fw on client add request
*
* @ dev : the device structure
* @ addr : me address
* @ status : response status
*
* Return : 0 on success and < 0 on failure
*/
static int mei_hbm_add_cl_resp ( struct mei_device * dev , u8 addr , u8 status )
{
2016-05-09 07:07:47 +03:00
struct mei_msg_hdr mei_hdr ;
struct hbm_add_client_response resp ;
2015-07-23 21:37:12 +03:00
const size_t len = sizeof ( struct hbm_add_client_response ) ;
int ret ;
dev_dbg ( dev - > dev , " adding client response \n " ) ;
2016-05-09 07:07:47 +03:00
mei_hbm_hdr ( & mei_hdr , len ) ;
2015-07-23 21:37:12 +03:00
2016-05-09 07:07:47 +03:00
memset ( & resp , 0 , sizeof ( struct hbm_add_client_response ) ) ;
resp . hbm_cmd = MEI_HBM_ADD_CLIENT_RES_CMD ;
resp . me_addr = addr ;
resp . status = status ;
2015-07-23 21:37:12 +03:00
2016-05-09 07:07:47 +03:00
ret = mei_write_message ( dev , & mei_hdr , & resp ) ;
2015-07-23 21:37:12 +03:00
if ( ret )
dev_err ( dev - > dev , " add client response write failed: ret = %d \n " ,
ret ) ;
return ret ;
}
/**
* mei_hbm_fw_add_cl_req - request from the fw to add a client
*
* @ dev : the device structure
* @ req : add client request
*
* Return : 0 on success and < 0 on failure
*/
static int mei_hbm_fw_add_cl_req ( struct mei_device * dev ,
struct hbm_add_client_request * req )
{
int ret ;
u8 status = MEI_HBMS_SUCCESS ;
BUILD_BUG_ON ( sizeof ( struct hbm_add_client_request ) ! =
sizeof ( struct hbm_props_response ) ) ;
ret = mei_hbm_me_cl_add ( dev , ( struct hbm_props_response * ) req ) ;
if ( ret )
status = ! MEI_HBMS_SUCCESS ;
2016-02-08 00:35:41 +03:00
if ( dev - > dev_state = = MEI_DEV_ENABLED )
schedule_work ( & dev - > bus_rescan_work ) ;
2015-07-23 21:37:12 +03:00
return mei_hbm_add_cl_resp ( dev , req - > me_addr , status ) ;
}
2015-07-26 09:54:16 +03:00
/**
* mei_hbm_cl_notify_req - send notification request
*
* @ dev : the device structure
* @ cl : a client to disconnect from
* @ start : true for start false for stop
*
* Return : 0 on success and - EIO on write failure
*/
int mei_hbm_cl_notify_req ( struct mei_device * dev ,
struct mei_cl * cl , u8 start )
{
2016-05-09 07:07:47 +03:00
struct mei_msg_hdr mei_hdr ;
struct hbm_notification_request req ;
2015-07-26 09:54:16 +03:00
const size_t len = sizeof ( struct hbm_notification_request ) ;
int ret ;
2016-05-09 07:07:47 +03:00
mei_hbm_hdr ( & mei_hdr , len ) ;
mei_hbm_cl_hdr ( cl , MEI_HBM_NOTIFY_REQ_CMD , & req , len ) ;
2015-07-26 09:54:16 +03:00
2016-05-09 07:07:47 +03:00
req . start = start ;
2015-07-26 09:54:16 +03:00
2016-05-09 07:07:47 +03:00
ret = mei_write_message ( dev , & mei_hdr , & req ) ;
2015-07-26 09:54:16 +03:00
if ( ret )
dev_err ( dev - > dev , " notify request failed: ret = %d \n " , ret ) ;
return ret ;
}
/**
* notify_res_to_fop - convert notification response to the proper
* notification FOP
*
* @ cmd : client notification start response command
*
* Return : MEI_FOP_NOTIFY_START or MEI_FOP_NOTIFY_STOP ;
*/
static inline enum mei_cb_file_ops notify_res_to_fop ( struct mei_hbm_cl_cmd * cmd )
{
struct hbm_notification_response * rs =
( struct hbm_notification_response * ) cmd ;
2015-07-26 09:54:18 +03:00
return mei_cl_notify_req2fop ( rs - > start ) ;
2015-07-26 09:54:16 +03:00
}
/**
* mei_hbm_cl_notify_start_res - update the client state according
* notify start response
*
* @ dev : the device structure
* @ cl : mei host client
* @ cmd : client notification start response command
*/
static void mei_hbm_cl_notify_start_res ( struct mei_device * dev ,
struct mei_cl * cl ,
struct mei_hbm_cl_cmd * cmd )
{
struct hbm_notification_response * rs =
( struct hbm_notification_response * ) cmd ;
cl_dbg ( dev , cl , " hbm: notify start response status=%d \n " , rs - > status ) ;
if ( rs - > status = = MEI_HBMS_SUCCESS | |
rs - > status = = MEI_HBMS_ALREADY_STARTED ) {
cl - > notify_en = true ;
cl - > status = 0 ;
} else {
cl - > status = - EINVAL ;
}
}
/**
* mei_hbm_cl_notify_stop_res - update the client state according
* notify stop response
*
* @ dev : the device structure
* @ cl : mei host client
* @ cmd : client notification stop response command
*/
static void mei_hbm_cl_notify_stop_res ( struct mei_device * dev ,
struct mei_cl * cl ,
struct mei_hbm_cl_cmd * cmd )
{
struct hbm_notification_response * rs =
( struct hbm_notification_response * ) cmd ;
cl_dbg ( dev , cl , " hbm: notify stop response status=%d \n " , rs - > status ) ;
if ( rs - > status = = MEI_HBMS_SUCCESS | |
rs - > status = = MEI_HBMS_NOT_STARTED ) {
cl - > notify_en = false ;
cl - > status = 0 ;
} else {
/* TODO: spec is not clear yet about other possible issues */
cl - > status = - EINVAL ;
}
}
/**
* mei_hbm_cl_notify - signal notification event
*
* @ dev : the device structure
* @ cmd : notification client message
*/
static void mei_hbm_cl_notify ( struct mei_device * dev ,
struct mei_hbm_cl_cmd * cmd )
{
struct mei_cl * cl ;
cl = mei_hbm_cl_find_by_cmd ( dev , cmd ) ;
2015-07-26 09:54:22 +03:00
if ( cl )
mei_cl_notify ( cl ) ;
2015-07-26 09:54:16 +03:00
}
2012-12-25 21:06:11 +04:00
/**
2013-04-04 20:05:05 +04:00
* mei_hbm_prop_req - request property for a single client
2012-12-25 21:06:11 +04:00
*
* @ dev : the device structure
2016-04-17 19:16:02 +03:00
* @ start_idx : client index to start search
2012-12-25 21:06:11 +04:00
*
2014-09-29 17:31:49 +04:00
* Return : 0 on success and < 0 on failure
2012-12-25 21:06:11 +04:00
*/
2016-04-17 19:16:02 +03:00
static int mei_hbm_prop_req ( struct mei_device * dev , unsigned long start_idx )
2012-12-25 21:06:07 +04:00
{
2016-05-09 07:07:47 +03:00
struct mei_msg_hdr mei_hdr ;
struct hbm_props_request prop_req ;
2012-12-25 21:06:07 +04:00
const size_t len = sizeof ( struct hbm_props_request ) ;
2016-04-17 19:16:02 +03:00
unsigned long addr ;
2014-01-08 22:19:21 +04:00
int ret ;
2012-12-25 21:06:07 +04:00
2016-04-17 19:16:02 +03:00
addr = find_next_bit ( dev - > me_clients_map , MEI_CLIENTS_MAX , start_idx ) ;
2012-12-25 21:06:07 +04:00
/* We got all client properties */
2016-04-17 19:16:02 +03:00
if ( addr = = MEI_CLIENTS_MAX ) {
2013-04-19 00:03:48 +04:00
dev - > hbm_state = MEI_HBM_STARTED ;
2016-02-08 00:35:43 +03:00
mei_host_client_init ( dev ) ;
2012-12-25 21:06:07 +04:00
return 0 ;
}
2016-05-09 07:07:47 +03:00
mei_hbm_hdr ( & mei_hdr , len ) ;
2012-12-25 21:06:07 +04:00
2016-05-09 07:07:47 +03:00
memset ( & prop_req , 0 , sizeof ( struct hbm_props_request ) ) ;
2012-12-25 21:06:07 +04:00
2016-05-09 07:07:47 +03:00
prop_req . hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD ;
prop_req . me_addr = addr ;
2012-12-25 21:06:07 +04:00
2016-05-09 07:07:47 +03:00
ret = mei_write_message ( dev , & mei_hdr , & prop_req ) ;
2014-01-08 22:19:21 +04:00
if ( ret ) {
2014-09-29 17:31:42 +04:00
dev_err ( dev - > dev , " properties request write failed: ret = %d \n " ,
2014-01-08 22:19:21 +04:00
ret ) ;
return ret ;
2012-12-25 21:06:07 +04:00
}
dev - > init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT ;
2016-09-25 13:25:31 +03:00
mei_schedule_stall_timer ( dev ) ;
2012-12-25 21:06:07 +04:00
return 0 ;
}
2015-10-13 15:02:39 +03:00
/**
2014-03-19 00:51:55 +04:00
* mei_hbm_pg - sends pg command
*
* @ dev : the device structure
* @ pg_cmd : the pg command code
*
2014-09-29 17:31:49 +04:00
* Return : - EIO on write failure
2014-08-21 15:29:21 +04:00
* - EOPNOTSUPP if the operation is not supported by the protocol
2014-03-19 00:51:55 +04:00
*/
int mei_hbm_pg ( struct mei_device * dev , u8 pg_cmd )
{
2016-05-09 07:07:47 +03:00
struct mei_msg_hdr mei_hdr ;
struct hbm_power_gate req ;
2014-03-19 00:51:55 +04:00
const size_t len = sizeof ( struct hbm_power_gate ) ;
int ret ;
2014-08-21 15:29:21 +04:00
if ( ! dev - > hbm_f_pg_supported )
return - EOPNOTSUPP ;
2016-05-09 07:07:47 +03:00
mei_hbm_hdr ( & mei_hdr , len ) ;
2014-03-19 00:51:55 +04:00
2016-05-09 07:07:47 +03:00
memset ( & req , 0 , len ) ;
req . hbm_cmd = pg_cmd ;
2014-03-19 00:51:55 +04:00
2016-05-09 07:07:47 +03:00
ret = mei_write_message ( dev , & mei_hdr , & req ) ;
2014-03-19 00:51:55 +04:00
if ( ret )
2014-09-29 17:31:42 +04:00
dev_err ( dev - > dev , " power gate command write failed. \n " ) ;
2014-03-19 00:51:55 +04:00
return ret ;
}
EXPORT_SYMBOL_GPL ( mei_hbm_pg ) ;
2012-12-25 21:06:10 +04:00
/**
2014-02-12 23:41:52 +04:00
* mei_hbm_stop_req - send stop request message
2012-12-25 21:06:10 +04:00
*
2014-09-29 17:31:49 +04:00
* @ dev : mei device
2014-02-12 23:41:52 +04:00
*
2014-09-29 17:31:49 +04:00
* Return : - EIO on write failure
2012-12-25 21:06:10 +04:00
*/
2014-02-12 23:41:52 +04:00
static int mei_hbm_stop_req ( struct mei_device * dev )
2012-12-25 21:06:10 +04:00
{
2016-05-09 07:07:47 +03:00
struct mei_msg_hdr mei_hdr ;
struct hbm_host_stop_request req ;
2012-12-25 21:06:10 +04:00
const size_t len = sizeof ( struct hbm_host_stop_request ) ;
2016-05-09 07:07:47 +03:00
mei_hbm_hdr ( & mei_hdr , len ) ;
2012-12-25 21:06:10 +04:00
2016-05-09 07:07:47 +03:00
memset ( & req , 0 , len ) ;
req . hbm_cmd = HOST_STOP_REQ_CMD ;
req . reason = DRIVER_STOP_REQUEST ;
2014-02-12 23:41:52 +04:00
2016-05-09 07:07:47 +03:00
return mei_write_message ( dev , & mei_hdr , & req ) ;
2012-12-25 21:06:10 +04:00
}
2012-12-25 21:06:07 +04:00
/**
2014-01-09 00:31:46 +04:00
* mei_hbm_cl_flow_control_req - sends flow control request .
2012-12-25 21:06:07 +04:00
*
* @ dev : the device structure
2012-12-25 21:06:11 +04:00
* @ cl : client info
2012-12-25 21:06:07 +04:00
*
2014-09-29 17:31:49 +04:00
* Return : - EIO on write failure
2012-12-25 21:06:07 +04:00
*/
2012-12-25 21:06:11 +04:00
int mei_hbm_cl_flow_control_req ( struct mei_device * dev , struct mei_cl * cl )
2012-12-25 21:06:07 +04:00
{
2017-03-20 16:04:04 +03:00
struct hbm_flow_control req ;
2014-09-29 17:31:37 +04:00
2014-03-16 16:35:55 +04:00
cl_dbg ( dev , cl , " sending flow control \n " ) ;
2017-03-20 16:04:04 +03:00
return mei_hbm_cl_write ( dev , cl , MEI_FLOW_CONTROL_CMD ,
& req , sizeof ( req ) ) ;
2012-12-25 21:06:07 +04:00
}
2012-12-25 21:06:12 +04:00
/**
2016-07-26 01:06:04 +03:00
* mei_hbm_add_single_tx_flow_ctrl_creds - adds single buffer credentials .
2012-12-25 21:06:12 +04:00
*
2013-04-04 20:05:05 +04:00
* @ dev : the device structure
2016-07-26 01:06:04 +03:00
* @ fctrl : flow control response bus message
2014-02-17 17:13:23 +04:00
*
2014-09-29 17:31:49 +04:00
* Return : 0 on success , < 0 otherwise
2012-12-25 21:06:12 +04:00
*/
2016-07-26 01:06:04 +03:00
static int mei_hbm_add_single_tx_flow_ctrl_creds ( struct mei_device * dev ,
struct hbm_flow_control * fctrl )
2012-12-25 21:06:12 +04:00
{
2014-02-17 17:13:23 +04:00
struct mei_me_client * me_cl ;
2015-01-11 01:07:16 +03:00
int rets ;
2014-02-17 17:13:23 +04:00
2016-07-26 01:06:04 +03:00
me_cl = mei_me_cl_by_id ( dev , fctrl - > me_addr ) ;
2014-08-24 13:08:55 +04:00
if ( ! me_cl ) {
2016-07-26 01:06:04 +03:00
dev_err ( dev - > dev , " no such me client %d \n " , fctrl - > me_addr ) ;
2014-08-24 13:08:55 +04:00
return - ENOENT ;
2014-02-17 17:13:23 +04:00
}
2015-01-11 01:07:16 +03:00
if ( WARN_ON ( me_cl - > props . single_recv_buf = = 0 ) ) {
rets = - EINVAL ;
goto out ;
}
2014-08-24 13:08:55 +04:00
2016-07-26 01:06:04 +03:00
me_cl - > tx_flow_ctrl_creds + + ;
2014-09-29 17:31:42 +04:00
dev_dbg ( dev - > dev , " recv flow ctrl msg ME %d (single) creds = %d. \n " ,
2016-07-26 01:06:04 +03:00
fctrl - > me_addr , me_cl - > tx_flow_ctrl_creds ) ;
2014-02-17 17:13:23 +04:00
2015-01-11 01:07:16 +03:00
rets = 0 ;
out :
mei_me_cl_put ( me_cl ) ;
return rets ;
2012-12-25 21:06:12 +04:00
}
/**
* mei_hbm_cl_flow_control_res - flow control response from me
*
* @ dev : the device structure
2016-07-26 01:06:04 +03:00
* @ fctrl : flow control response bus message
2012-12-25 21:06:12 +04:00
*/
2016-07-26 01:06:04 +03:00
static void mei_hbm_cl_tx_flow_ctrl_creds_res ( struct mei_device * dev ,
struct hbm_flow_control * fctrl )
2012-12-25 21:06:12 +04:00
{
2014-02-17 17:13:25 +04:00
struct mei_cl * cl ;
2012-12-25 21:06:12 +04:00
2016-07-26 01:06:04 +03:00
if ( ! fctrl - > host_addr ) {
2012-12-25 21:06:12 +04:00
/* single receive buffer */
2016-07-26 01:06:04 +03:00
mei_hbm_add_single_tx_flow_ctrl_creds ( dev , fctrl ) ;
2012-12-25 21:06:12 +04:00
return ;
}
2016-07-26 01:06:04 +03:00
cl = mei_hbm_cl_find_by_cmd ( dev , fctrl ) ;
2014-08-21 15:29:20 +04:00
if ( cl ) {
2016-07-26 01:06:04 +03:00
cl - > tx_flow_ctrl_creds + + ;
2014-08-21 15:29:20 +04:00
cl_dbg ( dev , cl , " flow control creds = %d. \n " ,
2016-07-26 01:06:04 +03:00
cl - > tx_flow_ctrl_creds ) ;
2012-12-25 21:06:12 +04:00
}
}
2012-12-25 21:06:07 +04:00
/**
2012-12-25 21:06:11 +04:00
* mei_hbm_cl_disconnect_req - sends disconnect message to fw .
2012-12-25 21:06:07 +04:00
*
* @ dev : the device structure
2012-12-25 21:06:11 +04:00
* @ cl : a client to disconnect from
2012-12-25 21:06:07 +04:00
*
2014-09-29 17:31:49 +04:00
* Return : - EIO on write failure
2012-12-25 21:06:07 +04:00
*/
2012-12-25 21:06:11 +04:00
int mei_hbm_cl_disconnect_req ( struct mei_device * dev , struct mei_cl * cl )
2012-12-25 21:06:07 +04:00
{
2017-03-20 16:04:04 +03:00
struct hbm_client_connect_request req ;
2014-09-29 17:31:37 +04:00
2017-03-20 16:04:04 +03:00
return mei_hbm_cl_write ( dev , cl , CLIENT_DISCONNECT_REQ_CMD ,
& req , sizeof ( req ) ) ;
2012-12-25 21:06:07 +04:00
}
2014-02-12 23:41:52 +04:00
/**
* mei_hbm_cl_disconnect_rsp - sends disconnect respose to the FW
*
* @ dev : the device structure
* @ cl : a client to disconnect from
*
2014-09-29 17:31:49 +04:00
* Return : - EIO on write failure
2014-02-12 23:41:52 +04:00
*/
int mei_hbm_cl_disconnect_rsp ( struct mei_device * dev , struct mei_cl * cl )
{
2017-03-20 16:04:04 +03:00
struct hbm_client_connect_response resp ;
2014-09-29 17:31:37 +04:00
2017-03-20 16:04:04 +03:00
return mei_hbm_cl_write ( dev , cl , CLIENT_DISCONNECT_RES_CMD ,
& resp , sizeof ( resp ) ) ;
2014-02-12 23:41:52 +04:00
}
2012-12-25 21:06:12 +04:00
/**
2014-08-21 15:29:18 +04:00
* mei_hbm_cl_disconnect_res - update the client state according
* disconnect response
2012-12-25 21:06:12 +04:00
*
2014-10-30 00:50:58 +03:00
* @ dev : the device structure
2014-08-21 15:29:18 +04:00
* @ cl : mei host client
* @ cmd : disconnect client response host bus message
2012-12-25 21:06:12 +04:00
*/
2014-10-30 00:50:58 +03:00
static void mei_hbm_cl_disconnect_res ( struct mei_device * dev , struct mei_cl * cl ,
2014-08-21 15:29:18 +04:00
struct mei_hbm_cl_cmd * cmd )
2012-12-25 21:06:12 +04:00
{
2014-08-21 15:29:18 +04:00
struct hbm_client_connect_response * rs =
( struct hbm_client_connect_response * ) cmd ;
2012-12-25 21:06:12 +04:00
2014-10-30 00:50:58 +03:00
cl_dbg ( dev , cl , " hbm: disconnect response status=%d \n " , rs - > status ) ;
2012-12-25 21:06:12 +04:00
2014-08-21 15:29:18 +04:00
if ( rs - > status = = MEI_CL_DISCONN_SUCCESS )
2015-05-04 09:43:52 +03:00
cl - > state = MEI_FILE_DISCONNECT_REPLY ;
2014-08-21 15:29:18 +04:00
cl - > status = 0 ;
2012-12-25 21:06:12 +04:00
}
2012-12-25 21:06:07 +04:00
/**
2012-12-25 21:06:11 +04:00
* mei_hbm_cl_connect_req - send connection request to specific me client
2012-12-25 21:06:07 +04:00
*
* @ dev : the device structure
2012-12-25 21:06:11 +04:00
* @ cl : a client to connect to
2012-12-25 21:06:07 +04:00
*
2014-09-29 17:31:49 +04:00
* Return : - EIO on write failure
2012-12-25 21:06:07 +04:00
*/
2012-12-25 21:06:11 +04:00
int mei_hbm_cl_connect_req ( struct mei_device * dev , struct mei_cl * cl )
2012-12-25 21:06:07 +04:00
{
2017-03-20 16:04:04 +03:00
struct hbm_client_connect_request req ;
2014-09-29 17:31:37 +04:00
2017-03-20 16:04:04 +03:00
return mei_hbm_cl_write ( dev , cl , CLIENT_CONNECT_REQ_CMD ,
& req , sizeof ( req ) ) ;
2012-12-25 21:06:07 +04:00
}
2012-12-25 21:06:12 +04:00
/**
2014-08-21 15:29:18 +04:00
* mei_hbm_cl_connect_res - update the client state according
* connection response
2012-12-25 21:06:12 +04:00
*
2014-10-30 00:50:58 +03:00
* @ dev : the device structure
2014-08-21 15:29:18 +04:00
* @ cl : mei host client
* @ cmd : connect client response host bus message
2012-12-25 21:06:12 +04:00
*/
2014-10-30 00:50:58 +03:00
static void mei_hbm_cl_connect_res ( struct mei_device * dev , struct mei_cl * cl ,
2014-08-21 15:29:18 +04:00
struct mei_hbm_cl_cmd * cmd )
2012-12-25 21:06:12 +04:00
{
2014-08-21 15:29:18 +04:00
struct hbm_client_connect_response * rs =
( struct hbm_client_connect_response * ) cmd ;
2012-12-25 21:06:12 +04:00
2014-10-30 00:50:58 +03:00
cl_dbg ( dev , cl , " hbm: connect response status=%s \n " ,
2014-02-17 17:13:20 +04:00
mei_cl_conn_status_str ( rs - > status ) ) ;
2012-12-25 21:06:12 +04:00
2014-08-21 15:29:18 +04:00
if ( rs - > status = = MEI_CL_CONN_SUCCESS )
cl - > state = MEI_FILE_CONNECTED ;
2015-07-23 21:37:12 +03:00
else {
2015-05-04 09:43:52 +03:00
cl - > state = MEI_FILE_DISCONNECT_REPLY ;
2016-02-08 00:35:41 +03:00
if ( rs - > status = = MEI_CL_CONN_NOT_FOUND ) {
2015-07-23 21:37:12 +03:00
mei_me_cl_del ( dev , cl - > me_cl ) ;
2016-02-08 00:35:41 +03:00
if ( dev - > dev_state = = MEI_DEV_ENABLED )
schedule_work ( & dev - > bus_rescan_work ) ;
}
2015-07-23 21:37:12 +03:00
}
2014-08-21 15:29:18 +04:00
cl - > status = mei_cl_conn_status_to_errno ( rs - > status ) ;
}
/**
* mei_hbm_cl_res - process hbm response received on behalf
* an client
*
* @ dev : the device structure
* @ rs : hbm client message
* @ fop_type : file operation type
*/
static void mei_hbm_cl_res ( struct mei_device * dev ,
struct mei_hbm_cl_cmd * rs ,
enum mei_cb_file_ops fop_type )
{
struct mei_cl * cl ;
struct mei_cl_cb * cb , * next ;
2012-12-25 21:06:12 +04:00
2014-08-21 15:29:18 +04:00
cl = NULL ;
2017-01-27 17:32:45 +03:00
list_for_each_entry_safe ( cb , next , & dev - > ctrl_rd_list , list ) {
2012-12-25 21:06:12 +04:00
2014-02-17 17:13:21 +04:00
cl = cb - > cl ;
2012-12-25 21:06:12 +04:00
2014-08-21 15:29:18 +04:00
if ( cb - > fop_type ! = fop_type )
2014-02-17 17:13:21 +04:00
continue ;
2012-12-25 21:06:12 +04:00
2014-02-17 17:13:21 +04:00
if ( mei_hbm_cl_addr_equal ( cl , rs ) ) {
2015-02-10 11:39:45 +03:00
list_del_init ( & cb - > list ) ;
2014-02-17 17:13:21 +04:00
break ;
2012-12-25 21:06:12 +04:00
}
}
2014-02-17 17:13:21 +04:00
if ( ! cl )
return ;
2014-08-21 15:29:18 +04:00
switch ( fop_type ) {
case MEI_FOP_CONNECT :
2014-10-30 00:50:58 +03:00
mei_hbm_cl_connect_res ( dev , cl , rs ) ;
2014-08-21 15:29:18 +04:00
break ;
case MEI_FOP_DISCONNECT :
2014-10-30 00:50:58 +03:00
mei_hbm_cl_disconnect_res ( dev , cl , rs ) ;
2014-08-21 15:29:18 +04:00
break ;
2015-07-26 09:54:16 +03:00
case MEI_FOP_NOTIFY_START :
mei_hbm_cl_notify_start_res ( dev , cl , rs ) ;
break ;
case MEI_FOP_NOTIFY_STOP :
mei_hbm_cl_notify_stop_res ( dev , cl , rs ) ;
break ;
2014-08-21 15:29:18 +04:00
default :
return ;
}
2014-02-17 17:13:21 +04:00
cl - > timer_count = 0 ;
2014-08-21 15:29:18 +04:00
wake_up ( & cl - > wait ) ;
2012-12-25 21:06:12 +04:00
}
2012-12-25 21:06:07 +04:00
/**
2014-01-09 00:31:46 +04:00
* mei_hbm_fw_disconnect_req - disconnect request initiated by ME firmware
* host sends disconnect response
2012-12-25 21:06:07 +04:00
*
* @ dev : the device structure .
2012-12-25 21:06:11 +04:00
* @ disconnect_req : disconnect request bus message from the me
2014-02-12 23:41:52 +04:00
*
2014-09-29 17:31:49 +04:00
* Return : - ENOMEM on allocation failure
2012-12-25 21:06:07 +04:00
*/
2014-02-12 23:41:52 +04:00
static int mei_hbm_fw_disconnect_req ( struct mei_device * dev ,
2012-12-25 21:06:07 +04:00
struct hbm_client_connect_request * disconnect_req )
{
2014-02-17 17:13:25 +04:00
struct mei_cl * cl ;
2014-02-12 23:41:52 +04:00
struct mei_cl_cb * cb ;
2012-12-25 21:06:07 +04:00
2014-08-21 15:29:20 +04:00
cl = mei_hbm_cl_find_by_cmd ( dev , disconnect_req ) ;
if ( cl ) {
2016-02-08 00:35:39 +03:00
cl_warn ( dev , cl , " fw disconnect request received \n " ) ;
2015-05-04 09:43:52 +03:00
cl - > state = MEI_FILE_DISCONNECTING ;
2014-08-21 15:29:20 +04:00
cl - > timer_count = 0 ;
2016-07-26 01:06:05 +03:00
cb = mei_cl_enqueue_ctrl_wr_cb ( cl , 0 , MEI_FOP_DISCONNECT_RSP ,
NULL ) ;
2014-08-21 15:29:20 +04:00
if ( ! cb )
return - ENOMEM ;
2012-12-25 21:06:07 +04:00
}
2014-02-12 23:41:52 +04:00
return 0 ;
2012-12-25 21:06:07 +04:00
}
2015-08-02 22:20:53 +03:00
/**
* mei_hbm_pg_enter_res - PG enter response received
*
* @ dev : the device structure .
*
* Return : 0 on success , - EPROTO on state mismatch
*/
static int mei_hbm_pg_enter_res ( struct mei_device * dev )
{
if ( mei_pg_state ( dev ) ! = MEI_PG_OFF | |
dev - > pg_event ! = MEI_PG_EVENT_WAIT ) {
dev_err ( dev - > dev , " hbm: pg entry response: state mismatch [%s, %d] \n " ,
mei_pg_state_str ( mei_pg_state ( dev ) ) , dev - > pg_event ) ;
return - EPROTO ;
}
dev - > pg_event = MEI_PG_EVENT_RECEIVED ;
wake_up ( & dev - > wait_pg ) ;
return 0 ;
}
2015-08-02 22:20:54 +03:00
/**
* mei_hbm_pg_resume - process with PG resume
*
* @ dev : the device structure .
*/
void mei_hbm_pg_resume ( struct mei_device * dev )
{
pm_request_resume ( dev - > dev ) ;
}
EXPORT_SYMBOL_GPL ( mei_hbm_pg_resume ) ;
2015-08-02 22:20:53 +03:00
/**
* mei_hbm_pg_exit_res - PG exit response received
*
* @ dev : the device structure .
*
* Return : 0 on success , - EPROTO on state mismatch
*/
static int mei_hbm_pg_exit_res ( struct mei_device * dev )
{
if ( mei_pg_state ( dev ) ! = MEI_PG_ON | |
( dev - > pg_event ! = MEI_PG_EVENT_WAIT & &
dev - > pg_event ! = MEI_PG_EVENT_IDLE ) ) {
dev_err ( dev - > dev , " hbm: pg exit response: state mismatch [%s, %d] \n " ,
mei_pg_state_str ( mei_pg_state ( dev ) ) , dev - > pg_event ) ;
return - EPROTO ;
}
switch ( dev - > pg_event ) {
case MEI_PG_EVENT_WAIT :
dev - > pg_event = MEI_PG_EVENT_RECEIVED ;
wake_up ( & dev - > wait_pg ) ;
break ;
case MEI_PG_EVENT_IDLE :
/*
* If the driver is not waiting on this then
* this is HW initiated exit from PG .
* Start runtime pm resume sequence to exit from PG .
*/
dev - > pg_event = MEI_PG_EVENT_RECEIVED ;
2015-08-02 22:20:54 +03:00
mei_hbm_pg_resume ( dev ) ;
2015-08-02 22:20:53 +03:00
break ;
default :
WARN ( 1 , " hbm: pg exit response: unexpected pg event = %d \n " ,
dev - > pg_event ) ;
return - EPROTO ;
}
return 0 ;
}
2014-08-21 15:29:21 +04:00
/**
2014-09-29 17:31:49 +04:00
* mei_hbm_config_features - check what hbm features and commands
2014-08-21 15:29:21 +04:00
* are supported by the fw
*
* @ dev : the device structure
*/
static void mei_hbm_config_features ( struct mei_device * dev )
{
/* Power Gating Isolation Support */
dev - > hbm_f_pg_supported = 0 ;
if ( dev - > version . major_version > HBM_MAJOR_VERSION_PGI )
dev - > hbm_f_pg_supported = 1 ;
if ( dev - > version . major_version = = HBM_MAJOR_VERSION_PGI & &
dev - > version . minor_version > = HBM_MINOR_VERSION_PGI )
dev - > hbm_f_pg_supported = 1 ;
2015-07-23 21:37:12 +03:00
if ( dev - > version . major_version > = HBM_MAJOR_VERSION_DC )
dev - > hbm_f_dc_supported = 1 ;
2015-07-23 21:37:13 +03:00
2016-02-08 00:35:42 +03:00
if ( dev - > version . major_version > = HBM_MAJOR_VERSION_IE )
dev - > hbm_f_ie_supported = 1 ;
2015-07-23 21:37:13 +03:00
/* disconnect on connect timeout instead of link reset */
if ( dev - > version . major_version > = HBM_MAJOR_VERSION_DOT )
dev - > hbm_f_dot_supported = 1 ;
2015-07-26 09:54:17 +03:00
/* Notification Event Support */
if ( dev - > version . major_version > = HBM_MAJOR_VERSION_EV )
dev - > hbm_f_ev_supported = 1 ;
2016-02-08 00:35:38 +03:00
/* Fixed Address Client Support */
if ( dev - > version . major_version > = HBM_MAJOR_VERSION_FA )
dev - > hbm_f_fa_supported = 1 ;
2017-01-11 02:27:21 +03:00
/* OS ver message Support */
if ( dev - > version . major_version > = HBM_MAJOR_VERSION_OS )
dev - > hbm_f_os_supported = 1 ;
2014-08-21 15:29:21 +04:00
}
2012-12-25 21:06:07 +04:00
2013-06-16 10:16:31 +04:00
/**
* mei_hbm_version_is_supported - checks whether the driver can
* support the hbm version of the device
*
* @ dev : the device structure
2014-09-29 17:31:49 +04:00
* Return : true if driver can support hbm version of the device
2013-06-16 10:16:31 +04:00
*/
bool mei_hbm_version_is_supported ( struct mei_device * dev )
{
return ( dev - > version . major_version < HBM_MAJOR_VERSION ) | |
( dev - > version . major_version = = HBM_MAJOR_VERSION & &
dev - > version . minor_version < = HBM_MINOR_VERSION ) ;
}
2012-12-25 21:06:07 +04:00
/**
* mei_hbm_dispatch - bottom half read routine after ISR to
* handle the read bus message cmd processing .
*
* @ dev : the device structure
2014-09-29 17:31:49 +04:00
* @ hdr : header of bus message
2014-01-08 22:19:21 +04:00
*
2014-09-29 17:31:49 +04:00
* Return : 0 on success and < 0 on failure
2012-12-25 21:06:07 +04:00
*/
2014-01-08 22:19:21 +04:00
int mei_hbm_dispatch ( struct mei_device * dev , struct mei_msg_hdr * hdr )
2012-12-25 21:06:07 +04:00
{
struct mei_bus_message * mei_msg ;
struct hbm_host_version_response * version_res ;
struct hbm_props_response * props_res ;
struct hbm_host_enum_response * enum_res ;
2015-07-23 21:37:12 +03:00
struct hbm_add_client_request * add_cl_req ;
int ret ;
2012-12-25 21:06:07 +04:00
2014-08-21 15:29:18 +04:00
struct mei_hbm_cl_cmd * cl_cmd ;
struct hbm_client_connect_request * disconnect_req ;
2016-07-26 01:06:04 +03:00
struct hbm_flow_control * fctrl ;
2014-08-21 15:29:18 +04:00
2012-12-25 21:06:07 +04:00
/* read the message to our buffer */
BUG_ON ( hdr - > length > = sizeof ( dev - > rd_msg_buf ) ) ;
mei_read_slots ( dev , dev - > rd_msg_buf , hdr - > length ) ;
mei_msg = ( struct mei_bus_message * ) dev - > rd_msg_buf ;
2014-08-21 15:29:18 +04:00
cl_cmd = ( struct mei_hbm_cl_cmd * ) mei_msg ;
2012-12-25 21:06:07 +04:00
2014-01-08 22:19:22 +04:00
/* ignore spurious message and prevent reset nesting
* hbm is put to idle during system reset
*/
if ( dev - > hbm_state = = MEI_HBM_IDLE ) {
2014-09-29 17:31:42 +04:00
dev_dbg ( dev - > dev , " hbm: state is idle ignore spurious messages \n " ) ;
2014-01-08 22:19:22 +04:00
return 0 ;
}
2012-12-25 21:06:07 +04:00
switch ( mei_msg - > hbm_cmd ) {
case HOST_START_RES_CMD :
2014-09-29 17:31:42 +04:00
dev_dbg ( dev - > dev , " hbm: start: response message received. \n " ) ;
2014-01-08 22:19:21 +04:00
dev - > init_clients_timer = 0 ;
2012-12-25 21:06:07 +04:00
version_res = ( struct hbm_host_version_response * ) mei_msg ;
2013-06-16 10:16:31 +04:00
2014-09-29 17:31:42 +04:00
dev_dbg ( dev - > dev , " HBM VERSION: DRIVER=%02d:%02d DEVICE=%02d:%02d \n " ,
2013-06-16 10:16:31 +04:00
HBM_MAJOR_VERSION , HBM_MINOR_VERSION ,
version_res - > me_max_version . major_version ,
version_res - > me_max_version . minor_version ) ;
if ( version_res - > host_version_supported ) {
dev - > version . major_version = HBM_MAJOR_VERSION ;
dev - > version . minor_version = HBM_MINOR_VERSION ;
} else {
dev - > version . major_version =
version_res - > me_max_version . major_version ;
dev - > version . minor_version =
version_res - > me_max_version . minor_version ;
}
if ( ! mei_hbm_version_is_supported ( dev ) ) {
2014-09-29 17:31:42 +04:00
dev_warn ( dev - > dev , " hbm: start: version mismatch - stopping the driver. \n " ) ;
2012-12-25 21:06:07 +04:00
2014-01-08 22:19:21 +04:00
dev - > hbm_state = MEI_HBM_STOPPED ;
2014-02-12 23:41:52 +04:00
if ( mei_hbm_stop_req ( dev ) ) {
2014-09-29 17:31:42 +04:00
dev_err ( dev - > dev , " hbm: start: failed to send stop request \n " ) ;
2014-01-08 22:19:21 +04:00
return - EIO ;
}
break ;
}
2013-04-19 00:03:48 +04:00
2014-08-21 15:29:21 +04:00
mei_hbm_config_features ( dev ) ;
2014-01-08 22:19:21 +04:00
if ( dev - > dev_state ! = MEI_DEV_INIT_CLIENTS | |
2014-08-21 15:29:19 +04:00
dev - > hbm_state ! = MEI_HBM_STARTING ) {
2014-09-29 17:31:42 +04:00
dev_err ( dev - > dev , " hbm: start: state mismatch, [%d, %d] \n " ,
2014-01-08 22:19:21 +04:00
dev - > dev_state , dev - > hbm_state ) ;
return - EPROTO ;
2012-12-25 21:06:10 +04:00
}
2012-12-25 21:06:07 +04:00
2014-01-08 22:19:21 +04:00
if ( mei_hbm_enum_clients_req ( dev ) ) {
2014-09-29 17:31:42 +04:00
dev_err ( dev - > dev , " hbm: start: failed to send enumeration request \n " ) ;
2014-01-08 22:19:21 +04:00
return - EIO ;
2012-12-25 21:06:07 +04:00
}
2014-08-21 15:29:19 +04:00
wake_up ( & dev - > wait_hbm_start ) ;
2012-12-25 21:06:07 +04:00
break ;
case CLIENT_CONNECT_RES_CMD :
2014-09-29 17:31:42 +04:00
dev_dbg ( dev - > dev , " hbm: client connect response: message received. \n " ) ;
2014-08-21 15:29:18 +04:00
mei_hbm_cl_res ( dev , cl_cmd , MEI_FOP_CONNECT ) ;
2012-12-25 21:06:07 +04:00
break ;
case CLIENT_DISCONNECT_RES_CMD :
2014-09-29 17:31:42 +04:00
dev_dbg ( dev - > dev , " hbm: client disconnect response: message received. \n " ) ;
2014-08-21 15:29:18 +04:00
mei_hbm_cl_res ( dev , cl_cmd , MEI_FOP_DISCONNECT ) ;
2012-12-25 21:06:07 +04:00
break ;
case MEI_FLOW_CONTROL_CMD :
2014-09-29 17:31:42 +04:00
dev_dbg ( dev - > dev , " hbm: client flow control response: message received. \n " ) ;
2014-01-08 22:19:21 +04:00
2016-07-26 01:06:04 +03:00
fctrl = ( struct hbm_flow_control * ) mei_msg ;
mei_hbm_cl_tx_flow_ctrl_creds_res ( dev , fctrl ) ;
2012-12-25 21:06:07 +04:00
break ;
2014-03-19 00:51:55 +04:00
case MEI_PG_ISOLATION_ENTRY_RES_CMD :
2015-08-02 22:20:53 +03:00
dev_dbg ( dev - > dev , " hbm: power gate isolation entry response received \n " ) ;
ret = mei_hbm_pg_enter_res ( dev ) ;
if ( ret )
return ret ;
2014-03-19 00:51:55 +04:00
break ;
case MEI_PG_ISOLATION_EXIT_REQ_CMD :
2015-08-02 22:20:53 +03:00
dev_dbg ( dev - > dev , " hbm: power gate isolation exit request received \n " ) ;
ret = mei_hbm_pg_exit_res ( dev ) ;
if ( ret )
return ret ;
2014-03-19 00:51:55 +04:00
break ;
2012-12-25 21:06:07 +04:00
case HOST_CLIENT_PROPERTIES_RES_CMD :
2014-09-29 17:31:42 +04:00
dev_dbg ( dev - > dev , " hbm: properties response: message received. \n " ) ;
2014-01-08 22:19:21 +04:00
dev - > init_clients_timer = 0 ;
2014-08-21 15:29:13 +04:00
if ( dev - > dev_state ! = MEI_DEV_INIT_CLIENTS | |
dev - > hbm_state ! = MEI_HBM_CLIENT_PROPERTIES ) {
2014-09-29 17:31:42 +04:00
dev_err ( dev - > dev , " hbm: properties response: state mismatch, [%d, %d] \n " ,
2014-08-21 15:29:13 +04:00
dev - > dev_state , dev - > hbm_state ) ;
2014-01-08 22:19:21 +04:00
return - EPROTO ;
}
2012-12-25 21:06:07 +04:00
props_res = ( struct hbm_props_response * ) mei_msg ;
2014-01-08 22:19:21 +04:00
if ( props_res - > status ) {
2014-09-29 17:31:42 +04:00
dev_err ( dev - > dev , " hbm: properties response: wrong status = %d %s \n " ,
2014-08-21 15:29:16 +04:00
props_res - > status ,
mei_hbm_status_str ( props_res - > status ) ) ;
2014-01-08 22:19:21 +04:00
return - EPROTO ;
2012-12-25 21:06:07 +04:00
}
2014-08-21 15:29:13 +04:00
mei_hbm_me_cl_add ( dev , props_res ) ;
2012-12-25 21:06:07 +04:00
2012-12-25 21:06:11 +04:00
/* request property for the next client */
2016-04-17 19:16:02 +03:00
if ( mei_hbm_prop_req ( dev , props_res - > me_addr + 1 ) )
2014-01-08 22:19:21 +04:00
return - EIO ;
2012-12-25 21:06:07 +04:00
break ;
case HOST_ENUM_RES_CMD :
2014-09-29 17:31:42 +04:00
dev_dbg ( dev - > dev , " hbm: enumeration response: message received \n " ) ;
2014-01-08 22:19:21 +04:00
dev - > init_clients_timer = 0 ;
2012-12-25 21:06:07 +04:00
enum_res = ( struct hbm_host_enum_response * ) mei_msg ;
2013-09-02 04:11:01 +04:00
BUILD_BUG_ON ( sizeof ( dev - > me_clients_map )
< sizeof ( enum_res - > valid_addresses ) ) ;
memcpy ( dev - > me_clients_map , enum_res - > valid_addresses ,
2014-08-21 15:29:13 +04:00
sizeof ( enum_res - > valid_addresses ) ) ;
2014-01-08 22:19:21 +04:00
if ( dev - > dev_state ! = MEI_DEV_INIT_CLIENTS | |
dev - > hbm_state ! = MEI_HBM_ENUM_CLIENTS ) {
2014-09-29 17:31:42 +04:00
dev_err ( dev - > dev , " hbm: enumeration response: state mismatch, [%d, %d] \n " ,
2014-01-08 22:19:21 +04:00
dev - > dev_state , dev - > hbm_state ) ;
return - EPROTO ;
2012-12-25 21:06:07 +04:00
}
2014-01-08 22:19:21 +04:00
dev - > hbm_state = MEI_HBM_CLIENT_PROPERTIES ;
/* first property request */
2016-04-17 19:16:02 +03:00
if ( mei_hbm_prop_req ( dev , 0 ) )
2014-01-08 22:19:21 +04:00
return - EIO ;
2012-12-25 21:06:07 +04:00
break ;
case HOST_STOP_RES_CMD :
2014-09-29 17:31:42 +04:00
dev_dbg ( dev - > dev , " hbm: stop response: message received \n " ) ;
2014-01-08 22:19:21 +04:00
dev - > init_clients_timer = 0 ;
if ( dev - > hbm_state ! = MEI_HBM_STOPPED ) {
2014-09-29 17:31:42 +04:00
dev_err ( dev - > dev , " hbm: stop response: state mismatch, [%d, %d] \n " ,
2014-01-08 22:19:21 +04:00
dev - > dev_state , dev - > hbm_state ) ;
return - EPROTO ;
}
2013-04-19 00:03:48 +04:00
2014-01-12 02:36:09 +04:00
dev - > dev_state = MEI_DEV_POWER_DOWN ;
2014-09-29 17:31:42 +04:00
dev_info ( dev - > dev , " hbm: stop response: resetting. \n " ) ;
2014-01-08 22:19:21 +04:00
/* force the reset */
return - EPROTO ;
2012-12-25 21:06:07 +04:00
break ;
case CLIENT_DISCONNECT_REQ_CMD :
2014-09-29 17:31:42 +04:00
dev_dbg ( dev - > dev , " hbm: disconnect request: message received \n " ) ;
2014-01-08 22:19:21 +04:00
2012-12-25 21:06:07 +04:00
disconnect_req = ( struct hbm_client_connect_request * ) mei_msg ;
2012-12-25 21:06:11 +04:00
mei_hbm_fw_disconnect_req ( dev , disconnect_req ) ;
2012-12-25 21:06:07 +04:00
break ;
case ME_STOP_REQ_CMD :
2014-09-29 17:31:42 +04:00
dev_dbg ( dev - > dev , " hbm: stop request: message received \n " ) ;
2014-01-08 22:19:21 +04:00
dev - > hbm_state = MEI_HBM_STOPPED ;
2014-02-12 23:41:52 +04:00
if ( mei_hbm_stop_req ( dev ) ) {
2014-09-29 17:31:42 +04:00
dev_err ( dev - > dev , " hbm: stop request: failed to send stop request \n " ) ;
2014-02-12 23:41:52 +04:00
return - EIO ;
}
2012-12-25 21:06:07 +04:00
break ;
2015-07-23 21:37:12 +03:00
case MEI_HBM_ADD_CLIENT_REQ_CMD :
dev_dbg ( dev - > dev , " hbm: add client request received \n " ) ;
/*
* after the host receives the enum_resp
* message clients may be added or removed
*/
2015-09-21 11:45:32 +03:00
if ( dev - > hbm_state < = MEI_HBM_ENUM_CLIENTS | |
2015-07-23 21:37:12 +03:00
dev - > hbm_state > = MEI_HBM_STOPPED ) {
dev_err ( dev - > dev , " hbm: add client: state mismatch, [%d, %d] \n " ,
dev - > dev_state , dev - > hbm_state ) ;
return - EPROTO ;
}
add_cl_req = ( struct hbm_add_client_request * ) mei_msg ;
ret = mei_hbm_fw_add_cl_req ( dev , add_cl_req ) ;
if ( ret ) {
dev_err ( dev - > dev , " hbm: add client: failed to send response %d \n " ,
ret ) ;
return - EIO ;
}
dev_dbg ( dev - > dev , " hbm: add client request processed \n " ) ;
break ;
2015-07-26 09:54:16 +03:00
case MEI_HBM_NOTIFY_RES_CMD :
dev_dbg ( dev - > dev , " hbm: notify response received \n " ) ;
mei_hbm_cl_res ( dev , cl_cmd , notify_res_to_fop ( cl_cmd ) ) ;
break ;
case MEI_HBM_NOTIFICATION_CMD :
dev_dbg ( dev - > dev , " hbm: notification \n " ) ;
mei_hbm_cl_notify ( dev , cl_cmd ) ;
break ;
2012-12-25 21:06:07 +04:00
default :
BUG ( ) ;
break ;
}
2014-01-08 22:19:21 +04:00
return 0 ;
2012-12-25 21:06:07 +04:00
}