Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you piece together the following clues to help me read temperatures out of the Winbond W83793 chip?

I am trying to get OpenHardwareMonitor to read temperature data out of the Winbond W83793 chip on my Supermicro X7DWA motherboard. The problem is that I don't have any low-level programming experience, and the available docs online do not seem to be sufficient in explaining how to access the temperatures.

However, over the month that I've been working on this problem, I have discovered a few values and low-level methods that may be the key to solving my problem. I just need to figure out how to use them to get what I want. That's where I turn to you, because you might understand what this information means, and how to apply it, unlike me. I've already done my fair share of poking around, resulting in many blue screens and computer restarts. Enough guessing, I need to piece these clues together. Here is what I know so far:

  1. To read from the chip, I will somehow need to access the SMBus, because that is the way monitoring programs, such as CPUID HWMonitor, are getting the information. OpenHardwareMonitor, as far as I know, does not have any code in it to access the SMBus, which is why it may not be reading from the chip. However, OpenHardwareMonitor has the following methods included in its Ring0 class, which it uses to access information from other chips. I may be able to use these methods to my advantage:

    void Ring0.WriteIOPort(uint port, byte value);
    byte Ring0.ReadIOPort(uint port);
    
  2. Among other information, HWMonitor reports the following information about the Winbond W83793 chip to me when I save a report:

    Register Space: SMBus, base address = 0x01100

    SMBus request: channel 0x0, address 0x2F

    It looks like these are important values, but I don't know exactly what they mean, and how I can use them in conjunction with the Ring0 methods above. Hmm... so many clues. The other values HWMonitor shows me are the actual voltages, temperatures, and fan speeds, and an array of hexadecimal values that represents data from somewhere on the chip, which I will reproduce here if you want to look at it.

  3. Finally, in the W83793 data sheet, on page 53 (if you have the document open), here are the addresses in hex of the temperatures I would like to read (I believe):

    TD1 Readout - Bank 0 Address 1C

    TD2 Readout - Bank 0 Address 1D

    TD3 Readout - Bank 0 Address 1E

    TD4 Readout - Bank 0 Address 1F

    Low bit Readout - Bank 0 Address 22

    TR1 Readout - Bank 0 Address 20

    TR2 Readout - Bank 0 Address 21

That is all I know so far. The OpenHardwareMonitor, W83793 chip, and Ring0 code are available via the links provided above. As I said earlier, I've been at it for a month, and I just haven't been able to solve this mystery yet. I hope you can help me. All this information may seem a bit intimidating, but I'm sure it will make sense to someone with some low-level programming experience.

To summarize my question, use the clues provided above to figure out how to get OpenHardwareMonitor to read temperatures out of the W83793 chip. I don't need details on creating a chip in OpenHardwareMonitor. I already have a class ready. I just need the sequence and format to write Ring0 commands in, if that's what I need to do.

EDIT: I found some more information. I printed an SMBus device report from HWMonitor, and among other things, I got this line, included here because it says 0x2F:

SMB device: I/O = 0x1100, address 0x2F, channel = 0

This must mean I need to somehow combine the addresses of the I/O with the address of the chip, which seems to be 0x2F. I tried adding them together but then I get all temperature readings to be 255, so that wasn't the right guess.

like image 891
Dalal Avatar asked Aug 22 '11 17:08

Dalal


2 Answers

The IO methods are what you need. On x86 hardware there are actually two address pools, not one. One is meant for memory, is referenced by the chip when reading instructions and has thousands of useful and convenient access methods. The other is meant for addressing external chips and has a very limited and relatively slow set of read and write operations. The methods you've identified give you access to the second area.

As the registers you want to read are in bank 0 then first you need to select bank 0 on the chip, as per page 12. Per the diagram in section 8.1.2.1 you need to write 0x80 to address 00. Based on your report that the base address for the chip is 0x01100, that should mean writing 0x80 to 0x01100 via WriteIOPort.

It's then likely that you should be able to read the values you want via ReadIOPort from 0x01100+0x1c, 0x01100+0x1d, etc.

I haven't had time fully to digest the document you link to, but those are reasonable guesses. Some chips have a slightly more complicated procedure where you have to write a value then acknowledge the result, but I don't see anything like that in the documentation. You also need to be wary of multitasking — if your code is interrupted between setting bank 0 and reading relevant registers then some process in between may set some other bank, causing the values you read to be arbitrary other values. I assume OpenHardwareMonitor has some sort of mechanism to deal with that, but it's worth keeping in mind if you try a quick purely userspace implementation and occasionally get weird results.

like image 121
Tommy Avatar answered Sep 21 '22 14:09

Tommy


In the end, the author of OpenHardwareMonitor kindly helped me out, and now I'm able to read temperatures out of my chip. While the entire solution to this problem is a little more complex and is still beyond me, here is the basic reading and writing using the Ring0 class, for anyone interested. Note that this is specific to my machine and chip. For you, the base address and slave address may be different, but you can find them using CPUID HWMonitor, by printing a report.

First, here are the constants that were used:

private const int   BASE_ADDRESS    = 0x1100;
private const uint  SLAVE_ADDRESS   = 0X2F; // as we figured out already
private const byte  HOST_STAT_REG   = 0;    // host status register
private const byte  HOST_BUSY       = 1;    
private const byte  HOST_CTRL_REG   = 2;    // host control register
private const byte  HOST_CMD_REG    = 3;    // host command register
private const byte  T_SLAVE_ADR_REG = 4;    // transmit slave address register
private const byte  HOST_DATA_0_REG = 5;  
private const byte  BYTE_DATA_COMM  = 0x08; // byte data command
private const byte  START_COMM      = 0x40; // start command
private const byte  READ            = 1;
private const byte  WRITE           = 0;

Next, here is the basic code to read a particular byte from a register on the chip:

// first wait until ready
byte status;
do
{
  status = Ring0.ReadIoPort(BASE_ADDRESS + HOST_STAT_REG);
} while ((status & HOST_BUSY) > 0);
if ((status & 0x1E) != 0)
{
  Ring0.WriteIoPort(BASE_ADDRESS + HOST_STAT_REG, status);
}

// now get the value
Ring0.WriteIoPort(BASE_ADDRESS + HOST_DATA_0_REG, 0);
Ring0.WriteIoPort(BASE_ADDRESS + HOST_COMM_REG, theRegister)
Ring0.WriteIoPort(BASE_ADDRESS + T_SLAVE_ADR_REG, 
            (byte)((SLAVE_ADDRESS << 1) | READ));
Ring0.WriteIoPort(BASE_ADDRESS + HOST_CTRL_REG,
            START_COMM | BYTE_DATA_COMM);
Ring0.ReadIoPort(BASE_ADDRESS + HOST_DATA_0_REGISTER); // this returns the value

// now wait for it to end
while ((Ring0.ReadIoPort(BASE_ADDRESS + HOST_STAT_REG) & HOST_BUSY) > 0) {}

While I don't understand it that well, this could serve as a rough guide to someone with more low-level experience than me who is having a similar problem.

like image 39
Dalal Avatar answered Sep 24 '22 14:09

Dalal