Merge pull request #1110 from richardg867/master

I2C/SMBus overhaul
This commit is contained in:
Miran Grča
2020-11-25 23:14:32 +01:00
committed by GitHub
35 changed files with 2111 additions and 1708 deletions

View File

@@ -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);

View File

@@ -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 */
}

View File

@@ -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,

View File

@@ -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)

View File

@@ -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");

View File

@@ -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 */

View File

@@ -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;

View File

@@ -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}
};

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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
View 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
View 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
View 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;
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -84,6 +84,7 @@ typedef struct
pc_timer_t timer;
nvr_t *nvr;
apm_t *apm;
void *i2c;
} acpi_t;

View File

@@ -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
View 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*/

View File

@@ -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*/

View File

@@ -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

View File

@@ -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,

View File

@@ -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*/

View File

@@ -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
};

View File

@@ -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,

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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)

View File

@@ -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,

View File

@@ -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 \