2011-05-15 14:43:44 +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:44 +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 .
*
*/
2013-03-27 18:58:30 +04:00
# include <linux/export.h>
2011-05-15 14:43:44 +04:00
# include <linux/pci.h>
# include <linux/sched.h>
# include <linux/wait.h>
# include <linux/delay.h>
2012-12-25 21:06:03 +04:00
# include <linux/mei.h>
2011-05-15 14:43:44 +04:00
# include "mei_dev.h"
2013-03-11 20:27:03 +04:00
# include "hbm.h"
2013-01-09 01:07:14 +04:00
# include "client.h"
2011-05-15 14:43:44 +04:00
2012-08-07 01:03:56 +04:00
const char * mei_dev_state_str ( int state )
{
# define MEI_DEV_STATE(state) case MEI_DEV_##state: return #state
switch ( state ) {
MEI_DEV_STATE ( INITIALIZING ) ;
MEI_DEV_STATE ( INIT_CLIENTS ) ;
MEI_DEV_STATE ( ENABLED ) ;
2013-04-19 23:01:36 +04:00
MEI_DEV_STATE ( RESETTING ) ;
2012-08-07 01:03:56 +04:00
MEI_DEV_STATE ( DISABLED ) ;
MEI_DEV_STATE ( POWER_DOWN ) ;
MEI_DEV_STATE ( POWER_UP ) ;
default :
2013-05-21 18:13:12 +04:00
return " unknown " ;
2012-08-07 01:03:56 +04:00
}
# undef MEI_DEV_STATE
}
2013-02-06 16:06:40 +04:00
void mei_device_init ( struct mei_device * dev )
2011-05-15 14:43:44 +04:00
{
/* setup our list array */
INIT_LIST_HEAD ( & dev - > file_list ) ;
2013-03-27 19:29:56 +04:00
INIT_LIST_HEAD ( & dev - > device_list ) ;
2011-05-15 14:43:44 +04:00
mutex_init ( & dev - > device_lock ) ;
2013-03-11 20:27:03 +04:00
init_waitqueue_head ( & dev - > wait_hw_ready ) ;
2011-05-15 14:43:44 +04:00
init_waitqueue_head ( & dev - > wait_recvd_msg ) ;
init_waitqueue_head ( & dev - > wait_stop_wd ) ;
2012-08-07 01:03:56 +04:00
dev - > dev_state = MEI_DEV_INITIALIZING ;
2011-06-06 11:44:34 +04:00
mei_io_list_init ( & dev - > read_list ) ;
mei_io_list_init ( & dev - > write_list ) ;
mei_io_list_init ( & dev - > write_waiting_list ) ;
mei_io_list_init ( & dev - > ctrl_wr_list ) ;
mei_io_list_init ( & dev - > ctrl_rd_list ) ;
2013-03-27 18:58:27 +04:00
INIT_DELAYED_WORK ( & dev - > timer_work , mei_timer ) ;
INIT_WORK ( & dev - > init_work , mei_host_client_init ) ;
INIT_LIST_HEAD ( & dev - > wd_cl . link ) ;
INIT_LIST_HEAD ( & dev - > iamthif_cl . link ) ;
mei_io_list_init ( & dev - > amthif_cmd_list ) ;
mei_io_list_init ( & dev - > amthif_rd_complete_list ) ;
2013-10-17 18:20:22 +04:00
bitmap_zero ( dev - > host_clients_map , MEI_CLIENTS_MAX ) ;
dev - > open_handle_count = 0 ;
/*
* Reserving the first client ID
* 0 : Reserved for MEI Bus Message communications
*/
bitmap_set ( dev - > host_clients_map , 0 , 1 ) ;
2011-05-15 14:43:44 +04:00
}
2013-03-27 18:58:30 +04:00
EXPORT_SYMBOL_GPL ( mei_device_init ) ;
2011-05-15 14:43:44 +04:00
/**
2013-03-27 18:58:28 +04:00
* mei_start - initializes host and fw to start work .
2011-05-15 14:43:44 +04:00
*
* @ dev : the device structure
*
* returns 0 on success , < 0 on failure .
*/
2013-03-27 18:58:28 +04:00
int mei_start ( struct mei_device * dev )
2011-05-15 14:43:44 +04:00
{
mutex_lock ( & dev - > device_lock ) ;
/* acknowledge interrupt and stop interupts */
2012-12-25 21:06:06 +04:00
mei_clear_interrupts ( dev ) ;
2011-05-15 14:43:44 +04:00
2013-01-09 01:07:31 +04:00
mei_hw_config ( dev ) ;
2012-06-26 00:46:27 +04:00
2011-05-15 14:43:44 +04:00
dev_dbg ( & dev - > pdev - > dev , " reset in start the mei device. \n " ) ;
mei_reset ( dev , 1 ) ;
2013-04-19 00:03:48 +04:00
if ( mei_hbm_start_wait ( dev ) ) {
dev_err ( & dev - > pdev - > dev , " HBM haven't started " ) ;
2013-01-09 01:07:31 +04:00
goto err ;
2011-05-15 14:43:44 +04:00
}
2013-01-09 01:07:31 +04:00
if ( ! mei_host_is_ready ( dev ) ) {
dev_err ( & dev - > pdev - > dev , " host is not ready. \n " ) ;
goto err ;
}
2011-05-15 14:43:44 +04:00
2013-02-06 16:06:41 +04:00
if ( ! mei_hw_is_ready ( dev ) ) {
2013-01-09 01:07:31 +04:00
dev_err ( & dev - > pdev - > dev , " ME is not ready. \n " ) ;
goto err ;
2011-05-15 14:43:44 +04:00
}
2013-06-16 10:16:31 +04:00
if ( ! mei_hbm_version_is_supported ( dev ) ) {
2011-05-15 14:43:44 +04:00
dev_dbg ( & dev - > pdev - > dev , " MEI start failed. \n " ) ;
2013-01-09 01:07:31 +04:00
goto err ;
2011-05-15 14:43:44 +04:00
}
dev_dbg ( & dev - > pdev - > dev , " link layer has been established. \n " ) ;
mutex_unlock ( & dev - > device_lock ) ;
2013-01-09 01:07:31 +04:00
return 0 ;
err :
dev_err ( & dev - > pdev - > dev , " link layer initialization failed. \n " ) ;
dev - > dev_state = MEI_DEV_DISABLED ;
mutex_unlock ( & dev - > device_lock ) ;
return - ENODEV ;
2011-05-15 14:43:44 +04:00
}
2013-03-27 18:58:30 +04:00
EXPORT_SYMBOL_GPL ( mei_start ) ;
2011-05-15 14:43:44 +04:00
/**
* mei_reset - resets host and fw .
*
* @ dev : the device structure
* @ interrupts_enabled : if interrupt should be enabled after reset .
*/
void mei_reset ( struct mei_device * dev , int interrupts_enabled )
{
bool unexpected ;
2013-06-23 11:42:49 +04:00
int ret ;
2011-05-15 14:43:44 +04:00
2012-08-07 01:03:56 +04:00
unexpected = ( dev - > dev_state ! = MEI_DEV_INITIALIZING & &
dev - > dev_state ! = MEI_DEV_DISABLED & &
dev - > dev_state ! = MEI_DEV_POWER_DOWN & &
dev - > dev_state ! = MEI_DEV_POWER_UP ) ;
2011-05-15 14:43:44 +04:00
2013-06-23 11:42:49 +04:00
ret = mei_hw_reset ( dev , interrupts_enabled ) ;
if ( ret ) {
dev_err ( & dev - > pdev - > dev , " hw reset failed disabling the device \n " ) ;
interrupts_enabled = false ;
dev - > dev_state = MEI_DEV_DISABLED ;
}
2011-05-15 14:43:44 +04:00
2013-04-19 00:03:48 +04:00
dev - > hbm_state = MEI_HBM_IDLE ;
2011-05-15 14:43:44 +04:00
2013-07-17 16:13:16 +04:00
if ( dev - > dev_state ! = MEI_DEV_INITIALIZING & &
dev - > dev_state ! = MEI_DEV_POWER_UP ) {
2012-08-07 01:03:56 +04:00
if ( dev - > dev_state ! = MEI_DEV_DISABLED & &
dev - > dev_state ! = MEI_DEV_POWER_DOWN )
2013-04-19 23:01:36 +04:00
dev - > dev_state = MEI_DEV_RESETTING ;
2011-05-15 14:43:44 +04:00
2013-07-25 21:15:53 +04:00
/* remove all waiting requests */
mei_cl_all_write_clear ( dev ) ;
2013-02-06 16:06:44 +04:00
mei_cl_all_disconnect ( dev ) ;
2013-07-25 21:15:53 +04:00
/* wake up all readings so they can be interrupted */
mei_cl_all_wakeup ( dev ) ;
2011-05-15 14:43:44 +04:00
/* remove entry if already in list */
2012-11-11 19:38:03 +04:00
dev_dbg ( & dev - > pdev - > dev , " remove iamthif and wd from the file list. \n " ) ;
2013-01-09 01:07:14 +04:00
mei_cl_unlink ( & dev - > wd_cl ) ;
mei_cl_unlink ( & dev - > iamthif_cl ) ;
2012-11-01 23:17:15 +04:00
mei_amthif_reset_params ( dev ) ;
2012-11-18 17:13:15 +04:00
memset ( & dev - > wr_ext_msg , 0 , sizeof ( dev - > wr_ext_msg ) ) ;
2011-05-15 14:43:44 +04:00
}
2013-09-02 14:29:47 +04:00
/* we're already in reset, cancel the init timer */
dev - > init_clients_timer = 0 ;
2011-06-06 11:44:33 +04:00
dev - > me_clients_num = 0 ;
2011-05-15 14:43:44 +04:00
dev - > rd_msg_hdr = 0 ;
2011-05-25 18:28:22 +04:00
dev - > wd_pending = false ;
2011-05-15 14:43:44 +04:00
if ( unexpected )
2012-08-07 01:03:56 +04:00
dev_warn ( & dev - > pdev - > dev , " unexpected reset: dev_state = %s \n " ,
mei_dev_state_str ( dev - > dev_state ) ) ;
2011-05-15 14:43:44 +04:00
2013-03-11 20:27:03 +04:00
if ( ! interrupts_enabled ) {
dev_dbg ( & dev - > pdev - > dev , " intr not enabled end of reset \n " ) ;
return ;
}
2013-06-23 23:49:04 +04:00
ret = mei_hw_start ( dev ) ;
if ( ret ) {
dev_err ( & dev - > pdev - > dev , " hw_start failed disabling the device \n " ) ;
dev - > dev_state = MEI_DEV_DISABLED ;
return ;
}
2013-03-11 20:27:03 +04:00
dev_dbg ( & dev - > pdev - > dev , " link is established start sending messages. \n " ) ;
/* link is established * start sending messages. */
dev - > dev_state = MEI_DEV_INIT_CLIENTS ;
mei_hbm_start_req ( dev ) ;
2011-05-15 14:43:44 +04:00
}
2013-03-27 18:58:30 +04:00
EXPORT_SYMBOL_GPL ( mei_reset ) ;
2011-05-15 14:43:44 +04:00
2013-03-10 15:56:08 +04:00
void mei_stop ( struct mei_device * dev )
{
dev_dbg ( & dev - > pdev - > dev , " stopping the device. \n " ) ;
2013-06-10 11:10:25 +04:00
flush_scheduled_work ( ) ;
2013-03-10 15:56:08 +04:00
mutex_lock ( & dev - > device_lock ) ;
cancel_delayed_work ( & dev - > timer_work ) ;
mei_wd_stop ( dev ) ;
2013-04-11 05:03:29 +04:00
mei_nfc_host_exit ( ) ;
2013-03-10 15:56:08 +04:00
dev - > dev_state = MEI_DEV_POWER_DOWN ;
mei_reset ( dev , 0 ) ;
mutex_unlock ( & dev - > device_lock ) ;
2013-03-27 18:58:26 +04:00
mei_watchdog_unregister ( dev ) ;
2013-03-10 15:56:08 +04:00
}
2013-03-27 18:58:30 +04:00
EXPORT_SYMBOL_GPL ( mei_stop ) ;
2011-05-15 14:43:44 +04:00
2012-11-18 17:13:20 +04:00
2011-05-15 14:43:44 +04:00