DRB locking implementation
This commit is contained in:
@@ -32,6 +32,7 @@
|
||||
#include <86box/hdc.h>
|
||||
#include <86box/machine.h>
|
||||
#include <86box/chipset.h>
|
||||
#include <86box/spd.h>
|
||||
|
||||
|
||||
#define MEM_STATE_SHADOW_R 0x01
|
||||
@@ -179,6 +180,33 @@ i420ex_smram_handler_phase1(i420ex_t *dev)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
i420ex_write_drbs(i420ex_t *dev)
|
||||
{
|
||||
uint8_t row, dimm;
|
||||
uint16_t size, vslots[SPD_MAX_SLOTS];
|
||||
|
||||
/* No SPD: let the SPD code split SIMMs into pairs as if they were "DIMM"s. */
|
||||
dimm = (4 + 1) >> 1; /* amount of "DIMM"s, also used to determine the maximum "DIMM" size */
|
||||
spd_populate(vslots, dimm, (mem_size >> 10), 1, 1 << (log2_ui16(machines[machine].max_ram / dimm)), 0);
|
||||
|
||||
/* Write DRBs for each row. */
|
||||
i420ex_log("Writing DRBs...\n");
|
||||
for (row = 0; row <= 4; row++) {
|
||||
dimm = (row >> 1);
|
||||
|
||||
/* No SPD: use the values calculated above. */
|
||||
size = (vslots[dimm] >> 1);
|
||||
|
||||
/* Populate DRB register, adding the previous DRB's value.. */
|
||||
dev->regs[0x60 | row] = ((row > 0) ? dev->regs[0x60 | (row - 1)] : 0);
|
||||
if (size)
|
||||
dev->regs[0x60 | row] += size;
|
||||
i420ex_log("DRB[%d] = %d MB (%02Xh raw)\n", row, size, dev->regs[0x60 | row]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
i420ex_write(int func, int addr, uint8_t val, void *priv)
|
||||
{
|
||||
@@ -232,8 +260,6 @@ i420ex_write(int func, int addr, uint8_t val, void *priv)
|
||||
break;
|
||||
case 0x4c: case 0x51:
|
||||
case 0x57:
|
||||
case 0x60: case 0x61: case 0x62: case 0x63:
|
||||
case 0x64:
|
||||
case 0x68: case 0x69:
|
||||
dev->regs[addr] = val;
|
||||
if (addr == 0x4c) {
|
||||
@@ -306,6 +332,9 @@ i420ex_write(int func, int addr, uint8_t val, void *priv)
|
||||
i420ex_map(0xec000, 0x04000, val >> 4);
|
||||
dev->regs[0x5f] = val;
|
||||
break;
|
||||
case 0x60: case 0x61: case 0x62: case 0x63: case 0x64:
|
||||
i420ex_write_drbs(dev);
|
||||
break;
|
||||
case 0x66: case 0x67:
|
||||
i420ex_log("Set IRQ routing: INT %c -> %02X\n", 0x41 + (addr & 0x01), val);
|
||||
dev->regs[addr] = val & 0x8f;
|
||||
|
@@ -6,7 +6,7 @@
|
||||
*
|
||||
* This file is part of the 86Box distribution.
|
||||
*
|
||||
* Implementation of the Intel PCISet chips from 420TX to 440BX.
|
||||
* Implementation of the Intel PCISet chips from 420TX to 440GX.
|
||||
*
|
||||
*
|
||||
*
|
||||
@@ -28,6 +28,8 @@
|
||||
#include <86box/device.h>
|
||||
#include <86box/keyboard.h>
|
||||
#include <86box/chipset.h>
|
||||
#include <86box/spd.h>
|
||||
#include <86box/machine.h>
|
||||
|
||||
|
||||
enum
|
||||
@@ -52,12 +54,32 @@ typedef struct
|
||||
{
|
||||
uint8_t pm2_cntrl, max_func,
|
||||
smram_locked, max_drb,
|
||||
drb_default;
|
||||
drb_unit, drb_default;
|
||||
uint8_t regs[2][256], regs_locked[2][256];
|
||||
int type;
|
||||
} i4x0_t;
|
||||
|
||||
|
||||
#ifdef ENABLE_I4X0_LOG
|
||||
int i4x0_do_log = ENABLE_I4X0_LOG;
|
||||
|
||||
|
||||
static void
|
||||
i4x0_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (i4x0_do_log) {
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define i4x0_log(fmt, ...)
|
||||
#endif
|
||||
|
||||
|
||||
static void
|
||||
i4x0_map(uint32_t addr, uint32_t size, int state)
|
||||
{
|
||||
@@ -247,6 +269,47 @@ pm2_cntrl_write(uint16_t addr, uint8_t val, void *p)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
i4x0_write_drbs(i4x0_t *dev)
|
||||
{
|
||||
uint8_t row, dimm;
|
||||
uint16_t size, vslots[SPD_MAX_SLOTS];
|
||||
|
||||
/* No SPD: let the SPD code split SIMMs into pairs as if they were "DIMM"s. */
|
||||
if (!spd_present) {
|
||||
dimm = (dev->max_drb + 1) >> 1; /* amount of "DIMM"s, also used to determine the maximum "DIMM" size */
|
||||
spd_populate(vslots, dimm, (mem_size >> 10), dev->drb_unit, 1 << (log2_ui16(machines[machine].max_ram / dimm)), 0);
|
||||
}
|
||||
|
||||
/* Write DRBs for each row. */
|
||||
i4x0_log("Writing DRBs... unit=%d max=%d\n", dev->drb_unit, dev->max_drb);
|
||||
for (row = 0; row <= dev->max_drb; 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 < dev->drb_unit) /* hack within a hack: turn a double-sided DIMM that is too small into a single-sided one */
|
||||
size = ((row & 1) ? 0 : dev->drb_unit);
|
||||
else
|
||||
size = ((row & 1) ? spd_devices[dimm]->row2 : spd_devices[dimm]->row1);
|
||||
}
|
||||
} else {
|
||||
/* No SPD: use the values calculated above. */
|
||||
size = (vslots[dimm] >> 1);
|
||||
}
|
||||
|
||||
/* Populate DRB register, adding the previous DRB's value.
|
||||
This will intentionally overflow on 440GX with 2 GB. */
|
||||
dev->regs[0][0x60 | row] = ((row > 0) ? dev->regs[0][0x60 | (row - 1)] : 0);
|
||||
if (size)
|
||||
dev->regs[0][0x60 | row] += (size / dev->drb_unit);
|
||||
i4x0_log("DRB[%d] = %d MB (%02Xh raw)\n", row, size, dev->regs[0][0x60 | row]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
i4x0_write(int func, int addr, uint8_t val, void *priv)
|
||||
{
|
||||
@@ -627,13 +690,17 @@ i4x0_write(int func, int addr, uint8_t val, void *priv)
|
||||
regs[0x5f] = val & 0x77;
|
||||
break;
|
||||
case 0x60: case 0x61: case 0x62: case 0x63: case 0x64:
|
||||
if ((addr & 0x7) <= dev->max_drb) {
|
||||
i4x0_write_drbs(dev);
|
||||
break;
|
||||
}
|
||||
switch (dev->type) {
|
||||
case INTEL_420TX: case INTEL_420ZX:
|
||||
case INTEL_430LX: case INTEL_430NX:
|
||||
case INTEL_430HX:
|
||||
case INTEL_440FX:
|
||||
case INTEL_440LX: case INTEL_440EX:
|
||||
case INTEL_440BX: case INTEL_440ZX:
|
||||
case INTEL_440BX: case INTEL_440ZX:
|
||||
default:
|
||||
regs[addr] = val;
|
||||
break;
|
||||
@@ -646,13 +713,17 @@ i4x0_write(int func, int addr, uint8_t val, void *priv)
|
||||
}
|
||||
break;
|
||||
case 0x65:
|
||||
if ((addr & 0x7) <= dev->max_drb) {
|
||||
i4x0_write_drbs(dev);
|
||||
break;
|
||||
}
|
||||
switch (dev->type) {
|
||||
case INTEL_420TX: case INTEL_420ZX:
|
||||
case INTEL_430LX: case INTEL_430NX:
|
||||
case INTEL_430HX:
|
||||
case INTEL_440FX:
|
||||
case INTEL_440LX: case INTEL_440EX:
|
||||
case INTEL_440GX:
|
||||
case INTEL_440GX:
|
||||
case INTEL_440BX: case INTEL_440ZX:
|
||||
regs[addr] = val;
|
||||
break;
|
||||
@@ -665,6 +736,10 @@ i4x0_write(int func, int addr, uint8_t val, void *priv)
|
||||
}
|
||||
break;
|
||||
case 0x66:
|
||||
if ((addr & 0x7) <= dev->max_drb) {
|
||||
i4x0_write_drbs(dev);
|
||||
break;
|
||||
}
|
||||
switch (dev->type) {
|
||||
case INTEL_430NX: case INTEL_430HX:
|
||||
case INTEL_440FX: case INTEL_440LX:
|
||||
@@ -675,12 +750,16 @@ i4x0_write(int func, int addr, uint8_t val, void *priv)
|
||||
}
|
||||
break;
|
||||
case 0x67:
|
||||
if ((addr & 0x7) <= dev->max_drb) {
|
||||
i4x0_write_drbs(dev);
|
||||
break;
|
||||
}
|
||||
switch (dev->type) {
|
||||
case INTEL_430NX: case INTEL_430HX:
|
||||
case INTEL_440FX: case INTEL_440LX:
|
||||
case INTEL_440EX:
|
||||
case INTEL_440BX: case INTEL_440GX:
|
||||
case INTEL_440ZX:
|
||||
case INTEL_440ZX:
|
||||
regs[addr] = val;
|
||||
break;
|
||||
case INTEL_430VX:
|
||||
@@ -1305,6 +1384,7 @@ static void
|
||||
regs[0x59] = 0x0f;
|
||||
regs[0x60] = regs[0x61] = regs[0x62] = regs[0x63] = regs[0x64] = regs[0x65] = 0x02;
|
||||
dev->max_drb = 5;
|
||||
dev->drb_unit = 4;
|
||||
dev->drb_default = 0x02;
|
||||
break;
|
||||
case INTEL_430LX:
|
||||
@@ -1323,6 +1403,7 @@ static void
|
||||
regs[0x59] = 0x0f;
|
||||
regs[0x60] = regs[0x61] = regs[0x62] = regs[0x63] = regs[0x64] = regs[0x65] = 0x02;
|
||||
dev->max_drb = 5;
|
||||
dev->drb_unit = 4;
|
||||
dev->drb_default = 0x02;
|
||||
break;
|
||||
case INTEL_430NX:
|
||||
@@ -1343,6 +1424,7 @@ static void
|
||||
regs[0x59] = 0x0f;
|
||||
regs[0x60] = regs[0x61] = regs[0x62] = regs[0x63] = regs[0x64] = regs[0x65] = regs[0x66] = regs[0x67] = 0x02;
|
||||
dev->max_drb = 7;
|
||||
dev->drb_unit = 4;
|
||||
dev->drb_default = 0x02;
|
||||
break;
|
||||
case INTEL_430FX:
|
||||
@@ -1358,6 +1440,7 @@ static void
|
||||
regs[0x60] = regs[0x61] = regs[0x62] = regs[0x63] = regs[0x64] = 0x02;
|
||||
regs[0x72] = 0x02;
|
||||
dev->max_drb = 4;
|
||||
dev->drb_unit = 4;
|
||||
dev->drb_default = 0x02;
|
||||
break;
|
||||
case INTEL_430HX:
|
||||
@@ -1372,6 +1455,7 @@ static void
|
||||
regs[0x60] = regs[0x61] = regs[0x62] = regs[0x63] = regs[0x64] = regs[0x65] = regs[0x66] = regs[0x67] = 0x02;
|
||||
regs[0x72] = 0x02;
|
||||
dev->max_drb = 7;
|
||||
dev->drb_unit = 4;
|
||||
dev->drb_default = 0x02;
|
||||
break;
|
||||
case INTEL_430VX:
|
||||
@@ -1393,6 +1477,7 @@ static void
|
||||
regs[0x74] = 0x0e;
|
||||
regs[0x78] = 0x23;
|
||||
dev->max_drb = 4;
|
||||
dev->drb_unit = 4;
|
||||
dev->drb_default = 0x02;
|
||||
break;
|
||||
case INTEL_430TX:
|
||||
@@ -1410,6 +1495,7 @@ static void
|
||||
regs[0x70] = 0x20;
|
||||
regs[0x72] = 0x02;
|
||||
dev->max_drb = 5;
|
||||
dev->drb_unit = 4;
|
||||
dev->drb_default = 0x02;
|
||||
break;
|
||||
case INTEL_440FX:
|
||||
@@ -1426,6 +1512,7 @@ static void
|
||||
regs[0x71] = 0x10;
|
||||
regs[0x72] = 0x02;
|
||||
dev->max_drb = 7;
|
||||
dev->drb_unit = 8;
|
||||
dev->drb_default = 0x02;
|
||||
break;
|
||||
case INTEL_440LX:
|
||||
@@ -1450,12 +1537,13 @@ static void
|
||||
regs[0xa5] = 0x02;
|
||||
regs[0xa7] = 0x1f;
|
||||
dev->max_drb = 7;
|
||||
dev->drb_unit = 8;
|
||||
dev->drb_default = 0x01;
|
||||
break;
|
||||
case INTEL_440EX:
|
||||
dev->max_func = 1;
|
||||
|
||||
regs[0x02] = 0x80; regs[0x03] = 0x71; /* 82443EX. Same Vendor ID as 440LX*/
|
||||
regs[0x02] = 0x80; regs[0x03] = 0x71; /* 82443EX. Same Vendor ID as 440LX */
|
||||
regs[0x06] = 0x90;
|
||||
regs[0x10] = 0x08;
|
||||
regs[0x34] = 0xa0;
|
||||
@@ -1474,6 +1562,7 @@ static void
|
||||
regs[0xa5] = 0x02;
|
||||
regs[0xa7] = 0x1f;
|
||||
dev->max_drb = 7;
|
||||
dev->drb_unit = 8;
|
||||
dev->drb_default = 0x01;
|
||||
break;
|
||||
case INTEL_440BX: case INTEL_440ZX:
|
||||
@@ -1502,6 +1591,7 @@ static void
|
||||
regs[0xa5] = 0x02;
|
||||
regs[0xa7] = 0x1f;
|
||||
dev->max_drb = 7;
|
||||
dev->drb_unit = 8;
|
||||
dev->drb_default = 0x01;
|
||||
break;
|
||||
case INTEL_440GX:
|
||||
@@ -1527,6 +1617,7 @@ static void
|
||||
regs[0xa5] = 0x02;
|
||||
regs[0xa7] = 0x1f;
|
||||
dev->max_drb = 7;
|
||||
dev->drb_unit = 8;
|
||||
dev->drb_default = 0x01;
|
||||
break;
|
||||
}
|
||||
|
@@ -100,9 +100,12 @@ typedef struct _spd_sdram_ {
|
||||
} spd_sdram_t;
|
||||
|
||||
|
||||
extern int spd_present;
|
||||
extern spd_t *spd_devices[SPD_MAX_SLOTS];
|
||||
|
||||
|
||||
extern uint8_t log2_ui16(uint16_t i);
|
||||
extern 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);
|
||||
extern void spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size);
|
||||
|
||||
|
||||
|
103
src/mem/spd.c
103
src/mem/spd.c
@@ -31,6 +31,7 @@
|
||||
#define SPD_ROLLUP(x) ((x) >= 16 ? ((x) - 15) : (x))
|
||||
|
||||
|
||||
int spd_present = 0;
|
||||
spd_t *spd_devices[SPD_MAX_SLOTS];
|
||||
uint8_t spd_data[SPD_MAX_SLOTS][SPD_DATA_SIZE];
|
||||
|
||||
@@ -156,41 +157,13 @@ comp_ui16_rev(const void *elem1, const void *elem2)
|
||||
|
||||
|
||||
void
|
||||
spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size)
|
||||
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)
|
||||
{
|
||||
uint8_t slot, slot_count, vslot, next_empty_vslot, i, split;
|
||||
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;
|
||||
|
||||
/* determine the minimum module size for this RAM type */
|
||||
switch (ram_type) {
|
||||
case SPD_TYPE_FPM:
|
||||
case SPD_TYPE_EDO:
|
||||
min_module_size = SPD_MIN_SIZE_EDO;
|
||||
break;
|
||||
|
||||
case SPD_TYPE_SDRAM:
|
||||
min_module_size = SPD_MIN_SIZE_SDRAM;
|
||||
break;
|
||||
|
||||
default:
|
||||
spd_log("SPD: unknown RAM type 0x%02X\n", ram_type);
|
||||
return;
|
||||
}
|
||||
|
||||
/* count how many (real) slots are enabled */
|
||||
slot_count = 0;
|
||||
for (slot = 0; slot < SPD_MAX_SLOTS; slot++) {
|
||||
vslots[slot] = 0;
|
||||
if (slot_mask & (1 << slot)) {
|
||||
slot_count++;
|
||||
}
|
||||
}
|
||||
uint8_t vslot, next_empty_vslot, split, i;
|
||||
uint16_t asym;
|
||||
|
||||
/* populate vslots with modules in power-of-2 capacities */
|
||||
total_size = (mem_size >> 10);
|
||||
memset(vslots, 0x00, SPD_MAX_SLOTS << 1);
|
||||
for (vslot = 0; vslot < slot_count && total_size; vslot++) {
|
||||
/* populate slot */
|
||||
vslots[vslot] = (1 << log2_ui16(MIN(total_size, max_module_size)));
|
||||
@@ -205,15 +178,17 @@ spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_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);
|
||||
/* work backwards to add the missing RAM as asymmetric modules if possible */
|
||||
if (enable_asym) {
|
||||
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);
|
||||
@@ -241,12 +216,52 @@ spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size)
|
||||
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 = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* re-sort vslots by descending capacity if any modules were split */
|
||||
/* sort vslots by descending capacity if any were split */
|
||||
if (split)
|
||||
qsort(vslots, slot_count, sizeof(uint16_t), comp_ui16_rev);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
spd_edo_t *edo_data;
|
||||
spd_sdram_t *sdram_data;
|
||||
|
||||
/* determine the minimum module size for this RAM type */
|
||||
switch (ram_type) {
|
||||
case SPD_TYPE_FPM:
|
||||
case SPD_TYPE_EDO:
|
||||
min_module_size = SPD_MIN_SIZE_EDO;
|
||||
break;
|
||||
|
||||
case SPD_TYPE_SDRAM:
|
||||
min_module_size = SPD_MIN_SIZE_SDRAM;
|
||||
break;
|
||||
|
||||
default:
|
||||
spd_log("SPD: unknown RAM type 0x%02X\n", ram_type);
|
||||
return;
|
||||
}
|
||||
|
||||
/* count how many (real) slots are enabled */
|
||||
slot_count = 0;
|
||||
for (slot = 0; slot < SPD_MAX_SLOTS; slot++) {
|
||||
vslots[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);
|
||||
|
||||
/* register SPD devices and populate their data according to the vslots */
|
||||
vslot = 0;
|
||||
@@ -377,7 +392,9 @@ spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size)
|
||||
break;
|
||||
}
|
||||
|
||||
device_add(info);
|
||||
//device_add(info);
|
||||
vslot++;
|
||||
}
|
||||
|
||||
spd_present = 1;
|
||||
}
|
||||
|
Reference in New Issue
Block a user