// SPDX-License-Identifier: GPL-2.0-or-later /* Copyright (c) 2003 Mark M. Hoffman <mhoffman@lightlink.com>
*/
/* This module must be considered BETA unless and until the chipset manufacturer releases a datasheet. The register definitions are based on the SiS630.
This module relies on quirk_sis_96x_smbus (drivers/pci/quirks.c) for just about every machine for which users have reported. If this module isn't detecting your 96x south bridge, have a look there.
We assume there can only be one SiS96x with one SMBus interface.
*/
/* Execute a SMBus transaction. int size is from SIS96x_QUICK to SIS96x_BLOCK_DATA
*/ staticint sis96x_transaction(int size)
{ int temp; int result = 0; int timeout = 0;
/* kill the transaction */
sis96x_write(SMB_HOST_CNT, 0x20);
/* check it again */ if (((temp = sis96x_read(SMB_CNT)) & 0x03) != 0x00) {
dev_dbg(&sis96x_adapter.dev, "Failed (0x%02x)\n", temp); return -EBUSY;
} else {
dev_dbg(&sis96x_adapter.dev, "Successful\n");
}
}
/* Turn off timeout interrupts, set fast host clock */
sis96x_write(SMB_CNT, 0x20);
/* clear all (sticky) status flags */
temp = sis96x_read(SMB_STS);
sis96x_write(SMB_STS, temp & 0x1e);
/* start the transaction by setting bit 4 and size bits */
sis96x_write(SMB_HOST_CNT, 0x10 | (size & 0x07));
/* We will always wait for a fraction of a second! */ do {
msleep(1);
temp = sis96x_read(SMB_STS);
} while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */ if (timeout > MAX_TIMEOUT) {
dev_dbg(&sis96x_adapter.dev, "SMBus Timeout! (0x%02x)\n", temp);
result = -ETIMEDOUT;
}
/* device error - probably missing ACK */ if (temp & 0x02) {
dev_dbg(&sis96x_adapter.dev, "Failed bus transaction!\n");
result = -ENXIO;
}
/* bus collision */ if (temp & 0x04) {
dev_dbg(&sis96x_adapter.dev, "Bus collision!\n");
result = -EIO;
}
/* Finish up by resetting the bus */
sis96x_write(SMB_STS, temp); if ((temp = sis96x_read(SMB_STS))) {
dev_dbg(&sis96x_adapter.dev, "Failed reset at " "end of transaction! (0x%02x)\n", temp);
}
return result;
}
/* Return negative errno on error. */ static s32 sis96x_access(struct i2c_adapter * adap, u16 addr, unsignedshort flags, char read_write,
u8 command, int size, union i2c_smbus_data * data)
{ int status;
if (sis96x_smbus_base) {
dev_err(&dev->dev, "Only one device supported.\n"); return -EBUSY;
}
pci_read_config_word(dev, PCI_CLASS_DEVICE, &ww); if (PCI_CLASS_SERIAL_SMBUS != ww) {
dev_err(&dev->dev, "Unsupported device class 0x%04x!\n", ww); return -ENODEV;
}
sis96x_smbus_base = pci_resource_start(dev, SIS96x_BAR); if (!sis96x_smbus_base) {
dev_err(&dev->dev, "SiS96x SMBus base address " "not initialized!\n"); return -EINVAL;
}
dev_info(&dev->dev, "SiS96x SMBus base address: 0x%04x\n",
sis96x_smbus_base);
retval = acpi_check_resource_conflict(&dev->resource[SIS96x_BAR]); if (retval) return -ENODEV;
/* Everything is happy, let's grab the memory and set things up. */ if (!request_region(sis96x_smbus_base, SMB_IOSIZE,
sis96x_driver.name)) {
dev_err(&dev->dev, "SMBus registers 0x%04x-0x%04x " "already in use!\n", sis96x_smbus_base,
sis96x_smbus_base + SMB_IOSIZE - 1);
sis96x_smbus_base = 0; return -EINVAL;
}
/* set up the sysfs linkage to our parent device */
sis96x_adapter.dev.parent = &dev->dev;
snprintf(sis96x_adapter.name, sizeof(sis96x_adapter.name), "SiS96x SMBus adapter at 0x%04x", sis96x_smbus_base);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.