Publish LumaOps source
This commit is contained in:
@@ -0,0 +1,300 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| i2c_smbus_linux.cpp |
|
||||
| |
|
||||
| Linux i2c/smbus driver |
|
||||
| |
|
||||
| Adam Honse (CalcProgrammer1) 14 Feb 2019 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include <linux/i2c-dev.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <cstring>
|
||||
#include "LogManager.h"
|
||||
#include "i2c_smbus.h"
|
||||
#include "i2c_smbus_linux.h"
|
||||
|
||||
s32 i2c_smbus_linux::i2c_smbus_xfer(u8 addr, char read_write, u8 command, int size, union i2c_smbus_data* data)
|
||||
{
|
||||
|
||||
struct i2c_smbus_ioctl_data args;
|
||||
|
||||
//Tell I2C host which slave address to transfer to
|
||||
ioctl(handle, I2C_SLAVE, addr);
|
||||
|
||||
args.read_write = read_write;
|
||||
args.command = command;
|
||||
args.size = size;
|
||||
args.data = data;
|
||||
|
||||
return ioctl(handle, I2C_SMBUS, &args);
|
||||
}
|
||||
|
||||
s32 i2c_smbus_linux::i2c_xfer(u8 addr, char read_write, int* size, u8* data)
|
||||
{
|
||||
i2c_rdwr_ioctl_data rdwr;
|
||||
i2c_msg msg;
|
||||
s32 ret_val;
|
||||
|
||||
msg.addr = addr;
|
||||
msg.flags = read_write;
|
||||
msg.len = *size;
|
||||
msg.buf = (u8*)malloc(*size);
|
||||
memcpy(msg.buf, data, *size);
|
||||
|
||||
rdwr.msgs = &msg;
|
||||
rdwr.nmsgs = 1;
|
||||
|
||||
ret_val = ioctl(handle, I2C_RDWR, &rdwr);
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| If operation was a read, copy read data and size |
|
||||
\*-------------------------------------------------*/
|
||||
if(read_write == I2C_SMBUS_READ)
|
||||
{
|
||||
*size = msg.len;
|
||||
memcpy(data, msg.buf, *size);
|
||||
}
|
||||
|
||||
free(msg.buf);
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
#include "Detector.h"
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <linux/limits.h>
|
||||
|
||||
bool i2c_smbus_linux_detect()
|
||||
{
|
||||
i2c_smbus_linux * bus;
|
||||
char device_string[PATH_MAX];
|
||||
char device_path[PATH_MAX];
|
||||
DIR * dir;
|
||||
char driver_path[PATH_MAX];
|
||||
struct dirent * ent;
|
||||
int test_fd;
|
||||
int ret = true;
|
||||
char path[PATH_MAX];
|
||||
char buff[64];
|
||||
unsigned short pci_device, pci_vendor, pci_subsystem_device, pci_subsystem_vendor;
|
||||
unsigned short port_id, bus_id;
|
||||
char *ptr;
|
||||
|
||||
// Start looking for I2C adapters in /sys/bus/i2c/devices/
|
||||
strcpy(driver_path, "/sys/bus/i2c/devices/");
|
||||
dir = opendir(driver_path);
|
||||
|
||||
if(dir == NULL)
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
|
||||
// Loop through all entries in i2c-adapter list
|
||||
while((ent = readdir(dir)) != NULL)
|
||||
{
|
||||
if(ent->d_type == DT_DIR || ent->d_type == DT_LNK)
|
||||
{
|
||||
if(strncmp(ent->d_name, "i2c-", 4) == 0)
|
||||
{
|
||||
strncpy(device_string, driver_path, sizeof(device_string) - 1);
|
||||
device_string[sizeof(device_string) - 1] = '\0';
|
||||
strncat(device_string, ent->d_name, sizeof(device_string) - strlen(device_string) - 1);
|
||||
device_string[sizeof(device_string) - 1] = '\0';
|
||||
strncat(device_string, "/name", sizeof(device_string) - strlen(device_string) - 1);
|
||||
device_string[sizeof(device_string) - 1] = '\0';
|
||||
test_fd = open(device_string, O_RDONLY);
|
||||
|
||||
if(test_fd)
|
||||
{
|
||||
memset(device_string, 0x00, sizeof(device_string));
|
||||
memset(device_path, 0x00, sizeof(device_path));
|
||||
|
||||
if(read(test_fd, device_string, sizeof(device_string)) < 0)
|
||||
{
|
||||
LOG_WARNING("[i2c_smbus_linux] Failed to read i2c device name");
|
||||
}
|
||||
|
||||
if(strlen(device_string) > 0 && strlen(device_string) < sizeof(device_string))
|
||||
{
|
||||
device_string[strlen(device_string) - 1] = 0x00;
|
||||
}
|
||||
|
||||
close(test_fd);
|
||||
|
||||
// Clear PCI Information
|
||||
pci_vendor = 0;
|
||||
pci_device = 0;
|
||||
pci_subsystem_vendor = 0;
|
||||
pci_subsystem_device = 0;
|
||||
port_id = 0;
|
||||
bus_id = 0;
|
||||
|
||||
// Get port ID for AMD GPU aux bus
|
||||
sscanf(device_string, "AMDGPU DM aux hw bus %hu", &port_id);
|
||||
|
||||
// Get port ID for Nvidia GPUs
|
||||
sscanf(device_string, "NVIDIA i2c adapter %hu at", &port_id);
|
||||
|
||||
// Get port ID for PIIX4 SMBus
|
||||
sscanf(device_string, "SMBus PIIX4 adapter port %hu at", &port_id);
|
||||
|
||||
// Get the Linux Bus ID
|
||||
sscanf(ent->d_name, "i2c-%hu", &bus_id);
|
||||
|
||||
// Get device path
|
||||
strncpy(path, driver_path, sizeof(path) - 1);
|
||||
path[sizeof(path) - 1] = '\0';
|
||||
strncat(path, ent->d_name, sizeof(path) - strlen(path) - 1);
|
||||
path[sizeof(path) - 1] = '\0';
|
||||
if(ent->d_type == DT_LNK || ent->d_type == DT_UNKNOWN)
|
||||
{
|
||||
ptr = realpath(path, NULL);
|
||||
if(ptr == NULL)
|
||||
continue;
|
||||
|
||||
strncpy(path, ptr, sizeof(path) - 1);
|
||||
path[sizeof(path) - 1] = '\0';
|
||||
free(ptr);
|
||||
|
||||
/*-------------------------------------------------------------*\
|
||||
| Truncate at last '/' to get the parent PCI device directory. |
|
||||
| For AMDGPU i2c buses the realpath resolves to something like: |
|
||||
| /sys/devices/pci.../0000:03:00.0/i2c-4 |
|
||||
| The parent (0000:03:00.0) contains vendor/device/subsystem |
|
||||
| files. Using /..' traversal is unreliable in sysfs; directly |
|
||||
| truncating the path is correct and portable. |
|
||||
\*-------------------------------------------------------------*/
|
||||
char* last_slash = strrchr(path, '/');
|
||||
if(last_slash == NULL || last_slash == path)
|
||||
continue;
|
||||
*last_slash = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
strncat(path, "/device", sizeof(path) - strlen(path) - 1);
|
||||
path[sizeof(path) - 1] = '\0';
|
||||
}
|
||||
ptr = path + strlen(path);
|
||||
|
||||
// Get PCI Vendor
|
||||
strcpy(ptr, "/vendor");
|
||||
test_fd = open(path, O_RDONLY);
|
||||
if (test_fd >= 0)
|
||||
{
|
||||
memset(buff, 0x00, sizeof(buff));
|
||||
|
||||
if(read(test_fd, buff, sizeof(buff)) < 0)
|
||||
{
|
||||
LOG_INFO("[i2c_smbus_linux] Failed to read i2c device PCI vendor ID");
|
||||
}
|
||||
|
||||
if(strlen(buff) > 0 && strlen(buff) < sizeof(buff))
|
||||
{
|
||||
buff[strlen(buff) - 1] = 0x00;
|
||||
}
|
||||
pci_vendor = strtoul(buff, NULL, 16);
|
||||
close(test_fd);
|
||||
}
|
||||
|
||||
// Get PCI Device
|
||||
strcpy(ptr, "/device");
|
||||
test_fd = open(path, O_RDONLY);
|
||||
if (test_fd >= 0)
|
||||
{
|
||||
memset(buff, 0x00, sizeof(buff));
|
||||
|
||||
if(read(test_fd, buff, sizeof(buff)) < 0)
|
||||
{
|
||||
LOG_INFO("[i2c_smbus_linux] Failed to read i2c device PCI device ID");
|
||||
}
|
||||
|
||||
if(strlen(buff) > 0 && strlen(buff) < sizeof(buff))
|
||||
{
|
||||
buff[strlen(buff) - 1] = 0x00;
|
||||
}
|
||||
pci_device = strtoul(buff, NULL, 16);
|
||||
close(test_fd);
|
||||
}
|
||||
|
||||
// Get PCI Subsystem Vendor
|
||||
strcpy(ptr, "/subsystem_vendor");
|
||||
test_fd = open(path, O_RDONLY);
|
||||
if (test_fd >= 0)
|
||||
{
|
||||
memset(buff, 0x00, sizeof(buff));
|
||||
|
||||
if(read(test_fd, buff, sizeof(buff)) < 0)
|
||||
{
|
||||
LOG_INFO("[i2c_smbus_linux] Failed to read i2c device PCI subvendor ID");
|
||||
}
|
||||
|
||||
if(strlen(buff) > 0 && strlen(buff) < sizeof(buff))
|
||||
{
|
||||
buff[strlen(buff) - 1] = 0x00;
|
||||
}
|
||||
pci_subsystem_vendor = strtoul(buff, NULL, 16);
|
||||
close(test_fd);
|
||||
}
|
||||
|
||||
// Get PCI Subsystem Device
|
||||
strcpy(ptr, "/subsystem_device");
|
||||
test_fd = open(path, O_RDONLY);
|
||||
if (test_fd >= 0)
|
||||
{
|
||||
memset(buff, 0x00, sizeof(buff));
|
||||
|
||||
if(read(test_fd, buff, sizeof(buff)) < 0)
|
||||
{
|
||||
LOG_INFO("[i2c_smbus_linux] Failed to read i2c device PCI subdevice ID");
|
||||
}
|
||||
|
||||
if(strlen(buff) > 0 && strlen(buff) < sizeof(buff))
|
||||
{
|
||||
buff[strlen(buff) - 1] = 0x00;
|
||||
}
|
||||
pci_subsystem_device = strtoul(buff, NULL, 16);
|
||||
close(test_fd);
|
||||
}
|
||||
|
||||
strncpy(device_path, "/dev/", sizeof(device_path) - 1);
|
||||
device_path[sizeof(device_path) - 1] = '\0';
|
||||
strncat(device_path, ent->d_name, sizeof(device_path) - strlen(device_path) - 1);
|
||||
device_path[sizeof(device_path) - 1] = '\0';
|
||||
test_fd = open(device_path, O_RDWR);
|
||||
|
||||
if (test_fd < 0)
|
||||
{
|
||||
ret = false;
|
||||
}
|
||||
|
||||
bus = new i2c_smbus_linux();
|
||||
snprintf(bus->device_name, sizeof(bus->device_name), "%s (%s)", device_string, device_path);
|
||||
bus->handle = test_fd;
|
||||
bus->pci_device = pci_device;
|
||||
bus->pci_vendor = pci_vendor;
|
||||
bus->pci_subsystem_device = pci_subsystem_device;
|
||||
bus->pci_subsystem_vendor = pci_subsystem_vendor;
|
||||
bus->port_id = port_id;
|
||||
bus->bus_id = bus_id;
|
||||
ResourceManager::get()->RegisterI2CBus(bus);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
REGISTER_I2C_BUS_DETECTOR(i2c_smbus_linux_detect);
|
||||
@@ -0,0 +1,22 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| i2c_smbus_linux.h |
|
||||
| |
|
||||
| Linux i2c/smbus driver |
|
||||
| |
|
||||
| Adam Honse (CalcProgrammer1) 14 Feb 2019 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include "i2c_smbus.h"
|
||||
|
||||
class i2c_smbus_linux : public i2c_smbus_interface
|
||||
{
|
||||
public:
|
||||
int handle;
|
||||
|
||||
private:
|
||||
s32 i2c_smbus_xfer(u8 addr, char read_write, u8 command, int size, i2c_smbus_data* data);
|
||||
s32 i2c_xfer(u8 addr, char read_write, int* size, u8* data);
|
||||
};
|
||||
@@ -0,0 +1,542 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| i2c_smbus_i801.cpp |
|
||||
| |
|
||||
| i801 SMBUS driver for MacOS |
|
||||
| |
|
||||
| Adam Honse (CalcProgrammer1) 29 Jan 2019 |
|
||||
| Portions based on Linux source code |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include "macUSPCIOAccess.h"
|
||||
|
||||
#include "Detector.h"
|
||||
#include "i2c_smbus_i801.h"
|
||||
#include "LogManager.h"
|
||||
#include "ResourceManager.h"
|
||||
#include "SettingsManager.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
i2c_smbus_i801::i2c_smbus_i801()
|
||||
{
|
||||
}
|
||||
|
||||
i2c_smbus_i801::~i2c_smbus_i801()
|
||||
{
|
||||
}
|
||||
|
||||
/* Return negative errno on error. */
|
||||
s32 i2c_smbus_i801::i801_access(u16 addr, char read_write, u8 command, int size, i2c_smbus_data *data)
|
||||
{
|
||||
int hwpec = 0;
|
||||
int block = 0;
|
||||
int ret = 0, xact = 0;
|
||||
//struct i801_priv *priv = i2c_get_adapdata(adap);
|
||||
|
||||
//mutex_lock(&priv->acpi_lock);
|
||||
//if (priv->acpi_reserved) {
|
||||
// mutex_unlock(&priv->acpi_lock);
|
||||
// return -EBUSY;
|
||||
//}
|
||||
|
||||
//pm_runtime_get_sync(&priv->pci_dev->dev);
|
||||
|
||||
//hwpec = (priv->features & FEATURE_SMBUS_PEC) && (flags & I2C_CLIENT_PEC)
|
||||
// && size != I2C_SMBUS_QUICK
|
||||
// && size != I2C_SMBUS_I2C_BLOCK_DATA;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case I2C_SMBUS_QUICK:
|
||||
WriteIoPortByte(SMBHSTADD, ((addr & 0x7f) << 1) | (read_write & 0x01));
|
||||
xact = I801_QUICK;
|
||||
break;
|
||||
case I2C_SMBUS_BYTE:
|
||||
WriteIoPortByte(SMBHSTADD, ((addr & 0x7f) << 1) | (read_write & 0x01));
|
||||
if (read_write == I2C_SMBUS_WRITE)
|
||||
WriteIoPortByte(SMBHSTCMD, command);
|
||||
xact = I801_BYTE;
|
||||
break;
|
||||
case I2C_SMBUS_BYTE_DATA:
|
||||
WriteIoPortByte(SMBHSTADD, ((addr & 0x7f) << 1) | (read_write & 0x01));
|
||||
WriteIoPortByte(SMBHSTCMD, command);
|
||||
if (read_write == I2C_SMBUS_WRITE)
|
||||
WriteIoPortByte(SMBHSTDAT0, data->byte);
|
||||
xact = I801_BYTE_DATA;
|
||||
break;
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
WriteIoPortByte(SMBHSTADD, ((addr & 0x7f) << 1) | (read_write & 0x01));
|
||||
WriteIoPortByte(SMBHSTCMD, command);
|
||||
if (read_write == I2C_SMBUS_WRITE)
|
||||
{
|
||||
WriteIoPortByte(SMBHSTDAT0, data->word & 0xff);
|
||||
WriteIoPortByte(SMBHSTDAT1, (data->word & 0xff00) >> 8);
|
||||
}
|
||||
xact = I801_WORD_DATA;
|
||||
break;
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
WriteIoPortByte(SMBHSTADD, ((addr & 0x7f) << 1) | (read_write & 0x01));
|
||||
WriteIoPortByte(SMBHSTCMD, command);
|
||||
block = 1;
|
||||
break;
|
||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
||||
/*
|
||||
* NB: page 240 of ICH5 datasheet shows that the R/#W
|
||||
* bit should be cleared here, even when reading.
|
||||
* However if SPD Write Disable is set (Lynx Point and later),
|
||||
* the read will fail if we don't set the R/#W bit.
|
||||
*/
|
||||
WriteIoPortByte(SMBHSTADD, ((addr & 0x7f) << 1) | (read_write & 0x01));
|
||||
|
||||
if (read_write == I2C_SMBUS_READ)
|
||||
{
|
||||
/* NB: page 240 of ICH5 datasheet also shows
|
||||
* that DATA1 is the cmd field when reading */
|
||||
WriteIoPortByte(SMBHSTDAT1, command);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteIoPortByte(SMBHSTCMD, command);
|
||||
}
|
||||
block = 1;
|
||||
break;
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
//if (hwpec) /* enable/disable hardware PEC */
|
||||
// outb_p(inb_p(SMBAUXCTL(priv)) | SMBAUXCTL_CRC, SMBAUXCTL(priv));
|
||||
//else
|
||||
WriteIoPortByte(SMBAUXCTL, ReadIoPortByte(SMBAUXCTL) & (~SMBAUXCTL_CRC));
|
||||
|
||||
if (block)
|
||||
ret = i801_block_transaction(data, read_write, size, hwpec);
|
||||
else
|
||||
ret = i801_transaction(xact);
|
||||
|
||||
/* Some BIOSes don't like it when PEC is enabled at reboot or resume
|
||||
time, so we forcibly disable it after every transaction. Turn off
|
||||
E32B for the same reason. */
|
||||
//if (hwpec || block)
|
||||
WriteIoPortByte(SMBAUXCTL, ReadIoPortByte(SMBAUXCTL) & ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B));
|
||||
|
||||
if (block)
|
||||
goto out;
|
||||
if (ret)
|
||||
goto out;
|
||||
if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK))
|
||||
goto out;
|
||||
|
||||
switch (xact & 0x7f)
|
||||
{
|
||||
case I801_BYTE: /* Result put in SMBHSTDAT0 */
|
||||
case I801_BYTE_DATA:
|
||||
data->byte = ReadIoPortByte(SMBHSTDAT0);
|
||||
break;
|
||||
case I801_WORD_DATA:
|
||||
data->word = ReadIoPortByte(SMBHSTDAT0) + (ReadIoPortByte(SMBHSTDAT1) << 8);
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
//pm_runtime_mark_last_busy(&priv->pci_dev->dev);
|
||||
//pm_runtime_put_autosuspend(&priv->pci_dev->dev);
|
||||
//mutex_unlock(&priv->acpi_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Block transaction function */
|
||||
int i2c_smbus_i801::i801_block_transaction(i2c_smbus_data *data, char read_write, int command, int hwpec)
|
||||
{
|
||||
int result = 0;
|
||||
//unsigned char hostc;
|
||||
|
||||
//if (command == I2C_SMBUS_I2C_BLOCK_DATA)
|
||||
//{
|
||||
// if (read_write == I2C_SMBUS_WRITE)
|
||||
// {
|
||||
// /* set I2C_EN bit in configuration register */
|
||||
// pci_read_config_byte(priv->pci_dev, SMBHSTCFG, &hostc);
|
||||
// pci_write_config_byte(priv->pci_dev, SMBHSTCFG,
|
||||
// hostc | SMBHSTCFG_I2C_EN);
|
||||
// }
|
||||
// else if (!(priv->features & FEATURE_I2C_BLOCK_READ)) {
|
||||
// dev_err(&priv->pci_dev->dev,
|
||||
// "I2C block read is unsupported!\n");
|
||||
// return -EOPNOTSUPP;
|
||||
// }
|
||||
//}
|
||||
|
||||
if (read_write == I2C_SMBUS_WRITE || command == I2C_SMBUS_I2C_BLOCK_DATA)
|
||||
{
|
||||
if (data->block[0] < 1)
|
||||
data->block[0] = 1;
|
||||
if (data->block[0] > I2C_SMBUS_BLOCK_MAX)
|
||||
data->block[0] = I2C_SMBUS_BLOCK_MAX;
|
||||
}
|
||||
else
|
||||
{
|
||||
data->block[0] = 32; /* max for SMBus block reads */
|
||||
}
|
||||
|
||||
/* Experience has shown that the block buffer can only be used for
|
||||
SMBus (not I2C) block transactions, even though the datasheet
|
||||
doesn't mention this limitation. */
|
||||
//if ((priv->features & FEATURE_BLOCK_BUFFER)
|
||||
// && command != I2C_SMBUS_I2C_BLOCK_DATA
|
||||
// && i801_set_block_buffer_mode(priv) == 0)
|
||||
// result = i801_block_transaction_by_block(priv, data,
|
||||
// read_write, hwpec);
|
||||
//else
|
||||
result = i801_block_transaction_byte_by_byte(data, read_write, command, hwpec);
|
||||
|
||||
//if (command == I2C_SMBUS_I2C_BLOCK_DATA
|
||||
// && read_write == I2C_SMBUS_WRITE) {
|
||||
// /* restore saved configuration register value */
|
||||
// pci_write_config_byte(priv->pci_dev, SMBHSTCFG, hostc);
|
||||
//}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* For "byte-by-byte" block transactions:
|
||||
* I2C write uses cmd=I801_BLOCK_DATA, I2C_EN=1
|
||||
* I2C read uses cmd=I801_I2C_BLOCK_DATA
|
||||
*/
|
||||
int i2c_smbus_i801::i801_block_transaction_byte_by_byte(i2c_smbus_data *data, char read_write, int command, int /*hwpec*/)
|
||||
{
|
||||
int i, len;
|
||||
int smbcmd;
|
||||
int status;
|
||||
int result;
|
||||
|
||||
result = i801_check_pre();
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
len = data->block[0];
|
||||
|
||||
if (read_write == I2C_SMBUS_WRITE)
|
||||
{
|
||||
WriteIoPortByte(SMBHSTDAT0, len);
|
||||
WriteIoPortByte(SMBBLKDAT, data->block[1]);
|
||||
}
|
||||
|
||||
if (command == I2C_SMBUS_I2C_BLOCK_DATA && read_write == I2C_SMBUS_READ)
|
||||
smbcmd = I801_I2C_BLOCK_DATA;
|
||||
else
|
||||
smbcmd = I801_BLOCK_DATA;
|
||||
|
||||
//if (priv->features & FEATURE_IRQ) {
|
||||
// priv->is_read = (read_write == I2C_SMBUS_READ);
|
||||
// if (len == 1 && priv->is_read)
|
||||
// smbcmd |= SMBHSTCNT_LAST_BYTE;
|
||||
// priv->cmd = smbcmd | SMBHSTCNT_INTREN;
|
||||
// priv->len = len;
|
||||
// priv->count = 0;
|
||||
// priv->data = &data->block[1];
|
||||
//
|
||||
// outb_p(priv->cmd | SMBHSTCNT_START, SMBHSTCNT(priv));
|
||||
// result = wait_event_timeout(priv->waitq,
|
||||
// (status = priv->status),
|
||||
// adap->timeout);
|
||||
// if (!result) {
|
||||
// status = -ETIMEDOUT;
|
||||
// dev_warn(&priv->pci_dev->dev,
|
||||
// "Timeout waiting for interrupt!\n");
|
||||
// }
|
||||
// priv->status = 0;
|
||||
// return i801_check_post(priv, status);
|
||||
//}
|
||||
|
||||
for (i = 1; i <= len; i++)
|
||||
{
|
||||
if (i == len && read_write == I2C_SMBUS_READ)
|
||||
smbcmd |= SMBHSTCNT_LAST_BYTE;
|
||||
WriteIoPortByte(SMBHSTCNT, smbcmd);
|
||||
|
||||
if (i == 1)
|
||||
WriteIoPortByte(SMBHSTCNT, ReadIoPortByte(SMBHSTCNT) | SMBHSTCNT_START);
|
||||
|
||||
status = i801_wait_byte_done();
|
||||
if (status)
|
||||
goto exit;
|
||||
|
||||
if (i == 1 && read_write == I2C_SMBUS_READ && command != I2C_SMBUS_I2C_BLOCK_DATA)
|
||||
{
|
||||
len = ReadIoPortByte(SMBHSTDAT0);
|
||||
if (len < 1 || len > I2C_SMBUS_BLOCK_MAX)
|
||||
{
|
||||
/* Recover */
|
||||
while (ReadIoPortByte(SMBHSTSTS) & SMBHSTSTS_HOST_BUSY)
|
||||
WriteIoPortByte(SMBHSTSTS, SMBHSTSTS_BYTE_DONE);
|
||||
WriteIoPortByte(SMBHSTSTS, SMBHSTSTS_INTR);
|
||||
return -EPROTO;
|
||||
}
|
||||
data->block[0] = len;
|
||||
}
|
||||
|
||||
/* Retrieve/store value in SMBBLKDAT */
|
||||
if (read_write == I2C_SMBUS_READ)
|
||||
data->block[i] = ReadIoPortByte(SMBBLKDAT);
|
||||
if (read_write == I2C_SMBUS_WRITE && i + 1 <= len)
|
||||
WriteIoPortByte(SMBBLKDAT, data->block[i + 1]);
|
||||
|
||||
/* signals SMBBLKDAT ready */
|
||||
WriteIoPortByte(SMBHSTSTS, SMBHSTSTS_BYTE_DONE);
|
||||
}
|
||||
|
||||
status = i801_wait_intr();
|
||||
exit:
|
||||
return i801_check_post(status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the status register to an error code, and clear it.
|
||||
* Note that status only contains the bits we want to clear, not the
|
||||
* actual register value.
|
||||
*/
|
||||
int i2c_smbus_i801::i801_check_post(int status)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
/*
|
||||
* If the SMBus is still busy, we give up
|
||||
* Note: This timeout condition only happens when using polling
|
||||
* transactions. For interrupt operation, NAK/timeout is indicated by
|
||||
* DEV_ERR.
|
||||
*/
|
||||
if (status < 0)
|
||||
{
|
||||
/* try to stop the current command */
|
||||
WriteIoPortByte(SMBHSTCNT, ReadIoPortByte(SMBHSTCNT) | SMBHSTCNT_KILL);
|
||||
//usleep_range(1000, 2000);
|
||||
std::this_thread::sleep_for(1ms);
|
||||
WriteIoPortByte(SMBHSTCNT, ReadIoPortByte(SMBHSTCNT) & (~SMBHSTCNT_KILL));
|
||||
|
||||
WriteIoPortByte(SMBHSTSTS, STATUS_FLAGS);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (status & SMBHSTSTS_FAILED)
|
||||
{
|
||||
result = -EIO;
|
||||
}
|
||||
|
||||
if (status & SMBHSTSTS_DEV_ERR)
|
||||
{
|
||||
/*
|
||||
* This may be a PEC error, check and clear it.
|
||||
*
|
||||
* AUXSTS is handled differently from HSTSTS.
|
||||
* For HSTSTS, i801_isr() or i801_wait_intr()
|
||||
* has already cleared the error bits in hardware,
|
||||
* and we are passed a copy of the original value
|
||||
* in "status".
|
||||
* For AUXSTS, the hardware register is left
|
||||
* for us to handle here.
|
||||
* This is asymmetric, slightly iffy, but safe,
|
||||
* since all this code is serialized and the CRCE
|
||||
* bit is harmless as long as it's cleared before
|
||||
* the next operation.
|
||||
*/
|
||||
//if ((priv->features & FEATURE_SMBUS_PEC) &&
|
||||
// (inb_p(SMBAUXSTS(priv)) & SMBAUXSTS_CRCE)) {
|
||||
// outb_p(SMBAUXSTS_CRCE, SMBAUXSTS(priv));
|
||||
// result = -EBADMSG;
|
||||
// dev_dbg(&priv->pci_dev->dev, "PEC error\n");
|
||||
//}
|
||||
//else {
|
||||
result = -ENXIO;
|
||||
// dev_dbg(&priv->pci_dev->dev, "No response\n");
|
||||
//}
|
||||
}
|
||||
|
||||
if (status & SMBHSTSTS_BUS_ERR)
|
||||
{
|
||||
result = -EAGAIN;
|
||||
}
|
||||
|
||||
/* Clear status flags except BYTE_DONE, to be cleared by caller */
|
||||
WriteIoPortByte(SMBHSTSTS, status);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Make sure the SMBus host is ready to start transmitting.
|
||||
Return 0 if it is, -EBUSY if it is not. */
|
||||
int i2c_smbus_i801::i801_check_pre()
|
||||
{
|
||||
int status;
|
||||
|
||||
status = ReadIoPortByte(SMBHSTSTS);
|
||||
if (status & SMBHSTSTS_HOST_BUSY)
|
||||
{
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
status &= STATUS_FLAGS;
|
||||
if (status)
|
||||
{
|
||||
WriteIoPortByte(SMBHSTSTS, status);
|
||||
status = ReadIoPortByte(SMBHSTSTS) & STATUS_FLAGS;
|
||||
if (status)
|
||||
{
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear CRC status if needed.
|
||||
* During normal operation, i801_check_post() takes care
|
||||
* of it after every operation. We do it here only in case
|
||||
* the hardware was already in this state when the driver
|
||||
* started.
|
||||
*/
|
||||
//if (priv->features & FEATURE_SMBUS_PEC) {
|
||||
// status = inb_p(SMBAUXSTS(priv)) & SMBAUXSTS_CRCE;
|
||||
// if (status) {
|
||||
// dev_dbg(&priv->pci_dev->dev,
|
||||
// "Clearing aux status flags (%02x)\n", status);
|
||||
// outb_p(status, SMBAUXSTS(priv));
|
||||
// status = inb_p(SMBAUXSTS(priv)) & SMBAUXSTS_CRCE;
|
||||
// if (status) {
|
||||
// dev_err(&priv->pci_dev->dev,
|
||||
// "Failed clearing aux status flags (%02x)\n",
|
||||
// status);
|
||||
// return -EBUSY;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2c_smbus_i801::i801_transaction(int xact)
|
||||
{
|
||||
int status;
|
||||
int result;
|
||||
|
||||
result = i801_check_pre();
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
WriteIoPortByte(SMBHSTCNT, ReadIoPortByte(SMBHSTCNT) & ~SMBHSTCNT_INTREN);
|
||||
//if (priv->features & FEATURE_IRQ)
|
||||
//{
|
||||
// outb_p(xact | SMBHSTCNT_INTREN | SMBHSTCNT_START,
|
||||
// SMBHSTCNT(priv));
|
||||
// result = wait_event_timeout(priv->waitq,
|
||||
// (status = priv->status),
|
||||
// adap->timeout);
|
||||
// if (!result) {
|
||||
// status = -ETIMEDOUT;
|
||||
// dev_warn(&priv->pci_dev->dev,
|
||||
// "Timeout waiting for interrupt!\n");
|
||||
// }
|
||||
// priv->status = 0;
|
||||
// return i801_check_post(priv, status);
|
||||
//}
|
||||
|
||||
/* the current contents of SMBHSTCNT can be overwritten, since PEC,
|
||||
* SMBSCMD are passed in xact */
|
||||
WriteIoPortByte(SMBHSTCNT, xact | SMBHSTCNT_START);
|
||||
|
||||
status = i801_wait_intr();
|
||||
return i801_check_post(status);
|
||||
}
|
||||
|
||||
/* Wait for either BYTE_DONE or an error flag being set */
|
||||
int i2c_smbus_i801::i801_wait_byte_done()
|
||||
{
|
||||
|
||||
int timeout = 0;
|
||||
int status;
|
||||
|
||||
/* We will always wait for a fraction of a second! */
|
||||
do
|
||||
{
|
||||
std::this_thread::sleep_for(1ms);
|
||||
//usleep_range(250, 500);
|
||||
status = ReadIoPortByte(SMBHSTSTS);
|
||||
} while (!(status & (STATUS_ERROR_FLAGS | SMBHSTSTS_BYTE_DONE)) && (timeout++ < MAX_RETRIES));
|
||||
|
||||
if (timeout > MAX_RETRIES)
|
||||
{
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
return status & STATUS_ERROR_FLAGS;
|
||||
}
|
||||
|
||||
/* Wait for BUSY being cleared and either INTR or an error flag being set */
|
||||
int i2c_smbus_i801::i801_wait_intr()
|
||||
{
|
||||
int timeout = 0;
|
||||
int status;
|
||||
|
||||
/* We will always wait for a fraction of a second! */
|
||||
do
|
||||
{
|
||||
//usleep_range(250, 500);
|
||||
status = ReadIoPortByte(SMBHSTSTS);
|
||||
} while (((status & SMBHSTSTS_HOST_BUSY) || !(status & (STATUS_ERROR_FLAGS | SMBHSTSTS_INTR))) && (timeout++ < MAX_RETRIES));
|
||||
|
||||
if (timeout > MAX_RETRIES)
|
||||
{
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
return status & (STATUS_ERROR_FLAGS | SMBHSTSTS_INTR);
|
||||
}
|
||||
|
||||
s32 i2c_smbus_i801::i2c_smbus_xfer(u8 addr, char read_write, u8 command, int size, i2c_smbus_data* data)
|
||||
{
|
||||
s32 result = i801_access(addr, read_write, command, size, data);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
s32 i2c_smbus_i801::i2c_xfer(u8 /*addr*/, char /*read_write*/, int* /*size*/, u8* /*data*/)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool i2c_smbus_i801_detect()
|
||||
{
|
||||
if(!GetMacUSPCIODriverStatus())
|
||||
{
|
||||
LOG_INFO("macUSPCIO is not loaded, i801 I2C bus detection aborted");
|
||||
return(false);
|
||||
}
|
||||
|
||||
uint8_t host_config = ReadConfigPortByte(SMBHSTCFG);
|
||||
if ((host_config & SMBHSTCFG_HST_EN) == 0)
|
||||
{
|
||||
LOG_INFO("i801 SMBus Disabled");
|
||||
return(false);
|
||||
}
|
||||
|
||||
i2c_smbus_interface * bus;
|
||||
bus = new i2c_smbus_i801();
|
||||
// addresses are referenced from: https://opensource.apple.com/source/IOPCIFamily/IOPCIFamily-146/IOKit/pci/IOPCIDevice.h.auto.html
|
||||
bus->pci_vendor = ReadConfigPortWord(0x00);
|
||||
bus->pci_device = ReadConfigPortWord(0x02);
|
||||
bus->pci_subsystem_vendor = ReadConfigPortWord(0x2c);
|
||||
bus->pci_subsystem_device = ReadConfigPortWord(0x2e);
|
||||
|
||||
if(!bus->pci_vendor || !bus->pci_device || !bus->pci_subsystem_vendor || !bus->pci_subsystem_device)
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
|
||||
snprintf(bus->device_name, 512, "Intel(R) SMBus - %X", bus->pci_device);
|
||||
((i2c_smbus_i801 *)bus)->i801_smba = ReadConfigPortWord(0x20) & 0xFFFE;
|
||||
ResourceManager::get()->RegisterI2CBus(bus);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
REGISTER_I2C_BUS_DETECTOR(i2c_smbus_i801_detect);
|
||||
@@ -0,0 +1,104 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| i2c_smbus_i801.h |
|
||||
| |
|
||||
| i801 SMBUS driver for MacOS |
|
||||
| |
|
||||
| Adam Honse (CalcProgrammer1) 29 Jan 2019 |
|
||||
| Portions based on Linux source code |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "i2c_smbus.h"
|
||||
|
||||
/* BIT shifting macro */
|
||||
#define BIT(x) ( 1 << x )
|
||||
|
||||
/* I801 SMBus address offsets */
|
||||
#define SMBHSTSTS (0 + i801_smba)
|
||||
#define SMBHSTCNT (2 + i801_smba)
|
||||
#define SMBHSTCMD (3 + i801_smba)
|
||||
#define SMBHSTADD (4 + i801_smba)
|
||||
#define SMBHSTDAT0 (5 + i801_smba)
|
||||
#define SMBHSTDAT1 (6 + i801_smba)
|
||||
#define SMBBLKDAT (7 + i801_smba)
|
||||
#define SMBPEC (8 + i801_smba) /* ICH3 and later */
|
||||
#define SMBAUXSTS (12 + i801_smba) /* ICH4 and later */
|
||||
#define SMBAUXCTL (13 + i801_smba) /* ICH4 and later */
|
||||
#define SMBSLVSTS (16 + i801_smba) /* ICH3 and later */
|
||||
#define SMBSLVCMD (17 + i801_smba) /* ICH3 and later */
|
||||
#define SMBNTFDADD (20 + i801_smba) /* ICH3 and later */
|
||||
|
||||
|
||||
/* Auxiliary status register bits, ICH4+ only */
|
||||
#define SMBAUXSTS_CRCE BIT(0)
|
||||
#define SMBAUXSTS_STCO BIT(1)
|
||||
|
||||
/* Auxiliary control register bits, ICH4+ only */
|
||||
#define SMBAUXCTL_CRC BIT(0)
|
||||
#define SMBAUXCTL_E32B BIT(1)
|
||||
|
||||
/* Other settings */
|
||||
#define MAX_RETRIES 400
|
||||
|
||||
/* I801 command constants */
|
||||
#define I801_QUICK 0x00
|
||||
#define I801_BYTE 0x04
|
||||
#define I801_BYTE_DATA 0x08
|
||||
#define I801_WORD_DATA 0x0C
|
||||
#define I801_PROC_CALL 0x10 /* unimplemented */
|
||||
#define I801_BLOCK_DATA 0x14
|
||||
#define I801_I2C_BLOCK_DATA 0x18 /* ICH5 and later */
|
||||
|
||||
/* I801 Host Control register bits */
|
||||
#define SMBHSTCNT_INTREN BIT(0)
|
||||
#define SMBHSTCNT_KILL BIT(1)
|
||||
#define SMBHSTCNT_LAST_BYTE BIT(5)
|
||||
#define SMBHSTCNT_START BIT(6)
|
||||
#define SMBHSTCNT_PEC_EN BIT(7) /* ICH3 and later */
|
||||
|
||||
/* I801 Hosts Status register bits */
|
||||
#define SMBHSTSTS_BYTE_DONE BIT(7)
|
||||
#define SMBHSTSTS_INUSE_STS BIT(6)
|
||||
#define SMBHSTSTS_SMBALERT_STS BIT(5)
|
||||
#define SMBHSTSTS_FAILED BIT(4)
|
||||
#define SMBHSTSTS_BUS_ERR BIT(3)
|
||||
#define SMBHSTSTS_DEV_ERR BIT(2)
|
||||
#define SMBHSTSTS_INTR BIT(1)
|
||||
#define SMBHSTSTS_HOST_BUSY BIT(0)
|
||||
|
||||
/* Host Notify Status register bits */
|
||||
#define SMBSLVSTS_HST_NTFY_STS BIT(0)
|
||||
|
||||
/* Host Notify Command register bits */
|
||||
#define SMBSLVCMD_HST_NTFY_INTREN BIT(0)
|
||||
|
||||
#define STATUS_ERROR_FLAGS (SMBHSTSTS_FAILED | SMBHSTSTS_BUS_ERR | SMBHSTSTS_DEV_ERR)
|
||||
#define STATUS_FLAGS (SMBHSTSTS_BYTE_DONE | SMBHSTSTS_INTR | STATUS_ERROR_FLAGS)
|
||||
|
||||
/* I801 Host Config */
|
||||
#define SMBHSTCFG 0x040
|
||||
#define SMBHSTCFG_HST_EN BIT(0)
|
||||
|
||||
class i2c_smbus_i801 : public i2c_smbus_interface
|
||||
{
|
||||
public:
|
||||
u16 i801_smba = 0xF000;
|
||||
i2c_smbus_i801();
|
||||
~i2c_smbus_i801();
|
||||
|
||||
private:
|
||||
s32 i801_access(u16 addr, char read_write, u8 command, int size, i2c_smbus_data *data);
|
||||
int i801_block_transaction(i2c_smbus_data *data, char read_write, int command, int hwpec);
|
||||
int i801_block_transaction_byte_by_byte(i2c_smbus_data *data, char read_write, int command, int hwpec);
|
||||
int i801_check_post(int status);
|
||||
int i801_check_pre();
|
||||
int i801_transaction(int xact);
|
||||
int i801_wait_byte_done();
|
||||
int i801_wait_intr();
|
||||
s32 i2c_smbus_xfer(u8 addr, char read_write, u8 command, int size, i2c_smbus_data* data);
|
||||
s32 i2c_xfer(u8 addr, char read_write, int* size, u8* data);
|
||||
};
|
||||
@@ -0,0 +1,275 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| i2c_smbus_nct6775.cpp |
|
||||
| |
|
||||
| Nuvoton NCT67xx SMBUS driver for MacOS |
|
||||
| |
|
||||
| Adam Honse (CalcProgrammer1) 19 May 2019 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include "macUSPCIOAccess.h"
|
||||
|
||||
#include "Detector.h"
|
||||
#include "i2c_smbus_nct6775.h"
|
||||
#include "LogManager.h"
|
||||
#include "ResourceManager.h"
|
||||
#include "SettingsManager.h"
|
||||
#include "super_io.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
i2c_smbus_nct6775::i2c_smbus_nct6775()
|
||||
{
|
||||
}
|
||||
|
||||
i2c_smbus_nct6775::~i2c_smbus_nct6775()
|
||||
{
|
||||
}
|
||||
|
||||
s32 i2c_smbus_nct6775::nct6775_access(u16 addr, char read_write, u8 command, int size, i2c_smbus_data *data)
|
||||
{
|
||||
int i, len, cnt;
|
||||
i2c_smbus_data tmp_data;
|
||||
int timeout = 0;
|
||||
|
||||
tmp_data.word = 0;
|
||||
cnt = 0;
|
||||
len = 0;
|
||||
|
||||
WriteIoPortByte(SMBHSTCTL, NCT6775_SOFT_RESET);
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case I2C_SMBUS_QUICK:
|
||||
WriteIoPortByte(SMBHSTADD, (addr << 1) | read_write);
|
||||
break;
|
||||
case I2C_SMBUS_BYTE_DATA:
|
||||
tmp_data.byte = data->byte;
|
||||
case I2C_SMBUS_BYTE:
|
||||
WriteIoPortByte(SMBHSTADD, (addr << 1) | read_write);
|
||||
WriteIoPortByte(SMBHSTIDX, command);
|
||||
if (read_write == I2C_SMBUS_WRITE)
|
||||
{
|
||||
WriteIoPortByte(SMBHSTDAT, tmp_data.byte);
|
||||
WriteIoPortByte(SMBHSTCMD, NCT6775_WRITE_BYTE);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteIoPortByte(SMBHSTCMD, NCT6775_READ_BYTE);
|
||||
}
|
||||
break;
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
WriteIoPortByte(SMBHSTADD, (addr << 1) | read_write);
|
||||
WriteIoPortByte(SMBHSTIDX, command);
|
||||
if (read_write == I2C_SMBUS_WRITE)
|
||||
{
|
||||
WriteIoPortByte(SMBHSTDAT, data->word & 0xFF);
|
||||
WriteIoPortByte(SMBHSTDAT, (data->word & 0xFF00) >> 8);
|
||||
WriteIoPortByte(SMBHSTCMD, NCT6775_WRITE_WORD);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteIoPortByte(SMBHSTCMD, NCT6775_READ_WORD);
|
||||
}
|
||||
break;
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
WriteIoPortByte(SMBHSTADD, (addr << 1) | read_write);
|
||||
WriteIoPortByte(SMBHSTIDX, command);
|
||||
if (read_write == I2C_SMBUS_WRITE)
|
||||
{
|
||||
len = data->block[0];
|
||||
if (len == 0 || len > I2C_SMBUS_BLOCK_MAX)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
WriteIoPortByte(SMBBLKSZ, len);
|
||||
|
||||
//Load 4 bytes into FIFO
|
||||
cnt = 1;
|
||||
if (len >= 4)
|
||||
{
|
||||
for (i = cnt; i <= 4; i++)
|
||||
{
|
||||
WriteIoPortByte(SMBHSTDAT, data->block[i]);
|
||||
}
|
||||
|
||||
len -= 4;
|
||||
cnt += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = cnt; i <= len; i++)
|
||||
{
|
||||
WriteIoPortByte(SMBHSTDAT, data->block[i]);
|
||||
}
|
||||
|
||||
len = 0;
|
||||
}
|
||||
|
||||
WriteIoPortByte(SMBHSTCMD, NCT6775_WRITE_BLOCK);
|
||||
}
|
||||
else
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
//Out32(SMBHSTCMD, NCT6775_READ_BLOCK);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
WriteIoPortByte(SMBHSTCTL, NCT6775_MANUAL_START);
|
||||
|
||||
while ((size == I2C_SMBUS_BLOCK_DATA) && (len > 0))
|
||||
{
|
||||
if (read_write == I2C_SMBUS_WRITE)
|
||||
{
|
||||
timeout = 0;
|
||||
while ((ReadIoPortByte(SMBHSTSTS) & NCT6775_FIFO_EMPTY) == 0)
|
||||
{
|
||||
if(timeout > NCT6775_MAX_RETRIES)
|
||||
{
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
std::this_thread::sleep_for(1ms);;
|
||||
timeout++;
|
||||
}
|
||||
|
||||
//Load more bytes into FIFO
|
||||
if (len >= 4)
|
||||
{
|
||||
for (i = cnt; i <= (cnt + 4); i++)
|
||||
{
|
||||
WriteIoPortByte(SMBHSTDAT, data->block[i]);
|
||||
}
|
||||
|
||||
len -= 4;
|
||||
cnt += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = cnt; i <= (cnt + len); i++)
|
||||
{
|
||||
WriteIoPortByte(SMBHSTDAT, data->block[i]);
|
||||
}
|
||||
|
||||
len = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
//wait for manual mode to complete
|
||||
timeout = 0;
|
||||
while ((ReadIoPortByte(SMBHSTSTS) & NCT6775_MANUAL_ACTIVE) != 0)
|
||||
{
|
||||
if(timeout > NCT6775_MAX_RETRIES)
|
||||
{
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
std::this_thread::sleep_for(1ms);;
|
||||
timeout++;
|
||||
}
|
||||
|
||||
if ((ReadIoPortByte(SMBHSTERR) & NCT6775_NO_ACK) != 0)
|
||||
{
|
||||
return -EPROTO;
|
||||
}
|
||||
else if ((read_write == I2C_SMBUS_WRITE) || (size == I2C_SMBUS_QUICK))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case I2C_SMBUS_QUICK:
|
||||
case I2C_SMBUS_BYTE_DATA:
|
||||
data->byte = (u8)ReadIoPortByte(SMBHSTDAT);
|
||||
break;
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
data->word = ReadIoPortByte(SMBHSTDAT) + (ReadIoPortByte(SMBHSTDAT) << 8);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 i2c_smbus_nct6775::i2c_smbus_xfer(u8 addr, char read_write, u8 command, int size, i2c_smbus_data* data)
|
||||
{
|
||||
s32 result = nct6775_access(addr, read_write, command, size, data);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
s32 i2c_smbus_nct6775::i2c_xfer(u8 /*addr*/, char /*read_write*/, int* /*size*/, u8* /*data*/)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool i2c_smbus_nct6775_detect()
|
||||
{
|
||||
if(!GetMacUSPCIODriverStatus())
|
||||
{
|
||||
LOG_INFO("macUSPCIO is not loaded, nct6775 I2C bus detection aborted");
|
||||
return(false);
|
||||
}
|
||||
|
||||
i2c_smbus_interface* bus;
|
||||
int sioaddr = 0x2E;
|
||||
superio_enter(sioaddr);
|
||||
|
||||
int val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8) | superio_inb(sioaddr, SIO_REG_DEVID + 1);
|
||||
|
||||
switch (val & SIO_ID_MASK)
|
||||
{
|
||||
case SIO_NCT5577_ID:
|
||||
case SIO_NCT6102_ID:
|
||||
case SIO_NCT6793_ID:
|
||||
case SIO_NCT6796_ID:
|
||||
case SIO_NCT6798_ID:
|
||||
// Create new nct6775 bus and invalidate the PCI ID information
|
||||
bus = new i2c_smbus_nct6775();
|
||||
bus->pci_vendor = 0xFFFF;
|
||||
bus->pci_device = 0xFFFF;
|
||||
bus->pci_subsystem_vendor = 0xFFFF;
|
||||
bus->pci_subsystem_device = 0xFFFF;
|
||||
|
||||
// Set logical device register to get SMBus base address
|
||||
superio_outb(sioaddr, SIO_REG_LOGDEV, SIO_LOGDEV_SMBUS);
|
||||
|
||||
// Get SMBus base address from configuration register
|
||||
int smba = (superio_inb(sioaddr, SIO_REG_SMBA) << 8) | superio_inb(sioaddr, SIO_REG_SMBA + 1);
|
||||
((i2c_smbus_nct6775*)bus)->nct6775_smba = smba;
|
||||
|
||||
// Set device name string
|
||||
switch (val & SIO_ID_MASK)
|
||||
{
|
||||
case SIO_NCT5577_ID:
|
||||
snprintf(bus->device_name, 512, "Nuvoton NCT5577D SMBus at %X", smba);
|
||||
break;
|
||||
case SIO_NCT6102_ID:
|
||||
snprintf(bus->device_name, 512, "Nuvoton NCT6102D/NCT6106D SMBus at %X", smba);
|
||||
break;
|
||||
case SIO_NCT6793_ID:
|
||||
snprintf(bus->device_name, 512, "Nuvoton NCT6793D SMBus at %X", smba);
|
||||
break;
|
||||
case SIO_NCT6796_ID:
|
||||
snprintf(bus->device_name, 512, "Nuvoton NCT6796D SMBus at %X", smba);
|
||||
break;
|
||||
case SIO_NCT6798_ID:
|
||||
snprintf(bus->device_name, 512, "Nuvoton NCT6798D SMBus at %X", smba);
|
||||
break;
|
||||
}
|
||||
|
||||
ResourceManager::get()->RegisterI2CBus(bus);
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
REGISTER_I2C_BUS_DETECTOR(i2c_smbus_nct6775_detect);
|
||||
@@ -0,0 +1,61 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| i2c_smbus_nct6775.h |
|
||||
| |
|
||||
| Nuvoton NCT67xx SMBUS driver for MacOS |
|
||||
| |
|
||||
| Adam Honse (CalcProgrammer1) 19 May 2019 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "i2c_smbus.h"
|
||||
|
||||
#define SMBHSTDAT (0 + nct6775_smba)
|
||||
#define SMBBLKSZ (1 + nct6775_smba)
|
||||
#define SMBHSTCMD (2 + nct6775_smba)
|
||||
#define SMBHSTIDX (3 + nct6775_smba) //Index field is the Command field on other controllers
|
||||
#define SMBHSTCTL (4 + nct6775_smba)
|
||||
#define SMBHSTADD (5 + nct6775_smba)
|
||||
#define SMBHSTERR (9 + nct6775_smba)
|
||||
#define SMBHSTSTS (0xE + nct6775_smba)
|
||||
|
||||
/* Command register */
|
||||
#define NCT6775_READ_BYTE 0
|
||||
#define NCT6775_READ_WORD 1
|
||||
#define NCT6775_READ_BLOCK 2
|
||||
#define NCT6775_BLOCK_WRITE_READ_PROC_CALL 3
|
||||
#define NCT6775_PROC_CALL 4
|
||||
#define NCT6775_WRITE_BYTE 8
|
||||
#define NCT6775_WRITE_WORD 9
|
||||
#define NCT6775_WRITE_BLOCK 10
|
||||
|
||||
/* Control register */
|
||||
#define NCT6775_MANUAL_START 128
|
||||
#define NCT6775_SOFT_RESET 64
|
||||
|
||||
/* Error register */
|
||||
#define NCT6775_NO_ACK 32
|
||||
|
||||
/* Status register */
|
||||
#define NCT6775_FIFO_EMPTY 1
|
||||
#define NCT6775_FIFO_FULL 2
|
||||
#define NCT6775_MANUAL_ACTIVE 4
|
||||
|
||||
/* Other settings */
|
||||
#define NCT6775_MAX_RETRIES 400
|
||||
|
||||
class i2c_smbus_nct6775: public i2c_smbus_interface
|
||||
{
|
||||
public:
|
||||
u16 nct6775_smba = 0x0290;
|
||||
i2c_smbus_nct6775();
|
||||
~i2c_smbus_nct6775();
|
||||
|
||||
private:
|
||||
s32 nct6775_access(u16 addr, char read_write, u8 command, int size, i2c_smbus_data *data);
|
||||
s32 i2c_smbus_xfer(u8 addr, char read_write, u8 command, int size, i2c_smbus_data* data);
|
||||
s32 i2c_xfer(u8 addr, char read_write, int* size, u8* data);
|
||||
};
|
||||
@@ -0,0 +1,288 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| i2c_smbus_piix4.cpp |
|
||||
| |
|
||||
| PIIX4 SMBUS driver for MacOS |
|
||||
| |
|
||||
| Adam Honse (CalcProgrammer1) 08 Aug 2018 |
|
||||
| Portions based on Linux source code |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include "macUSPCIOAccess.h"
|
||||
|
||||
#include "Detector.h"
|
||||
#include "i2c_smbus_piix4.h"
|
||||
#include "LogManager.h"
|
||||
#include "pci_ids.h"
|
||||
#include "ResourceManager.h"
|
||||
#include "SettingsManager.h"
|
||||
|
||||
i2c_smbus_piix4::i2c_smbus_piix4()
|
||||
{
|
||||
json drivers_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("Drivers");
|
||||
|
||||
bool amd_smbus_reduce_cpu = false;
|
||||
if(drivers_settings.contains("amd_smbus_reduce_cpu"))
|
||||
{
|
||||
amd_smbus_reduce_cpu = drivers_settings["amd_smbus_reduce_cpu"].get<bool>();
|
||||
}
|
||||
|
||||
delay_timer = amd_smbus_reduce_cpu;
|
||||
}
|
||||
|
||||
i2c_smbus_piix4::~i2c_smbus_piix4()
|
||||
{
|
||||
}
|
||||
|
||||
//Logic adapted from piix4_transaction() in i2c-piix4.c
|
||||
int i2c_smbus_piix4::piix4_transaction()
|
||||
{
|
||||
int result = 0;
|
||||
int temp;
|
||||
int timeout = 0;
|
||||
|
||||
/* start the transaction by setting bit 6 */
|
||||
temp = ReadIoPortByte(SMBHSTCNT);
|
||||
WriteIoPortByte(SMBHSTCNT, temp | 0x040);
|
||||
|
||||
/* We will always wait for a fraction of a second! (See PIIX4 docs errata) */
|
||||
/*---------------------------------------------------------------------------------------------------------------*\
|
||||
| The above comment from 2002 or earlier is still relevant for the Zen 4 architecture. |
|
||||
| AMD takes bug-for-bug compatibility seriously. It's the least they can do since they've classified Ryzen |
|
||||
| system programming documentation as trade secret. |
|
||||
| |
|
||||
| Instead of just waiting for an unspecified amount of time, we try to follow exactly what the |
|
||||
| Intel 83271 Specification Update says about SMBHSTSTS. |
|
||||
| |
|
||||
|-----------------------------------------------------------------------------------------------------------------|
|
||||
| [Bit] 1 SMBus Interrupt/Host Completion (INTER)—R/WC. |
|
||||
| 1 = Indicates that the host transaction has completed or that the source of an SMBus interrupt was the |
|
||||
| completion of the last host command. |
|
||||
| 0 = Host transaction has not completed or that an SMBus interrupt was not caused by host command completion. |
|
||||
| This bit is only set by hardware and can only be reset by writing a 1 to this bit position. |
|
||||
|-----------------------------------------------------------------------------------------------------------------|
|
||||
| [Bit] 0 Host Busy (HOST_BUSY)—RO. |
|
||||
| 1 = Indicates that the SMBus controller host interface is in the process of completing a command. |
|
||||
| 0 = SMBus controller host interface is not processing a command. None of the other registers should be accessed |
|
||||
| if this bit is set. Note that there may be moderate latency before the transaction begins and the Host Busy bit |
|
||||
| gets set. |
|
||||
\*---------------------------------------------------------------------------------------------------------------*/
|
||||
temp = 0;
|
||||
|
||||
if(delay_timer)
|
||||
{
|
||||
do
|
||||
{
|
||||
usleep(RETRY_DELAY_US);
|
||||
temp = ReadIoPortByte(SMBHSTSTS);
|
||||
}
|
||||
while((++timeout < MAX_TIMEOUT) && ((temp & 0x03) != 0x02));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::chrono::steady_clock::time_point deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(500);
|
||||
do
|
||||
{
|
||||
temp = ReadIoPortByte(SMBHSTSTS);
|
||||
}
|
||||
while((std::chrono::steady_clock::now() < deadline) && ((temp & 0x03) != 0x02));
|
||||
temp = ReadIoPortByte(SMBHSTSTS); // we can be preempted between reading the register and checking for the timeout
|
||||
}
|
||||
|
||||
/* If the SMBus is still busy, we give up */
|
||||
if (temp & 0x01)
|
||||
{
|
||||
result = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (temp & 0x10)
|
||||
{
|
||||
result = -EIO;
|
||||
}
|
||||
|
||||
if (temp & 0x08)
|
||||
{
|
||||
result = -EIO;
|
||||
}
|
||||
|
||||
if (temp & 0x04)
|
||||
{
|
||||
result = -ENXIO;
|
||||
}
|
||||
|
||||
temp = ReadIoPortByte(SMBHSTSTS);
|
||||
if (temp != 0x00)
|
||||
{
|
||||
WriteIoPortByte(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, temp;
|
||||
|
||||
/* Make sure the SMBus host is ready to start transmitting */
|
||||
temp = ReadIoPortByte(SMBHSTSTS);
|
||||
if (temp != 0x00)
|
||||
{
|
||||
WriteIoPortByte(SMBHSTSTS, temp);
|
||||
temp = ReadIoPortByte(SMBHSTSTS);
|
||||
if (temp != 0x00)
|
||||
{
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case I2C_SMBUS_QUICK:
|
||||
WriteIoPortByte(SMBHSTADD, (addr << 1) | read_write);
|
||||
size = PIIX4_QUICK;
|
||||
break;
|
||||
case I2C_SMBUS_BYTE:
|
||||
WriteIoPortByte(SMBHSTADD, (addr << 1) | read_write);
|
||||
if (read_write == I2C_SMBUS_WRITE)
|
||||
{
|
||||
WriteIoPortByte(SMBHSTCMD, command);
|
||||
}
|
||||
size = PIIX4_BYTE;
|
||||
break;
|
||||
case I2C_SMBUS_BYTE_DATA:
|
||||
WriteIoPortByte(SMBHSTADD, (addr << 1) | read_write);
|
||||
WriteIoPortByte(SMBHSTCMD, command);
|
||||
if (read_write == I2C_SMBUS_WRITE)
|
||||
{
|
||||
WriteIoPortByte(SMBHSTDAT0, data->byte);
|
||||
}
|
||||
size = PIIX4_BYTE_DATA;
|
||||
break;
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
WriteIoPortByte(SMBHSTADD, (addr << 1) | read_write);
|
||||
WriteIoPortByte(SMBHSTCMD, command);
|
||||
if (read_write == I2C_SMBUS_WRITE)
|
||||
{
|
||||
WriteIoPortByte(SMBHSTDAT0, data->word & 0xFF);
|
||||
WriteIoPortByte(SMBHSTDAT1, (data->word & 0xFF00) >> 8);
|
||||
}
|
||||
size = PIIX4_WORD_DATA;
|
||||
break;
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
WriteIoPortByte(SMBHSTADD, (addr << 1) | read_write);
|
||||
WriteIoPortByte(SMBHSTCMD, command);
|
||||
if (read_write == I2C_SMBUS_WRITE)
|
||||
{
|
||||
len = data->block[0];
|
||||
if (len == 0 || len > I2C_SMBUS_BLOCK_MAX)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
WriteIoPortByte(SMBHSTDAT0, len);
|
||||
ReadIoPortByte(SMBHSTCNT);
|
||||
for (i = 1; i <= len; i++)
|
||||
{
|
||||
WriteIoPortByte(SMBBLKDAT, data->block[i]);
|
||||
}
|
||||
}
|
||||
size = PIIX4_BLOCK_DATA;
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
WriteIoPortByte(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)ReadIoPortByte(SMBHSTDAT0);
|
||||
break;
|
||||
case PIIX4_WORD_DATA:
|
||||
data->word = ReadIoPortByte(SMBHSTDAT0) + (ReadIoPortByte(SMBHSTDAT1) << 8);
|
||||
break;
|
||||
case PIIX4_BLOCK_DATA:
|
||||
data->block[0] = (u8)ReadIoPortByte(SMBHSTDAT0);
|
||||
if (data->block[0] == 0 || data->block[0] > I2C_SMBUS_BLOCK_MAX)
|
||||
{
|
||||
return -EPROTO;
|
||||
}
|
||||
ReadIoPortByte(SMBHSTCNT);
|
||||
for (i = 1; i <= data->block[0]; i++)
|
||||
{
|
||||
data->block[i] = (u8)ReadIoPortByte(SMBBLKDAT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 i2c_smbus_piix4::i2c_smbus_xfer(u8 addr, char read_write, u8 command, int size, i2c_smbus_data* data)
|
||||
{
|
||||
s32 result = piix4_access(addr, read_write, command, size, data);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
s32 i2c_smbus_piix4::i2c_xfer(u8 /*addr*/, char /*read_write*/, int* /*size*/, u8* /*data*/)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool i2c_smbus_piix4_detect()
|
||||
{
|
||||
if(!GetMacUSPCIODriverStatus())
|
||||
{
|
||||
LOG_INFO("macUSPCIO is not loaded, piix4 I2C bus detection aborted");
|
||||
return(false);
|
||||
}
|
||||
|
||||
// addresses are referenced from: https://opensource.apple.com/source/IOPCIFamily/IOPCIFamily-146/IOKit/pci/IOPCIDevice.h.auto.html
|
||||
uint16_t vendor_id = ReadConfigPortWord(0x00);
|
||||
uint16_t device_id = ReadConfigPortWord(0x02);
|
||||
uint16_t subsystem_vendor_id = ReadConfigPortWord(0x2c);
|
||||
uint16_t subsystem_device_id = ReadConfigPortWord(0x2e);
|
||||
|
||||
if(vendor_id != AMD_VEN || !device_id || !subsystem_vendor_id || !subsystem_device_id)
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
|
||||
i2c_smbus_interface * bus;
|
||||
|
||||
bus = new i2c_smbus_piix4();
|
||||
bus->pci_vendor = vendor_id;
|
||||
bus->pci_device = device_id;
|
||||
bus->pci_subsystem_vendor = subsystem_vendor_id;
|
||||
bus->pci_subsystem_device = subsystem_device_id;
|
||||
strcpy(bus->device_name, "Advanced Micro Devices, Inc PIIX4 SMBus at 0x0B00");
|
||||
((i2c_smbus_piix4 *)bus)->piix4_smba = 0x0B00;
|
||||
ResourceManager::get()->RegisterI2CBus(bus);
|
||||
|
||||
bus = new i2c_smbus_piix4();
|
||||
bus->pci_vendor = vendor_id;
|
||||
bus->pci_device = device_id;
|
||||
bus->pci_subsystem_vendor = subsystem_vendor_id;
|
||||
bus->pci_subsystem_device = subsystem_device_id;
|
||||
((i2c_smbus_piix4 *)bus)->piix4_smba = 0x0B20;
|
||||
strcpy(bus->device_name, "Advanced Micro Devices, Inc PIIX4 SMBus at 0x0B20");
|
||||
ResourceManager::get()->RegisterI2CBus(bus);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
REGISTER_I2C_BUS_DETECTOR(i2c_smbus_piix4_detect);
|
||||
@@ -0,0 +1,55 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| i2c_smbus_piix4.h |
|
||||
| |
|
||||
| PIIX4 SMBUS driver for MacOS |
|
||||
| |
|
||||
| Adam Honse (CalcProgrammer1) 08 Aug 2018 |
|
||||
| Portions based on Linux source code |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "i2c_smbus.h"
|
||||
|
||||
// PIIX4 SMBus address offsets
|
||||
#define SMBHSTSTS (0 + piix4_smba)
|
||||
#define SMBHSLVSTS (1 + piix4_smba)
|
||||
#define SMBHSTCNT (2 + piix4_smba)
|
||||
#define SMBHSTCMD (3 + piix4_smba)
|
||||
#define SMBHSTADD (4 + piix4_smba)
|
||||
#define SMBHSTDAT0 (5 + piix4_smba)
|
||||
#define SMBHSTDAT1 (6 + piix4_smba)
|
||||
#define SMBBLKDAT (7 + piix4_smba)
|
||||
#define SMBSLVCNT (8 + piix4_smba)
|
||||
#define SMBSHDWCMD (9 + piix4_smba)
|
||||
#define SMBSLVEVT (0xA + piix4_smba)
|
||||
#define SMBSLVDAT (0xC + piix4_smba)
|
||||
|
||||
#define MAX_TIMEOUT 5000
|
||||
#define RETRY_DELAY_US 250
|
||||
|
||||
// PIIX4 constants
|
||||
#define PIIX4_QUICK 0x00
|
||||
#define PIIX4_BYTE 0x04
|
||||
#define PIIX4_BYTE_DATA 0x08
|
||||
#define PIIX4_WORD_DATA 0x0C
|
||||
#define PIIX4_BLOCK_DATA 0x14
|
||||
|
||||
class i2c_smbus_piix4 : public i2c_smbus_interface
|
||||
{
|
||||
public:
|
||||
u16 piix4_smba = 0x0B00;
|
||||
i2c_smbus_piix4();
|
||||
~i2c_smbus_piix4();
|
||||
|
||||
private:
|
||||
int piix4_transaction();
|
||||
s32 piix4_access(u16 addr, char read_write, u8 command, int size, i2c_smbus_data *data);
|
||||
s32 i2c_smbus_xfer(u8 addr, char read_write, u8 command, int size, i2c_smbus_data* data);
|
||||
s32 i2c_xfer(u8 addr, char read_write, int* size, u8* data);
|
||||
|
||||
bool delay_timer;
|
||||
};
|
||||
@@ -0,0 +1,309 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| i2c_smbus_amdadl.cpp |
|
||||
| |
|
||||
| Definitions and types for AMD ADL I2C functions |
|
||||
| |
|
||||
| Niels Westphal (crashniels) 30 May 2020 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include <string>
|
||||
#include "Detector.h"
|
||||
#include "i2c_smbus_amdadl.h"
|
||||
#include "LogManager.h"
|
||||
#include "pci_ids.h"
|
||||
|
||||
typedef int ( *ADL2_MAIN_CONTROL_CREATE )(ADL_MAIN_MALLOC_CALLBACK, int, ADL_CONTEXT_HANDLE*);
|
||||
typedef int ( *ADL2_MAIN_CONTROL_DESTROY )(ADL_CONTEXT_HANDLE);
|
||||
typedef int ( *ADL2_ADAPTER_NUMBEROFADAPTERS_GET ) ( ADL_CONTEXT_HANDLE , int* );
|
||||
typedef int ( *ADL2_ADAPTER_PRIMARY_GET) (ADL_CONTEXT_HANDLE, int* lpPrimaryAdapterIndex);
|
||||
typedef int ( *ADL2_ADAPTER_ADAPTERINFOX2_GET) (ADL_CONTEXT_HANDLE, AdapterInfo**);
|
||||
typedef int ( *ADL2_ADAPTER_ADAPTERINFOX4_GET) (ADL_CONTEXT_HANDLE, int iAdapterIndex, int* numAdapters, AdapterInfoX2** lppAdapterInfoX2);
|
||||
typedef int ( *ADL2_DISPLAY_WRITEANDREADI2C) (ADL_CONTEXT_HANDLE, int iAdapterIndex, ADLI2C* plI2C);
|
||||
|
||||
ADL2_MAIN_CONTROL_CREATE ADL2_Main_Control_Create;
|
||||
ADL2_MAIN_CONTROL_DESTROY ADL2_Main_Control_Destroy;
|
||||
ADL2_ADAPTER_NUMBEROFADAPTERS_GET ADL2_Adapter_NumberOfAdapters_Get;
|
||||
ADL2_ADAPTER_PRIMARY_GET ADL2_Adapter_Primary_Get;
|
||||
ADL2_ADAPTER_ADAPTERINFOX2_GET ADL2_Adapter_AdapterInfoX2_Get;
|
||||
ADL2_ADAPTER_ADAPTERINFOX4_GET ADL2_Adapter_AdapterInfoX4_Get;
|
||||
ADL2_DISPLAY_WRITEANDREADI2C ADL2_Display_WriteAndReadI2C;
|
||||
|
||||
int LoadLibraries()
|
||||
{
|
||||
HINSTANCE hDLL;
|
||||
|
||||
hDLL = LoadLibrary("atiadlxx.dll");
|
||||
|
||||
if(hDLL == NULL)
|
||||
{
|
||||
/*---------------------------------------------------------------------*\
|
||||
| A 32 bit calling application on 64 bit OS will fail to LoadLibrary. |
|
||||
| Try to load the 32 bit library (atiadlxy.dll) instead |
|
||||
\*---------------------------------------------------------------------*/
|
||||
hDLL = LoadLibrary("atiadlxy.dll");
|
||||
return ADL_ERR;
|
||||
}
|
||||
else
|
||||
{
|
||||
ADL2_Main_Control_Create = (ADL2_MAIN_CONTROL_CREATE)GetProcAddress(hDLL, "ADL2_Main_Control_Create");
|
||||
ADL2_Main_Control_Destroy = (ADL2_MAIN_CONTROL_DESTROY)GetProcAddress(hDLL, "ADL2_Main_Control_Destroy");
|
||||
ADL2_Adapter_NumberOfAdapters_Get = (ADL2_ADAPTER_NUMBEROFADAPTERS_GET)GetProcAddress(hDLL, "ADL2_Adapter_NumberOfAdapters_Get");
|
||||
ADL2_Adapter_Primary_Get = (ADL2_ADAPTER_PRIMARY_GET)GetProcAddress(hDLL, "ADL2_Adapter_Primary_Get");
|
||||
ADL2_Adapter_AdapterInfoX2_Get = (ADL2_ADAPTER_ADAPTERINFOX2_GET)GetProcAddress(hDLL, "ADL2_Adapter_AdapterInfoX2_Get");
|
||||
ADL2_Adapter_AdapterInfoX4_Get = (ADL2_ADAPTER_ADAPTERINFOX4_GET)GetProcAddress(hDLL, "ADL2_Adapter_AdapterInfoX4_Get");
|
||||
ADL2_Display_WriteAndReadI2C = (ADL2_DISPLAY_WRITEANDREADI2C)GetProcAddress(hDLL, "ADL2_Display_WriteAndReadI2C");
|
||||
|
||||
/*---------------------------------------------------------------------*\
|
||||
| Only return OK if all function pointers are valid |
|
||||
\*---------------------------------------------------------------------*/
|
||||
if( ADL2_Main_Control_Create
|
||||
&& ADL2_Main_Control_Destroy
|
||||
&& ADL2_Adapter_NumberOfAdapters_Get
|
||||
&& ADL2_Adapter_Primary_Get
|
||||
&& ADL2_Adapter_AdapterInfoX2_Get
|
||||
&& ADL2_Adapter_AdapterInfoX4_Get
|
||||
&& ADL2_Display_WriteAndReadI2C)
|
||||
{
|
||||
return ADL_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ADL_ERR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Memory allocation function
|
||||
void* __stdcall ADL_Main_Memory_Alloc ( int iSize )
|
||||
{
|
||||
void* lpBuffer = malloc ( iSize );
|
||||
return lpBuffer;
|
||||
}
|
||||
|
||||
// Optional Memory de-allocation function
|
||||
void __stdcall ADL_Main_Memory_Free ( void* lpBuffer )
|
||||
{
|
||||
if ( NULL != lpBuffer )
|
||||
{
|
||||
free ( lpBuffer );
|
||||
lpBuffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_smbus_amdadl::i2c_smbus_amdadl(ADL_CONTEXT_HANDLE context, int adapter_index)
|
||||
{
|
||||
int num_of_devices;
|
||||
AdapterInfoX2* info;
|
||||
|
||||
this->context = context;
|
||||
|
||||
if (ADL_OK != ADL2_Adapter_AdapterInfoX4_Get(context, adapter_index, &num_of_devices, &info))
|
||||
{
|
||||
printf("Cannot get Adapter Info!\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string pnp_str = info->strPNPString;
|
||||
|
||||
std::size_t ven_loc = pnp_str.find("VEN_");
|
||||
std::size_t dev_loc = pnp_str.find("DEV_");
|
||||
std::size_t sub_loc = pnp_str.find("SUBSYS_");
|
||||
|
||||
if((ven_loc != std::string::npos) && (dev_loc != std::string::npos) && (sub_loc != std::string::npos))
|
||||
{
|
||||
std::string ven_str = pnp_str.substr(ven_loc + 4, 4);
|
||||
std::string dev_str = pnp_str.substr(dev_loc + 4, 4);
|
||||
std::string sbv_str = pnp_str.substr(sub_loc + 11, 4);
|
||||
std::string sbd_str = pnp_str.substr(sub_loc + 7, 4);
|
||||
|
||||
int ven_id = (int)std::stoul(ven_str, nullptr, 16);
|
||||
int dev_id = (int)std::stoul(dev_str, nullptr, 16);
|
||||
int sbv_id = (int)std::stoul(sbv_str, nullptr, 16);
|
||||
int sbd_id = (int)std::stoul(sbd_str, nullptr, 16);
|
||||
|
||||
this->pci_vendor = ven_id;
|
||||
this->pci_device = dev_id;
|
||||
this->pci_subsystem_vendor = sbv_id;
|
||||
this->pci_subsystem_device = sbd_id;
|
||||
this->port_id = 1;
|
||||
strcpy(this->device_name, "AMD ADL");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s32 i2c_smbus_amdadl::i2c_smbus_xfer(u8 addr, char read_write, u8 command, int size, i2c_smbus_data* data)
|
||||
{
|
||||
int PrimaryDisplay;
|
||||
int ret;
|
||||
int data_size = 0;
|
||||
char* data_ptr;
|
||||
|
||||
ADLI2C* pI2C;
|
||||
ADLI2C I2Cstore;
|
||||
pI2C = &I2Cstore;
|
||||
|
||||
char i2c_buf[I2C_SMBUS_BLOCK_MAX + 8];
|
||||
|
||||
pI2C->iSize = sizeof(ADLI2C);
|
||||
pI2C->iSpeed = 100;
|
||||
pI2C->iLine = 1; //location of the Aura chip
|
||||
pI2C->iAddress = addr << 1;
|
||||
pI2C->iOffset = 0;
|
||||
pI2C->pcData = (char*)data;
|
||||
|
||||
if (ADL_OK != ADL2_Adapter_Primary_Get(context, &PrimaryDisplay))
|
||||
{
|
||||
printf("Cannot get Display!\n");
|
||||
return ADL_ERR;
|
||||
}
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case I2C_SMBUS_QUICK:
|
||||
//garbo data
|
||||
//pI2C->iDataSize = 0;
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_BYTE:
|
||||
//pI2C->iDataSize = 1;
|
||||
//break;
|
||||
|
||||
case I2C_SMBUS_BYTE_DATA:
|
||||
data_size = 1;
|
||||
data_ptr = (char*)data;
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
data_size = 2;
|
||||
data_ptr = (char*)data;
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
data_size = data->block[0] + 1;
|
||||
data_ptr = (char*)&data->block[0];
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
||||
data_size = data->block[0];
|
||||
data_ptr = (char*)&data->block[1];
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (read_write == I2C_SMBUS_READ)
|
||||
{
|
||||
/* An SMBus read can be achieved by setting the offset to the command (register address) */
|
||||
pI2C->iOffset = command;
|
||||
pI2C->iAction = ADL_DL_I2C_ACTIONREAD;
|
||||
pI2C->iDataSize = data_size;
|
||||
pI2C->pcData = (char *)data_ptr;
|
||||
|
||||
ret = ADL2_Display_WriteAndReadI2C(context, PrimaryDisplay, pI2C);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* An SMBus write has one extra byte, the register address, before the data */
|
||||
pI2C->iAction = ADL_DL_I2C_ACTIONWRITE;
|
||||
pI2C->iDataSize = data_size + 1;
|
||||
pI2C->pcData = i2c_buf;
|
||||
|
||||
i2c_buf[0] = command;
|
||||
memcpy(&i2c_buf[1], data_ptr, data_size);
|
||||
|
||||
ret = ADL2_Display_WriteAndReadI2C(context, PrimaryDisplay, pI2C);
|
||||
}
|
||||
|
||||
return(ret);
|
||||
};
|
||||
|
||||
s32 i2c_smbus_amdadl::i2c_xfer(u8 addr, char read_write, int* size, u8* data)
|
||||
{
|
||||
int PrimaryDisplay;
|
||||
int ret;
|
||||
int data_size = *size;
|
||||
|
||||
ADLI2C* pI2C;
|
||||
ADLI2C I2Cstore;
|
||||
pI2C = &I2Cstore;
|
||||
|
||||
pI2C->iSize = sizeof(ADLI2C);
|
||||
pI2C->iSpeed = 100;
|
||||
pI2C->iLine = 1; //location of the Aura chip
|
||||
pI2C->iAddress = addr << 1;
|
||||
pI2C->iOffset = 0;
|
||||
pI2C->iDataSize = data_size;
|
||||
|
||||
if (ADL_OK != ADL2_Adapter_Primary_Get(context, &PrimaryDisplay))
|
||||
{
|
||||
printf("Cannot get Display!\n");
|
||||
return ADL_ERR;
|
||||
}
|
||||
|
||||
if (read_write == I2C_SMBUS_READ)
|
||||
{
|
||||
pI2C->iAction = ADL_DL_I2C_ACTIONREAD;
|
||||
pI2C->pcData = (char*)data;
|
||||
ret = ADL2_Display_WriteAndReadI2C(context, PrimaryDisplay, pI2C);
|
||||
}
|
||||
else
|
||||
{
|
||||
pI2C->iAction = ADL_DL_I2C_ACTIONWRITE;
|
||||
pI2C->pcData = (char*)data;
|
||||
|
||||
ret = ADL2_Display_WriteAndReadI2C(context, PrimaryDisplay, pI2C);
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
bool i2c_smbus_amdadl_detect()
|
||||
{
|
||||
ADL_CONTEXT_HANDLE context;
|
||||
|
||||
if(ADL_OK == LoadLibraries())
|
||||
{
|
||||
if (ADL_OK != ADL2_Main_Control_Create(::ADL_Main_Memory_Alloc, 1, &context))
|
||||
{
|
||||
printf("Cannot get handle!\n");
|
||||
return(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
int num_of_devices;
|
||||
AdapterInfoX2* info;
|
||||
if (ADL_OK == ADL2_Adapter_AdapterInfoX4_Get(context, -1, &num_of_devices, &info))
|
||||
{
|
||||
int last_bus_number = -1;
|
||||
for(int i = 0; i < num_of_devices; i++)
|
||||
{
|
||||
AdapterInfoX2 current = *(info + i);
|
||||
if(last_bus_number == current.iBusNumber)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
last_bus_number = current.iBusNumber;
|
||||
i2c_smbus_amdadl * adl_bus = new i2c_smbus_amdadl(context, current.iAdapterIndex);
|
||||
|
||||
if(adl_bus->pci_vendor != AMD_GPU_VEN)
|
||||
{
|
||||
delete adl_bus;
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG_INFO("ADL GPU Device %04X:%04X Subsystem: %04X:%04X", adl_bus->pci_vendor, adl_bus->pci_device,adl_bus->pci_subsystem_vendor,adl_bus->pci_subsystem_device);
|
||||
ResourceManager::get()->RegisterI2CBus(adl_bus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return(true);
|
||||
} /* DetectAMDADLI2CBusses() */
|
||||
|
||||
REGISTER_I2C_BUS_DETECTOR(i2c_smbus_amdadl_detect);
|
||||
@@ -0,0 +1,37 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| i2c_smbus_amdadl.h |
|
||||
| |
|
||||
| Definitions and types for AMD ADL I2C functions |
|
||||
| |
|
||||
| Niels Westphal (crashniels) 30 May 2020 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tchar.h>
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
#include "i2c_smbus.h"
|
||||
#include "adl_sdk.h"
|
||||
#include "adl_defines.h"
|
||||
#include "adl_structures.h"
|
||||
#include "windows.h"
|
||||
|
||||
class i2c_smbus_amdadl : public i2c_smbus_interface
|
||||
{
|
||||
public:
|
||||
i2c_smbus_amdadl(ADL_CONTEXT_HANDLE context, int adapter_index);
|
||||
|
||||
int LoadLibraries();
|
||||
//void* __stdcall ADL_Main_Memory_Alloc ( int iSize );
|
||||
//void __stdcall ADL_Main_Memory_Free ( void* lpBuffer );
|
||||
int ADL_Initialize();
|
||||
|
||||
private:
|
||||
s32 i2c_smbus_xfer(u8 addr, char read_write, u8 command, int size, i2c_smbus_data* data);
|
||||
s32 i2c_xfer(u8 addr, char read_write, int* size, u8* data);
|
||||
ADL_CONTEXT_HANDLE context = NULL;
|
||||
};
|
||||
@@ -0,0 +1,204 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| i2c_smbus_nvapi.cpp |
|
||||
| |
|
||||
| Nvidia NvAPI I2C driver |
|
||||
| |
|
||||
| Adam Honse (CalcProgrammer1) 21 Feb 2020 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include "i2c_smbus_nvapi.h"
|
||||
|
||||
i2c_smbus_nvapi::i2c_smbus_nvapi(NV_PHYSICAL_GPU_HANDLE handle)
|
||||
{
|
||||
this->handle = handle;
|
||||
}
|
||||
|
||||
s32 i2c_smbus_nvapi::i2c_smbus_xfer(u8 addr, char read_write, u8 command, int mode, i2c_smbus_data* data)
|
||||
{
|
||||
NV_STATUS ret;
|
||||
unsigned int unknown = 0;
|
||||
NV_I2C_INFO_V3 i2c_data;
|
||||
uint8_t data_buf[I2C_SMBUS_BLOCK_MAX];
|
||||
uint8_t chip_addr;
|
||||
|
||||
// Set up chip register address to command, one byte in length
|
||||
chip_addr = command;
|
||||
i2c_data.i2c_reg_address = &chip_addr;
|
||||
i2c_data.reg_addr_size = 1;
|
||||
|
||||
// Set up data buffer, zero bytes in length
|
||||
i2c_data.data = data_buf;
|
||||
i2c_data.size = 0;
|
||||
|
||||
// Always use GPU port 1 - this is where RGB controllers are attached
|
||||
i2c_data.is_ddc_port = 0;
|
||||
i2c_data.port_id = 1;
|
||||
i2c_data.is_port_id_set = 1;
|
||||
|
||||
// Use default speed
|
||||
i2c_data.i2c_speed = 0xFFFF;
|
||||
i2c_data.i2c_speed_khz = NV_I2C_SPEED::NVAPI_I2C_SPEED_DEFAULT;
|
||||
|
||||
// Load device address
|
||||
i2c_data.i2c_dev_address = (addr << 1);
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case I2C_SMBUS_BYTE:
|
||||
// One byte of data with no register address
|
||||
i2c_data.reg_addr_size = 0;
|
||||
data_buf[0] = command;
|
||||
i2c_data.size = 1;
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_BYTE_DATA:
|
||||
// One byte of data with one byte of register address
|
||||
data_buf[0] = data->byte;
|
||||
i2c_data.size = 1;
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
// One word of data with one byte of register address
|
||||
data_buf[0] = (data->word & 0x00ff);
|
||||
data_buf[1] = (data->word & 0xff00) >> 8;
|
||||
i2c_data.size = 2;
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
data_buf[0] = data->block[0];
|
||||
memcpy(&data_buf[1], &(data->block[1]), data->block[0]);
|
||||
i2c_data.size = data->block[0] + 1;
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
||||
memcpy(&data_buf[0], &(data->block[1]), data->block[0]);
|
||||
i2c_data.size = data->block[0];
|
||||
break;
|
||||
|
||||
// Not supported
|
||||
case I2C_SMBUS_QUICK:
|
||||
return -1;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Perform read or write
|
||||
if(read_write == I2C_SMBUS_WRITE)
|
||||
{
|
||||
ret = NvAPI_I2CWriteEx(handle, &i2c_data, &unknown);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = NvAPI_I2CReadEx(handle, &i2c_data, &unknown);
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case I2C_SMBUS_BYTE:
|
||||
case I2C_SMBUS_BYTE_DATA:
|
||||
data->byte = i2c_data.data[0];
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
data->word = (i2c_data.data[0] | (i2c_data.data[1] << 8));
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
||||
data->block[0] = i2c_data.size;
|
||||
memcpy( &(data->block[1]), i2c_data.data, i2c_data.size);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
s32 i2c_smbus_nvapi::i2c_xfer(u8 addr, char read_write, int* size, u8* data)
|
||||
{
|
||||
NV_STATUS ret;
|
||||
unsigned int unknown = 0;
|
||||
NV_I2C_INFO_V3 i2c_data;
|
||||
|
||||
i2c_data.i2c_reg_address = NULL;
|
||||
i2c_data.reg_addr_size = 0;
|
||||
|
||||
// Set up data buffer, zero bytes in length
|
||||
i2c_data.data = data;
|
||||
i2c_data.size = *size;
|
||||
|
||||
// Always use GPU port 1 - this is where RGB controllers are attached
|
||||
i2c_data.is_ddc_port = 0;
|
||||
i2c_data.port_id = 1;
|
||||
i2c_data.is_port_id_set = 1;
|
||||
|
||||
// Use default speed
|
||||
i2c_data.i2c_speed = 0xFFFF;
|
||||
i2c_data.i2c_speed_khz = NV_I2C_SPEED::NVAPI_I2C_SPEED_DEFAULT;
|
||||
|
||||
// Load device address
|
||||
i2c_data.i2c_dev_address = (addr << 1);
|
||||
|
||||
// Perform read or write
|
||||
if(read_write == I2C_SMBUS_WRITE)
|
||||
{
|
||||
ret = NvAPI_I2CWriteEx(handle, &i2c_data, &unknown);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = NvAPI_I2CReadEx(handle, &i2c_data, &unknown);
|
||||
|
||||
*size = i2c_data.size;
|
||||
memcpy(data, i2c_data.data, i2c_data.size);
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
#include "Detector.h"
|
||||
|
||||
bool i2c_smbus_nvapi_detect()
|
||||
{
|
||||
static NV_PHYSICAL_GPU_HANDLE gpu_handles[64];
|
||||
static NV_S32 gpu_count = 0;
|
||||
NV_U32 device_id;
|
||||
NV_U32 ext_device_id;
|
||||
NV_STATUS res;
|
||||
NV_U32 revision_id;
|
||||
NV_U32 sub_system_id;
|
||||
|
||||
NvAPI_Initialize();
|
||||
|
||||
NvAPI_EnumPhysicalGPUs(gpu_handles, &gpu_count);
|
||||
|
||||
for(NV_S32 gpu_idx = 0; gpu_idx < gpu_count; gpu_idx++)
|
||||
{
|
||||
i2c_smbus_nvapi * nvapi_bus = new i2c_smbus_nvapi(gpu_handles[gpu_idx]);
|
||||
|
||||
snprintf(nvapi_bus->device_name, 512, "Nvidia NvAPI I2C on GPU %d", gpu_idx);
|
||||
|
||||
res = NvAPI_GPU_GetPCIIdentifiers(gpu_handles[gpu_idx], &device_id, &sub_system_id, &revision_id, &ext_device_id);
|
||||
|
||||
if (res == 0)
|
||||
{
|
||||
nvapi_bus->pci_device = device_id >> 16;
|
||||
nvapi_bus->pci_vendor = device_id & 0xffff;
|
||||
nvapi_bus->pci_subsystem_device = sub_system_id >> 16;
|
||||
nvapi_bus->pci_subsystem_vendor = sub_system_id & 0xffff;
|
||||
nvapi_bus->port_id = 1;
|
||||
}
|
||||
|
||||
ResourceManager::get()->RegisterI2CBus(nvapi_bus);
|
||||
}
|
||||
|
||||
return(true);
|
||||
} /* DetectNvAPII2CBusses() */
|
||||
|
||||
REGISTER_I2C_BUS_DETECTOR(i2c_smbus_nvapi_detect);
|
||||
@@ -0,0 +1,28 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| i2c_smbus_nvapi.h |
|
||||
| |
|
||||
| Nvidia NvAPI I2C driver |
|
||||
| |
|
||||
| Adam Honse (CalcProgrammer1) 21 Feb 2020 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "i2c_smbus.h"
|
||||
#include "nvapi.h"
|
||||
|
||||
class i2c_smbus_nvapi : public i2c_smbus_interface
|
||||
{
|
||||
public:
|
||||
i2c_smbus_nvapi(NV_PHYSICAL_GPU_HANDLE handle);
|
||||
s32 i2c_read_block_data(u8 addr, u8 length, u8 *values);
|
||||
s32 i2c_write_block_data(u8 addr, u8 length, u8 *values);
|
||||
|
||||
private:
|
||||
s32 i2c_smbus_xfer(u8 addr, char read_write, u8 command, int mode, i2c_smbus_data* data);
|
||||
s32 i2c_xfer(u8 addr, char read_write, int* size, u8* data);
|
||||
NV_PHYSICAL_GPU_HANDLE handle;
|
||||
};
|
||||
@@ -0,0 +1,458 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| i2c_smbus_pawnio.cpp |
|
||||
| |
|
||||
| PawnIO SMBUS driver for Windows |
|
||||
| |
|
||||
| Stephen Horvath (Steve-Tech) 04 May 2025 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include <string>
|
||||
#include "Detector.h"
|
||||
#include "i2c_smbus_pawnio.h"
|
||||
#include "LogManager.h"
|
||||
#include "PawnIOLib.h"
|
||||
#include "ResourceManager.h"
|
||||
#include "SettingsManager.h"
|
||||
#include "wmi.h"
|
||||
|
||||
std::unordered_map<std::string, int> i2c_smbus_pawnio::using_handle;
|
||||
|
||||
s32 imc_index_sel(HANDLE pawnio_handle, s32 index)
|
||||
{
|
||||
const SIZE_T in_size = 1;
|
||||
ULONG64 in[in_size] = {(ULONG64)index};
|
||||
const SIZE_T out_size = 1;
|
||||
ULONG64 out[out_size];
|
||||
SIZE_T return_size;
|
||||
HRESULT status;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Execute IMC slot_sel ioctl |
|
||||
\*-----------------------------------------------------*/
|
||||
status = pawnio_execute(pawnio_handle, "ioctl_smbus_index", in, in_size, out, 1, &return_size);
|
||||
|
||||
return(status ? -EIO : 0);
|
||||
}
|
||||
|
||||
s32 piix4_port_sel(HANDLE pawnio_handle, s32 port)
|
||||
{
|
||||
const SIZE_T in_size = 1;
|
||||
ULONG64 in[in_size] = {(ULONG64)port};
|
||||
const SIZE_T out_size = 1;
|
||||
ULONG64 out[out_size];
|
||||
SIZE_T return_size;
|
||||
HRESULT status;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Execute PIIX4 port_sel ioctl |
|
||||
\*-----------------------------------------------------*/
|
||||
status = pawnio_execute(pawnio_handle, "ioctl_piix4_port_sel", in, in_size, out, 1, &return_size);
|
||||
|
||||
return(status ? -EIO : 0);
|
||||
}
|
||||
|
||||
s32 set_sleep_mode(HANDLE pawnio_handle, s32 sleep_mode)
|
||||
{
|
||||
const SIZE_T in_size = 1;
|
||||
ULONG64 in[in_size] = {(ULONG64)sleep_mode};
|
||||
const SIZE_T out_size = 0;
|
||||
SIZE_T return_size;
|
||||
HRESULT status;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Execute PIIX4 port_sel ioctl |
|
||||
\*-----------------------------------------------------*/
|
||||
status = pawnio_execute(pawnio_handle, "ioctl_set_sleep_mode", in, in_size, NULL, 1, &return_size);
|
||||
|
||||
return(status ? -EIO : 0);
|
||||
}
|
||||
|
||||
i2c_smbus_pawnio::i2c_smbus_pawnio(HANDLE handle, std::string name)
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Store bus information |
|
||||
| TODO: Remove name field once all drivers use the same |
|
||||
| ioctl names |
|
||||
\*-----------------------------------------------------*/
|
||||
this->handle = handle;
|
||||
this->name = name;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Get driver settings |
|
||||
\*-----------------------------------------------------*/
|
||||
json drivers_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("Drivers");
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Get bus information |
|
||||
\*-----------------------------------------------------*/
|
||||
const SIZE_T in_size = 1;
|
||||
ULONG64 in[in_size] = {0};
|
||||
const SIZE_T out_size = 3;
|
||||
ULONG64 out[out_size];
|
||||
SIZE_T return_size;
|
||||
HRESULT status;
|
||||
|
||||
status = pawnio_execute(handle, "ioctl_identity", in, in_size, out, out_size, &return_size);
|
||||
|
||||
if(!status)
|
||||
{
|
||||
this->pci_vendor = (int)(out[2] & 0x000000000000FFFF);
|
||||
this->pci_device = (int)((out[2] & 0x00000000FFFF0000) >> 16);
|
||||
this->pci_subsystem_vendor = (int)((out[2] & 0x0000FFFF0000FFFF) >> 32);
|
||||
this->pci_subsystem_device = (int)((out[2] & 0xFFFF000000000000) >> 48);
|
||||
|
||||
char name_str[9];
|
||||
name_str[0] = (char)(out[0] & 0x00000000000000FF);
|
||||
name_str[1] = (char)((out[0] & 0x000000000000FF00) >> 8);
|
||||
name_str[2] = (char)((out[0] & 0x0000000000FF0000) >> 16);
|
||||
name_str[3] = (char)((out[0] & 0x00000000FF000000) >> 24);
|
||||
name_str[4] = (char)((out[0] & 0x000000FF00000000) >> 32);
|
||||
name_str[5] = (char)((out[0] & 0x0000FF0000000000) >> 40);
|
||||
name_str[6] = (char)((out[0] & 0x00FF000000000000) >> 48);
|
||||
name_str[7] = (char)((out[0] & 0xFF00000000000000) >> 56);
|
||||
name_str[8] = 0;
|
||||
|
||||
strncpy(this->device_name, name_str, 512 );
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Get shared SMBus access setting |
|
||||
\*-----------------------------------------------------*/
|
||||
bool shared_smbus_access = true;
|
||||
|
||||
if(drivers_settings.contains("shared_smbus_access"))
|
||||
{
|
||||
shared_smbus_access = drivers_settings["shared_smbus_access"].get<bool>();
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Get shared SMBus access setting |
|
||||
\*-----------------------------------------------------*/
|
||||
unsigned int smbus_sleep_mode = PAWNIO_SLEEPMODE_ALWAYSSLEEP;
|
||||
|
||||
if(drivers_settings.contains("smbus_sleep_mode"))
|
||||
{
|
||||
smbus_sleep_mode = drivers_settings["smbus_sleep_mode"];
|
||||
}
|
||||
|
||||
if(smbus_sleep_mode >= PAWNIO_SLEEPMODE_MAX)
|
||||
{
|
||||
smbus_sleep_mode = PAWNIO_SLEEPMODE_ALWAYSSLEEP;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Initialize sleep mode |
|
||||
\*-----------------------------------------------------*/
|
||||
set_sleep_mode(handle, smbus_sleep_mode);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Create global SMBus mutex if enabled |
|
||||
\*-----------------------------------------------------*/
|
||||
if(shared_smbus_access)
|
||||
{
|
||||
global_smbus_access_handle = CreateMutexA(NULL, FALSE, GLOBAL_SMBUS_MUTEX_NAME);
|
||||
}
|
||||
|
||||
using_handle[name]++;
|
||||
}
|
||||
|
||||
i2c_smbus_pawnio::~i2c_smbus_pawnio()
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Close global SMBus mutex |
|
||||
\*-----------------------------------------------------*/
|
||||
if(global_smbus_access_handle != NULL)
|
||||
{
|
||||
CloseHandle(global_smbus_access_handle);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| TODO: find a way to do this without name field |
|
||||
\*-----------------------------------------------------*/
|
||||
if(--using_handle[name] == 0 && pawnio_close(handle))
|
||||
{
|
||||
LOG_ERROR("PawnIO failed to close");
|
||||
}
|
||||
}
|
||||
|
||||
s32 i2c_smbus_pawnio::i2c_smbus_xfer(u8 addr, char read_write, u8 command, int size, i2c_smbus_data* data)
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Lock SMBus mutex |
|
||||
\*-----------------------------------------------------*/
|
||||
if(global_smbus_access_handle != NULL)
|
||||
{
|
||||
WaitForSingleObject(global_smbus_access_handle, INFINITE);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Pack input data |
|
||||
\*-----------------------------------------------------*/
|
||||
const SIZE_T in_size = 9;
|
||||
ULONG64 in[in_size];
|
||||
const SIZE_T out_size = 5;
|
||||
ULONG64 out[out_size];
|
||||
SIZE_T return_size;
|
||||
HRESULT status;
|
||||
|
||||
in[0] = addr;
|
||||
in[1] = read_write;
|
||||
in[2] = command;
|
||||
in[3] = size;
|
||||
|
||||
if(data != NULL)
|
||||
{
|
||||
memcpy( &in[4], data, sizeof(i2c_smbus_data));
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Perform SMBus transfer |
|
||||
\*-----------------------------------------------------*/
|
||||
status = pawnio_execute(handle, "ioctl_smbus_xfer", in, in_size, out, out_size, &return_size);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Unpack output data |
|
||||
\*-----------------------------------------------------*/
|
||||
if(data != NULL)
|
||||
{
|
||||
memcpy( data, &out[0], sizeof(i2c_smbus_data));
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Unlock SMBus mutex |
|
||||
\*-----------------------------------------------------*/
|
||||
if(global_smbus_access_handle != NULL)
|
||||
{
|
||||
ReleaseMutex(global_smbus_access_handle);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| If the PawnIO driver returned an error, convert it to |
|
||||
| the appropriate i2c_smbus error instead |
|
||||
\*-----------------------------------------------------*/
|
||||
if(status != 0)
|
||||
{
|
||||
status = -1;
|
||||
}
|
||||
|
||||
return(status);
|
||||
}
|
||||
|
||||
s32 i2c_smbus_pawnio::i2c_xfer(u8 /*addr*/, char /*read_write*/, int* /*size*/, u8* /*data*/)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
|
||||
HRESULT i2c_smbus_pawnio::start_pawnio(std::string filename, PHANDLE phandle)
|
||||
{
|
||||
char exePath[MAX_PATH];
|
||||
HANDLE handle;
|
||||
HRESULT status;
|
||||
|
||||
LOG_INFO("Start PawnIO: %s", filename.c_str());
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Get the path of the executable |
|
||||
\*-----------------------------------------------------*/
|
||||
if(!GetModuleFileNameA(NULL, exePath, MAX_PATH))
|
||||
{
|
||||
LOG_ERROR("Failed to get executable path, PawnIO initialization aborted");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Construct the path to `filename` in the executable's |
|
||||
| directory |
|
||||
\*-----------------------------------------------------*/
|
||||
std::filesystem::path exeDir = std::filesystem::path(exePath).parent_path();
|
||||
std::filesystem::path filePath = exeDir / filename;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Check if the file exists |
|
||||
\*-----------------------------------------------------*/
|
||||
if(!std::filesystem::exists(filePath))
|
||||
{
|
||||
LOG_ERROR("Failed to find %s in the executable's directory, PawnIO initialization aborted", filename.c_str());
|
||||
return(E_FAIL);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Open the file |
|
||||
\*-----------------------------------------------------*/
|
||||
std::ifstream file(filePath, std::ios::binary);
|
||||
|
||||
if(!file.is_open())
|
||||
{
|
||||
LOG_ERROR("Failed to open %s, PawnIO initialization aborted", filename.c_str());
|
||||
return(E_FAIL);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Read the contents of the file into a vector of bytes |
|
||||
\*-----------------------------------------------------*/
|
||||
std::vector<char> blob((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Close the file |
|
||||
\*-----------------------------------------------------*/
|
||||
file.close();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Open PawnIO driver |
|
||||
\*-----------------------------------------------------*/
|
||||
status = pawnio_open(phandle);
|
||||
|
||||
if(status)
|
||||
{
|
||||
if(status == E_ACCESSDENIED)
|
||||
{
|
||||
LOG_ERROR("Permission Denied, PawnIO initialization aborted");
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("Could not open PawnIO, PawnIO initialization aborted");
|
||||
}
|
||||
|
||||
return(status);
|
||||
}
|
||||
|
||||
handle = *phandle;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Load the file into PawnIO |
|
||||
\*-----------------------------------------------------*/
|
||||
status = pawnio_load(handle, reinterpret_cast<const UCHAR*>(blob.data()), blob.size());
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Check if the load was successful |
|
||||
\*-----------------------------------------------------*/
|
||||
if(status)
|
||||
{
|
||||
pawnio_close(handle);
|
||||
|
||||
if (status == HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED))
|
||||
{
|
||||
LOG_INFO("PawnIO module initialization aborted (unsupported)");
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("PawnIO module initialization aborted (code=%ld)", status);
|
||||
}
|
||||
|
||||
return(status);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Log a message and return OK if PawnIO successfully |
|
||||
| opened |
|
||||
\*-----------------------------------------------------*/
|
||||
LOG_INFO("PawnIO initialized successully");
|
||||
return(S_OK);
|
||||
}
|
||||
|
||||
bool i2c_smbus_pawnio_detect()
|
||||
{
|
||||
ULONG dll_version;
|
||||
if(pawnio_version(&dll_version))
|
||||
{
|
||||
LOG_INFO("PawnIO is not loaded, PawnIO I2C bus detection aborted");
|
||||
return(false);
|
||||
}
|
||||
|
||||
i2c_smbus_interface * bus;
|
||||
HANDLE pawnio_handle;
|
||||
bool bus_detected;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Set the detected flag to false, successfully |
|
||||
| any bus will result in a successful return status |
|
||||
\*-----------------------------------------------------*/
|
||||
bus_detected = false;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Try to load Intel (i801) SMBus driver |
|
||||
\*-----------------------------------------------------*/
|
||||
if(i2c_smbus_pawnio::start_pawnio("SmbusI801.bin", &pawnio_handle) == S_OK)
|
||||
{
|
||||
bus_detected = true;
|
||||
|
||||
bus = new i2c_smbus_pawnio(pawnio_handle, "i801");
|
||||
ResourceManager::get()->RegisterI2CBus(bus);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Try to load AMD (PIIX4) SMBus driver - primary bus |
|
||||
\*-----------------------------------------------------*/
|
||||
if(i2c_smbus_pawnio::start_pawnio("SmbusPIIX4.bin", &pawnio_handle) == S_OK)
|
||||
{
|
||||
bus_detected = true;
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Select port 0 |
|
||||
\*-------------------------------------------------*/
|
||||
piix4_port_sel(pawnio_handle, 0);
|
||||
|
||||
bus = new i2c_smbus_pawnio(pawnio_handle, "piix4");
|
||||
ResourceManager::get()->RegisterI2CBus(bus);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Try to load AMD (PIIX4 SMBus driver - secondary bus |
|
||||
\*-----------------------------------------------------*/
|
||||
if(i2c_smbus_pawnio::start_pawnio("SmbusPIIX4.bin", &pawnio_handle) == S_OK)
|
||||
{
|
||||
bus_detected = true;
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Select port 1 |
|
||||
\*-------------------------------------------------*/
|
||||
piix4_port_sel(pawnio_handle, 1);
|
||||
|
||||
bus = new i2c_smbus_pawnio(pawnio_handle, "piix4");
|
||||
ResourceManager::get()->RegisterI2CBus(bus);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Try to load Nuvoton (NCT6793) SMBus driver |
|
||||
\*-----------------------------------------------------*/
|
||||
if(i2c_smbus_pawnio::start_pawnio("SmbusNCT6793.bin", &pawnio_handle) == S_OK)
|
||||
{
|
||||
bus_detected = true;
|
||||
|
||||
bus = new i2c_smbus_pawnio(pawnio_handle, "NCT6793");
|
||||
ResourceManager::get()->RegisterI2CBus(bus);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Try to load Intel Skylake IMC SMBus driver |
|
||||
\*-----------------------------------------------------*/
|
||||
if(i2c_smbus_pawnio::start_pawnio("SmbusIntelSkylakeIMC.bin", &pawnio_handle) == S_OK)
|
||||
{
|
||||
bus_detected = true;
|
||||
|
||||
imc_index_sel(pawnio_handle, 0);
|
||||
|
||||
bus = new i2c_smbus_pawnio(pawnio_handle, "Intel Skylake IMC");
|
||||
ResourceManager::get()->RegisterI2CBus(bus);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Try to load Intel Skylake IMC SMBus driver |
|
||||
\*-----------------------------------------------------*/
|
||||
if(i2c_smbus_pawnio::start_pawnio("SmbusIntelSkylakeIMC.bin", &pawnio_handle) == S_OK)
|
||||
{
|
||||
bus_detected = true;
|
||||
|
||||
imc_index_sel(pawnio_handle, 1);
|
||||
|
||||
bus = new i2c_smbus_pawnio(pawnio_handle, "Intel Skylake IMC");
|
||||
ResourceManager::get()->RegisterI2CBus(bus);
|
||||
}
|
||||
|
||||
return(bus_detected);
|
||||
}
|
||||
|
||||
REGISTER_I2C_BUS_DETECTOR(i2c_smbus_pawnio_detect);
|
||||
@@ -0,0 +1,50 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| i2c_smbus_piix4_pawnio.h |
|
||||
| |
|
||||
| PawnIO PIIX4 SMBUS driver for Windows |
|
||||
| |
|
||||
| Stephen Horvath (Steve-Tech) 21 Apr 2025 |
|
||||
| Based on original OpenRGB PIIX4 source code |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "windows.h"
|
||||
|
||||
#include "i2c_smbus.h"
|
||||
|
||||
#define GLOBAL_SMBUS_MUTEX_NAME "Global\\Access_SMBUS.HTP.Method"
|
||||
|
||||
enum
|
||||
{
|
||||
PAWNIO_SLEEPMODE_ALWAYSBUSY = 0,
|
||||
PAWNIO_SLEEPMODE_SHORTBUSY = 1,
|
||||
PAWNIO_SLEEPMODE_ALWAYSSLEEP = 2,
|
||||
PAWNIO_SLEEPMODE_MAX
|
||||
};
|
||||
|
||||
class i2c_smbus_pawnio : public i2c_smbus_interface
|
||||
{
|
||||
public:
|
||||
static std::unordered_map<std::string, int> using_handle;
|
||||
i2c_smbus_pawnio(HANDLE handle, std::string name);
|
||||
~i2c_smbus_pawnio();
|
||||
|
||||
static HRESULT start_pawnio(std::string filename, PHANDLE phandle);
|
||||
|
||||
private:
|
||||
s32 pawnio_read(u8 addr, char read_write, u8 command, int size, i2c_smbus_data *data);
|
||||
s32 pawnio_write(u8 addr, char read_write, u8 command, int size, i2c_smbus_data *data);
|
||||
s32 i2c_smbus_xfer(u8 addr, char read_write, u8 command, int size, i2c_smbus_data* data);
|
||||
s32 i2c_xfer(u8 addr, char read_write, int* size, u8* data);
|
||||
|
||||
HANDLE global_smbus_access_handle;
|
||||
std::string name;
|
||||
HANDLE handle;
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| i2c_amd_gpu.h |
|
||||
| |
|
||||
| Bits specific to AMD GPUs to reliably detect |
|
||||
| the I2C bus that has RGB control |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include "i2c_smbus.h"
|
||||
|
||||
inline constexpr const char * RECOGNIZED_I2C_BUS_NAMES[] =
|
||||
{
|
||||
// Windows i2c bus
|
||||
"AMD ADL",
|
||||
// Linux i2c bus name since kernel 6.15
|
||||
"AMDGPU DM i2c OEM bus",
|
||||
// Linux i2c bus name since kernel 6.15 - older GPUs
|
||||
"AMDGPU i2c bit bus OEM 0x97",
|
||||
nullptr
|
||||
};
|
||||
|
||||
inline bool is_amd_gpu_i2c_bus(const i2c_smbus_interface *bus)
|
||||
{
|
||||
const char *name;
|
||||
size_t idx = 0;
|
||||
while((name = RECOGNIZED_I2C_BUS_NAMES[idx++]) != nullptr)
|
||||
{
|
||||
const char *pos = std::strstr(bus->device_name, name);
|
||||
if(pos == bus->device_name)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| i2c_smbus.cpp |
|
||||
| |
|
||||
| Device-independent i2c/SMBus communication functions |
|
||||
| |
|
||||
| Adam Honse (CalcProgrammer1) 08 Aug 2018 |
|
||||
| Portions based on Linux source code |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include "i2c_smbus.h"
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
i2c_smbus_interface::i2c_smbus_interface()
|
||||
{
|
||||
this->port_id = -1;
|
||||
this->pci_device = -1;
|
||||
this->pci_vendor = -1;
|
||||
this->pci_subsystem_device = -1;
|
||||
this->pci_subsystem_vendor = -1;
|
||||
this->bus_id = -1;
|
||||
}
|
||||
|
||||
i2c_smbus_interface::~i2c_smbus_interface()
|
||||
{
|
||||
}
|
||||
|
||||
s32 i2c_smbus_interface::i2c_smbus_write_quick(u8 addr, u8 value)
|
||||
{
|
||||
return i2c_smbus_xfer_call(addr, value, 0, I2C_SMBUS_QUICK, NULL);
|
||||
}
|
||||
|
||||
s32 i2c_smbus_interface::i2c_smbus_read_byte(u8 addr)
|
||||
{
|
||||
i2c_smbus_data data;
|
||||
if (i2c_smbus_xfer_call(addr, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &data))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return data.byte;
|
||||
}
|
||||
}
|
||||
|
||||
s32 i2c_smbus_interface::i2c_smbus_write_byte(u8 addr, u8 value)
|
||||
{
|
||||
return i2c_smbus_xfer_call(addr, I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL);
|
||||
}
|
||||
|
||||
s32 i2c_smbus_interface::i2c_smbus_read_byte_data(u8 addr, u8 command)
|
||||
{
|
||||
i2c_smbus_data data;
|
||||
if (i2c_smbus_xfer_call(addr, I2C_SMBUS_READ, command, I2C_SMBUS_BYTE_DATA, &data))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return data.byte;
|
||||
}
|
||||
}
|
||||
|
||||
s32 i2c_smbus_interface::i2c_smbus_write_byte_data(u8 addr, u8 command, u8 value)
|
||||
{
|
||||
i2c_smbus_data data;
|
||||
data.byte = value;
|
||||
return i2c_smbus_xfer_call(addr, I2C_SMBUS_WRITE, command, I2C_SMBUS_BYTE_DATA, &data);
|
||||
}
|
||||
|
||||
s32 i2c_smbus_interface::i2c_smbus_read_word_data(u8 addr, u8 command)
|
||||
{
|
||||
i2c_smbus_data data;
|
||||
if (i2c_smbus_xfer_call(addr, I2C_SMBUS_READ, command, I2C_SMBUS_WORD_DATA, &data))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return data.word;
|
||||
}
|
||||
}
|
||||
|
||||
s32 i2c_smbus_interface::i2c_smbus_write_word_data(u8 addr, u8 command, u16 value)
|
||||
{
|
||||
i2c_smbus_data data;
|
||||
data.word = value;
|
||||
return i2c_smbus_xfer_call(addr, I2C_SMBUS_WRITE, command, I2C_SMBUS_WORD_DATA, &data);
|
||||
}
|
||||
|
||||
s32 i2c_smbus_interface::i2c_smbus_read_block_data(u8 addr, u8 command, u8 *values)
|
||||
{
|
||||
i2c_smbus_data data;
|
||||
if (i2c_smbus_xfer_call(addr, I2C_SMBUS_READ, command, I2C_SMBUS_BLOCK_DATA, &data))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(values, &data.block[1], data.block[0]);
|
||||
return data.block[0];
|
||||
}
|
||||
}
|
||||
|
||||
s32 i2c_smbus_interface::i2c_smbus_write_block_data(u8 addr, u8 command, u8 length, const u8 *values)
|
||||
{
|
||||
i2c_smbus_data data;
|
||||
if (length > I2C_SMBUS_BLOCK_MAX)
|
||||
{
|
||||
length = I2C_SMBUS_BLOCK_MAX;
|
||||
}
|
||||
data.block[0] = length;
|
||||
memcpy(&data.block[1], values, length);
|
||||
return i2c_smbus_xfer_call(addr, I2C_SMBUS_WRITE, command, I2C_SMBUS_BLOCK_DATA, &data);
|
||||
}
|
||||
|
||||
s32 i2c_smbus_interface::i2c_smbus_read_i2c_block_data(u8 addr, u8 command, u8 length, u8 *values)
|
||||
{
|
||||
i2c_smbus_data data;
|
||||
if (length > I2C_SMBUS_BLOCK_MAX)
|
||||
{
|
||||
length = I2C_SMBUS_BLOCK_MAX;
|
||||
}
|
||||
data.block[0] = length;
|
||||
if (i2c_smbus_xfer_call(addr, I2C_SMBUS_READ, command, I2C_SMBUS_I2C_BLOCK_DATA, &data))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(values, &data.block[1], data.block[0]);
|
||||
return data.block[0];
|
||||
}
|
||||
}
|
||||
|
||||
s32 i2c_smbus_interface::i2c_smbus_write_i2c_block_data(u8 addr, u8 command, u8 length, const u8 *values)
|
||||
{
|
||||
i2c_smbus_data data;
|
||||
if (length > I2C_SMBUS_BLOCK_MAX)
|
||||
{
|
||||
length = I2C_SMBUS_BLOCK_MAX;
|
||||
}
|
||||
data.block[0] = length;
|
||||
memcpy(&data.block[1], values, length);
|
||||
return i2c_smbus_xfer_call(addr, I2C_SMBUS_WRITE, command, I2C_SMBUS_I2C_BLOCK_DATA, &data);
|
||||
}
|
||||
|
||||
s32 i2c_smbus_interface::i2c_smbus_xfer_call(u8 addr, char read_write, u8 command, int size, i2c_smbus_data* data)
|
||||
{
|
||||
i2c_smbus_xfer_mutex.lock();
|
||||
|
||||
s32 i2c_ret = i2c_smbus_xfer(addr, read_write, command, size, data);
|
||||
|
||||
i2c_smbus_xfer_mutex.unlock();
|
||||
|
||||
return(i2c_ret);
|
||||
}
|
||||
|
||||
s32 i2c_smbus_interface::i2c_xfer_call(u8 addr, char read_write, int* size, u8 *data)
|
||||
{
|
||||
i2c_smbus_xfer_mutex.lock();
|
||||
|
||||
s32 i2c_ret = i2c_xfer(addr, read_write, size, data);
|
||||
|
||||
i2c_smbus_xfer_mutex.unlock();
|
||||
|
||||
return(i2c_ret);
|
||||
}
|
||||
|
||||
s32 i2c_smbus_interface::i2c_read_block(u8 addr, int* size, u8* data)
|
||||
{
|
||||
return i2c_xfer_call(addr, I2C_SMBUS_READ, size, data);
|
||||
}
|
||||
|
||||
s32 i2c_smbus_interface::i2c_write_block(u8 addr, int size, u8 *data)
|
||||
{
|
||||
return i2c_xfer_call(addr, I2C_SMBUS_WRITE, &size, data);
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| i2c_smbus.h |
|
||||
| |
|
||||
| Device-independent i2c/SMBus communication functions |
|
||||
| |
|
||||
| Adam Honse (CalcProgrammer1) 08 Aug 2018 |
|
||||
| Portions based on Linux source code |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#ifndef I2C_SMBUS_H
|
||||
#define I2C_SMBUS_H
|
||||
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef int s32;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
//Data for SMBus Messages
|
||||
#define I2C_SMBUS_BLOCK_MAX 32
|
||||
|
||||
union i2c_smbus_data
|
||||
{
|
||||
u8 byte;
|
||||
u16 word;
|
||||
u8 block[I2C_SMBUS_BLOCK_MAX + 2];
|
||||
};
|
||||
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include <linux/i2c.h>
|
||||
|
||||
#endif /* __linux__ */
|
||||
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__)
|
||||
|
||||
//Data for SMBus Messages
|
||||
#define I2C_SMBUS_BLOCK_MAX 32
|
||||
|
||||
union i2c_smbus_data
|
||||
{
|
||||
u8 byte;
|
||||
u16 word;
|
||||
u8 block[I2C_SMBUS_BLOCK_MAX + 2];
|
||||
};
|
||||
|
||||
#endif /* __APPLE__ or __FreeBSD__ */
|
||||
|
||||
// i2c_smbus_xfer read or write markers
|
||||
#define I2C_SMBUS_READ 1
|
||||
#define I2C_SMBUS_WRITE 0
|
||||
|
||||
// SMBus transaction types (size parameter in the above functions)
|
||||
#define I2C_SMBUS_QUICK 0
|
||||
#define I2C_SMBUS_BYTE 1
|
||||
#define I2C_SMBUS_BYTE_DATA 2
|
||||
#define I2C_SMBUS_WORD_DATA 3
|
||||
#define I2C_SMBUS_PROC_CALL 4
|
||||
#define I2C_SMBUS_BLOCK_DATA 5
|
||||
#define I2C_SMBUS_I2C_BLOCK_BROKEN 6
|
||||
#define I2C_SMBUS_BLOCK_PROC_CALL 7 /* SMBus 2.0 */
|
||||
#define I2C_SMBUS_I2C_BLOCK_DATA 8
|
||||
|
||||
|
||||
class i2c_smbus_interface
|
||||
{
|
||||
public:
|
||||
char device_name[512];
|
||||
|
||||
int port_id;
|
||||
int pci_device;
|
||||
int pci_vendor;
|
||||
int pci_subsystem_device;
|
||||
int pci_subsystem_vendor;
|
||||
|
||||
int bus_id;
|
||||
|
||||
i2c_smbus_interface();
|
||||
virtual ~i2c_smbus_interface();
|
||||
|
||||
//Functions derived from i2c-core.c
|
||||
s32 i2c_smbus_write_quick(u8 addr, u8 value);
|
||||
s32 i2c_smbus_read_byte(u8 addr);
|
||||
s32 i2c_smbus_write_byte(u8 addr, u8 value);
|
||||
s32 i2c_smbus_read_byte_data(u8 addr, u8 command);
|
||||
s32 i2c_smbus_write_byte_data(u8 addr, u8 command, u8 value);
|
||||
s32 i2c_smbus_read_word_data(u8 addr, u8 command);
|
||||
s32 i2c_smbus_write_word_data(u8 addr, u8 command, u16 value);
|
||||
s32 i2c_smbus_read_block_data(u8 addr, u8 command, u8 *values);
|
||||
s32 i2c_smbus_write_block_data(u8 addr, u8 command, u8 length, const u8 *values);
|
||||
s32 i2c_smbus_read_i2c_block_data(u8 addr, u8 command, u8 length, u8 *values);
|
||||
s32 i2c_smbus_write_i2c_block_data(u8 addr, u8 command, u8 length, const u8 *values);
|
||||
|
||||
//Addtional functions added for pure I2C block operations
|
||||
s32 i2c_read_block(u8 addr, int* size, u8* data);
|
||||
s32 i2c_write_block(u8 addr, int size, u8* data);
|
||||
|
||||
//Handle SMBus and I2C transfer calls in a single thread
|
||||
s32 i2c_smbus_xfer_call(u8 addr, char read_write, u8 command, int size, i2c_smbus_data* data);
|
||||
s32 i2c_xfer_call(u8 addr, char read_write, int* size, u8 *data);
|
||||
|
||||
virtual s32 i2c_smbus_xfer(u8 addr, char read_write, u8 command, int size, i2c_smbus_data* data) = 0;
|
||||
virtual s32 i2c_xfer(u8 addr, char read_write, int* size, u8* data) = 0;
|
||||
|
||||
private:
|
||||
std::mutex i2c_smbus_xfer_mutex;
|
||||
|
||||
};
|
||||
|
||||
#endif /* I2C_SMBUS_H */
|
||||
Reference in New Issue
Block a user