mirror of
https://github.com/CalcProgrammer1/OpenRGB.git
synced 2026-02-14 17:21:26 -05:00
* Split detection system out into its own class, DetectionManager
* Clean up ResourceManger's many callbacks into just two, one for detection and one general purpose
289 lines
9.8 KiB
C++
289 lines
9.8 KiB
C++
/*---------------------------------------------------------*\
|
|
| 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 "DetectionManager.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;
|
|
DetectionManager::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");
|
|
DetectionManager::get()->RegisterI2CBus(bus);
|
|
|
|
return(true);
|
|
}
|
|
|
|
REGISTER_I2C_BUS_DETECTOR(i2c_smbus_piix4_detect);
|