2010-04-26 21:13:05 +04:00
/*
* intel_scu_ipc . c : Driver for the Intel SCU IPC mechanism
*
* ( C ) Copyright 2008 - 2010 Intel Corporation
* Author : Sreedhara DS ( sreedhara . ds @ intel . com )
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; version 2
* of the License .
*
2011-03-17 23:18:22 +03:00
* SCU running in ARC processor communicates with other entity running in IA
2010-04-26 21:13:05 +04:00
* core through IPC mechanism which in turn messaging between IA core ad SCU .
* SCU has two IPC mechanism IPC - 1 and IPC - 2. IPC - 1 is used between IA32 and
* SCU where IPC - 2 is used between P - Unit and SCU . This driver delas with
* IPC - 1 Driver provides an API for power control unit registers ( e . g . MSIC )
* along with other APIs .
*/
# include <linux/delay.h>
# include <linux/errno.h>
# include <linux/init.h>
2011-12-22 04:26:03 +04:00
# include <linux/device.h>
2010-04-26 21:13:05 +04:00
# include <linux/pm.h>
# include <linux/pci.h>
# include <linux/interrupt.h>
2010-09-13 18:55:05 +04:00
# include <linux/sfi.h>
2011-05-27 20:33:10 +04:00
# include <linux/module.h>
2013-10-18 02:35:27 +04:00
# include <asm/intel-mid.h>
2010-04-26 21:13:05 +04:00
# include <asm/intel_scu_ipc.h>
/* IPC defines the following message types */
# define IPCMSG_WATCHDOG_TIMER 0xF8 /* Set Kernel Watchdog Threshold */
# define IPCMSG_BATTERY 0xEF /* Coulomb Counter Accumulator */
# define IPCMSG_FW_UPDATE 0xFE /* Firmware update */
# define IPCMSG_PCNTRL 0xFF /* Power controller unit read/write */
# define IPCMSG_FW_REVISION 0xF4 /* Get firmware revision */
/* Command id associated with message IPCMSG_PCNTRL */
# define IPC_CMD_PCNTRL_W 0 /* Register write */
# define IPC_CMD_PCNTRL_R 1 /* Register read */
# define IPC_CMD_PCNTRL_M 2 /* Register read-modify-write */
/*
* IPC register summary
*
* IPC register blocks are memory mapped at fixed address of 0xFF11C000
* To read or write information to the SCU , driver writes to IPC - 1 memory
* mapped registers ( base address 0xFF11C000 ) . The following is the IPC
* mechanism
*
* 1. IA core cDMI interface claims this transaction and converts it to a
* Transaction Layer Packet ( TLP ) message which is sent across the cDMI .
*
* 2. South Complex cDMI block receives this message and writes it to
* the IPC - 1 register block , causing an interrupt to the SCU
*
* 3. SCU firmware decodes this interrupt and IPC message and the appropriate
* message handler is called within firmware .
*/
2010-07-26 13:04:24 +04:00
# define IPC_WWBUF_SIZE 20 /* IPC Write buffer Size */
# define IPC_RWBUF_SIZE 20 /* IPC Read buffer Size */
2013-11-16 04:21:54 +04:00
# define IPC_IOC 0x100 /* IPC command register IOC bit */
2013-11-15 02:15:04 +04:00
/* intel scu ipc driver data*/
struct intel_scu_ipc_pdata_t {
u32 ipc_base ;
u32 i2c_base ;
u32 ipc_len ;
u32 i2c_len ;
2013-11-16 04:21:54 +04:00
u8 irq_mode ;
2013-11-15 02:15:04 +04:00
} ;
2013-12-03 04:20:00 +04:00
static struct intel_scu_ipc_pdata_t intel_scu_ipc_lincroft_pdata = {
. ipc_base = 0xff11c000 ,
. i2c_base = 0xff12b000 ,
. ipc_len = 0x100 ,
. i2c_len = 0x10 ,
. irq_mode = 0 ,
} ;
/* Penwell and Cloverview */
static struct intel_scu_ipc_pdata_t intel_scu_ipc_penwell_pdata = {
. ipc_base = 0xff11c000 ,
. i2c_base = 0xff12b000 ,
. ipc_len = 0x100 ,
. i2c_len = 0x10 ,
. irq_mode = 1 ,
} ;
static struct intel_scu_ipc_pdata_t intel_scu_ipc_tangier_pdata = {
. ipc_base = 0xff009000 ,
. i2c_base = 0xff00d000 ,
. ipc_len = 0x100 ,
. i2c_len = 0x10 ,
. irq_mode = 0 ,
2013-11-15 02:15:04 +04:00
} ;
2010-04-26 21:13:05 +04:00
static int ipc_probe ( struct pci_dev * dev , const struct pci_device_id * id ) ;
static void ipc_remove ( struct pci_dev * pdev ) ;
struct intel_scu_ipc_dev {
struct pci_dev * pdev ;
void __iomem * ipc_base ;
void __iomem * i2c_base ;
2013-11-16 04:21:54 +04:00
struct completion cmd_complete ;
u8 irq_mode ;
2010-04-26 21:13:05 +04:00
} ;
static struct intel_scu_ipc_dev ipcdev ; /* Only one for now */
2010-07-26 13:02:25 +04:00
static int platform ; /* Platform type */
2010-04-26 21:13:05 +04:00
/*
* IPC Read Buffer ( Read Only ) :
* 16 byte buffer for receiving data from SCU , if IPC command
* processing results in response data
*/
# define IPC_READ_BUFFER 0x90
# define IPC_I2C_CNTRL_ADDR 0
# define I2C_DATA_ADDR 0x04
static DEFINE_MUTEX ( ipclock ) ; /* lock used to prevent multiple call to SCU */
/*
* Command Register ( Write Only ) :
* A write to this register results in an interrupt to the SCU core processor
* Format :
* | rfu2 ( 8 ) | size ( 8 ) | command id ( 4 ) | rfu1 ( 3 ) | ioc ( 1 ) | command ( 8 ) |
*/
static inline void ipc_command ( u32 cmd ) /* Send ipc command */
{
2013-11-16 04:21:54 +04:00
if ( ipcdev . irq_mode ) {
reinit_completion ( & ipcdev . cmd_complete ) ;
writel ( cmd | IPC_IOC , ipcdev . ipc_base ) ;
}
2010-04-26 21:13:05 +04:00
writel ( cmd , ipcdev . ipc_base ) ;
}
/*
* IPC Write Buffer ( Write Only ) :
* 16 - byte buffer for sending data associated with IPC command to
* SCU . Size of the data is specified in the IPC_COMMAND_REG register
*/
static inline void ipc_data_writel ( u32 data , u32 offset ) /* Write ipc data */
{
writel ( data , ipcdev . ipc_base + 0x80 + offset ) ;
}
/*
* Status Register ( Read Only ) :
* Driver will read this register to get the ready / busy status of the IPC
* block and error status of the IPC command that was just processed by SCU
* Format :
* | rfu3 ( 8 ) | error code ( 8 ) | initiator id ( 8 ) | cmd id ( 4 ) | rfu1 ( 2 ) | error ( 1 ) | busy ( 1 ) |
*/
static inline u8 ipc_read_status ( void )
{
return __raw_readl ( ipcdev . ipc_base + 0x04 ) ;
}
static inline u8 ipc_data_readb ( u32 offset ) /* Read ipc byte data */
{
return readb ( ipcdev . ipc_base + IPC_READ_BUFFER + offset ) ;
}
2010-07-26 13:02:46 +04:00
static inline u32 ipc_data_readl ( u32 offset ) /* Read ipc u32 data */
2010-04-26 21:13:05 +04:00
{
return readl ( ipcdev . ipc_base + IPC_READ_BUFFER + offset ) ;
}
static inline int busy_loop ( void ) /* Wait till scu status is busy */
{
u32 status = 0 ;
u32 loop_count = 0 ;
status = ipc_read_status ( ) ;
while ( status & 1 ) {
udelay ( 1 ) ; /* scu processing time is in few u secods */
status = ipc_read_status ( ) ;
loop_count + + ;
/* break if scu doesn't reset busy bit after huge retry */
if ( loop_count > 100000 ) {
dev_err ( & ipcdev . pdev - > dev , " IPC timed out " ) ;
return - ETIMEDOUT ;
}
}
2010-07-26 13:06:12 +04:00
if ( ( status > > 1 ) & 1 )
return - EIO ;
return 0 ;
2010-04-26 21:13:05 +04:00
}
2013-11-16 04:21:54 +04:00
/* Wait till ipc ioc interrupt is received or timeout in 3 HZ */
static inline int ipc_wait_for_interrupt ( void )
{
int status ;
if ( ! wait_for_completion_timeout ( & ipcdev . cmd_complete , 3 * HZ ) ) {
struct device * dev = & ipcdev . pdev - > dev ;
dev_err ( dev , " IPC timed out \n " ) ;
return - ETIMEDOUT ;
}
status = ipc_read_status ( ) ;
if ( ( status > > 1 ) & 1 )
return - EIO ;
return 0 ;
}
int intel_scu_ipc_check_status ( void )
{
return ipcdev . irq_mode ? ipc_wait_for_interrupt ( ) : busy_loop ( ) ;
}
2010-04-26 21:13:05 +04:00
/* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */
static int pwr_reg_rdwr ( u16 * addr , u8 * data , u32 count , u32 op , u32 id )
{
2012-03-06 03:01:02 +04:00
int nc ;
2010-04-26 21:13:05 +04:00
u32 offset = 0 ;
2011-01-25 17:12:12 +03:00
int err ;
2010-07-26 13:02:46 +04:00
u8 cbuf [ IPC_WWBUF_SIZE ] = { } ;
2010-04-26 21:13:05 +04:00
u32 * wbuf = ( u32 * ) & cbuf ;
mutex_lock ( & ipclock ) ;
2010-07-26 13:02:46 +04:00
2010-07-26 13:04:37 +04:00
memset ( cbuf , 0 , sizeof ( cbuf ) ) ;
2010-04-26 21:13:05 +04:00
if ( ipcdev . pdev = = NULL ) {
mutex_unlock ( & ipclock ) ;
return - ENODEV ;
}
2012-03-06 03:01:02 +04:00
for ( nc = 0 ; nc < count ; nc + + , offset + = 2 ) {
cbuf [ offset ] = addr [ nc ] ;
cbuf [ offset + 1 ] = addr [ nc ] > > 8 ;
}
2010-04-26 21:13:05 +04:00
2012-03-06 03:01:02 +04:00
if ( id = = IPC_CMD_PCNTRL_R ) {
for ( nc = 0 , offset = 0 ; nc < count ; nc + + , offset + = 4 )
ipc_data_writel ( wbuf [ nc ] , offset ) ;
ipc_command ( ( count * 2 ) < < 16 | id < < 12 | 0 < < 8 | op ) ;
} else if ( id = = IPC_CMD_PCNTRL_W ) {
for ( nc = 0 ; nc < count ; nc + + , offset + = 1 )
cbuf [ offset ] = data [ nc ] ;
for ( nc = 0 , offset = 0 ; nc < count ; nc + + , offset + = 4 )
ipc_data_writel ( wbuf [ nc ] , offset ) ;
ipc_command ( ( count * 3 ) < < 16 | id < < 12 | 0 < < 8 | op ) ;
} else if ( id = = IPC_CMD_PCNTRL_M ) {
cbuf [ offset ] = data [ 0 ] ;
cbuf [ offset + 1 ] = data [ 1 ] ;
ipc_data_writel ( wbuf [ 0 ] , 0 ) ; /* Write wbuff */
ipc_command ( 4 < < 16 | id < < 12 | 0 < < 8 | op ) ;
2010-07-26 13:02:46 +04:00
}
2010-04-26 21:13:05 +04:00
2013-11-16 04:21:54 +04:00
err = intel_scu_ipc_check_status ( ) ;
2013-11-15 02:15:06 +04:00
if ( ! err & & id = = IPC_CMD_PCNTRL_R ) { /* Read rbuf */
2010-04-26 21:13:05 +04:00
/* Workaround: values are read as 0 without memcpy_fromio */
2010-07-26 13:02:46 +04:00
memcpy_fromio ( cbuf , ipcdev . ipc_base + 0x90 , 16 ) ;
2012-03-06 03:01:02 +04:00
for ( nc = 0 ; nc < count ; nc + + )
data [ nc ] = ipc_data_readb ( nc ) ;
2010-04-26 21:13:05 +04:00
}
mutex_unlock ( & ipclock ) ;
return err ;
}
/**
* intel_scu_ipc_ioread8 - read a word via the SCU
* @ addr : register on SCU
* @ data : return pointer for read byte
*
* Read a single register . Returns 0 on success or an error code . All
* locking between SCU accesses is handled for the caller .
*
* This function may sleep .
*/
int intel_scu_ipc_ioread8 ( u16 addr , u8 * data )
{
return pwr_reg_rdwr ( & addr , data , 1 , IPCMSG_PCNTRL , IPC_CMD_PCNTRL_R ) ;
}
EXPORT_SYMBOL ( intel_scu_ipc_ioread8 ) ;
/**
* intel_scu_ipc_ioread16 - read a word via the SCU
* @ addr : register on SCU
* @ data : return pointer for read word
*
* Read a register pair . Returns 0 on success or an error code . All
* locking between SCU accesses is handled for the caller .
*
* This function may sleep .
*/
int intel_scu_ipc_ioread16 ( u16 addr , u16 * data )
{
u16 x [ 2 ] = { addr , addr + 1 } ;
return pwr_reg_rdwr ( x , ( u8 * ) data , 2 , IPCMSG_PCNTRL , IPC_CMD_PCNTRL_R ) ;
}
EXPORT_SYMBOL ( intel_scu_ipc_ioread16 ) ;
/**
* intel_scu_ipc_ioread32 - read a dword via the SCU
* @ addr : register on SCU
* @ data : return pointer for read dword
*
* Read four registers . Returns 0 on success or an error code . All
* locking between SCU accesses is handled for the caller .
*
* This function may sleep .
*/
int intel_scu_ipc_ioread32 ( u16 addr , u32 * data )
{
u16 x [ 4 ] = { addr , addr + 1 , addr + 2 , addr + 3 } ;
return pwr_reg_rdwr ( x , ( u8 * ) data , 4 , IPCMSG_PCNTRL , IPC_CMD_PCNTRL_R ) ;
}
EXPORT_SYMBOL ( intel_scu_ipc_ioread32 ) ;
/**
* intel_scu_ipc_iowrite8 - write a byte via the SCU
* @ addr : register on SCU
* @ data : byte to write
*
* Write a single register . Returns 0 on success or an error code . All
* locking between SCU accesses is handled for the caller .
*
* This function may sleep .
*/
int intel_scu_ipc_iowrite8 ( u16 addr , u8 data )
{
return pwr_reg_rdwr ( & addr , & data , 1 , IPCMSG_PCNTRL , IPC_CMD_PCNTRL_W ) ;
}
EXPORT_SYMBOL ( intel_scu_ipc_iowrite8 ) ;
/**
* intel_scu_ipc_iowrite16 - write a word via the SCU
* @ addr : register on SCU
* @ data : word to write
*
* Write two registers . Returns 0 on success or an error code . All
* locking between SCU accesses is handled for the caller .
*
* This function may sleep .
*/
int intel_scu_ipc_iowrite16 ( u16 addr , u16 data )
{
u16 x [ 2 ] = { addr , addr + 1 } ;
return pwr_reg_rdwr ( x , ( u8 * ) & data , 2 , IPCMSG_PCNTRL , IPC_CMD_PCNTRL_W ) ;
}
EXPORT_SYMBOL ( intel_scu_ipc_iowrite16 ) ;
/**
* intel_scu_ipc_iowrite32 - write a dword via the SCU
* @ addr : register on SCU
* @ data : dword to write
*
* Write four registers . Returns 0 on success or an error code . All
* locking between SCU accesses is handled for the caller .
*
* This function may sleep .
*/
int intel_scu_ipc_iowrite32 ( u16 addr , u32 data )
{
u16 x [ 4 ] = { addr , addr + 1 , addr + 2 , addr + 3 } ;
return pwr_reg_rdwr ( x , ( u8 * ) & data , 4 , IPCMSG_PCNTRL , IPC_CMD_PCNTRL_W ) ;
}
EXPORT_SYMBOL ( intel_scu_ipc_iowrite32 ) ;
/**
* intel_scu_ipc_readvv - read a set of registers
* @ addr : register list
* @ data : bytes to return
* @ len : length of array
*
* Read registers . Returns 0 on success or an error code . All
* locking between SCU accesses is handled for the caller .
*
* The largest array length permitted by the hardware is 5 items .
*
* This function may sleep .
*/
int intel_scu_ipc_readv ( u16 * addr , u8 * data , int len )
{
return pwr_reg_rdwr ( addr , data , len , IPCMSG_PCNTRL , IPC_CMD_PCNTRL_R ) ;
}
EXPORT_SYMBOL ( intel_scu_ipc_readv ) ;
/**
* intel_scu_ipc_writev - write a set of registers
* @ addr : register list
* @ data : bytes to write
* @ len : length of array
*
* Write registers . Returns 0 on success or an error code . All
* locking between SCU accesses is handled for the caller .
*
* The largest array length permitted by the hardware is 5 items .
*
* This function may sleep .
*
*/
int intel_scu_ipc_writev ( u16 * addr , u8 * data , int len )
{
return pwr_reg_rdwr ( addr , data , len , IPCMSG_PCNTRL , IPC_CMD_PCNTRL_W ) ;
}
EXPORT_SYMBOL ( intel_scu_ipc_writev ) ;
/**
* intel_scu_ipc_update_register - r / m / w a register
* @ addr : register address
* @ bits : bits to update
* @ mask : mask of bits to update
*
* Read - modify - write power control unit register . The first data argument
* must be register value and second is mask value
* mask is a bitmap that indicates which bits to update .
* 0 = masked . Don ' t modify this bit , 1 = modify this bit .
* returns 0 on success or an error code .
*
* This function may sleep . Locking between SCU accesses is handled
* for the caller .
*/
int intel_scu_ipc_update_register ( u16 addr , u8 bits , u8 mask )
{
u8 data [ 2 ] = { bits , mask } ;
return pwr_reg_rdwr ( & addr , data , 1 , IPCMSG_PCNTRL , IPC_CMD_PCNTRL_M ) ;
}
EXPORT_SYMBOL ( intel_scu_ipc_update_register ) ;
/**
* intel_scu_ipc_simple_command - send a simple command
* @ cmd : command
* @ sub : sub type
*
* Issue a simple command to the SCU . Do not use this interface if
* you must then access data as any data values may be overwritten
* by another SCU access by the time this function returns .
*
* This function may sleep . Locking for SCU accesses is handled for
* the caller .
*/
int intel_scu_ipc_simple_command ( int cmd , int sub )
{
2011-01-25 17:12:12 +03:00
int err ;
2010-04-26 21:13:05 +04:00
mutex_lock ( & ipclock ) ;
if ( ipcdev . pdev = = NULL ) {
mutex_unlock ( & ipclock ) ;
return - ENODEV ;
}
2010-07-19 12:37:42 +04:00
ipc_command ( sub < < 12 | cmd ) ;
2013-11-16 04:21:54 +04:00
err = intel_scu_ipc_check_status ( ) ;
2010-04-26 21:13:05 +04:00
mutex_unlock ( & ipclock ) ;
return err ;
}
EXPORT_SYMBOL ( intel_scu_ipc_simple_command ) ;
/**
* intel_scu_ipc_command - command with data
* @ cmd : command
* @ sub : sub type
* @ in : input data
2010-07-19 12:37:42 +04:00
* @ inlen : input length in dwords
2010-04-26 21:13:05 +04:00
* @ out : output data
2010-07-19 12:37:42 +04:00
* @ outlein : output length in dwords
2010-04-26 21:13:05 +04:00
*
* Issue a command to the SCU which involves data transfers . Do the
* data copies under the lock but leave it for the caller to interpret
*/
int intel_scu_ipc_command ( int cmd , int sub , u32 * in , int inlen ,
u32 * out , int outlen )
{
2011-01-25 17:12:12 +03:00
int i , err ;
2010-04-26 21:13:05 +04:00
mutex_lock ( & ipclock ) ;
if ( ipcdev . pdev = = NULL ) {
mutex_unlock ( & ipclock ) ;
return - ENODEV ;
}
for ( i = 0 ; i < inlen ; i + + )
ipc_data_writel ( * in + + , 4 * i ) ;
2010-07-26 13:06:31 +04:00
ipc_command ( ( inlen < < 16 ) | ( sub < < 12 ) | cmd ) ;
2013-11-16 04:21:54 +04:00
err = intel_scu_ipc_check_status ( ) ;
2010-04-26 21:13:05 +04:00
2013-11-15 02:15:06 +04:00
if ( ! err ) {
for ( i = 0 ; i < outlen ; i + + )
* out + + = ipc_data_readl ( 4 * i ) ;
}
2010-04-26 21:13:05 +04:00
mutex_unlock ( & ipclock ) ;
return err ;
}
EXPORT_SYMBOL ( intel_scu_ipc_command ) ;
/*I2C commands */
# define IPC_I2C_WRITE 1 /* I2C Write command */
# define IPC_I2C_READ 2 /* I2C Read command */
/**
* intel_scu_ipc_i2c_cntrl - I2C read / write operations
* @ addr : I2C address + command bits
* @ data : data to read / write
*
* Perform an an I2C read / write operation via the SCU . All locking is
* handled for the caller . This function may sleep .
*
* Returns an error code or 0 on success .
*
* This has to be in the IPC driver for the locking .
*/
int intel_scu_ipc_i2c_cntrl ( u32 addr , u32 * data )
{
u32 cmd = 0 ;
mutex_lock ( & ipclock ) ;
2010-07-19 12:37:42 +04:00
if ( ipcdev . pdev = = NULL ) {
mutex_unlock ( & ipclock ) ;
return - ENODEV ;
}
2010-04-26 21:13:05 +04:00
cmd = ( addr > > 24 ) & 0xFF ;
if ( cmd = = IPC_I2C_READ ) {
writel ( addr , ipcdev . i2c_base + IPC_I2C_CNTRL_ADDR ) ;
/* Write not getting updated without delay */
mdelay ( 1 ) ;
* data = readl ( ipcdev . i2c_base + I2C_DATA_ADDR ) ;
} else if ( cmd = = IPC_I2C_WRITE ) {
2010-08-24 17:32:38 +04:00
writel ( * data , ipcdev . i2c_base + I2C_DATA_ADDR ) ;
2010-04-26 21:13:05 +04:00
mdelay ( 1 ) ;
writel ( addr , ipcdev . i2c_base + IPC_I2C_CNTRL_ADDR ) ;
} else {
dev_err ( & ipcdev . pdev - > dev ,
" intel_scu_ipc: I2C INVALID_CMD = 0x%x \n " , cmd ) ;
mutex_unlock ( & ipclock ) ;
2010-10-22 18:43:55 +04:00
return - EIO ;
2010-04-26 21:13:05 +04:00
}
mutex_unlock ( & ipclock ) ;
return 0 ;
}
EXPORT_SYMBOL ( intel_scu_ipc_i2c_cntrl ) ;
/*
* Interrupt handler gets called when ioc bit of IPC_COMMAND_REG set to 1
* When ioc bit is set to 1 , caller api must wait for interrupt handler called
* which in turn unlocks the caller api . Currently this is not used
*
* This is edge triggered so we need take no action to clear anything
*/
static irqreturn_t ioc ( int irq , void * dev_id )
{
2013-11-16 04:21:54 +04:00
if ( ipcdev . irq_mode )
complete ( & ipcdev . cmd_complete ) ;
2010-04-26 21:13:05 +04:00
return IRQ_HANDLED ;
}
/**
* ipc_probe - probe an Intel SCU IPC
* @ dev : the PCI device matching
* @ id : entry in the match table
*
* Enable and install an intel SCU IPC . This appears in the PCI space
* but uses some hard coded addresses as well .
*/
static int ipc_probe ( struct pci_dev * dev , const struct pci_device_id * id )
{
2013-12-03 04:20:00 +04:00
int err ;
2013-11-15 02:15:04 +04:00
struct intel_scu_ipc_pdata_t * pdata ;
2010-04-26 21:13:05 +04:00
resource_size_t pci_resource ;
if ( ipcdev . pdev ) /* We support only one SCU */
return - EBUSY ;
2013-12-03 04:20:00 +04:00
pdata = ( struct intel_scu_ipc_pdata_t * ) id - > driver_data ;
2013-11-15 02:15:04 +04:00
2010-04-26 21:13:05 +04:00
ipcdev . pdev = pci_dev_get ( dev ) ;
2013-11-16 04:21:54 +04:00
ipcdev . irq_mode = pdata - > irq_mode ;
2010-04-26 21:13:05 +04:00
err = pci_enable_device ( dev ) ;
if ( err )
return err ;
err = pci_request_regions ( dev , " intel_scu_ipc " ) ;
if ( err )
return err ;
pci_resource = pci_resource_start ( dev , 0 ) ;
if ( ! pci_resource )
return - ENOMEM ;
2013-11-16 04:21:54 +04:00
init_completion ( & ipcdev . cmd_complete ) ;
2010-04-26 21:13:05 +04:00
if ( request_irq ( dev - > irq , ioc , 0 , " intel_scu_ipc " , & ipcdev ) )
return - EBUSY ;
2013-11-15 02:15:04 +04:00
ipcdev . ipc_base = ioremap_nocache ( pdata - > ipc_base , pdata - > ipc_len ) ;
2010-04-26 21:13:05 +04:00
if ( ! ipcdev . ipc_base )
return - ENOMEM ;
2013-11-15 02:15:04 +04:00
ipcdev . i2c_base = ioremap_nocache ( pdata - > i2c_base , pdata - > i2c_len ) ;
2010-04-26 21:13:05 +04:00
if ( ! ipcdev . i2c_base ) {
iounmap ( ipcdev . ipc_base ) ;
return - ENOMEM ;
}
2010-11-09 14:22:58 +03:00
intel_scu_devices_create ( ) ;
2010-04-26 21:13:05 +04:00
return 0 ;
}
/**
* ipc_remove - remove a bound IPC device
* @ pdev : PCI device
*
* In practice the SCU is not removable but this function is also
* called for each device on a module unload or cleanup which is the
* path that will get used .
*
* Free up the mappings and release the PCI resources
*/
static void ipc_remove ( struct pci_dev * pdev )
{
free_irq ( pdev - > irq , & ipcdev ) ;
pci_release_regions ( pdev ) ;
pci_dev_put ( ipcdev . pdev ) ;
iounmap ( ipcdev . ipc_base ) ;
iounmap ( ipcdev . i2c_base ) ;
ipcdev . pdev = NULL ;
2010-11-09 14:22:58 +03:00
intel_scu_devices_destroy ( ) ;
2010-04-26 21:13:05 +04:00
}
2011-07-07 06:22:46 +04:00
static DEFINE_PCI_DEVICE_TABLE ( pci_ids ) = {
2013-12-03 04:20:00 +04:00
{
PCI_VDEVICE ( INTEL , 0x082a ) ,
( kernel_ulong_t ) & intel_scu_ipc_lincroft_pdata ,
} , {
PCI_VDEVICE ( INTEL , 0x080e ) ,
( kernel_ulong_t ) & intel_scu_ipc_penwell_pdata ,
} , {
PCI_VDEVICE ( INTEL , 0x08ea ) ,
( kernel_ulong_t ) & intel_scu_ipc_penwell_pdata ,
} , {
PCI_VDEVICE ( INTEL , 0x11a0 ) ,
( kernel_ulong_t ) & intel_scu_ipc_tangier_pdata ,
} , {
0 ,
}
2010-04-26 21:13:05 +04:00
} ;
MODULE_DEVICE_TABLE ( pci , pci_ids ) ;
static struct pci_driver ipc_driver = {
. name = " intel_scu_ipc " ,
. id_table = pci_ids ,
. probe = ipc_probe ,
. remove = ipc_remove ,
} ;
static int __init intel_scu_ipc_init ( void )
{
2013-10-18 02:35:29 +04:00
platform = intel_mid_identify_cpu ( ) ;
2010-07-26 13:03:58 +04:00
if ( platform = = 0 )
return - ENODEV ;
2010-04-26 21:13:05 +04:00
return pci_register_driver ( & ipc_driver ) ;
}
static void __exit intel_scu_ipc_exit ( void )
{
pci_unregister_driver ( & ipc_driver ) ;
}
MODULE_AUTHOR ( " Sreedhara DS <sreedhara.ds@intel.com> " ) ;
MODULE_DESCRIPTION ( " Intel SCU IPC driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_init ( intel_scu_ipc_init ) ;
module_exit ( intel_scu_ipc_exit ) ;