Implement SPD
This commit is contained in:
@@ -35,6 +35,7 @@
|
|||||||
#include "sio.h"
|
#include "sio.h"
|
||||||
#include "sst_flash.h"
|
#include "sst_flash.h"
|
||||||
#include "hwm.h"
|
#include "hwm.h"
|
||||||
|
#include "spd.h"
|
||||||
#include "video.h"
|
#include "video.h"
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "machine.h"
|
#include "machine.h"
|
||||||
@@ -134,6 +135,7 @@ machine_at_6abx3_init(const machine_t *model)
|
|||||||
// device_add(&intel_flash_bxt_device);
|
// device_add(&intel_flash_bxt_device);
|
||||||
// device_add(&sst_flash_29ee020_device);
|
// device_add(&sst_flash_29ee020_device);
|
||||||
device_add(&intel_flash_bxt_device);
|
device_add(&intel_flash_bxt_device);
|
||||||
|
spd_register(SPD_TYPE_SDRAM, 0xF, 256);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -166,6 +168,7 @@ machine_at_p2bls_init(const machine_t *model)
|
|||||||
device_add(&keyboard_ps2_pci_device);
|
device_add(&keyboard_ps2_pci_device);
|
||||||
device_add(&w83977ef_device);
|
device_add(&w83977ef_device);
|
||||||
device_add(&sst_flash_39sf020_device);
|
device_add(&sst_flash_39sf020_device);
|
||||||
|
spd_register(SPD_TYPE_SDRAM, 0xF, 256);
|
||||||
|
|
||||||
hwm_values_t machine_hwm = {
|
hwm_values_t machine_hwm = {
|
||||||
{ /* fan speeds */
|
{ /* fan speeds */
|
||||||
@@ -225,6 +228,7 @@ machine_at_p6bxt_init(const machine_t *model)
|
|||||||
device_add(&w83977tf_device);
|
device_add(&w83977tf_device);
|
||||||
device_add(&keyboard_ps2_pci_device);
|
device_add(&keyboard_ps2_pci_device);
|
||||||
device_add(&intel_flash_bxt_device);
|
device_add(&intel_flash_bxt_device);
|
||||||
|
spd_register(SPD_TYPE_SDRAM, 0x7, 256);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -260,6 +264,7 @@ machine_at_63a_init(const machine_t *model)
|
|||||||
device_add(&w83977tf_device);
|
device_add(&w83977tf_device);
|
||||||
device_add(&keyboard_ps2_pci_device);
|
device_add(&keyboard_ps2_pci_device);
|
||||||
device_add(&intel_flash_bxt_device);
|
device_add(&intel_flash_bxt_device);
|
||||||
|
spd_register(SPD_TYPE_SDRAM, 0x3, 128);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
281
src/spd.c
Normal file
281
src/spd.c
Normal file
@@ -0,0 +1,281 @@
|
|||||||
|
/*
|
||||||
|
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
||||||
|
* running old operating systems and software designed for IBM
|
||||||
|
* PC systems and compatibles from 1981 through fairly recent
|
||||||
|
* system designs based on the PCI bus.
|
||||||
|
*
|
||||||
|
* This file is part of the 86Box distribution.
|
||||||
|
*
|
||||||
|
* Emulation of SPD (Serial Presence Detect) devices.
|
||||||
|
*
|
||||||
|
* Version: @(#)spd.c 1.0.0 2020/03/24
|
||||||
|
*
|
||||||
|
* Authors: RichardG, <richardg867@gmail.com>
|
||||||
|
*
|
||||||
|
* Copyright 2020 RichardG.
|
||||||
|
*/
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <wchar.h>
|
||||||
|
#define HAVE_STDARG_H
|
||||||
|
#include "86box.h"
|
||||||
|
#include "device.h"
|
||||||
|
#include "smbus.h"
|
||||||
|
#include "spd.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define SPD_MAX_SLOTS 8
|
||||||
|
#define SPD_DATA_SIZE 256
|
||||||
|
|
||||||
|
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct _spd_ {
|
||||||
|
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];
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
spd_log(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
if (spd_do_log) {
|
||||||
|
va_start(ap, fmt);
|
||||||
|
pclog_ex(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define spd_log(fmt, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t
|
||||||
|
spd_read_byte(uint8_t addr, void *priv)
|
||||||
|
{
|
||||||
|
spd_t *dev = (spd_t *) priv;
|
||||||
|
return spd_read_byte_cmd(addr, dev->addr_register, priv);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t
|
||||||
|
spd_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv)
|
||||||
|
{
|
||||||
|
spd_t *dev = (spd_t *) priv;
|
||||||
|
uint8_t ret = *(spd_data[dev->slot] + cmd);
|
||||||
|
spd_log("SPD: read(%02x, %02x) = %02x\n", addr, cmd, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
spd_write_byte(uint8_t addr, uint8_t val, void *priv)
|
||||||
|
{
|
||||||
|
spd_t *dev = (spd_t *) priv;
|
||||||
|
dev->addr_register = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
spd_close(void *priv)
|
||||||
|
{
|
||||||
|
spd_t *dev = (spd_t *) 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_write_byte, NULL, NULL, NULL,
|
||||||
|
dev);
|
||||||
|
|
||||||
|
free(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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_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);
|
||||||
|
|
||||||
|
return dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t
|
||||||
|
log2_ui16(uint16_t i)
|
||||||
|
{
|
||||||
|
uint8_t ret = 0;
|
||||||
|
while ((i >>= 1))
|
||||||
|
ret++;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
comp_ui16_rev(const void *elem1, const void *elem2)
|
||||||
|
{
|
||||||
|
uint16_t a = *((uint16_t *)elem1);
|
||||||
|
uint16_t b = *((uint16_t *)elem2);
|
||||||
|
return ((a > b) ? -1 : ((a < b) ? 1 : 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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_size, total_size, vslots[SPD_MAX_SLOTS];
|
||||||
|
spd_sdram_t *sdram_data;
|
||||||
|
|
||||||
|
/* determine the minimum module size for this RAM type */
|
||||||
|
switch (ram_type) {
|
||||||
|
case SPD_TYPE_SDRAM:
|
||||||
|
min_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 with modules in power-of-2 capacities */
|
||||||
|
total_size = mem_size / 1024;
|
||||||
|
for (vslot = 0; vslot < slot_count && total_size; vslot++) {
|
||||||
|
/* populate slot */
|
||||||
|
vslots[vslot] = (1 << log2_ui16(MIN(total_size, max_module_size)));
|
||||||
|
spd_log("SPD: assigning %d MB to vslot %d\n", vslots[vslot], vslot);
|
||||||
|
total_size -= vslots[vslot];
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* populate empty vslots by splitting modules while possible */
|
||||||
|
split = 1;
|
||||||
|
while (split) {
|
||||||
|
/* look for a module to split */
|
||||||
|
split = 0;
|
||||||
|
for (vslot = 0; vslot < slot_count; vslot++) {
|
||||||
|
if (vslots[vslot] < (min_size * 2))
|
||||||
|
continue; /* no module here or module is too small to be split */
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
if (!next_empty_vslot)
|
||||||
|
break; /* no empty vslots 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 = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* re-sort vslots by descending capacity if any modules were split */
|
||||||
|
if (split)
|
||||||
|
qsort(vslots, slot_count, sizeof(uint16_t), comp_ui16_rev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* register SPD devices and populate their data according to the vslots */
|
||||||
|
vslot = 0;
|
||||||
|
for (slot = 0; slot < SPD_MAX_SLOTS; slot++) {
|
||||||
|
if (!(slot_mask & (1 << slot)))
|
||||||
|
continue; /* slot disabled */
|
||||||
|
|
||||||
|
if (!vslots[vslot])
|
||||||
|
break; /* no slots left to fill */
|
||||||
|
|
||||||
|
spd_log("SPD: registering slot %d = %d MB\n", slot, vslots[vslot]);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
switch (ram_type) {
|
||||||
|
case SPD_TYPE_SDRAM:
|
||||||
|
sdram_data = (spd_sdram_t *)&spd_data[slot];
|
||||||
|
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 = 5 + log2_ui16(vslots[vslot]);
|
||||||
|
sdram_data->col_bits = 9;
|
||||||
|
sdram_data->rows = 2;
|
||||||
|
sdram_data->data_width_lsb = 64;
|
||||||
|
sdram_data->data_width_msb = 0;
|
||||||
|
sdram_data->signal_level = SPD_SDR_SIGNAL_LVTTL;
|
||||||
|
sdram_data->tclk = sdram_data->tac = 0x10;
|
||||||
|
sdram_data->config = 0;
|
||||||
|
sdram_data->refresh_rate = SPD_SDR_REFRESH_SELF | SPD_SDR_REFRESH_NORMAL;
|
||||||
|
sdram_data->sdram_width = 8;
|
||||||
|
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->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 = sdram_data->tac2 = 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);
|
||||||
|
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]);
|
||||||
|
sdram_data->mfg_year = 0x20;
|
||||||
|
sdram_data->mfg_week = 0x01;
|
||||||
|
sdram_data->freq = 100;
|
||||||
|
sdram_data->features = 0xFF;
|
||||||
|
|
||||||
|
for (i = 0; i < 63; i++)
|
||||||
|
sdram_data->checksum += spd_data[slot][i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
device_add(spd_devices[slot]);
|
||||||
|
vslot++;
|
||||||
|
}
|
||||||
|
}
|
73
src/spd.h
Normal file
73
src/spd.h
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
||||||
|
* running old operating systems and software designed for IBM
|
||||||
|
* PC systems and compatibles from 1981 through fairly recent
|
||||||
|
* system designs based on the PCI bus.
|
||||||
|
*
|
||||||
|
* This file is part of the 86Box distribution.
|
||||||
|
*
|
||||||
|
* Emulation of SPD (Serial Presence Detect) devices.
|
||||||
|
*
|
||||||
|
* Version: @(#)spd.h 1.0.0 2020/03/24
|
||||||
|
*
|
||||||
|
* Authors: RichardG, <richardg867@gmail.com>
|
||||||
|
*
|
||||||
|
* Copyright 2020 RichardG.
|
||||||
|
*/
|
||||||
|
#ifndef EMU_SPD_H
|
||||||
|
# define EMU_SPD_H
|
||||||
|
|
||||||
|
|
||||||
|
#define SPD_BASE_ADDR 0x50
|
||||||
|
|
||||||
|
#define SPD_TYPE_EDO 0x02
|
||||||
|
#define SPD_TYPE_SDRAM 0x04
|
||||||
|
|
||||||
|
#define SPD_MIN_SIZE_SDRAM 8
|
||||||
|
|
||||||
|
#define SPD_SDR_SIGNAL_LVTTL 0x01
|
||||||
|
|
||||||
|
#define SPD_SDR_REFRESH_NORMAL 0x00
|
||||||
|
#define SPD_SDR_REFRESH_SELF 0x80
|
||||||
|
|
||||||
|
#define SPD_SDR_BURST_PAGE 0x80
|
||||||
|
|
||||||
|
#define SPD_SDR_ATTR_BUFFERED 0x01
|
||||||
|
#define SPD_SDR_ATTR_REGISTERED 0x02
|
||||||
|
|
||||||
|
#define SPD_SDR_ATTR_EARLY_RAS 0x01
|
||||||
|
#define SPD_SDR_ATTR_AUTO_PC 0x02
|
||||||
|
#define SPD_SDR_ATTR_PC_ALL 0x04
|
||||||
|
#define SPD_SDR_ATTR_W1R_BURST 0x08
|
||||||
|
#define SPD_SDR_ATTR_VCC_LOW_5 0x10
|
||||||
|
#define SPD_SDR_ATTR_VCC_HI_5 0x20
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct _spd_sdram_ {
|
||||||
|
uint8_t bytes_used, spd_size, mem_type,
|
||||||
|
row_bits, col_bits, rows,
|
||||||
|
data_width_lsb, data_width_msb,
|
||||||
|
signal_level, tclk, tac,
|
||||||
|
config, refresh_rate,
|
||||||
|
sdram_width, ecc_width,
|
||||||
|
tccd, burst, banks, cas, cs, we,
|
||||||
|
mod_attr, dev_attr,
|
||||||
|
tclk2, tac2, tclk3, tac3,
|
||||||
|
trp, trrd, trcd, tras,
|
||||||
|
bank_density,
|
||||||
|
ca_setup, ca_hold, data_setup, data_hold,
|
||||||
|
reserved[26],
|
||||||
|
spd_rev, checksum,
|
||||||
|
mfg_jedec[8], mfg_loc;
|
||||||
|
char part_no[18];
|
||||||
|
uint8_t rev_code[2],
|
||||||
|
mfg_year, mfg_week, serial[4], mfg_specific[27],
|
||||||
|
freq, features,
|
||||||
|
other_data[128];
|
||||||
|
} spd_sdram_t;
|
||||||
|
|
||||||
|
|
||||||
|
extern void spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /*EMU_SPD_H*/
|
@@ -605,7 +605,7 @@ DEVOBJ := bugger.o hwm.o hwm_w83781d.o ibm_5161.o isamem.o isartc.o lpt.o postc
|
|||||||
sio_w83787f.o \
|
sio_w83787f.o \
|
||||||
sio_w83877f.o sio_w83977f.o \
|
sio_w83877f.o sio_w83977f.o \
|
||||||
sio_um8669f.o \
|
sio_um8669f.o \
|
||||||
smbus.o \
|
smbus.o spd.o \
|
||||||
keyboard.o \
|
keyboard.o \
|
||||||
keyboard_xt.o keyboard_at.o \
|
keyboard_xt.o keyboard_at.o \
|
||||||
gameport.o \
|
gameport.o \
|
||||||
|
@@ -610,7 +610,7 @@ DEVOBJ := bugger.o hwm.o hwm_w83781d.o ibm_5161.o isamem.o isartc.o lpt.o postc
|
|||||||
sio_w83787f.o \
|
sio_w83787f.o \
|
||||||
sio_w83877f.o sio_w83977f.o \
|
sio_w83877f.o sio_w83977f.o \
|
||||||
sio_um8669f.o \
|
sio_um8669f.o \
|
||||||
smbus.o \
|
smbus.o spd.o \
|
||||||
keyboard.o \
|
keyboard.o \
|
||||||
keyboard_xt.o keyboard_at.o \
|
keyboard_xt.o keyboard_at.o \
|
||||||
gameport.o \
|
gameport.o \
|
||||||
|
Reference in New Issue
Block a user