SMBus
Retreived from http://www.xbox-linux.org/wiki/SMBus_Controller
by Michael Steil (original version: 15/26 June 2002)
All non-PC components of the Xbox console are connected through an I²C/SMbus interface. I²C/SMBus is a slow low-cost serial bus with each device having a unique 7-bit ID. (Wikipedia has informative articles about I²C and SMBus, you might want to check them out.)
There are only two operations of an SMBus controller: write and read. Writing means that an 8 bit command code and an 8 or 16 bit operand are sent to an SMBus device. Reading means that an 8 bit command code is sent to the device and an 8 or 16 bit answer is expected.
The controller for the SMBus interface in the Xbox is a PCI device with the DevID 01B4 and it is built into MPCX southbridge. It supports bus arbitration and the Xbox kernel will retry transactions if a collision occurs. This means it is safe to use a second SMBus master (like a USB-SMBus adapter) while the Xbox is running as long as the second master also supports arbitration. Adapters based on the Silicon Labs CP2112 or Microchip MCP2221 chips will work, for example.
SMBus Controller Port Layout
Port | Description |
---|---|
0xC000 | Status
|
0xC002 | Control
|
0xC004 | Address |
0xC006 | Data |
0xC008 | Command |
This is very similar (but not identical) to the AMD756/AMD766/AMD768 SMBus controllers. The lm_sensors project includes GPLed Linux kernel code for it since version 2.6.4 (kernel/busses/i2c-amd756.c).
SMBus Controller Programming
The following two routines illustrate how to read and write data from and to an SMBus device:
int SMBusWriteCommand(unsigned char slave, unsigned char command, int isWord, unsigned short data) { again: _outp(0xc004, (slave<<1)&0xfe); _outp(0xc008, command); _outpw(0xc006, data); _outpw(0xc000, _inpw(0xc000)); _outp(0xc002, (isWord) ? 0x0b : 0x0a); while ((_inp(0xc000) & 8)); /* wait while busy */ if (_inp(0xc000) & 0x02) goto again; /* retry transmission */ if (_inp(0xc000) & 0x34) return 0; /* fatal error */ return 1; }
int SMBusReadCommand(unsigned char slave, unsigned char command, int isWord, unsigned short *data) { again: _outp(0xc004, (slave<<1)|0x01); _outp(0xc008, command); _outpw(0xc000, _inpw(0xc000)); _outp(0xc002, (isWord) ? 0x0b : 0x0a); while ((_inp(0xc000) & 8)); /* wait while busy */ if (_inp(0xc000) & 0x02) goto again; /* retry transmission */ if (_inp(0xc000) & 0x34) return 0; /* fatal error */ *data = _inpw(0xc006); return 1; }
To avoid busy waiting of the CPU, the SMBus controller can also issue an interrupt when the operation is complete, by setting bit #4 in the control port when initiating the transfer.
Xbox SMBus Devices
The following four devices are connected to the Xbox SMBus:
Device | Hardware Address | Software Address |
PIC16LC | 0x10 | 0x20 |
Conexant CX25871 Video Encoder | 0x45 | 0x8a |
ADM1032 System Temperature Monitor | 0x4c | 0x98 |
Serial EEPROM | 0x54 | 0xa8 |
Do not confuse the hardware with the software addresses: The software ID is the hardware ID shifted by one bit left. The code above expects the hardware ID.
Actually, these addresses are not literally accurate; you find that the hardware address is 0x54 for the EEPROM: the actual address of the EEPROM on the i2c bus is 1010 (0xa), however, you will also notice that 0x54 is 1010100 in binary, and it seems that this 100 is also appended onto the other devices as well (although their software address is naturally read as say 10101000 - obviously because of the left shifting, however, it could be speculated that it would be possible to communicate with devices over the SMBus that are connected via i2c, so long as you knew their base address.