SPD improvements

* Word and block read support for the SMBus ROM
* Asymmetric module support to cover more RAM configurations
* Add 86Box version as revision code
* Fix supported CAS latencies
* Fixed slot count on the GA-6BXC
This commit is contained in:
RichardG867
2020-05-16 20:32:28 -03:00
parent 6743bc3816
commit cff43c07c3
3 changed files with 115 additions and 44 deletions

View File

@@ -47,6 +47,16 @@
#define SPD_SDR_ATTR_VCC_HI_5 0x20
typedef struct _spd_ {
const device_t *info;
uint8_t slot;
uint16_t size;
uint16_t row1;
uint16_t row2;
uint8_t addr_register;
} spd_t;
typedef struct _spd_edo_ {
uint8_t bytes_used, spd_size, mem_type,
row_bits, col_bits, banks,
@@ -72,7 +82,7 @@ typedef struct _spd_sdram_ {
signal_level, tclk, tac,
config, refresh_rate,
sdram_width, ecc_width,
tccd, burst, banks, cas, cs, we,
tccd, burst, banks, cas, cslat, we,
mod_attr, dev_attr,
tclk2, tac2, tclk3, tac3,
trp, trrd, trcd, tras,
@@ -90,6 +100,9 @@ typedef struct _spd_sdram_ {
} spd_sdram_t;
extern spd_t *spd_devices[SPD_MAX_SLOTS];
extern void spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size);

View File

@@ -114,7 +114,7 @@ machine_at_6bxc_init(const machine_t *model)
device_add(&keyboard_ps2_pci_device);
device_add(&um8669f_device); /*ITE 8671*/
device_add(&sst_flash_39sf020_device);
spd_register(SPD_TYPE_SDRAM, 0xF, 256);
spd_register(SPD_TYPE_SDRAM, 0x7, 256);
return ret;
}

142
src/spd.c
View File

@@ -27,16 +27,11 @@
#include <86box/spd.h>
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define SPD_ROLLUP(x) ((x) >= 16 ? ((x) - 15) : (x))
typedef struct _spd_ {
uint8_t slot;
uint8_t addr_register;
} spd_t;
device_t *spd_devices[SPD_MAX_SLOTS];
spd_t *spd_devices[SPD_MAX_SLOTS];
uint8_t spd_data[SPD_MAX_SLOTS][SPD_DATA_SIZE];
@@ -44,7 +39,7 @@ static uint8_t spd_read_byte(uint8_t addr, void *priv);
static uint8_t spd_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv);
static void spd_write_byte(uint8_t addr, uint8_t val, void *priv);
#define ENABLE_SPD_LOG 1
#ifdef ENABLE_SPD_LOG
int spd_do_log = ENABLE_SPD_LOG;
@@ -83,6 +78,24 @@ spd_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv)
}
uint16_t
spd_read_word_cmd(uint8_t addr, uint8_t cmd, void *priv)
{
return (spd_read_byte_cmd(addr, cmd + 1, priv) << 8) | spd_read_byte_cmd(addr, cmd, priv);
}
uint8_t
spd_read_block_cmd(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv)
{
uint8_t read = 0;
for (uint8_t i = cmd; i < len && i < SPD_DATA_SIZE; i++) {
data[read++] = spd_read_byte_cmd(addr, i, priv);
}
return read;
}
void
spd_write_byte(uint8_t addr, uint8_t val, void *priv)
{
@@ -99,7 +112,7 @@ spd_close(void *priv)
spd_log("SPD: closing slot %d (SMBus %02Xh)\n", dev->slot, SPD_BASE_ADDR + dev->slot);
smbus_removehandler(SPD_BASE_ADDR + dev->slot, 1,
spd_read_byte, spd_read_byte_cmd, NULL, NULL,
spd_read_byte, spd_read_byte_cmd, spd_read_word_cmd, spd_read_block_cmd,
spd_write_byte, NULL, NULL, NULL,
dev);
@@ -110,17 +123,14 @@ spd_close(void *priv)
static void *
spd_init(const device_t *info)
{
spd_t *dev = (spd_t *) malloc(sizeof(spd_t));
memset(dev, 0, sizeof(spd_t));
dev->slot = info->local;
spd_t *dev = spd_devices[info->local];
spd_log("SPD: initializing slot %d (SMBus %02Xh)\n", dev->slot, SPD_BASE_ADDR + dev->slot);
smbus_sethandler(SPD_BASE_ADDR + dev->slot, 1,
spd_read_byte, spd_read_byte_cmd, NULL, NULL,
spd_write_byte, NULL, NULL, NULL,
dev);
spd_read_byte, spd_read_byte_cmd, spd_read_word_cmd, spd_read_block_cmd,
spd_write_byte, NULL, NULL, NULL,
dev);
return dev;
}
@@ -139,8 +149,8 @@ log2_ui16(uint16_t i)
int
comp_ui16_rev(const void *elem1, const void *elem2)
{
uint16_t a = *((uint16_t *)elem1);
uint16_t b = *((uint16_t *)elem2);
uint16_t a = *((uint16_t *) elem1);
uint16_t b = *((uint16_t *) elem2);
return ((a > b) ? -1 : ((a < b) ? 1 : 0));
}
@@ -149,7 +159,8 @@ void
spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size)
{
uint8_t slot, slot_count, vslot, next_empty_vslot, i, split;
uint16_t min_module_size, total_size, vslots[SPD_MAX_SLOTS];
uint16_t min_module_size, total_size, vslots[SPD_MAX_SLOTS], asym;
device_t *info;
spd_edo_t *edo_data;
spd_sdram_t *sdram_data;
@@ -192,8 +203,21 @@ spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size)
}
}
if (total_size > 0) /* did we populate everything? */
spd_log("SPD: not enough RAM slots (%d) to cover memory (%d MB short)\n", slot_count, total_size);
/* did we populate all the RAM? */
if (total_size) {
/* work backwards to add the missing RAM as asymmetric modules */
vslot = slot_count - 1;
do {
asym = (1 << log2_ui16(MIN(total_size, vslots[vslot])));
if (vslots[vslot] + asym <= max_module_size) {
vslots[vslot] += asym;
total_size -= asym;
}
} while (vslot-- > 0 && total_size);
if (total_size) /* still not enough */
spd_log("SPD: not enough RAM slots (%d) to cover memory (%d MB short)\n", slot_count, total_size);
}
/* populate empty vslots by splitting modules... */
split = (total_size == 0); /* ...if possible */
@@ -201,8 +225,8 @@ spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size)
/* look for a module to split */
split = 0;
for (vslot = 0; vslot < slot_count; vslot++) {
if (vslots[vslot] < (min_module_size << 1))
continue; /* no module here or module is too small to be split */
if ((vslots[vslot] < (min_module_size << 1)) || (vslots[vslot] != (1 << log2_ui16(vslots[vslot]))))
continue; /* no module here, module is too small to be split, or asymmetric module */
/* find next empty vslot */
next_empty_vslot = 0;
@@ -230,14 +254,28 @@ spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size)
if (!(slot_mask & (1 << slot)))
continue; /* slot disabled */
spd_log("SPD: registering slot %d = vslot %d = %d MB\n", slot, vslot, vslots[vslot]);
info = (device_t *) malloc(sizeof(device_t));
memset(info, 0, sizeof(device_t));
info->name = "Serial Presence Detect ROM";
info->local = slot;
info->init = spd_init;
info->close = spd_close;
spd_devices[slot] = (device_t *) malloc(sizeof(device_t));
memset(spd_devices[slot], 0, sizeof(device_t));
spd_devices[slot]->name = "Serial Presence Detect ROM";
spd_devices[slot]->local = slot;
spd_devices[slot]->init = spd_init;
spd_devices[slot]->close = spd_close;
spd_devices[slot] = (spd_t *) malloc(sizeof(spd_t));
memset(spd_devices[slot], 0, sizeof(spd_t));
spd_devices[slot]->info = info;
spd_devices[slot]->slot = slot;
spd_devices[slot]->size = vslots[vslot];
/* determine the second row size, from which the first row size can be obtained */
asym = (vslots[vslot] - (1 << log2_ui16(vslots[vslot]))); /* separate the powers of 2 */
if (!asym) /* is the module asymmetric? */
asym = (vslots[vslot] >> 1); /* symmetric, therefore divide by 2 */
spd_devices[slot]->row1 = (vslots[vslot] - asym);
spd_devices[slot]->row2 = asym;
spd_log("SPD: registering slot %d = vslot %d = %d MB (%d/%d)\n", slot, vslot, vslots[vslot], spd_devices[slot]->row1, spd_devices[slot]->row2);
switch (ram_type) {
case SPD_TYPE_FPM:
@@ -245,13 +283,18 @@ spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size)
edo_data = (spd_edo_t *) &spd_data[slot];
memset(edo_data, 0, sizeof(spd_edo_t));
/* FIXME: very little information about EDO SPD is available,
let alone software to interpret it correctly. */
/* EDO SPD is specified by JEDEC and present in some modules, but
most utilities cannot interpret it correctly. SIV32 at least gets
the module capacities right, so it was used as a reference here. */
edo_data->bytes_used = 0x80;
edo_data->spd_size = 0x08;
edo_data->mem_type = ram_type;
edo_data->row_bits = 6 + log2_ui16(vslots[vslot]);
edo_data->row_bits = SPD_ROLLUP(7 + log2_ui16(spd_devices[slot]->row1)); /* first row */
edo_data->col_bits = 9;
if (spd_devices[slot]->row1 != spd_devices[slot]->row2) { /* the upper 4 bits of row_bits/col_bits should be 0 on a symmetric module */
edo_data->row_bits |= (SPD_ROLLUP(7 + log2_ui16(spd_devices[slot]->row2)) << 4); /* second row, if different from first */
edo_data->col_bits |= (9 << 4); /* same as first row, but just in case */
}
edo_data->banks = 2;
edo_data->data_width_lsb = 64;
edo_data->signal_level = SPD_SIGNAL_LVTTL;
@@ -261,9 +304,11 @@ spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size)
edo_data->dram_width = 8;
edo_data->spd_rev = 0x12;
sprintf(edo_data->part_no, "86Box-%s-%03dM", (ram_type == SPD_TYPE_FPM) ? "FPM" : "EDO", vslots[vslot]);
sprintf(edo_data->part_no, EMU_NAME "-%s-%03dM", (ram_type == SPD_TYPE_FPM) ? "FPM" : "EDO", vslots[vslot]);
for (i = strlen(edo_data->part_no); i < sizeof(edo_data->part_no); i++)
edo_data->part_no[i] = ' ';
edo_data->part_no[i] = ' '; /* part number should be space-padded */
edo_data->rev_code[0] = EMU_VERSION_MAJ;
edo_data->rev_code[1] = (((EMU_VERSION_MIN / 10) << 4) | (EMU_VERSION_MIN % 10));
edo_data->mfg_year = 20;
edo_data->mfg_week = 17;
@@ -280,8 +325,12 @@ spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size)
sdram_data->bytes_used = 0x80;
sdram_data->spd_size = 0x08;
sdram_data->mem_type = ram_type;
sdram_data->row_bits = 5 + log2_ui16(vslots[vslot]);
sdram_data->row_bits = SPD_ROLLUP(6 + log2_ui16(spd_devices[slot]->row1)); /* first row */
sdram_data->col_bits = 9;
if (spd_devices[slot]->row1 != spd_devices[slot]->row2) { /* the upper 4 bits of row_bits/col_bits should be 0 on a symmetric module */
sdram_data->row_bits |= (SPD_ROLLUP(6 + log2_ui16(spd_devices[slot]->row2)) << 4); /* second row, if different from first */
sdram_data->col_bits |= (9 << 4); /* same as first row, but just in case */
}
sdram_data->rows = 2;
sdram_data->data_width_lsb = 64;
sdram_data->signal_level = SPD_SIGNAL_LVTTL;
@@ -292,20 +341,29 @@ spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size)
sdram_data->tccd = 1;
sdram_data->burst = SPD_SDR_BURST_PAGE | 1 | 2 | 4 | 8;
sdram_data->banks = 4;
sdram_data->cas = sdram_data->cs = sdram_data->we = 0x7F;
sdram_data->cas = 0x1c; /* CAS 5/4/3 supported */
sdram_data->cslat = sdram_data->we = 0x7f;
sdram_data->dev_attr = SPD_SDR_ATTR_EARLY_RAS | SPD_SDR_ATTR_AUTO_PC | SPD_SDR_ATTR_PC_ALL | SPD_SDR_ATTR_W1R_BURST;
sdram_data->tclk2 = 0xA0; /* 10 ns = 100 MHz */
sdram_data->tclk3 = 0xF0; /* 15 ns = 66.7 MHz */
sdram_data->tac2 = sdram_data->tac3 = 0x10;
sdram_data->trp = sdram_data->trrd = sdram_data->trcd = sdram_data->tras = 1;
sdram_data->bank_density = 1 << (log2_ui16(vslots[vslot] >> 1) - 2);
if (spd_devices[slot]->row1 != spd_devices[slot]->row2) {
/* Utilities interpret bank_density a bit differently on asymmetric modules. */
sdram_data->bank_density = (1 << (log2_ui16(spd_devices[slot]->row1 >> 1) - 2)); /* first row */
sdram_data->bank_density |= (1 << (log2_ui16(spd_devices[slot]->row2 >> 1) - 2)); /* second row */
} else {
sdram_data->bank_density = (1 << (log2_ui16(spd_devices[slot]->row1 >> 1) - 1)); /* symmetric module = only one bit is set */
}
sdram_data->ca_setup = sdram_data->data_setup = 0x15;
sdram_data->ca_hold = sdram_data->data_hold = 0x08;
sdram_data->spd_rev = 0x12;
sprintf(sdram_data->part_no, "86Box-SDR-%03dM", vslots[vslot]);
sprintf(sdram_data->part_no, EMU_NAME "-SDR-%03dM", vslots[vslot]);
for (i = strlen(sdram_data->part_no); i < sizeof(sdram_data->part_no); i++)
sdram_data->part_no[i] = ' ';
sdram_data->part_no[i] = ' '; /* part number should be space-padded */
sdram_data->rev_code[0] = EMU_VERSION_MAJ;
sdram_data->rev_code[1] = (((EMU_VERSION_MIN / 10) << 4) | (EMU_VERSION_MIN % 10));
sdram_data->mfg_year = 20;
sdram_data->mfg_week = 13;
@@ -319,7 +377,7 @@ spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size)
break;
}
device_add(spd_devices[slot]);
device_add(info);
vslot++;
}
}