2011-05-15 13:43:43 +03:00
/*
*
* Intel Management Engine Interface ( Intel MEI ) Linux driver
2012-02-09 19:25:53 +02:00
* Copyright ( c ) 2003 - 2012 , Intel Corporation .
2011-05-15 13:43:43 +03: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/pci.h>
2012-05-09 16:38:59 +03:00
# include <linux/mei.h>
2012-12-25 19:06:03 +02:00
# include "mei_dev.h"
2013-01-08 23:07:17 +02:00
# include "hw-me.h"
2011-05-15 13:43:43 +03:00
2012-12-25 19:06:06 +02:00
/**
* mei_reg_read - Reads 32 bit data from the mei device
*
* @ dev : the device structure
* @ offset : offset from which to read the data
*
* returns register value ( u32 )
*/
static inline u32 mei_reg_read ( const struct mei_device * dev ,
unsigned long offset )
{
return ioread32 ( dev - > mem_addr + offset ) ;
}
/**
* mei_reg_write - Writes 32 bit data to the mei device
*
* @ dev : the device structure
* @ offset : offset from which to write the data
* @ value : register value to write ( u32 )
*/
static inline void mei_reg_write ( const struct mei_device * dev ,
unsigned long offset , u32 value )
{
iowrite32 ( value , dev - > mem_addr + offset ) ;
}
2011-05-15 13:43:43 +03:00
2012-12-25 19:06:06 +02:00
/**
2013-01-08 23:07:24 +02:00
* mei_mecbrw_read - Reads 32 bit data from ME circular buffer
* read window register
2012-12-25 19:06:06 +02:00
*
* @ dev : the device structure
*
2013-01-08 23:07:24 +02:00
* returns ME_CB_RW register value ( u32 )
2012-12-25 19:06:06 +02:00
*/
u32 mei_mecbrw_read ( const struct mei_device * dev )
{
return mei_reg_read ( dev , ME_CB_RW ) ;
}
/**
* mei_mecsr_read - Reads 32 bit data from the ME CSR
*
* @ dev : the device structure
*
* returns ME_CSR_HA register value ( u32 )
*/
2013-01-08 23:07:31 +02:00
static inline u32 mei_mecsr_read ( const struct mei_device * dev )
2012-12-25 19:06:06 +02:00
{
return mei_reg_read ( dev , ME_CSR_HA ) ;
}
2011-05-15 13:43:43 +03:00
/**
2013-01-08 23:07:24 +02:00
* mei_hcsr_read - Reads 32 bit data from the host CSR
*
* @ dev : the device structure
*
* returns H_CSR register value ( u32 )
*/
2013-01-08 23:07:31 +02:00
static inline u32 mei_hcsr_read ( const struct mei_device * dev )
2013-01-08 23:07:24 +02:00
{
return mei_reg_read ( dev , H_CSR ) ;
}
/**
* mei_hcsr_set - writes H_CSR register to the mei device ,
2011-05-15 13:43:43 +03:00
* and ignores the H_IS bit for it is write - one - to - zero .
*
* @ dev : the device structure
*/
2013-01-08 23:07:30 +02:00
static inline void mei_hcsr_set ( struct mei_device * dev , u32 hcsr )
2011-05-15 13:43:43 +03:00
{
2013-01-08 23:07:30 +02:00
hcsr & = ~ H_IS ;
mei_reg_write ( dev , H_CSR , hcsr ) ;
2011-05-15 13:43:43 +03:00
}
2013-01-08 23:07:31 +02:00
/**
* me_hw_config - configure hw dependent settings
*
* @ dev : mei device
*/
void mei_hw_config ( struct mei_device * dev )
{
u32 hcsr = mei_hcsr_read ( dev ) ;
/* Doesn't change in runtime */
dev - > hbuf_depth = ( hcsr & H_CBD ) > > 24 ;
}
2011-05-15 13:43:43 +03:00
/**
2013-01-08 23:07:24 +02:00
* mei_clear_interrupts - clear and stop interrupts
2012-12-25 19:06:06 +02:00
*
* @ dev : the device structure
*/
void mei_clear_interrupts ( struct mei_device * dev )
{
2013-01-08 23:07:28 +02:00
u32 hcsr = mei_hcsr_read ( dev ) ;
if ( ( hcsr & H_IS ) = = H_IS )
mei_reg_write ( dev , H_CSR , hcsr ) ;
2012-12-25 19:06:06 +02:00
}
/**
* mei_enable_interrupts - enables mei device interrupts
2011-05-15 13:43:43 +03:00
*
* @ dev : the device structure
*/
void mei_enable_interrupts ( struct mei_device * dev )
{
2013-01-08 23:07:28 +02:00
u32 hcsr = mei_hcsr_read ( dev ) ;
hcsr | = H_IE ;
2013-01-08 23:07:30 +02:00
mei_hcsr_set ( dev , hcsr ) ;
2011-05-15 13:43:43 +03:00
}
/**
2012-12-25 19:06:06 +02:00
* mei_disable_interrupts - disables mei device interrupts
2011-05-15 13:43:43 +03:00
*
* @ dev : the device structure
*/
void mei_disable_interrupts ( struct mei_device * dev )
{
2013-01-08 23:07:28 +02:00
u32 hcsr = mei_hcsr_read ( dev ) ;
hcsr & = ~ H_IE ;
2013-01-08 23:07:30 +02:00
mei_hcsr_set ( dev , hcsr ) ;
2011-05-15 13:43:43 +03:00
}
2013-01-08 23:07:27 +02:00
/**
* mei_hw_reset - resets fw via mei csr register .
*
* @ dev : the device structure
* @ interrupts_enabled : if interrupt should be enabled after reset .
*/
void mei_hw_reset ( struct mei_device * dev , bool intr_enable )
{
u32 hcsr = mei_hcsr_read ( dev ) ;
dev_dbg ( & dev - > pdev - > dev , " before reset HCSR = 0x%08x. \n " , hcsr ) ;
hcsr | = ( H_RST | H_IG ) ;
if ( intr_enable )
hcsr | = H_IE ;
else
hcsr & = ~ H_IE ;
2013-01-08 23:07:30 +02:00
mei_hcsr_set ( dev , hcsr ) ;
2013-01-08 23:07:27 +02:00
2013-01-08 23:07:30 +02:00
hcsr = mei_hcsr_read ( dev ) | H_IG ;
2013-01-08 23:07:27 +02:00
hcsr & = ~ H_RST ;
2013-01-08 23:07:30 +02:00
mei_hcsr_set ( dev , hcsr ) ;
2013-01-08 23:07:27 +02:00
hcsr = mei_hcsr_read ( dev ) ;
dev_dbg ( & dev - > pdev - > dev , " current HCSR = 0x%08x. \n " , hcsr ) ;
}
2013-01-08 23:07:29 +02:00
/**
* mei_host_set_ready - enable device
*
* @ dev - mei device
* returns bool
*/
void mei_host_set_ready ( struct mei_device * dev )
{
dev - > host_hw_state | = H_IE | H_IG | H_RDY ;
2013-01-08 23:07:30 +02:00
mei_hcsr_set ( dev , dev - > host_hw_state ) ;
2013-01-08 23:07:29 +02:00
}
/**
* mei_host_is_ready - check whether the host has turned ready
*
* @ dev - mei device
* returns bool
*/
bool mei_host_is_ready ( struct mei_device * dev )
{
2013-01-08 23:07:31 +02:00
dev - > host_hw_state = mei_hcsr_read ( dev ) ;
2013-01-08 23:07:29 +02:00
return ( dev - > host_hw_state & H_RDY ) = = H_RDY ;
}
/**
* mei_me_is_ready - check whether the me has turned ready
*
* @ dev - mei device
* returns bool
*/
bool mei_me_is_ready ( struct mei_device * dev )
{
2013-01-08 23:07:31 +02:00
dev - > me_hw_state = mei_mecsr_read ( dev ) ;
2013-01-08 23:07:29 +02:00
return ( dev - > me_hw_state & ME_RDY_HRA ) = = ME_RDY_HRA ;
}
2012-12-25 19:06:06 +02:00
/**
* mei_interrupt_quick_handler - The ISR of the MEI device
*
* @ irq : The irq number
* @ dev_id : pointer to the device structure
*
* returns irqreturn_t
*/
irqreturn_t mei_interrupt_quick_handler ( int irq , void * dev_id )
{
struct mei_device * dev = ( struct mei_device * ) dev_id ;
u32 csr_reg = mei_hcsr_read ( dev ) ;
if ( ( csr_reg & H_IS ) ! = H_IS )
return IRQ_NONE ;
/* clear H_IS bit in H_CSR */
mei_reg_write ( dev , H_CSR , csr_reg ) ;
return IRQ_WAKE_THREAD ;
}
2011-05-15 13:43:43 +03:00
/**
2012-06-25 23:46:28 +03:00
* mei_hbuf_filled_slots - gets number of device filled buffer slots
2011-05-15 13:43:43 +03:00
*
* @ device : the device structure
*
* returns number of filled slots
*/
2012-06-25 23:46:28 +03:00
static unsigned char mei_hbuf_filled_slots ( struct mei_device * dev )
2011-05-15 13:43:43 +03:00
{
char read_ptr , write_ptr ;
2012-06-25 23:46:28 +03:00
dev - > host_hw_state = mei_hcsr_read ( dev ) ;
2011-05-15 13:43:43 +03:00
read_ptr = ( char ) ( ( dev - > host_hw_state & H_CBRP ) > > 8 ) ;
write_ptr = ( char ) ( ( dev - > host_hw_state & H_CBWP ) > > 16 ) ;
return ( unsigned char ) ( write_ptr - read_ptr ) ;
}
/**
2012-06-25 23:46:28 +03:00
* mei_hbuf_is_empty - checks if host buffer is empty .
2011-05-15 13:43:43 +03:00
*
* @ dev : the device structure
*
2012-06-25 23:46:28 +03:00
* returns true if empty , false - otherwise .
2011-05-15 13:43:43 +03:00
*/
2012-06-25 23:46:28 +03:00
bool mei_hbuf_is_empty ( struct mei_device * dev )
2011-05-15 13:43:43 +03:00
{
2012-06-25 23:46:28 +03:00
return mei_hbuf_filled_slots ( dev ) = = 0 ;
2011-05-15 13:43:43 +03:00
}
/**
2012-06-25 23:46:28 +03:00
* mei_hbuf_empty_slots - counts write empty slots .
2011-05-15 13:43:43 +03:00
*
* @ dev : the device structure
*
* returns - 1 ( ESLOTS_OVERFLOW ) if overflow , otherwise empty slots count
*/
2012-06-25 23:46:28 +03:00
int mei_hbuf_empty_slots ( struct mei_device * dev )
2011-05-15 13:43:43 +03:00
{
2012-06-25 23:46:27 +03:00
unsigned char filled_slots , empty_slots ;
2011-05-15 13:43:43 +03:00
2012-06-25 23:46:28 +03:00
filled_slots = mei_hbuf_filled_slots ( dev ) ;
2012-06-25 23:46:27 +03:00
empty_slots = dev - > hbuf_depth - filled_slots ;
2011-05-15 13:43:43 +03:00
/* check for overflow */
2012-06-25 23:46:27 +03:00
if ( filled_slots > dev - > hbuf_depth )
2011-05-15 13:43:43 +03:00
return - EOVERFLOW ;
return empty_slots ;
}
/**
* mei_write_message - writes a message to mei device .
*
* @ dev : the device structure
2012-12-25 19:05:59 +02:00
* @ hader : mei HECI header of message
* @ buf : message payload will be written
2011-05-15 13:43:43 +03:00
*
2012-03-14 14:39:42 +02:00
* This function returns - EIO if write has failed
2011-05-15 13:43:43 +03:00
*/
2012-06-19 09:13:35 +03:00
int mei_write_message ( struct mei_device * dev , struct mei_msg_hdr * header ,
2012-12-25 19:05:59 +02:00
unsigned char * buf )
2011-05-15 13:43:43 +03:00
{
2012-06-19 09:13:35 +03:00
unsigned long rem , dw_cnt ;
2012-12-25 19:05:59 +02:00
unsigned long length = header - > length ;
2012-06-19 09:13:35 +03:00
u32 * reg_buf = ( u32 * ) buf ;
2013-01-08 23:07:30 +02:00
u32 hcsr ;
2012-06-19 09:13:35 +03:00
int i ;
int empty_slots ;
2011-05-15 13:43:43 +03:00
2012-12-25 19:06:00 +02:00
dev_dbg ( & dev - > pdev - > dev , MEI_HDR_FMT , MEI_HDR_PRM ( header ) ) ;
2011-05-15 13:43:43 +03:00
2012-06-25 23:46:28 +03:00
empty_slots = mei_hbuf_empty_slots ( dev ) ;
2012-06-19 09:13:35 +03:00
dev_dbg ( & dev - > pdev - > dev , " empty slots = %hu. \n " , empty_slots ) ;
2011-05-15 13:43:43 +03:00
2012-07-04 19:24:52 +03:00
dw_cnt = mei_data2slots ( length ) ;
2012-06-19 09:13:35 +03:00
if ( empty_slots < 0 | | dw_cnt > empty_slots )
2012-03-14 14:39:42 +02:00
return - EIO ;
2011-05-15 13:43:43 +03:00
mei_reg_write ( dev , H_CB_WW , * ( ( u32 * ) header ) ) ;
2012-06-19 09:13:35 +03:00
for ( i = 0 ; i < length / 4 ; i + + )
mei_reg_write ( dev , H_CB_WW , reg_buf [ i ] ) ;
2011-05-15 13:43:43 +03:00
2012-06-19 09:13:35 +03:00
rem = length & 0x3 ;
if ( rem > 0 ) {
u32 reg = 0 ;
memcpy ( & reg , & buf [ length - rem ] , rem ) ;
mei_reg_write ( dev , H_CB_WW , reg ) ;
2011-05-15 13:43:43 +03:00
}
2013-01-08 23:07:30 +02:00
hcsr = mei_hcsr_read ( dev ) | H_IG ;
mei_hcsr_set ( dev , hcsr ) ;
2013-01-08 23:07:29 +02:00
if ( ! mei_me_is_ready ( dev ) )
2012-03-14 14:39:42 +02:00
return - EIO ;
2011-05-15 13:43:43 +03:00
2012-03-14 14:39:42 +02:00
return 0 ;
2011-05-15 13:43:43 +03:00
}
/**
* mei_count_full_read_slots - counts read full slots .
*
* @ dev : the device structure
*
* returns - 1 ( ESLOTS_OVERFLOW ) if overflow , otherwise filled slots count
*/
int mei_count_full_read_slots ( struct mei_device * dev )
{
char read_ptr , write_ptr ;
unsigned char buffer_depth , filled_slots ;
dev - > me_hw_state = mei_mecsr_read ( dev ) ;
buffer_depth = ( unsigned char ) ( ( dev - > me_hw_state & ME_CBD_HRA ) > > 24 ) ;
read_ptr = ( char ) ( ( dev - > me_hw_state & ME_CBRP_HRA ) > > 8 ) ;
write_ptr = ( char ) ( ( dev - > me_hw_state & ME_CBWP_HRA ) > > 16 ) ;
filled_slots = ( unsigned char ) ( write_ptr - read_ptr ) ;
/* check for overflow */
if ( filled_slots > buffer_depth )
return - EOVERFLOW ;
dev_dbg ( & dev - > pdev - > dev , " filled_slots =%08x \n " , filled_slots ) ;
return ( int ) filled_slots ;
}
/**
* mei_read_slots - reads a message from mei device .
*
* @ dev : the device structure
* @ buffer : message buffer will be written
* @ buffer_length : message size will be read
*/
2012-02-09 19:25:54 +02:00
void mei_read_slots ( struct mei_device * dev , unsigned char * buffer ,
unsigned long buffer_length )
2011-05-15 13:43:43 +03:00
{
2012-02-09 19:25:54 +02:00
u32 * reg_buf = ( u32 * ) buffer ;
2013-01-08 23:07:30 +02:00
u32 hcsr ;
2011-05-15 13:43:43 +03:00
2012-02-09 19:25:54 +02:00
for ( ; buffer_length > = sizeof ( u32 ) ; buffer_length - = sizeof ( u32 ) )
* reg_buf + + = mei_mecbrw_read ( dev ) ;
2011-05-15 13:43:43 +03:00
if ( buffer_length > 0 ) {
2012-02-09 19:25:54 +02:00
u32 reg = mei_mecbrw_read ( dev ) ;
memcpy ( reg_buf , & reg , buffer_length ) ;
2011-05-15 13:43:43 +03:00
}
2013-01-08 23:07:30 +02:00
hcsr = mei_hcsr_read ( dev ) | H_IG ;
mei_hcsr_set ( dev , hcsr ) ;
2011-05-15 13:43:43 +03:00
}