I2C overhaul part 5: late, but there's still stuff to do

This commit is contained in:
RichardG867
2020-11-23 14:49:49 -03:00
parent 1e80ac1d15
commit d6b1d2c63a
11 changed files with 227 additions and 145 deletions

View File

@@ -33,8 +33,9 @@
int spd_present = 0;
spd_t *spd_devices[SPD_MAX_SLOTS];
uint8_t spd_data[SPD_MAX_SLOTS][SPD_DATA_SIZE];
spd_t *spd_modules[SPD_MAX_SLOTS];
static const device_t spd_device;
#ifdef ENABLE_SPD_LOG
@@ -60,30 +61,30 @@ spd_log(const char *fmt, ...)
static void
spd_close(void *priv)
{
spd_t *dev = (spd_t *) priv;
spd_log("SPD: close()\n");
spd_log("SPD: closing slot %d (SMBus %02X)\n", dev->slot, SPD_BASE_ADDR + dev->slot);
i2c_eeprom_close(dev->eeprom);
for (uint8_t i = 0; i < SPD_MAX_SLOTS; i++) {
if (spd_modules[i])
i2c_eeprom_close(spd_modules[i]->eeprom);
}
spd_present = 0;
free(dev);
}
static void *
spd_init(const device_t *info)
{
spd_t *dev = spd_devices[info->local];
spd_log("SPD: init()\n");
spd_log("SPD: initializing slot %d (SMBus %02X)\n", dev->slot, SPD_BASE_ADDR + dev->slot);
dev->eeprom = i2c_eeprom_init(i2c_smbus, SPD_BASE_ADDR + dev->slot, spd_data[info->local], SPD_DATA_SIZE, 0);
for (uint8_t i = 0; i < SPD_MAX_SLOTS; i++) {
if (spd_modules[i])
spd_modules[i]->eeprom = i2c_eeprom_init(i2c_smbus, SPD_BASE_ADDR + i, spd_modules[i]->data, sizeof(spd_modules[i]->data), 0);
}
spd_present = 1;
return dev;
return &spd_modules;
}
@@ -107,71 +108,71 @@ comp_ui16_rev(const void *elem1, const void *elem2)
void
spd_populate(uint16_t *vslots, uint8_t slot_count, uint16_t total_size, uint16_t min_module_size, uint16_t max_module_size, uint8_t enable_asym)
spd_populate(uint16_t *rows, uint8_t slot_count, uint16_t total_size, uint16_t min_module_size, uint16_t max_module_size, uint8_t enable_asym)
{
uint8_t vslot, next_empty_vslot, split, i;
uint8_t row, next_empty_row, split, i;
uint16_t asym;
/* populate vslots with modules in power-of-2 capacities */
memset(vslots, 0x00, SPD_MAX_SLOTS << 1);
for (vslot = 0; vslot < slot_count && total_size; vslot++) {
/* Populate rows with modules in power-of-2 capacities. */
memset(rows, 0, SPD_MAX_SLOTS << 1);
for (row = 0; row < slot_count && total_size; row++) {
/* populate slot */
vslots[vslot] = 1 << log2_ui16(MIN(total_size, max_module_size));
if (total_size >= vslots[vslot]) {
spd_log("SPD: initial vslot %d = %d MB\n", vslot, vslots[vslot]);
total_size -= vslots[vslot];
rows[row] = 1 << log2_ui16(MIN(total_size, max_module_size));
if (total_size >= rows[row]) {
spd_log("SPD: Initial row %d = %d MB\n", row, rows[row]);
total_size -= rows[row];
} else {
vslots[vslot] = 0;
rows[row] = 0;
break;
}
}
/* did we populate all the RAM? */
/* Did we populate all the RAM? */
if (total_size) {
/* work backwards to add the missing RAM as asymmetric modules if possible */
/* Work backwards to add the missing RAM as asymmetric modules if possible. */
if (enable_asym) {
vslot = slot_count - 1;
row = slot_count - 1;
do {
asym = (1 << log2_ui16(MIN(total_size, vslots[vslot])));
if (vslots[vslot] + asym <= max_module_size) {
vslots[vslot] += asym;
asym = (1 << log2_ui16(MIN(total_size, rows[row])));
if (rows[row] + asym <= max_module_size) {
rows[row] += asym;
total_size -= asym;
}
} while ((vslot-- > 0) && total_size);
} while ((row-- > 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... */
split = (total_size == 0); /* ...if possible */
/* Populate empty rows by splitting modules... */
split = (total_size == 0); /* ...if possible. */
while (split) {
/* look for a module to split */
/* Look for a module to split. */
split = 0;
for (vslot = 0; vslot < slot_count; vslot++) {
if ((vslots[vslot] < (min_module_size << 1)) || (vslots[vslot] != (1 << log2_ui16(vslots[vslot]))))
for (row = 0; row < slot_count; row++) {
if ((rows[row] < (min_module_size << 1)) || (rows[row] != (1 << log2_ui16(rows[row]))))
continue; /* no module here, module is too small to be split, or asymmetric module */
/* find next empty vslot */
next_empty_vslot = 0;
for (i = vslot + 1; i < slot_count && !next_empty_vslot; i++) {
if (!vslots[i])
next_empty_vslot = i;
/* Find next empty row. */
next_empty_row = 0;
for (i = row + 1; i < slot_count && !next_empty_row; i++) {
if (!rows[i])
next_empty_row = i;
}
if (!next_empty_vslot)
break; /* no empty vslots left */
if (!next_empty_row)
break; /* no empty rows left */
/* split the module into its own vslot and the next empty vslot */
spd_log("SPD: splitting vslot %d (%d MB) into %d and %d (%d MB each)\n", vslot, vslots[vslot], vslot, next_empty_vslot, vslots[vslot] >> 1);
vslots[vslot] = vslots[next_empty_vslot] = vslots[vslot] >> 1;
/* Split the module into its own row and the next empty row. */
spd_log("SPD: splitting row %d (%d MB) into %d and %d (%d MB each)\n", row, rows[row], row, next_empty_row, rows[row] >> 1);
rows[row] = rows[next_empty_row] = rows[row] >> 1;
split = 1;
break;
}
/* sort vslots by descending capacity if any were split */
/* Sort rows by descending capacity if any were split. */
if (split)
qsort(vslots, slot_count, sizeof(uint16_t), comp_ui16_rev);
qsort(rows, slot_count, sizeof(uint16_t), comp_ui16_rev);
}
}
@@ -179,13 +180,12 @@ spd_populate(uint16_t *vslots, uint8_t slot_count, uint16_t total_size, uint16_t
void
spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size)
{
uint8_t slot, slot_count, vslot, i;
uint16_t min_module_size, vslots[SPD_MAX_SLOTS], asym;
device_t *info;
uint8_t slot, slot_count, row, i;
uint16_t min_module_size, rows[SPD_MAX_SLOTS], asym;
spd_edo_t *edo_data;
spd_sdram_t *sdram_data;
/* determine the minimum module size for this RAM type */
/* Determine the minimum module size for this RAM type. */
switch (ram_type) {
case SPD_TYPE_FPM:
case SPD_TYPE_EDO:
@@ -201,51 +201,42 @@ spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size)
return;
}
/* count how many (real) slots are enabled */
/* Count how many slots are enabled. */
slot_count = 0;
for (slot = 0; slot < SPD_MAX_SLOTS; slot++) {
vslots[slot] = 0;
if (slot_mask & (1 << slot)) {
rows[slot] = 0;
if (slot_mask & (1 << slot))
slot_count++;
}
}
/* populate vslots */
spd_populate(vslots, slot_count, (mem_size >> 10), min_module_size, max_module_size, 1);
/* Populate rows. */
spd_populate(rows, slot_count, (mem_size >> 10), min_module_size, max_module_size, 1);
/* register SPD devices and populate their data according to the vslots */
vslot = 0;
for (slot = 0; slot < SPD_MAX_SLOTS && vslots[vslot]; slot++) {
/* Register SPD devices and populate their data according to the rows. */
row = 0;
for (slot = 0; slot < SPD_MAX_SLOTS && rows[row]; slot++) {
if (!(slot_mask & (1 << slot)))
continue; /* slot disabled */
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_modules[slot] = (spd_t *) malloc(sizeof(spd_t));
memset(spd_modules[slot], 0, sizeof(spd_t));
spd_modules[slot]->slot = slot;
spd_modules[slot]->size = rows[row];
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 */
/* Determine the second row size, from which the first row size can be obtained. */
asym = rows[row] - (1 << log2_ui16(rows[row])); /* separate the powers of 2 */
if (!asym) /* is the module asymmetric? */
asym = vslots[vslot] >> 1; /* symmetric, therefore divide by 2 */
asym = rows[row] >> 1; /* symmetric, therefore divide by 2 */
spd_devices[slot]->row1 = vslots[vslot] - asym;
spd_devices[slot]->row2 = asym;
spd_modules[slot]->row1 = rows[row] - asym;
spd_modules[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);
spd_log("SPD: Registering slot %d = row %d = %d MB (%d/%d)\n", slot, row, rows[row], spd_modules[slot]->row1, spd_modules[slot]->row2);
switch (ram_type) {
case SPD_TYPE_FPM:
case SPD_TYPE_EDO:
edo_data = (spd_edo_t *) &spd_data[slot];
edo_data = (spd_edo_t *) &spd_modules[slot]->data;
memset(edo_data, 0, sizeof(spd_edo_t));
/* EDO SPD is specified by JEDEC and present in some modules, but
@@ -254,10 +245,10 @@ spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size)
edo_data->bytes_used = 0x80;
edo_data->spd_size = 0x08;
edo_data->mem_type = ram_type;
edo_data->row_bits = SPD_ROLLUP(7 + log2_ui16(spd_devices[slot]->row1)); /* first row */
edo_data->row_bits = SPD_ROLLUP(7 + log2_ui16(spd_modules[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 */
if (spd_modules[slot]->row1 != spd_modules[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_modules[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;
@@ -269,7 +260,7 @@ 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, EMU_NAME "-%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", rows[row]);
for (i = strlen(edo_data->part_no); i < sizeof(edo_data->part_no); i++)
edo_data->part_no[i] = ' '; /* part number should be space-padded */
edo_data->rev_code[0] = BCD8(EMU_VERSION_MAJ);
@@ -278,22 +269,22 @@ spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size)
edo_data->mfg_week = 17;
for (i = 0; i < 63; i++)
edo_data->checksum += spd_data[slot][i];
edo_data->checksum += spd_modules[slot]->data[i];
for (i = 0; i < 129; i++)
edo_data->checksum2 += spd_data[slot][i];
edo_data->checksum2 += spd_modules[slot]->data[i];
break;
case SPD_TYPE_SDRAM:
sdram_data = (spd_sdram_t *) &spd_data[slot];
sdram_data = (spd_sdram_t *) &spd_modules[slot]->data;
memset(sdram_data, 0, sizeof(spd_sdram_t));
sdram_data->bytes_used = 0x80;
sdram_data->spd_size = 0x08;
sdram_data->mem_type = ram_type;
sdram_data->row_bits = SPD_ROLLUP(6 + log2_ui16(spd_devices[slot]->row1)); /* first row */
sdram_data->row_bits = SPD_ROLLUP(6 + log2_ui16(spd_modules[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 */
if (spd_modules[slot]->row1 != spd_modules[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_modules[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;
@@ -313,18 +304,18 @@ spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size)
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;
if (spd_devices[slot]->row1 != spd_devices[slot]->row2) {
if (spd_modules[slot]->row1 != spd_modules[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 */
sdram_data->bank_density = 1 << (log2_ui16(spd_modules[slot]->row1 >> 1) - 2); /* first row */
sdram_data->bank_density |= 1 << (log2_ui16(spd_modules[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->bank_density = 1 << (log2_ui16(spd_modules[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, EMU_NAME "-SDR-%03dM", vslots[vslot]);
sprintf(sdram_data->part_no, EMU_NAME "-SDR-%03dM", rows[row]);
for (i = strlen(sdram_data->part_no); i < sizeof(sdram_data->part_no); i++)
sdram_data->part_no[i] = ' '; /* part number should be space-padded */
sdram_data->rev_code[0] = BCD8(EMU_VERSION_MAJ);
@@ -336,15 +327,16 @@ spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size)
sdram_data->features = 0xFF;
for (i = 0; i < 63; i++)
sdram_data->checksum += spd_data[slot][i];
sdram_data->checksum += spd_modules[slot]->data[i];
for (i = 0; i < 129; i++)
sdram_data->checksum2 += spd_data[slot][i];
sdram_data->checksum2 += spd_modules[slot]->data[i];
break;
}
device_add(info);
vslot++;
row++;
}
device_add(&spd_device);
}
@@ -352,7 +344,7 @@ void
spd_write_drbs(uint8_t *regs, uint8_t reg_min, uint8_t reg_max, uint8_t drb_unit)
{
uint8_t row, dimm, drb, apollo = 0;
uint16_t size, vslots[SPD_MAX_SLOTS];
uint16_t size, rows[SPD_MAX_SLOTS];
/* Special case for VIA Apollo Pro family, which jumps from 5F to 56. */
if (reg_max < reg_min) {
@@ -363,26 +355,26 @@ spd_write_drbs(uint8_t *regs, uint8_t reg_min, uint8_t reg_max, uint8_t drb_unit
/* No SPD: split SIMMs into pairs as if they were "DIMM"s. */
if (!spd_present) {
dimm = ((reg_max - reg_min) + 1) >> 1; /* amount of "DIMM"s, also used to determine the maximum "DIMM" size */
spd_populate(vslots, dimm, mem_size >> 10, drb_unit, 1 << (log2_ui16(machines[machine].max_ram / dimm)), 0);
spd_populate(rows, dimm, mem_size >> 10, drb_unit, 1 << (log2_ui16(machines[machine].max_ram / dimm)), 0);
}
/* Write DRBs for each row. */
spd_log("Writing DRBs... regs=[%02X:%02X] unit=%d\n", reg_min, reg_max, drb_unit);
spd_log("SPD: Writing DRBs... regs=[%02X:%02X] unit=%d\n", reg_min, reg_max, drb_unit);
for (row = 0; row <= (reg_max - reg_min); row++) {
dimm = (row >> 1);
size = 0;
if (spd_present) {
/* SPD enabled: use SPD info for this slot, if present. */
if (spd_devices[dimm]) {
if (spd_devices[dimm]->row1 < drb_unit) /* hack within a hack: turn a double-sided DIMM that is too small into a single-sided one */
if (spd_modules[dimm]) {
if (spd_modules[dimm]->row1 < drb_unit) /* hack within a hack: turn a double-sided DIMM that is too small into a single-sided one */
size = (row & 1) ? 0 : drb_unit;
else
size = (row & 1) ? spd_devices[dimm]->row2 : spd_devices[dimm]->row1;
size = (row & 1) ? spd_modules[dimm]->row2 : spd_modules[dimm]->row1;
}
} else {
/* No SPD: use the values calculated above. */
size = (vslots[dimm] >> 1);
size = (rows[dimm] >> 1);
}
/* Determine the DRB register to write. */
@@ -399,6 +391,16 @@ spd_write_drbs(uint8_t *regs, uint8_t reg_min, uint8_t reg_max, uint8_t drb_unit
regs[drb] = regs[drb - 1];
if (size)
regs[drb] += size / drb_unit; /* this will intentionally overflow on 440GX with 2 GB */
spd_log("DRB[%d] = %d MB (%02Xh raw)\n", row, size, regs[drb]);
spd_log("SPD: DRB[%d] = %d MB (%02Xh raw)\n", row, size, regs[drb]);
}
}
static const device_t spd_device = {
"Serial Presence Detect ROMs",
DEVICE_ISA,
0,
spd_init, spd_close, NULL,
{ NULL }, NULL, NULL,
NULL
};