2011-05-15 14:43:47 +04:00
/*
*
* Intel Management Engine Interface ( Intel MEI ) Linux driver
2012-02-09 21:25:53 +04:00
* Copyright ( c ) 2003 - 2012 , Intel Corporation .
2011-05-15 14:43:47 +04:00
*
* 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 .
*
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/device.h>
# include <linux/sched.h>
2011-09-07 10:03:09 +04:00
# include <linux/watchdog.h>
2011-05-15 14:43:47 +04:00
2012-12-25 21:06:03 +04:00
# include <linux/mei.h>
2011-05-15 14:43:47 +04:00
# include "mei_dev.h"
2013-01-09 01:07:12 +04:00
# include "hbm.h"
2013-01-09 01:07:14 +04:00
# include "client.h"
2011-05-15 14:43:47 +04:00
static const u8 mei_start_wd_params [ ] = { 0x02 , 0x12 , 0x13 , 0x10 } ;
static const u8 mei_stop_wd_params [ ] = { 0x02 , 0x02 , 0x14 , 0x10 } ;
2011-12-22 20:50:50 +04:00
/*
* AMT Watchdog Device
*/
# define INTEL_AMT_WATCHDOG_ID "INTCAMT"
2011-05-15 14:43:47 +04:00
/* UUIDs for AMT F/W clients */
const uuid_le mei_wd_guid = UUID_LE ( 0x05B79A6F , 0x4628 , 0x4D7F , 0x89 ,
0x9D , 0xA9 , 0x15 , 0x14 , 0xCB ,
0x32 , 0xAB ) ;
2012-03-19 19:58:43 +04:00
static void mei_wd_set_start_timeout ( struct mei_device * dev , u16 timeout )
2011-05-15 14:43:47 +04:00
{
2014-09-29 17:31:42 +04:00
dev_dbg ( dev - > dev , " wd: set timeout=%d. \n " , timeout ) ;
2012-08-16 20:39:42 +04:00
memcpy ( dev - > wd_data , mei_start_wd_params , MEI_WD_HDR_SIZE ) ;
memcpy ( dev - > wd_data + MEI_WD_HDR_SIZE , & timeout , sizeof ( u16 ) ) ;
2011-05-15 14:43:47 +04:00
}
/**
2012-07-04 20:24:51 +04:00
* mei_wd_host_init - connect to the watchdog client
2011-05-15 14:43:47 +04:00
*
* @ dev : the device structure
2013-04-04 20:05:05 +04:00
*
2014-09-29 17:31:49 +04:00
* Return : - ENOTTY if wd client cannot be found
2012-04-04 00:34:58 +04:00
* - EIO if write has failed
2012-07-04 20:24:51 +04:00
* 0 on success
2011-05-15 14:43:47 +04:00
*/
2012-04-04 00:34:58 +04:00
int mei_wd_host_init ( struct mei_device * dev )
2011-05-15 14:43:47 +04:00
{
2013-01-09 01:07:22 +04:00
struct mei_cl * cl = & dev - > wd_cl ;
2014-08-24 13:08:55 +04:00
struct mei_me_client * me_cl ;
2013-01-09 01:07:22 +04:00
int ret ;
mei_cl_init ( cl , dev ) ;
2011-05-15 14:43:47 +04:00
2012-08-16 20:39:42 +04:00
dev - > wd_timeout = MEI_WD_DEFAULT_TIMEOUT ;
2012-08-16 20:39:43 +04:00
dev - > wd_state = MEI_WD_IDLE ;
2011-05-15 14:43:47 +04:00
2013-01-09 01:07:22 +04:00
/* check for valid client id */
2014-08-24 13:08:55 +04:00
me_cl = mei_me_cl_by_uuid ( dev , & mei_wd_guid ) ;
if ( ! me_cl ) {
2014-09-29 17:31:42 +04:00
dev_info ( dev - > dev , " wd: failed to find the client \n " ) ;
2014-02-19 19:35:49 +04:00
return - ENOTTY ;
2012-04-04 00:34:58 +04:00
}
2014-08-24 13:08:55 +04:00
cl - > me_client_id = me_cl - > client_id ;
2014-08-21 15:29:15 +04:00
cl - > cl_uuid = me_cl - > props . protocol_name ;
2015-01-11 01:07:16 +03:00
mei_me_cl_put ( me_cl ) ;
2013-01-09 01:07:22 +04:00
ret = mei_cl_link ( cl , MEI_WD_HOST_CLIENT_ID ) ;
if ( ret < 0 ) {
2014-09-29 17:31:42 +04:00
dev_info ( dev - > dev , " wd: failed link client \n " ) ;
2013-10-21 23:05:39 +04:00
return ret ;
2013-01-09 01:07:22 +04:00
}
2014-02-17 17:13:21 +04:00
ret = mei_cl_connect ( cl , NULL ) ;
if ( ret ) {
2014-09-29 17:31:42 +04:00
dev_err ( dev - > dev , " wd: failed to connect = %d \n " , ret ) ;
2014-02-17 17:13:21 +04:00
mei_cl_unlink ( cl ) ;
return ret ;
2011-05-15 14:43:47 +04:00
}
2011-09-07 10:03:07 +04:00
2014-02-17 17:13:21 +04:00
ret = mei_watchdog_register ( dev ) ;
if ( ret ) {
mei_cl_disconnect ( cl ) ;
mei_cl_unlink ( cl ) ;
}
return ret ;
2011-05-15 14:43:47 +04:00
}
/**
* mei_wd_send - sends watch dog message to fw .
*
* @ dev : the device structure
*
2014-09-29 17:31:49 +04:00
* Return : 0 if success ,
2011-05-15 14:43:47 +04:00
* - EIO when message send fails
* - EINVAL when invalid message is to be sent
2014-02-19 19:35:50 +04:00
* - ENODEV on flow control failure
2011-05-15 14:43:47 +04:00
*/
int mei_wd_send ( struct mei_device * dev )
{
2014-02-19 19:35:50 +04:00
struct mei_cl * cl = & dev - > wd_cl ;
2012-12-25 21:06:10 +04:00
struct mei_msg_hdr hdr ;
2014-02-19 19:35:50 +04:00
int ret ;
2011-05-15 14:43:47 +04:00
2014-02-19 19:35:50 +04:00
hdr . host_addr = cl - > host_client_id ;
hdr . me_addr = cl - > me_client_id ;
2012-12-25 21:06:10 +04:00
hdr . msg_complete = 1 ;
hdr . reserved = 0 ;
2013-12-17 17:56:56 +04:00
hdr . internal = 0 ;
2011-05-15 14:43:47 +04:00
2012-08-16 20:39:42 +04:00
if ( ! memcmp ( dev - > wd_data , mei_start_wd_params , MEI_WD_HDR_SIZE ) )
2012-12-25 21:06:10 +04:00
hdr . length = MEI_WD_START_MSG_SIZE ;
2012-08-16 20:39:42 +04:00
else if ( ! memcmp ( dev - > wd_data , mei_stop_wd_params , MEI_WD_HDR_SIZE ) )
2012-12-25 21:06:10 +04:00
hdr . length = MEI_WD_STOP_MSG_SIZE ;
2014-02-19 19:35:50 +04:00
else {
2014-09-29 17:31:42 +04:00
dev_err ( dev - > dev , " wd: invalid message is to be sent, aborting \n " ) ;
2011-05-15 14:43:47 +04:00
return - EINVAL ;
2014-02-19 19:35:50 +04:00
}
ret = mei_write_message ( dev , & hdr , dev - > wd_data ) ;
if ( ret ) {
2014-09-29 17:31:42 +04:00
dev_err ( dev - > dev , " wd: write message failed \n " ) ;
2014-02-19 19:35:50 +04:00
return ret ;
}
2011-05-15 14:43:47 +04:00
2014-02-19 19:35:50 +04:00
ret = mei_cl_flow_ctrl_reduce ( cl ) ;
if ( ret ) {
2014-09-29 17:31:42 +04:00
dev_err ( dev - > dev , " wd: flow_ctrl_reduce failed. \n " ) ;
2014-02-19 19:35:50 +04:00
return ret ;
}
return 0 ;
2011-05-15 14:43:47 +04:00
}
2011-09-07 10:03:17 +04:00
/**
* mei_wd_stop - sends watchdog stop message to fw .
*
* @ dev : the device structure
*
2014-09-29 17:31:49 +04:00
* Return : 0 if success
2014-02-19 19:35:51 +04:00
* on error :
* - EIO when message send fails
2011-09-07 10:03:17 +04:00
* - EINVAL when invalid message is to be sent
2014-02-19 19:35:51 +04:00
* - ETIME on message timeout
2011-09-07 10:03:17 +04:00
*/
2012-08-16 20:39:43 +04:00
int mei_wd_stop ( struct mei_device * dev )
2011-05-15 14:43:47 +04:00
{
int ret ;
2012-08-16 20:39:43 +04:00
if ( dev - > wd_cl . state ! = MEI_FILE_CONNECTED | |
dev - > wd_state ! = MEI_WD_RUNNING )
2011-05-15 14:43:47 +04:00
return 0 ;
2012-08-16 20:39:42 +04:00
memcpy ( dev - > wd_data , mei_stop_wd_params , MEI_WD_STOP_MSG_SIZE ) ;
2012-08-16 20:39:43 +04:00
dev - > wd_state = MEI_WD_STOPPING ;
2011-05-15 14:43:47 +04:00
2013-01-09 01:07:14 +04:00
ret = mei_cl_flow_ctrl_creds ( & dev - > wd_cl ) ;
2011-05-15 14:43:47 +04:00
if ( ret < 0 )
2014-02-19 19:35:51 +04:00
goto err ;
2011-05-15 14:43:47 +04:00
2014-02-19 19:35:47 +04:00
if ( ret & & mei_hbuf_acquire ( dev ) ) {
2014-02-19 19:35:50 +04:00
ret = mei_wd_send ( dev ) ;
if ( ret )
2014-02-19 19:35:51 +04:00
goto err ;
2011-05-25 18:28:22 +04:00
dev - > wd_pending = false ;
2011-05-15 14:43:47 +04:00
} else {
2011-05-25 18:28:22 +04:00
dev - > wd_pending = true ;
2011-05-15 14:43:47 +04:00
}
2012-08-16 20:39:43 +04:00
2011-05-15 14:43:47 +04:00
mutex_unlock ( & dev - > device_lock ) ;
2014-02-19 19:35:51 +04:00
ret = wait_event_timeout ( dev - > wait_stop_wd ,
dev - > wd_state = = MEI_WD_IDLE ,
msecs_to_jiffies ( MEI_WD_STOP_TIMEOUT ) ) ;
2011-05-15 14:43:47 +04:00
mutex_lock ( & dev - > device_lock ) ;
2014-02-19 19:35:51 +04:00
if ( dev - > wd_state ! = MEI_WD_IDLE ) {
/* timeout */
ret = - ETIME ;
2014-09-29 17:31:42 +04:00
dev_warn ( dev - > dev , " wd: stop failed to complete ret=%d \n " , ret ) ;
2014-02-19 19:35:51 +04:00
goto err ;
2011-06-13 17:39:31 +04:00
}
2014-09-29 17:31:42 +04:00
dev_dbg ( dev - > dev , " wd: stop completed after %u msec \n " ,
2014-02-19 19:35:51 +04:00
MEI_WD_STOP_TIMEOUT - jiffies_to_msecs ( ret ) ) ;
return 0 ;
err :
2011-05-15 14:43:47 +04:00
return ret ;
}
2015-02-10 11:39:35 +03:00
/**
2011-09-07 10:03:10 +04:00
* mei_wd_ops_start - wd start command from the watchdog core .
*
2015-02-10 11:39:35 +03:00
* @ wd_dev : watchdog device struct
2011-09-07 10:03:10 +04:00
*
2014-09-29 17:31:49 +04:00
* Return : 0 if success , negative errno code for failure
2011-09-07 10:03:10 +04:00
*/
static int mei_wd_ops_start ( struct watchdog_device * wd_dev )
{
int err = - ENODEV ;
struct mei_device * dev ;
2012-08-16 20:39:44 +04:00
dev = watchdog_get_drvdata ( wd_dev ) ;
2011-09-07 10:03:10 +04:00
if ( ! dev )
return - ENODEV ;
mutex_lock ( & dev - > device_lock ) ;
2012-08-07 01:03:56 +04:00
if ( dev - > dev_state ! = MEI_DEV_ENABLED ) {
2014-09-29 17:31:42 +04:00
dev_dbg ( dev - > dev , " wd: dev_state != MEI_DEV_ENABLED dev_state = %s \n " ,
2012-08-07 01:03:56 +04:00
mei_dev_state_str ( dev - > dev_state ) ) ;
2011-09-07 10:03:10 +04:00
goto end_unlock ;
}
if ( dev - > wd_cl . state ! = MEI_FILE_CONNECTED ) {
2014-09-29 17:31:42 +04:00
dev_dbg ( dev - > dev , " MEI Driver is not connected to Watchdog Client \n " ) ;
2011-09-07 10:03:10 +04:00
goto end_unlock ;
}
2011-09-07 10:03:12 +04:00
mei_wd_set_start_timeout ( dev , dev - > wd_timeout ) ;
2011-09-07 10:03:10 +04:00
err = 0 ;
end_unlock :
mutex_unlock ( & dev - > device_lock ) ;
return err ;
}
2015-02-10 11:39:35 +03:00
/**
2011-09-07 10:03:10 +04:00
* mei_wd_ops_stop - wd stop command from the watchdog core .
*
2015-02-10 11:39:35 +03:00
* @ wd_dev : watchdog device struct
2011-09-07 10:03:10 +04:00
*
2014-09-29 17:31:49 +04:00
* Return : 0 if success , negative errno code for failure
2011-09-07 10:03:10 +04:00
*/
static int mei_wd_ops_stop ( struct watchdog_device * wd_dev )
{
struct mei_device * dev ;
2012-08-16 20:39:44 +04:00
dev = watchdog_get_drvdata ( wd_dev ) ;
2011-09-07 10:03:10 +04:00
if ( ! dev )
return - ENODEV ;
mutex_lock ( & dev - > device_lock ) ;
2012-08-16 20:39:43 +04:00
mei_wd_stop ( dev ) ;
2011-09-07 10:03:10 +04:00
mutex_unlock ( & dev - > device_lock ) ;
return 0 ;
}
2015-02-10 11:39:35 +03:00
/**
2011-09-07 10:03:11 +04:00
* mei_wd_ops_ping - wd ping command from the watchdog core .
*
2015-02-10 11:39:35 +03:00
* @ wd_dev : watchdog device struct
2011-09-07 10:03:11 +04:00
*
2014-09-29 17:31:49 +04:00
* Return : 0 if success , negative errno code for failure
2011-09-07 10:03:11 +04:00
*/
static int mei_wd_ops_ping ( struct watchdog_device * wd_dev )
{
struct mei_device * dev ;
2014-10-02 14:39:32 +04:00
struct mei_cl * cl ;
2014-02-19 19:35:47 +04:00
int ret ;
2011-09-07 10:03:11 +04:00
2012-08-16 20:39:44 +04:00
dev = watchdog_get_drvdata ( wd_dev ) ;
2011-09-07 10:03:11 +04:00
if ( ! dev )
return - ENODEV ;
2014-10-02 14:39:32 +04:00
cl = & dev - > wd_cl ;
2011-09-07 10:03:11 +04:00
mutex_lock ( & dev - > device_lock ) ;
2014-10-02 14:39:32 +04:00
if ( cl - > state ! = MEI_FILE_CONNECTED ) {
2014-09-29 17:31:42 +04:00
dev_err ( dev - > dev , " wd: not connected. \n " ) ;
2011-09-07 10:03:11 +04:00
ret = - ENODEV ;
goto end ;
}
2012-08-16 20:39:43 +04:00
dev - > wd_state = MEI_WD_RUNNING ;
2014-10-02 14:39:32 +04:00
ret = mei_cl_flow_ctrl_creds ( cl ) ;
2014-02-19 19:35:47 +04:00
if ( ret < 0 )
goto end ;
2014-10-02 14:39:32 +04:00
2011-09-07 10:03:11 +04:00
/* Check if we can send the ping to HW*/
2014-02-19 19:35:47 +04:00
if ( ret & & mei_hbuf_acquire ( dev ) ) {
2014-09-29 17:31:42 +04:00
dev_dbg ( dev - > dev , " wd: sending ping \n " ) ;
2011-09-07 10:03:11 +04:00
2014-02-19 19:35:50 +04:00
ret = mei_wd_send ( dev ) ;
if ( ret )
2011-09-07 10:03:11 +04:00
goto end ;
2014-02-19 19:35:50 +04:00
dev - > wd_pending = false ;
2011-09-07 10:03:11 +04:00
} else {
dev - > wd_pending = true ;
}
end :
mutex_unlock ( & dev - > device_lock ) ;
return ret ;
}
2015-02-10 11:39:35 +03:00
/**
2011-09-07 10:03:12 +04:00
* mei_wd_ops_set_timeout - wd set timeout command from the watchdog core .
*
2015-02-10 11:39:35 +03:00
* @ wd_dev : watchdog device struct
* @ timeout : timeout value to set
2011-09-07 10:03:12 +04:00
*
2014-09-29 17:31:49 +04:00
* Return : 0 if success , negative errno code for failure
2011-09-07 10:03:12 +04:00
*/
2013-04-14 00:41:25 +04:00
static int mei_wd_ops_set_timeout ( struct watchdog_device * wd_dev ,
unsigned int timeout )
2011-09-07 10:03:12 +04:00
{
struct mei_device * dev ;
2012-08-16 20:39:44 +04:00
dev = watchdog_get_drvdata ( wd_dev ) ;
2011-09-07 10:03:12 +04:00
if ( ! dev )
return - ENODEV ;
/* Check Timeout value */
2012-08-16 20:39:42 +04:00
if ( timeout < MEI_WD_MIN_TIMEOUT | | timeout > MEI_WD_MAX_TIMEOUT )
2011-09-07 10:03:12 +04:00
return - EINVAL ;
mutex_lock ( & dev - > device_lock ) ;
dev - > wd_timeout = timeout ;
2012-02-29 23:20:58 +04:00
wd_dev - > timeout = timeout ;
2011-09-07 10:03:12 +04:00
mei_wd_set_start_timeout ( dev , dev - > wd_timeout ) ;
mutex_unlock ( & dev - > device_lock ) ;
return 0 ;
}
2011-09-07 10:03:10 +04:00
/*
* Watchdog Device structs
*/
2011-10-23 20:30:39 +04:00
static const struct watchdog_ops wd_ops = {
2011-09-07 10:03:10 +04:00
. owner = THIS_MODULE ,
. start = mei_wd_ops_start ,
. stop = mei_wd_ops_stop ,
2011-09-07 10:03:11 +04:00
. ping = mei_wd_ops_ping ,
2011-09-07 10:03:12 +04:00
. set_timeout = mei_wd_ops_set_timeout ,
2011-09-07 10:03:10 +04:00
} ;
2011-10-23 20:30:39 +04:00
static const struct watchdog_info wd_info = {
2011-09-07 10:03:10 +04:00
. identity = INTEL_AMT_WATCHDOG_ID ,
2012-08-16 20:39:41 +04:00
. options = WDIOF_KEEPALIVEPING |
WDIOF_SETTIMEOUT |
WDIOF_ALARMONLY ,
2011-09-07 10:03:10 +04:00
} ;
2012-04-02 21:32:38 +04:00
static struct watchdog_device amt_wd_dev = {
2011-09-07 10:03:10 +04:00
. info = & wd_info ,
. ops = & wd_ops ,
2012-08-16 20:39:42 +04:00
. timeout = MEI_WD_DEFAULT_TIMEOUT ,
. min_timeout = MEI_WD_MIN_TIMEOUT ,
. max_timeout = MEI_WD_MAX_TIMEOUT ,
2011-09-07 10:03:10 +04:00
} ;
2014-02-17 17:13:21 +04:00
int mei_watchdog_register ( struct mei_device * dev )
2011-12-22 20:50:50 +04:00
{
2014-02-17 17:13:21 +04:00
int ret ;
/* unlock to perserve correct locking order */
mutex_unlock ( & dev - > device_lock ) ;
ret = watchdog_register_device ( & amt_wd_dev ) ;
mutex_lock ( & dev - > device_lock ) ;
if ( ret ) {
2014-09-29 17:31:42 +04:00
dev_err ( dev - > dev , " wd: unable to register watchdog device = %d. \n " ,
2014-02-17 17:13:21 +04:00
ret ) ;
return ret ;
2011-12-22 20:50:50 +04:00
}
2012-08-16 20:39:44 +04:00
2014-09-29 17:31:42 +04:00
dev_dbg ( dev - > dev , " wd: successfully register watchdog interface. \n " ) ;
2012-08-16 20:39:44 +04:00
watchdog_set_drvdata ( & amt_wd_dev , dev ) ;
2014-02-17 17:13:21 +04:00
return 0 ;
2011-12-22 20:50:50 +04:00
}
void mei_watchdog_unregister ( struct mei_device * dev )
{
2012-12-16 15:23:17 +04:00
if ( watchdog_get_drvdata ( & amt_wd_dev ) = = NULL )
2012-08-16 20:39:44 +04:00
return ;
watchdog_set_drvdata ( & amt_wd_dev , NULL ) ;
watchdog_unregister_device ( & amt_wd_dev ) ;
2011-12-22 20:50:50 +04:00
}