2005-04-17 02:20:36 +04:00
/**
* i2c - ali1563 . c - i2c driver for the ALi 1563 Southbridge
*
* Copyright ( C ) 2004 Patrick Mochel
2006-12-12 20:18:30 +03:00
* 2005 Rudolf Marek < r . marek @ assembler . cz >
2005-04-17 02:20:36 +04:00
*
* The 1563 southbridge is deceptively similar to the 1533 , with a
* few notable exceptions . One of those happens to be the fact they
* upgraded the i2c core to be 2.0 compliant , and happens to be almost
* identical to the i2c controller found in the Intel 801 south
* bridges .
*
* This driver is based on a mix of the 15 x3 , 1535 , and i801 drivers ,
* with a little help from the ALi 1563 spec .
*
* This file is released under the GPLv2
*/
# include <linux/module.h>
# include <linux/delay.h>
# include <linux/i2c.h>
# include <linux/pci.h>
# include <linux/init.h>
2008-07-15 00:38:33 +04:00
# include <linux/acpi.h>
2005-04-17 02:20:36 +04:00
# define ALI1563_MAX_TIMEOUT 500
# define ALI1563_SMBBA 0x80
# define ALI1563_SMB_IOEN 1
# define ALI1563_SMB_HOSTEN 2
# define ALI1563_SMB_IOSIZE 16
# define SMB_HST_STS (ali1563_smba + 0)
# define SMB_HST_CNTL1 (ali1563_smba + 1)
# define SMB_HST_CNTL2 (ali1563_smba + 2)
# define SMB_HST_CMD (ali1563_smba + 3)
# define SMB_HST_ADD (ali1563_smba + 4)
# define SMB_HST_DAT0 (ali1563_smba + 5)
# define SMB_HST_DAT1 (ali1563_smba + 6)
# define SMB_BLK_DAT (ali1563_smba + 7)
# define HST_STS_BUSY 0x01
# define HST_STS_INTR 0x02
# define HST_STS_DEVERR 0x04
# define HST_STS_BUSERR 0x08
# define HST_STS_FAIL 0x10
# define HST_STS_DONE 0x80
# define HST_STS_BAD 0x1c
# define HST_CNTL1_TIMEOUT 0x80
# define HST_CNTL1_LAST 0x40
# define HST_CNTL2_KILL 0x04
# define HST_CNTL2_START 0x40
# define HST_CNTL2_QUICK 0x00
# define HST_CNTL2_BYTE 0x01
# define HST_CNTL2_BYTE_DATA 0x02
# define HST_CNTL2_WORD_DATA 0x03
# define HST_CNTL2_BLOCK 0x05
2005-04-21 13:07:56 +04:00
# define HST_CNTL2_SIZEMASK 0x38
2005-04-17 02:20:36 +04:00
2005-09-25 18:37:04 +04:00
static struct pci_driver ali1563_pci_driver ;
2005-04-17 02:20:36 +04:00
static unsigned short ali1563_smba ;
2005-04-21 13:07:56 +04:00
static int ali1563_transaction ( struct i2c_adapter * a , int size )
2005-04-17 02:20:36 +04:00
{
u32 data ;
int timeout ;
2008-07-15 00:38:25 +04:00
int status = - EIO ;
2005-04-17 02:20:36 +04:00
dev_dbg ( & a - > dev , " Transaction (pre): STS=%02x, CNTL1=%02x, "
" CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x \n " ,
inb_p ( SMB_HST_STS ) , inb_p ( SMB_HST_CNTL1 ) , inb_p ( SMB_HST_CNTL2 ) ,
inb_p ( SMB_HST_CMD ) , inb_p ( SMB_HST_ADD ) , inb_p ( SMB_HST_DAT0 ) ,
inb_p ( SMB_HST_DAT1 ) ) ;
data = inb_p ( SMB_HST_STS ) ;
if ( data & HST_STS_BAD ) {
2005-04-21 13:07:56 +04:00
dev_err ( & a - > dev , " ali1563: Trying to reset busy device \n " ) ;
2005-04-17 02:20:36 +04:00
outb_p ( data | HST_STS_BAD , SMB_HST_STS ) ;
data = inb_p ( SMB_HST_STS ) ;
if ( data & HST_STS_BAD )
return - EBUSY ;
}
outb_p ( inb_p ( SMB_HST_CNTL2 ) | HST_CNTL2_START , SMB_HST_CNTL2 ) ;
timeout = ALI1563_MAX_TIMEOUT ;
2010-01-16 22:43:13 +03:00
do {
2005-04-17 02:20:36 +04:00
msleep ( 1 ) ;
2010-01-16 22:43:13 +03:00
} while ( ( ( data = inb_p ( SMB_HST_STS ) ) & HST_STS_BUSY ) & & - - timeout ) ;
2005-04-17 02:20:36 +04:00
dev_dbg ( & a - > dev , " Transaction (post): STS=%02x, CNTL1=%02x, "
" CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x \n " ,
inb_p ( SMB_HST_STS ) , inb_p ( SMB_HST_CNTL1 ) , inb_p ( SMB_HST_CNTL2 ) ,
inb_p ( SMB_HST_CMD ) , inb_p ( SMB_HST_ADD ) , inb_p ( SMB_HST_DAT0 ) ,
inb_p ( SMB_HST_DAT1 ) ) ;
if ( timeout & & ! ( data & HST_STS_BAD ) )
return 0 ;
2005-04-21 13:07:56 +04:00
if ( ! timeout ) {
dev_err ( & a - > dev , " Timeout - Trying to KILL transaction! \n " ) ;
2005-04-17 02:20:36 +04:00
/* Issue 'kill' to host controller */
outb_p ( HST_CNTL2_KILL , SMB_HST_CNTL2 ) ;
2005-04-21 13:07:56 +04:00
data = inb_p ( SMB_HST_STS ) ;
2008-07-15 00:38:25 +04:00
status = - ETIMEDOUT ;
2005-04-21 13:07:56 +04:00
}
/* device error - no response, ignore the autodetection case */
2008-07-15 00:38:25 +04:00
if ( data & HST_STS_DEVERR ) {
if ( size ! = HST_CNTL2_QUICK )
dev_err ( & a - > dev , " Device error! \n " ) ;
status = - ENXIO ;
2005-04-21 13:07:56 +04:00
}
/* bus collision */
if ( data & HST_STS_BUSERR ) {
dev_err ( & a - > dev , " Bus collision! \n " ) ;
/* Issue timeout, hoping it helps */
2005-04-17 02:20:36 +04:00
outb_p ( HST_CNTL1_TIMEOUT , SMB_HST_CNTL1 ) ;
2005-04-21 13:07:56 +04:00
}
if ( data & HST_STS_FAIL ) {
dev_err ( & a - > dev , " Cleaning fail after KILL! \n " ) ;
outb_p ( 0x0 , SMB_HST_CNTL2 ) ;
}
2008-07-15 00:38:25 +04:00
return status ;
2005-04-17 02:20:36 +04:00
}
static int ali1563_block_start ( struct i2c_adapter * a )
{
u32 data ;
int timeout ;
2008-07-15 00:38:25 +04:00
int status = - EIO ;
2005-04-17 02:20:36 +04:00
dev_dbg ( & a - > dev , " Block (pre): STS=%02x, CNTL1=%02x, "
" CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x \n " ,
inb_p ( SMB_HST_STS ) , inb_p ( SMB_HST_CNTL1 ) , inb_p ( SMB_HST_CNTL2 ) ,
inb_p ( SMB_HST_CMD ) , inb_p ( SMB_HST_ADD ) , inb_p ( SMB_HST_DAT0 ) ,
inb_p ( SMB_HST_DAT1 ) ) ;
data = inb_p ( SMB_HST_STS ) ;
if ( data & HST_STS_BAD ) {
dev_warn ( & a - > dev , " ali1563: Trying to reset busy device \n " ) ;
outb_p ( data | HST_STS_BAD , SMB_HST_STS ) ;
data = inb_p ( SMB_HST_STS ) ;
if ( data & HST_STS_BAD )
return - EBUSY ;
}
/* Clear byte-ready bit */
outb_p ( data | HST_STS_DONE , SMB_HST_STS ) ;
/* Start transaction and wait for byte-ready bit to be set */
outb_p ( inb_p ( SMB_HST_CNTL2 ) | HST_CNTL2_START , SMB_HST_CNTL2 ) ;
timeout = ALI1563_MAX_TIMEOUT ;
2010-01-16 22:43:13 +03:00
do {
2005-04-17 02:20:36 +04:00
msleep ( 1 ) ;
2010-01-16 22:43:13 +03:00
} while ( ! ( ( data = inb_p ( SMB_HST_STS ) ) & HST_STS_DONE ) & & - - timeout ) ;
2005-04-17 02:20:36 +04:00
dev_dbg ( & a - > dev , " Block (post): STS=%02x, CNTL1=%02x, "
" CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x \n " ,
inb_p ( SMB_HST_STS ) , inb_p ( SMB_HST_CNTL1 ) , inb_p ( SMB_HST_CNTL2 ) ,
inb_p ( SMB_HST_CMD ) , inb_p ( SMB_HST_ADD ) , inb_p ( SMB_HST_DAT0 ) ,
inb_p ( SMB_HST_DAT1 ) ) ;
if ( timeout & & ! ( data & HST_STS_BAD ) )
return 0 ;
2008-07-15 00:38:25 +04:00
if ( timeout = = 0 )
status = - ETIMEDOUT ;
if ( data & HST_STS_DEVERR )
status = - ENXIO ;
2005-04-21 13:07:56 +04:00
dev_err ( & a - > dev , " SMBus Error: %s%s%s%s%s \n " ,
2008-07-15 00:38:25 +04:00
timeout ? " " : " Timeout " ,
2005-04-17 02:20:36 +04:00
data & HST_STS_FAIL ? " Transaction Failed " : " " ,
data & HST_STS_BUSERR ? " No response or Bus Collision " : " " ,
data & HST_STS_DEVERR ? " Device Error " : " " ,
! ( data & HST_STS_DONE ) ? " Transaction Never Finished " : " " ) ;
2008-07-15 00:38:25 +04:00
return status ;
2005-04-17 02:20:36 +04:00
}
static int ali1563_block ( struct i2c_adapter * a , union i2c_smbus_data * data , u8 rw )
{
int i , len ;
int error = 0 ;
/* Do we need this? */
outb_p ( HST_CNTL1_LAST , SMB_HST_CNTL1 ) ;
if ( rw = = I2C_SMBUS_WRITE ) {
len = data - > block [ 0 ] ;
if ( len < 1 )
len = 1 ;
else if ( len > 32 )
len = 32 ;
outb_p ( len , SMB_HST_DAT0 ) ;
outb_p ( data - > block [ 1 ] , SMB_BLK_DAT ) ;
} else
len = 32 ;
outb_p ( inb_p ( SMB_HST_CNTL2 ) | HST_CNTL2_BLOCK , SMB_HST_CNTL2 ) ;
for ( i = 0 ; i < len ; i + + ) {
if ( rw = = I2C_SMBUS_WRITE ) {
outb_p ( data - > block [ i + 1 ] , SMB_BLK_DAT ) ;
if ( ( error = ali1563_block_start ( a ) ) )
break ;
} else {
if ( ( error = ali1563_block_start ( a ) ) )
break ;
if ( i = = 0 ) {
len = inb_p ( SMB_HST_DAT0 ) ;
if ( len < 1 )
len = 1 ;
else if ( len > 32 )
len = 32 ;
}
data - > block [ i + 1 ] = inb_p ( SMB_BLK_DAT ) ;
}
}
/* Do we need this? */
outb_p ( HST_CNTL1_LAST , SMB_HST_CNTL1 ) ;
return error ;
}
static s32 ali1563_access ( struct i2c_adapter * a , u16 addr ,
unsigned short flags , char rw , u8 cmd ,
int size , union i2c_smbus_data * data )
{
int error = 0 ;
int timeout ;
u32 reg ;
for ( timeout = ALI1563_MAX_TIMEOUT ; timeout ; timeout - - ) {
if ( ! ( reg = inb_p ( SMB_HST_STS ) & HST_STS_BUSY ) )
break ;
}
if ( ! timeout )
dev_warn ( & a - > dev , " SMBus not idle. HST_STS = %02x \n " , reg ) ;
outb_p ( 0xff , SMB_HST_STS ) ;
/* Map the size to what the chip understands */
switch ( size ) {
case I2C_SMBUS_QUICK :
size = HST_CNTL2_QUICK ;
break ;
case I2C_SMBUS_BYTE :
size = HST_CNTL2_BYTE ;
break ;
case I2C_SMBUS_BYTE_DATA :
size = HST_CNTL2_BYTE_DATA ;
break ;
case I2C_SMBUS_WORD_DATA :
size = HST_CNTL2_WORD_DATA ;
break ;
case I2C_SMBUS_BLOCK_DATA :
size = HST_CNTL2_BLOCK ;
break ;
2008-07-15 00:38:25 +04:00
default :
dev_warn ( & a - > dev , " Unsupported transaction %d \n " , size ) ;
error = - EOPNOTSUPP ;
goto Done ;
2005-04-17 02:20:36 +04:00
}
outb_p ( ( ( addr & 0x7f ) < < 1 ) | ( rw & 0x01 ) , SMB_HST_ADD ) ;
2005-04-21 13:07:56 +04:00
outb_p ( ( inb_p ( SMB_HST_CNTL2 ) & ~ HST_CNTL2_SIZEMASK ) | ( size < < 3 ) , SMB_HST_CNTL2 ) ;
2005-04-17 02:20:36 +04:00
/* Write the command register */
2005-04-21 13:07:56 +04:00
2005-04-17 02:20:36 +04:00
switch ( size ) {
case HST_CNTL2_BYTE :
if ( rw = = I2C_SMBUS_WRITE )
2005-04-21 13:07:56 +04:00
/* Beware it uses DAT0 register and not CMD! */
outb_p ( cmd , SMB_HST_DAT0 ) ;
2005-04-17 02:20:36 +04:00
break ;
case HST_CNTL2_BYTE_DATA :
outb_p ( cmd , SMB_HST_CMD ) ;
if ( rw = = I2C_SMBUS_WRITE )
outb_p ( data - > byte , SMB_HST_DAT0 ) ;
break ;
case HST_CNTL2_WORD_DATA :
outb_p ( cmd , SMB_HST_CMD ) ;
if ( rw = = I2C_SMBUS_WRITE ) {
outb_p ( data - > word & 0xff , SMB_HST_DAT0 ) ;
outb_p ( ( data - > word & 0xff00 ) > > 8 , SMB_HST_DAT1 ) ;
}
break ;
case HST_CNTL2_BLOCK :
outb_p ( cmd , SMB_HST_CMD ) ;
error = ali1563_block ( a , data , rw ) ;
goto Done ;
}
2005-04-21 13:07:56 +04:00
if ( ( error = ali1563_transaction ( a , size ) ) )
2005-04-17 02:20:36 +04:00
goto Done ;
if ( ( rw = = I2C_SMBUS_WRITE ) | | ( size = = HST_CNTL2_QUICK ) )
goto Done ;
switch ( size ) {
case HST_CNTL2_BYTE : /* Result put in SMBHSTDAT0 */
data - > byte = inb_p ( SMB_HST_DAT0 ) ;
break ;
case HST_CNTL2_BYTE_DATA :
data - > byte = inb_p ( SMB_HST_DAT0 ) ;
break ;
case HST_CNTL2_WORD_DATA :
data - > word = inb_p ( SMB_HST_DAT0 ) + ( inb_p ( SMB_HST_DAT1 ) < < 8 ) ;
break ;
}
Done :
return error ;
}
static u32 ali1563_func ( struct i2c_adapter * a )
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA ;
}
static int __devinit ali1563_setup ( struct pci_dev * dev )
{
u16 ctrl ;
pci_read_config_word ( dev , ALI1563_SMBBA , & ctrl ) ;
/* SMB I/O Base in high 12 bits and must be aligned with the
* size of the I / O space . */
ali1563_smba = ctrl & ~ ( ALI1563_SMB_IOSIZE - 1 ) ;
if ( ! ali1563_smba ) {
dev_warn ( & dev - > dev , " ali1563_smba Uninitialized \n " ) ;
goto Err ;
}
2007-02-14 00:08:57 +03:00
/* Check if device is enabled */
if ( ! ( ctrl & ALI1563_SMB_HOSTEN ) ) {
dev_warn ( & dev - > dev , " Host Controller not enabled \n " ) ;
goto Err ;
}
if ( ! ( ctrl & ALI1563_SMB_IOEN ) ) {
dev_warn ( & dev - > dev , " I/O space not enabled, trying manually \n " ) ;
pci_write_config_word ( dev , ALI1563_SMBBA ,
ctrl | ALI1563_SMB_IOEN ) ;
pci_read_config_word ( dev , ALI1563_SMBBA , & ctrl ) ;
if ( ! ( ctrl & ALI1563_SMB_IOEN ) ) {
dev_err ( & dev - > dev , " I/O space still not enabled, "
" giving up \n " ) ;
goto Err ;
}
}
2008-07-15 00:38:33 +04:00
if ( acpi_check_region ( ali1563_smba , ALI1563_SMB_IOSIZE ,
ali1563_pci_driver . name ) )
goto Err ;
2005-09-25 18:37:04 +04:00
if ( ! request_region ( ali1563_smba , ALI1563_SMB_IOSIZE ,
ali1563_pci_driver . name ) ) {
2007-02-14 00:08:57 +03:00
dev_err ( & dev - > dev , " Could not allocate I/O space at 0x%04x \n " ,
ali1563_smba ) ;
2005-04-17 02:20:36 +04:00
goto Err ;
}
2007-02-14 00:08:57 +03:00
dev_info ( & dev - > dev , " Found ALi1563 SMBus at 0x%04x \n " , ali1563_smba ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
Err :
return - ENODEV ;
}
static void ali1563_shutdown ( struct pci_dev * dev )
{
release_region ( ali1563_smba , ALI1563_SMB_IOSIZE ) ;
}
2006-09-04 00:39:46 +04:00
static const struct i2c_algorithm ali1563_algorithm = {
2005-04-17 02:20:36 +04:00
. smbus_xfer = ali1563_access ,
. functionality = ali1563_func ,
} ;
static struct i2c_adapter ali1563_adapter = {
. owner = THIS_MODULE ,
2008-07-15 00:38:29 +04:00
. class = I2C_CLASS_HWMON | I2C_CLASS_SPD ,
2005-04-17 02:20:36 +04:00
. algo = & ali1563_algorithm ,
} ;
static int __devinit ali1563_probe ( struct pci_dev * dev ,
const struct pci_device_id * id_table )
{
int error ;
if ( ( error = ali1563_setup ( dev ) ) )
2007-02-14 00:08:57 +03:00
goto exit ;
2005-04-17 02:20:36 +04:00
ali1563_adapter . dev . parent = & dev - > dev ;
2009-01-07 16:29:18 +03:00
snprintf ( ali1563_adapter . name , sizeof ( ali1563_adapter . name ) ,
" SMBus ALi 1563 Adapter @ %04x " , ali1563_smba ) ;
2005-04-17 02:20:36 +04:00
if ( ( error = i2c_add_adapter ( & ali1563_adapter ) ) )
2007-02-14 00:08:57 +03:00
goto exit_shutdown ;
return 0 ;
exit_shutdown :
ali1563_shutdown ( dev ) ;
exit :
dev_warn ( & dev - > dev , " ALi1563 SMBus probe failed (%d) \n " , error ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
static void __devexit ali1563_remove ( struct pci_dev * dev )
{
i2c_del_adapter ( & ali1563_adapter ) ;
ali1563_shutdown ( dev ) ;
}
2012-01-12 23:32:04 +04:00
static DEFINE_PCI_DEVICE_TABLE ( ali1563_id_table ) = {
2005-04-17 02:20:36 +04:00
{ PCI_DEVICE ( PCI_VENDOR_ID_AL , PCI_DEVICE_ID_AL_M1563 ) } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( pci , ali1563_id_table ) ;
static struct pci_driver ali1563_pci_driver = {
2005-09-25 18:37:04 +04:00
. name = " ali1563_smbus " ,
2005-04-17 02:20:36 +04:00
. id_table = ali1563_id_table ,
. probe = ali1563_probe ,
. remove = __devexit_p ( ali1563_remove ) ,
} ;
static int __init ali1563_init ( void )
{
return pci_register_driver ( & ali1563_pci_driver ) ;
}
module_init ( ali1563_init ) ;
static void __exit ali1563_exit ( void )
{
pci_unregister_driver ( & ali1563_pci_driver ) ;
}
module_exit ( ali1563_exit ) ;
MODULE_LICENSE ( " GPL " ) ;