diff --git a/doc/busses/i2c-amd8111 b/doc/busses/i2c-amd8111 index d1677dd9..c247206a 100644 --- a/doc/busses/i2c-amd8111 +++ b/doc/busses/i2c-amd8111 @@ -1,15 +1,16 @@ Kernel driver `i2c-adm8111.o' -Status: Untested. +Status: Slightly tested. Supported adapters: * AMD-8111 SMBus 2.0 PCI interface - * Possibly any other interface that implements the ACPI 2.0 EC interface - Datasheets: Publicly available at AMD and ACPI websites + +Datasheets: + AMD datasheet not yet available, but almost everything can be found in + publically available ACPI 2.0 specification, which the adapter follows. Author: Vojtech Pavlik - Module Parameters ----------------- @@ -30,15 +31,22 @@ in your 'lspci -v', then this driver is for your chipset. Process Call Support -------------------- -The chip supports it, but it's not implemented in the driver at the moment. +Supported. SMBus 2.0 Support ----------------- -The chip supports SMBus 2.0, however the only extension the driver -supports is PEC. Slave mode or host notification are not implemented. +Supported. Both PEC and block process call support is implemented. +Slave mode or host notification are not yet implemented. +Notes +----- Note that for the 8111, there are two SMBus adapters. The SMBus 2.0 adapter is supported by this driver, and the SMBus 1.0 adapter is supported by the i2c-amd756 driver. + +The 8111 used with the AMD Hammer. While the module should work either with +CONFIG_X86 or CONFIG_X86_64, it will only be compiled with CONFIG_X86_64 +(as it was the easiest way to prevent compilation in 2.2 kernels). +If you would like to compile it for CONFIG_X86, modify kernel/busses/Module.mk. diff --git a/kernel/busses/Module.mk b/kernel/busses/Module.mk index 18b8c7e6..a8b0097a 100644 --- a/kernel/busses/Module.mk +++ b/kernel/busses/Module.mk @@ -40,9 +40,12 @@ endif ifneq ($(shell if grep -q '^CONFIG_I2C_AMD756=y' $(LINUX)/.config; then echo 1; fi),1) KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-amd756.o endif +# Should also work for CONFIG_X86 but this is the easiest way to prevent compilation in 2.2 kernels... +ifeq ($(shell if grep -q '^CONFIG_X86_64=' $(LINUX)/.config; then echo 1; fi),1) ifneq ($(shell if grep -q '^CONFIG_I2C_AMD8111=y' $(LINUX)/.config; then echo 1; fi),1) KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-amd8111.o endif +endif ifneq ($(shell if grep -q '^CONFIG_I2C_HYDRA=y' $(LINUX)/.config; then echo 1; fi),1) KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-hydra.o endif diff --git a/kernel/busses/i2c-amd8111.c b/kernel/busses/i2c-amd8111.c index 24e9b123..22869fa2 100644 --- a/kernel/busses/i2c-amd8111.c +++ b/kernel/busses/i2c-amd8111.c @@ -80,7 +80,7 @@ unsigned int amd_ec_wait_write(struct amd_smbus *smbus) udelay(1); if (!timeout) { - printk(KERN_WARNING "i2c-amd811.c: Timeout while waiting for IBF to clear\n"); + printk(KERN_WARNING "i2c-amd8111.c: Timeout while waiting for IBF to clear\n"); return -1; } @@ -166,6 +166,8 @@ unsigned int amd_ec_write(struct amd_smbus *smbus, unsigned char address, unsign #define AMD_SMB_STATUS_BUSY 0x1A #define AMD_SMB_STATUS_PEC 0x1F +#define AMD_SMB_PRTCL_WRITE 0x00 +#define AMD_SMB_PRTCL_READ 0x01 #define AMD_SMB_PRTCL_QUICK 0x02 #define AMD_SMB_PRTCL_BYTE 0x04 #define AMD_SMB_PRTCL_BYTE_DATA 0x06 @@ -181,59 +183,89 @@ s32 amd8111_access(struct i2c_adapter * adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data * data) { struct amd_smbus *smbus = adap->algo_data; - unsigned char protocol = read_write; /* 1 = read, 0 = write */ - unsigned char temp[2]; - int i, timeout = 5000; + unsigned char protocol, len, pec, temp[2]; + int i; + + protocol = (read_write == I2C_SMBUS_READ) ? AMD_SMB_PRTCL_READ : AMD_SMB_PRTCL_WRITE; + pec = (flags & I2C_CLIENT_PEC) ? AMD_SMB_PRTCL_PEC : 0; switch (size) { case I2C_SMBUS_QUICK: protocol |= AMD_SMB_PRTCL_QUICK; + read_write = I2C_SMBUS_WRITE; break; case I2C_SMBUS_BYTE: - if (!read_write) + if (read_write == I2C_SMBUS_WRITE) amd_ec_write(smbus, AMD_SMB_DATA, data->byte); protocol |= AMD_SMB_PRTCL_BYTE; break; case I2C_SMBUS_BYTE_DATA: amd_ec_write(smbus, AMD_SMB_CMD, command); - if (!read_write) + if (read_write == I2C_SMBUS_WRITE) amd_ec_write(smbus, AMD_SMB_DATA, data->byte); protocol |= AMD_SMB_PRTCL_BYTE_DATA; break; + case I2C_SMBUS_WORD_DATA_PEC: + protocol |= AMD_SMB_PRTCL_PEC; case I2C_SMBUS_WORD_DATA: amd_ec_write(smbus, AMD_SMB_CMD, command); - if (!read_write) { - amd_ec_write(smbus, AMD_SMB_DATA, data->byte); - amd_ec_write(smbus, AMD_SMB_DATA + 1, data->byte >> 8); + if (read_write == I2C_SMBUS_WRITE) { + amd_ec_write(smbus, AMD_SMB_DATA, data->word); + amd_ec_write(smbus, AMD_SMB_DATA + 1, data->word >> 8); } - protocol |= AMD_SMB_PRTCL_WORD_DATA; + protocol |= AMD_SMB_PRTCL_WORD_DATA | pec; break; - case I2C_SMBUS_BLOCK_DATA: case I2C_SMBUS_BLOCK_DATA_PEC: - case I2C_SMBUS_I2C_BLOCK_DATA: + protocol |= AMD_SMB_PRTCL_PEC; + case I2C_SMBUS_BLOCK_DATA: amd_ec_write(smbus, AMD_SMB_CMD, command); - if (!read_write) - for (i = 0; i < data->block[0]; i++) + if (read_write == I2C_SMBUS_WRITE) { + len = min_t(u8, data->block[0], 32); + amd_ec_write(smbus, AMD_SMB_BCNT, len); + for (i = 0; i < len; i++) amd_ec_write(smbus, AMD_SMB_DATA + i, data->block[i + 1]); - switch (size) { - case I2C_SMBUS_BLOCK_DATA: - protocol |= AMD_SMB_PRTCL_BLOCK_DATA; - break; - case I2C_SMBUS_BLOCK_DATA_PEC: - protocol |= AMD_SMB_PRTCL_BLOCK_DATA | AMD_SMB_PRTCL_PEC; - break; - case I2C_SMBUS_I2C_BLOCK_DATA: - protocol |= AMD_SMB_PRTCL_I2C_BLOCK_DATA; - break; } + protocol |= AMD_SMB_PRTCL_BLOCK_DATA | pec; + break; + + case I2C_SMBUS_I2C_BLOCK_DATA: + len = min_t(u8, data->block[0], 32); + amd_ec_write(smbus, AMD_SMB_CMD, command); + amd_ec_write(smbus, AMD_SMB_BCNT, len); + if (read_write == I2C_SMBUS_WRITE) + for (i = 0; i < len; i++) + amd_ec_write(smbus, AMD_SMB_DATA + i, data->block[i + 1]); + protocol |= AMD_SMB_PRTCL_I2C_BLOCK_DATA; + break; + + case I2C_SMBUS_PROC_CALL_PEC: + protocol |= AMD_SMB_PRTCL_PEC; + case I2C_SMBUS_PROC_CALL: + amd_ec_write(smbus, AMD_SMB_CMD, command); + amd_ec_write(smbus, AMD_SMB_DATA, data->word); + amd_ec_write(smbus, AMD_SMB_DATA + 1, data->word >> 8); + protocol = AMD_SMB_PRTCL_PROC_CALL | pec; + read_write = I2C_SMBUS_READ; + break; + + case I2C_SMBUS_BLOCK_PROC_CALL_PEC: + protocol |= AMD_SMB_PRTCL_PEC; + case I2C_SMBUS_BLOCK_PROC_CALL: + protocol |= pec; + len = min_t(u8, data->block[0], 31); + amd_ec_write(smbus, AMD_SMB_CMD, command); + amd_ec_write(smbus, AMD_SMB_BCNT, len); + for (i = 0; i < len; i++) + amd_ec_write(smbus, AMD_SMB_DATA + i, data->block[i + 1]); + protocol = AMD_SMB_PRTCL_BLOCK_PROC_CALL | pec; + read_write = I2C_SMBUS_READ; break; - case I2C_SMBUS_PROC_CALL: /* We don't support proc_call yet */ default: printk(KERN_WARNING "i2c-amd8111.c: Unsupported transaction %d\n", size); return -1; @@ -244,21 +276,21 @@ s32 amd8111_access(struct i2c_adapter * adap, u16 addr, unsigned short flags, amd_ec_read(smbus, AMD_SMB_STS, temp + 0); - while (timeout && ~temp[0] & AMD_SMB_STS_DONE) { + if (~temp[0] & AMD_SMB_STS_DONE) { + udelay(500); amd_ec_read(smbus, AMD_SMB_STS, temp + 0); - udelay(10); - timeout--; } - if (!timeout) - return -1; - if (~temp[0] & AMD_SMB_STS_DONE) { - printk(KERN_WARNING "i2c-amd8111.c: Transaction completed with error %#x\n", temp[0]); - return -1; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ/100); + amd_ec_read(smbus, AMD_SMB_STS, temp + 0); } - if (!read_write) /* We're done, no values to return; */ + if ((~temp[0] & AMD_SMB_STS_DONE) || (temp[0] & AMD_SMB_STS_STATUS)) + return -1; + + if (read_write == I2C_SMBUS_WRITE) return 0; switch (size) { @@ -269,6 +301,9 @@ s32 amd8111_access(struct i2c_adapter * adap, u16 addr, unsigned short flags, break; case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_WORD_DATA_PEC: + case I2C_SMBUS_PROC_CALL: + case I2C_SMBUS_PROC_CALL_PEC: amd_ec_read(smbus, AMD_SMB_DATA, temp + 0); amd_ec_read(smbus, AMD_SMB_DATA + 1, temp + 1); data->word = (temp[1] << 8) | temp[0]; @@ -276,9 +311,14 @@ s32 amd8111_access(struct i2c_adapter * adap, u16 addr, unsigned short flags, case I2C_SMBUS_BLOCK_DATA: case I2C_SMBUS_BLOCK_DATA_PEC: + case I2C_SMBUS_BLOCK_PROC_CALL: + case I2C_SMBUS_BLOCK_PROC_CALL_PEC: + amd_ec_read(smbus, AMD_SMB_BCNT, &len); + len = min_t(u8, len, 32); case I2C_SMBUS_I2C_BLOCK_DATA: - for (i = 0; i < data->block[0]; i++) + for (i = 0; i < len; i++) amd_ec_read(smbus, AMD_SMB_DATA + i, data->block + i + 1); + data->block[0] = len; break; } @@ -297,10 +337,12 @@ void amd8111_dec(struct i2c_adapter *adapter) u32 amd8111_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 | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK | - I2C_FUNC_SMBUS_BLOCK_DATA_PEC | I2C_FUNC_SMBUS_HWPEC_CALC; + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_WORD_DATA_PEC | + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_DATA_PEC | + I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_PROC_CALL_PEC | + I2C_FUNC_SMBUS_BLOCK_PROC_CALL | I2C_FUNC_SMBUS_BLOCK_PROC_CALL_PEC | + I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_HWPEC_CALC; } static struct i2c_algorithm smbus_algorithm = { @@ -326,18 +368,10 @@ static int __devinit amd8111_probe(struct pci_dev *dev, const struct pci_device_ smbus->base = pci_resource_start(dev, 0); smbus->size = pci_resource_len(dev, 0); -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,14) if (!request_region(smbus->base, smbus->size, "amd8111 SMBus 2.0")) { kfree(smbus); return -1; } -#else - if (check_region(smbus->base, smbus->size) < 0) { - kfree(smbus); - return -1; - } - request_region(smbus->base, smbus->size, "amd8111 SMBus 2.0"); -#endif sprintf(smbus->adapter.name, "SMBus2 AMD8111 adapter at %04x", smbus->base); smbus->adapter.id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_AMD8111; @@ -346,8 +380,6 @@ static int __devinit amd8111_probe(struct pci_dev *dev, const struct pci_device_ smbus->adapter.inc_use = amd8111_inc; smbus->adapter.dec_use = amd8111_dec; - pci_write_config_dword(dev, AMD_PCI_MISC, 0); - if (i2c_add_adapter(&smbus->adapter)) { printk(KERN_WARNING "i2c-amd8111.c: Failed to register adapter.\n"); release_region(smbus->base, smbus->size); @@ -355,6 +387,8 @@ static int __devinit amd8111_probe(struct pci_dev *dev, const struct pci_device_ return -1; } + pci_write_config_dword(smbus->dev, AMD_PCI_MISC, 0); + printk(KERN_INFO "i2c-amd8111.c: AMD8111 SMBus 2.0 adapter at pci%s\n", dev->slot_name); return 0; }