2005-04-16 15:20:36 -07:00
/*
Copyright ( c ) 1998 - 2002 Frodo Looijaard < frodol @ dds . nl > and
Philip Edelbrock < phil @ netroedge . 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 ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that 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 .
*/
/*
Supports :
Intel PIIX4 , 440 MX
2009-03-28 21:34:46 +01:00
Serverworks OSB4 , CSB5 , CSB6 , HT - 1000 , HT - 1100
2012-07-24 14:13:57 +02:00
ATI IXP200 , IXP300 , IXP400 , SB600 , SB700 / SP5100 , SB800
2014-01-22 14:05:46 -08:00
AMD Hudson - 2 , ML , CZ
2005-04-16 15:20:36 -07:00
SMSC Victory66
2012-07-24 14:13:57 +02:00
Note : we assume there can only be one device , with one or more
SMBus interfaces .
2005-04-16 15:20:36 -07:00
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/pci.h>
# include <linux/kernel.h>
# include <linux/delay.h>
# include <linux/stddef.h>
# include <linux/ioport.h>
# include <linux/i2c.h>
2012-10-05 22:23:55 +02:00
# include <linux/slab.h>
2005-04-16 15:20:36 -07:00
# include <linux/dmi.h>
2008-07-14 22:38:33 +02:00
# include <linux/acpi.h>
2010-05-21 18:41:01 +02:00
# include <linux/io.h>
2005-04-16 15:20:36 -07:00
/* PIIX4 SMBus address offsets */
# define SMBHSTSTS (0 + piix4_smba)
# define SMBHSLVSTS (1 + piix4_smba)
# define SMBHSTCNT (2 + piix4_smba)
# define SMBHSTCMD (3 + piix4_smba)
# define SMBHSTADD (4 + piix4_smba)
# define SMBHSTDAT0 (5 + piix4_smba)
# define SMBHSTDAT1 (6 + piix4_smba)
# define SMBBLKDAT (7 + piix4_smba)
# define SMBSLVCNT (8 + piix4_smba)
# define SMBSHDWCMD (9 + piix4_smba)
# define SMBSLVEVT (0xA + piix4_smba)
# define SMBSLVDAT (0xC + piix4_smba)
/* count for request_region */
# define SMBIOSIZE 8
/* PCI Address Constants */
# define SMBBA 0x090
# define SMBHSTCFG 0x0D2
# define SMBSLVC 0x0D3
# define SMBSHDW1 0x0D4
# define SMBSHDW2 0x0D5
# define SMBREV 0x0D6
/* Other settings */
# define MAX_TIMEOUT 500
# define ENABLE_INT9 0
/* PIIX4 constants */
# define PIIX4_QUICK 0x00
# define PIIX4_BYTE 0x04
# define PIIX4_BYTE_DATA 0x08
# define PIIX4_WORD_DATA 0x0C
# define PIIX4_BLOCK_DATA 0x14
/* insmod parameters */
/* If force is set to anything different from 0, we forcibly enable the
PIIX4 . DANGEROUS ! */
2005-09-25 16:23:07 +02:00
static int force ;
2005-04-16 15:20:36 -07:00
module_param ( force , int , 0 ) ;
MODULE_PARM_DESC ( force , " Forcibly enable the PIIX4. DANGEROUS! " ) ;
/* If force_addr is set to anything different from 0, we forcibly enable
the PIIX4 at the given address . VERY DANGEROUS ! */
2005-09-25 16:23:07 +02:00
static int force_addr ;
2005-04-16 15:20:36 -07:00
module_param ( force_addr , int , 0 ) ;
MODULE_PARM_DESC ( force_addr ,
" Forcibly enable the PIIX4 at the given address. "
" EXTREMELY DANGEROUS! " ) ;
2008-05-11 20:37:05 +02:00
static int srvrworks_csb5_delay ;
2005-09-25 16:37:04 +02:00
static struct pci_driver piix4_driver ;
2005-04-16 15:20:36 -07:00
2012-11-27 15:59:38 -05:00
static const struct dmi_system_id piix4_dmi_blacklist [ ] = {
2008-05-11 20:37:05 +02:00
{
. ident = " Sapphire AM2RD790 " ,
. matches = {
DMI_MATCH ( DMI_BOARD_VENDOR , " SAPPHIRE Inc. " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " PC-AM2RD790 " ) ,
} ,
} ,
{
. ident = " DFI Lanparty UT 790FX " ,
. matches = {
DMI_MATCH ( DMI_BOARD_VENDOR , " DFI Inc. " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " LP UT 790FX " ) ,
} ,
} ,
{ }
} ;
/* The IBM entry is in a separate table because we only check it
on Intel - based systems */
2012-11-27 15:59:38 -05:00
static const struct dmi_system_id piix4_dmi_ibm [ ] = {
2005-04-16 15:20:36 -07:00
{
. ident = " IBM " ,
. matches = { DMI_MATCH ( DMI_SYS_VENDOR , " IBM " ) , } ,
} ,
{ } ,
} ;
2012-07-24 14:13:56 +02:00
struct i2c_piix4_adapdata {
unsigned short smba ;
} ;
2012-11-27 15:59:38 -05:00
static int piix4_setup ( struct pci_dev * PIIX4_dev ,
const struct pci_device_id * id )
2005-04-16 15:20:36 -07:00
{
unsigned char temp ;
2012-07-24 14:13:56 +02:00
unsigned short piix4_smba ;
2005-04-16 15:20:36 -07:00
2008-05-11 20:37:05 +02:00
if ( ( PIIX4_dev - > vendor = = PCI_VENDOR_ID_SERVERWORKS ) & &
( PIIX4_dev - > device = = PCI_DEVICE_ID_SERVERWORKS_CSB5 ) )
srvrworks_csb5_delay = 1 ;
2008-05-11 20:37:05 +02:00
/* On some motherboards, it was reported that accessing the SMBus
caused severe hardware problems */
if ( dmi_check_system ( piix4_dmi_blacklist ) ) {
dev_err ( & PIIX4_dev - > dev ,
" Accessing the SMBus on this system is unsafe! \n " ) ;
return - EPERM ;
}
2005-04-16 15:20:36 -07:00
/* Don't access SMBus on IBM systems which get corrupted eeproms */
2008-05-11 20:37:05 +02:00
if ( dmi_check_system ( piix4_dmi_ibm ) & &
2005-04-16 15:20:36 -07:00
PIIX4_dev - > vendor = = PCI_VENDOR_ID_INTEL ) {
2006-04-25 13:37:25 +02:00
dev_err ( & PIIX4_dev - > dev , " IBM system detected; this module "
2005-04-16 15:20:36 -07:00
" may corrupt your serial eeprom! Refusing to load "
" module! \n " ) ;
return - EPERM ;
}
/* Determine the address of the SMBus areas */
if ( force_addr ) {
piix4_smba = force_addr & 0xfff0 ;
force = 0 ;
} else {
pci_read_config_word ( PIIX4_dev , SMBBA , & piix4_smba ) ;
piix4_smba & = 0xfff0 ;
if ( piix4_smba = = 0 ) {
2008-07-14 22:38:25 +02:00
dev_err ( & PIIX4_dev - > dev , " SMBus base address "
2005-04-16 15:20:36 -07:00
" uninitialized - upgrade BIOS or use "
" force_addr=0xaddr \n " ) ;
return - ENODEV ;
}
}
2008-07-14 22:38:33 +02:00
if ( acpi_check_region ( piix4_smba , SMBIOSIZE , piix4_driver . name ) )
2009-10-04 22:53:45 +02:00
return - ENODEV ;
2008-07-14 22:38:33 +02:00
2005-09-25 16:37:04 +02:00
if ( ! request_region ( piix4_smba , SMBIOSIZE , piix4_driver . name ) ) {
2008-07-14 22:38:25 +02:00
dev_err ( & PIIX4_dev - > dev , " SMBus region 0x%x already in use! \n " ,
2005-04-16 15:20:36 -07:00
piix4_smba ) ;
2008-07-14 22:38:25 +02:00
return - EBUSY ;
2005-04-16 15:20:36 -07:00
}
pci_read_config_byte ( PIIX4_dev , SMBHSTCFG , & temp ) ;
/* If force_addr is set, we program the new address here. Just to make
sure , we disable the PIIX4 first . */
if ( force_addr ) {
pci_write_config_byte ( PIIX4_dev , SMBHSTCFG , temp & 0xfe ) ;
pci_write_config_word ( PIIX4_dev , SMBBA , piix4_smba ) ;
pci_write_config_byte ( PIIX4_dev , SMBHSTCFG , temp | 0x01 ) ;
dev_info ( & PIIX4_dev - > dev , " WARNING: SMBus interface set to "
" new address %04x! \n " , piix4_smba ) ;
} else if ( ( temp & 1 ) = = 0 ) {
if ( force ) {
/* This should never need to be done, but has been
* noted that many Dell machines have the SMBus
* interface on the PIIX4 disabled ! ? NOTE : This assumes
* I / O space and other allocations WERE done by the
* Bios ! Don ' t complain if your hardware does weird
* things after enabling this . : ' ) Check for Bios
* updates before resorting to this .
*/
pci_write_config_byte ( PIIX4_dev , SMBHSTCFG ,
temp | 1 ) ;
2012-12-16 21:11:55 +01:00
dev_notice ( & PIIX4_dev - > dev ,
" WARNING: SMBus interface has been FORCEFULLY ENABLED! \n " ) ;
2005-04-16 15:20:36 -07:00
} else {
dev_err ( & PIIX4_dev - > dev ,
2014-01-23 16:59:38 +01:00
" SMBus Host Controller not enabled! \n " ) ;
2005-04-16 15:20:36 -07:00
release_region ( piix4_smba , SMBIOSIZE ) ;
return - ENODEV ;
}
}
2006-04-25 13:06:41 +02:00
if ( ( ( temp & 0x0E ) = = 8 ) | | ( ( temp & 0x0E ) = = 2 ) )
2014-01-23 16:59:38 +01:00
dev_dbg ( & PIIX4_dev - > dev , " Using IRQ for SMBus \n " ) ;
2005-04-16 15:20:36 -07:00
else if ( ( temp & 0x0E ) = = 0 )
2014-01-23 16:59:38 +01:00
dev_dbg ( & PIIX4_dev - > dev , " Using SMI# for SMBus \n " ) ;
2005-04-16 15:20:36 -07:00
else
dev_err ( & PIIX4_dev - > dev , " Illegal Interrupt configuration "
" (or code out of date)! \n " ) ;
pci_read_config_byte ( PIIX4_dev , SMBREV , & temp ) ;
2008-07-14 22:38:25 +02:00
dev_info ( & PIIX4_dev - > dev ,
" SMBus Host Controller at 0x%x, revision %d \n " ,
piix4_smba , temp ) ;
2005-04-16 15:20:36 -07:00
2012-07-24 14:13:56 +02:00
return piix4_smba ;
2005-04-16 15:20:36 -07:00
}
2012-11-27 15:59:38 -05:00
static int piix4_setup_sb800 ( struct pci_dev * PIIX4_dev ,
2013-07-14 23:17:26 +02:00
const struct pci_device_id * id , u8 aux )
2009-03-28 21:34:46 +01:00
{
2012-07-24 14:13:56 +02:00
unsigned short piix4_smba ;
2009-03-28 21:34:46 +01:00
unsigned short smba_idx = 0xcd6 ;
2014-01-22 14:05:46 -08:00
u8 smba_en_lo , smba_en_hi , smb_en , smb_en_status ;
u8 i2ccfg , i2ccfg_offset = 0x10 ;
2009-03-28 21:34:46 +01:00
2009-11-07 13:10:46 +01:00
/* SB800 and later SMBus does not support forcing address */
2009-03-28 21:34:46 +01:00
if ( force | | force_addr ) {
2009-11-07 13:10:46 +01:00
dev_err ( & PIIX4_dev - > dev , " SMBus does not support "
2009-03-28 21:34:46 +01:00
" forcing address! \n " ) ;
return - EINVAL ;
}
/* Determine the address of the SMBus areas */
2014-01-22 14:05:46 -08:00
if ( ( PIIX4_dev - > vendor = = PCI_VENDOR_ID_AMD & &
PIIX4_dev - > device = = PCI_DEVICE_ID_AMD_HUDSON2_SMBUS & &
PIIX4_dev - > revision > = 0x41 ) | |
( PIIX4_dev - > vendor = = PCI_VENDOR_ID_AMD & &
PIIX4_dev - > device = = 0x790b & &
PIIX4_dev - > revision > = 0x49 ) )
smb_en = 0x00 ;
else
smb_en = ( aux ) ? 0x28 : 0x2c ;
2013-07-14 23:17:26 +02:00
2009-03-28 21:34:46 +01:00
if ( ! request_region ( smba_idx , 2 , " smba_idx " ) ) {
dev_err ( & PIIX4_dev - > dev , " SMBus base address index region "
" 0x%x already in use! \n " , smba_idx ) ;
return - EBUSY ;
}
outb_p ( smb_en , smba_idx ) ;
smba_en_lo = inb_p ( smba_idx + 1 ) ;
outb_p ( smb_en + 1 , smba_idx ) ;
smba_en_hi = inb_p ( smba_idx + 1 ) ;
release_region ( smba_idx , 2 ) ;
2014-01-22 14:05:46 -08:00
if ( ! smb_en ) {
smb_en_status = smba_en_lo & 0x10 ;
piix4_smba = smba_en_hi < < 8 ;
if ( aux )
piix4_smba | = 0x20 ;
} else {
smb_en_status = smba_en_lo & 0x01 ;
piix4_smba = ( ( smba_en_hi < < 8 ) | smba_en_lo ) & 0xffe0 ;
}
if ( ! smb_en_status ) {
2009-03-28 21:34:46 +01:00
dev_err ( & PIIX4_dev - > dev ,
2014-01-23 16:59:38 +01:00
" SMBus Host Controller not enabled! \n " ) ;
2009-03-28 21:34:46 +01:00
return - ENODEV ;
}
if ( acpi_check_region ( piix4_smba , SMBIOSIZE , piix4_driver . name ) )
2009-10-04 22:53:45 +02:00
return - ENODEV ;
2009-03-28 21:34:46 +01:00
if ( ! request_region ( piix4_smba , SMBIOSIZE , piix4_driver . name ) ) {
dev_err ( & PIIX4_dev - > dev , " SMBus region 0x%x already in use! \n " ,
piix4_smba ) ;
return - EBUSY ;
}
2013-07-14 23:17:26 +02:00
/* Aux SMBus does not support IRQ information */
if ( aux ) {
dev_info ( & PIIX4_dev - > dev ,
2014-01-22 14:06:52 -08:00
" Auxiliary SMBus Host Controller at 0x%x \n " ,
piix4_smba ) ;
2013-07-14 23:17:26 +02:00
return piix4_smba ;
}
2009-03-28 21:34:46 +01:00
/* Request the SMBus I2C bus config region */
if ( ! request_region ( piix4_smba + i2ccfg_offset , 1 , " i2ccfg " ) ) {
dev_err ( & PIIX4_dev - > dev , " SMBus I2C bus config region "
" 0x%x already in use! \n " , piix4_smba + i2ccfg_offset ) ;
release_region ( piix4_smba , SMBIOSIZE ) ;
return - EBUSY ;
}
i2ccfg = inb_p ( piix4_smba + i2ccfg_offset ) ;
release_region ( piix4_smba + i2ccfg_offset , 1 ) ;
if ( i2ccfg & 1 )
2014-01-23 16:59:38 +01:00
dev_dbg ( & PIIX4_dev - > dev , " Using IRQ for SMBus \n " ) ;
2009-03-28 21:34:46 +01:00
else
2014-01-23 16:59:38 +01:00
dev_dbg ( & PIIX4_dev - > dev , " Using SMI# for SMBus \n " ) ;
2009-03-28 21:34:46 +01:00
dev_info ( & PIIX4_dev - > dev ,
" SMBus Host Controller at 0x%x, revision %d \n " ,
piix4_smba , i2ccfg > > 4 ) ;
2012-07-24 14:13:56 +02:00
return piix4_smba ;
2009-03-28 21:34:46 +01:00
}
2012-11-27 15:59:38 -05:00
static int piix4_setup_aux ( struct pci_dev * PIIX4_dev ,
const struct pci_device_id * id ,
unsigned short base_reg_addr )
2012-07-24 14:13:57 +02:00
{
/* Set up auxiliary SMBus controllers found on some
* AMD chipsets e . g . SP5100 ( SB700 derivative ) */
unsigned short piix4_smba ;
/* Read address of auxiliary SMBus controller */
pci_read_config_word ( PIIX4_dev , base_reg_addr , & piix4_smba ) ;
if ( ( piix4_smba & 1 ) = = 0 ) {
dev_dbg ( & PIIX4_dev - > dev ,
" Auxiliary SMBus controller not enabled \n " ) ;
return - ENODEV ;
}
piix4_smba & = 0xfff0 ;
if ( piix4_smba = = 0 ) {
dev_dbg ( & PIIX4_dev - > dev ,
" Auxiliary SMBus base address uninitialized \n " ) ;
return - ENODEV ;
}
if ( acpi_check_region ( piix4_smba , SMBIOSIZE , piix4_driver . name ) )
return - ENODEV ;
if ( ! request_region ( piix4_smba , SMBIOSIZE , piix4_driver . name ) ) {
dev_err ( & PIIX4_dev - > dev , " Auxiliary SMBus region 0x%x "
" already in use! \n " , piix4_smba ) ;
return - EBUSY ;
}
dev_info ( & PIIX4_dev - > dev ,
" Auxiliary SMBus Host Controller at 0x%x \n " ,
piix4_smba ) ;
return piix4_smba ;
}
2012-07-24 14:13:56 +02:00
static int piix4_transaction ( struct i2c_adapter * piix4_adapter )
2005-04-16 15:20:36 -07:00
{
2012-07-24 14:13:56 +02:00
struct i2c_piix4_adapdata * adapdata = i2c_get_adapdata ( piix4_adapter ) ;
unsigned short piix4_smba = adapdata - > smba ;
2005-04-16 15:20:36 -07:00
int temp ;
int result = 0 ;
int timeout = 0 ;
2012-07-24 14:13:56 +02:00
dev_dbg ( & piix4_adapter - > dev , " Transaction (pre): CNT=%02x, CMD=%02x, "
2005-04-16 15:20:36 -07:00
" ADD=%02x, DAT0=%02x, DAT1=%02x \n " , inb_p ( SMBHSTCNT ) ,
inb_p ( SMBHSTCMD ) , inb_p ( SMBHSTADD ) , inb_p ( SMBHSTDAT0 ) ,
inb_p ( SMBHSTDAT1 ) ) ;
/* Make sure the SMBus host is ready to start transmitting */
if ( ( temp = inb_p ( SMBHSTSTS ) ) ! = 0x00 ) {
2012-07-24 14:13:56 +02:00
dev_dbg ( & piix4_adapter - > dev , " SMBus busy (%02x). "
2005-06-23 22:18:08 +02:00
" Resetting... \n " , temp ) ;
2005-04-16 15:20:36 -07:00
outb_p ( temp , SMBHSTSTS ) ;
if ( ( temp = inb_p ( SMBHSTSTS ) ) ! = 0x00 ) {
2012-07-24 14:13:56 +02:00
dev_err ( & piix4_adapter - > dev , " Failed! (%02x) \n " , temp ) ;
2008-07-14 22:38:25 +02:00
return - EBUSY ;
2005-04-16 15:20:36 -07:00
} else {
2012-07-24 14:13:56 +02:00
dev_dbg ( & piix4_adapter - > dev , " Successful! \n " ) ;
2005-04-16 15:20:36 -07:00
}
}
/* start the transaction by setting bit 6 */
outb_p ( inb ( SMBHSTCNT ) | 0x040 , SMBHSTCNT ) ;
/* We will always wait for a fraction of a second! (See PIIX4 docs errata) */
2008-05-11 20:37:05 +02:00
if ( srvrworks_csb5_delay ) /* Extra delay for SERVERWORKS_CSB5 */
msleep ( 2 ) ;
else
msleep ( 1 ) ;
2010-01-16 20:43:12 +01:00
while ( ( + + timeout < MAX_TIMEOUT ) & &
2008-05-11 20:37:05 +02:00
( ( temp = inb_p ( SMBHSTSTS ) ) & 0x01 ) )
2005-04-16 15:20:36 -07:00
msleep ( 1 ) ;
/* If the SMBus is still busy, we give up */
2010-01-16 20:43:12 +01:00
if ( timeout = = MAX_TIMEOUT ) {
2012-07-24 14:13:56 +02:00
dev_err ( & piix4_adapter - > dev , " SMBus Timeout! \n " ) ;
2008-07-14 22:38:25 +02:00
result = - ETIMEDOUT ;
2005-04-16 15:20:36 -07:00
}
if ( temp & 0x10 ) {
2008-07-14 22:38:25 +02:00
result = - EIO ;
2012-07-24 14:13:56 +02:00
dev_err ( & piix4_adapter - > dev , " Error: Failed bus transaction \n " ) ;
2005-04-16 15:20:36 -07:00
}
if ( temp & 0x08 ) {
2008-07-14 22:38:25 +02:00
result = - EIO ;
2012-07-24 14:13:56 +02:00
dev_dbg ( & piix4_adapter - > dev , " Bus collision! SMBus may be "
2005-04-16 15:20:36 -07:00
" locked until next hard reset. (sorry!) \n " ) ;
/* Clock stops and slave is stuck in mid-transmission */
}
if ( temp & 0x04 ) {
2008-07-14 22:38:25 +02:00
result = - ENXIO ;
2012-07-24 14:13:56 +02:00
dev_dbg ( & piix4_adapter - > dev , " Error: no response! \n " ) ;
2005-04-16 15:20:36 -07:00
}
if ( inb_p ( SMBHSTSTS ) ! = 0x00 )
outb_p ( inb ( SMBHSTSTS ) , SMBHSTSTS ) ;
if ( ( temp = inb_p ( SMBHSTSTS ) ) ! = 0x00 ) {
2012-07-24 14:13:56 +02:00
dev_err ( & piix4_adapter - > dev , " Failed reset at end of "
2005-04-16 15:20:36 -07:00
" transaction (%02x) \n " , temp ) ;
}
2012-07-24 14:13:56 +02:00
dev_dbg ( & piix4_adapter - > dev , " Transaction (post): CNT=%02x, CMD=%02x, "
2005-04-16 15:20:36 -07:00
" ADD=%02x, DAT0=%02x, DAT1=%02x \n " , inb_p ( SMBHSTCNT ) ,
inb_p ( SMBHSTCMD ) , inb_p ( SMBHSTADD ) , inb_p ( SMBHSTDAT0 ) ,
inb_p ( SMBHSTDAT1 ) ) ;
return result ;
}
2008-07-14 22:38:25 +02:00
/* Return negative errno on error. */
2005-04-16 15:20:36 -07:00
static s32 piix4_access ( struct i2c_adapter * adap , u16 addr ,
unsigned short flags , char read_write ,
u8 command , int size , union i2c_smbus_data * data )
{
2012-07-24 14:13:56 +02:00
struct i2c_piix4_adapdata * adapdata = i2c_get_adapdata ( adap ) ;
unsigned short piix4_smba = adapdata - > smba ;
2005-04-16 15:20:36 -07:00
int i , len ;
2008-07-14 22:38:25 +02:00
int status ;
2005-04-16 15:20:36 -07:00
switch ( size ) {
case I2C_SMBUS_QUICK :
2008-07-14 22:38:25 +02:00
outb_p ( ( addr < < 1 ) | read_write ,
2005-04-16 15:20:36 -07:00
SMBHSTADD ) ;
size = PIIX4_QUICK ;
break ;
case I2C_SMBUS_BYTE :
2008-07-14 22:38:25 +02:00
outb_p ( ( addr < < 1 ) | read_write ,
2005-04-16 15:20:36 -07:00
SMBHSTADD ) ;
if ( read_write = = I2C_SMBUS_WRITE )
outb_p ( command , SMBHSTCMD ) ;
size = PIIX4_BYTE ;
break ;
case I2C_SMBUS_BYTE_DATA :
2008-07-14 22:38:25 +02:00
outb_p ( ( addr < < 1 ) | read_write ,
2005-04-16 15:20:36 -07:00
SMBHSTADD ) ;
outb_p ( command , SMBHSTCMD ) ;
if ( read_write = = I2C_SMBUS_WRITE )
outb_p ( data - > byte , SMBHSTDAT0 ) ;
size = PIIX4_BYTE_DATA ;
break ;
case I2C_SMBUS_WORD_DATA :
2008-07-14 22:38:25 +02:00
outb_p ( ( addr < < 1 ) | read_write ,
2005-04-16 15:20:36 -07:00
SMBHSTADD ) ;
outb_p ( command , SMBHSTCMD ) ;
if ( read_write = = I2C_SMBUS_WRITE ) {
outb_p ( data - > word & 0xff , SMBHSTDAT0 ) ;
outb_p ( ( data - > word & 0xff00 ) > > 8 , SMBHSTDAT1 ) ;
}
size = PIIX4_WORD_DATA ;
break ;
case I2C_SMBUS_BLOCK_DATA :
2008-07-14 22:38:25 +02:00
outb_p ( ( addr < < 1 ) | read_write ,
2005-04-16 15:20:36 -07:00
SMBHSTADD ) ;
outb_p ( command , SMBHSTCMD ) ;
if ( read_write = = I2C_SMBUS_WRITE ) {
len = data - > block [ 0 ] ;
2008-07-14 22:38:25 +02:00
if ( len = = 0 | | len > I2C_SMBUS_BLOCK_MAX )
return - EINVAL ;
2005-04-16 15:20:36 -07:00
outb_p ( len , SMBHSTDAT0 ) ;
i = inb_p ( SMBHSTCNT ) ; /* Reset SMBBLKDAT */
for ( i = 1 ; i < = len ; i + + )
outb_p ( data - > block [ i ] , SMBBLKDAT ) ;
}
size = PIIX4_BLOCK_DATA ;
break ;
2008-07-14 22:38:25 +02:00
default :
dev_warn ( & adap - > dev , " Unsupported transaction %d \n " , size ) ;
return - EOPNOTSUPP ;
2005-04-16 15:20:36 -07:00
}
outb_p ( ( size & 0x1C ) + ( ENABLE_INT9 & 1 ) , SMBHSTCNT ) ;
2012-07-24 14:13:56 +02:00
status = piix4_transaction ( adap ) ;
2008-07-14 22:38:25 +02:00
if ( status )
return status ;
2005-04-16 15:20:36 -07:00
if ( ( read_write = = I2C_SMBUS_WRITE ) | | ( size = = PIIX4_QUICK ) )
return 0 ;
switch ( size ) {
2008-04-29 23:11:37 +02:00
case PIIX4_BYTE :
2005-04-16 15:20:36 -07:00
case PIIX4_BYTE_DATA :
data - > byte = inb_p ( SMBHSTDAT0 ) ;
break ;
case PIIX4_WORD_DATA :
data - > word = inb_p ( SMBHSTDAT0 ) + ( inb_p ( SMBHSTDAT1 ) < < 8 ) ;
break ;
case PIIX4_BLOCK_DATA :
data - > block [ 0 ] = inb_p ( SMBHSTDAT0 ) ;
2008-07-14 22:38:25 +02:00
if ( data - > block [ 0 ] = = 0 | | data - > block [ 0 ] > I2C_SMBUS_BLOCK_MAX )
return - EPROTO ;
2005-04-16 15:20:36 -07:00
i = inb_p ( SMBHSTCNT ) ; /* Reset SMBBLKDAT */
for ( i = 1 ; i < = data - > block [ 0 ] ; i + + )
data - > block [ i ] = inb_p ( SMBBLKDAT ) ;
break ;
}
return 0 ;
}
static u32 piix4_func ( struct i2c_adapter * adapter )
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA ;
}
2006-09-03 22:39:46 +02:00
static const struct i2c_algorithm smbus_algorithm = {
2005-04-16 15:20:36 -07:00
. smbus_xfer = piix4_access ,
. functionality = piix4_func ,
} ;
2013-12-03 08:11:20 +09:00
static const struct pci_device_id piix4_ids [ ] = {
2008-01-27 18:14:51 +01:00
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , PCI_DEVICE_ID_INTEL_82371AB_3 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , PCI_DEVICE_ID_INTEL_82443MX_3 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_EFAR , PCI_DEVICE_ID_EFAR_SLC90E66_3 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_ATI , PCI_DEVICE_ID_ATI_IXP200_SMBUS ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_ATI , PCI_DEVICE_ID_ATI_IXP300_SMBUS ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_ATI , PCI_DEVICE_ID_ATI_IXP400_SMBUS ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_ATI , PCI_DEVICE_ID_ATI_SBX00_SMBUS ) } ,
2009-11-07 13:10:46 +01:00
{ PCI_DEVICE ( PCI_VENDOR_ID_AMD , PCI_DEVICE_ID_AMD_HUDSON2_SMBUS ) } ,
2013-06-03 18:24:55 +08:00
{ PCI_DEVICE ( PCI_VENDOR_ID_AMD , 0x790b ) } ,
2008-01-27 18:14:51 +01:00
{ PCI_DEVICE ( PCI_VENDOR_ID_SERVERWORKS ,
PCI_DEVICE_ID_SERVERWORKS_OSB4 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_SERVERWORKS ,
PCI_DEVICE_ID_SERVERWORKS_CSB5 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_SERVERWORKS ,
PCI_DEVICE_ID_SERVERWORKS_CSB6 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_SERVERWORKS ,
PCI_DEVICE_ID_SERVERWORKS_HT1000SB ) } ,
2009-03-28 21:34:46 +01:00
{ PCI_DEVICE ( PCI_VENDOR_ID_SERVERWORKS ,
PCI_DEVICE_ID_SERVERWORKS_HT1100LD ) } ,
2005-04-16 15:20:36 -07:00
{ 0 , }
} ;
MODULE_DEVICE_TABLE ( pci , piix4_ids ) ;
2012-07-24 14:13:56 +02:00
static struct i2c_adapter * piix4_main_adapter ;
2012-07-24 14:13:57 +02:00
static struct i2c_adapter * piix4_aux_adapter ;
2012-07-24 14:13:56 +02:00
2012-11-27 15:59:38 -05:00
static int piix4_add_adapter ( struct pci_dev * dev , unsigned short smba ,
struct i2c_adapter * * padap )
2012-07-24 14:13:56 +02:00
{
struct i2c_adapter * adap ;
struct i2c_piix4_adapdata * adapdata ;
int retval ;
adap = kzalloc ( sizeof ( * adap ) , GFP_KERNEL ) ;
if ( adap = = NULL ) {
release_region ( smba , SMBIOSIZE ) ;
return - ENOMEM ;
}
adap - > owner = THIS_MODULE ;
adap - > class = I2C_CLASS_HWMON | I2C_CLASS_SPD ;
adap - > algo = & smbus_algorithm ;
adapdata = kzalloc ( sizeof ( * adapdata ) , GFP_KERNEL ) ;
if ( adapdata = = NULL ) {
kfree ( adap ) ;
release_region ( smba , SMBIOSIZE ) ;
return - ENOMEM ;
}
adapdata - > smba = smba ;
/* set up the sysfs linkage to our parent device */
adap - > dev . parent = & dev - > dev ;
snprintf ( adap - > name , sizeof ( adap - > name ) ,
" SMBus PIIX4 adapter at %04x " , smba ) ;
i2c_set_adapdata ( adap , adapdata ) ;
retval = i2c_add_adapter ( adap ) ;
if ( retval ) {
dev_err ( & dev - > dev , " Couldn't register adapter! \n " ) ;
kfree ( adapdata ) ;
kfree ( adap ) ;
release_region ( smba , SMBIOSIZE ) ;
return retval ;
}
* padap = adap ;
return 0 ;
}
2012-11-27 15:59:38 -05:00
static int piix4_probe ( struct pci_dev * dev , const struct pci_device_id * id )
2005-04-16 15:20:36 -07:00
{
int retval ;
2009-09-18 22:45:50 +02:00
if ( ( dev - > vendor = = PCI_VENDOR_ID_ATI & &
dev - > device = = PCI_DEVICE_ID_ATI_SBX00_SMBUS & &
dev - > revision > = 0x40 ) | |
dev - > vendor = = PCI_VENDOR_ID_AMD )
2009-03-28 21:34:46 +01:00
/* base address location etc changed in SB800 */
2013-07-14 23:17:26 +02:00
retval = piix4_setup_sb800 ( dev , id , 0 ) ;
2009-03-28 21:34:46 +01:00
else
retval = piix4_setup ( dev , id ) ;
2012-07-24 14:13:57 +02:00
/* If no main SMBus found, give up */
2012-07-24 14:13:56 +02:00
if ( retval < 0 )
2005-04-16 15:20:36 -07:00
return retval ;
2012-07-24 14:13:57 +02:00
/* Try to register main SMBus adapter, give up if we can't */
retval = piix4_add_adapter ( dev , retval , & piix4_main_adapter ) ;
if ( retval < 0 )
return retval ;
/* Check for auxiliary SMBus on some AMD chipsets */
2013-07-14 23:17:26 +02:00
retval = - ENODEV ;
2012-07-24 14:13:57 +02:00
if ( dev - > vendor = = PCI_VENDOR_ID_ATI & &
2013-07-14 23:17:26 +02:00
dev - > device = = PCI_DEVICE_ID_ATI_SBX00_SMBUS ) {
if ( dev - > revision < 0x40 ) {
retval = piix4_setup_aux ( dev , id , 0x58 ) ;
} else {
/* SB800 added aux bus too */
retval = piix4_setup_sb800 ( dev , id , 1 ) ;
2012-07-24 14:13:57 +02:00
}
}
2013-07-14 23:17:26 +02:00
if ( dev - > vendor = = PCI_VENDOR_ID_AMD & &
dev - > device = = PCI_DEVICE_ID_AMD_HUDSON2_SMBUS ) {
retval = piix4_setup_sb800 ( dev , id , 1 ) ;
}
if ( retval > 0 ) {
/* Try to add the aux adapter if it exists,
* piix4_add_adapter will clean up if this fails */
piix4_add_adapter ( dev , retval , & piix4_aux_adapter ) ;
}
2012-07-24 14:13:57 +02:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2012-11-27 15:59:38 -05:00
static void piix4_adap_remove ( struct i2c_adapter * adap )
2005-04-16 15:20:36 -07:00
{
2012-07-24 14:13:56 +02:00
struct i2c_piix4_adapdata * adapdata = i2c_get_adapdata ( adap ) ;
if ( adapdata - > smba ) {
i2c_del_adapter ( adap ) ;
release_region ( adapdata - > smba , SMBIOSIZE ) ;
2012-07-24 14:13:56 +02:00
kfree ( adapdata ) ;
kfree ( adap ) ;
2005-04-16 15:20:36 -07:00
}
}
2012-11-27 15:59:38 -05:00
static void piix4_remove ( struct pci_dev * dev )
2012-07-24 14:13:56 +02:00
{
2012-07-24 14:13:56 +02:00
if ( piix4_main_adapter ) {
piix4_adap_remove ( piix4_main_adapter ) ;
piix4_main_adapter = NULL ;
}
2012-07-24 14:13:57 +02:00
if ( piix4_aux_adapter ) {
piix4_adap_remove ( piix4_aux_adapter ) ;
piix4_aux_adapter = NULL ;
}
2012-07-24 14:13:56 +02:00
}
2005-04-16 15:20:36 -07:00
static struct pci_driver piix4_driver = {
. name = " piix4_smbus " ,
. id_table = piix4_ids ,
. probe = piix4_probe ,
2012-11-27 15:59:38 -05:00
. remove = piix4_remove ,
2005-04-16 15:20:36 -07:00
} ;
2012-07-24 14:13:56 +02:00
module_pci_driver ( piix4_driver ) ;
2005-04-16 15:20:36 -07:00
MODULE_AUTHOR ( " Frodo Looijaard <frodol@dds.nl> and "
" Philip Edelbrock <phil@netroedge.com> " ) ;
MODULE_DESCRIPTION ( " PIIX4 SMBus driver " ) ;
MODULE_LICENSE ( " GPL " ) ;