OpenRGB/i2c_smbus/i2c_smbus_piix4.cpp

172 lines
No EOL
3.7 KiB
C++

/*-----------------------------------------*\
| i2c_smbus_piix4.cpp |
| |
| PIIX4 SMBUS driver for Windows |
| |
| Adam Honse (CalcProgrammer1) 8/8/2018 |
| Portions based on Linux source code |
| GNU GPL v2 |
\*-----------------------------------------*/
#include "i2c_smbus_piix4.h"
#include <Windows.h>
#include "inpout32.h"
#pragma comment(lib, "inpout32.lib")
//Logic adapted from piix4_transaction() in i2c-piix4.c
int i2c_smbus_piix4::piix4_transaction()
{
int result = 0;
int temp;
int timeout = 0;
/* Make sure the SMBus host is ready to start transmitting */
temp = Inp32(SMBHSTSTS);
if (temp != 0x00)
{
Out32(SMBHSTSTS, temp);
temp = Inp32(SMBHSTSTS);
if (temp != 0x00)
{
return -EBUSY;
}
}
/* start the transaction by setting bit 6 */
temp = Inp32(SMBHSTCNT);
Out32(SMBHSTCNT, temp | 0x040);
/* We will always wait for a fraction of a second! (See PIIX4 docs errata) */
temp = 0;
while ((++timeout < MAX_TIMEOUT) && temp <= 1)
{
temp = Inp32(SMBHSTSTS);
}
/* If the SMBus is still busy, we give up */
if (timeout == MAX_TIMEOUT)
{
result = -ETIMEDOUT;
}
if (temp & 0x10)
{
result = -EIO;
}
if (temp & 0x08)
{
result = -EIO;
}
if (temp & 0x04)
{
result = -ENXIO;
}
temp = Inp32(SMBHSTSTS);
if (temp != 0x00)
{
Out32(SMBHSTSTS, temp);
}
return result;
}
//Logic adapted from piix4_access() in i2c-piix4.c
s32 i2c_smbus_piix4::piix4_access(u16 addr, char read_write, u8 command, int size, i2c_smbus_data *data)
{
int i, len, status;
switch (size)
{
case I2C_SMBUS_QUICK:
Out32(SMBHSTADD, (addr << 1) | read_write);
size = PIIX4_QUICK;
break;
case I2C_SMBUS_BYTE_DATA:
Out32(SMBHSTADD, (addr << 1) | read_write);
Out32(SMBHSTCMD, command);
if (read_write == I2C_SMBUS_WRITE)
{
Out32(SMBHSTDAT0, data->byte);
}
size = PIIX4_BYTE_DATA;
break;
case I2C_SMBUS_WORD_DATA:
Out32(SMBHSTADD, (addr << 1) | read_write);
Out32(SMBHSTCMD, command);
if (read_write == I2C_SMBUS_WRITE)
{
Out32(SMBHSTDAT0, data->word & 0xFF);
Out32(SMBHSTDAT1, (data->word & 0xFF00) >> 8);
}
size = PIIX4_WORD_DATA;
break;
case I2C_SMBUS_BLOCK_DATA:
Out32(SMBHSTADD, (addr << 1) | read_write);
Out32(SMBHSTCMD, command);
if (read_write == I2C_SMBUS_WRITE)
{
len = data->block[0];
if (len == 0 || len > I2C_SMBUS_BLOCK_MAX)
{
return -EINVAL;
}
Out32(SMBHSTDAT0, len);
Inp32(SMBHSTCNT);
for (i = 1; i <= len; i++)
{
Out32(SMBBLKDAT, data->block[i]);
}
}
size = PIIX4_BLOCK_DATA;
break;
default:
return -EOPNOTSUPP;
}
Out32(SMBHSTCNT, (size & 0x1C));
status = piix4_transaction();
if (status)
return status;
if ((read_write == I2C_SMBUS_WRITE) || (size == PIIX4_QUICK))
return 0;
switch (size)
{
case PIIX4_BYTE:
case PIIX4_BYTE_DATA:
data->byte = (u8)Inp32(SMBHSTDAT0);
break;
case PIIX4_WORD_DATA:
data->word = Inp32(SMBHSTDAT0) + (Inp32(SMBHSTDAT1) << 8);
break;
case PIIX4_BLOCK_DATA:
data->block[0] = (u8)Inp32(SMBHSTDAT0);
if (data->block[0] == 0 || data->block[0] > I2C_SMBUS_BLOCK_MAX)
{
return -EPROTO;
}
Inp32(SMBHSTCNT);
for (i = 1; i <= data->block[0]; i++)
{
data->block[i] = (u8)Inp32(SMBBLKDAT);
}
break;
}
return 0;
}
s32 i2c_smbus_piix4::i2c_smbus_xfer(u8 addr, char read_write, u8 command, int size, i2c_smbus_data* data)
{
return piix4_access(addr, read_write, command, size, data);
}