Newer
Older
.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 const struct dmi_system_id mux_dmi_table[] = {
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
{
.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,
},
{ }
};
/* Setup multiplexing if needed */
static int i801_add_mux(struct i801_priv *priv)
{
struct device *dev = &priv->adapter.dev;
const struct i801_mux_config *mux_config;
struct i2c_mux_gpio_platform_data gpio_data;
if (!priv->mux_drvdata)
return 0;
mux_config = priv->mux_drvdata;
/* 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.gpio_chip = mux_config->gpio_chip;
gpio_data.gpios = mux_config->gpios;
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",
PLATFORM_DEVID_AUTO, &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 i801_del_mux(struct i801_priv *priv)
{
if (priv->mux_pdev)
platform_device_unregister(priv->mux_pdev);
}
static unsigned int 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) {
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
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
static const struct itco_wdt_platform_data tco_platform_data = {
.name = "Intel PCH",
.version = 4,
};
static DEFINE_SPINLOCK(p2sb_spinlock);
static void i801_add_tco(struct i801_priv *priv)
{
struct pci_dev *pci_dev = priv->pci_dev;
struct resource tco_res[3], *res;
struct platform_device *pdev;
unsigned int devfn;
u32 tco_base, tco_ctl;
u32 base_addr, ctrl_val;
u64 base64_addr;
if (!(priv->features & FEATURE_TCO))
return;
pci_read_config_dword(pci_dev, TCOBASE, &tco_base);
pci_read_config_dword(pci_dev, TCOCTL, &tco_ctl);
if (!(tco_ctl & TCOCTL_EN))
return;
memset(tco_res, 0, sizeof(tco_res));
res = &tco_res[ICH_RES_IO_TCO];
res->start = tco_base & ~1;
res->end = res->start + 32 - 1;
res->flags = IORESOURCE_IO;
/*
* Power Management registers.
*/
devfn = PCI_DEVFN(PCI_SLOT(pci_dev->devfn), 2);
pci_bus_read_config_dword(pci_dev->bus, devfn, ACPIBASE, &base_addr);
res = &tco_res[ICH_RES_IO_SMI];
res->start = (base_addr & ~1) + ACPIBASE_SMI_OFF;
res->end = res->start + 3;
res->flags = IORESOURCE_IO;
/*
* Enable the ACPI I/O space.
*/
pci_bus_read_config_dword(pci_dev->bus, devfn, ACPICTRL, &ctrl_val);
ctrl_val |= ACPICTRL_EN;
pci_bus_write_config_dword(pci_dev->bus, devfn, ACPICTRL, ctrl_val);
/*
* We must access the NO_REBOOT bit over the Primary to Sideband
* bridge (P2SB). The BIOS prevents the P2SB device from being
* enumerated by the PCI subsystem, so we need to unhide/hide it
* to lookup the P2SB BAR.
*/
spin_lock(&p2sb_spinlock);
devfn = PCI_DEVFN(PCI_SLOT(pci_dev->devfn), 1);
/* Unhide the P2SB device */
pci_bus_write_config_byte(pci_dev->bus, devfn, 0xe1, 0x0);
pci_bus_read_config_dword(pci_dev->bus, devfn, SBREG_BAR, &base_addr);
base64_addr = base_addr & 0xfffffff0;
pci_bus_read_config_dword(pci_dev->bus, devfn, SBREG_BAR + 0x4, &base_addr);
base64_addr |= (u64)base_addr << 32;
/* Hide the P2SB device */
pci_bus_write_config_byte(pci_dev->bus, devfn, 0xe1, 0x1);
spin_unlock(&p2sb_spinlock);
res = &tco_res[ICH_RES_MEM_OFF];
res->start = (resource_size_t)base64_addr + SBREG_SMBCTRL;
res->end = res->start + 3;
res->flags = IORESOURCE_MEM;
pdev = platform_device_register_resndata(&pci_dev->dev, "iTCO_wdt", -1,
tco_res, 3, &tco_platform_data,
sizeof(tco_platform_data));
if (IS_ERR(pdev)) {
dev_warn(&pci_dev->dev, "failed to create iTCO device\n");
return;
}
priv->tco_pdev = pdev;
}
static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
struct i801_priv *priv;
priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
i2c_set_adapdata(&priv->adapter, priv);
priv->adapter.owner = THIS_MODULE;
priv->adapter.class = i801_get_adapter_class(priv);
priv->adapter.algo = &smbus_algorithm;
priv->pci_dev = dev;
case PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_SMBUS:
case PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_SMBUS:
case PCI_DEVICE_ID_INTEL_DNV_SMBUS:
priv->features |= FEATURE_I2C_BLOCK_READ;
priv->features |= FEATURE_IRQ;
priv->features |= FEATURE_SMBUS_PEC;
priv->features |= FEATURE_BLOCK_BUFFER;
priv->features |= FEATURE_TCO;
break;
case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF0:
case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1:
case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2:
case PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS0:
case PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS1:
case PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2:
priv->features |= FEATURE_IDF;
/* fall through */
priv->features |= FEATURE_I2C_BLOCK_READ;
priv->features |= FEATURE_IRQ;
/* fall through */
case PCI_DEVICE_ID_INTEL_82801DB_3:
priv->features |= FEATURE_SMBUS_PEC;
priv->features |= FEATURE_BLOCK_BUFFER;
/* fall through */
case PCI_DEVICE_ID_INTEL_82801CA_3:
case PCI_DEVICE_ID_INTEL_82801BA_2:
case PCI_DEVICE_ID_INTEL_82801AB_3:
case PCI_DEVICE_ID_INTEL_82801AA_3:
/* Disable features on user request */
for (i = 0; i < ARRAY_SIZE(i801_feature_names); i++) {
if (priv->features & disable_features & (1 << i))
dev_notice(&dev->dev, "%s disabled by user\n",
i801_feature_names[i]);
}
priv->features &= ~disable_features;
err = pcim_enable_device(dev);
if (err) {
dev_err(&dev->dev, "Failed to enable SMBus PCI device (%d)\n",
err);
return err;
pcim_pin_device(dev);
/* Determine the address of the SMBus area */
priv->smba = pci_resource_start(dev, SMBBAR);
if (!priv->smba) {
dev_err(&dev->dev,
"SMBus base address uninitialized, upgrade BIOS\n");
return -ENODEV;
err = acpi_check_resource_conflict(&dev->resource[SMBBAR]);
return -ENODEV;
err = pcim_iomap_regions(dev, 1 << SMBBAR,
dev_driver_string(&dev->dev));
dev_err(&dev->dev,
"Failed to request SMBus region 0x%lx-0x%Lx\n",
priv->smba,
(unsigned long long)pci_resource_end(dev, SMBBAR));
return err;
pci_read_config_byte(priv->pci_dev, SMBHSTCFG, &temp);
priv->original_hstcfg = temp;
temp &= ~SMBHSTCFG_I2C_EN; /* SMBus timing */
if (!(temp & SMBHSTCFG_HST_EN)) {
dev_info(&dev->dev, "Enabling SMBus device\n");
temp |= SMBHSTCFG_HST_EN;
}
pci_write_config_byte(priv->pci_dev, SMBHSTCFG, temp);
if (temp & SMBHSTCFG_SMB_SMI_EN) {
dev_dbg(&dev->dev, "SMBus using interrupt SMI#\n");
/* Disable SMBus interrupt feature if SMBus using SMI# */
priv->features &= ~FEATURE_IRQ;
}
/* Clear special mode bits */
if (priv->features & (FEATURE_SMBUS_PEC | FEATURE_BLOCK_BUFFER))
outb_p(inb_p(SMBAUXCTL(priv)) &
~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv));
/* Default timeout in interrupt mode: 200 ms */
priv->adapter.timeout = HZ / 5;
if (priv->features & FEATURE_IRQ) {
u16 pcictl, pcists;
/* Complain if an interrupt is already pending */
pci_read_config_word(priv->pci_dev, SMBPCISTS, &pcists);
if (pcists & SMBPCISTS_INTS)
dev_warn(&dev->dev, "An interrupt is pending!\n");
/* Check if interrupts have been disabled */
pci_read_config_word(priv->pci_dev, SMBPCICTL, &pcictl);
if (pcictl & SMBPCICTL_INTDIS) {
dev_info(&dev->dev, "Interrupts are disabled\n");
priv->features &= ~FEATURE_IRQ;
}
}
if (priv->features & FEATURE_IRQ) {
init_waitqueue_head(&priv->waitq);
err = devm_request_irq(&dev->dev, dev->irq, i801_isr,
IRQF_SHARED,
dev_driver_string(&dev->dev), priv);
if (err) {
dev_err(&dev->dev, "Failed to allocate irq %d: %d\n",
dev->irq, err);
priv->features &= ~FEATURE_IRQ;
dev_info(&dev->dev, "SMBus using %s\n",
priv->features & FEATURE_IRQ ? "PCI interrupt" : "polling");
i801_add_tco(priv);
/* set up the sysfs linkage to our parent device */
priv->adapter.dev.parent = &dev->dev;
/* Retry up to 3 times on lost arbitration */
priv->adapter.retries = 3;
snprintf(priv->adapter.name, sizeof(priv->adapter.name),
"SMBus I801 adapter at %04lx", priv->smba);
err = i2c_add_adapter(&priv->adapter);
if (err) {
dev_err(&dev->dev, "Failed to add SMBus adapter\n");
return err;
i801_probe_optional_slaves(priv);
/* We ignore errors - multiplexing is optional */
i801_add_mux(priv);
pci_set_drvdata(dev, priv);
return 0;
static void 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);
platform_device_unregister(priv->tco_pdev);
/*
* do not call pci_disable_device(dev) since it can cause hard hangs on
* some systems during power-off (eg. Fujitsu-Siemens Lifebook E8010)
*/
#ifdef CONFIG_PM
static int i801_suspend(struct pci_dev *dev, pm_message_t mesg)
{
struct i801_priv *priv = pci_get_drvdata(dev);
pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg);
pci_set_power_state(dev, pci_choose_state(dev, mesg));
return 0;
}
static int i801_resume(struct pci_dev *dev)
{
pci_set_power_state(dev, PCI_D0);
pci_restore_state(dev);
return 0;
}
#else
#define i801_suspend NULL
#define i801_resume NULL
#endif
static struct pci_driver i801_driver = {
.name = "i801_smbus",
.id_table = i801_ids,
.probe = i801_probe,
.suspend = i801_suspend,
.resume = i801_resume,
};
static int __init i2c_i801_init(void)
{
if (dmi_name_in_vendors("FUJITSU"))
input_apanel_init();
return pci_register_driver(&i801_driver);
}
static void __exit i2c_i801_exit(void)
{
pci_unregister_driver(&i801_driver);
}
MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>, Jean Delvare <jdelvare@suse.de>");
MODULE_DESCRIPTION("I801 SMBus driver");
MODULE_LICENSE("GPL");
module_init(i2c_i801_init);
module_exit(i2c_i801_exit);