mirror of
https://github.com/86Box/86Box.git
synced 2026-02-22 01:25:33 -07:00
45
src/acpi.c
45
src/acpi.c
@@ -36,6 +36,7 @@
|
||||
#include <86box/apm.h>
|
||||
#include <86box/acpi.h>
|
||||
#include <86box/machine.h>
|
||||
#include <86box/i2c.h>
|
||||
|
||||
|
||||
#ifdef ENABLE_ACPI_LOG
|
||||
@@ -92,7 +93,7 @@ acpi_raise_smi(void *priv)
|
||||
if ((!dev->regs.smi_lock || !dev->regs.smi_active)) {
|
||||
smi_line = 1;
|
||||
dev->regs.smi_active = 1;
|
||||
}
|
||||
}
|
||||
} else if (dev->vendor == VEN_INTEL) {
|
||||
smi_line = 1;
|
||||
/* Clear bit 16 of GLBCTL. */
|
||||
@@ -311,9 +312,18 @@ acpi_reg_read_via(int size, uint16_t addr, void *p)
|
||||
ret = dev->regs.gpio_val & 0xff;
|
||||
break;
|
||||
case 0x44:
|
||||
/* GPIO port Output Value */
|
||||
if (size == 1)
|
||||
/* GPIO port Input Value */
|
||||
if (size == 1) {
|
||||
ret = dev->regs.extsmi_val & 0xff;
|
||||
|
||||
if (dev->i2c) {
|
||||
ret &= 0xf9;
|
||||
if (!(dev->regs.gpio_dir & 0x02) && i2c_gpio_get_scl(dev->i2c))
|
||||
ret |= 0x02;
|
||||
if (!(dev->regs.gpio_dir & 0x04) && i2c_gpio_get_sda(dev->i2c))
|
||||
ret |= 0x04;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x46: case 0x47:
|
||||
/* GPO Port Output Value */
|
||||
@@ -639,6 +649,14 @@ acpi_reg_write_via_common(int size, uint16_t addr, uint8_t val, void *p)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
acpi_i2c_set(acpi_t *dev)
|
||||
{
|
||||
if (dev->i2c)
|
||||
i2c_gpio_set(dev->i2c, !(dev->regs.gpio_dir & 0x02) || (dev->regs.gpio_val & 0x02), !(dev->regs.gpio_dir & 0x04) || (dev->regs.gpio_val & 0x04));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
acpi_reg_write_via(int size, uint16_t addr, uint8_t val, void *p)
|
||||
{
|
||||
@@ -652,13 +670,17 @@ acpi_reg_write_via(int size, uint16_t addr, uint8_t val, void *p)
|
||||
switch (addr) {
|
||||
case 0x40:
|
||||
/* GPIO Direction Control */
|
||||
if (size == 1)
|
||||
dev->regs.gpio_dir = val & 0xff;
|
||||
if (size == 1) {
|
||||
dev->regs.gpio_dir = val & 0x7f;
|
||||
acpi_i2c_set(dev);
|
||||
}
|
||||
break;
|
||||
case 0x42:
|
||||
/* GPIO port Output Value */
|
||||
if (size == 1)
|
||||
dev->regs.gpio_val = val & 0xff;
|
||||
if (size == 1) {
|
||||
dev->regs.gpio_val = val & 0x1f;
|
||||
acpi_i2c_set(dev);
|
||||
}
|
||||
break;
|
||||
case 0x46: case 0x47:
|
||||
/* GPO Port Output Value */
|
||||
@@ -1184,6 +1206,12 @@ acpi_close(void *priv)
|
||||
{
|
||||
acpi_t *dev = (acpi_t *) priv;
|
||||
|
||||
if (dev->i2c) {
|
||||
if (i2c_smbus == i2c_gpio_get_bus(dev->i2c))
|
||||
i2c_smbus = NULL;
|
||||
i2c_gpio_close(dev->i2c);
|
||||
}
|
||||
|
||||
timer_disable(&dev->timer);
|
||||
|
||||
free(dev);
|
||||
@@ -1206,6 +1234,9 @@ acpi_init(const device_t *info)
|
||||
if (dev->vendor == VEN_INTEL) {
|
||||
dev->apm = device_add(&apm_pci_acpi_device);
|
||||
io_sethandler(0x00b2, 0x0002, acpi_apm_in, NULL, NULL, acpi_apm_out, NULL, NULL, dev);
|
||||
} else if (dev->vendor == VEN_VIA) {
|
||||
dev->i2c = i2c_gpio_init("smbus_vt82c586b");
|
||||
i2c_smbus = i2c_gpio_get_bus(dev->i2c);
|
||||
}
|
||||
|
||||
timer_add(&dev->timer, acpi_timer_count, dev, 0);
|
||||
|
||||
@@ -277,7 +277,7 @@ ali6117_reset(void *priv)
|
||||
dev->regs[0x31] = 0x01;
|
||||
dev->regs[0x34] = 0x04; /* enable internal RTC */
|
||||
dev->regs[0x35] = 0x20; /* enable internal KBC */
|
||||
dev->regs[0x36] = (dev->local & 0x4); /* M6117D ID */
|
||||
dev->regs[0x36] = dev->local & 0x4; /* M6117D ID */
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -41,11 +41,10 @@
|
||||
#include <86box/chipset.h>
|
||||
|
||||
|
||||
#define STPC_NB_CLIENT 0x01
|
||||
#define STPC_ISAB_CLIENT 0x02
|
||||
#define STPC_ISAB_CONSUMER2 0x04
|
||||
#define STPC_IDE_ATLAS 0x08
|
||||
#define STPC_USB 0x10
|
||||
#define STPC_CONSUMER2 0x104a020b
|
||||
#define STPC_ATLAS 0x104a0210
|
||||
#define STPC_ELITE 0x104a021a
|
||||
#define STPC_CLIENT 0x100e55cc
|
||||
|
||||
|
||||
typedef struct stpc_t
|
||||
@@ -119,7 +118,7 @@ stpc_recalcmapping(stpc_t *dev)
|
||||
shadowbios_write = 0;
|
||||
|
||||
for (reg = 0; reg <= 3; reg++) {
|
||||
for (bitpair = 0; bitpair <= (reg == 3 ? 0 : 3); bitpair++) {
|
||||
for (bitpair = 0; bitpair <= ((reg == 3) ? 0 : 3); bitpair++) {
|
||||
if (reg == 3) {
|
||||
size = 0x10000;
|
||||
base = 0xf0000;
|
||||
@@ -166,7 +165,7 @@ stpc_host_write(uint16_t addr, uint8_t val, void *priv)
|
||||
|
||||
if (addr == dev->host_base)
|
||||
dev->host_offset = val;
|
||||
else if (addr == dev->host_base + 4)
|
||||
else if (addr == (dev->host_base + 4))
|
||||
dev->host_regs[dev->host_offset] = val;
|
||||
}
|
||||
|
||||
@@ -179,7 +178,7 @@ stpc_host_read(uint16_t addr, void *priv)
|
||||
|
||||
if (addr == dev->host_base)
|
||||
ret = dev->host_offset;
|
||||
else if (addr == dev->host_base + 4)
|
||||
else if (addr == (dev->host_base + 4))
|
||||
ret = dev->host_regs[dev->host_offset];
|
||||
else
|
||||
ret = 0xff;
|
||||
@@ -198,7 +197,7 @@ stpc_localbus_write(uint16_t addr, uint8_t val, void *priv)
|
||||
|
||||
if (addr == dev->localbus_base)
|
||||
dev->localbus_offset = val;
|
||||
else if (addr == dev->localbus_base + 4)
|
||||
else if (addr == (dev->localbus_base + 4))
|
||||
dev->localbus_regs[addr] = val;
|
||||
}
|
||||
|
||||
@@ -211,7 +210,7 @@ stpc_localbus_read(uint16_t addr, void *priv)
|
||||
|
||||
if (addr == dev->localbus_base)
|
||||
ret = dev->localbus_offset;
|
||||
else if (addr == dev->localbus_base + 4)
|
||||
else if (addr == (dev->localbus_base + 4))
|
||||
ret = dev->localbus_regs[dev->localbus_offset];
|
||||
else
|
||||
ret = 0xff;
|
||||
@@ -329,8 +328,8 @@ stpc_ide_bm_handlers(stpc_t *dev)
|
||||
{
|
||||
uint16_t base = (dev->pci_conf[2][0x20] & 0xf0) | (dev->pci_conf[2][0x21] << 8);
|
||||
|
||||
sff_bus_master_handler(dev->bm[0], (dev->pci_conf[2][0x04] & 1), base);
|
||||
sff_bus_master_handler(dev->bm[1], (dev->pci_conf[2][0x04] & 1), base + 8);
|
||||
sff_bus_master_handler(dev->bm[0], dev->pci_conf[2][0x04] & 1, base);
|
||||
sff_bus_master_handler(dev->bm[1], dev->pci_conf[2][0x04] & 1, base + 8);
|
||||
}
|
||||
|
||||
|
||||
@@ -352,7 +351,7 @@ stpc_ide_write(int func, int addr, uint8_t val, void *priv)
|
||||
break;
|
||||
|
||||
case 0x05:
|
||||
dev->pci_conf[2][addr] = (val & 0x01);
|
||||
dev->pci_conf[2][addr] = val & 0x01;
|
||||
break;
|
||||
|
||||
case 0x07:
|
||||
@@ -441,13 +440,13 @@ stpc_ide_read(int func, int addr, void *priv)
|
||||
uint8_t ret;
|
||||
|
||||
if (func > 0)
|
||||
ret = 0xff;
|
||||
ret = 0xff;
|
||||
else {
|
||||
ret = dev->pci_conf[2][addr];
|
||||
if (addr == 0x48) {
|
||||
ret &= 0xfc;
|
||||
ret |= (!!(dev->bm[0]->status & 0x04));
|
||||
ret |= ((!!(dev->bm[1]->status & 0x04)) << 1);
|
||||
ret |= !!(dev->bm[0]->status & 0x04);
|
||||
ret |= (!!(dev->bm[1]->status & 0x04)) << 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -461,9 +460,9 @@ stpc_isab_write(int func, int addr, uint8_t val, void *priv)
|
||||
{
|
||||
stpc_t *dev = (stpc_t *) priv;
|
||||
|
||||
if (func == 1 && !(dev->local & STPC_IDE_ATLAS)) {
|
||||
stpc_ide_write(0, addr, val, priv);
|
||||
return;
|
||||
if ((func == 1) && (dev->local != STPC_ATLAS)) {
|
||||
stpc_ide_write(0, addr, val, priv);
|
||||
return;
|
||||
}
|
||||
|
||||
stpc_log("STPC: isab_write(%d, %02X, %02X)\n", func, addr, val);
|
||||
@@ -492,12 +491,12 @@ stpc_isab_read(int func, int addr, void *priv)
|
||||
stpc_t *dev = (stpc_t *) priv;
|
||||
uint8_t ret;
|
||||
|
||||
if ((func == 1) && !(dev->local & STPC_IDE_ATLAS))
|
||||
ret = stpc_ide_read(0, addr, priv);
|
||||
if ((func == 1) && (dev->local != STPC_ATLAS))
|
||||
ret = stpc_ide_read(0, addr, priv);
|
||||
else if (func > 0)
|
||||
ret = 0xff;
|
||||
ret = 0xff;
|
||||
else
|
||||
ret = dev->pci_conf[1][addr];
|
||||
ret = dev->pci_conf[1][addr];
|
||||
|
||||
stpc_log("STPC: isab_read(%d, %02X) = %02X\n", func, addr, ret);
|
||||
return ret;
|
||||
@@ -547,9 +546,9 @@ stpc_usb_read(int func, int addr, void *priv)
|
||||
uint8_t ret;
|
||||
|
||||
if (func > 0)
|
||||
ret = 0xff;
|
||||
ret = 0xff;
|
||||
else
|
||||
ret = dev->pci_conf[3][addr];
|
||||
ret = dev->pci_conf[3][addr];
|
||||
|
||||
stpc_log("STPC: usb_read(%d, %02X) = %02X\n", func, addr, ret);
|
||||
return ret;
|
||||
@@ -589,34 +588,35 @@ stpc_remap_localbus(stpc_t *dev, uint16_t localbus_base)
|
||||
static uint8_t
|
||||
stpc_serial_handlers(uint8_t val)
|
||||
{
|
||||
stpc_serial_t *dev;
|
||||
if (!(dev = device_get_priv(&stpc_serial_device))) {
|
||||
stpc_log("STPC: Not remapping UARTs, disabled by strap (raw %02X)\n", val);
|
||||
return 0;
|
||||
stpc_serial_t *dev = device_get_priv(&stpc_serial_device);
|
||||
if (!dev) {
|
||||
stpc_log("STPC: Not remapping UARTs, disabled by strap (raw %02X)\n", val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t uart0_io = 0x3f8, uart0_irq = 4, uart1_io = 0x3f8, uart1_irq = 3;
|
||||
uint16_t uart0_io = 0x3f8, uart1_io = 0x3f8;
|
||||
uint8_t uart0_irq = 4, uart1_irq = 3;
|
||||
|
||||
if (val & 0x10)
|
||||
uart1_io -= 0x100;
|
||||
uart1_io &= 0xfeff;
|
||||
if (val & 0x20)
|
||||
uart1_io -= 0x10;
|
||||
uart1_io &= 0xffef;
|
||||
if (val & 0x40)
|
||||
uart0_io -= 0x100;
|
||||
uart0_io &= 0xfeff;
|
||||
if (val & 0x80)
|
||||
uart0_io -= 0x10;
|
||||
uart0_io &= 0xffef;
|
||||
|
||||
if (uart0_io == uart1_io) {
|
||||
/* Apply defaults if both UARTs are set to the same address. */
|
||||
stpc_log("STPC: Both UARTs set to %02X, resetting to defaults\n", uart0_io);
|
||||
uart0_io = 0x3f8;
|
||||
uart1_io = 0x2f8;
|
||||
/* Apply defaults if both UARTs are set to the same address. */
|
||||
stpc_log("STPC: Both UARTs set to %02X, resetting to defaults\n", uart0_io);
|
||||
uart0_io = 0x3f8;
|
||||
uart1_io = 0x2f8;
|
||||
}
|
||||
|
||||
if (uart0_io < 0x300) {
|
||||
/* The address for UART0 defines the IRQs for both ports. */
|
||||
uart0_irq = 3;
|
||||
uart1_irq = 4;
|
||||
if (!(uart0_io & 0x100)) {
|
||||
/* The address for UART0 establishes the IRQs for both ports. */
|
||||
uart0_irq = 3;
|
||||
uart1_irq = 4;
|
||||
}
|
||||
|
||||
stpc_log("STPC: Remapping UART0 to %04X %d and UART1 to %04X %d (raw %02X)\n", uart0_io, uart0_irq, uart1_io, uart1_irq, val);
|
||||
@@ -714,9 +714,9 @@ stpc_reg_read(uint16_t addr, void *priv)
|
||||
if (addr == 0x22)
|
||||
ret = dev->reg_offset;
|
||||
else if (dev->reg_offset >= 0xc0)
|
||||
return 0xff; /* Cyrix CPU registers: let the CPU code handle these */
|
||||
return 0xff; /* let the CPU code handle Cyrix CPU registers */
|
||||
else if ((dev->reg_offset == 0x56) || (dev->reg_offset == 0x57)) {
|
||||
/* ELCR is in here, not in port 4D0h. */
|
||||
/* ELCR registers. */
|
||||
ret = pic_elcr_read(dev->reg_offset, (dev->reg_offset & 1) ? &pic2 : &pic);
|
||||
if (dev->reg_offset == 0x57)
|
||||
ret |= (dev->regs[dev->reg_offset] & 0x01);
|
||||
@@ -738,9 +738,9 @@ stpc_reset(void *priv)
|
||||
memset(dev->regs, 0, sizeof(dev->regs));
|
||||
dev->regs[0x7b] = 0xff;
|
||||
if (device_get_priv(&stpc_lpt_device))
|
||||
dev->regs[0x4c] |= 0x80; /* LPT strap */
|
||||
dev->regs[0x4c] |= 0x80; /* LPT strap */
|
||||
if (stpc_serial_handlers(0x00))
|
||||
dev->regs[0x4c] |= 0x03; /* UART straps */
|
||||
dev->regs[0x4c] |= 0x03; /* UART straps */
|
||||
}
|
||||
|
||||
|
||||
@@ -754,14 +754,12 @@ stpc_setup(stpc_t *dev)
|
||||
stpc_reg_read, NULL, NULL, stpc_reg_write, NULL, NULL, dev);
|
||||
|
||||
/* Northbridge */
|
||||
if (dev->local & STPC_NB_CLIENT) {
|
||||
/* Client */
|
||||
if (dev->local & STPC_CLIENT) {
|
||||
dev->pci_conf[0][0x00] = 0x0e;
|
||||
dev->pci_conf[0][0x01] = 0x10;
|
||||
dev->pci_conf[0][0x02] = 0x64;
|
||||
dev->pci_conf[0][0x03] = 0x05;
|
||||
} else {
|
||||
/* Atlas, Elite, Consumer II */
|
||||
dev->pci_conf[0][0x00] = 0x4a;
|
||||
dev->pci_conf[0][0x01] = 0x10;
|
||||
dev->pci_conf[0][0x02] = 0x0a;
|
||||
@@ -776,31 +774,10 @@ stpc_setup(stpc_t *dev)
|
||||
dev->pci_conf[0][0x0b] = 0x06;
|
||||
|
||||
/* ISA Bridge */
|
||||
if (dev->local & STPC_ISAB_CLIENT) {
|
||||
/* Client */
|
||||
dev->pci_conf[1][0x00] = 0x0e;
|
||||
dev->pci_conf[1][0x01] = 0x10;
|
||||
dev->pci_conf[1][0x02] = 0xcc;
|
||||
dev->pci_conf[1][0x03] = 0x55;
|
||||
} else if (dev->local & STPC_ISAB_CONSUMER2) {
|
||||
/* Consumer II */
|
||||
dev->pci_conf[1][0x00] = 0x4a;
|
||||
dev->pci_conf[1][0x01] = 0x10;
|
||||
dev->pci_conf[1][0x02] = 0x0b;
|
||||
dev->pci_conf[1][0x03] = 0x02;
|
||||
} else if (dev->local & STPC_IDE_ATLAS) {
|
||||
/* Atlas */
|
||||
dev->pci_conf[1][0x00] = 0x4a;
|
||||
dev->pci_conf[1][0x01] = 0x10;
|
||||
dev->pci_conf[1][0x02] = 0x10;
|
||||
dev->pci_conf[1][0x03] = 0x02;
|
||||
} else {
|
||||
/* Elite */
|
||||
dev->pci_conf[1][0x00] = 0x4a;
|
||||
dev->pci_conf[1][0x01] = 0x10;
|
||||
dev->pci_conf[1][0x02] = 0x1a;
|
||||
dev->pci_conf[1][0x03] = 0x02;
|
||||
}
|
||||
dev->pci_conf[1][0x00] = dev->local >> 16;
|
||||
dev->pci_conf[1][0x01] = dev->local >> 24;
|
||||
dev->pci_conf[1][0x02] = dev->local;
|
||||
dev->pci_conf[1][0x03] = dev->local >> 8;
|
||||
|
||||
dev->pci_conf[1][0x04] = 0x0f;
|
||||
|
||||
@@ -812,24 +789,19 @@ stpc_setup(stpc_t *dev)
|
||||
|
||||
/* NOTE: This is an erratum in the STPC Atlas programming manual, the programming manuals for the other
|
||||
STPC chipsets say 0x80, which is indeed multi-function (as the STPC Atlas programming manual
|
||||
indicates as well, and Windows 2000 also issues a 0x7B STOP error if it is 0x40. */
|
||||
indicates as well), and Windows 2000 also issues a 0x7B STOP error if it is 0x40. */
|
||||
dev->pci_conf[1][0x0e] = /*0x40*/ 0x80;
|
||||
|
||||
/* IDE */
|
||||
if (dev->local & STPC_ISAB_CLIENT) {
|
||||
dev->pci_conf[2][0x00] = 0x0e;
|
||||
dev->pci_conf[2][0x01] = 0x10;
|
||||
} else {
|
||||
dev->pci_conf[2][0x00] = 0x4a;
|
||||
dev->pci_conf[2][0x01] = 0x10;
|
||||
}
|
||||
dev->pci_conf[2][0x00] = dev->local >> 16;
|
||||
dev->pci_conf[2][0x01] = dev->local >> 24;
|
||||
|
||||
if (dev->local & STPC_IDE_ATLAS) {
|
||||
dev->pci_conf[2][0x02] = 0x28;
|
||||
dev->pci_conf[2][0x03] = 0x02;
|
||||
if (dev->local == STPC_ATLAS) {
|
||||
dev->pci_conf[2][0x02] = 0x28;
|
||||
dev->pci_conf[2][0x03] = 0x02;
|
||||
} else {
|
||||
dev->pci_conf[2][0x02] = dev->pci_conf[1][0x02];
|
||||
dev->pci_conf[2][0x03] = dev->pci_conf[1][0x03];
|
||||
dev->pci_conf[2][0x02] = dev->pci_conf[1][0x02];
|
||||
dev->pci_conf[2][0x03] = dev->pci_conf[1][0x03];
|
||||
}
|
||||
|
||||
dev->pci_conf[2][0x06] = 0x80;
|
||||
@@ -841,7 +813,7 @@ stpc_setup(stpc_t *dev)
|
||||
|
||||
/* NOTE: This is an erratum in the STPC Atlas programming manual, the programming manuals for the other
|
||||
STPC chipsets say 0x80, which is indeed multi-function (as the STPC Atlas programming manual
|
||||
indicates as well, and Windows 2000 also issues a 0x7B STOP error if it is 0x40. */
|
||||
indicates as well), and Windows 2000 also issues a 0x7B STOP error if it is 0x40. */
|
||||
dev->pci_conf[2][0x0e] = /*0x40*/ 0x80;
|
||||
|
||||
dev->pci_conf[2][0x10] = 0x01;
|
||||
@@ -861,22 +833,22 @@ stpc_setup(stpc_t *dev)
|
||||
|
||||
/* USB */
|
||||
if (dev->usb) {
|
||||
dev->pci_conf[3][0x00] = 0x4a;
|
||||
dev->pci_conf[3][0x01] = 0x10;
|
||||
dev->pci_conf[3][0x02] = 0x30;
|
||||
dev->pci_conf[3][0x03] = 0x02;
|
||||
dev->pci_conf[3][0x00] = dev->local >> 16;
|
||||
dev->pci_conf[3][0x01] = dev->local >> 24;
|
||||
dev->pci_conf[3][0x02] = 0x30;
|
||||
dev->pci_conf[3][0x03] = 0x02;
|
||||
|
||||
dev->pci_conf[3][0x06] = 0x80;
|
||||
dev->pci_conf[3][0x07] = 0x02;
|
||||
dev->pci_conf[3][0x06] = 0x80;
|
||||
dev->pci_conf[3][0x07] = 0x02;
|
||||
|
||||
dev->pci_conf[3][0x09] = 0x10;
|
||||
dev->pci_conf[3][0x0a] = 0x03;
|
||||
dev->pci_conf[3][0x0b] = 0x0c;
|
||||
dev->pci_conf[3][0x09] = 0x10;
|
||||
dev->pci_conf[3][0x0a] = 0x03;
|
||||
dev->pci_conf[3][0x0b] = 0x0c;
|
||||
|
||||
/* NOTE: This is an erratum in the STPC Atlas programming manual, the programming manuals for the other
|
||||
STPC chipsets say 0x80, which is indeed multi-function (as the STPC Atlas programming manual
|
||||
indicates as well, and Windows 2000 also issues a 0x7B STOP error if it is 0x40. */
|
||||
dev->pci_conf[3][0x0e] = /*0x40*/ 0x80;
|
||||
indicates as well), and Windows 2000 also issues a 0x7B STOP error if it is 0x40. */
|
||||
dev->pci_conf[3][0x0e] = /*0x40*/ 0x80;
|
||||
}
|
||||
|
||||
/* PCI setup */
|
||||
@@ -912,11 +884,10 @@ stpc_init(const device_t *info)
|
||||
|
||||
pci_add_card(PCI_ADD_NORTHBRIDGE, stpc_nb_read, stpc_nb_write, dev);
|
||||
dev->ide_slot = pci_add_card(PCI_ADD_SOUTHBRIDGE, stpc_isab_read, stpc_isab_write, dev);
|
||||
if (dev->local & STPC_IDE_ATLAS)
|
||||
dev->ide_slot = pci_add_card(PCI_ADD_SOUTHBRIDGE, stpc_ide_read, stpc_ide_write, dev);
|
||||
if (dev->local & STPC_USB) {
|
||||
dev->usb = device_add(&usb_device);
|
||||
pci_add_card(PCI_ADD_SOUTHBRIDGE, stpc_usb_read, stpc_usb_write, dev);
|
||||
if (dev->local == STPC_ATLAS) {
|
||||
dev->ide_slot = pci_add_card(PCI_ADD_SOUTHBRIDGE, stpc_ide_read, stpc_ide_write, dev);
|
||||
dev->usb = device_add(&usb_device);
|
||||
pci_add_card(PCI_ADD_SOUTHBRIDGE, stpc_usb_read, stpc_usb_write, dev);
|
||||
}
|
||||
|
||||
dev->bm[0] = device_add_inst(&sff8038i_device, 1);
|
||||
@@ -978,43 +949,43 @@ stpc_lpt_handlers(stpc_lpt_t *dev, uint8_t val)
|
||||
uint8_t old_addr = (dev->reg1 & 0x03), new_addr = (val & 0x03);
|
||||
|
||||
switch (old_addr) {
|
||||
case 0x1:
|
||||
lpt3_remove();
|
||||
break;
|
||||
case 0x1:
|
||||
lpt3_remove();
|
||||
break;
|
||||
|
||||
case 0x2:
|
||||
lpt1_remove();
|
||||
break;
|
||||
case 0x2:
|
||||
lpt1_remove();
|
||||
break;
|
||||
|
||||
case 0x3:
|
||||
lpt2_remove();
|
||||
break;
|
||||
case 0x3:
|
||||
lpt2_remove();
|
||||
break;
|
||||
}
|
||||
|
||||
switch (new_addr) {
|
||||
case 0x1:
|
||||
stpc_log("STPC: Remapping parallel port to LPT3\n");
|
||||
lpt3_init(0x3bc);
|
||||
break;
|
||||
case 0x1:
|
||||
stpc_log("STPC: Remapping parallel port to LPT3\n");
|
||||
lpt3_init(0x3bc);
|
||||
break;
|
||||
|
||||
case 0x2:
|
||||
stpc_log("STPC: Remapping parallel port to LPT1\n");
|
||||
lpt1_init(0x378);
|
||||
break;
|
||||
case 0x2:
|
||||
stpc_log("STPC: Remapping parallel port to LPT1\n");
|
||||
lpt1_init(0x378);
|
||||
break;
|
||||
|
||||
case 0x3:
|
||||
stpc_log("STPC: Remapping parallel port to LPT2\n");
|
||||
lpt2_init(0x278);
|
||||
break;
|
||||
case 0x3:
|
||||
stpc_log("STPC: Remapping parallel port to LPT2\n");
|
||||
lpt2_init(0x278);
|
||||
break;
|
||||
|
||||
default:
|
||||
stpc_log("STPC: Disabling parallel port\n");
|
||||
break;
|
||||
default:
|
||||
stpc_log("STPC: Disabling parallel port\n");
|
||||
break;
|
||||
}
|
||||
|
||||
dev->reg1 = (val & 0x08);
|
||||
dev->reg1 |= new_addr;
|
||||
dev->reg1 |= 0x84; /* reserved bits that default to 1 - hardwired? */
|
||||
dev->reg1 |= 0x84; /* reserved bits that default to 1; hardwired? */
|
||||
}
|
||||
|
||||
|
||||
@@ -1024,22 +995,22 @@ stpc_lpt_write(uint16_t addr, uint8_t val, void *priv)
|
||||
stpc_lpt_t *dev = (stpc_lpt_t *) priv;
|
||||
|
||||
if (dev->unlocked < 2) {
|
||||
/* Cheat a little bit: in reality, any write to any
|
||||
I/O port is supposed to reset the unlock counter. */
|
||||
if ((addr == 0x3f0) && (val == 0x55))
|
||||
dev->unlocked++;
|
||||
else
|
||||
dev->unlocked = 0;
|
||||
/* Cheat a little bit: in reality, any write to any
|
||||
I/O port is supposed to reset the unlock counter. */
|
||||
if ((addr == 0x3f0) && (val == 0x55))
|
||||
dev->unlocked++;
|
||||
else
|
||||
dev->unlocked = 0;
|
||||
} else if (addr == 0x3f0) {
|
||||
if (val == 0xaa)
|
||||
dev->unlocked = 0;
|
||||
else
|
||||
dev->offset = val;
|
||||
if (val == 0xaa)
|
||||
dev->unlocked = 0;
|
||||
else
|
||||
dev->offset = val;
|
||||
} else if (dev->offset == 1) {
|
||||
/* dev->reg1 is set by stpc_lpt_handlers */
|
||||
stpc_lpt_handlers(dev, val);
|
||||
/* dev->reg1 is set by stpc_lpt_handlers */
|
||||
stpc_lpt_handlers(dev, val);
|
||||
} else if (dev->offset == 4) {
|
||||
dev->reg4 = (val & 0x03);
|
||||
dev->reg4 = (val & 0x03);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1092,7 +1063,7 @@ const device_t stpc_client_device =
|
||||
{
|
||||
"STPC Client",
|
||||
DEVICE_PCI,
|
||||
STPC_NB_CLIENT | STPC_ISAB_CLIENT,
|
||||
STPC_CLIENT,
|
||||
stpc_init,
|
||||
stpc_close,
|
||||
stpc_reset,
|
||||
@@ -1106,7 +1077,7 @@ const device_t stpc_consumer2_device =
|
||||
{
|
||||
"STPC Consumer-II",
|
||||
DEVICE_PCI,
|
||||
STPC_ISAB_CONSUMER2,
|
||||
STPC_CONSUMER2,
|
||||
stpc_init,
|
||||
stpc_close,
|
||||
stpc_reset,
|
||||
@@ -1120,7 +1091,7 @@ const device_t stpc_elite_device =
|
||||
{
|
||||
"STPC Elite",
|
||||
DEVICE_PCI,
|
||||
0,
|
||||
STPC_ELITE,
|
||||
stpc_init,
|
||||
stpc_close,
|
||||
stpc_reset,
|
||||
@@ -1134,7 +1105,7 @@ const device_t stpc_atlas_device =
|
||||
{
|
||||
"STPC Atlas",
|
||||
DEVICE_PCI,
|
||||
STPC_IDE_ATLAS | STPC_USB,
|
||||
STPC_ATLAS,
|
||||
stpc_init,
|
||||
stpc_close,
|
||||
stpc_reset,
|
||||
|
||||
@@ -423,7 +423,7 @@ pipc_read(int func, int addr, void *priv)
|
||||
ret |= 0x10;
|
||||
}
|
||||
}
|
||||
else if ((func <= (pm_func + 2)) && !(dev->pci_isa_regs[0x85] & ((func == (pm_func + 1)) ? 0x04 : 0x08))) /* AC97 / MC97 */
|
||||
else if ((func <= (pm_func + 2)) && !(dev->pci_isa_regs[0x85] & ((func == (pm_func + 1)) ? 0x04 : 0x08)) && 0) /* AC97 / MC97; temporarily disabled while unimplemented */
|
||||
ret = dev->ac97_regs[func - pm_func - 1][addr];
|
||||
|
||||
pipc_log("PIPC: read(%d, %02X) = %02X\n", func, addr, ret);
|
||||
@@ -805,6 +805,9 @@ pipc_write(int func, int addr, uint8_t val, void *priv)
|
||||
if ((func == (pm_func + 2)) && ((addr == 0x4a) || (addr == 0x4b) || (dev->pci_isa_regs[0x85] & 0x08)))
|
||||
return;
|
||||
|
||||
if (1) /* temporarily disabled while unimplemented */
|
||||
return;
|
||||
|
||||
switch (addr) {
|
||||
default:
|
||||
dev->ac97_regs[func - pm_func - 1][addr] = val;
|
||||
@@ -868,7 +871,9 @@ pipc_init(const device_t *info)
|
||||
|
||||
dev->nvr = device_add(&via_nvr_device);
|
||||
|
||||
if (dev->local >= VIA_PIPC_596A)
|
||||
if (dev->local >= VIA_PIPC_686B)
|
||||
dev->smbus = device_add(&via_smbus_device);
|
||||
else if (dev->local >= VIA_PIPC_596A)
|
||||
dev->smbus = device_add(&piix4_smbus_device);
|
||||
|
||||
if (dev->local >= VIA_PIPC_596A)
|
||||
|
||||
@@ -626,6 +626,7 @@ load_machine(void)
|
||||
}
|
||||
}
|
||||
cpu_s = (CPU *) &cpu_f->cpus[cpu];
|
||||
cpu_override = config_get_int(cat, "cpu_override", 0);
|
||||
|
||||
cpu_waitstates = config_get_int(cat, "cpu_waitstates", 0);
|
||||
|
||||
@@ -1798,6 +1799,10 @@ save_machine(void)
|
||||
config_set_string(cat, "cpu_family", (char *) cpu_f->internal_name);
|
||||
config_set_int(cat, "cpu_speed", cpu_f->cpus[cpu].rspeed);
|
||||
config_set_double(cat, "cpu_multi", cpu_f->cpus[cpu].multi);
|
||||
if (cpu_override)
|
||||
config_set_int(cat, "cpu_override", cpu_override);
|
||||
else
|
||||
config_delete_var(cat, "cpu_override");
|
||||
|
||||
/* Forwards compatibility with the previous CPU model system. */
|
||||
config_delete_var(cat, "cpu_manufacturer");
|
||||
|
||||
@@ -151,6 +151,7 @@ uint32_t smbase = 0x30000;
|
||||
|
||||
cpu_family_t *cpu_f;
|
||||
CPU *cpu_s;
|
||||
int cpu_override;
|
||||
int cpu_effective;
|
||||
int cpu_multi;
|
||||
double cpu_dmulti;
|
||||
@@ -331,6 +332,9 @@ cpu_get_family(const char *internal_name)
|
||||
uint8_t
|
||||
cpu_is_eligible(const cpu_family_t *cpu_family, int cpu, int machine)
|
||||
{
|
||||
if (cpu_override > 1) /* full override */
|
||||
return 1;
|
||||
|
||||
/* Get machine. */
|
||||
const machine_t *machine_s = &machines[machine];
|
||||
|
||||
@@ -344,6 +348,9 @@ cpu_is_eligible(const cpu_family_t *cpu_family, int cpu, int machine)
|
||||
if (!(cpu_family->package & packages)) /* package type */
|
||||
return 0;
|
||||
|
||||
if (cpu_override) /* partial override */
|
||||
return 1;
|
||||
|
||||
const CPU *cpu_s = &cpu_family->cpus[cpu];
|
||||
|
||||
if (machine_s->cpu_block & cpu_s->cpu_type) /* CPU type blocklist */
|
||||
|
||||
@@ -375,6 +375,7 @@ extern const cpu_family_t cpu_families[];
|
||||
extern const cpu_legacy_machine_t cpu_legacy_table[];
|
||||
extern cpu_family_t *cpu_f;
|
||||
extern CPU *cpu_s;
|
||||
extern int cpu_override;
|
||||
|
||||
extern int cpu_isintel;
|
||||
extern int cpu_iscyrix;
|
||||
|
||||
@@ -976,9 +976,9 @@ static const cpu_legacy_table_t cpus_pcjr[] = {
|
||||
};
|
||||
|
||||
static const cpu_legacy_table_t cpus_europc[] = {
|
||||
{"8088", 4772728, 1},
|
||||
{"8088", 7159092, 1},
|
||||
{"8088", 9545456, 1},
|
||||
{"8088_europc", 4772728, 1},
|
||||
{"8088_europc", 7159092, 1},
|
||||
{"8088_europc", 9545456, 1},
|
||||
{NULL, 0, 0}
|
||||
};
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#include <86box/86box.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/io.h>
|
||||
#include <86box/smbus.h>
|
||||
#include <86box/i2c.h>
|
||||
#include <86box/hwm.h>
|
||||
|
||||
|
||||
@@ -41,17 +41,14 @@ typedef struct {
|
||||
uint16_t regs[32];
|
||||
uint8_t addr_register;
|
||||
|
||||
uint8_t smbus_addr;
|
||||
uint8_t i2c_addr, i2c_state;
|
||||
} gl518sm_t;
|
||||
|
||||
|
||||
static uint8_t gl518sm_smbus_read_byte(uint8_t addr, void *priv);
|
||||
static uint8_t gl518sm_smbus_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv);
|
||||
static uint16_t gl518sm_smbus_read_word_cmd(uint8_t addr, uint8_t cmd, void *priv);
|
||||
static uint8_t gl518sm_i2c_start(void *bus, uint8_t addr, uint8_t read, void *priv);
|
||||
static uint8_t gl518sm_i2c_read(void *bus, uint8_t addr, void *priv);
|
||||
static uint16_t gl518sm_read(gl518sm_t *dev, uint8_t reg);
|
||||
static void gl518sm_smbus_write_byte(uint8_t addr, uint8_t val, void *priv);
|
||||
static void gl518sm_smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val, void *priv);
|
||||
static void gl518sm_smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val, void *priv);
|
||||
static uint8_t gl518sm_i2c_write(void *bus, uint8_t addr, uint8_t data, void *priv);
|
||||
static uint8_t gl518sm_write(gl518sm_t *dev, uint8_t reg, uint16_t val);
|
||||
static void gl518sm_reset(gl518sm_t *dev);
|
||||
|
||||
@@ -81,41 +78,45 @@ gl518sm_remap(gl518sm_t *dev, uint8_t addr)
|
||||
{
|
||||
gl518sm_log("GL518SM: remapping to SMBus %02Xh\n", addr);
|
||||
|
||||
smbus_removehandler(dev->smbus_addr, 1,
|
||||
gl518sm_smbus_read_byte, gl518sm_smbus_read_byte_cmd, gl518sm_smbus_read_word_cmd, NULL,
|
||||
gl518sm_smbus_write_byte, gl518sm_smbus_write_byte_cmd, gl518sm_smbus_write_word_cmd, NULL,
|
||||
dev);
|
||||
i2c_removehandler(i2c_smbus, dev->i2c_addr, 1, gl518sm_i2c_start, gl518sm_i2c_read, gl518sm_i2c_write, NULL, dev);
|
||||
|
||||
if (addr < 0x80) smbus_sethandler(addr, 1,
|
||||
gl518sm_smbus_read_byte, gl518sm_smbus_read_byte_cmd, gl518sm_smbus_read_word_cmd, NULL,
|
||||
gl518sm_smbus_write_byte, gl518sm_smbus_write_byte_cmd, gl518sm_smbus_write_word_cmd, NULL,
|
||||
dev);
|
||||
if (addr < 0x80)
|
||||
i2c_sethandler(i2c_smbus, addr, 1, gl518sm_i2c_start, gl518sm_i2c_read, gl518sm_i2c_write, NULL, dev);
|
||||
|
||||
dev->smbus_addr = addr;
|
||||
dev->i2c_addr = addr;
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
gl518sm_smbus_read_byte(uint8_t addr, void *priv)
|
||||
gl518sm_i2c_start(void *bus, uint8_t addr, uint8_t read, void *priv)
|
||||
{
|
||||
gl518sm_t *dev = (gl518sm_t *) priv;
|
||||
return gl518sm_read(dev, dev->addr_register);
|
||||
|
||||
dev->i2c_state = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
gl518sm_smbus_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv)
|
||||
gl518sm_i2c_read(void *bus, uint8_t addr, void *priv)
|
||||
{
|
||||
gl518sm_t *dev = (gl518sm_t *) priv;
|
||||
return gl518sm_read(dev, cmd);
|
||||
}
|
||||
uint16_t read = gl518sm_read(dev, dev->addr_register);
|
||||
uint8_t ret = 0;
|
||||
|
||||
if (dev->i2c_state == 0)
|
||||
dev->i2c_state = 1;
|
||||
|
||||
static uint16_t
|
||||
gl518sm_smbus_read_word_cmd(uint8_t addr, uint8_t cmd, void *priv)
|
||||
{
|
||||
gl518sm_t *dev = (gl518sm_t *) priv;
|
||||
return gl518sm_read(dev, cmd);
|
||||
if ((dev->i2c_state == 1) && (dev->addr_register >= 0x07) && (dev->addr_register <= 0x0c)) { /* two-byte registers: read MSB first */
|
||||
dev->i2c_state = 2;
|
||||
ret = read >> 8;
|
||||
} else {
|
||||
ret = read;
|
||||
dev->addr_register++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -157,37 +158,36 @@ gl518sm_read(gl518sm_t *dev, uint8_t reg)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Duplicate the low byte to the high byte on single-byte registers, although real hardware behavior is undefined. */
|
||||
if ((reg < 0x07) || (reg > 0x0c))
|
||||
ret |= ret << 8;
|
||||
|
||||
gl518sm_log("GL518SM: read(%02X) = %04X\n", reg, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gl518sm_smbus_write_byte(uint8_t addr, uint8_t val, void *priv)
|
||||
static uint8_t
|
||||
gl518sm_i2c_write(void *bus, uint8_t addr, uint8_t data, void *priv)
|
||||
{
|
||||
gl518sm_t *dev = (gl518sm_t *) priv;
|
||||
dev->addr_register = val;
|
||||
}
|
||||
|
||||
switch (dev->i2c_state++) {
|
||||
case 0:
|
||||
dev->addr_register = data;
|
||||
break;
|
||||
|
||||
static void
|
||||
gl518sm_smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val, void *priv)
|
||||
{
|
||||
gl518sm_t *dev = (gl518sm_t *) priv;
|
||||
gl518sm_write(dev, cmd, val);
|
||||
}
|
||||
case 1:
|
||||
gl518sm_write(dev, dev->addr_register, (gl518sm_read(dev, dev->addr_register) & 0xff00) | data);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
gl518sm_write(dev, dev->addr_register, (gl518sm_read(dev, dev->addr_register) << 8) | data);
|
||||
break;
|
||||
|
||||
static void
|
||||
gl518sm_smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val, void *priv)
|
||||
{
|
||||
gl518sm_t *dev = (gl518sm_t *) priv;
|
||||
gl518sm_write(dev, cmd, val);
|
||||
default:
|
||||
dev->i2c_state = 3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -23,22 +23,19 @@
|
||||
#include <wchar.h>
|
||||
#include <86box/86box.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/smbus.h>
|
||||
#include <86box/i2c.h>
|
||||
#include <86box/hwm.h>
|
||||
|
||||
|
||||
#define LM75_TEMP_TO_REG(t) ((t) << 8)
|
||||
|
||||
|
||||
static uint8_t lm75_smbus_read_byte(uint8_t addr, void *priv);
|
||||
static uint8_t lm75_smbus_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv);
|
||||
static uint16_t lm75_smbus_read_word_cmd(uint8_t addr, uint8_t cmd, void *priv);
|
||||
static void lm75_smbus_write_byte(uint8_t addr, uint8_t val, void *priv);
|
||||
static void lm75_smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val, void *priv);
|
||||
static void lm75_smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val, void *priv);
|
||||
static uint8_t lm75_i2c_start(void *bus, uint8_t addr, uint8_t read, void *priv);
|
||||
static uint8_t lm75_i2c_read(void *bus, uint8_t addr, void *priv);
|
||||
static uint8_t lm75_i2c_write(void *bus, uint8_t addr, uint8_t data, void *priv);
|
||||
static void lm75_reset(lm75_t *dev);
|
||||
|
||||
|
||||
#define ENABLE_LM75_LOG 1
|
||||
#ifdef ENABLE_LM75_LOG
|
||||
int lm75_do_log = ENABLE_LM75_LOG;
|
||||
|
||||
@@ -64,61 +61,67 @@ lm75_remap(lm75_t *dev, uint8_t addr)
|
||||
{
|
||||
lm75_log("LM75: remapping to SMBus %02Xh\n", addr);
|
||||
|
||||
if (dev->smbus_addr < 0x80) smbus_removehandler(dev->smbus_addr, 1,
|
||||
lm75_smbus_read_byte, lm75_smbus_read_byte_cmd, lm75_smbus_read_word_cmd, NULL,
|
||||
lm75_smbus_write_byte, lm75_smbus_write_byte_cmd, lm75_smbus_write_word_cmd, NULL,
|
||||
dev);
|
||||
if (dev->i2c_addr < 0x80)
|
||||
i2c_removehandler(i2c_smbus, dev->i2c_addr, 1, lm75_i2c_start, lm75_i2c_read, lm75_i2c_write, NULL, dev);
|
||||
|
||||
if (addr < 0x80) smbus_sethandler(addr, 1,
|
||||
lm75_smbus_read_byte, lm75_smbus_read_byte_cmd, lm75_smbus_read_word_cmd, NULL,
|
||||
lm75_smbus_write_byte, lm75_smbus_write_byte_cmd, lm75_smbus_write_word_cmd, NULL,
|
||||
dev);
|
||||
if (addr < 0x80)
|
||||
i2c_sethandler(i2c_smbus, addr, 1, lm75_i2c_start, lm75_i2c_read, lm75_i2c_write, NULL, dev);
|
||||
|
||||
dev->smbus_addr = addr;
|
||||
dev->i2c_addr = addr;
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
lm75_smbus_read_byte(uint8_t addr, void *priv)
|
||||
lm75_i2c_start(void *bus, uint8_t addr, uint8_t read, void *priv)
|
||||
{
|
||||
lm75_t *dev = (lm75_t *) priv;
|
||||
return lm75_read(dev, dev->addr_register);
|
||||
|
||||
dev->i2c_state = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
lm75_smbus_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv)
|
||||
lm75_i2c_read(void *bus, uint8_t addr, void *priv)
|
||||
{
|
||||
lm75_t *dev = (lm75_t *) priv;
|
||||
return lm75_read(dev, cmd);
|
||||
}
|
||||
uint8_t ret = 0;
|
||||
|
||||
static uint16_t
|
||||
lm75_smbus_read_word_cmd(uint8_t addr, uint8_t cmd, void *priv)
|
||||
{
|
||||
lm75_t *dev = (lm75_t *) priv;
|
||||
uint8_t rethi = 0;
|
||||
uint8_t retlo = 0;
|
||||
if (dev->i2c_state == 0)
|
||||
dev->i2c_state = 1;
|
||||
|
||||
switch (cmd & 0x3) {
|
||||
case 0x0: /* temperature */
|
||||
rethi = lm75_read(dev, 0x0);
|
||||
retlo = lm75_read(dev, 0x1);
|
||||
break;
|
||||
case 0x1: /* configuration */
|
||||
rethi = retlo = lm75_read(dev, 0x2);
|
||||
break;
|
||||
case 0x2: /* Thyst */
|
||||
rethi = lm75_read(dev, 0x3);
|
||||
retlo = lm75_read(dev, 0x4);
|
||||
break;
|
||||
case 0x3: /* Tos */
|
||||
rethi = lm75_read(dev, 0x5);
|
||||
retlo = lm75_read(dev, 0x6);
|
||||
break;
|
||||
/* The AS99127F hardware monitor uses the addresses of its LM75 devices
|
||||
to access some of its proprietary registers. Pass this operation on to
|
||||
the main monitor address through an internal I2C call, if necessary. */
|
||||
if ((dev->addr_register > 0x7) && ((dev->addr_register & 0xf8) != 0x50) && (dev->as99127f_i2c_addr < 0x80)) {
|
||||
i2c_start(i2c_smbus, dev->as99127f_i2c_addr, 1);
|
||||
i2c_write(i2c_smbus, dev->as99127f_i2c_addr, dev->addr_register);
|
||||
ret = i2c_read(i2c_smbus, dev->as99127f_i2c_addr);
|
||||
i2c_stop(i2c_smbus, dev->as99127f_i2c_addr);
|
||||
} else {
|
||||
switch (dev->addr_register & 0x3) {
|
||||
case 0x0: /* temperature */
|
||||
ret = lm75_read(dev, (dev->i2c_state == 1) ? 0x0 : 0x1);
|
||||
break;
|
||||
|
||||
case 0x1: /* configuration */
|
||||
ret = lm75_read(dev, 0x2);
|
||||
break;
|
||||
|
||||
case 0x2: /* Thyst */
|
||||
ret = lm75_read(dev, (dev->i2c_state == 1) ? 0x3 : 0x4);
|
||||
break;
|
||||
case 0x3: /* Tos */
|
||||
ret = lm75_read(dev, (dev->i2c_state == 1) ? 0x5 : 0x6);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (retlo << 8) | rethi; /* byte-swapped for some reason */
|
||||
if (++dev->i2c_state > 2)
|
||||
dev->i2c_state = 2;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -127,12 +130,7 @@ lm75_read(lm75_t *dev, uint8_t reg)
|
||||
{
|
||||
uint8_t ret;
|
||||
|
||||
/* The AS99127F hardware monitor uses the addresses of its LM75 devices
|
||||
to access some of its proprietary registers. Pass this operation on to
|
||||
the main monitor address through an internal SMBus call, if necessary. */
|
||||
if ((reg > 0x7) && ((reg & 0xf8) != 0x50) && (dev->as99127f_smbus_addr < 0x80))
|
||||
ret = smbus_read_byte_cmd(dev->as99127f_smbus_addr, reg);
|
||||
else if ((reg & 0x7) == 0x0) /* temperature high byte */
|
||||
if ((reg & 0x7) == 0x0) /* temperature high byte */
|
||||
ret = LM75_TEMP_TO_REG(dev->values->temperatures[dev->local >> 8]) >> 8;
|
||||
else if ((reg & 0x7) == 0x1) /* temperature low byte */
|
||||
ret = LM75_TEMP_TO_REG(dev->values->temperatures[dev->local >> 8]);
|
||||
@@ -145,47 +143,54 @@ lm75_read(lm75_t *dev, uint8_t reg)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
lm75_smbus_write_byte(uint8_t addr, uint8_t val, void *priv)
|
||||
static uint8_t
|
||||
lm75_i2c_write(void *bus, uint8_t addr, uint8_t data, void *priv)
|
||||
{
|
||||
lm75_t *dev = (lm75_t *) priv;
|
||||
dev->addr_register = val;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
lm75_smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val, void *priv)
|
||||
{
|
||||
lm75_t *dev = (lm75_t *) priv;
|
||||
lm75_write(dev, cmd, val);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
lm75_smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val, void *priv)
|
||||
{
|
||||
lm75_t *dev = (lm75_t *) priv;
|
||||
uint8_t valhi = (val >> 8);
|
||||
uint8_t vallo = (val & 0xff);
|
||||
|
||||
switch (cmd & 0x3) {
|
||||
case 0x0: /* temperature */
|
||||
lm75_write(dev, 0x0, valhi);
|
||||
lm75_write(dev, 0x1, vallo);
|
||||
break;
|
||||
case 0x1: /* configuration */
|
||||
lm75_write(dev, 0x2, vallo);
|
||||
break;
|
||||
case 0x2: /* Thyst */
|
||||
lm75_write(dev, 0x3, valhi);
|
||||
lm75_write(dev, 0x4, vallo);
|
||||
break;
|
||||
case 0x3: /* Tos */
|
||||
lm75_write(dev, 0x5, valhi);
|
||||
lm75_write(dev, 0x6, vallo);
|
||||
break;
|
||||
break;
|
||||
if ((dev->i2c_state > 2) || ((dev->i2c_state == 2) && ((dev->addr_register & 0x3) == 0x1))) {
|
||||
return 0;
|
||||
} else if (dev->i2c_state == 0) {
|
||||
dev->i2c_state = 1;
|
||||
/* Linux lm75.c driver relies on the address register not changing if bit 2 is set. */
|
||||
if ((dev->as99127f_i2c_addr < 0x80) || !(data & 0x04))
|
||||
dev->addr_register = data;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* The AS99127F hardware monitor uses the addresses of its LM75 devices
|
||||
to access some of its proprietary registers. Pass this operation on to
|
||||
the main monitor address through an internal I2C call, if necessary. */
|
||||
if ((dev->addr_register > 0x7) && ((dev->addr_register & 0xf8) != 0x50) && (dev->as99127f_i2c_addr < 0x80)) {
|
||||
i2c_start(i2c_smbus, dev->as99127f_i2c_addr, 0);
|
||||
i2c_write(i2c_smbus, dev->as99127f_i2c_addr, dev->addr_register);
|
||||
i2c_write(i2c_smbus, dev->as99127f_i2c_addr, data);
|
||||
i2c_stop(i2c_smbus, dev->as99127f_i2c_addr);
|
||||
return 1;
|
||||
} else {
|
||||
switch (dev->addr_register & 0x3) {
|
||||
case 0x0: /* temperature */
|
||||
lm75_write(dev, (dev->i2c_state == 1) ? 0x0 : 0x1, data);
|
||||
break;
|
||||
|
||||
case 0x1: /* configuration */
|
||||
lm75_write(dev, 0x2, data);
|
||||
break;
|
||||
|
||||
case 0x2: /* Thyst */
|
||||
lm75_write(dev, (dev->i2c_state == 1) ? 0x3 : 0x4, data);
|
||||
break;
|
||||
|
||||
case 0x3: /* Tos */
|
||||
lm75_write(dev, (dev->i2c_state == 1) ? 0x5 : 0x6, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (dev->i2c_state == 1)
|
||||
dev->i2c_state = 2;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -194,14 +199,6 @@ lm75_write(lm75_t *dev, uint8_t reg, uint8_t val)
|
||||
{
|
||||
lm75_log("LM75: write(%02X, %02X)\n", reg, val);
|
||||
|
||||
/* The AS99127F hardware monitor uses the addresses of its LM75 devices
|
||||
to access some of its proprietary registers. Pass this operation on to
|
||||
the main monitor address through an internal SMBus call, if necessary. */
|
||||
if ((reg > 0x7) && ((reg & 0xf8) != 0x50) && (dev->as99127f_smbus_addr < 0x80)) {
|
||||
smbus_write_byte_cmd(dev->as99127f_smbus_addr, reg, val);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t reg_idx = (reg & 0x7);
|
||||
|
||||
if ((reg_idx <= 0x1) || (reg_idx == 0x7))
|
||||
@@ -247,7 +244,7 @@ lm75_init(const device_t *info)
|
||||
hwm_values.temperatures[dev->local >> 8] = 30;
|
||||
dev->values = &hwm_values;
|
||||
|
||||
dev->as99127f_smbus_addr = 0x80;
|
||||
dev->as99127f_i2c_addr = 0x80;
|
||||
|
||||
lm75_reset(dev);
|
||||
|
||||
|
||||
@@ -25,11 +25,11 @@
|
||||
#include <86box/device.h>
|
||||
#include <86box/io.h>
|
||||
#include "cpu.h"
|
||||
#include <86box/smbus.h>
|
||||
#include <86box/i2c.h>
|
||||
#include <86box/hwm.h>
|
||||
|
||||
|
||||
#define LM78_SMBUS 0x010000
|
||||
#define LM78_I2C 0x010000
|
||||
#define LM78_W83781D 0x020000
|
||||
#define LM78_AS99127F_REV1 0x040000
|
||||
#define LM78_AS99127F_REV2 0x080000
|
||||
@@ -56,19 +56,16 @@ typedef struct {
|
||||
uint8_t addr_register;
|
||||
uint8_t data_register;
|
||||
|
||||
uint8_t smbus_addr;
|
||||
uint8_t i2c_addr, i2c_state;
|
||||
} lm78_t;
|
||||
|
||||
|
||||
static uint8_t lm78_i2c_start(void *bus, uint8_t addr, uint8_t read, void *priv);
|
||||
static uint8_t lm78_isa_read(uint16_t port, void *priv);
|
||||
static uint8_t lm78_smbus_read_byte(uint8_t addr, void *priv);
|
||||
static uint8_t lm78_smbus_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv);
|
||||
static uint16_t lm78_smbus_read_word_cmd(uint8_t addr, uint8_t cmd, void *priv);
|
||||
static uint8_t lm78_i2c_read(void *bus, uint8_t addr, void *priv);
|
||||
static uint8_t lm78_read(lm78_t *dev, uint8_t reg, uint8_t bank);
|
||||
static void lm78_isa_write(uint16_t port, uint8_t val, void *priv);
|
||||
static void lm78_smbus_write_byte(uint8_t addr, uint8_t val, void *priv);
|
||||
static void lm78_smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val, void *priv);
|
||||
static void lm78_smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val, void *priv);
|
||||
static uint8_t lm78_i2c_write(void *bus, uint8_t addr, uint8_t data, void *priv);
|
||||
static uint8_t lm78_write(lm78_t *dev, uint8_t reg, uint8_t val, uint8_t bank);
|
||||
static void lm78_reset(lm78_t *dev, uint8_t initialization);
|
||||
|
||||
@@ -98,34 +95,40 @@ lm78_remap(lm78_t *dev, uint8_t addr)
|
||||
{
|
||||
lm75_t *lm75;
|
||||
|
||||
if (!(dev->local & LM78_SMBUS)) return;
|
||||
if (!(dev->local & LM78_I2C)) return;
|
||||
|
||||
lm78_log("LM78: remapping to SMBus %02Xh\n", addr);
|
||||
|
||||
smbus_removehandler(dev->smbus_addr, 1,
|
||||
lm78_smbus_read_byte, lm78_smbus_read_byte_cmd, lm78_smbus_read_word_cmd, NULL,
|
||||
lm78_smbus_write_byte, lm78_smbus_write_byte_cmd, lm78_smbus_write_word_cmd, NULL,
|
||||
dev);
|
||||
i2c_removehandler(i2c_smbus, dev->i2c_addr, 1, lm78_i2c_start, lm78_i2c_read, lm78_i2c_write, NULL, dev);
|
||||
|
||||
if (addr < 0x80) smbus_sethandler(addr, 1,
|
||||
lm78_smbus_read_byte, lm78_smbus_read_byte_cmd, lm78_smbus_read_word_cmd, NULL,
|
||||
lm78_smbus_write_byte, lm78_smbus_write_byte_cmd, lm78_smbus_write_word_cmd, NULL,
|
||||
dev);
|
||||
if (addr < 0x80)
|
||||
i2c_sethandler(i2c_smbus, addr, 1, lm78_i2c_start, lm78_i2c_read, lm78_i2c_write, NULL, dev);
|
||||
|
||||
dev->smbus_addr = addr;
|
||||
dev->i2c_addr = addr;
|
||||
|
||||
if (dev->local & LM78_AS99127F) {
|
||||
/* Store the main SMBus address on the LM75 devices to ensure reads/writes
|
||||
/* Store the main I2C address on the LM75 devices to ensure reads/writes
|
||||
to the AS99127F's proprietary registers are passed through to this side. */
|
||||
for (uint8_t i = 0; i <= 1; i++) {
|
||||
lm75 = device_get_priv(dev->lm75[i]);
|
||||
if (lm75)
|
||||
lm75->as99127f_smbus_addr = dev->smbus_addr;
|
||||
lm75->as99127f_i2c_addr = dev->i2c_addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
lm78_i2c_start(void *bus, uint8_t addr, uint8_t read, void *priv)
|
||||
{
|
||||
lm78_t *dev = (lm78_t *) priv;
|
||||
|
||||
dev->i2c_state = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
lm78_isa_read(uint16_t port, void *priv)
|
||||
{
|
||||
@@ -159,26 +162,11 @@ lm78_isa_read(uint16_t port, void *priv)
|
||||
|
||||
|
||||
static uint8_t
|
||||
lm78_smbus_read_byte(uint8_t addr, void *priv)
|
||||
lm78_i2c_read(void *bus, uint8_t addr, void *priv)
|
||||
{
|
||||
lm78_t *dev = (lm78_t *) priv;
|
||||
return lm78_read(dev, dev->addr_register, LM78_WINBOND_BANK);
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
lm78_smbus_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv)
|
||||
{
|
||||
lm78_t *dev = (lm78_t *) priv;
|
||||
return lm78_read(dev, cmd, LM78_WINBOND_BANK);
|
||||
}
|
||||
|
||||
|
||||
static uint16_t
|
||||
lm78_smbus_read_word_cmd(uint8_t addr, uint8_t cmd, void *priv)
|
||||
{
|
||||
lm78_t *dev = (lm78_t *) priv;
|
||||
return (lm78_read(dev, cmd, LM78_WINBOND_BANK) << 8) | lm78_read(dev, cmd, LM78_WINBOND_BANK);
|
||||
return lm78_read(dev, dev->addr_register++, LM78_WINBOND_BANK);
|
||||
}
|
||||
|
||||
|
||||
@@ -264,27 +252,18 @@ lm78_isa_write(uint16_t port, uint8_t val, void *priv)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
lm78_smbus_write_byte(uint8_t addr, uint8_t val, void *priv)
|
||||
static uint8_t
|
||||
lm78_i2c_write(void *bus, uint8_t addr, uint8_t val, void *priv)
|
||||
{
|
||||
lm78_t *dev = (lm78_t *) priv;
|
||||
dev->addr_register = val;
|
||||
}
|
||||
|
||||
if (dev->i2c_state == 0) {
|
||||
dev->i2c_state = 1;
|
||||
dev->addr_register = val;
|
||||
} else
|
||||
lm78_write(dev, dev->addr_register++, val, LM78_WINBOND_BANK);
|
||||
|
||||
static void
|
||||
lm78_smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val, void *priv)
|
||||
{
|
||||
lm78_t *dev = (lm78_t *) priv;
|
||||
lm78_write(dev, cmd, val, LM78_WINBOND_BANK);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
lm78_smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val, void *priv)
|
||||
{
|
||||
lm78_t *dev = (lm78_t *) priv;
|
||||
lm78_write(dev, cmd, val, LM78_WINBOND_BANK);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -354,13 +333,13 @@ lm78_write(lm78_t *dev, uint8_t reg, uint8_t val, uint8_t bank)
|
||||
|
||||
switch (reg) {
|
||||
case 0x40:
|
||||
if (val & 0x80) /* INITIALIZATION bit resets all registers except main SMBus address */
|
||||
if (val & 0x80) /* INITIALIZATION bit resets all registers except main I2C address */
|
||||
lm78_reset(dev, 1);
|
||||
break;
|
||||
|
||||
case 0x48:
|
||||
/* set main SMBus address */
|
||||
if (dev->local & LM78_SMBUS)
|
||||
/* set main I2C address */
|
||||
if (dev->local & LM78_I2C)
|
||||
lm78_remap(dev, dev->regs[0x48] & 0x7f);
|
||||
break;
|
||||
|
||||
@@ -376,8 +355,8 @@ lm78_write(lm78_t *dev, uint8_t reg, uint8_t val, uint8_t bank)
|
||||
break;
|
||||
|
||||
case 0x4a:
|
||||
/* set LM75 SMBus addresses (Winbond only) */
|
||||
if (dev->local & LM78_SMBUS) {
|
||||
/* set LM75 I2C addresses (Winbond only) */
|
||||
if (dev->local & LM78_I2C) {
|
||||
for (uint8_t i = 0; i <= 1; i++) {
|
||||
lm75 = device_get_priv(dev->lm75[i]);
|
||||
if (!lm75)
|
||||
@@ -412,10 +391,10 @@ lm78_reset(lm78_t *dev, uint8_t initialization)
|
||||
dev->regs[0x40] = 0x08;
|
||||
dev->regs[0x46] = 0x40;
|
||||
dev->regs[0x47] = 0x50;
|
||||
if (dev->local & LM78_SMBUS) {
|
||||
if (!initialization) /* don't reset main SMBus address if the reset was triggered by the INITIALIZATION bit */
|
||||
dev->smbus_addr = 0x2d;
|
||||
dev->regs[0x48] = dev->smbus_addr;
|
||||
if (dev->local & LM78_I2C) {
|
||||
if (!initialization) /* don't reset main I2C address if the reset was triggered by the INITIALIZATION bit */
|
||||
dev->i2c_addr = 0x2d;
|
||||
dev->regs[0x48] = dev->i2c_addr;
|
||||
if (dev->local & LM78_WINBOND)
|
||||
dev->regs[0x4a] = 0x01;
|
||||
} else {
|
||||
@@ -469,7 +448,7 @@ lm78_reset(lm78_t *dev, uint8_t initialization)
|
||||
dev->regs[0x49] = 0x40;
|
||||
}
|
||||
|
||||
lm78_remap(dev, dev->smbus_addr);
|
||||
lm78_remap(dev, dev->i2c_addr);
|
||||
}
|
||||
|
||||
|
||||
@@ -519,7 +498,7 @@ lm78_init(const device_t *info)
|
||||
|
||||
/* Set chip-specific default values. */
|
||||
if (dev->local & LM78_AS99127F) {
|
||||
/* AS99127: different -12V Rin value (bruteforced) */
|
||||
/* AS99127: different -12V Rin value (bruteforced) */
|
||||
defaults.voltages[5] = LM78_NEG_VOLTAGE(12000, 2400);
|
||||
} else if (dev->local & LM78_W83782D) {
|
||||
/* W83782D: different negative voltage formula */
|
||||
@@ -536,7 +515,7 @@ lm78_init(const device_t *info)
|
||||
dev->lm75[i] = (device_t *) malloc(sizeof(device_t));
|
||||
memcpy(dev->lm75[i], &lm75_w83781d_device, sizeof(device_t));
|
||||
dev->lm75[i]->local = (i + 1) << 8;
|
||||
if (dev->local & LM78_SMBUS)
|
||||
if (dev->local & LM78_I2C)
|
||||
dev->lm75[i]->local |= 0x48 + i;
|
||||
device_add(dev->lm75[i]);
|
||||
} else {
|
||||
@@ -558,7 +537,7 @@ lm78_init(const device_t *info)
|
||||
const device_t lm78_device = {
|
||||
"National Semiconductor LM78 Hardware Monitor",
|
||||
DEVICE_ISA,
|
||||
0x290 | LM78_SMBUS,
|
||||
0x290 | LM78_I2C,
|
||||
lm78_init, lm78_close, NULL,
|
||||
{ NULL }, NULL, NULL,
|
||||
NULL
|
||||
@@ -569,19 +548,19 @@ const device_t lm78_device = {
|
||||
const device_t w83781d_device = {
|
||||
"Winbond W83781D Hardware Monitor",
|
||||
DEVICE_ISA,
|
||||
0x290 | LM78_SMBUS | LM78_W83781D,
|
||||
0x290 | LM78_I2C | LM78_W83781D,
|
||||
lm78_init, lm78_close, NULL,
|
||||
{ NULL }, NULL, NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
/* The ASUS AS99127F is a customized W83781D with no ISA interface (SMBus
|
||||
/* The ASUS AS99127F is a customized W83781D with no ISA interface (I2C
|
||||
only), added proprietary registers and different chip/vendor IDs. */
|
||||
const device_t as99127f_device = {
|
||||
"ASUS AS99127F Rev. 1 Hardware Monitor",
|
||||
DEVICE_ISA,
|
||||
LM78_SMBUS | LM78_AS99127F_REV1,
|
||||
LM78_I2C | LM78_AS99127F_REV1,
|
||||
lm78_init, lm78_close, NULL,
|
||||
{ NULL }, NULL, NULL,
|
||||
NULL
|
||||
@@ -592,7 +571,7 @@ const device_t as99127f_device = {
|
||||
const device_t as99127f_rev2_device = {
|
||||
"ASUS AS99127F Rev. 2 Hardware Monitor",
|
||||
DEVICE_ISA,
|
||||
LM78_SMBUS | LM78_AS99127F_REV2,
|
||||
LM78_I2C | LM78_AS99127F_REV2,
|
||||
lm78_init, lm78_close, NULL,
|
||||
{ NULL }, NULL, NULL,
|
||||
NULL
|
||||
@@ -603,7 +582,7 @@ const device_t as99127f_rev2_device = {
|
||||
const device_t w83782d_device = {
|
||||
"Winbond W83782D Hardware Monitor",
|
||||
DEVICE_ISA,
|
||||
0x290 | LM78_SMBUS | LM78_W83782D,
|
||||
0x290 | LM78_I2C | LM78_W83782D,
|
||||
lm78_init, lm78_close, NULL,
|
||||
{ NULL }, NULL, NULL,
|
||||
NULL
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include <86box/86box.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/io.h>
|
||||
#include <86box/smbus.h>
|
||||
#include <86box/hwm.h>
|
||||
|
||||
|
||||
|
||||
329
src/device/i2c.c
Normal file
329
src/device/i2c.c
Normal file
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Implementation of the I2C bus and its operations.
|
||||
*
|
||||
*
|
||||
*
|
||||
* 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/86box.h>
|
||||
#include <86box/i2c.h>
|
||||
|
||||
|
||||
#define NADDRS 128 /* I2C supports 128 addresses */
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
|
||||
typedef struct _i2c_ {
|
||||
uint8_t (*start)(void *bus, uint8_t addr, uint8_t read, void *priv);
|
||||
uint8_t (*read)(void *bus, uint8_t addr, void *priv);
|
||||
uint8_t (*write)(void *bus, uint8_t addr, uint8_t data, void *priv);
|
||||
void (*stop)(void *bus, uint8_t addr, void *priv);
|
||||
|
||||
void *priv;
|
||||
|
||||
struct _i2c_ *prev, *next;
|
||||
} i2c_t;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
i2c_t *devices[NADDRS], *last[NADDRS];
|
||||
} i2c_bus_t;
|
||||
|
||||
|
||||
void *i2c_smbus;
|
||||
|
||||
|
||||
#ifdef ENABLE_I2C_LOG
|
||||
int i2c_do_log = ENABLE_I2C_LOG;
|
||||
|
||||
|
||||
static void
|
||||
i2c_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (i2c_do_log) {
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define i2c_log(fmt, ...)
|
||||
#endif
|
||||
|
||||
|
||||
void *
|
||||
i2c_addbus(char *name)
|
||||
{
|
||||
i2c_bus_t *bus = (i2c_bus_t *) malloc(sizeof(i2c_bus_t));
|
||||
memset(bus, 0, sizeof(i2c_bus_t));
|
||||
|
||||
bus->name = name;
|
||||
|
||||
return bus;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
i2c_removebus(void *bus_handle)
|
||||
{
|
||||
int c;
|
||||
i2c_t *p, *q;
|
||||
i2c_bus_t *bus = (i2c_bus_t *) bus_handle;
|
||||
|
||||
if (!bus_handle)
|
||||
return;
|
||||
|
||||
for (c = 0; c < NADDRS; c++) {
|
||||
p = bus->devices[c];
|
||||
if (!p)
|
||||
continue;
|
||||
while(p) {
|
||||
q = p->next;
|
||||
free(p);
|
||||
p = q;
|
||||
}
|
||||
}
|
||||
|
||||
free(bus);
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
i2c_getbusname(void *bus_handle)
|
||||
{
|
||||
i2c_bus_t *bus = (i2c_bus_t *) bus_handle;
|
||||
|
||||
if (!bus_handle)
|
||||
return(NULL);
|
||||
|
||||
return(bus->name);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
i2c_sethandler(void *bus_handle, uint8_t base, int size,
|
||||
uint8_t (*start)(void *bus, uint8_t addr, uint8_t read, void *priv),
|
||||
uint8_t (*read)(void *bus, uint8_t addr, void *priv),
|
||||
uint8_t (*write)(void *bus, uint8_t addr, uint8_t data, void *priv),
|
||||
void (*stop)(void *bus, uint8_t addr, void *priv),
|
||||
void *priv)
|
||||
{
|
||||
int c;
|
||||
i2c_t *p, *q = NULL;
|
||||
i2c_bus_t *bus = (i2c_bus_t *) bus_handle;
|
||||
|
||||
if (!bus_handle || ((base + size) > NADDRS))
|
||||
return;
|
||||
|
||||
for (c = 0; c < size; c++) {
|
||||
p = bus->last[base + c];
|
||||
q = (i2c_t *) malloc(sizeof(i2c_t));
|
||||
memset(q, 0, sizeof(i2c_t));
|
||||
if (p) {
|
||||
p->next = q;
|
||||
q->prev = p;
|
||||
} else {
|
||||
bus->devices[base + c] = q;
|
||||
q->prev = NULL;
|
||||
}
|
||||
|
||||
q->start = start;
|
||||
q->read = read;
|
||||
q->write = write;
|
||||
q->stop = stop;
|
||||
|
||||
q->priv = priv;
|
||||
q->next = NULL;
|
||||
|
||||
bus->last[base + c] = q;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
i2c_removehandler(void *bus_handle, uint8_t base, int size,
|
||||
uint8_t (*start)(void *bus, uint8_t addr, uint8_t read, void *priv),
|
||||
uint8_t (*read)(void *bus, uint8_t addr, void *priv),
|
||||
uint8_t (*write)(void *bus, uint8_t addr, uint8_t data, void *priv),
|
||||
void (*stop)(void *bus, uint8_t addr, void *priv),
|
||||
void *priv)
|
||||
{
|
||||
int c;
|
||||
i2c_t *p, *q;
|
||||
i2c_bus_t *bus = (i2c_bus_t *) bus_handle;
|
||||
|
||||
if (!bus_handle || ((base + size) > NADDRS))
|
||||
return;
|
||||
|
||||
for (c = 0; c < size; c++) {
|
||||
p = bus->devices[base + c];
|
||||
if (!p)
|
||||
continue;
|
||||
while(p) {
|
||||
q = p->next;
|
||||
if ((p->start == start) && (p->read == read) && (p->write == write) && (p->stop == stop) && (p->priv == priv)) {
|
||||
if (p->prev)
|
||||
p->prev->next = p->next;
|
||||
else
|
||||
bus->devices[base + c] = p->next;
|
||||
if (p->next)
|
||||
p->next->prev = p->prev;
|
||||
else
|
||||
bus->last[base + c] = p->prev;
|
||||
free(p);
|
||||
p = NULL;
|
||||
break;
|
||||
}
|
||||
p = q;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
i2c_handler(int set, void *bus_handle, uint8_t base, int size,
|
||||
uint8_t (*start)(void *bus, uint8_t addr, uint8_t read, void *priv),
|
||||
uint8_t (*read)(void *bus, uint8_t addr, void *priv),
|
||||
uint8_t (*write)(void *bus, uint8_t addr, uint8_t data, void *priv),
|
||||
void (*stop)(void *bus, uint8_t addr, void *priv),
|
||||
void *priv)
|
||||
{
|
||||
if (set)
|
||||
i2c_sethandler(bus_handle, base, size, start, read, write, stop, priv);
|
||||
else
|
||||
i2c_removehandler(bus_handle, base, size, start, read, write, stop, priv);
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
i2c_has_device(void *bus_handle, uint8_t addr)
|
||||
{
|
||||
i2c_bus_t *bus = (i2c_bus_t *) bus_handle;
|
||||
|
||||
if (!bus)
|
||||
return 0;
|
||||
|
||||
i2c_log("I2C: has_device(%s, %02X) = %d\n", bus->name, addr, !!bus->devices[addr]);
|
||||
|
||||
return(!!bus->devices[addr]);
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
i2c_start(void *bus_handle, uint8_t addr, uint8_t read)
|
||||
{
|
||||
uint8_t ret = 0;
|
||||
i2c_bus_t *bus = (i2c_bus_t *) bus_handle;
|
||||
i2c_t *p;
|
||||
|
||||
if (!bus)
|
||||
return(ret);
|
||||
|
||||
p = bus->devices[addr];
|
||||
if (p) {
|
||||
while(p) {
|
||||
if (p->start) {
|
||||
ret |= p->start(bus_handle, addr, read, p->priv);
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_log("I2C: start(%s, %02X)\n", bus->name, addr);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
i2c_read(void *bus_handle, uint8_t addr)
|
||||
{
|
||||
uint8_t ret = 0;
|
||||
i2c_bus_t *bus = (i2c_bus_t *) bus_handle;
|
||||
i2c_t *p;
|
||||
|
||||
if (!bus)
|
||||
return(ret);
|
||||
|
||||
p = bus->devices[addr];
|
||||
if (p) {
|
||||
while(p) {
|
||||
if (p->read) {
|
||||
ret = p->read(bus_handle, addr, p->priv);
|
||||
break;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_log("I2C: read(%s, %02X) = %02X\n", bus->name, addr, ret);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
i2c_write(void *bus_handle, uint8_t addr, uint8_t data)
|
||||
{
|
||||
uint8_t ret = 0;
|
||||
i2c_t *p;
|
||||
i2c_bus_t *bus = (i2c_bus_t *) bus_handle;
|
||||
|
||||
if (!bus)
|
||||
return(ret);
|
||||
|
||||
p = bus->devices[addr];
|
||||
if (p) {
|
||||
while(p) {
|
||||
if (p->write) {
|
||||
ret |= p->write(bus_handle, addr, data, p->priv);
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_log("I2C: write(%s, %02X, %02X) = %d\n", bus->name, addr, data, ret);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
i2c_stop(void *bus_handle, uint8_t addr)
|
||||
{
|
||||
i2c_bus_t *bus = (i2c_bus_t *) bus_handle;
|
||||
i2c_t *p;
|
||||
|
||||
if (!bus)
|
||||
return;
|
||||
|
||||
p = bus->devices[addr];
|
||||
if (p) {
|
||||
while(p) {
|
||||
if (p->stop) {
|
||||
p->stop(bus_handle, addr, p->priv);
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_log("I2C: stop(%s, %02X)\n", bus->name, addr);
|
||||
}
|
||||
145
src/device/i2c_eeprom.c
Normal file
145
src/device/i2c_eeprom.c
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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 the 24Cxx series of I2C EEPROMs.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: RichardG, <richardg867@gmail.com>
|
||||
*
|
||||
* Copyright 2020 RichardG.
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#define HAVE_STDARG_H
|
||||
#include <wchar.h>
|
||||
#include <86box/86box.h>
|
||||
#include <86box/i2c.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
void *i2c;
|
||||
uint8_t addr, *data, writable;
|
||||
|
||||
uint32_t addr_mask, addr_register;
|
||||
uint8_t addr_len, addr_pos;
|
||||
} i2c_eeprom_t;
|
||||
|
||||
|
||||
#ifdef ENABLE_I2C_EEPROM_LOG
|
||||
int i2c_eeprom_do_log = ENABLE_I2C_EEPROM_LOG;
|
||||
|
||||
|
||||
static void
|
||||
i2c_eeprom_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (i2c_eeprom_do_log) {
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define i2c_eeprom_log(fmt, ...)
|
||||
#endif
|
||||
|
||||
|
||||
uint8_t
|
||||
i2c_eeprom_start(void *bus, uint8_t addr, uint8_t read, void *priv)
|
||||
{
|
||||
i2c_eeprom_t *dev = (i2c_eeprom_t *) priv;
|
||||
|
||||
i2c_eeprom_log("I2C EEPROM %s %02X: start()\n", i2c_getbusname(dev->i2c), dev->addr);
|
||||
|
||||
dev->addr_pos = 0;
|
||||
dev->addr_register = (addr << dev->addr_len) & dev->addr_mask;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
i2c_eeprom_read(void *bus, uint8_t addr, void *priv)
|
||||
{
|
||||
i2c_eeprom_t *dev = (i2c_eeprom_t *) priv;
|
||||
uint8_t ret = dev->data[dev->addr_register];
|
||||
|
||||
i2c_eeprom_log("I2C EEPROM %s %02X: read(%06X) = %02X\n", i2c_getbusname(dev->i2c), dev->addr, dev->addr_register, ret);
|
||||
if (++dev->addr_register > dev->addr_mask) /* roll-over */
|
||||
dev->addr_register = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
i2c_eeprom_write(void *bus, uint8_t addr, uint8_t data, void *priv)
|
||||
{
|
||||
i2c_eeprom_t *dev = (i2c_eeprom_t *) priv;
|
||||
|
||||
if (dev->addr_pos < dev->addr_len) {
|
||||
dev->addr_register <<= 8;
|
||||
dev->addr_register |= data;
|
||||
dev->addr_register &= (1 << dev->addr_len) - 1;
|
||||
dev->addr_register |= addr << dev->addr_len;
|
||||
dev->addr_register &= dev->addr_mask;
|
||||
i2c_eeprom_log("I2C EEPROM %s %02X: write(address, %04X)\n", i2c_getbusname(dev->i2c), dev->addr, dev->addr_register);
|
||||
dev->addr_pos += 8;
|
||||
} else {
|
||||
i2c_eeprom_log("I2C EEPROM %s %02X: write(%06X, %02X) = %d\n", i2c_getbusname(dev->i2c), dev->addr, dev->addr_register, data, !!dev->writable);
|
||||
if (dev->writable)
|
||||
dev->data[dev->addr_register] = data;
|
||||
if (++dev->addr_register > dev->addr_mask) /* roll-over */
|
||||
dev->addr_register = 0;
|
||||
return dev->writable;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
i2c_eeprom_init(void *i2c, uint8_t addr, uint8_t *data, uint32_t size, uint8_t writable)
|
||||
{
|
||||
i2c_eeprom_t *dev = (i2c_eeprom_t *) malloc(sizeof(i2c_eeprom_t));
|
||||
memset(dev, 0, sizeof(i2c_eeprom_t));
|
||||
|
||||
size &= 0x7fffff; /* address space limit of 8 MB = 7 bits from I2C address + 16 bits */
|
||||
|
||||
i2c_eeprom_log("I2C EEPROM %s %02X: init(%d, %d)\n", i2c_getbusname(i2c), addr, size, writable);
|
||||
|
||||
dev->i2c = i2c;
|
||||
dev->addr = addr;
|
||||
dev->data = data;
|
||||
dev->writable = writable;
|
||||
|
||||
dev->addr_len = (size >= 4096) ? 16 : 8; /* use 16-bit addresses on 24C32 and above */
|
||||
dev->addr_mask = size - 1;
|
||||
|
||||
i2c_sethandler(dev->i2c, dev->addr & ~(dev->addr_mask >> dev->addr_len), (dev->addr_mask >> dev->addr_len) + 1, i2c_eeprom_start, i2c_eeprom_read, i2c_eeprom_write, NULL, dev);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
i2c_eeprom_close(void *dev_handle)
|
||||
{
|
||||
i2c_eeprom_t *dev = (i2c_eeprom_t *) dev_handle;
|
||||
|
||||
i2c_eeprom_log("I2C EEPROM %s %02X: close()\n", i2c_getbusname(dev->i2c), dev->addr);
|
||||
|
||||
i2c_removehandler(dev->i2c, dev->addr & ~(dev->addr_mask >> dev->addr_len), (dev->addr_mask >> dev->addr_len) + 1, i2c_eeprom_start, i2c_eeprom_read, i2c_eeprom_write, NULL, dev);
|
||||
|
||||
free(dev);
|
||||
}
|
||||
347
src/device/i2c_gpio.c
Normal file
347
src/device/i2c_gpio.c
Normal file
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* 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 a GPIO-based I2C device.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
|
||||
* RichardG, <richardg867@gmail.com>
|
||||
*
|
||||
* Copyright 2008-2020 Sarah Walker.
|
||||
* Copyright 2020 RichardG.
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#define HAVE_STDARG_H
|
||||
#include <wchar.h>
|
||||
#include <86box/86box.h>
|
||||
#include <86box/i2c.h>
|
||||
|
||||
|
||||
enum {
|
||||
TRANSMITTER_SLAVE = 1,
|
||||
TRANSMITTER_MASTER = 2
|
||||
};
|
||||
|
||||
enum {
|
||||
I2C_IDLE = 0,
|
||||
I2C_RECEIVE,
|
||||
I2C_RECEIVE_WAIT,
|
||||
I2C_TRANSMIT_START,
|
||||
I2C_TRANSMIT,
|
||||
I2C_ACKNOWLEDGE,
|
||||
I2C_NOTACKNOWLEDGE,
|
||||
I2C_TRANSACKNOWLEDGE,
|
||||
I2C_TRANSMIT_WAIT
|
||||
};
|
||||
|
||||
enum {
|
||||
SLAVE_IDLE = 0,
|
||||
SLAVE_RECEIVEADDR,
|
||||
SLAVE_RECEIVEDATA,
|
||||
SLAVE_SENDDATA,
|
||||
SLAVE_INVALID
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
char *bus_name;
|
||||
void *i2c;
|
||||
uint8_t scl, sda, receive_wait_sda, state, slave_state, slave_addr,
|
||||
slave_read, last_sda, pos, transmit, byte;
|
||||
} i2c_gpio_t;
|
||||
|
||||
|
||||
#ifdef ENABLE_I2C_GPIO_LOG
|
||||
int i2c_gpio_do_log = ENABLE_I2C_GPIO_LOG;
|
||||
|
||||
|
||||
static void
|
||||
i2c_gpio_log(int level, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (i2c_gpio_do_log >= level) {
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define i2c_gpio_log(fmt, ...)
|
||||
#endif
|
||||
|
||||
|
||||
void *
|
||||
i2c_gpio_init(char *bus_name)
|
||||
{
|
||||
i2c_gpio_t *dev = (i2c_gpio_t *) malloc(sizeof(i2c_gpio_t));
|
||||
memset(dev, 0, sizeof(i2c_gpio_t));
|
||||
|
||||
i2c_gpio_log(1, "I2C GPIO %s: init()\n", bus_name);
|
||||
|
||||
dev->bus_name = bus_name;
|
||||
dev->i2c = i2c_addbus(dev->bus_name);
|
||||
dev->scl = dev->sda = 1;
|
||||
dev->slave_addr = 0xff;
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
i2c_gpio_close(void *dev_handle)
|
||||
{
|
||||
i2c_gpio_t *dev = (i2c_gpio_t *) dev_handle;
|
||||
|
||||
i2c_gpio_log(1, "I2C GPIO %s: close()\n", dev->bus_name);
|
||||
|
||||
i2c_removebus(dev->i2c);
|
||||
|
||||
free(dev);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
i2c_gpio_next_byte(i2c_gpio_t *dev)
|
||||
{
|
||||
dev->byte = i2c_read(dev->i2c, dev->slave_addr);
|
||||
i2c_gpio_log(1, "I2C GPIO %s: Transmitting data %02X\n", dev->bus_name, dev->byte);
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
i2c_gpio_write(i2c_gpio_t *dev)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
switch (dev->slave_state) {
|
||||
case SLAVE_IDLE:
|
||||
i = dev->slave_addr;
|
||||
dev->slave_addr = dev->byte >> 1;
|
||||
dev->slave_read = dev->byte & 1;
|
||||
|
||||
i2c_gpio_log(1, "I2C GPIO %s: Initiating %s address %02X\n", dev->bus_name, dev->slave_read ? "read from" : "write to", dev->slave_addr);
|
||||
|
||||
if (!i2c_has_device(dev->i2c, dev->slave_addr) ||
|
||||
((i == 0xff) && !i2c_start(dev->i2c, dev->slave_addr, dev->slave_read))) { /* start only once per transfer */
|
||||
dev->slave_state = SLAVE_INVALID;
|
||||
dev->slave_addr = 0xff;
|
||||
return I2C_NOTACKNOWLEDGE;
|
||||
}
|
||||
|
||||
if (dev->slave_read) {
|
||||
dev->slave_state = SLAVE_SENDDATA;
|
||||
dev->transmit = TRANSMITTER_SLAVE;
|
||||
dev->byte = i2c_read(dev->i2c, dev->slave_addr);
|
||||
} else {
|
||||
dev->slave_state = SLAVE_RECEIVEADDR;
|
||||
dev->transmit = TRANSMITTER_MASTER;
|
||||
}
|
||||
break;
|
||||
|
||||
case SLAVE_RECEIVEADDR:
|
||||
i2c_gpio_log(1, "I2C GPIO %s: Receiving address %02X\n", dev->bus_name, dev->byte);
|
||||
dev->slave_state = dev->slave_read ? SLAVE_SENDDATA : SLAVE_RECEIVEDATA;
|
||||
if (!i2c_write(dev->i2c, dev->slave_addr, dev->byte))
|
||||
return I2C_NOTACKNOWLEDGE;
|
||||
break;
|
||||
|
||||
case SLAVE_RECEIVEDATA:
|
||||
i2c_gpio_log(1, "I2C GPIO %s: Receiving data %02X\n", dev->bus_name, dev->byte);
|
||||
if (!i2c_write(dev->i2c, dev->slave_addr, dev->byte))
|
||||
return I2C_NOTACKNOWLEDGE;
|
||||
break;
|
||||
|
||||
case SLAVE_INVALID:
|
||||
return I2C_NOTACKNOWLEDGE;
|
||||
}
|
||||
|
||||
return I2C_ACKNOWLEDGE;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
i2c_gpio_stop(i2c_gpio_t *dev)
|
||||
{
|
||||
i2c_gpio_log(1, "I2C GPIO %s: Stopping transfer\n", dev->bus_name);
|
||||
|
||||
if (dev->slave_addr != 0xff) /* don't stop if no transfer was in progress */
|
||||
i2c_stop(dev->i2c, dev->slave_addr);
|
||||
|
||||
dev->slave_addr = 0xff;
|
||||
dev->slave_state = SLAVE_IDLE;
|
||||
dev->transmit = TRANSMITTER_MASTER;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
i2c_gpio_set(void *dev_handle, uint8_t scl, uint8_t sda)
|
||||
{
|
||||
i2c_gpio_t *dev = (i2c_gpio_t *) dev_handle;
|
||||
|
||||
i2c_gpio_log(3, "I2C GPIO %s: scl=%d->%d sda=%d->%d last_valid_sda=%d state=%d\n", dev->bus_name, dev->scl, scl, dev->last_sda, sda, dev->sda, dev->state);
|
||||
|
||||
switch (dev->state) {
|
||||
case I2C_IDLE:
|
||||
if (scl && dev->last_sda && !sda) { /* start condition; dev->scl check breaks NCR SDMS */
|
||||
i2c_gpio_log(2, "I2C GPIO %s: Start condition received (from IDLE)\n", dev->bus_name);
|
||||
dev->state = I2C_RECEIVE;
|
||||
dev->pos = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case I2C_RECEIVE_WAIT:
|
||||
if (!dev->scl && scl)
|
||||
dev->state = I2C_RECEIVE;
|
||||
else if (!dev->scl && !scl && dev->last_sda && sda) /* workaround for repeated start condition on Windows XP DDC */
|
||||
dev->receive_wait_sda = 1;
|
||||
/* fall-through */
|
||||
|
||||
case I2C_RECEIVE:
|
||||
if (!dev->scl && scl) {
|
||||
dev->byte <<= 1;
|
||||
if (sda)
|
||||
dev->byte |= 1;
|
||||
else
|
||||
dev->byte &= 0xfe;
|
||||
if (++dev->pos == 8)
|
||||
dev->state = i2c_gpio_write(dev);
|
||||
} else if (dev->scl && scl) {
|
||||
if (sda && !dev->last_sda) { /* stop condition */
|
||||
i2c_gpio_log(2, "I2C GPIO %s: Stop condition received (from RECEIVE)\n", dev->bus_name);
|
||||
dev->state = I2C_IDLE;
|
||||
i2c_gpio_stop(dev);
|
||||
} else if (!sda && dev->last_sda) { /* start condition */
|
||||
i2c_gpio_log(2, "I2C GPIO %s: Start condition received (from RECEIVE)\n", dev->bus_name);
|
||||
dev->pos = 0;
|
||||
dev->slave_state = SLAVE_IDLE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case I2C_ACKNOWLEDGE:
|
||||
if (!dev->scl && scl) {
|
||||
i2c_gpio_log(2, "I2C GPIO %s: Acknowledging transfer to %02X\n", dev->bus_name, dev->slave_addr);
|
||||
sda = 0;
|
||||
dev->receive_wait_sda = 0; /* ack */
|
||||
dev->pos = 0;
|
||||
dev->state = (dev->transmit == TRANSMITTER_MASTER) ? I2C_RECEIVE_WAIT : I2C_TRANSMIT;
|
||||
}
|
||||
break;
|
||||
|
||||
case I2C_NOTACKNOWLEDGE:
|
||||
if (!dev->scl && scl) {
|
||||
i2c_gpio_log(2, "I2C GPIO %s: Not acknowledging transfer\n", dev->bus_name);
|
||||
sda = 1;
|
||||
dev->pos = 0;
|
||||
dev->state = I2C_IDLE;
|
||||
dev->slave_state = SLAVE_IDLE;
|
||||
}
|
||||
break;
|
||||
|
||||
case I2C_TRANSACKNOWLEDGE:
|
||||
if (!dev->scl && scl) {
|
||||
if (sda) { /* not acknowledged; must be end of transfer */
|
||||
i2c_gpio_log(2, "I2C GPIO %s: End of transfer\n", dev->bus_name);
|
||||
dev->state = I2C_IDLE;
|
||||
i2c_gpio_stop(dev);
|
||||
} else { /* next byte to transfer */
|
||||
dev->state = I2C_TRANSMIT_START;
|
||||
i2c_gpio_next_byte(dev);
|
||||
dev->pos = 0;
|
||||
i2c_gpio_log(2, "I2C GPIO %s: Next byte = %02X\n", dev->bus_name, dev->byte);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case I2C_TRANSMIT_WAIT:
|
||||
if (dev->scl && scl) {
|
||||
if (dev->last_sda && !sda) { /* start condition */
|
||||
i2c_gpio_next_byte(dev);
|
||||
dev->pos = 0;
|
||||
i2c_gpio_log(2, "I2C GPIO %s: Next byte = %02X\n", dev->bus_name, dev->byte);
|
||||
}
|
||||
if (!dev->last_sda && sda) { /* stop condition */
|
||||
i2c_gpio_log(2, "I2C GPIO %s: Stop condition received (from TRANSMIT_WAIT)\n", dev->bus_name);
|
||||
dev->state = I2C_IDLE;
|
||||
i2c_gpio_stop(dev);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case I2C_TRANSMIT_START:
|
||||
if (!dev->scl && scl)
|
||||
dev->state = I2C_TRANSMIT;
|
||||
if (dev->scl && scl && !dev->last_sda && sda) { /* stop condition */
|
||||
i2c_gpio_log(2, "I2C GPIO %s: Stop condition received (from TRANSMIT_START)\n", dev->bus_name);
|
||||
dev->state = I2C_IDLE;
|
||||
i2c_gpio_stop(dev);
|
||||
}
|
||||
/* fall-through */
|
||||
|
||||
case I2C_TRANSMIT:
|
||||
if (!dev->scl && scl) {
|
||||
dev->scl = scl;
|
||||
if (!dev->pos)
|
||||
i2c_gpio_log(2, "I2C GPIO %s: Transmit byte %02X\n", dev->bus_name, dev->byte);
|
||||
dev->sda = sda = dev->byte & 0x80;
|
||||
i2c_gpio_log(2, "I2C GPIO %s: Transmit bit %02X %d\n", dev->bus_name, dev->byte, dev->pos);
|
||||
dev->byte <<= 1;
|
||||
dev->pos++;
|
||||
return;
|
||||
}
|
||||
if (dev->scl && !scl && (dev->pos == 8)) {
|
||||
dev->state = I2C_TRANSACKNOWLEDGE;
|
||||
i2c_gpio_log(2, "I2C GPIO %s: Acknowledge mode\n", dev->bus_name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!dev->scl && scl)
|
||||
dev->sda = sda;
|
||||
dev->last_sda = sda;
|
||||
dev->scl = scl;
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
i2c_gpio_get_scl(void *dev_handle)
|
||||
{
|
||||
i2c_gpio_t *dev = (i2c_gpio_t *) dev_handle;
|
||||
return dev->scl;
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
i2c_gpio_get_sda(void *dev_handle)
|
||||
{
|
||||
i2c_gpio_t *dev = (i2c_gpio_t *) dev_handle;
|
||||
switch (dev->state) {
|
||||
case I2C_TRANSMIT:
|
||||
case I2C_ACKNOWLEDGE:
|
||||
return dev->sda;
|
||||
|
||||
case I2C_RECEIVE_WAIT:
|
||||
return dev->receive_wait_sda;
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
i2c_gpio_get_bus(void *dev_handle)
|
||||
{
|
||||
i2c_gpio_t *dev = (i2c_gpio_t *) dev_handle;
|
||||
return dev->i2c;
|
||||
}
|
||||
@@ -1,403 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Implement SMBus (System Management Bus) and its operations.
|
||||
*
|
||||
*
|
||||
*
|
||||
* 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/86box.h>
|
||||
#include <86box/smbus.h>
|
||||
|
||||
|
||||
#define NADDRS 128 /* SMBus supports 128 addresses */
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
|
||||
typedef struct _smbus_ {
|
||||
uint8_t (*read_byte)(uint8_t addr, void *priv);
|
||||
uint8_t (*read_byte_cmd)(uint8_t addr, uint8_t cmd, void *priv);
|
||||
uint16_t (*read_word_cmd)(uint8_t addr, uint8_t cmd, void *priv);
|
||||
uint8_t (*read_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv);
|
||||
|
||||
void (*write_byte)(uint8_t addr, uint8_t val, void *priv);
|
||||
void (*write_byte_cmd)(uint8_t addr, uint8_t cmd, uint8_t val, void *priv);
|
||||
void (*write_word_cmd)(uint8_t addr, uint8_t cmd, uint16_t val, void *priv);
|
||||
void (*write_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv);
|
||||
|
||||
void *priv;
|
||||
|
||||
struct _smbus_ *prev, *next;
|
||||
} smbus_t;
|
||||
|
||||
int smbus_initialized = 0;
|
||||
smbus_t *smbus[NADDRS], *smbus_last[NADDRS];
|
||||
|
||||
|
||||
#ifdef ENABLE_SMBUS_LOG
|
||||
int smbus_do_log = ENABLE_SMBUS_LOG;
|
||||
|
||||
|
||||
static void
|
||||
smbus_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (smbus_do_log) {
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define smbus_log(fmt, ...)
|
||||
#endif
|
||||
|
||||
|
||||
void
|
||||
smbus_init(void)
|
||||
{
|
||||
int c;
|
||||
smbus_t *p, *q;
|
||||
|
||||
if (!smbus_initialized) {
|
||||
for (c=0; c<NADDRS; c++)
|
||||
smbus[c] = smbus_last[c] = NULL;
|
||||
smbus_initialized = 1;
|
||||
}
|
||||
|
||||
for (c=0; c<NADDRS; c++) {
|
||||
if (smbus_last[c]) {
|
||||
/* Address c has at least one handler. */
|
||||
p = smbus_last[c];
|
||||
/* After this loop, p will have the pointer to the first handler. */
|
||||
while (p) {
|
||||
q = p->prev;
|
||||
free(p);
|
||||
p = q;
|
||||
}
|
||||
p = NULL;
|
||||
}
|
||||
|
||||
/* smbus[c] should be NULL. */
|
||||
smbus[c] = smbus_last[c] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
smbus_sethandler(uint8_t base, int size,
|
||||
uint8_t (*read_byte)(uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint16_t (*read_word_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint8_t (*read_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void (*write_byte)(uint8_t addr, uint8_t val, void *priv),
|
||||
void (*write_byte_cmd)(uint8_t addr, uint8_t cmd, uint8_t val, void *priv),
|
||||
void (*write_word_cmd)(uint8_t addr, uint8_t cmd, uint16_t val, void *priv),
|
||||
void (*write_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void *priv)
|
||||
{
|
||||
int c;
|
||||
smbus_t *p, *q = NULL;
|
||||
|
||||
if ((base + size) >= NADDRS)
|
||||
return;
|
||||
|
||||
for (c = 0; c < size; c++) {
|
||||
p = smbus_last[base + c];
|
||||
q = (smbus_t *) malloc(sizeof(smbus_t));
|
||||
memset(q, 0, sizeof(smbus_t));
|
||||
if (p) {
|
||||
p->next = q;
|
||||
q->prev = p;
|
||||
} else {
|
||||
smbus[base + c] = q;
|
||||
q->prev = NULL;
|
||||
}
|
||||
|
||||
q->read_byte = read_byte;
|
||||
q->read_byte_cmd = read_byte_cmd;
|
||||
q->read_word_cmd = read_word_cmd;
|
||||
q->read_block_cmd = read_block_cmd;
|
||||
|
||||
q->write_byte = write_byte;
|
||||
q->write_byte_cmd = write_byte_cmd;
|
||||
q->write_word_cmd = write_word_cmd;
|
||||
q->write_block_cmd = write_block_cmd;
|
||||
|
||||
q->priv = priv;
|
||||
q->next = NULL;
|
||||
|
||||
smbus_last[base + c] = q;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
smbus_removehandler(uint8_t base, int size,
|
||||
uint8_t (*read_byte)(uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint16_t (*read_word_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint8_t (*read_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void (*write_byte)(uint8_t addr, uint8_t val, void *priv),
|
||||
void (*write_byte_cmd)(uint8_t addr, uint8_t cmd, uint8_t val, void *priv),
|
||||
void (*write_word_cmd)(uint8_t addr, uint8_t cmd, uint16_t val, void *priv),
|
||||
void (*write_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void *priv)
|
||||
{
|
||||
int c;
|
||||
smbus_t *p, *q;
|
||||
|
||||
if ((base + size) >= NADDRS)
|
||||
return;
|
||||
|
||||
for (c = 0; c < size; c++) {
|
||||
p = smbus[base + c];
|
||||
if (!p)
|
||||
continue;
|
||||
while(p) {
|
||||
q = p->next;
|
||||
if ((p->read_byte == read_byte) && (p->read_byte_cmd == read_byte_cmd) &&
|
||||
(p->read_word_cmd == read_word_cmd) && (p->read_block_cmd == read_block_cmd) &&
|
||||
(p->write_byte == write_byte) && (p->write_byte_cmd == write_byte_cmd) &&
|
||||
(p->write_word_cmd == write_word_cmd) && (p->write_block_cmd == write_block_cmd) &&
|
||||
(p->priv == priv)) {
|
||||
if (p->prev)
|
||||
p->prev->next = p->next;
|
||||
else
|
||||
smbus[base + c] = p->next;
|
||||
if (p->next)
|
||||
p->next->prev = p->prev;
|
||||
else
|
||||
smbus_last[base + c] = p->prev;
|
||||
free(p);
|
||||
p = NULL;
|
||||
break;
|
||||
}
|
||||
p = q;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
smbus_handler(int set, uint8_t base, int size,
|
||||
uint8_t (*read_byte)(uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint16_t (*read_word_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint8_t (*read_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void (*write_byte)(uint8_t addr, uint8_t val, void *priv),
|
||||
void (*write_byte_cmd)(uint8_t addr, uint8_t cmd, uint8_t val, void *priv),
|
||||
void (*write_word_cmd)(uint8_t addr, uint8_t cmd, uint16_t val, void *priv),
|
||||
void (*write_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void *priv)
|
||||
{
|
||||
if (set)
|
||||
smbus_sethandler(base, size, read_byte, read_byte_cmd, read_word_cmd, read_block_cmd, write_byte, write_byte_cmd, write_word_cmd, write_block_cmd, priv);
|
||||
else
|
||||
smbus_removehandler(base, size, read_byte, read_byte_cmd, read_word_cmd, read_block_cmd, write_byte, write_byte_cmd, write_word_cmd, write_block_cmd, priv);
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
smbus_has_device(uint8_t addr)
|
||||
{
|
||||
return(!!smbus[addr]);
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
smbus_read_byte(uint8_t addr)
|
||||
{
|
||||
uint8_t ret = 0xff;
|
||||
smbus_t *p;
|
||||
int found = 0;
|
||||
|
||||
p = smbus[addr];
|
||||
if (p) {
|
||||
while(p) {
|
||||
if (p->read_byte) {
|
||||
ret &= p->read_byte(addr, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
smbus_log("SMBus: read_byte(%02X) = %02X\n", addr, ret);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
uint8_t
|
||||
smbus_read_byte_cmd(uint8_t addr, uint8_t cmd)
|
||||
{
|
||||
uint8_t ret = 0xff;
|
||||
smbus_t *p;
|
||||
int found = 0;
|
||||
|
||||
p = smbus[addr];
|
||||
if (p) {
|
||||
while(p) {
|
||||
if (p->read_byte_cmd) {
|
||||
ret &= p->read_byte_cmd(addr, cmd, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
smbus_log("SMBus: read_byte_cmd(%02X, %02X) = %02X\n", addr, cmd, ret);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
uint16_t
|
||||
smbus_read_word_cmd(uint8_t addr, uint8_t cmd)
|
||||
{
|
||||
uint16_t ret = 0xffff;
|
||||
smbus_t *p;
|
||||
int found = 0;
|
||||
|
||||
p = smbus[addr];
|
||||
if (p) {
|
||||
while(p) {
|
||||
if (p->read_word_cmd) {
|
||||
ret &= p->read_word_cmd(addr, cmd, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
smbus_log("SMBus: read_word_cmd(%02X, %02X) = %04X\n", addr, cmd, ret);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
uint8_t
|
||||
smbus_read_block_cmd(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len)
|
||||
{
|
||||
uint8_t ret = 0;
|
||||
smbus_t *p;
|
||||
int found = 0;
|
||||
|
||||
p = smbus[addr];
|
||||
if (p) {
|
||||
while(p) {
|
||||
if (p->read_block_cmd) {
|
||||
ret = MAX(ret, p->read_block_cmd(addr, cmd, data, len, p->priv));
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
smbus_log("SMBus: read_block_cmd(%02X, %02X) = %02X\n", addr, cmd, len);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
smbus_write_byte(uint8_t addr, uint8_t val)
|
||||
{
|
||||
smbus_t *p;
|
||||
int found = 0;
|
||||
|
||||
if (smbus[addr]) {
|
||||
p = smbus[addr];
|
||||
while(p) {
|
||||
if (p->write_byte) {
|
||||
p->write_byte(addr, val, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
smbus_log("SMBus: write_byte(%02X, %02X)\n", addr, val);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val)
|
||||
{
|
||||
smbus_t *p;
|
||||
int found = 0;
|
||||
|
||||
if (smbus[addr]) {
|
||||
p = smbus[addr];
|
||||
while(p) {
|
||||
if (p->write_byte_cmd) {
|
||||
p->write_byte_cmd(addr, cmd, val, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
smbus_log("SMBus: write_byte_cmd(%02X, %02X, %02X)\n", addr, cmd, val);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val)
|
||||
{
|
||||
smbus_t *p;
|
||||
int found = 0;
|
||||
|
||||
if (smbus[addr]) {
|
||||
p = smbus[addr];
|
||||
while(p) {
|
||||
if (p->write_word_cmd) {
|
||||
p->write_word_cmd(addr, cmd, val, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
smbus_log("SMBus: write_word_cmd(%02X, %02X, %04X)\n", addr, cmd, val);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
smbus_write_block_cmd(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len)
|
||||
{
|
||||
smbus_t *p;
|
||||
int found = 0;
|
||||
|
||||
p = smbus[addr];
|
||||
if (p) {
|
||||
while(p) {
|
||||
if (p->write_block_cmd) {
|
||||
p->write_block_cmd(addr, cmd, data, len, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
smbus_log("SMBus: write_block_cmd(%02X, %02X, %02X)\n", addr, cmd, len);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -25,7 +25,7 @@
|
||||
#include <86box/io.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/timer.h>
|
||||
#include <86box/smbus.h>
|
||||
#include <86box/i2c.h>
|
||||
#include <86box/smbus_piix4.h>
|
||||
|
||||
|
||||
@@ -59,22 +59,28 @@ smbus_piix4_read(uint16_t addr, void *priv)
|
||||
case 0x00:
|
||||
ret = dev->stat;
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
dev->index = 0; /* reading from this resets the block data index */
|
||||
ret = dev->ctl;
|
||||
break;
|
||||
|
||||
case 0x03:
|
||||
ret = dev->cmd;
|
||||
break;
|
||||
|
||||
case 0x04:
|
||||
ret = dev->addr;
|
||||
break;
|
||||
|
||||
case 0x05:
|
||||
ret = dev->data0;
|
||||
break;
|
||||
|
||||
case 0x06:
|
||||
ret = dev->data1;
|
||||
break;
|
||||
|
||||
case 0x07:
|
||||
ret = dev->data[dev->index++];
|
||||
if (dev->index >= SMBUS_PIIX4_BLOCK_DATA_SIZE)
|
||||
@@ -82,7 +88,7 @@ smbus_piix4_read(uint16_t addr, void *priv)
|
||||
break;
|
||||
}
|
||||
|
||||
smbus_piix4_log("SMBus PIIX4: read(%02x) = %02x\n", addr, ret);
|
||||
smbus_piix4_log("SMBus PIIX4: read(%02X) = %02x\n", addr, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -92,96 +98,210 @@ static void
|
||||
smbus_piix4_write(uint16_t addr, uint8_t val, void *priv)
|
||||
{
|
||||
smbus_piix4_t *dev = (smbus_piix4_t *) priv;
|
||||
uint8_t smbus_addr, smbus_read, prev_stat;
|
||||
uint16_t temp;
|
||||
uint8_t smbus_addr, cmd, read, block_len, prev_stat;
|
||||
uint16_t timer_bytes = 0, i;
|
||||
|
||||
smbus_piix4_log("SMBus PIIX4: write(%02x, %02x)\n", addr, val);
|
||||
smbus_piix4_log("SMBus PIIX4: write(%02X, %02X)\n", addr, val);
|
||||
|
||||
prev_stat = dev->next_stat;
|
||||
dev->next_stat = 0;
|
||||
dev->next_stat = 0x00;
|
||||
switch (addr - dev->io_base) {
|
||||
case 0x00:
|
||||
/* some status bits are reset by writing 1 to them */
|
||||
for (smbus_addr = 0x02; smbus_addr <= 0x10; smbus_addr <<= 1) {
|
||||
for (smbus_addr = 0x02; smbus_addr <= 0x10; smbus_addr <<= 1) { /* handle clearable bits */
|
||||
if (val & smbus_addr)
|
||||
dev->stat &= ~smbus_addr;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
dev->ctl = val & ~(0x40); /* START always reads 0 */
|
||||
dev->ctl = val & ((dev->local == SMBUS_VIA) ? 0x3f : 0x1f);
|
||||
if (val & 0x02) { /* cancel an in-progress command if KILL is set */
|
||||
/* cancel only if a command is in progress */
|
||||
if (prev_stat) {
|
||||
dev->stat = 0x10; /* raise FAILED */
|
||||
if (prev_stat) { /* cancel only if a command is in progress */
|
||||
timer_disable(&dev->response_timer);
|
||||
dev->stat = 0x10; /* raise FAILED */
|
||||
}
|
||||
}
|
||||
if (val & 0x40) { /* dispatch command if START is set */
|
||||
timer_bytes++; /* address */
|
||||
|
||||
smbus_addr = (dev->addr >> 1);
|
||||
if (!smbus_has_device(smbus_addr)) {
|
||||
/* raise DEV_ERR if no device is at this address */
|
||||
dev->next_stat = 0x4;
|
||||
read = dev->addr & 0x01;
|
||||
|
||||
/* Raise DEV_ERR if no device is at this address, or if the device returned NAK when starting the transfer. */
|
||||
if (!i2c_has_device(i2c_smbus, smbus_addr) || !i2c_start(i2c_smbus, smbus_addr, read)) {
|
||||
dev->next_stat = 0x04;
|
||||
break;
|
||||
}
|
||||
smbus_read = (dev->addr & 0x01);
|
||||
|
||||
/* decode the 3-bit command protocol */
|
||||
dev->next_stat = 0x2; /* raise INTER (command completed) by default */
|
||||
switch ((val >> 2) & 0x7) {
|
||||
dev->next_stat = 0x02; /* raise INTER (command completed) by default */
|
||||
|
||||
/* Decode the command protocol.
|
||||
VIA-specific modes (0x4 and [0x6:0xf]) are undocumented and required real hardware research. */
|
||||
cmd = (val >> 2) & 0xf;
|
||||
smbus_piix4_log("SMBus PIIX4: protocol=%X cmd=%02X data0=%02X data1=%02X\n", cmd, dev->cmd, dev->data0, dev->data1);
|
||||
switch (cmd) {
|
||||
case 0x0: /* quick R/W */
|
||||
break;
|
||||
|
||||
case 0x1: /* byte R/W */
|
||||
if (smbus_read)
|
||||
dev->data0 = smbus_read_byte(smbus_addr);
|
||||
else
|
||||
smbus_write_byte(smbus_addr, dev->data0);
|
||||
if (read) /* byte read */
|
||||
dev->data0 = i2c_read(i2c_smbus, smbus_addr);
|
||||
else /* byte write */
|
||||
i2c_write(i2c_smbus, smbus_addr, dev->data0);
|
||||
timer_bytes++;
|
||||
|
||||
break;
|
||||
|
||||
case 0x2: /* byte data R/W */
|
||||
if (smbus_read)
|
||||
dev->data0 = smbus_read_byte_cmd(smbus_addr, dev->cmd);
|
||||
else
|
||||
smbus_write_byte_cmd(smbus_addr, dev->cmd, dev->data0);
|
||||
/* command write */
|
||||
i2c_write(i2c_smbus, smbus_addr, dev->cmd);
|
||||
timer_bytes++;
|
||||
|
||||
if (read) /* byte read */
|
||||
dev->data0 = i2c_read(i2c_smbus, smbus_addr);
|
||||
else /* byte write */
|
||||
i2c_write(i2c_smbus, smbus_addr, dev->data0);
|
||||
timer_bytes++;
|
||||
|
||||
break;
|
||||
|
||||
case 0x3: /* word data R/W */
|
||||
if (smbus_read) {
|
||||
temp = smbus_read_word_cmd(smbus_addr, dev->cmd);
|
||||
dev->data0 = (temp & 0xFF);
|
||||
dev->data1 = (temp >> 8);
|
||||
} else {
|
||||
temp = ((dev->data1 << 8) | dev->data0);
|
||||
smbus_write_word_cmd(smbus_addr, dev->cmd, temp);
|
||||
/* command write */
|
||||
i2c_write(i2c_smbus, smbus_addr, dev->cmd);
|
||||
timer_bytes++;
|
||||
|
||||
if (read) { /* word read */
|
||||
dev->data0 = i2c_read(i2c_smbus, smbus_addr);
|
||||
dev->data1 = i2c_read(i2c_smbus, smbus_addr);
|
||||
} else { /* word write */
|
||||
i2c_write(i2c_smbus, smbus_addr, dev->data0);
|
||||
i2c_write(i2c_smbus, smbus_addr, dev->data1);
|
||||
}
|
||||
timer_bytes += 2;
|
||||
|
||||
break;
|
||||
|
||||
case 0x4: /* process call */
|
||||
if (dev->local != SMBUS_VIA) /* VIA only */
|
||||
goto unknown_protocol;
|
||||
|
||||
if (!read) { /* command write (only when writing) */
|
||||
i2c_write(i2c_smbus, smbus_addr, dev->cmd);
|
||||
timer_bytes++;
|
||||
}
|
||||
|
||||
/* fall-through */
|
||||
|
||||
case 0xc: /* I2C process call */
|
||||
if (!read) { /* word write (only when writing) */
|
||||
i2c_write(i2c_smbus, smbus_addr, dev->data0);
|
||||
i2c_write(i2c_smbus, smbus_addr, dev->data1);
|
||||
timer_bytes += 2;
|
||||
}
|
||||
|
||||
/* word read */
|
||||
dev->data0 = i2c_read(i2c_smbus, smbus_addr);
|
||||
dev->data1 = i2c_read(i2c_smbus, smbus_addr);
|
||||
timer_bytes += 2;
|
||||
|
||||
break;
|
||||
|
||||
case 0x5: /* block R/W */
|
||||
if (smbus_read)
|
||||
dev->data0 = smbus_read_block_cmd(smbus_addr, dev->cmd, dev->data, SMBUS_PIIX4_BLOCK_DATA_SIZE);
|
||||
else
|
||||
smbus_write_block_cmd(smbus_addr, dev->cmd, dev->data, dev->data0);
|
||||
timer_bytes++; /* count the SMBus length byte now */
|
||||
|
||||
/* fall-through */
|
||||
|
||||
case 0xd: /* I2C block R/W */
|
||||
i2c_write(i2c_smbus, smbus_addr, dev->cmd);
|
||||
timer_bytes++;
|
||||
|
||||
if (read) {
|
||||
/* block read [data0] (I2C) or [first byte] (SMBus) bytes */
|
||||
block_len = (cmd == 0x5) ? i2c_read(i2c_smbus, smbus_addr) : dev->data0;
|
||||
for (i = 0; i < block_len; i++)
|
||||
dev->data[i & SMBUS_PIIX4_BLOCK_DATA_MASK] = i2c_read(i2c_smbus, smbus_addr);
|
||||
} else {
|
||||
block_len = dev->data0;
|
||||
if (cmd == 0x5) /* send length [data0] as first byte on SMBus */
|
||||
i2c_write(i2c_smbus, smbus_addr, block_len);
|
||||
/* block write [data0] bytes */
|
||||
for (i = 0; i < block_len; i++) {
|
||||
if (!i2c_write(i2c_smbus, smbus_addr, dev->data[i & SMBUS_PIIX4_BLOCK_DATA_MASK]))
|
||||
break;
|
||||
}
|
||||
}
|
||||
timer_bytes += i;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
/* other command protocols have undefined behavior, but raise DEV_ERR to be safe */
|
||||
dev->next_stat = 0x4;
|
||||
case 0x6: /* I2C with 10-bit address */
|
||||
if (dev->local != SMBUS_VIA) /* VIA only */
|
||||
goto unknown_protocol;
|
||||
|
||||
/* command write */
|
||||
i2c_write(i2c_smbus, smbus_addr, dev->cmd);
|
||||
timer_bytes += 1;
|
||||
|
||||
/* fall-through */
|
||||
|
||||
case 0xe: /* I2C with 7-bit address */
|
||||
if (!read) { /* word write (only when writing) */
|
||||
i2c_write(i2c_smbus, smbus_addr, dev->data0);
|
||||
i2c_write(i2c_smbus, smbus_addr, dev->data1);
|
||||
timer_bytes += 2;
|
||||
}
|
||||
|
||||
/* block read [first byte] bytes */
|
||||
block_len = dev->data[0];
|
||||
for (i = 0; i < block_len; i++)
|
||||
dev->data[i & SMBUS_PIIX4_BLOCK_DATA_MASK] = i2c_read(i2c_smbus, smbus_addr);
|
||||
timer_bytes += i;
|
||||
|
||||
break;
|
||||
|
||||
case 0xf: /* universal */
|
||||
/* block write [data0] bytes */
|
||||
for (i = 0; i < dev->data0; i++) {
|
||||
if (!i2c_write(i2c_smbus, smbus_addr, dev->data[i & SMBUS_PIIX4_BLOCK_DATA_MASK]))
|
||||
break;
|
||||
}
|
||||
timer_bytes += i;
|
||||
|
||||
/* block read [data1] bytes */
|
||||
for (i = 0; i < dev->data1; i++)
|
||||
dev->data[i & SMBUS_PIIX4_BLOCK_DATA_MASK] = i2c_read(i2c_smbus, smbus_addr);
|
||||
timer_bytes += i;
|
||||
|
||||
break;
|
||||
|
||||
default: /* unknown */
|
||||
unknown_protocol:
|
||||
dev->next_stat = 0x04; /* raise DEV_ERR */
|
||||
timer_bytes = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Finish transfer. */
|
||||
i2c_stop(i2c_smbus, smbus_addr);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x03:
|
||||
dev->cmd = val;
|
||||
break;
|
||||
|
||||
case 0x04:
|
||||
dev->addr = val;
|
||||
break;
|
||||
|
||||
case 0x05:
|
||||
dev->data0 = val;
|
||||
break;
|
||||
|
||||
case 0x06:
|
||||
dev->data1 = val;
|
||||
break;
|
||||
|
||||
case 0x07:
|
||||
dev->data[dev->index++] = val;
|
||||
if (dev->index >= SMBUS_PIIX4_BLOCK_DATA_SIZE)
|
||||
@@ -189,11 +309,11 @@ smbus_piix4_write(uint16_t addr, uint8_t val, void *priv)
|
||||
break;
|
||||
}
|
||||
|
||||
/* if a status register update was given, dispatch it after 10ms to ensure nothing breaks */
|
||||
if (dev->next_stat) {
|
||||
dev->stat = 0x1; /* raise HOST_BUSY while waiting */
|
||||
if (dev->next_stat) { /* schedule dispatch of any pending status register update */
|
||||
dev->stat = 0x01; /* raise HOST_BUSY while waiting */
|
||||
timer_disable(&dev->response_timer);
|
||||
timer_set_delay_u64(&dev->response_timer, 10 * TIMER_USEC);
|
||||
/* delay = ((half clock for start + half clock for stop) + (bytes * (8 bits + ack))) * 60us period measured on real VIA 686B */
|
||||
timer_set_delay_u64(&dev->response_timer, (1 + (timer_bytes * 9)) * 60 * TIMER_USEC);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,7 +323,7 @@ smbus_piix4_response(void *priv)
|
||||
{
|
||||
smbus_piix4_t *dev = (smbus_piix4_t *) priv;
|
||||
|
||||
/* dispatch the status register update */
|
||||
/* Dispatch the status register update. */
|
||||
dev->stat = dev->next_stat;
|
||||
}
|
||||
|
||||
@@ -228,7 +348,9 @@ smbus_piix4_init(const device_t *info)
|
||||
smbus_piix4_t *dev = (smbus_piix4_t *) malloc(sizeof(smbus_piix4_t));
|
||||
memset(dev, 0, sizeof(smbus_piix4_t));
|
||||
|
||||
smbus_init();
|
||||
dev->local = info->local;
|
||||
i2c_smbus = dev->i2c = i2c_addbus((dev->local == SMBUS_VIA) ? "smbus_vt82c686b" : "smbus_piix4");
|
||||
|
||||
timer_add(&dev->response_timer, smbus_piix4_response, dev, 0);
|
||||
|
||||
return dev;
|
||||
@@ -240,6 +362,10 @@ smbus_piix4_close(void *priv)
|
||||
{
|
||||
smbus_piix4_t *dev = (smbus_piix4_t *) priv;
|
||||
|
||||
if (i2c_smbus == dev->i2c)
|
||||
i2c_smbus = NULL;
|
||||
i2c_removebus(dev->i2c);
|
||||
|
||||
free(dev);
|
||||
}
|
||||
|
||||
@@ -247,7 +373,16 @@ smbus_piix4_close(void *priv)
|
||||
const device_t piix4_smbus_device = {
|
||||
"PIIX4-compatible SMBus Host Controller",
|
||||
DEVICE_AT,
|
||||
0,
|
||||
SMBUS_PIIX4,
|
||||
smbus_piix4_init, smbus_piix4_close, NULL,
|
||||
{ NULL }, NULL, NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
const device_t via_smbus_device = {
|
||||
"VIA VT82C686B SMBus Host Controller",
|
||||
DEVICE_AT,
|
||||
SMBUS_VIA,
|
||||
smbus_piix4_init, smbus_piix4_close, NULL,
|
||||
{ NULL }, NULL, NULL,
|
||||
NULL
|
||||
|
||||
@@ -84,6 +84,7 @@ typedef struct
|
||||
pc_timer_t timer;
|
||||
nvr_t *nvr;
|
||||
apm_t *apm;
|
||||
void *i2c;
|
||||
} acpi_t;
|
||||
|
||||
|
||||
|
||||
@@ -35,9 +35,9 @@ typedef struct {
|
||||
uint8_t regs[8];
|
||||
uint8_t addr_register;
|
||||
uint8_t temp_idx;
|
||||
uint8_t smbus_addr;
|
||||
uint8_t i2c_addr, i2c_state;
|
||||
|
||||
uint8_t as99127f_smbus_addr;
|
||||
uint8_t as99127f_i2c_addr;
|
||||
} lm75_t;
|
||||
|
||||
|
||||
|
||||
70
src/include/86box/i2c.h
Normal file
70
src/include/86box/i2c.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Definitions for the I2C handler.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: RichardG, <richardg867@gmail.com>
|
||||
*
|
||||
* Copyright 2020 RichardG.
|
||||
*/
|
||||
#ifndef EMU_I2C_H
|
||||
# define EMU_I2C_H
|
||||
|
||||
|
||||
/* i2c.c */
|
||||
extern void *i2c_smbus;
|
||||
|
||||
|
||||
/* i2c.c */
|
||||
extern void *i2c_addbus(char *name);
|
||||
extern void i2c_removebus(void *bus_handle);
|
||||
extern char *i2c_getbusname(void *bus_handle);
|
||||
|
||||
extern void i2c_sethandler(void *bus_handle, uint8_t base, int size,
|
||||
uint8_t (*start)(void *bus, uint8_t addr, uint8_t read, void *priv),
|
||||
uint8_t (*read)(void *bus, uint8_t addr, void *priv),
|
||||
uint8_t (*write)(void *bus, uint8_t addr, uint8_t data, void *priv),
|
||||
void (*stop)(void *bus, uint8_t addr, void *priv),
|
||||
void *priv);
|
||||
|
||||
extern void i2c_removehandler(void *bus_handle, uint8_t base, int size,
|
||||
uint8_t (*start)(void *bus, uint8_t addr, uint8_t read, void *priv),
|
||||
uint8_t (*read)(void *bus, uint8_t addr, void *priv),
|
||||
uint8_t (*write)(void *bus, uint8_t addr, uint8_t data, void *priv),
|
||||
void (*stop)(void *bus, uint8_t addr, void *priv),
|
||||
void *priv);
|
||||
|
||||
extern void i2c_handler(int set, void *bus_handle, uint8_t base, int size,
|
||||
uint8_t (*start)(void *bus, uint8_t addr, uint8_t read, void *priv),
|
||||
uint8_t (*read)(void *bus, uint8_t addr, void *priv),
|
||||
uint8_t (*write)(void *bus, uint8_t addr, uint8_t data, void *priv),
|
||||
void (*stop)(void *bus, uint8_t addr, void *priv),
|
||||
void *priv);
|
||||
|
||||
extern uint8_t i2c_has_device(void *bus_handle, uint8_t addr);
|
||||
extern uint8_t i2c_start(void *bus_handle, uint8_t addr, uint8_t read);
|
||||
extern uint8_t i2c_read(void *bus_handle, uint8_t addr);
|
||||
extern uint8_t i2c_write(void *bus_handle, uint8_t addr, uint8_t data);
|
||||
extern void i2c_stop(void *bus_handle, uint8_t addr);
|
||||
|
||||
/* i2c_eeprom.c */
|
||||
extern void *i2c_eeprom_init(void *i2c, uint8_t addr, uint8_t *data, uint32_t size, uint8_t writable);
|
||||
extern void i2c_eeprom_close(void *dev_handle);
|
||||
|
||||
/* i2c_gpio.c */
|
||||
extern void *i2c_gpio_init(char *bus_name);
|
||||
extern void i2c_gpio_close(void *dev_handle);
|
||||
extern void i2c_gpio_set(void *dev_handle, uint8_t scl, uint8_t sda);
|
||||
extern uint8_t i2c_gpio_get_scl(void *dev_handle);
|
||||
extern uint8_t i2c_gpio_get_sda(void *dev_handle);
|
||||
extern void *i2c_gpio_get_bus();
|
||||
|
||||
|
||||
#endif /*EMU_I2C_H*/
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Definitions for the SMBus handler.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: RichardG, <richardg867@gmail.com>
|
||||
*
|
||||
* Copyright 2020 RichardG.
|
||||
*/
|
||||
#ifndef EMU_SMBUS_H
|
||||
# define EMU_SMBUS_H
|
||||
|
||||
|
||||
extern void smbus_init(void);
|
||||
|
||||
extern void smbus_sethandler(uint8_t base, int size,
|
||||
uint8_t (*read_byte)(uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint16_t (*read_word_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint8_t (*read_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void (*write_byte)(uint8_t addr, uint8_t val, void *priv),
|
||||
void (*write_byte_cmd)(uint8_t addr, uint8_t cmd, uint8_t val, void *priv),
|
||||
void (*write_word_cmd)(uint8_t addr, uint8_t cmd, uint16_t val, void *priv),
|
||||
void (*write_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void *priv);
|
||||
|
||||
extern void smbus_removehandler(uint8_t base, int size,
|
||||
uint8_t (*read_byte)(uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint16_t (*read_word_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint8_t (*read_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void (*write_byte)(uint8_t addr, uint8_t val, void *priv),
|
||||
void (*write_byte_cmd)(uint8_t addr, uint8_t cmd, uint8_t val, void *priv),
|
||||
void (*write_word_cmd)(uint8_t addr, uint8_t cmd, uint16_t val, void *priv),
|
||||
void (*write_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void *priv);
|
||||
|
||||
extern void smbus_handler(int set, uint8_t base, int size,
|
||||
uint8_t (*read_byte)(uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint16_t (*read_word_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint8_t (*read_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void (*write_byte)(uint8_t addr, uint8_t val, void *priv),
|
||||
void (*write_byte_cmd)(uint8_t addr, uint8_t cmd, uint8_t val, void *priv),
|
||||
void (*write_word_cmd)(uint8_t addr, uint8_t cmd, uint16_t val, void *priv),
|
||||
void (*write_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void *priv);
|
||||
|
||||
extern uint8_t smbus_has_device(uint8_t addr);
|
||||
extern uint8_t smbus_read_byte(uint8_t addr);
|
||||
extern uint8_t smbus_read_byte_cmd(uint8_t addr, uint8_t cmd);
|
||||
extern uint16_t smbus_read_word_cmd(uint8_t addr, uint8_t cmd);
|
||||
extern uint8_t smbus_read_block_cmd(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len);
|
||||
extern void smbus_write_byte(uint8_t addr, uint8_t val);
|
||||
extern void smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val);
|
||||
extern void smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val);
|
||||
extern void smbus_write_block_cmd(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len);
|
||||
|
||||
|
||||
#endif /*EMU_SMBUS_H*/
|
||||
@@ -19,16 +19,22 @@
|
||||
|
||||
|
||||
#define SMBUS_PIIX4_BLOCK_DATA_SIZE 32
|
||||
#define SMBUS_PIIX4_BLOCK_DATA_MASK (SMBUS_PIIX4_BLOCK_DATA_SIZE - 1)
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
enum {
|
||||
SMBUS_PIIX4 = 0,
|
||||
SMBUS_VIA
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint32_t local;
|
||||
uint16_t io_base;
|
||||
uint8_t stat, next_stat, ctl, cmd, addr,
|
||||
data0, data1,
|
||||
index,
|
||||
data[SMBUS_PIIX4_BLOCK_DATA_SIZE];
|
||||
index, data[SMBUS_PIIX4_BLOCK_DATA_SIZE];
|
||||
pc_timer_t response_timer;
|
||||
void *i2c;
|
||||
} smbus_piix4_t;
|
||||
|
||||
|
||||
@@ -37,6 +43,7 @@ extern void smbus_piix4_remap(smbus_piix4_t *dev, uint16_t new_io_base, uint8_t
|
||||
|
||||
#ifdef EMU_DEVICE_H
|
||||
extern const device_t piix4_smbus_device;
|
||||
extern const device_t via_smbus_device;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -47,17 +47,17 @@
|
||||
#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;
|
||||
typedef struct {
|
||||
uint8_t slot;
|
||||
uint16_t size;
|
||||
uint16_t row1;
|
||||
uint16_t row2;
|
||||
|
||||
uint8_t addr_register;
|
||||
uint8_t data[SPD_DATA_SIZE];
|
||||
void *eeprom;
|
||||
} spd_t;
|
||||
|
||||
typedef struct _spd_edo_ {
|
||||
typedef struct {
|
||||
uint8_t bytes_used, spd_size, mem_type,
|
||||
row_bits, col_bits, banks,
|
||||
data_width_lsb, data_width_msb,
|
||||
@@ -75,7 +75,7 @@ typedef struct _spd_edo_ {
|
||||
checksum2;
|
||||
} spd_edo_t;
|
||||
|
||||
typedef struct _spd_sdram_ {
|
||||
typedef struct {
|
||||
uint8_t bytes_used, spd_size, mem_type,
|
||||
row_bits, col_bits, rows,
|
||||
data_width_lsb, data_width_msb,
|
||||
|
||||
@@ -1,4 +1,25 @@
|
||||
void ddc_init(void);
|
||||
void ddc_i2c_change(int new_clock, int new_data);
|
||||
int ddc_read_clock(void);
|
||||
int ddc_read_data(void);
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* DDC monitor emulation definitions.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
|
||||
* RichardG, <richardg867@gmail.com>
|
||||
*
|
||||
* Copyright 2008-2020 Sarah Walker.
|
||||
* Copyright 2020 RichardG.
|
||||
*/
|
||||
#ifndef EMU_VID_DDC_H
|
||||
# define EMU_VID_DDC_H
|
||||
|
||||
extern void *ddc_init(void *i2c);
|
||||
extern void ddc_close(void *eeprom);
|
||||
|
||||
#endif /*EMU_VID_DDC_H*/
|
||||
|
||||
537
src/mem/spd.c
537
src/mem/spd.c
@@ -23,7 +23,7 @@
|
||||
#define HAVE_STDARG_H
|
||||
#include <86box/86box.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/smbus.h>
|
||||
#include <86box/i2c.h>
|
||||
#include <86box/spd.h>
|
||||
#include <86box/version.h>
|
||||
#include <86box/machine.h>
|
||||
@@ -33,13 +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 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);
|
||||
static const device_t spd_device;
|
||||
|
||||
|
||||
#ifdef ENABLE_SPD_LOG
|
||||
@@ -52,9 +48,9 @@ spd_log(const char *fmt, ...)
|
||||
va_list ap;
|
||||
|
||||
if (spd_do_log) {
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#else
|
||||
@@ -62,83 +58,33 @@ spd_log(const char *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;
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
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: close()\n");
|
||||
|
||||
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, spd_read_word_cmd, spd_read_block_cmd,
|
||||
spd_write_byte, NULL, NULL, NULL,
|
||||
dev);
|
||||
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 %02Xh)\n", dev->slot, SPD_BASE_ADDR + dev->slot);
|
||||
|
||||
smbus_sethandler(SPD_BASE_ADDR + dev->slot, 1,
|
||||
spd_read_byte, spd_read_byte_cmd, spd_read_word_cmd, spd_read_block_cmd,
|
||||
spd_write_byte, NULL, NULL, NULL,
|
||||
dev);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -147,7 +93,7 @@ log2_ui16(uint16_t i)
|
||||
{
|
||||
uint8_t ret = 0;
|
||||
while ((i >>= 1))
|
||||
ret++;
|
||||
ret++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -162,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 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];
|
||||
} else {
|
||||
vslots[vslot] = 0;
|
||||
break;
|
||||
}
|
||||
/* 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 */
|
||||
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 {
|
||||
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 */
|
||||
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);
|
||||
}
|
||||
/* Work backwards to add the missing RAM as asymmetric modules if possible. */
|
||||
if (enable_asym) {
|
||||
row = slot_count - 1;
|
||||
do {
|
||||
asym = (1 << log2_ui16(MIN(total_size, rows[row])));
|
||||
if (rows[row] + asym <= max_module_size) {
|
||||
rows[row] += asym;
|
||||
total_size -= asym;
|
||||
}
|
||||
} 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);
|
||||
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 */
|
||||
/* Populate empty rows by splitting modules... */
|
||||
split = (total_size == 0); /* ...if possible. */
|
||||
while (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]))))
|
||||
continue; /* no module here, module is too small to be split, or asymmetric module */
|
||||
/* Look for a module to split. */
|
||||
split = 0;
|
||||
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;
|
||||
}
|
||||
if (!next_empty_vslot)
|
||||
break; /* no empty vslots left */
|
||||
/* 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_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 = 1;
|
||||
break;
|
||||
}
|
||||
/* 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 */
|
||||
if (split)
|
||||
qsort(vslots, slot_count, sizeof(uint16_t), comp_ui16_rev);
|
||||
/* Sort rows by descending capacity if any were split. */
|
||||
if (split)
|
||||
qsort(rows, slot_count, sizeof(uint16_t), comp_ui16_rev);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,172 +180,163 @@ 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:
|
||||
min_module_size = SPD_MIN_SIZE_EDO;
|
||||
break;
|
||||
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;
|
||||
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;
|
||||
spd_log("SPD: unknown RAM type %02X\n", ram_type);
|
||||
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)) {
|
||||
slot_count++;
|
||||
}
|
||||
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++) {
|
||||
if (!(slot_mask & (1 << slot)))
|
||||
continue; /* slot disabled */
|
||||
/* 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 = rows[row] - (1 << log2_ui16(rows[row])); /* separate the powers of 2 */
|
||||
if (!asym) /* is the module asymmetric? */
|
||||
asym = rows[row] >> 1; /* symmetric, therefore divide by 2 */
|
||||
|
||||
/* 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_modules[slot]->row1 = rows[row] - asym;
|
||||
spd_modules[slot]->row2 = asym;
|
||||
|
||||
spd_devices[slot]->row1 = (vslots[vslot] - asym);
|
||||
spd_devices[slot]->row2 = asym;
|
||||
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);
|
||||
|
||||
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:
|
||||
case SPD_TYPE_EDO:
|
||||
edo_data = (spd_edo_t *) &spd_modules[slot]->data;
|
||||
memset(edo_data, 0, sizeof(spd_edo_t));
|
||||
|
||||
switch (ram_type) {
|
||||
case SPD_TYPE_FPM:
|
||||
case SPD_TYPE_EDO:
|
||||
edo_data = (spd_edo_t *) &spd_data[slot];
|
||||
memset(edo_data, 0, sizeof(spd_edo_t));
|
||||
/* 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 = SPD_ROLLUP(7 + log2_ui16(spd_modules[slot]->row1)); /* first row */
|
||||
edo_data->col_bits = 9;
|
||||
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;
|
||||
edo_data->data_width_lsb = 64;
|
||||
edo_data->signal_level = SPD_SIGNAL_LVTTL;
|
||||
edo_data->trac = 50;
|
||||
edo_data->tcac = 13;
|
||||
edo_data->refresh_rate = SPD_REFRESH_NORMAL;
|
||||
edo_data->dram_width = 8;
|
||||
|
||||
/* 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 = 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;
|
||||
edo_data->trac = 50;
|
||||
edo_data->tcac = 13;
|
||||
edo_data->refresh_rate = SPD_REFRESH_NORMAL;
|
||||
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", 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);
|
||||
edo_data->rev_code[1] = BCD8(EMU_VERSION_MIN);
|
||||
edo_data->mfg_year = 20;
|
||||
edo_data->mfg_week = 17;
|
||||
|
||||
edo_data->spd_rev = 0x12;
|
||||
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] = ' '; /* part number should be space-padded */
|
||||
edo_data->rev_code[0] = BCD8(EMU_VERSION_MAJ);
|
||||
edo_data->rev_code[1] = BCD8(EMU_VERSION_MIN);
|
||||
edo_data->mfg_year = 20;
|
||||
edo_data->mfg_week = 17;
|
||||
for (i = 0; i < 63; i++)
|
||||
edo_data->checksum += spd_modules[slot]->data[i];
|
||||
for (i = 0; i < 129; i++)
|
||||
edo_data->checksum2 += spd_modules[slot]->data[i];
|
||||
break;
|
||||
|
||||
for (i = 0; i < 63; i++)
|
||||
edo_data->checksum += spd_data[slot][i];
|
||||
for (i = 0; i < 129; i++)
|
||||
edo_data->checksum2 += spd_data[slot][i];
|
||||
break;
|
||||
case SPD_TYPE_SDRAM:
|
||||
sdram_data = (spd_sdram_t *) &spd_modules[slot]->data;
|
||||
memset(sdram_data, 0, sizeof(spd_sdram_t));
|
||||
|
||||
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 = SPD_ROLLUP(6 + log2_ui16(spd_modules[slot]->row1)); /* first row */
|
||||
sdram_data->col_bits = 9;
|
||||
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;
|
||||
sdram_data->data_width_lsb = 64;
|
||||
sdram_data->signal_level = SPD_SIGNAL_LVTTL;
|
||||
sdram_data->tclk = 0x75; /* 7.5 ns = 133.3 MHz */
|
||||
sdram_data->tac = 0x10;
|
||||
sdram_data->refresh_rate = SPD_SDR_REFRESH_SELF | SPD_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 = 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;
|
||||
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_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_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->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->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;
|
||||
sdram_data->tclk = 0x75; /* 7.5 ns = 133.3 MHz */
|
||||
sdram_data->tac = 0x10;
|
||||
sdram_data->refresh_rate = SPD_SDR_REFRESH_SELF | SPD_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 = 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;
|
||||
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, 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);
|
||||
sdram_data->rev_code[1] = BCD8(EMU_VERSION_MIN);
|
||||
sdram_data->mfg_year = 20;
|
||||
sdram_data->mfg_week = 13;
|
||||
|
||||
sdram_data->spd_rev = 0x12;
|
||||
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] = ' '; /* part number should be space-padded */
|
||||
sdram_data->rev_code[0] = BCD8(EMU_VERSION_MAJ);
|
||||
sdram_data->rev_code[1] = BCD8(EMU_VERSION_MIN);
|
||||
sdram_data->mfg_year = 20;
|
||||
sdram_data->mfg_week = 13;
|
||||
sdram_data->freq = 100;
|
||||
sdram_data->features = 0xFF;
|
||||
|
||||
sdram_data->freq = 100;
|
||||
sdram_data->features = 0xFF;
|
||||
for (i = 0; i < 63; i++)
|
||||
sdram_data->checksum += spd_modules[slot]->data[i];
|
||||
for (i = 0; i < 129; i++)
|
||||
sdram_data->checksum2 += spd_modules[slot]->data[i];
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < 63; i++)
|
||||
sdram_data->checksum += spd_data[slot][i];
|
||||
for (i = 0; i < 129; i++)
|
||||
sdram_data->checksum2 += spd_data[slot][i];
|
||||
break;
|
||||
}
|
||||
|
||||
device_add(info);
|
||||
vslot++;
|
||||
row++;
|
||||
}
|
||||
|
||||
device_add(&spd_device);
|
||||
}
|
||||
|
||||
|
||||
@@ -407,53 +344,63 @@ 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) {
|
||||
apollo = reg_max;
|
||||
reg_max = reg_min + 7;
|
||||
apollo = reg_max;
|
||||
reg_max = reg_min + 7;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
dimm = ((reg_max - reg_min) + 1) >> 1; /* amount of "DIMM"s, also used to determine the maximum "DIMM" size */
|
||||
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;
|
||||
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 */
|
||||
size = ((row & 1) ? 0 : 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);
|
||||
}
|
||||
if (spd_present) {
|
||||
/* SPD enabled: use SPD info for this slot, if present. */
|
||||
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_modules[dimm]->row2 : spd_modules[dimm]->row1;
|
||||
}
|
||||
} else {
|
||||
/* No SPD: use the values calculated above. */
|
||||
size = (rows[dimm] >> 1);
|
||||
}
|
||||
|
||||
/* Determine the DRB register to write. */
|
||||
drb = reg_min + row;
|
||||
if ((apollo) && ((drb & 0xf) < 0xa))
|
||||
drb = apollo + (drb & 0xf);
|
||||
/* Determine the DRB register to write. */
|
||||
drb = reg_min + row;
|
||||
if (apollo && ((drb & 0xf) < 0xa))
|
||||
drb = apollo + (drb & 0xf);
|
||||
|
||||
/* Write DRB register, adding the previous DRB's value. */
|
||||
if (row == 0)
|
||||
regs[drb] = 0;
|
||||
else if ((apollo) && (drb == apollo))
|
||||
regs[drb] = regs[drb | 0xf]; /* 5F comes before 56 */
|
||||
else
|
||||
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]);
|
||||
/* Write DRB register, adding the previous DRB's value. */
|
||||
if (row == 0)
|
||||
regs[drb] = 0;
|
||||
else if ((apollo) && (drb == apollo))
|
||||
regs[drb] = regs[drb | 0xf]; /* 5F comes before 56 */
|
||||
else
|
||||
regs[drb] = regs[drb - 1];
|
||||
if (size)
|
||||
regs[drb] += size / drb_unit; /* this will intentionally overflow on 440GX with 2 GB */
|
||||
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
|
||||
};
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include <86box/device.h>
|
||||
#include <86box/nvr.h>
|
||||
#include <86box/plat.h>
|
||||
#include <86box/i2c.h>
|
||||
#include <86box/scsi.h>
|
||||
#include <86box/scsi_device.h>
|
||||
#include <86box/scsi_ncr53c8xx.h>
|
||||
@@ -185,6 +186,7 @@
|
||||
/* Flag set if this is a tagged command. */
|
||||
#define NCR_TAG_VALID (1 << 16)
|
||||
|
||||
#define NCR_NVRAM_SIZE 2048
|
||||
#define NCR_BUF_SIZE 4096
|
||||
|
||||
typedef struct ncr53c8xx_request {
|
||||
@@ -225,15 +227,9 @@ typedef struct {
|
||||
int msg_action;
|
||||
int msg_len;
|
||||
uint8_t msg[NCR_MAX_MSGIN_LEN];
|
||||
#ifdef USE_NVRAM
|
||||
uint16_t nvram;
|
||||
uint8_t nvram_t;
|
||||
uint8_t nvram_op;
|
||||
uint8_t nvram_param;
|
||||
uint8_t nvram_start;
|
||||
uint8_t nvram_index;
|
||||
#endif
|
||||
uint8_t ram[NCR_BUF_SIZE]; /* NCR 53c875 RAM (4 kB). */
|
||||
uint8_t nvram[NCR_NVRAM_SIZE]; /* 24C16 EEPROM (16 Kbit) */
|
||||
void *i2c, *eeprom;
|
||||
uint8_t ram[NCR_BUF_SIZE]; /* NCR 53C875 RAM (4 KB) */
|
||||
/* 0 if SCRIPTS are running or stopped.
|
||||
* 1 if a Wait Reselect instruction has been issued.
|
||||
* 2 if processing DMA from ncr53c8xx_execute_script.
|
||||
@@ -434,10 +430,7 @@ ncr53c8xx_soft_reset(ncr53c8xx_t *dev)
|
||||
dev->gpreg = 0;
|
||||
dev->slpar = 0;
|
||||
dev->sstop = 1;
|
||||
dev->gpcntl = 0x0f;
|
||||
#ifdef USE_NVRAM
|
||||
dev->nvram_t = dev->nvram_index = 0;
|
||||
#endif
|
||||
dev->gpcntl = 0x03;
|
||||
|
||||
if (dev->chip >= CHIP_825) {
|
||||
/* This *IS* a wide SCSI controller, so reset all SCSI
|
||||
@@ -474,7 +467,7 @@ ncr53c8xx_read(ncr53c8xx_t *dev, uint32_t addr, uint8_t *buf, uint32_t len)
|
||||
buf[i] = inb((uint16_t) (addr + i));
|
||||
} else {
|
||||
ncr53c8xx_log("NCR 810: Reading from memory address %08X\n", addr);
|
||||
dma_bm_read(addr, buf, len, 4);
|
||||
dma_bm_read(addr, buf, len, 4);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,7 +485,7 @@ ncr53c8xx_write(ncr53c8xx_t *dev, uint32_t addr, uint8_t *buf, uint32_t len)
|
||||
outb((uint16_t) (addr + i), buf[i]);
|
||||
} else {
|
||||
ncr53c8xx_log("NCR 810: Writing to memory address %08X\n", addr);
|
||||
dma_bm_write(addr, buf, len, 4);
|
||||
dma_bm_write(addr, buf, len, 4);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -511,10 +504,10 @@ static
|
||||
void do_irq(ncr53c8xx_t *dev, int level)
|
||||
{
|
||||
if (level) {
|
||||
pci_set_irq(dev->pci_slot, PCI_INTA);
|
||||
pci_set_irq(dev->pci_slot, PCI_INTA);
|
||||
ncr53c8xx_log("Raising IRQ...\n");
|
||||
} else {
|
||||
pci_clear_irq(dev->pci_slot, PCI_INTA);
|
||||
pci_clear_irq(dev->pci_slot, PCI_INTA);
|
||||
ncr53c8xx_log("Lowering IRQ...\n");
|
||||
}
|
||||
}
|
||||
@@ -852,8 +845,8 @@ ncr53c8xx_do_msgin(ncr53c8xx_t *dev)
|
||||
switch to PHASE_MO. */
|
||||
switch (dev->msg_action) {
|
||||
case 0:
|
||||
ncr53c8xx_set_phase(dev, PHASE_CMD);
|
||||
break;
|
||||
ncr53c8xx_set_phase(dev, PHASE_CMD);
|
||||
break;
|
||||
case 1:
|
||||
ncr53c8xx_disconnect(dev);
|
||||
break;
|
||||
@@ -1444,7 +1437,7 @@ ncr53c8xx_callback(void *p)
|
||||
if (dev->waiting)
|
||||
timer_on_auto(&dev->timer, 40.0);
|
||||
else
|
||||
ncr53c8xx_process_script(dev);
|
||||
ncr53c8xx_process_script(dev);
|
||||
}
|
||||
|
||||
if (dev->sstop)
|
||||
@@ -1452,146 +1445,22 @@ ncr53c8xx_callback(void *p)
|
||||
}
|
||||
|
||||
|
||||
#ifdef USE_NVRAM
|
||||
static void
|
||||
ncr53c8xx_eeprom(ncr53c8xx_t *dev, int save)
|
||||
ncr53c8xx_eeprom(ncr53c8xx_t *dev, uint8_t save)
|
||||
{
|
||||
FILE *f;
|
||||
|
||||
if (save)
|
||||
f = nvr_fopen(dev->nvr_path, L"wb");
|
||||
else
|
||||
f = nvr_fopen(dev->nvr_path, L"rb");
|
||||
if (f)
|
||||
{
|
||||
if (save) {
|
||||
fwrite((uint8_t *) &dev->nvram, 1, 1, f);
|
||||
fwrite(((uint8_t *) &dev->nvram) + 1, 1, 1, f);
|
||||
} else {
|
||||
fread((uint8_t *) &dev->nvram, 1, 1, f);
|
||||
fread(((uint8_t *) &dev->nvram) + 1, 1, 1, f);
|
||||
}
|
||||
f = nvr_fopen(dev->nvr_path, save ? L"wb": L"rb");
|
||||
if (f) {
|
||||
if (save)
|
||||
fwrite(&dev->nvram, sizeof(dev->nvram), 1, f);
|
||||
else
|
||||
fread(&dev->nvram, sizeof(dev->nvram), 1, f);
|
||||
fclose(f);
|
||||
f = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define ADDRESS_LENGTH 9
|
||||
#define ADDRESS_END (ADDRESS_LENGTH + 2)
|
||||
#define ADDRESS_SHIFT (ADDRESS_LENGTH - 2)
|
||||
#define ADDRESS_MASK 0x3f
|
||||
#define DATA_LENGTH 8
|
||||
#define DATA_START (ADDRESS_END + 1)
|
||||
#define DATA_END (ADDRESS_END + DATA_LENGTH)
|
||||
|
||||
|
||||
static void
|
||||
ncr53c8xx_serial_eeprom_write(ncr53c8xx_t *dev, uint8_t val)
|
||||
{
|
||||
uint8_t temp, old = dev->nvram_t;
|
||||
dev->nvram_t = val & 0x03;
|
||||
|
||||
if (val & 0x02) {
|
||||
if (!dev->nvram_index) {
|
||||
/* First signal clocked in after clock is high, start bit. */
|
||||
dev->nvram_start = 1;
|
||||
ncr53c8xx_log("[W] Serial EEPROM: Start bit\n");
|
||||
dev->nvram_op = 0;
|
||||
} else if ((dev->nvram_index == 1) || (dev->nvram_index == 2)) {
|
||||
if (!dev->nvram_start)
|
||||
return;
|
||||
|
||||
dev->nvram_op = (val & 0x01) << (dev->nvram_index - 1);
|
||||
if (dev->nvram_index == 2) {
|
||||
// ncr53c8xx_log("[W] Serial EEPROM: Opcode: %01X\n", dev->nvram_op);
|
||||
dev->nvram_param = 0;
|
||||
}
|
||||
} else if ((dev->nvram_index >= 3) && (dev->nvram_index <= ADDRESS_END)) {
|
||||
if (!dev->nvram_start)
|
||||
return;
|
||||
|
||||
dev->nvram_param = (val & 0x01) << (dev->nvram_index - 3);
|
||||
if (dev->nvram_index < ADDRESS_END) {
|
||||
dev->nvram_index++;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (dev->nvram_op) {
|
||||
case 0x00:
|
||||
temp = dev->nvram_param >> ADDRESS_SHIFT;
|
||||
switch(temp) {
|
||||
case 0x00:
|
||||
ncr53c8xx_log("[W] Serial EEPROM: EWDS\n");
|
||||
break;
|
||||
case 0x01:
|
||||
ncr53c8xx_log("[W] Serial EEPROM: WRAL\n");
|
||||
break;
|
||||
case 0x02:
|
||||
ncr53c8xx_log("[W] Serial EEPROM: ERAL\n");
|
||||
break;
|
||||
case 0x03:
|
||||
ncr53c8xx_log("[W] Serial EEPROM: EWEN\n");
|
||||
break;
|
||||
default:
|
||||
ncr53c8xx_log("[W] Serial EEPROM: UNKNOWN 00\n");
|
||||
break;
|
||||
}
|
||||
dev->nvram_start = dev->nvram_index = dev->nvram_op = dev->nvram_param = 0;
|
||||
return;
|
||||
case 0x01:
|
||||
ncr53c8xx_log("[W] Serial EEPROM: WRITE\n");
|
||||
break;
|
||||
case 0x02:
|
||||
ncr53c8xx_log("[W] Serial EEPROM: READ\n");
|
||||
break;
|
||||
case 0x03:
|
||||
ncr53c8xx_log("[W] Serial EEPROM: ERASE\n");
|
||||
break;
|
||||
default:
|
||||
ncr53c8xx_log("[W] Serial EEPROM: UNKNOWN\n");
|
||||
break;
|
||||
}
|
||||
} else if ((dev->nvram_index >= DATA_START) && (dev->nvram_index <= DATA_END)) {
|
||||
if (!dev->nvram_start)
|
||||
return;
|
||||
|
||||
if (dev->nvram_index == DATA_END) {
|
||||
ncr53c8xx_log("[W] Serial EEPROM: Data end\n");
|
||||
dev->nvram_start = dev->nvram_index = dev->nvram_op = dev->nvram_param = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
dev->nvram_index++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
ncr53c8xx_serial_eeprom_read(ncr53c8xx_t *dev)
|
||||
{
|
||||
uint8_t temp = 0;
|
||||
|
||||
if (dev->gpreg & 0x02) {
|
||||
if ((dev->nvram_index >= DATA_START) && (dev->nvram_index <= DATA_END)) {
|
||||
if (!dev->nvram_start)
|
||||
return temp;
|
||||
|
||||
dev->nvram_index++;
|
||||
|
||||
if (dev->nvram_index == DATA_END) {
|
||||
ncr53c8xx_log("[R] Serial EEPROM: Data end\n");
|
||||
dev->nvram_start = dev->nvram_index = dev->nvram_op = dev->nvram_param = 0;
|
||||
return temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static void
|
||||
ncr53c8xx_reg_writeb(ncr53c8xx_t *dev, uint32_t offset, uint8_t val)
|
||||
{
|
||||
@@ -1659,12 +1528,8 @@ ncr53c8xx_reg_writeb(ncr53c8xx_t *dev, uint32_t offset, uint8_t val)
|
||||
break;
|
||||
case 0x07: /* GPREG */
|
||||
ncr53c8xx_log("NCR 810: GPREG write %02X\n", val);
|
||||
tmp = dev->gpreg;
|
||||
dev->gpreg = val & 0xfe;
|
||||
#ifdef USE_NVRAM
|
||||
if ((dev->gpcntl & 0xc3) == 0x00)
|
||||
ncr53c8xx_serial_eeprom_write(dev, val & 0x03);
|
||||
#endif
|
||||
dev->gpreg = val;
|
||||
i2c_gpio_set(dev->i2c, !!(dev->gpreg & 0x02), !!(dev->gpreg & 0x01));
|
||||
break;
|
||||
case 0x08: /* SFBR */
|
||||
/* The CPU is not allowed to write to this register. However the
|
||||
@@ -1893,13 +1758,18 @@ ncr53c8xx_reg_readb(ncr53c8xx_t *dev, uint32_t offset)
|
||||
ncr53c8xx_log("NCR 810: Read SDID %02X\n", dev->sdid);
|
||||
return dev->sdid;
|
||||
case 0x07: /* GPREG */
|
||||
#ifdef USE_NVRAM
|
||||
tmp = (dev->gpreg & (dev->gpcntl ^ 0x1f)) & 0x1e;
|
||||
if ((dev->gpcntl & 0xc3) == 0x01)
|
||||
tmp |= ncr53c8xx_serial_eeprom_read(dev);
|
||||
#else
|
||||
tmp = ((dev->gpreg & (dev->gpcntl ^ 0x1f)) & 0x1e) | 0x01;
|
||||
#endif
|
||||
tmp = (dev->gpreg & (dev->gpcntl ^ 0x1f)) & 0x1f;
|
||||
if ((dev->gpcntl & 0x41) == 0x01) {
|
||||
tmp &= 0xfe;
|
||||
if (i2c_gpio_get_sda(dev->i2c))
|
||||
tmp |= 0x01;
|
||||
}
|
||||
if ((dev->gpcntl & 0x82) == 0x02) {
|
||||
tmp &= 0xfd;
|
||||
if (i2c_gpio_get_scl(dev->i2c))
|
||||
tmp |= 0x02;
|
||||
}
|
||||
|
||||
ncr53c8xx_log("NCR 810: Read GPREG %02X\n", tmp);
|
||||
return tmp;
|
||||
case 0x08: /* Revision ID */
|
||||
@@ -2677,8 +2547,8 @@ ncr53c8xx_init(const device_t *info)
|
||||
dev->chip_rev = 0x26;
|
||||
dev->nvr_path = L"ncr53c825a.nvr";
|
||||
}
|
||||
ncr53c8xx_pci_bar[2].addr_regs[0] = 0;
|
||||
ncr53c8xx_pci_bar[3].addr = 0xffff0000;
|
||||
ncr53c8xx_pci_bar[2].addr_regs[0] = 0;
|
||||
ncr53c8xx_pci_bar[3].addr = 0xffff0000;
|
||||
/* Need to make it align on a 16k boundary as that's this emulator's
|
||||
memory mapping granularity. */
|
||||
ncr53c8xx_ram_init(dev, 0x0fffc000);
|
||||
@@ -2695,10 +2565,11 @@ ncr53c8xx_init(const device_t *info)
|
||||
dev->nvr_path = L"ncr53c810.nvr";
|
||||
}
|
||||
|
||||
#ifdef USE_NVRAM
|
||||
dev->i2c = i2c_gpio_init("nvr_ncr53c8xx");
|
||||
dev->eeprom = i2c_eeprom_init(i2c_gpio_get_bus(dev->i2c), 0x50, dev->nvram, sizeof(dev->nvram), 1);
|
||||
|
||||
/* Load the serial EEPROM. */
|
||||
ncr53c8xx_eeprom(dev, 0);
|
||||
#endif
|
||||
|
||||
ncr53c8xx_soft_reset(dev);
|
||||
|
||||
@@ -2714,6 +2585,15 @@ ncr53c8xx_close(void *priv)
|
||||
ncr53c8xx_t *dev = (ncr53c8xx_t *)priv;
|
||||
|
||||
if (dev) {
|
||||
if (dev->eeprom)
|
||||
i2c_eeprom_close(dev->eeprom);
|
||||
|
||||
if (dev->i2c)
|
||||
i2c_gpio_close(dev->i2c);
|
||||
|
||||
/* Save the serial EEPROM. */
|
||||
ncr53c8xx_eeprom(dev, 1);
|
||||
|
||||
free(dev);
|
||||
dev = NULL;
|
||||
}
|
||||
@@ -2732,7 +2612,7 @@ static const device_config_t ncr53c8xx_pci_config[] = {
|
||||
|
||||
const device_t ncr53c810_pci_device =
|
||||
{
|
||||
"NCR 53c810",
|
||||
"NCR 53C810",
|
||||
DEVICE_PCI,
|
||||
0x01,
|
||||
ncr53c8xx_init, ncr53c8xx_close, NULL,
|
||||
@@ -2742,7 +2622,7 @@ const device_t ncr53c810_pci_device =
|
||||
|
||||
const device_t ncr53c810_onboard_pci_device =
|
||||
{
|
||||
"NCR 53c810 On-Board",
|
||||
"NCR 53C810 On-Board",
|
||||
DEVICE_PCI,
|
||||
0x8001,
|
||||
ncr53c8xx_init, ncr53c8xx_close, NULL,
|
||||
@@ -2752,7 +2632,7 @@ const device_t ncr53c810_onboard_pci_device =
|
||||
|
||||
const device_t ncr53c825a_pci_device =
|
||||
{
|
||||
"NCR 53c825A",
|
||||
"NCR 53C825A",
|
||||
DEVICE_PCI,
|
||||
CHIP_825,
|
||||
ncr53c8xx_init, ncr53c8xx_close, NULL,
|
||||
@@ -2762,7 +2642,7 @@ const device_t ncr53c825a_pci_device =
|
||||
|
||||
const device_t ncr53c860_pci_device =
|
||||
{
|
||||
"NCR 53c860",
|
||||
"NCR 53C860",
|
||||
DEVICE_PCI,
|
||||
CHIP_860,
|
||||
ncr53c8xx_init, ncr53c8xx_close, NULL,
|
||||
@@ -2772,7 +2652,7 @@ const device_t ncr53c860_pci_device =
|
||||
|
||||
const device_t ncr53c875_pci_device =
|
||||
{
|
||||
"NCR 53c875",
|
||||
"NCR 53C875",
|
||||
DEVICE_PCI,
|
||||
CHIP_875,
|
||||
ncr53c8xx_init, ncr53c8xx_close, NULL,
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <86box/rom.h>
|
||||
#include <86box/plat.h>
|
||||
#include <86box/video.h>
|
||||
#include <86box/i2c.h>
|
||||
#include <86box/vid_ddc.h>
|
||||
#include <86box/vid_svga.h>
|
||||
#include <86box/vid_svga_render.h>
|
||||
@@ -251,6 +252,8 @@ typedef struct mach64_t
|
||||
uint32_t buf_offset[2], buf_pitch[2];
|
||||
|
||||
int overlay_v_acc;
|
||||
|
||||
void *i2c, *ddc;
|
||||
} mach64_t;
|
||||
|
||||
static video_timings_t timing_mach64_isa = {VIDEO_ISA, 3, 3, 6, 5, 5, 10};
|
||||
@@ -1879,11 +1882,11 @@ uint8_t mach64_ext_readb(uint32_t addr, void *p)
|
||||
|
||||
if ((ret & (1 << 4)) && !(ret & (1 << 1)))
|
||||
gpio_state &= ~(1 << 1);
|
||||
if (!(ret & (1 << 4)) && !ddc_read_data())
|
||||
if (!(ret & (1 << 4)) && !i2c_gpio_get_sda(mach64->i2c))
|
||||
gpio_state &= ~(1 << 1);
|
||||
if ((ret & (1 << 5)) && !(ret & (1 << 2)))
|
||||
gpio_state &= ~(1 << 2);
|
||||
if (!(ret & (1 << 5)) && !ddc_read_clock())
|
||||
if (!(ret & (1 << 5)) && !i2c_gpio_get_scl(mach64->i2c))
|
||||
gpio_state &= ~(1 << 2);
|
||||
|
||||
ret = (ret & ~6) | gpio_state;
|
||||
@@ -2380,8 +2383,8 @@ void mach64_ext_writeb(uint32_t addr, uint8_t val, void *p)
|
||||
svga_set_ramdac_type(svga, (mach64->dac_cntl & 0x100) ? RAMDAC_8BIT : RAMDAC_6BIT);
|
||||
ati68860_set_ramdac_type(mach64->svga.ramdac, (mach64->dac_cntl & 0x100) ? RAMDAC_8BIT : RAMDAC_6BIT);
|
||||
data = (val & (1 << 4)) ? ((val & (1 << 1)) ? 1 : 0) : 1;
|
||||
clk = (val & (1 << 5)) ? ((val & (1 << 2)) ? 1 : 0) : 1;
|
||||
ddc_i2c_change(clk, data);
|
||||
clk = (val & (1 << 5)) ? ((val & (1 << 2)) ? 1 : 0) : 1;
|
||||
i2c_gpio_set(mach64->i2c, clk, data);
|
||||
break;
|
||||
|
||||
case 0xd0: case 0xd1: case 0xd2: case 0xd3:
|
||||
@@ -3368,7 +3371,8 @@ static void *mach64_common_init(const device_t *info)
|
||||
mach64->fifo_not_full_event = thread_create_event();
|
||||
mach64->fifo_thread = thread_create(fifo_thread, mach64);
|
||||
|
||||
ddc_init();
|
||||
mach64->i2c = i2c_gpio_init("ddc_ati_mach64");
|
||||
mach64->ddc = ddc_init(i2c_gpio_get_bus(mach64->i2c));
|
||||
|
||||
return mach64;
|
||||
}
|
||||
@@ -3460,6 +3464,9 @@ void mach64_close(void *p)
|
||||
thread_destroy_event(mach64->wake_fifo_thread);
|
||||
thread_destroy_event(mach64->fifo_not_full_event);
|
||||
|
||||
ddc_close(mach64->ddc);
|
||||
i2c_gpio_close(mach64->i2c);
|
||||
|
||||
free(mach64);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
#include <86box/device.h>
|
||||
#include <86box/timer.h>
|
||||
#include <86box/video.h>
|
||||
#include <86box/i2c.h>
|
||||
#include <86box/vid_ddc.h>
|
||||
#include <86box/vid_svga.h>
|
||||
#include <86box/vid_svga_render.h>
|
||||
|
||||
@@ -208,6 +210,8 @@ typedef struct gd54xx_t
|
||||
|
||||
uint32_t extpallook[256];
|
||||
PALETTE extpal;
|
||||
|
||||
void *i2c, *ddc;
|
||||
} gd54xx_t;
|
||||
|
||||
|
||||
@@ -355,6 +359,10 @@ gd54xx_out(uint16_t addr, uint8_t val, void *p)
|
||||
if (svga->crtc[0x27] < CIRRUS_ID_CLGD5429)
|
||||
gd54xx->unlocked = (svga->seqregs[6] == 0x12);
|
||||
break;
|
||||
case 0x08:
|
||||
if (gd54xx->i2c)
|
||||
i2c_gpio_set(gd54xx->i2c, !!(val & 0x01), !!(val & 0x02));
|
||||
break;
|
||||
case 0x0b: case 0x0c: case 0x0d: case 0x0e: /* VCLK stuff */
|
||||
gd54xx->vclk_n[svga->seqaddr-0x0b] = val;
|
||||
break;
|
||||
@@ -697,6 +705,15 @@ gd54xx_in(uint16_t addr, void *p)
|
||||
case 6:
|
||||
ret = svga->seqregs[6];
|
||||
break;
|
||||
case 0x08:
|
||||
if (gd54xx->i2c) {
|
||||
ret &= 0x7b;
|
||||
if (i2c_gpio_get_scl(gd54xx->i2c))
|
||||
ret |= 0x04;
|
||||
if (i2c_gpio_get_sda(gd54xx->i2c))
|
||||
ret |= 0x80;
|
||||
}
|
||||
break;
|
||||
case 0x0b: case 0x0c: case 0x0d: case 0x0e:
|
||||
ret = gd54xx->vclk_n[svga->seqaddr-0x0b];
|
||||
break;
|
||||
@@ -3252,6 +3269,11 @@ static void
|
||||
mca_add(gd5428_mca_read, gd5428_mca_write, gd5428_mca_feedb, NULL, gd54xx);
|
||||
}
|
||||
|
||||
if (gd54xx_is_5434(svga)) {
|
||||
gd54xx->i2c = i2c_gpio_init("ddc_cl54xx");
|
||||
gd54xx->ddc = ddc_init(i2c_gpio_get_bus(gd54xx->i2c));
|
||||
}
|
||||
|
||||
return gd54xx;
|
||||
}
|
||||
|
||||
@@ -3359,6 +3381,11 @@ gd54xx_close(void *p)
|
||||
gd54xx_t *gd54xx = (gd54xx_t *)p;
|
||||
|
||||
svga_close(&gd54xx->svga);
|
||||
|
||||
if (gd54xx->i2c) {
|
||||
ddc_close(gd54xx->ddc);
|
||||
i2c_gpio_close(gd54xx->i2c);
|
||||
}
|
||||
|
||||
free(gd54xx);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
/*
|
||||
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
||||
* running old operating systems and software designed for IBM
|
||||
@@ -13,322 +10,192 @@
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
|
||||
* Authors: RichardG, <richardg867@gmail.com>
|
||||
*
|
||||
* Copyright 2008-2020 Sarah Walker.
|
||||
* Copyright 2020 RichardG.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <wchar.h>
|
||||
#include <math.h>
|
||||
#include <86box/86box.h>
|
||||
#include "cpu.h"
|
||||
#include <86box/vid_ddc.h>
|
||||
#include <86box/i2c.h>
|
||||
|
||||
|
||||
static uint8_t edid_data[128] =
|
||||
{
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, /*Fixed header pattern*/
|
||||
0x09, 0xf8, /*Manufacturer "BOX" - apparently unassigned by UEFI - and it has to be big endian*/
|
||||
0x00, 0x00, /*Product code*/
|
||||
0x12, 0x34, 0x56, 0x78, /*Serial number*/
|
||||
0x01, 9, /*Manufacturer week and year*/
|
||||
0x01, 0x03, /*EDID version (1.3)*/
|
||||
#define STD_TIMING(idx, width, aspect_ratio) do { \
|
||||
edid->standard_timings[idx].horiz_pixels = ((width) >> 3) - 31; \
|
||||
edid->standard_timings[idx].aspect_ratio_refresh_rate = (aspect_ratio) << 6; /* 60 Hz */ \
|
||||
} while (0)
|
||||
|
||||
0x08, /*Analogue input, separate sync*/
|
||||
34, 0, /*Landscape, 4:3*/
|
||||
0, /*Gamma*/
|
||||
0x08, /*RGB colour*/
|
||||
0x81, 0xf1, 0xa3, 0x57, 0x53, 0x9f, 0x27, 0x0a, 0x50, /*Chromaticity*/
|
||||
|
||||
0xff, 0xff, 0xff, /*Established timing bitmap*/
|
||||
0x00, 0x00, /*Standard timing information*/
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
|
||||
/*Detailed mode descriptions*/
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
0x00, /*No extensions*/
|
||||
0x00
|
||||
enum {
|
||||
STD_ASPECT_16_10 = 0x0,
|
||||
STD_ASPECT_4_3,
|
||||
STD_ASPECT_5_4,
|
||||
STD_ASPECT_16_9
|
||||
};
|
||||
|
||||
/*This should probably be split off into a separate I2C module*/
|
||||
enum
|
||||
{
|
||||
TRANSMITTER_MONITOR = 1,
|
||||
TRANSMITTER_HOST = -1
|
||||
};
|
||||
typedef struct {
|
||||
uint8_t horiz_pixels, aspect_ratio_refresh_rate;
|
||||
} edid_standard_timing_t;
|
||||
|
||||
enum
|
||||
{
|
||||
I2C_IDLE = 0,
|
||||
I2C_RECEIVE,
|
||||
I2C_RECEIVE_WAIT,
|
||||
I2C_TRANSMIT_START,
|
||||
I2C_TRANSMIT,
|
||||
I2C_ACKNOWLEDGE,
|
||||
I2C_TRANSACKNOWLEDGE,
|
||||
I2C_TRANSMIT_WAIT
|
||||
};
|
||||
typedef struct {
|
||||
uint8_t pixel_clock_lsb, pixel_clock_msb, h_active_lsb, h_blank_lsb,
|
||||
h_active_blank_msb, v_active_lsb, v_blank_lsb, v_active_blank_msb,
|
||||
h_front_porch_lsb, h_sync_pulse_lsb, v_front_porch_sync_pulse_lsb,
|
||||
hv_front_porch_sync_pulse_msb, h_size_lsb, v_size_lsb, hv_size_msb,
|
||||
h_border, v_border, features;
|
||||
} edid_detailed_timing_t;
|
||||
|
||||
enum
|
||||
{
|
||||
PROM_IDLE = 0,
|
||||
PROM_RECEIVEADDR,
|
||||
PROM_RECEIVEDATA,
|
||||
PROM_SENDDATA,
|
||||
PROM_INVALID
|
||||
};
|
||||
typedef struct {
|
||||
uint8_t magic[2], reserved, tag, range_limit_offsets;
|
||||
union {
|
||||
char ascii[13];
|
||||
struct {
|
||||
uint8_t min_v_field, max_v_field, min_h_line, max_h_line, max_pixel_clock,
|
||||
timing_type;
|
||||
union {
|
||||
uint8_t padding[7];
|
||||
struct {
|
||||
uint8_t reserved, gtf_start_freq, gtf_c, gtf_m_lsb, gtf_m_msb,
|
||||
gtf_k, gtf_j;
|
||||
};
|
||||
struct {
|
||||
uint8_t cvt_version, add_clock_precision, max_active_pixels,
|
||||
aspect_ratios, aspect_ratio_pref, scaling_support,
|
||||
refresh_pref;
|
||||
};
|
||||
};
|
||||
} range_limits;
|
||||
struct {
|
||||
edid_standard_timing_t timings[6];
|
||||
uint8_t padding;
|
||||
} ext_standard_timings;
|
||||
struct {
|
||||
uint8_t version;
|
||||
struct {
|
||||
uint8_t lines_lsb, lines_msb_aspect_ratio, refresh_rate;
|
||||
} timings[4];
|
||||
} cvt_timings;
|
||||
struct {
|
||||
uint8_t version, timings[6], reserved[6];
|
||||
} established_timings3;
|
||||
};
|
||||
} edid_descriptor_t;
|
||||
|
||||
static struct
|
||||
{
|
||||
int clock, data;
|
||||
int state;
|
||||
int last_data;
|
||||
int pos;
|
||||
int transmit;
|
||||
uint8_t byte;
|
||||
} i2c;
|
||||
typedef struct {
|
||||
uint8_t magic[8], mfg[2], mfg_product[2], serial[4], mfg_week, mfg_year,
|
||||
edid_version, edid_rev;
|
||||
uint8_t input_params, horiz_size, vert_size, gamma, features;
|
||||
uint8_t red_green_lsb, blue_white_lsb, red_x_msb, red_y_msb, green_x_msb,
|
||||
green_y_msb, blue_x_msb, blue_y_msb, white_x_msb, white_y_msb;
|
||||
uint8_t established_timings[3];
|
||||
edid_standard_timing_t standard_timings[8];
|
||||
union {
|
||||
edid_detailed_timing_t detailed_timings[4];
|
||||
edid_descriptor_t descriptors[4];
|
||||
};
|
||||
uint8_t extensions, checksum;
|
||||
|
||||
static struct
|
||||
{
|
||||
int state;
|
||||
int addr;
|
||||
int rw;
|
||||
} prom;
|
||||
uint8_t ext_tag, ext_rev, ext_dtd_offset, ext_native_dtds;
|
||||
union {
|
||||
edid_detailed_timing_t ext_detailed_timings[6];
|
||||
edid_descriptor_t ext_descriptors[6];
|
||||
};
|
||||
uint8_t padding[15], checksum2;
|
||||
} edid_t;
|
||||
|
||||
static void prom_stop(void)
|
||||
|
||||
void *
|
||||
ddc_init(void *i2c)
|
||||
{
|
||||
// pclog("prom_stop()\n");
|
||||
prom.state = PROM_IDLE;
|
||||
i2c.transmit = TRANSMITTER_HOST;
|
||||
edid_t *edid = malloc(sizeof(edid_t));
|
||||
memset(edid, 0, sizeof(edid_t));
|
||||
|
||||
memset(&edid->magic[1], 0xff, sizeof(edid->magic) - 2);
|
||||
|
||||
edid->mfg[0] = 0x09; /* manufacturer "BOX" (apparently unassigned by UEFI) */
|
||||
edid->mfg[1] = 0xf8;
|
||||
edid->mfg_week = 48;
|
||||
edid->mfg_year = 2020 - 1990;
|
||||
edid->edid_version = 0x01;
|
||||
edid->edid_rev = 0x04; /* EDID 1.4 */
|
||||
|
||||
edid->input_params = 0x0e; /* analog input; separate sync; composite sync; sync on green */
|
||||
edid->horiz_size = ((4.0 / 3.0) * 100) - 99; /* landscape 4:3 */
|
||||
edid->features = 0x09; /* RGB color; GTF/CVT */
|
||||
|
||||
edid->red_green_lsb = 0x81;
|
||||
edid->blue_white_lsb = 0xf1;
|
||||
edid->red_x_msb = 0xa3;
|
||||
edid->red_y_msb = 0x57;
|
||||
edid->green_x_msb = 0x53;
|
||||
edid->green_y_msb = 0x9f;
|
||||
edid->blue_x_msb = 0x27;
|
||||
edid->blue_y_msb = 0x0a;
|
||||
edid->white_x_msb = 0x50;
|
||||
edid->white_y_msb = 0x00;
|
||||
|
||||
memset(&edid->established_timings, 0xff, sizeof(edid->established_timings)); /* all enabled */
|
||||
|
||||
#if 0
|
||||
memset(&edid->standard_timings, 0x01, sizeof(edid->standard_timings)); /* pad unused entries */
|
||||
#endif
|
||||
STD_TIMING(0, 1280, STD_ASPECT_16_9); /* 1280x720 */
|
||||
STD_TIMING(1, 1280, STD_ASPECT_16_10); /* 1280x800 */
|
||||
STD_TIMING(2, 1366, STD_ASPECT_16_9); /* 1360x768 (closest to 1366x768) */
|
||||
STD_TIMING(3, 1440, STD_ASPECT_16_10); /* 1440x900 */
|
||||
STD_TIMING(4, 1600, STD_ASPECT_16_9); /* 1600x900 */
|
||||
STD_TIMING(5, 1680, STD_ASPECT_16_10); /* 1680x1050 */
|
||||
STD_TIMING(6, 1920, STD_ASPECT_16_9); /* 1920x1080 */
|
||||
STD_TIMING(7, 2048, STD_ASPECT_4_3); /* 2048x1536 */
|
||||
|
||||
/* Detailed timings for the preferred mode of 800x600 @ 60 Hz */
|
||||
edid->detailed_timings[0].pixel_clock_lsb = 4000 & 0xff; /* 40.000 MHz */
|
||||
edid->detailed_timings[0].pixel_clock_msb = 4000 >> 8;
|
||||
edid->detailed_timings[0].h_active_lsb = 800 & 0xff;
|
||||
edid->detailed_timings[0].h_blank_lsb = 256 & 0xff;
|
||||
edid->detailed_timings[0].h_active_blank_msb = ((800 >> 4) & 0xf0) | ((256 >> 8) & 0x0f);
|
||||
edid->detailed_timings[0].v_active_lsb = 600 & 0xff;
|
||||
edid->detailed_timings[0].v_blank_lsb = 28;
|
||||
edid->detailed_timings[0].v_active_blank_msb = (600 >> 4) & 0xf0;
|
||||
edid->detailed_timings[0].h_front_porch_lsb = 40;
|
||||
edid->detailed_timings[0].h_sync_pulse_lsb = 128;
|
||||
edid->detailed_timings[0].v_front_porch_sync_pulse_lsb = (1 << 4) | 4;
|
||||
|
||||
edid->descriptors[1].tag = 0xf7; /* established timings 3 */
|
||||
edid->descriptors[1].established_timings3.version = 0x0a;
|
||||
memset(&edid->descriptors[1].established_timings3.timings, 0xff, sizeof(edid->descriptors[1].established_timings3.timings)); /* all enabled */
|
||||
|
||||
edid->descriptors[2].tag = 0xfc; /* display name */
|
||||
memcpy(&edid->descriptors[2].ascii, "86Box Monitor", 13); /* exactly 13 characters (would otherwise require LF termination and space padding) */
|
||||
|
||||
edid->descriptors[3].tag = 0xfd; /* range limits */
|
||||
edid->descriptors[3].range_limits.min_v_field = 1;
|
||||
edid->descriptors[3].range_limits.max_v_field = -1;
|
||||
edid->descriptors[3].range_limits.min_h_line = 1;
|
||||
edid->descriptors[3].range_limits.max_h_line = -1;
|
||||
edid->descriptors[3].range_limits.max_pixel_clock = -1;
|
||||
edid->descriptors[3].range_limits.timing_type = 0x00; /* default GTF */
|
||||
edid->descriptors[3].range_limits.padding[0] = 0x0a;
|
||||
memset(&edid->descriptors[3].range_limits.padding[1], 0x20, sizeof(edid->descriptors[3].range_limits.padding) - 1);
|
||||
|
||||
uint8_t *edid_bytes = (uint8_t *) edid;
|
||||
for (uint8_t c = 0; c < 127; c++)
|
||||
edid->checksum += edid_bytes[c];
|
||||
edid->checksum = 256 - edid->checksum;
|
||||
for (uint8_t c = 128; c < 255; c++)
|
||||
edid->checksum2 += edid_bytes[c];
|
||||
edid->checksum2 = 256 - edid->checksum2;
|
||||
|
||||
return i2c_eeprom_init(i2c, 0x50, edid_bytes, sizeof(edid_t), 0);
|
||||
}
|
||||
|
||||
static void prom_next_byte(void)
|
||||
|
||||
void
|
||||
ddc_close(void *eeprom)
|
||||
{
|
||||
// pclog("prom_next_byte(%d)\n", prom.addr);
|
||||
i2c.byte = edid_data[(prom.addr++) & 0x7F];
|
||||
}
|
||||
|
||||
static void prom_write(uint8_t byte)
|
||||
{
|
||||
// pclog("prom_write: byte=%02x\n", byte);
|
||||
switch (prom.state)
|
||||
{
|
||||
case PROM_IDLE:
|
||||
if ((byte & 0xfe) != 0xa0)
|
||||
{
|
||||
// pclog("I2C address not PROM\n");
|
||||
prom.state = PROM_INVALID;
|
||||
break;
|
||||
}
|
||||
prom.rw = byte & 1;
|
||||
if (prom.rw)
|
||||
{
|
||||
prom.state = PROM_SENDDATA;
|
||||
i2c.transmit = TRANSMITTER_MONITOR;
|
||||
i2c.byte = edid_data[(prom.addr++) & 0x7F];
|
||||
// pclog("PROM - %02X from %02X\n",i2c.byte, prom.addr-1);
|
||||
// pclog("Transmitter now PROM\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
prom.state = PROM_RECEIVEADDR;
|
||||
i2c.transmit = TRANSMITTER_HOST;
|
||||
}
|
||||
// pclog("PROM R/W=%i\n",promrw);
|
||||
return;
|
||||
|
||||
case PROM_RECEIVEADDR:
|
||||
// pclog("PROM addr=%02X\n",byte);
|
||||
prom.addr = byte;
|
||||
if (prom.rw)
|
||||
prom.state = PROM_SENDDATA;
|
||||
else
|
||||
prom.state = PROM_RECEIVEDATA;
|
||||
break;
|
||||
|
||||
case PROM_RECEIVEDATA:
|
||||
// pclog("PROM write %02X %02X\n",promaddr,byte);
|
||||
break;
|
||||
|
||||
case PROM_SENDDATA:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ddc_i2c_change(int new_clock, int new_data)
|
||||
{
|
||||
// pclog("I2C update clock %i->%i data %i->%i state %i\n",i2c.clock,new_clock,i2c.last_data,new_data,i2c.state);
|
||||
switch (i2c.state)
|
||||
{
|
||||
case I2C_IDLE:
|
||||
if (i2c.clock && new_clock)
|
||||
{
|
||||
if (i2c.last_data && !new_data) /*Start bit*/
|
||||
{
|
||||
// pclog("Start bit received\n");
|
||||
i2c.state = I2C_RECEIVE;
|
||||
i2c.pos = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case I2C_RECEIVE_WAIT:
|
||||
if (!i2c.clock && new_clock)
|
||||
i2c.state = I2C_RECEIVE;
|
||||
case I2C_RECEIVE:
|
||||
if (!i2c.clock && new_clock)
|
||||
{
|
||||
i2c.byte <<= 1;
|
||||
if (new_data)
|
||||
i2c.byte |= 1;
|
||||
else
|
||||
i2c.byte &= 0xFE;
|
||||
i2c.pos++;
|
||||
if (i2c.pos == 8)
|
||||
{
|
||||
prom_write(i2c.byte);
|
||||
i2c.state = I2C_ACKNOWLEDGE;
|
||||
}
|
||||
}
|
||||
else if (i2c.clock && new_clock && new_data && !i2c.last_data) /*Stop bit*/
|
||||
{
|
||||
// pclog("Stop bit received\n");
|
||||
i2c.state = I2C_IDLE;
|
||||
prom_stop();
|
||||
}
|
||||
else if (i2c.clock && new_clock && !new_data && i2c.last_data) /*Start bit*/
|
||||
{
|
||||
// pclog("Start bit received\n");
|
||||
i2c.pos = 0;
|
||||
prom.state = PROM_IDLE;
|
||||
}
|
||||
break;
|
||||
|
||||
case I2C_ACKNOWLEDGE:
|
||||
if (!i2c.clock && new_clock)
|
||||
{
|
||||
// pclog("Acknowledging transfer\n");
|
||||
new_data = 0;
|
||||
i2c.pos = 0;
|
||||
if (i2c.transmit == TRANSMITTER_HOST)
|
||||
i2c.state = I2C_RECEIVE_WAIT;
|
||||
else
|
||||
i2c.state = I2C_TRANSMIT;
|
||||
}
|
||||
break;
|
||||
|
||||
case I2C_TRANSACKNOWLEDGE:
|
||||
if (!i2c.clock && new_clock)
|
||||
{
|
||||
if (new_data) /*It's not acknowledged - must be end of transfer*/
|
||||
{
|
||||
// pclog("End of transfer\n");
|
||||
i2c.state = I2C_IDLE;
|
||||
prom_stop();
|
||||
}
|
||||
else /*Next byte to transfer*/
|
||||
{
|
||||
i2c.state = I2C_TRANSMIT_START;
|
||||
prom_next_byte();
|
||||
i2c.pos = 0;
|
||||
// pclog("Next byte - %02X\n",i2c.byte);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case I2C_TRANSMIT_WAIT:
|
||||
if (i2c.clock && new_clock)
|
||||
{
|
||||
if (i2c.last_data && !new_data) /*Start bit*/
|
||||
{
|
||||
prom_next_byte();
|
||||
i2c.pos = 0;
|
||||
// pclog("Next byte - %02X\n",i2c.byte);
|
||||
}
|
||||
if (!i2c.last_data && new_data) /*Stop bit*/
|
||||
{
|
||||
// pclog("Stop bit received\n");
|
||||
i2c.state = I2C_IDLE;
|
||||
prom_stop();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case I2C_TRANSMIT_START:
|
||||
if (!i2c.clock && new_clock)
|
||||
i2c.state = I2C_TRANSMIT;
|
||||
if (i2c.clock && new_clock && !i2c.last_data && new_data) /*Stop bit*/
|
||||
{
|
||||
// pclog("Stop bit received\n");
|
||||
i2c.state = I2C_IDLE;
|
||||
prom_stop();
|
||||
}
|
||||
case I2C_TRANSMIT:
|
||||
if (!i2c.clock && new_clock)
|
||||
{
|
||||
i2c.clock = new_clock;
|
||||
// if (!i2c.pos)
|
||||
// pclog("Transmit byte %02x\n", i2c.byte);
|
||||
i2c.data = new_data = i2c.byte & 0x80;
|
||||
// pclog("Transmit bit %i %i\n", i2c.byte, i2c.pos);
|
||||
i2c.byte <<= 1;
|
||||
i2c.pos++;
|
||||
return;
|
||||
}
|
||||
if (i2c.clock && !new_clock && i2c.pos == 8)
|
||||
{
|
||||
i2c.state = I2C_TRANSACKNOWLEDGE;
|
||||
// pclog("Acknowledge mode\n");
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
if (!i2c.clock && new_clock)
|
||||
i2c.data = new_data;
|
||||
i2c.last_data = new_data;
|
||||
i2c.clock = new_clock;
|
||||
}
|
||||
|
||||
int ddc_read_clock(void)
|
||||
{
|
||||
return i2c.clock;
|
||||
}
|
||||
int ddc_read_data(void)
|
||||
{
|
||||
if (i2c.state == I2C_TRANSMIT || i2c.state == I2C_ACKNOWLEDGE)
|
||||
return i2c.data;
|
||||
if (i2c.state == I2C_RECEIVE_WAIT)
|
||||
return 0; /*ACK*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ddc_init(void)
|
||||
{
|
||||
int c;
|
||||
uint8_t checksum = 0;
|
||||
|
||||
for (c = 0; c < 127; c++)
|
||||
checksum += edid_data[c];
|
||||
edid_data[127] = 256 - checksum;
|
||||
|
||||
i2c.clock = 1;
|
||||
i2c.data = 1;
|
||||
i2c_eeprom_close(eeprom);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#include <86box/dma.h>
|
||||
#include <86box/plat.h>
|
||||
#include <86box/video.h>
|
||||
#include <86box/i2c.h>
|
||||
#include <86box/vid_ddc.h>
|
||||
#include <86box/vid_svga.h>
|
||||
#include <86box/vid_svga_render.h>
|
||||
|
||||
@@ -499,6 +501,8 @@ typedef struct mystique_t
|
||||
|
||||
mutex_t *lock;
|
||||
} dma;
|
||||
|
||||
void *i2c, *i2c_ddc, *ddc;
|
||||
} mystique_t;
|
||||
|
||||
|
||||
@@ -1019,7 +1023,15 @@ mystique_read_xreg(mystique_t *mystique, int reg)
|
||||
ret = mystique->xgenioctrl;
|
||||
break;
|
||||
case XREG_XGENIODATA:
|
||||
ret = mystique->xgeniodata;
|
||||
ret = mystique->xgeniodata & 0xf0;
|
||||
if (i2c_gpio_get_scl(mystique->i2c_ddc))
|
||||
ret |= 0x08;
|
||||
if (i2c_gpio_get_scl(mystique->i2c))
|
||||
ret |= 0x04;
|
||||
if (i2c_gpio_get_sda(mystique->i2c_ddc))
|
||||
ret |= 0x02;
|
||||
if (i2c_gpio_get_sda(mystique->i2c))
|
||||
ret |= 0x01;
|
||||
break;
|
||||
|
||||
case XREG_XSYSPLLM:
|
||||
@@ -1154,6 +1166,8 @@ mystique_write_xreg(mystique_t *mystique, int reg, uint8_t val)
|
||||
|
||||
case XREG_XGENIOCTRL:
|
||||
mystique->xgenioctrl = val;
|
||||
i2c_gpio_set(mystique->i2c_ddc, !(mystique->xgenioctrl & 0x08) || (mystique->xgeniodata & 0x08), !(mystique->xgenioctrl & 0x02) || (mystique->xgeniodata & 0x02));
|
||||
i2c_gpio_set(mystique->i2c, !(mystique->xgenioctrl & 0x04) || (mystique->xgeniodata & 0x04), !(mystique->xgenioctrl & 0x01) || (mystique->xgeniodata & 0x01));
|
||||
break;
|
||||
case XREG_XGENIODATA:
|
||||
mystique->xgeniodata = val;
|
||||
@@ -5009,6 +5023,10 @@ mystique_init(const device_t *info)
|
||||
|
||||
mystique->svga.vsync_callback = mystique_vsync_callback;
|
||||
|
||||
mystique->i2c = i2c_gpio_init("i2c_mga");
|
||||
mystique->i2c_ddc = i2c_gpio_init("ddc_mga");
|
||||
mystique->ddc = ddc_init(i2c_gpio_get_bus(mystique->i2c_ddc));
|
||||
|
||||
return mystique;
|
||||
}
|
||||
|
||||
@@ -5025,6 +5043,10 @@ mystique_close(void *p)
|
||||
|
||||
svga_close(&mystique->svga);
|
||||
|
||||
ddc_close(mystique->ddc);
|
||||
i2c_gpio_close(mystique->i2c_ddc);
|
||||
i2c_gpio_close(mystique->i2c);
|
||||
|
||||
free(mystique);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#include <86box/rom.h>
|
||||
#include <86box/plat.h>
|
||||
#include <86box/video.h>
|
||||
#include <86box/i2c.h>
|
||||
#include <86box/vid_ddc.h>
|
||||
#include <86box/vid_svga.h>
|
||||
#include <86box/vid_svga_render.h>
|
||||
#include "cpu.h"
|
||||
@@ -266,6 +268,9 @@ typedef struct s3_t
|
||||
int translate;
|
||||
int enable_8514;
|
||||
volatile int busy, force_busy;
|
||||
|
||||
uint8_t serialport;
|
||||
void *i2c, *ddc;
|
||||
} s3_t;
|
||||
|
||||
#define INT_VSY (1 << 0)
|
||||
@@ -274,6 +279,11 @@ typedef struct s3_t
|
||||
#define INT_FIFO_EMP (1 << 3)
|
||||
#define INT_MASK 0xf
|
||||
|
||||
#define SERIAL_PORT_SCW (1 << 0)
|
||||
#define SERIAL_PORT_SDW (1 << 1)
|
||||
#define SERIAL_PORT_SCR (1 << 2)
|
||||
#define SERIAL_PORT_SDR (1 << 3)
|
||||
|
||||
static void s3_updatemapping(s3_t *s3);
|
||||
|
||||
static void s3_accel_write(uint32_t addr, uint8_t val, void *p);
|
||||
@@ -1051,6 +1061,10 @@ s3_accel_write_fifo(s3_t *s3, uint32_t addr, uint8_t val)
|
||||
s3->accel.advfunc_cntl = val;
|
||||
s3_updatemapping(s3);
|
||||
break;
|
||||
case 0xff20:
|
||||
s3->serialport = val;
|
||||
i2c_gpio_set(s3->i2c, !!(val & SERIAL_PORT_SCW), !!(val & SERIAL_PORT_SDW));
|
||||
break;
|
||||
default:
|
||||
s3_accel_out_fifo(s3, addr & 0xffff, val);
|
||||
break;
|
||||
@@ -1084,7 +1098,10 @@ s3_accel_write_fifo_w(s3_t *s3, uint32_t addr, uint16_t val)
|
||||
default:
|
||||
s3_accel_write_fifo(s3, addr, val);
|
||||
s3_accel_write_fifo(s3, addr + 1, val >> 8);
|
||||
break;
|
||||
break;
|
||||
case 0xff20:
|
||||
s3_accel_write_fifo(s3, addr, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -1233,6 +1250,10 @@ s3_accel_write_fifo_l(s3_t *s3, uint32_t addr, uint32_t val)
|
||||
s3_updatemapping(s3);
|
||||
break;
|
||||
|
||||
case 0xff20:
|
||||
s3_accel_write_fifo(s3, addr, val);
|
||||
break;
|
||||
|
||||
default:
|
||||
s3_accel_write_fifo(s3, addr, val);
|
||||
s3_accel_write_fifo(s3, addr + 1, val >> 8);
|
||||
@@ -2900,6 +2921,14 @@ s3_accel_in(uint16_t port, void *p)
|
||||
else if ((s3->accel.cmd & 0x600) == 0x000 && (s3->accel.cmd & 0x100))
|
||||
s3_accel_start(1, 1, 0xffffffff, 0xff, s3);
|
||||
return temp;
|
||||
|
||||
case 0xff20: case 0xff21:
|
||||
temp = s3->serialport & ~(SERIAL_PORT_SCR | SERIAL_PORT_SDR);
|
||||
if ((s3->serialport & SERIAL_PORT_SCW) && i2c_gpio_get_scl(s3->i2c))
|
||||
temp |= SERIAL_PORT_SCR;
|
||||
if ((s3->serialport & SERIAL_PORT_SDW) && i2c_gpio_get_sda(s3->i2c))
|
||||
temp |= SERIAL_PORT_SDR;
|
||||
return temp;
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
@@ -2976,7 +3005,7 @@ s3_accel_read(uint32_t addr, void *p)
|
||||
case 0x8504:
|
||||
return s3->subsys_stat;
|
||||
case 0x8505:
|
||||
return s3->subsys_cntl;;
|
||||
return s3->subsys_cntl;
|
||||
default:
|
||||
return s3_accel_in(addr & 0xffff, p);
|
||||
}
|
||||
@@ -4643,6 +4672,9 @@ static void *s3_init(const device_t *info)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s3->i2c = i2c_gpio_init("ddc_s3");
|
||||
s3->ddc = ddc_init(i2c_gpio_get_bus(s3->i2c));
|
||||
|
||||
return s3;
|
||||
}
|
||||
|
||||
@@ -4736,6 +4768,9 @@ static void s3_close(void *p)
|
||||
thread_destroy_event(s3->wake_fifo_thread);
|
||||
thread_destroy_event(s3->fifo_not_full_event);
|
||||
|
||||
ddc_close(s3->ddc);
|
||||
i2c_gpio_close(s3->i2c);
|
||||
|
||||
free(s3);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <86box/device.h>
|
||||
#include <86box/plat.h>
|
||||
#include <86box/video.h>
|
||||
#include <86box/i2c.h>
|
||||
#include <86box/vid_ddc.h>
|
||||
#include <86box/vid_svga.h>
|
||||
#include <86box/vid_svga_render.h>
|
||||
@@ -284,8 +285,10 @@ typedef struct virge_t
|
||||
int virge_busy;
|
||||
|
||||
uint8_t subsys_stat, subsys_cntl, advfunc_cntl;
|
||||
|
||||
|
||||
uint8_t serialport;
|
||||
|
||||
void *i2c, *ddc;
|
||||
} virge_t;
|
||||
|
||||
static video_timings_t timing_diamond_stealth3d_2000_vlb = {VIDEO_BUS, 2, 2, 3, 28, 28, 45};
|
||||
@@ -444,7 +447,11 @@ static void s3_virge_out(uint16_t addr, uint8_t val, void *p)
|
||||
return;
|
||||
if ((svga->crtcreg == 0x36) && (svga->crtc[0x39] != 0xa5))
|
||||
return;
|
||||
if (svga->crtcreg >= 0x80)
|
||||
if ((svga->crtcreg >= 0x80)
|
||||
#if defined(DEV_BRANCH) && defined(USE_S3TRIO3D2X)
|
||||
&& !((virge->chip == S3_TRIO3D2X) && (svga->crtcreg == 0xaa))
|
||||
#endif
|
||||
)
|
||||
return;
|
||||
old = svga->crtc[svga->crtcreg];
|
||||
svga->crtc[svga->crtcreg] = val;
|
||||
@@ -547,6 +554,12 @@ static void s3_virge_out(uint16_t addr, uint8_t val, void *p)
|
||||
default: svga->bpp = 8; break;
|
||||
}
|
||||
break;
|
||||
|
||||
#if defined(DEV_BRANCH) && defined(USE_S3TRIO3D2X)
|
||||
case 0xaa:
|
||||
i2c_gpio_set(virge->i2c, !!(val & SERIAL_PORT_SCW), !!(val & SERIAL_PORT_SDW));
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
if (old != val)
|
||||
{
|
||||
@@ -604,6 +617,18 @@ static uint8_t s3_virge_in(uint16_t addr, void *p)
|
||||
case 0x51: ret = (svga->crtc[0x51] & 0xf0) | ((virge->bank >> 2) & 0xc) | ((virge->ma_ext >> 2) & 3); break;
|
||||
case 0x69: ret = virge->ma_ext; break;
|
||||
case 0x6a: ret = virge->bank; break;
|
||||
#if defined(DEV_BRANCH) && defined(USE_S3TRIO3D2X)
|
||||
case 0xaa: /* Trio3D DDC */
|
||||
if (virge->chip == S3_TRIO3D2X) {
|
||||
ret = svga->crtc[0xaa] & ~(SERIAL_PORT_SCR | SERIAL_PORT_SDR);
|
||||
if ((svga->crtc[0xaa] & SERIAL_PORT_SCW) && i2c_gpio_get_scl(virge->i2c))
|
||||
ret |= SERIAL_PORT_SCR;
|
||||
if ((svga->crtc[0xaa] & SERIAL_PORT_SDW) && i2c_gpio_get_sda(virge->i2c))
|
||||
ret |= SERIAL_PORT_SDR;
|
||||
break;
|
||||
}
|
||||
/* fall-through */
|
||||
#endif
|
||||
default: ret = svga->crtc[svga->crtcreg]; break;
|
||||
}
|
||||
break;
|
||||
@@ -894,11 +919,11 @@ s3_virge_mmio_read(uint32_t addr, void *p)
|
||||
|
||||
case 0xff20: case 0xff21:
|
||||
ret = virge->serialport & ~(SERIAL_PORT_SCR | SERIAL_PORT_SDR);
|
||||
if ((virge->serialport & SERIAL_PORT_SCW) && ddc_read_clock())
|
||||
if ((virge->serialport & SERIAL_PORT_SCW) && i2c_gpio_get_scl(virge->i2c))
|
||||
ret |= SERIAL_PORT_SCR;
|
||||
if ((virge->serialport & SERIAL_PORT_SDW) && ddc_read_data())
|
||||
if ((virge->serialport & SERIAL_PORT_SDW) && i2c_gpio_get_sda(virge->i2c))
|
||||
ret |= SERIAL_PORT_SDR;
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
return 0xff;
|
||||
}
|
||||
@@ -1180,7 +1205,7 @@ static void s3_virge_mmio_write(uint32_t addr, uint8_t val, void *p)
|
||||
|
||||
case 0xff20:
|
||||
virge->serialport = val;
|
||||
ddc_i2c_change((val & SERIAL_PORT_SCW) ? 1 : 0, (val & SERIAL_PORT_SDW) ? 1 : 0);
|
||||
i2c_gpio_set(virge->i2c, !!(val & SERIAL_PORT_SCW), !!(val & SERIAL_PORT_SDW));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -3845,7 +3870,8 @@ static void *s3_virge_init(const device_t *info)
|
||||
virge->fifo_not_full_event = thread_create_event();
|
||||
virge->fifo_thread = thread_create(fifo_thread, virge);
|
||||
|
||||
ddc_init();
|
||||
virge->i2c = i2c_gpio_init("ddc_s3_virge");
|
||||
virge->ddc = ddc_init(i2c_gpio_get_bus(virge->i2c));
|
||||
|
||||
return virge;
|
||||
}
|
||||
@@ -3864,6 +3890,9 @@ static void s3_virge_close(void *p)
|
||||
thread_destroy_event(virge->fifo_not_full_event);
|
||||
|
||||
svga_close(&virge->svga);
|
||||
|
||||
ddc_close(virge->ddc);
|
||||
i2c_gpio_close(virge->i2c);
|
||||
|
||||
free(virge);
|
||||
}
|
||||
|
||||
@@ -149,9 +149,9 @@ video_cards[] = {
|
||||
{ "virge375_vbe20_pci", &s3_virge_375_4_pci_device },
|
||||
{ "cl_gd5446_stb_pci", &gd5446_stb_pci_device },
|
||||
{ "tgui9440_pci", &tgui9440_pci_device },
|
||||
{ "voodoo_banshee_pci", &voodoo_banshee_device },
|
||||
{ "voodoo3_2k_pci", &voodoo_3_2000_device },
|
||||
{ "voodoo3_3k_pci", &voodoo_3_3000_device },
|
||||
{ "voodoo_banshee_pci", &voodoo_banshee_device },
|
||||
{ "mach64gx_vlb", &mach64gx_vlb_device },
|
||||
{ "et4000w32p_vlb", &et4000w32p_cardex_vlb_device },
|
||||
#if defined(DEV_BRANCH) && defined(USE_CL5422)
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <86box/device.h>
|
||||
#include <86box/plat.h>
|
||||
#include <86box/video.h>
|
||||
#include <86box/i2c.h>
|
||||
#include <86box/vid_ddc.h>
|
||||
#include <86box/vid_svga.h>
|
||||
#include <86box/vid_svga_render.h>
|
||||
@@ -116,6 +117,8 @@ typedef struct banshee_t
|
||||
uint32_t desktop_stride_tiled;
|
||||
|
||||
int type;
|
||||
|
||||
void *i2c, *i2c_ddc, *ddc;
|
||||
} banshee_t;
|
||||
|
||||
enum
|
||||
@@ -684,7 +687,8 @@ static void banshee_ext_outl(uint16_t addr, uint32_t val, void *p)
|
||||
case Video_vidSerialParallelPort:
|
||||
banshee->vidSerialParallelPort = val;
|
||||
// banshee_log("vidSerialParallelPort: write %08x %08x %04x(%08x):%08x\n", val, val & (VIDSERIAL_DDC_DCK_W | VIDSERIAL_DDC_DDA_W), CS,cs,cpu_state.pc);
|
||||
ddc_i2c_change((val & VIDSERIAL_DDC_DCK_W) ? 1 : 0, (val & VIDSERIAL_DDC_DDA_W) ? 1 : 0);
|
||||
i2c_gpio_set(banshee->i2c_ddc, !!(val & VIDSERIAL_DDC_DCK_W), !!(val & VIDSERIAL_DDC_DDA_W));
|
||||
i2c_gpio_set(banshee->i2c, !!(val & VIDSERIAL_I2C_SCK_W), !!(val & VIDSERIAL_I2C_SDA_W));
|
||||
break;
|
||||
|
||||
case Video_vidScreenSize:
|
||||
@@ -917,14 +921,14 @@ static uint32_t banshee_ext_inl(uint16_t addr, void *p)
|
||||
|
||||
case Video_vidSerialParallelPort:
|
||||
ret = banshee->vidSerialParallelPort & ~(VIDSERIAL_DDC_DCK_R | VIDSERIAL_DDC_DDA_R);
|
||||
if ((banshee->vidSerialParallelPort & VIDSERIAL_DDC_DCK_W) && ddc_read_clock())
|
||||
if (!(banshee->vidSerialParallelPort & VIDSERIAL_DDC_DCK_W) || i2c_gpio_get_scl(banshee->i2c_ddc))
|
||||
ret |= VIDSERIAL_DDC_DCK_R;
|
||||
if ((banshee->vidSerialParallelPort & VIDSERIAL_DDC_DDA_W) && ddc_read_data())
|
||||
if (!(banshee->vidSerialParallelPort & VIDSERIAL_DDC_DDA_W) || i2c_gpio_get_sda(banshee->i2c_ddc))
|
||||
ret |= VIDSERIAL_DDC_DDA_R;
|
||||
ret = ret & ~(VIDSERIAL_I2C_SCK_R | VIDSERIAL_I2C_SDA_R);
|
||||
if (banshee->vidSerialParallelPort & VIDSERIAL_I2C_SCK_W)
|
||||
if (!(banshee->vidSerialParallelPort & VIDSERIAL_I2C_SCK_W) || i2c_gpio_get_scl(banshee->i2c))
|
||||
ret |= VIDSERIAL_I2C_SCK_R;
|
||||
if (banshee->vidSerialParallelPort & VIDSERIAL_I2C_SDA_W)
|
||||
if (!(banshee->vidSerialParallelPort & VIDSERIAL_I2C_SDA_W) || i2c_gpio_get_sda(banshee->i2c))
|
||||
ret |= VIDSERIAL_I2C_SDA_R;
|
||||
// banshee_log("vidSerialParallelPort: read %08x %08x %04x(%08x):%08x\n", ret, ret & (VIDSERIAL_DDC_DCK_R | VIDSERIAL_DDC_DDA_R), CS,cs,cpu_state.pc);
|
||||
break;
|
||||
@@ -2640,7 +2644,9 @@ static void *banshee_init_common(const device_t *info, wchar_t *fn, int has_sgra
|
||||
|
||||
banshee->vidSerialParallelPort = VIDSERIAL_DDC_DCK_W | VIDSERIAL_DDC_DDA_W;
|
||||
|
||||
ddc_init();
|
||||
banshee->i2c = i2c_gpio_init("i2c_voodoo_banshee");
|
||||
banshee->i2c_ddc = i2c_gpio_init("ddc_voodoo_banshee");
|
||||
banshee->ddc = ddc_init(i2c_gpio_get_bus(banshee->i2c_ddc));
|
||||
|
||||
video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_banshee);
|
||||
|
||||
@@ -2687,6 +2693,9 @@ static void banshee_close(void *p)
|
||||
|
||||
voodoo_card_close(banshee->voodoo);
|
||||
svga_close(&banshee->svga);
|
||||
ddc_close(banshee->ddc);
|
||||
i2c_gpio_close(banshee->i2c_ddc);
|
||||
i2c_gpio_close(banshee->i2c);
|
||||
|
||||
free(banshee);
|
||||
}
|
||||
@@ -2708,7 +2717,7 @@ static void banshee_force_redraw(void *p)
|
||||
|
||||
const device_t voodoo_banshee_device =
|
||||
{
|
||||
"Voodoo Banshee PCI (reference)",
|
||||
"3dfx Voodoo Banshee",
|
||||
DEVICE_PCI,
|
||||
0,
|
||||
banshee_init,
|
||||
@@ -2722,7 +2731,7 @@ const device_t voodoo_banshee_device =
|
||||
|
||||
const device_t creative_voodoo_banshee_device =
|
||||
{
|
||||
"Creative Labs 3D Blaster Banshee PCI",
|
||||
"Creative 3D Blaster Banshee",
|
||||
DEVICE_PCI,
|
||||
0,
|
||||
creative_banshee_init,
|
||||
@@ -2736,7 +2745,7 @@ const device_t creative_voodoo_banshee_device =
|
||||
|
||||
const device_t voodoo_3_2000_device =
|
||||
{
|
||||
"Voodoo 3 2000 PCI",
|
||||
"3dfx Voodoo3 2000",
|
||||
DEVICE_PCI,
|
||||
0,
|
||||
v3_2000_init,
|
||||
@@ -2750,7 +2759,7 @@ const device_t voodoo_3_2000_device =
|
||||
|
||||
const device_t voodoo_3_3000_device =
|
||||
{
|
||||
"Voodoo 3 3000 PCI",
|
||||
"3dfx Voodoo3 3000",
|
||||
DEVICE_PCI,
|
||||
0,
|
||||
v3_3000_init,
|
||||
|
||||
@@ -654,7 +654,7 @@ MCHOBJ := machine.o machine_table.o \
|
||||
|
||||
DEVOBJ := bugger.o hwm.o hwm_lm75.o hwm_lm78.o hwm_gl518sm.o hwm_vt82c686.o ibm_5161.o isamem.o isartc.o \
|
||||
lpt.o pci_bridge.o postcard.o serial.o vpc2007.o \
|
||||
smbus.o smbus_piix4.o \
|
||||
i2c.o i2c_eeprom.o i2c_gpio.o smbus_piix4.o \
|
||||
keyboard.o \
|
||||
keyboard_xt.o keyboard_at.o \
|
||||
mouse.o \
|
||||
|
||||
Reference in New Issue
Block a user