VIA-CUDA: hackish support for SPD.

This commit is contained in:
Maxim Poliakovski 2019-10-07 03:18:18 +02:00
parent cf3f8b6db1
commit 01c38b7348
3 changed files with 119 additions and 10 deletions

View File

@ -3,6 +3,11 @@
#include "macio.h"
#include "viacuda.h"
/** Heathrow Mac I/O device emulation.
Author: Max Poliakovski 2019
*/
using namespace std;
HeathrowIC::HeathrowIC() : PCIDevice("mac-io/heathrow")
@ -113,6 +118,9 @@ uint32_t HeathrowIC::mio_ctrl_read(uint32_t offset, int size)
cout << "read from MIO:Int_Clear1 register" << endl;
res = this->int_clear1;
break;
case 0x34: /* heathrowIDs / HEATHROW_MBCR (Linux): media bay config reg? */
res = 0xF0700000UL;
break;
case 0x38:
cout << "read from MIO:Feat_Ctrl register" << endl;
res = this->feat_ctrl;

View File

@ -5,6 +5,10 @@
//if you want to distribute this.
//(divingkatae#1017 on Discord)
/** VIA-CUDA combo device emulation.
Author: Max Poliakovski 2019
*/
#include <iostream>
#include <cinttypes>
@ -187,15 +191,25 @@ void ViaCuda::cuda_write(uint8_t new_state)
}
}
void ViaCuda::cuda_null_response(uint32_t pkt_type, uint32_t pkt_flag, uint32_t cmd)
void ViaCuda::cuda_response_header(uint32_t pkt_type, uint32_t pkt_flag)
{
this->out_buf[0] = pkt_type;
this->out_buf[1] = pkt_flag;
this->out_buf[2] = cmd;
this->out_buf[2] = this->in_buf[1]; /* copy original cmd */
this->out_count = 3;
this->out_pos = 0;
}
void ViaCuda::cuda_error_response(uint32_t error)
{
this->out_buf[0] = CUDA_PKT_ERROR;
this->out_buf[1] = error;
this->out_buf[2] = this->in_buf[0];
this->out_buf[3] = this->in_buf[1]; /* copy original cmd */
this->out_count = 4;
this->out_pos = 0;
}
void ViaCuda::cuda_process_packet()
{
if (this->in_count < 2) {
@ -204,10 +218,10 @@ void ViaCuda::cuda_process_packet()
}
switch(this->in_buf[0]) {
case 0:
case CUDA_PKT_ADB:
cout << "Cuda: ADB packet received" << endl;
break;
case 1:
case CUDA_PKT_PSEUDO:
cout << "Cuda: pseudo command packet received" << endl;
cout << "Command: " << hex << (uint32_t)(this->in_buf[1]) << endl;
cout << "Data count: " << dec << this->in_count << endl;
@ -226,21 +240,87 @@ void ViaCuda::cuda_pseudo_command(int cmd, int data_count)
{
switch(cmd) {
case CUDA_READ_WRITE_I2C:
cuda_null_response(1, 0, cmd);
cuda_response_header(CUDA_PKT_PSEUDO, 0);
/* bit 0 of the I2C address byte indicates operation kind:
0 - write to device, 1 - read from device
In the case of reading, Cuda will append one-byte result
to the response packet header */
if (this->in_buf[2] & 1) {
this->out_buf[3] = 0xDD; /* send dummy byte for now */
this->out_count++;
i2c_simple_transaction(this->in_buf[2], &this->in_buf[3], this->in_count - 3);
break;
case CUDA_COMB_FMT_I2C:
/* HACK:
This command performs the so-called open-ended transaction, i.e.
Cuda will continue to send data as long as handshaking is completed
for each byte. To support that, we'd need another emulation approach.
Fortunately, HWInit is known to read/write max. 4 bytes at once
so we're going to use a prefilled buffer to make it work.
*/
cuda_response_header(CUDA_PKT_PSEUDO, 0);
if (this->in_count >= 5) {
i2c_comb_transaction(this->in_buf[2], this->in_buf[3], this->in_buf[4],
&this->in_buf[5], this->in_count - 5);
}
break;
case CUDA_OUT_PB0: /* undocumented call! */
cout << "Cuda: send " << dec << (int)(this->in_buf[2]) << " to PB0" << endl;
cuda_null_response(1, 0, cmd);
cuda_response_header(CUDA_PKT_PSEUDO, 0);
break;
default:
cout << "Cuda: unsupported pseudo command 0x" << hex << cmd << endl;
cuda_error_response(CUDA_ERR_BAD_CMD);
}
}
void ViaCuda::i2c_simple_transaction(uint8_t dev_addr, const uint8_t *in_buf,
int in_bytes)
{
int tr_type = dev_addr & 1;
switch(dev_addr & 0xFE) {
case 0x50: /* unknown device on the Gossamer board */
if (tr_type) { /* read */
/* send dummy byte for now */
this->out_buf[this->out_count++] = 0xDD;
} else {
/* ignore writes */
}
break;
default:
cout << "Unsupported I2C device 0x" << hex << (int)dev_addr << endl;
cuda_error_response(CUDA_ERR_I2C);
}
}
void ViaCuda::i2c_comb_transaction(uint8_t dev_addr, uint8_t sub_addr,
uint8_t dev_addr1, const uint8_t *in_buf, int in_bytes)
{
int tr_type = dev_addr1 & 1;
if ((dev_addr & 0xFE) != (dev_addr1 & 0xFE)) {
cout << "I2C combined, dev_addr mismatch!" << endl;
return;
}
switch(dev_addr1 & 0xFE) {
case 0xAE: /* SDRAM EEPROM, no clue which one */
if (tr_type) { /* read */
if (sub_addr != 2) {
cout << "Unsupported read position 0x" << hex << (int)sub_addr
<< " in SDRAM EEPROM 0x" << hex << (int)dev_addr1;
return;
}
/* FIXME: hardcoded SPD EEPROM values! This should be a proper
I2C device with user-configurable params */
this->out_buf[this->out_count++] = 0x04; /* memory type = SDRAM */
this->out_buf[this->out_count++] = 0x0B; /* row address bits per bank */
this->out_buf[this->out_count++] = 0x09; /* col address bits per bank */
this->out_buf[this->out_count++] = 0x02; /* num of RAM banks */
} else {
/* ignore writes */
}
break;
default:
cout << "Unsupported I2C device 0x" << hex << (int)dev_addr1 << endl;
cuda_error_response(CUDA_ERR_I2C);
}
}

View File

@ -58,6 +58,15 @@ enum {
CUDA_TREQ = 0x08 /* Cuda requests transaction from host */
};
/** Cuda packet types. */
enum {
CUDA_PKT_ADB = 0,
CUDA_PKT_PSEUDO = 1,
CUDA_PKT_ERROR = 2,
CUDA_PKT_TICK = 3,
CUDA_PKT_POWER = 4
};
/** Cuda pseudo commands. */
enum {
CUDA_READ_WRITE_I2C = 0x22, /* read/write I2C device */
@ -65,6 +74,12 @@ enum {
CUDA_OUT_PB0 = 0x26, /* output one bit to Cuda's PB0 line */
};
/** Cuda error codes. */
enum {
CUDA_ERR_BAD_CMD = 2, /* invalid pseudo command */
CUDA_ERR_I2C = 5 /* invalid I2C data or no acknowledge */
};
class ViaCuda
{
@ -94,9 +109,15 @@ private:
bool cuda_ready();
void assert_sr_int();
void cuda_write(uint8_t new_state);
void cuda_null_response(uint32_t pkt_type, uint32_t pkt_flag, uint32_t cmd);
void cuda_response_header(uint32_t pkt_type, uint32_t pkt_flag);
void cuda_error_response(uint32_t error);
void cuda_process_packet();
void cuda_pseudo_command(int cmd, int data_count);
/* I2C related methods */
void i2c_simple_transaction(uint8_t dev_addr, const uint8_t *in_buf, int in_bytes);
void i2c_comb_transaction(uint8_t dev_addr, uint8_t sub_addr, uint8_t dev_addr1,
const uint8_t *in_buf, int in_bytes);
};
#endif /* VIACUDA_H */