2013-02-06 14:06:39 +02: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 .
*
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/kernel.h>
# include <linux/device.h>
# include <linux/fs.h>
# include <linux/errno.h>
# include <linux/types.h>
# include <linux/fcntl.h>
# include <linux/aio.h>
# include <linux/pci.h>
# include <linux/poll.h>
# include <linux/ioctl.h>
# include <linux/cdev.h>
# include <linux/sched.h>
# include <linux/uuid.h>
# include <linux/compat.h>
# include <linux/jiffies.h>
# include <linux/interrupt.h>
# include <linux/miscdevice.h>
# include <linux/mei.h>
# include "mei_dev.h"
# include "client.h"
2014-03-11 14:49:23 +02:00
# include "hw-me-regs.h"
# include "hw-me.h"
2013-02-06 14:06:39 +02:00
/* mei_pci_tbl - PCI Device ID Table */
2014-03-16 14:35:58 +02:00
static const struct pci_device_id mei_me_pci_tbl [ ] = {
2013-02-06 14:06:39 +02:00
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_82946GZ ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_82G35 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_82Q965 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_82G965 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_82GM965 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_82GME965 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_ICH9_82Q35 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_ICH9_82G33 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_ICH9_82Q33 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_ICH9_82X38 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_ICH9_3200 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_ICH9_6 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_ICH9_7 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_ICH9_8 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_ICH9_9 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_ICH9_10 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_ICH9M_1 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_ICH9M_2 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_ICH9M_3 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_ICH9M_4 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_ICH10_1 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_ICH10_2 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_ICH10_3 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_ICH10_4 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_IBXPK_1 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_IBXPK_2 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_CPT_1 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_PBG_1 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_PPT_1 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_PPT_2 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_PPT_3 ) } ,
2013-12-05 09:34:44 +02:00
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_LPT_H ) } ,
2013-10-16 12:09:43 +03:00
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_LPT_W ) } ,
2013-02-06 14:06:39 +02:00
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_LPT_LP ) } ,
2013-12-05 09:34:44 +02:00
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_LPT_HR ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , MEI_DEV_ID_WPT_LP ) } ,
2013-02-06 14:06:39 +02:00
/* required last entry */
{ 0 , }
} ;
2013-03-27 16:58:29 +02:00
MODULE_DEVICE_TABLE ( pci , mei_me_pci_tbl ) ;
2013-02-06 14:06:39 +02:00
/**
* mei_quirk_probe - probe for devices that doesn ' t valid ME interface
2013-04-05 01:05:05 +09:00
*
2013-02-06 14:06:39 +02:00
* @ pdev : PCI device structure
* @ ent : entry into pci_device_table
*
* returns true if ME Interface is valid , false otherwise
*/
2013-03-27 16:58:29 +02:00
static bool mei_me_quirk_probe ( struct pci_dev * pdev ,
2013-02-06 14:06:39 +02:00
const struct pci_device_id * ent )
{
u32 reg ;
2014-03-25 21:25:18 +02:00
/* Cougar Point || Patsburg */
if ( ent - > device = = MEI_DEV_ID_CPT_1 | |
ent - > device = = MEI_DEV_ID_PBG_1 ) {
pci_read_config_dword ( pdev , PCI_CFG_HFS_2 , & reg ) ;
/* make sure that bit 9 (NM) is up and bit 10 (DM) is down */
if ( ( reg & 0x600 ) = = 0x200 )
goto no_mei ;
2013-02-06 14:06:39 +02:00
}
2014-03-25 21:25:18 +02:00
/* Lynx Point */
if ( ent - > device = = MEI_DEV_ID_LPT_H | |
ent - > device = = MEI_DEV_ID_LPT_W | |
ent - > device = = MEI_DEV_ID_LPT_HR ) {
/* Read ME FW Status check for SPS Firmware */
pci_read_config_dword ( pdev , PCI_CFG_HFS_1 , & reg ) ;
/* if bits [19:16] = 15, running SPS Firmware */
if ( ( reg & 0xf0000 ) = = 0xf0000 )
goto no_mei ;
}
2013-02-06 14:06:39 +02:00
return true ;
2014-03-25 21:25:18 +02:00
no_mei :
dev_info ( & pdev - > dev , " Device doesn't have valid ME Interface \n " ) ;
return false ;
2013-02-06 14:06:39 +02:00
}
/**
* mei_probe - Device Initialization Routine
*
* @ pdev : PCI device structure
* @ ent : entry in kcs_pci_tbl
*
* returns 0 on success , < 0 on failure .
*/
2013-03-27 16:58:29 +02:00
static int mei_me_probe ( struct pci_dev * pdev , const struct pci_device_id * ent )
2013-02-06 14:06:39 +02:00
{
struct mei_device * dev ;
2013-02-06 14:06:40 +02:00
struct mei_me_hw * hw ;
2013-02-06 14:06:39 +02:00
int err ;
2013-03-27 16:58:29 +02:00
if ( ! mei_me_quirk_probe ( pdev , ent ) ) {
2013-02-06 14:06:39 +02:00
err = - ENODEV ;
goto end ;
}
/* enable pci dev */
err = pci_enable_device ( pdev ) ;
if ( err ) {
dev_err ( & pdev - > dev , " failed to enable pci device. \n " ) ;
goto end ;
}
/* set PCI host mastering */
pci_set_master ( pdev ) ;
/* pci request regions for mei driver */
err = pci_request_regions ( pdev , KBUILD_MODNAME ) ;
if ( err ) {
dev_err ( & pdev - > dev , " failed to get pci regions. \n " ) ;
goto disable_device ;
}
2013-12-17 15:56:57 +02:00
if ( dma_set_mask ( & pdev - > dev , DMA_BIT_MASK ( 64 ) ) | |
dma_set_coherent_mask ( & pdev - > dev , DMA_BIT_MASK ( 64 ) ) ) {
err = dma_set_mask ( & pdev - > dev , DMA_BIT_MASK ( 32 ) ) ;
if ( err )
err = dma_set_coherent_mask ( & pdev - > dev ,
DMA_BIT_MASK ( 32 ) ) ;
}
if ( err ) {
dev_err ( & pdev - > dev , " No usable DMA configuration, aborting \n " ) ;
goto release_regions ;
}
2013-02-06 14:06:39 +02:00
/* allocates and initializes the mei dev structure */
2013-02-06 14:06:40 +02:00
dev = mei_me_dev_init ( pdev ) ;
2013-02-06 14:06:39 +02:00
if ( ! dev ) {
err = - ENOMEM ;
goto release_regions ;
}
2013-02-06 14:06:40 +02:00
hw = to_me_hw ( dev ) ;
2013-02-06 14:06:39 +02:00
/* mapping IO device memory */
2013-02-06 14:06:40 +02:00
hw - > mem_addr = pci_iomap ( pdev , 0 , 0 ) ;
if ( ! hw - > mem_addr ) {
2013-02-06 14:06:39 +02:00
dev_err ( & pdev - > dev , " mapping I/O device memory failure. \n " ) ;
err = - ENOMEM ;
goto free_device ;
}
pci_enable_msi ( pdev ) ;
/* request and enable interrupt */
if ( pci_dev_msi_enabled ( pdev ) )
err = request_threaded_irq ( pdev - > irq ,
NULL ,
2013-02-06 14:06:42 +02:00
mei_me_irq_thread_handler ,
2013-02-06 14:06:39 +02:00
IRQF_ONESHOT , KBUILD_MODNAME , dev ) ;
else
err = request_threaded_irq ( pdev - > irq ,
2013-02-06 14:06:42 +02:00
mei_me_irq_quick_handler ,
mei_me_irq_thread_handler ,
2013-02-06 14:06:39 +02:00
IRQF_SHARED , KBUILD_MODNAME , dev ) ;
if ( err ) {
dev_err ( & pdev - > dev , " request_threaded_irq failure. irq = %d \n " ,
pdev - > irq ) ;
goto disable_msi ;
}
2013-03-27 16:58:28 +02:00
if ( mei_start ( dev ) ) {
2013-02-06 14:06:39 +02:00
dev_err ( & pdev - > dev , " init hw failure. \n " ) ;
err = - ENODEV ;
goto release_irq ;
}
2013-04-05 22:10:34 +03:00
err = mei_register ( dev ) ;
2013-02-06 14:06:39 +02:00
if ( err )
goto release_irq ;
pci_set_drvdata ( pdev , dev ) ;
schedule_delayed_work ( & dev - > timer_work , HZ ) ;
2013-10-21 22:05:42 +03:00
dev_dbg ( & pdev - > dev , " initialization successful. \n " ) ;
2013-02-06 14:06:39 +02:00
return 0 ;
release_irq :
2013-11-11 13:26:06 +02:00
mei_cancel_work ( dev ) ;
2013-02-06 14:06:39 +02:00
mei_disable_interrupts ( dev ) ;
free_irq ( pdev - > irq , dev ) ;
disable_msi :
pci_disable_msi ( pdev ) ;
2013-02-06 14:06:40 +02:00
pci_iounmap ( pdev , hw - > mem_addr ) ;
2013-02-06 14:06:39 +02:00
free_device :
kfree ( dev ) ;
release_regions :
pci_release_regions ( pdev ) ;
disable_device :
pci_disable_device ( pdev ) ;
end :
dev_err ( & pdev - > dev , " initialization failed. \n " ) ;
return err ;
}
/**
* mei_remove - Device Removal Routine
*
* @ pdev : PCI device structure
*
* mei_remove is called by the PCI subsystem to alert the driver
* that it should release a PCI device .
*/
2013-03-27 16:58:29 +02:00
static void mei_me_remove ( struct pci_dev * pdev )
2013-02-06 14:06:39 +02:00
{
struct mei_device * dev ;
2013-02-06 14:06:40 +02:00
struct mei_me_hw * hw ;
2013-02-06 14:06:39 +02:00
dev = pci_get_drvdata ( pdev ) ;
if ( ! dev )
return ;
2013-02-06 14:06:40 +02:00
hw = to_me_hw ( dev ) ;
2013-02-06 14:06:39 +02:00
2013-10-17 13:52:10 +03:00
dev_dbg ( & pdev - > dev , " stop \n " ) ;
2013-03-10 13:56:08 +02:00
mei_stop ( dev ) ;
2013-02-06 14:06:39 +02:00
/* disable interrupts */
mei_disable_interrupts ( dev ) ;
free_irq ( pdev - > irq , dev ) ;
pci_disable_msi ( pdev ) ;
2013-02-06 14:06:40 +02:00
if ( hw - > mem_addr )
pci_iounmap ( pdev , hw - > mem_addr ) ;
2013-02-06 14:06:39 +02:00
2013-04-05 22:10:34 +03:00
mei_deregister ( dev ) ;
2013-02-06 14:06:39 +02:00
kfree ( dev ) ;
pci_release_regions ( pdev ) ;
pci_disable_device ( pdev ) ;
}
2014-02-18 14:31:08 +02:00
# ifdef CONFIG_PM_SLEEP
2013-03-27 16:58:29 +02:00
static int mei_me_pci_suspend ( struct device * device )
2013-02-06 14:06:39 +02:00
{
struct pci_dev * pdev = to_pci_dev ( device ) ;
struct mei_device * dev = pci_get_drvdata ( pdev ) ;
if ( ! dev )
return - ENODEV ;
2013-10-17 13:52:10 +03:00
dev_dbg ( & pdev - > dev , " suspend \n " ) ;
2013-02-06 14:06:39 +02:00
2013-03-10 13:56:08 +02:00
mei_stop ( dev ) ;
mei_disable_interrupts ( dev ) ;
2013-02-06 14:06:39 +02:00
free_irq ( pdev - > irq , dev ) ;
pci_disable_msi ( pdev ) ;
2013-03-10 13:56:08 +02:00
return 0 ;
2013-02-06 14:06:39 +02:00
}
2013-03-27 16:58:29 +02:00
static int mei_me_pci_resume ( struct device * device )
2013-02-06 14:06:39 +02:00
{
struct pci_dev * pdev = to_pci_dev ( device ) ;
struct mei_device * dev ;
int err ;
dev = pci_get_drvdata ( pdev ) ;
if ( ! dev )
return - ENODEV ;
pci_enable_msi ( pdev ) ;
/* request and enable interrupt */
if ( pci_dev_msi_enabled ( pdev ) )
err = request_threaded_irq ( pdev - > irq ,
NULL ,
2013-02-06 14:06:42 +02:00
mei_me_irq_thread_handler ,
2013-02-06 14:06:39 +02:00
IRQF_ONESHOT , KBUILD_MODNAME , dev ) ;
else
err = request_threaded_irq ( pdev - > irq ,
2013-02-06 14:06:42 +02:00
mei_me_irq_quick_handler ,
mei_me_irq_thread_handler ,
2013-02-06 14:06:39 +02:00
IRQF_SHARED , KBUILD_MODNAME , dev ) ;
if ( err ) {
dev_err ( & pdev - > dev , " request_threaded_irq failed: irq = %d. \n " ,
pdev - > irq ) ;
return err ;
}
2014-01-12 00:36:09 +02:00
err = mei_restart ( dev ) ;
if ( err )
return err ;
2013-02-06 14:06:39 +02:00
/* Start timer if stopped in suspend */
schedule_delayed_work ( & dev - > timer_work , HZ ) ;
2014-01-12 00:36:09 +02:00
return 0 ;
2013-02-06 14:06:39 +02:00
}
2014-02-18 14:31:08 +02:00
2013-03-27 16:58:29 +02:00
static SIMPLE_DEV_PM_OPS ( mei_me_pm_ops , mei_me_pci_suspend , mei_me_pci_resume ) ;
# define MEI_ME_PM_OPS (&mei_me_pm_ops)
2013-02-06 14:06:39 +02:00
# else
2013-03-27 16:58:29 +02:00
# define MEI_ME_PM_OPS NULL
2014-02-18 14:31:08 +02:00
# endif /* CONFIG_PM_SLEEP */
2013-02-06 14:06:39 +02:00
/*
* PCI driver structure
*/
2013-03-27 16:58:29 +02:00
static struct pci_driver mei_me_driver = {
2013-02-06 14:06:39 +02:00
. name = KBUILD_MODNAME ,
2013-03-27 16:58:29 +02:00
. id_table = mei_me_pci_tbl ,
. probe = mei_me_probe ,
. remove = mei_me_remove ,
. shutdown = mei_me_remove ,
. driver . pm = MEI_ME_PM_OPS ,
2013-02-06 14:06:39 +02:00
} ;
2013-03-27 16:58:29 +02:00
module_pci_driver ( mei_me_driver ) ;
2013-02-06 14:06:39 +02:00
MODULE_AUTHOR ( " Intel Corporation " ) ;
MODULE_DESCRIPTION ( " Intel(R) Management Engine Interface " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;