@ -80,6 +80,13 @@
# include <linux/dmi.h>
# include <linux/slab.h>
# include <linux/wait.h>
# include <linux/err.h>
# if defined CONFIG_I2C_MUX || defined CONFIG_I2C_MUX_MODULE
# include <linux/gpio.h>
# include <linux/i2c-mux-gpio.h>
# include <linux/platform_device.h>
# endif
/* I801 SMBus address offsets */
# define SMBHSTSTS(p) (0 + (p)->smba)
@ -158,6 +165,15 @@
# define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22
# define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS 0x9c22
struct i801_mux_config {
char * gpio_chip ;
unsigned values [ 3 ] ;
int n_values ;
unsigned classes [ 3 ] ;
unsigned gpios [ 2 ] ; /* Relative to gpio_chip->base */
int n_gpios ;
} ;
struct i801_priv {
struct i2c_adapter adapter ;
unsigned long smba ;
@ -175,6 +191,12 @@ struct i801_priv {
int count ;
int len ;
u8 * data ;
# if defined CONFIG_I2C_MUX || defined CONFIG_I2C_MUX_MODULE
const struct i801_mux_config * mux_drvdata ;
unsigned mux_priv [ 2 ] ;
struct platform_device * mux_pdev ;
# endif
} ;
static struct pci_driver i801_driver ;
@ -900,6 +922,193 @@ static void __init input_apanel_init(void) {}
static void __devinit i801_probe_optional_slaves ( struct i801_priv * priv ) { }
# endif /* CONFIG_X86 && CONFIG_DMI */
# if defined CONFIG_I2C_MUX || defined CONFIG_I2C_MUX_MODULE
static struct i801_mux_config i801_mux_config_asus_z8_d12 = {
. gpio_chip = " gpio_ich " ,
. values = { 0x02 , 0x03 } ,
. n_values = 2 ,
. classes = { I2C_CLASS_SPD , I2C_CLASS_SPD } ,
. gpios = { 52 , 53 } ,
. n_gpios = 2 ,
} ;
static struct i801_mux_config i801_mux_config_asus_z8_d18 = {
. gpio_chip = " gpio_ich " ,
. values = { 0x02 , 0x03 , 0x01 } ,
. n_values = 3 ,
. classes = { I2C_CLASS_SPD , I2C_CLASS_SPD , I2C_CLASS_SPD } ,
. gpios = { 52 , 53 } ,
. n_gpios = 2 ,
} ;
static struct dmi_system_id __devinitdata mux_dmi_table [ ] = {
{
. matches = {
DMI_MATCH ( DMI_BOARD_VENDOR , " ASUSTeK Computer INC. " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " Z8NA-D6(C) " ) ,
} ,
. driver_data = & i801_mux_config_asus_z8_d12 ,
} ,
{
. matches = {
DMI_MATCH ( DMI_BOARD_VENDOR , " ASUSTeK Computer INC. " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " Z8P(N)E-D12(X) " ) ,
} ,
. driver_data = & i801_mux_config_asus_z8_d12 ,
} ,
{
. matches = {
DMI_MATCH ( DMI_BOARD_VENDOR , " ASUSTeK Computer INC. " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " Z8NH-D12 " ) ,
} ,
. driver_data = & i801_mux_config_asus_z8_d12 ,
} ,
{
. matches = {
DMI_MATCH ( DMI_BOARD_VENDOR , " ASUSTeK Computer INC. " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " Z8PH-D12/IFB " ) ,
} ,
. driver_data = & i801_mux_config_asus_z8_d12 ,
} ,
{
. matches = {
DMI_MATCH ( DMI_BOARD_VENDOR , " ASUSTeK Computer INC. " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " Z8NR-D12 " ) ,
} ,
. driver_data = & i801_mux_config_asus_z8_d12 ,
} ,
{
. matches = {
DMI_MATCH ( DMI_BOARD_VENDOR , " ASUSTeK Computer INC. " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " Z8P(N)H-D12 " ) ,
} ,
. driver_data = & i801_mux_config_asus_z8_d12 ,
} ,
{
. matches = {
DMI_MATCH ( DMI_BOARD_VENDOR , " ASUSTeK Computer INC. " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " Z8PG-D18 " ) ,
} ,
. driver_data = & i801_mux_config_asus_z8_d18 ,
} ,
{
. matches = {
DMI_MATCH ( DMI_BOARD_VENDOR , " ASUSTeK Computer INC. " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " Z8PE-D18 " ) ,
} ,
. driver_data = & i801_mux_config_asus_z8_d18 ,
} ,
{
. matches = {
DMI_MATCH ( DMI_BOARD_VENDOR , " ASUSTeK Computer INC. " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " Z8PS-D12 " ) ,
} ,
. driver_data = & i801_mux_config_asus_z8_d12 ,
} ,
{ }
} ;
static int __devinit match_gpio_chip_by_label ( struct gpio_chip * chip ,
void * data )
{
return ! strcmp ( chip - > label , data ) ;
}
/* Setup multiplexing if needed */
static int __devinit i801_add_mux ( struct i801_priv * priv )
{
struct device * dev = & priv - > adapter . dev ;
const struct i801_mux_config * mux_config ;
struct gpio_chip * gpio ;
struct i2c_mux_gpio_platform_data gpio_data ;
int i , err ;
if ( ! priv - > mux_drvdata )
return 0 ;
mux_config = priv - > mux_drvdata ;
/* Find GPIO chip */
gpio = gpiochip_find ( mux_config - > gpio_chip , match_gpio_chip_by_label ) ;
if ( gpio ) {
dev_info ( dev ,
" GPIO chip %s found, SMBus multiplexing enabled \n " ,
mux_config - > gpio_chip ) ;
} else {
dev_err ( dev ,
" GPIO chip %s not found, SMBus multiplexing disabled \n " ,
mux_config - > gpio_chip ) ;
return - ENODEV ;
}
/* Find absolute GPIO pin numbers */
if ( ARRAY_SIZE ( priv - > mux_priv ) < mux_config - > n_gpios ) {
dev_err ( dev , " i801_priv.mux_priv too small (%zu, need %d) \n " ,
ARRAY_SIZE ( priv - > mux_priv ) , mux_config - > n_gpios ) ;
return - ENODEV ;
}
for ( i = 0 ; i < mux_config - > n_gpios ; i + + )
priv - > mux_priv [ i ] = gpio - > base + mux_config - > gpios [ i ] ;
/* Prepare the platform data */
memset ( & gpio_data , 0 , sizeof ( struct i2c_mux_gpio_platform_data ) ) ;
gpio_data . parent = priv - > adapter . nr ;
gpio_data . values = mux_config - > values ;
gpio_data . n_values = mux_config - > n_values ;
gpio_data . classes = mux_config - > classes ;
gpio_data . gpios = priv - > mux_priv ;
gpio_data . n_gpios = mux_config - > n_gpios ;
gpio_data . idle = I2C_MUX_GPIO_NO_IDLE ;
/* Register the mux device */
priv - > mux_pdev = platform_device_register_data ( dev , " i2c-mux-gpio " ,
priv - > mux_priv [ 0 ] , & gpio_data ,
sizeof ( struct i2c_mux_gpio_platform_data ) ) ;
if ( IS_ERR ( priv - > mux_pdev ) ) {
err = PTR_ERR ( priv - > mux_pdev ) ;
priv - > mux_pdev = NULL ;
dev_err ( dev , " Failed to register i2c-mux-gpio device \n " ) ;
return err ;
}
return 0 ;
}
static void __devexit i801_del_mux ( struct i801_priv * priv )
{
if ( priv - > mux_pdev )
platform_device_unregister ( priv - > mux_pdev ) ;
}
static unsigned int __devinit i801_get_adapter_class ( struct i801_priv * priv )
{
const struct dmi_system_id * id ;
const struct i801_mux_config * mux_config ;
unsigned int class = I2C_CLASS_HWMON | I2C_CLASS_SPD ;
int i ;
id = dmi_first_match ( mux_dmi_table ) ;
if ( id ) {
/* Remove from branch classes from trunk */
mux_config = id - > driver_data ;
for ( i = 0 ; i < mux_config - > n_values ; i + + )
class & = ~ mux_config - > classes [ i ] ;
/* Remember for later */
priv - > mux_drvdata = mux_config ;
}
return class ;
}
# else
static inline int i801_add_mux ( struct i801_priv * priv ) { return 0 ; }
static inline void i801_del_mux ( struct i801_priv * priv ) { }
static inline unsigned int i801_get_adapter_class ( struct i801_priv * priv )
{
return I2C_CLASS_HWMON | I2C_CLASS_SPD ;
}
# endif
static int __devinit i801_probe ( struct pci_dev * dev ,
const struct pci_device_id * id )
{
@ -913,7 +1122,7 @@ static int __devinit i801_probe(struct pci_dev *dev,
i2c_set_adapdata ( & priv - > adapter , priv ) ;
priv - > adapter . owner = THIS_MODULE ;
priv - > adapter . class = I2C_CLASS_HWMON | I2C_CLASS_SPD ;
priv - > adapter . class = i801_get_adapter_class ( priv ) ;
priv - > adapter . algo = & smbus_algorithm ;
priv - > pci_dev = dev ;
@ -1033,6 +1242,8 @@ static int __devinit i801_probe(struct pci_dev *dev,
}
i801_probe_optional_slaves ( priv ) ;
/* We ignore errors - multiplexing is optional */
i801_add_mux ( priv ) ;
pci_set_drvdata ( dev , priv ) ;
@ -1052,6 +1263,7 @@ static void __devexit i801_remove(struct pci_dev *dev)
{
struct i801_priv * priv = pci_get_drvdata ( dev ) ;
i801_del_mux ( priv ) ;
i2c_del_adapter ( & priv - > adapter ) ;
pci_write_config_byte ( dev , SMBHSTCFG , priv - > original_hstcfg ) ;