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 #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_ { typedef struct _spd_edo_ {
uint8_t bytes_used, spd_size, mem_type, uint8_t bytes_used, spd_size, mem_type,
row_bits, col_bits, banks, row_bits, col_bits, banks,
@@ -72,7 +82,7 @@ typedef struct _spd_sdram_ {
signal_level, tclk, tac, signal_level, tclk, tac,
config, refresh_rate, config, refresh_rate,
sdram_width, ecc_width, sdram_width, ecc_width,
tccd, burst, banks, cas, cs, we, tccd, burst, banks, cas, cslat, we,
mod_attr, dev_attr, mod_attr, dev_attr,
tclk2, tac2, tclk3, tac3, tclk2, tac2, tclk3, tac3,
trp, trrd, trcd, tras, trp, trrd, trcd, tras,
@@ -90,6 +100,9 @@ typedef struct _spd_sdram_ {
} spd_sdram_t; } 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); 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(&keyboard_ps2_pci_device);
device_add(&um8669f_device); /*ITE 8671*/ device_add(&um8669f_device); /*ITE 8671*/
device_add(&sst_flash_39sf020_device); device_add(&sst_flash_39sf020_device);
spd_register(SPD_TYPE_SDRAM, 0xF, 256); spd_register(SPD_TYPE_SDRAM, 0x7, 256);
return ret; return ret;
} }

134
src/spd.c
View File

@@ -28,15 +28,10 @@
#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_ { spd_t *spd_devices[SPD_MAX_SLOTS];
uint8_t slot;
uint8_t addr_register;
} spd_t;
device_t *spd_devices[SPD_MAX_SLOTS];
uint8_t spd_data[SPD_MAX_SLOTS][SPD_DATA_SIZE]; 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 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); static void spd_write_byte(uint8_t addr, uint8_t val, void *priv);
#define ENABLE_SPD_LOG 1
#ifdef ENABLE_SPD_LOG #ifdef ENABLE_SPD_LOG
int spd_do_log = 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 void
spd_write_byte(uint8_t addr, uint8_t val, void *priv) 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); spd_log("SPD: closing slot %d (SMBus %02Xh)\n", dev->slot, SPD_BASE_ADDR + dev->slot);
smbus_removehandler(SPD_BASE_ADDR + dev->slot, 1, 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, spd_write_byte, NULL, NULL, NULL,
dev); dev);
@@ -110,15 +123,12 @@ spd_close(void *priv)
static void * static void *
spd_init(const device_t *info) spd_init(const device_t *info)
{ {
spd_t *dev = (spd_t *) malloc(sizeof(spd_t)); spd_t *dev = spd_devices[info->local];
memset(dev, 0, sizeof(spd_t));
dev->slot = info->local;
spd_log("SPD: initializing slot %d (SMBus %02Xh)\n", dev->slot, SPD_BASE_ADDR + dev->slot); spd_log("SPD: initializing slot %d (SMBus %02Xh)\n", dev->slot, SPD_BASE_ADDR + dev->slot);
smbus_sethandler(SPD_BASE_ADDR + dev->slot, 1, smbus_sethandler(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, spd_write_byte, NULL, NULL, NULL,
dev); dev);
@@ -139,8 +149,8 @@ log2_ui16(uint16_t i)
int int
comp_ui16_rev(const void *elem1, const void *elem2) comp_ui16_rev(const void *elem1, const void *elem2)
{ {
uint16_t a = *((uint16_t *)elem1); uint16_t a = *((uint16_t *) elem1);
uint16_t b = *((uint16_t *)elem2); uint16_t b = *((uint16_t *) elem2);
return ((a > b) ? -1 : ((a < b) ? 1 : 0)); 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) 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; 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_edo_t *edo_data;
spd_sdram_t *sdram_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? */ /* 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); 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... */ /* populate empty vslots by splitting modules... */
split = (total_size == 0); /* ...if possible */ 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 */ /* look for a module to split */
split = 0; split = 0;
for (vslot = 0; vslot < slot_count; vslot++) { for (vslot = 0; vslot < slot_count; vslot++) {
if (vslots[vslot] < (min_module_size << 1)) if ((vslots[vslot] < (min_module_size << 1)) || (vslots[vslot] != (1 << log2_ui16(vslots[vslot]))))
continue; /* no module here or module is too small to be split */ continue; /* no module here, module is too small to be split, or asymmetric module */
/* find next empty vslot */ /* find next empty vslot */
next_empty_vslot = 0; 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))) if (!(slot_mask & (1 << slot)))
continue; /* slot disabled */ 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)); spd_devices[slot] = (spd_t *) malloc(sizeof(spd_t));
memset(spd_devices[slot], 0, sizeof(device_t)); memset(spd_devices[slot], 0, sizeof(spd_t));
spd_devices[slot]->name = "Serial Presence Detect ROM"; spd_devices[slot]->info = info;
spd_devices[slot]->local = slot; spd_devices[slot]->slot = slot;
spd_devices[slot]->init = spd_init; spd_devices[slot]->size = vslots[vslot];
spd_devices[slot]->close = spd_close;
/* 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) { switch (ram_type) {
case SPD_TYPE_FPM: 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]; edo_data = (spd_edo_t *) &spd_data[slot];
memset(edo_data, 0, sizeof(spd_edo_t)); memset(edo_data, 0, sizeof(spd_edo_t));
/* FIXME: very little information about EDO SPD is available, /* EDO SPD is specified by JEDEC and present in some modules, but
let alone software to interpret it correctly. */ 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->bytes_used = 0x80;
edo_data->spd_size = 0x08; edo_data->spd_size = 0x08;
edo_data->mem_type = ram_type; 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; 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->banks = 2;
edo_data->data_width_lsb = 64; edo_data->data_width_lsb = 64;
edo_data->signal_level = SPD_SIGNAL_LVTTL; 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->dram_width = 8;
edo_data->spd_rev = 0x12; 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++) 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_year = 20;
edo_data->mfg_week = 17; 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->bytes_used = 0x80;
sdram_data->spd_size = 0x08; sdram_data->spd_size = 0x08;
sdram_data->mem_type = ram_type; 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; 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->rows = 2;
sdram_data->data_width_lsb = 64; sdram_data->data_width_lsb = 64;
sdram_data->signal_level = SPD_SIGNAL_LVTTL; 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->tccd = 1;
sdram_data->burst = SPD_SDR_BURST_PAGE | 1 | 2 | 4 | 8; sdram_data->burst = SPD_SDR_BURST_PAGE | 1 | 2 | 4 | 8;
sdram_data->banks = 4; 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->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->tclk2 = 0xA0; /* 10 ns = 100 MHz */
sdram_data->tclk3 = 0xF0; /* 15 ns = 66.7 MHz */ sdram_data->tclk3 = 0xF0; /* 15 ns = 66.7 MHz */
sdram_data->tac2 = sdram_data->tac3 = 0x10; sdram_data->tac2 = sdram_data->tac3 = 0x10;
sdram_data->trp = sdram_data->trrd = sdram_data->trcd = sdram_data->tras = 1; 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_setup = sdram_data->data_setup = 0x15;
sdram_data->ca_hold = sdram_data->data_hold = 0x08; sdram_data->ca_hold = sdram_data->data_hold = 0x08;
sdram_data->spd_rev = 0x12; 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++) 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_year = 20;
sdram_data->mfg_week = 13; 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; break;
} }
device_add(spd_devices[slot]); device_add(info);
vslot++; vslot++;
} }
} }