DDC/I2C/SMBus overhaul (incomplete, commit for the night)
This commit is contained in:
@@ -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,17 @@ typedef struct {
|
||||
uint16_t regs[32];
|
||||
uint8_t addr_register;
|
||||
|
||||
uint8_t smbus_addr;
|
||||
uint8_t i2c_addr;
|
||||
} 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_read_byte(void *bus, uint8_t addr, void *priv);
|
||||
static uint8_t gl518sm_i2c_read_byte_cmd(void *bus, uint8_t addr, uint8_t cmd, void *priv);
|
||||
static uint16_t gl518sm_i2c_read_word_cmd(void *bus, uint8_t addr, uint8_t cmd, 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 void gl518sm_i2c_write_byte(void *bus, uint8_t addr, uint8_t val, void *priv);
|
||||
static void gl518sm_i2c_write_byte_cmd(void *bus, uint8_t addr, uint8_t cmd, uint8_t val, void *priv);
|
||||
static void gl518sm_i2c_write_word_cmd(void *bus, uint8_t addr, uint8_t cmd, uint16_t val, void *priv);
|
||||
static uint8_t gl518sm_write(gl518sm_t *dev, uint8_t reg, uint16_t val);
|
||||
static void gl518sm_reset(gl518sm_t *dev);
|
||||
|
||||
@@ -79,24 +79,24 @@ gl518sm_log(const char *fmt, ...)
|
||||
static void
|
||||
gl518sm_remap(gl518sm_t *dev, uint8_t addr)
|
||||
{
|
||||
gl518sm_log("GL518SM: remapping to SMBus %02Xh\n", addr);
|
||||
gl518sm_log("GL518SM: remapping to I2C %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,
|
||||
NULL, gl518sm_i2c_read_byte, gl518sm_i2c_read_byte_cmd, gl518sm_i2c_read_word_cmd, NULL,
|
||||
NULL, gl518sm_i2c_write_byte, gl518sm_i2c_write_byte_cmd, gl518sm_i2c_write_word_cmd, 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,
|
||||
NULL, gl518sm_i2c_read_byte, gl518sm_i2c_read_byte_cmd, gl518sm_i2c_read_word_cmd, NULL,
|
||||
NULL, gl518sm_i2c_write_byte, gl518sm_i2c_write_byte_cmd, gl518sm_i2c_write_word_cmd, NULL,
|
||||
dev);
|
||||
|
||||
dev->smbus_addr = addr;
|
||||
dev->i2c_addr = addr;
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
gl518sm_smbus_read_byte(uint8_t addr, void *priv)
|
||||
gl518sm_i2c_read_byte(void *bus, uint8_t addr, void *priv)
|
||||
{
|
||||
gl518sm_t *dev = (gl518sm_t *) priv;
|
||||
return gl518sm_read(dev, dev->addr_register);
|
||||
@@ -104,7 +104,7 @@ 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)
|
||||
gl518sm_i2c_read_byte_cmd(void *bus, uint8_t addr, uint8_t cmd, void *priv)
|
||||
{
|
||||
gl518sm_t *dev = (gl518sm_t *) priv;
|
||||
return gl518sm_read(dev, cmd);
|
||||
@@ -112,7 +112,7 @@ 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)
|
||||
gl518sm_i2c_read_word_cmd(void *bus, uint8_t addr, uint8_t cmd, void *priv)
|
||||
{
|
||||
gl518sm_t *dev = (gl518sm_t *) priv;
|
||||
return gl518sm_read(dev, cmd);
|
||||
@@ -168,7 +168,7 @@ gl518sm_read(gl518sm_t *dev, uint8_t reg)
|
||||
|
||||
|
||||
static void
|
||||
gl518sm_smbus_write_byte(uint8_t addr, uint8_t val, void *priv)
|
||||
gl518sm_i2c_write_byte(void *bus, uint8_t addr, uint8_t val, void *priv)
|
||||
{
|
||||
gl518sm_t *dev = (gl518sm_t *) priv;
|
||||
dev->addr_register = val;
|
||||
@@ -176,7 +176,7 @@ 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)
|
||||
gl518sm_i2c_write_byte_cmd(void *bus, uint8_t addr, uint8_t cmd, uint8_t val, void *priv)
|
||||
{
|
||||
gl518sm_t *dev = (gl518sm_t *) priv;
|
||||
gl518sm_write(dev, cmd, val);
|
||||
@@ -184,7 +184,7 @@ 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)
|
||||
gl518sm_i2c_write_word_cmd(void *bus, uint8_t addr, uint8_t cmd, uint16_t val, void *priv)
|
||||
{
|
||||
gl518sm_t *dev = (gl518sm_t *) priv;
|
||||
gl518sm_write(dev, cmd, val);
|
||||
@@ -290,7 +290,7 @@ gl518sm_init(const device_t *info)
|
||||
}
|
||||
|
||||
|
||||
/* GL518SM on SMBus address 2Ch */
|
||||
/* GL518SM on I2C address 2Ch */
|
||||
const device_t gl518sm_2c_device = {
|
||||
"Genesys Logic GL518SM Hardware Monitor",
|
||||
DEVICE_ISA,
|
||||
@@ -300,7 +300,7 @@ const device_t gl518sm_2c_device = {
|
||||
NULL
|
||||
};
|
||||
|
||||
/* GL518SM on SMBus address 2Dh */
|
||||
/* GL518SM on I2C address 2Dh */
|
||||
const device_t gl518sm_2d_device = {
|
||||
"Genesys Logic GL518SM Hardware Monitor",
|
||||
DEVICE_ISA,
|
||||
|
@@ -23,19 +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_read_byte(void *bus, uint8_t addr, void *priv);
|
||||
static uint8_t lm75_i2c_read_byte_cmd(void *bus, uint8_t addr, uint8_t cmd, void *priv);
|
||||
static uint16_t lm75_i2c_read_word_cmd(void *bus, uint8_t addr, uint8_t cmd, void *priv);
|
||||
static void lm75_i2c_write_byte(void *bus, uint8_t addr, uint8_t val, void *priv);
|
||||
static void lm75_i2c_write_byte_cmd(void *bus, uint8_t addr, uint8_t cmd, uint8_t val, void *priv);
|
||||
static void lm75_i2c_write_word_cmd(void *bus, uint8_t addr, uint8_t cmd, uint16_t val, void *priv);
|
||||
static void lm75_reset(lm75_t *dev);
|
||||
|
||||
|
||||
@@ -62,24 +62,24 @@ lm75_log(const char *fmt, ...)
|
||||
void
|
||||
lm75_remap(lm75_t *dev, uint8_t addr)
|
||||
{
|
||||
lm75_log("LM75: remapping to SMBus %02Xh\n", addr);
|
||||
lm75_log("LM75: remapping to I2C %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,
|
||||
NULL, lm75_i2c_read_byte, lm75_i2c_read_byte_cmd, lm75_i2c_read_word_cmd, NULL,
|
||||
NULL, lm75_i2c_write_byte, lm75_i2c_write_byte_cmd, lm75_i2c_write_word_cmd, 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,
|
||||
NULL, lm75_i2c_read_byte, lm75_i2c_read_byte_cmd, lm75_i2c_read_word_cmd, NULL,
|
||||
NULL, lm75_i2c_write_byte, lm75_i2c_write_byte_cmd, lm75_i2c_write_word_cmd, NULL,
|
||||
dev);
|
||||
|
||||
dev->smbus_addr = addr;
|
||||
dev->i2c_addr = addr;
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
lm75_smbus_read_byte(uint8_t addr, void *priv)
|
||||
lm75_i2c_read_byte(void *bus, uint8_t addr, void *priv)
|
||||
{
|
||||
lm75_t *dev = (lm75_t *) priv;
|
||||
return lm75_read(dev, dev->addr_register);
|
||||
@@ -87,14 +87,14 @@ 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)
|
||||
lm75_i2c_read_byte_cmd(void *bus, uint8_t addr, uint8_t cmd, void *priv)
|
||||
{
|
||||
lm75_t *dev = (lm75_t *) priv;
|
||||
return lm75_read(dev, cmd);
|
||||
}
|
||||
|
||||
static uint16_t
|
||||
lm75_smbus_read_word_cmd(uint8_t addr, uint8_t cmd, void *priv)
|
||||
lm75_i2c_read_word_cmd(void *bus, uint8_t addr, uint8_t cmd, void *priv)
|
||||
{
|
||||
lm75_t *dev = (lm75_t *) priv;
|
||||
uint8_t rethi = 0;
|
||||
@@ -129,9 +129,9 @@ lm75_read(lm75_t *dev, uint8_t reg)
|
||||
|
||||
/* 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);
|
||||
the main monitor address through an internal I2C call, if necessary. */
|
||||
if ((reg > 0x7) && ((reg & 0xf8) != 0x50) && (dev->as99127f_i2c_addr < 0x80))
|
||||
ret = i2c_read_byte_cmd(i2c_smbus, dev->as99127f_i2c_addr, reg);
|
||||
else 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 */
|
||||
@@ -146,7 +146,7 @@ lm75_read(lm75_t *dev, uint8_t reg)
|
||||
|
||||
|
||||
static void
|
||||
lm75_smbus_write_byte(uint8_t addr, uint8_t val, void *priv)
|
||||
lm75_i2c_write_byte(void *bus, uint8_t addr, uint8_t val, void *priv)
|
||||
{
|
||||
lm75_t *dev = (lm75_t *) priv;
|
||||
dev->addr_register = val;
|
||||
@@ -154,7 +154,7 @@ 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)
|
||||
lm75_i2c_write_byte_cmd(void *bus, uint8_t addr, uint8_t cmd, uint8_t val, void *priv)
|
||||
{
|
||||
lm75_t *dev = (lm75_t *) priv;
|
||||
lm75_write(dev, cmd, val);
|
||||
@@ -162,7 +162,7 @@ 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)
|
||||
lm75_i2c_write_word_cmd(void *bus, uint8_t addr, uint8_t cmd, uint16_t val, void *priv)
|
||||
{
|
||||
lm75_t *dev = (lm75_t *) priv;
|
||||
uint8_t valhi = (val >> 8);
|
||||
@@ -196,9 +196,9 @@ lm75_write(lm75_t *dev, uint8_t reg, uint8_t 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);
|
||||
the main monitor address through an internal I2C call, if necessary. */
|
||||
if ((reg > 0x7) && ((reg & 0xf8) != 0x50) && (dev->as99127f_i2c_addr < 0x80)) {
|
||||
i2c_write_byte_cmd(i2c_smbus, dev->as99127f_i2c_addr, reg, val);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -247,7 +247,7 @@ lm75_init(const device_t *info)
|
||||
hwm_values.temperatures[dev->local >> 8] = 30;
|
||||
dev->values = &hwm_values;
|
||||
|
||||
dev->as99127f_smbus_addr = 0x80;
|
||||
dev->as99127f_i2c_addr = 0x80;
|
||||
|
||||
lm75_reset(dev);
|
||||
|
||||
|
@@ -25,11 +25,11 @@
|
||||
#include <86box/device.h>
|
||||
#include <86box/io.h>
|
||||
#include "cpu.h"
|
||||
#include <86box/smbus.h>
|
||||
#include <86box/i2c.h>
|
||||
#include <86box/hwm.h>
|
||||
|
||||
|
||||
#define LM78_SMBUS 0x010000
|
||||
#define LM78_I2C 0x010000
|
||||
#define LM78_W83781D 0x020000
|
||||
#define LM78_AS99127F_REV1 0x040000
|
||||
#define LM78_AS99127F_REV2 0x080000
|
||||
@@ -56,19 +56,19 @@ typedef struct {
|
||||
uint8_t addr_register;
|
||||
uint8_t data_register;
|
||||
|
||||
uint8_t smbus_addr;
|
||||
uint8_t i2c_addr;
|
||||
} lm78_t;
|
||||
|
||||
|
||||
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_byte(void *bus, uint8_t addr, void *priv);
|
||||
static uint8_t lm78_i2c_read_byte_cmd(void *bus, uint8_t addr, uint8_t cmd, void *priv);
|
||||
static uint16_t lm78_i2c_read_word_cmd(void *bus, uint8_t addr, uint8_t cmd, 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 void lm78_i2c_write_byte(void *bus, uint8_t addr, uint8_t val, void *priv);
|
||||
static void lm78_i2c_write_byte_cmd(void *bus, uint8_t addr, uint8_t cmd, uint8_t val, void *priv);
|
||||
static void lm78_i2c_write_word_cmd(void *bus, uint8_t addr, uint8_t cmd, uint16_t val, 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,29 +98,29 @@ 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);
|
||||
lm78_log("LM78: remapping to I2C %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,
|
||||
NULL, lm78_i2c_read_byte, lm78_i2c_read_byte_cmd, lm78_i2c_read_word_cmd, NULL,
|
||||
NULL, lm78_i2c_write_byte, lm78_i2c_write_byte_cmd, lm78_i2c_write_word_cmd, 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,
|
||||
NULL, lm78_i2c_read_byte, lm78_i2c_read_byte_cmd, lm78_i2c_read_word_cmd, NULL,
|
||||
NULL, lm78_i2c_write_byte, lm78_i2c_write_byte_cmd, lm78_i2c_write_word_cmd, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,7 +159,7 @@ lm78_isa_read(uint16_t port, void *priv)
|
||||
|
||||
|
||||
static uint8_t
|
||||
lm78_smbus_read_byte(uint8_t addr, void *priv)
|
||||
lm78_i2c_read_byte(void *bus, uint8_t addr, void *priv)
|
||||
{
|
||||
lm78_t *dev = (lm78_t *) priv;
|
||||
return lm78_read(dev, dev->addr_register, LM78_WINBOND_BANK);
|
||||
@@ -167,7 +167,7 @@ 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)
|
||||
lm78_i2c_read_byte_cmd(void *bus, uint8_t addr, uint8_t cmd, void *priv)
|
||||
{
|
||||
lm78_t *dev = (lm78_t *) priv;
|
||||
return lm78_read(dev, cmd, LM78_WINBOND_BANK);
|
||||
@@ -175,7 +175,7 @@ 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)
|
||||
lm78_i2c_read_word_cmd(void *bus, 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);
|
||||
@@ -265,7 +265,7 @@ 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)
|
||||
lm78_i2c_write_byte(void *bus, uint8_t addr, uint8_t val, void *priv)
|
||||
{
|
||||
lm78_t *dev = (lm78_t *) priv;
|
||||
dev->addr_register = val;
|
||||
@@ -273,7 +273,7 @@ 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)
|
||||
lm78_i2c_write_byte_cmd(void *bus, 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);
|
||||
@@ -281,7 +281,7 @@ 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)
|
||||
lm78_i2c_write_word_cmd(void *bus, 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);
|
||||
@@ -354,13 +354,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 +376,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 +412,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 +469,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 +519,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 +536,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 +558,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 +569,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 +592,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 +603,7 @@ const device_t as99127f_rev2_device = {
|
||||
const device_t w83782d_device = {
|
||||
"Winbond W83782D Hardware Monitor",
|
||||
DEVICE_ISA,
|
||||
0x290 | LM78_SMBUS | LM78_W83782D,
|
||||
0x290 | LM78_I2C | LM78_W83782D,
|
||||
lm78_init, lm78_close, NULL,
|
||||
{ NULL }, NULL, NULL,
|
||||
NULL
|
||||
|
@@ -24,7 +24,6 @@
|
||||
#include <86box/86box.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/io.h>
|
||||
#include <86box/smbus.h>
|
||||
#include <86box/hwm.h>
|
||||
|
||||
|
||||
|
496
src/device/i2c.c
Normal file
496
src/device/i2c.c
Normal file
@@ -0,0 +1,496 @@
|
||||
/*
|
||||
* 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 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_ {
|
||||
void (*read_quick)(void *bus, uint8_t addr, void *priv);
|
||||
uint8_t (*read_byte)(void *bus, uint8_t addr, void *priv);
|
||||
uint8_t (*read_byte_cmd)(void *bus, uint8_t addr, uint8_t cmd, void *priv);
|
||||
uint16_t (*read_word_cmd)(void *bus, uint8_t addr, uint8_t cmd, void *priv);
|
||||
uint8_t (*read_block_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv);
|
||||
|
||||
void (*write_quick)(void *bus, uint8_t addr, void *priv);
|
||||
void (*write_byte)(void *bus, uint8_t addr, uint8_t val, void *priv);
|
||||
void (*write_byte_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint8_t val, void *priv);
|
||||
void (*write_word_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint16_t val, void *priv);
|
||||
void (*write_block_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, 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;
|
||||
|
||||
#define ENABLE_I2C_LOG 1
|
||||
#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)
|
||||
{
|
||||
if (!bus_handle)
|
||||
return;
|
||||
|
||||
free(bus_handle);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
i2c_sethandler(void *bus_handle, uint8_t base, int size,
|
||||
void (*read_quick)(void *bus, uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte)(void *bus, uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte_cmd)(void *bus, uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint16_t (*read_word_cmd)(void *bus, uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint8_t (*read_block_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void (*write_quick)(void *bus, uint8_t addr, void *priv),
|
||||
void (*write_byte)(void *bus, uint8_t addr, uint8_t val, void *priv),
|
||||
void (*write_byte_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint8_t val, void *priv),
|
||||
void (*write_word_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint16_t val, void *priv),
|
||||
void (*write_block_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, 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->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;
|
||||
|
||||
bus->last[base + c] = q;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
i2c_removehandler(void *bus_handle, uint8_t base, int size,
|
||||
void (*read_quick)(void *bus, uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte)(void *bus, uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte_cmd)(void *bus, uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint16_t (*read_word_cmd)(void *bus, uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint8_t (*read_block_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void (*write_quick)(void *bus, uint8_t addr, void *priv),
|
||||
void (*write_byte)(void *bus, uint8_t addr, uint8_t val, void *priv),
|
||||
void (*write_byte_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint8_t val, void *priv),
|
||||
void (*write_word_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint16_t val, void *priv),
|
||||
void (*write_block_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, 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->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
|
||||
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,
|
||||
void (*read_quick)(void *bus, uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte)(void *bus, uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte_cmd)(void *bus, uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint16_t (*read_word_cmd)(void *bus, uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint8_t (*read_block_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void (*write_quick)(void *bus, uint8_t addr, void *priv),
|
||||
void (*write_byte)(void *bus, uint8_t addr, uint8_t val, void *priv),
|
||||
void (*write_byte_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint8_t val, void *priv),
|
||||
void (*write_word_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint16_t val, void *priv),
|
||||
void (*write_block_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void *priv)
|
||||
{
|
||||
if (set)
|
||||
i2c_sethandler(bus_handle, base, size, read_quick, read_byte, read_byte_cmd, read_word_cmd, read_block_cmd, write_quick, write_byte, write_byte_cmd, write_word_cmd, write_block_cmd, priv);
|
||||
else
|
||||
i2c_removehandler(bus_handle, base, size, read_quick, read_byte, read_byte_cmd, read_word_cmd, read_block_cmd, write_quick, write_byte, write_byte_cmd, write_word_cmd, write_block_cmd, 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;
|
||||
|
||||
return(!!bus->devices[addr]);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
i2c_read_quick(void *bus_handle, uint8_t addr)
|
||||
{
|
||||
i2c_bus_t *bus = (i2c_bus_t *) bus_handle;
|
||||
i2c_t *p;
|
||||
int found = 0;
|
||||
|
||||
if (!bus)
|
||||
return;
|
||||
|
||||
p = bus->devices[addr];
|
||||
if (p) {
|
||||
while(p) {
|
||||
if (p->read_byte) {
|
||||
p->read_quick(bus_handle, addr, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_log("I2C: read_quick(%s, %02X)\n", bus->name, addr);
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
i2c_read_byte(void *bus_handle, uint8_t addr)
|
||||
{
|
||||
uint8_t ret = 0xff;
|
||||
i2c_bus_t *bus = (i2c_bus_t *) bus_handle;
|
||||
i2c_t *p;
|
||||
int found = 0;
|
||||
|
||||
if (!bus)
|
||||
return(ret);
|
||||
|
||||
p = bus->devices[addr];
|
||||
if (p) {
|
||||
while(p) {
|
||||
if (p->read_byte) {
|
||||
ret &= p->read_byte(bus_handle, addr, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_log("I2C: read_byte(%s, %02X) = %02X\n", bus->name, addr, ret);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
uint8_t
|
||||
i2c_read_byte_cmd(void *bus_handle, uint8_t addr, uint8_t cmd)
|
||||
{
|
||||
uint8_t ret = 0xff;
|
||||
i2c_bus_t *bus = (i2c_bus_t *) bus_handle;
|
||||
i2c_t *p;
|
||||
int found = 0;
|
||||
|
||||
if (!bus)
|
||||
return(ret);
|
||||
|
||||
p = bus->devices[addr];
|
||||
if (p) {
|
||||
while(p) {
|
||||
if (p->read_byte_cmd) {
|
||||
ret &= p->read_byte_cmd(bus_handle, addr, cmd, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_log("I2C: read_byte_cmd(%s, %02X, %02X) = %02X\n", bus->name, addr, cmd, ret);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
uint16_t
|
||||
i2c_read_word_cmd(void *bus_handle, uint8_t addr, uint8_t cmd)
|
||||
{
|
||||
uint16_t ret = 0xffff;
|
||||
i2c_bus_t *bus = (i2c_bus_t *) bus_handle;
|
||||
i2c_t *p;
|
||||
int found = 0;
|
||||
|
||||
if (!bus)
|
||||
return(ret);
|
||||
|
||||
p = bus->devices[addr];
|
||||
if (p) {
|
||||
while(p) {
|
||||
if (p->read_word_cmd) {
|
||||
ret &= p->read_word_cmd(bus_handle, addr, cmd, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_log("I2C: read_word_cmd(%s, %02X, %02X) = %04X\n", bus->name, addr, cmd, ret);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
uint8_t
|
||||
i2c_read_block_cmd(void *bus_handle, uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len)
|
||||
{
|
||||
uint8_t ret = 0;
|
||||
i2c_bus_t *bus = (i2c_bus_t *) bus_handle;
|
||||
i2c_t *p;
|
||||
int found = 0;
|
||||
|
||||
if (!bus)
|
||||
return(ret);
|
||||
|
||||
p = bus->devices[addr];
|
||||
if (p) {
|
||||
while(p) {
|
||||
if (p->read_block_cmd) {
|
||||
ret = MAX(ret, p->read_block_cmd(bus_handle, addr, cmd, data, len, p->priv));
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_log("I2C: read_block_cmd(%s, %02X, %02X) = %02X\n", bus->name, addr, cmd, len);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
i2c_write_quick(void *bus_handle, uint8_t addr)
|
||||
{
|
||||
i2c_bus_t *bus = (i2c_bus_t *) bus_handle;
|
||||
i2c_t *p;
|
||||
int found = 0;
|
||||
|
||||
if (!bus)
|
||||
return;
|
||||
|
||||
p = bus->devices[addr];
|
||||
if (p) {
|
||||
while(p) {
|
||||
if (p->read_byte) {
|
||||
p->write_quick(bus_handle, addr, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_log("I2C: write_quick(%s, %02X)\n", bus->name, addr);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
i2c_write_byte(void *bus_handle, uint8_t addr, uint8_t val)
|
||||
{
|
||||
i2c_t *p;
|
||||
i2c_bus_t *bus = (i2c_bus_t *) bus_handle;
|
||||
int found = 0;
|
||||
|
||||
if (!bus)
|
||||
return;
|
||||
|
||||
if (bus->devices[addr]) {
|
||||
p = bus->devices[addr];
|
||||
while(p) {
|
||||
if (p->write_byte) {
|
||||
p->write_byte(bus_handle, addr, val, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_log("I2C: write_byte(%s, %02X, %02X)\n", bus->name, addr, val);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
i2c_write_byte_cmd(void *bus_handle, uint8_t addr, uint8_t cmd, uint8_t val)
|
||||
{
|
||||
i2c_t *p;
|
||||
i2c_bus_t *bus = (i2c_bus_t *) bus_handle;
|
||||
int found = 0;
|
||||
|
||||
if (!bus)
|
||||
return;
|
||||
|
||||
if (bus->devices[addr]) {
|
||||
p = bus->devices[addr];
|
||||
while(p) {
|
||||
if (p->write_byte_cmd) {
|
||||
p->write_byte_cmd(bus_handle, addr, cmd, val, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_log("I2C: write_byte_cmd(%s, %02X, %02X, %02X)\n", bus->name, addr, cmd, val);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
i2c_write_word_cmd(void *bus_handle, uint8_t addr, uint8_t cmd, uint16_t val)
|
||||
{
|
||||
i2c_t *p;
|
||||
i2c_bus_t *bus = (i2c_bus_t *) bus_handle;
|
||||
int found = 0;
|
||||
|
||||
if (!bus)
|
||||
return;
|
||||
|
||||
if (bus->devices[addr]) {
|
||||
p = bus->devices[addr];
|
||||
while(p) {
|
||||
if (p->write_word_cmd) {
|
||||
p->write_word_cmd(bus_handle, addr, cmd, val, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_log("I2C: write_word_cmd(%s, %02X, %02X, %04X)\n", bus->name, addr, cmd, val);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
i2c_write_block_cmd(void *bus_handle, uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len)
|
||||
{
|
||||
i2c_t *p;
|
||||
i2c_bus_t *bus = (i2c_bus_t *) bus_handle;
|
||||
int found = 0;
|
||||
|
||||
if (!bus)
|
||||
return;
|
||||
|
||||
p = bus->devices[addr];
|
||||
if (p) {
|
||||
while(p) {
|
||||
if (p->write_block_cmd) {
|
||||
p->write_block_cmd(bus_handle, addr, cmd, data, len, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_log("I2C: write_block_cmd(%s, %02X, %02X, %02X)\n", bus->name, addr, cmd, len);
|
||||
|
||||
return;
|
||||
}
|
277
src/device/i2c_gpio.c
Normal file
277
src/device/i2c_gpio.c
Normal file
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* 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 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 <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 <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_TRANSACKNOWLEDGE,
|
||||
I2C_TRANSMIT_WAIT
|
||||
};
|
||||
|
||||
enum {
|
||||
SLAVE_IDLE = 0,
|
||||
SLAVE_RECEIVEADDR,
|
||||
SLAVE_RECEIVEDATA,
|
||||
SLAVE_SENDDATA,
|
||||
SLAVE_INVALID
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
void *i2c;
|
||||
uint8_t scl, sda, state, slave_state, slave_addr, slave_reg,
|
||||
slave_writing, slave_rw, last_sda, pos, transmit, byte;
|
||||
} i2c_gpio_t;
|
||||
|
||||
|
||||
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));
|
||||
|
||||
dev->i2c = i2c_addbus(bus_name);
|
||||
dev->scl = dev->sda = 1;
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
i2c_gpio_close(void *dev_handle)
|
||||
{
|
||||
i2c_gpio_t *dev = (i2c_gpio_t *) dev_handle;
|
||||
|
||||
i2c_removebus(dev->i2c);
|
||||
|
||||
free(dev);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
i2c_gpio_next_byte(i2c_gpio_t *dev)
|
||||
{
|
||||
dev->byte = i2c_read_byte(dev->i2c, dev->slave_addr);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
i2c_gpio_write(i2c_gpio_t *dev)
|
||||
{
|
||||
switch (dev->slave_state) {
|
||||
case SLAVE_IDLE:
|
||||
dev->slave_addr = dev->byte >> 1;
|
||||
if (!i2c_has_device(dev->i2c, dev->slave_addr)) {
|
||||
dev->slave_state = SLAVE_INVALID;
|
||||
break;
|
||||
}
|
||||
dev->slave_rw = dev->byte & 1;
|
||||
dev->slave_writing = 0;
|
||||
if (dev->slave_rw) {
|
||||
dev->slave_state = SLAVE_SENDDATA;
|
||||
dev->transmit = TRANSMITTER_SLAVE;
|
||||
dev->byte = i2c_read_byte(dev->i2c, dev->slave_addr);
|
||||
} else {
|
||||
dev->slave_state = SLAVE_RECEIVEADDR;
|
||||
dev->transmit = TRANSMITTER_MASTER;
|
||||
}
|
||||
pclog("slave_idle %02X %d\n", dev->slave_addr, dev->slave_rw);
|
||||
break;
|
||||
|
||||
case SLAVE_RECEIVEADDR:
|
||||
pclog("slave_receiveaddr %02X %d\n", dev->slave_addr, dev->slave_rw);
|
||||
i2c_write_byte(dev->i2c, dev->slave_addr, dev->byte);
|
||||
dev->slave_writing = 1;
|
||||
dev->slave_reg = dev->byte;
|
||||
dev->slave_state = dev->slave_rw ? SLAVE_SENDDATA : SLAVE_RECEIVEDATA;
|
||||
break;
|
||||
|
||||
case SLAVE_RECEIVEDATA:
|
||||
pclog("slave_receivedata %02X %d = %02X\n", dev->slave_addr, dev->slave_rw, dev->byte);
|
||||
dev->slave_writing = 0;
|
||||
i2c_write_byte_cmd(dev->i2c, dev->slave_addr, dev->slave_reg, dev->byte);
|
||||
break;
|
||||
|
||||
case SLAVE_SENDDATA:
|
||||
pclog("slave_senddata %02X %d = %02X\n", dev->slave_addr, dev->slave_rw, dev->byte);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
i2c_gpio_stop(i2c_gpio_t *dev)
|
||||
{
|
||||
pclog("stopping... state = %d\n", dev->slave_state);
|
||||
if (dev->slave_writing)
|
||||
i2c_write_byte(dev->i2c, dev->slave_addr, dev->slave_reg);
|
||||
|
||||
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;
|
||||
|
||||
switch (dev->state) {
|
||||
case I2C_IDLE:
|
||||
if (dev->scl && scl && dev->last_sda && !sda) { /* start bit */
|
||||
dev->state = I2C_RECEIVE;
|
||||
dev->pos = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case I2C_RECEIVE_WAIT:
|
||||
if (!dev->scl && scl)
|
||||
dev->state = I2C_RECEIVE;
|
||||
/* 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) {
|
||||
i2c_gpio_write(dev);
|
||||
dev->state = I2C_ACKNOWLEDGE;
|
||||
}
|
||||
} else if (dev->scl && scl) {
|
||||
if (sda && !dev->last_sda) { /* stop bit */
|
||||
dev->state = I2C_IDLE;
|
||||
i2c_gpio_stop(dev);
|
||||
} else if (!sda && dev->last_sda) { /* start bit */
|
||||
dev->pos = 0;
|
||||
dev->slave_state = SLAVE_IDLE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case I2C_ACKNOWLEDGE:
|
||||
if (!dev->scl && scl) {
|
||||
sda = 0;
|
||||
dev->pos = 0;
|
||||
dev->state = (dev->transmit == TRANSMITTER_MASTER) ? I2C_RECEIVE_WAIT : I2C_TRANSMIT;
|
||||
}
|
||||
break;
|
||||
|
||||
case I2C_TRANSACKNOWLEDGE:
|
||||
if (!dev->scl && scl) {
|
||||
if (sda) { /* not acknowledged; must be end of transfer */
|
||||
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;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case I2C_TRANSMIT_WAIT:
|
||||
if (dev->scl && scl) {
|
||||
if (dev->last_sda && !sda) { /* start bit */
|
||||
i2c_gpio_next_byte(dev);
|
||||
dev->pos = 0;
|
||||
}
|
||||
if (!dev->last_sda && sda) { /* stop bit */
|
||||
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 bit */
|
||||
dev->state = I2C_IDLE;
|
||||
i2c_gpio_stop(dev);
|
||||
}
|
||||
/* fall-through */
|
||||
|
||||
case I2C_TRANSMIT:
|
||||
if (!dev->scl && scl) {
|
||||
dev->scl = scl;
|
||||
dev->sda = sda = dev->byte & 0x80;
|
||||
dev->byte <<= 1;
|
||||
dev->pos++;
|
||||
return;
|
||||
}
|
||||
if (dev->scl && !scl && (dev->pos == 8))
|
||||
dev->state = I2C_TRANSACKNOWLEDGE;
|
||||
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;
|
||||
if ((dev->state == I2C_TRANSMIT) || (dev->state == I2C_ACKNOWLEDGE))
|
||||
return dev->sda;
|
||||
else if (dev->state == I2C_RECEIVE_WAIT)
|
||||
return 0; /* ack */
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
i2c_gpio_get_bus(void *dev_handle)
|
||||
{
|
||||
i2c_gpio_t *dev = (i2c_gpio_t *) dev_handle;
|
||||
return dev->i2c;
|
||||
}
|
@@ -1,403 +0,0 @@
|
||||
/*
|
||||
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
||||
* running old operating systems and software designed for IBM
|
||||
* PC systems and compatibles from 1981 through fairly recent
|
||||
* system designs based on the PCI bus.
|
||||
*
|
||||
* This file is part of the 86Box distribution.
|
||||
*
|
||||
* Implement SMBus (System Management Bus) and its operations.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: RichardG, <richardg867@gmail.com>
|
||||
*
|
||||
* Copyright 2020 RichardG.
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#define HAVE_STDARG_H
|
||||
#include <86box/86box.h>
|
||||
#include <86box/smbus.h>
|
||||
|
||||
|
||||
#define NADDRS 128 /* SMBus supports 128 addresses */
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
|
||||
typedef struct _smbus_ {
|
||||
uint8_t (*read_byte)(uint8_t addr, void *priv);
|
||||
uint8_t (*read_byte_cmd)(uint8_t addr, uint8_t cmd, void *priv);
|
||||
uint16_t (*read_word_cmd)(uint8_t addr, uint8_t cmd, void *priv);
|
||||
uint8_t (*read_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv);
|
||||
|
||||
void (*write_byte)(uint8_t addr, uint8_t val, void *priv);
|
||||
void (*write_byte_cmd)(uint8_t addr, uint8_t cmd, uint8_t val, void *priv);
|
||||
void (*write_word_cmd)(uint8_t addr, uint8_t cmd, uint16_t val, void *priv);
|
||||
void (*write_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv);
|
||||
|
||||
void *priv;
|
||||
|
||||
struct _smbus_ *prev, *next;
|
||||
} smbus_t;
|
||||
|
||||
int smbus_initialized = 0;
|
||||
smbus_t *smbus[NADDRS], *smbus_last[NADDRS];
|
||||
|
||||
|
||||
#ifdef ENABLE_SMBUS_LOG
|
||||
int smbus_do_log = ENABLE_SMBUS_LOG;
|
||||
|
||||
|
||||
static void
|
||||
smbus_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (smbus_do_log) {
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define smbus_log(fmt, ...)
|
||||
#endif
|
||||
|
||||
|
||||
void
|
||||
smbus_init(void)
|
||||
{
|
||||
int c;
|
||||
smbus_t *p, *q;
|
||||
|
||||
if (!smbus_initialized) {
|
||||
for (c=0; c<NADDRS; c++)
|
||||
smbus[c] = smbus_last[c] = NULL;
|
||||
smbus_initialized = 1;
|
||||
}
|
||||
|
||||
for (c=0; c<NADDRS; c++) {
|
||||
if (smbus_last[c]) {
|
||||
/* Address c has at least one handler. */
|
||||
p = smbus_last[c];
|
||||
/* After this loop, p will have the pointer to the first handler. */
|
||||
while (p) {
|
||||
q = p->prev;
|
||||
free(p);
|
||||
p = q;
|
||||
}
|
||||
p = NULL;
|
||||
}
|
||||
|
||||
/* smbus[c] should be NULL. */
|
||||
smbus[c] = smbus_last[c] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
smbus_sethandler(uint8_t base, int size,
|
||||
uint8_t (*read_byte)(uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint16_t (*read_word_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint8_t (*read_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void (*write_byte)(uint8_t addr, uint8_t val, void *priv),
|
||||
void (*write_byte_cmd)(uint8_t addr, uint8_t cmd, uint8_t val, void *priv),
|
||||
void (*write_word_cmd)(uint8_t addr, uint8_t cmd, uint16_t val, void *priv),
|
||||
void (*write_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void *priv)
|
||||
{
|
||||
int c;
|
||||
smbus_t *p, *q = NULL;
|
||||
|
||||
if ((base + size) >= NADDRS)
|
||||
return;
|
||||
|
||||
for (c = 0; c < size; c++) {
|
||||
p = smbus_last[base + c];
|
||||
q = (smbus_t *) malloc(sizeof(smbus_t));
|
||||
memset(q, 0, sizeof(smbus_t));
|
||||
if (p) {
|
||||
p->next = q;
|
||||
q->prev = p;
|
||||
} else {
|
||||
smbus[base + c] = q;
|
||||
q->prev = NULL;
|
||||
}
|
||||
|
||||
q->read_byte = read_byte;
|
||||
q->read_byte_cmd = read_byte_cmd;
|
||||
q->read_word_cmd = read_word_cmd;
|
||||
q->read_block_cmd = read_block_cmd;
|
||||
|
||||
q->write_byte = write_byte;
|
||||
q->write_byte_cmd = write_byte_cmd;
|
||||
q->write_word_cmd = write_word_cmd;
|
||||
q->write_block_cmd = write_block_cmd;
|
||||
|
||||
q->priv = priv;
|
||||
q->next = NULL;
|
||||
|
||||
smbus_last[base + c] = q;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
smbus_removehandler(uint8_t base, int size,
|
||||
uint8_t (*read_byte)(uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint16_t (*read_word_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint8_t (*read_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void (*write_byte)(uint8_t addr, uint8_t val, void *priv),
|
||||
void (*write_byte_cmd)(uint8_t addr, uint8_t cmd, uint8_t val, void *priv),
|
||||
void (*write_word_cmd)(uint8_t addr, uint8_t cmd, uint16_t val, void *priv),
|
||||
void (*write_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void *priv)
|
||||
{
|
||||
int c;
|
||||
smbus_t *p, *q;
|
||||
|
||||
if ((base + size) >= NADDRS)
|
||||
return;
|
||||
|
||||
for (c = 0; c < size; c++) {
|
||||
p = smbus[base + c];
|
||||
if (!p)
|
||||
continue;
|
||||
while(p) {
|
||||
q = p->next;
|
||||
if ((p->read_byte == read_byte) && (p->read_byte_cmd == read_byte_cmd) &&
|
||||
(p->read_word_cmd == read_word_cmd) && (p->read_block_cmd == read_block_cmd) &&
|
||||
(p->write_byte == write_byte) && (p->write_byte_cmd == write_byte_cmd) &&
|
||||
(p->write_word_cmd == write_word_cmd) && (p->write_block_cmd == write_block_cmd) &&
|
||||
(p->priv == priv)) {
|
||||
if (p->prev)
|
||||
p->prev->next = p->next;
|
||||
else
|
||||
smbus[base + c] = p->next;
|
||||
if (p->next)
|
||||
p->next->prev = p->prev;
|
||||
else
|
||||
smbus_last[base + c] = p->prev;
|
||||
free(p);
|
||||
p = NULL;
|
||||
break;
|
||||
}
|
||||
p = q;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
smbus_handler(int set, uint8_t base, int size,
|
||||
uint8_t (*read_byte)(uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint16_t (*read_word_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint8_t (*read_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void (*write_byte)(uint8_t addr, uint8_t val, void *priv),
|
||||
void (*write_byte_cmd)(uint8_t addr, uint8_t cmd, uint8_t val, void *priv),
|
||||
void (*write_word_cmd)(uint8_t addr, uint8_t cmd, uint16_t val, void *priv),
|
||||
void (*write_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void *priv)
|
||||
{
|
||||
if (set)
|
||||
smbus_sethandler(base, size, read_byte, read_byte_cmd, read_word_cmd, read_block_cmd, write_byte, write_byte_cmd, write_word_cmd, write_block_cmd, priv);
|
||||
else
|
||||
smbus_removehandler(base, size, read_byte, read_byte_cmd, read_word_cmd, read_block_cmd, write_byte, write_byte_cmd, write_word_cmd, write_block_cmd, priv);
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
smbus_has_device(uint8_t addr)
|
||||
{
|
||||
return(!!smbus[addr]);
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
smbus_read_byte(uint8_t addr)
|
||||
{
|
||||
uint8_t ret = 0xff;
|
||||
smbus_t *p;
|
||||
int found = 0;
|
||||
|
||||
p = smbus[addr];
|
||||
if (p) {
|
||||
while(p) {
|
||||
if (p->read_byte) {
|
||||
ret &= p->read_byte(addr, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
smbus_log("SMBus: read_byte(%02X) = %02X\n", addr, ret);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
uint8_t
|
||||
smbus_read_byte_cmd(uint8_t addr, uint8_t cmd)
|
||||
{
|
||||
uint8_t ret = 0xff;
|
||||
smbus_t *p;
|
||||
int found = 0;
|
||||
|
||||
p = smbus[addr];
|
||||
if (p) {
|
||||
while(p) {
|
||||
if (p->read_byte_cmd) {
|
||||
ret &= p->read_byte_cmd(addr, cmd, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
smbus_log("SMBus: read_byte_cmd(%02X, %02X) = %02X\n", addr, cmd, ret);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
uint16_t
|
||||
smbus_read_word_cmd(uint8_t addr, uint8_t cmd)
|
||||
{
|
||||
uint16_t ret = 0xffff;
|
||||
smbus_t *p;
|
||||
int found = 0;
|
||||
|
||||
p = smbus[addr];
|
||||
if (p) {
|
||||
while(p) {
|
||||
if (p->read_word_cmd) {
|
||||
ret &= p->read_word_cmd(addr, cmd, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
smbus_log("SMBus: read_word_cmd(%02X, %02X) = %04X\n", addr, cmd, ret);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
uint8_t
|
||||
smbus_read_block_cmd(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len)
|
||||
{
|
||||
uint8_t ret = 0;
|
||||
smbus_t *p;
|
||||
int found = 0;
|
||||
|
||||
p = smbus[addr];
|
||||
if (p) {
|
||||
while(p) {
|
||||
if (p->read_block_cmd) {
|
||||
ret = MAX(ret, p->read_block_cmd(addr, cmd, data, len, p->priv));
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
smbus_log("SMBus: read_block_cmd(%02X, %02X) = %02X\n", addr, cmd, len);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
smbus_write_byte(uint8_t addr, uint8_t val)
|
||||
{
|
||||
smbus_t *p;
|
||||
int found = 0;
|
||||
|
||||
if (smbus[addr]) {
|
||||
p = smbus[addr];
|
||||
while(p) {
|
||||
if (p->write_byte) {
|
||||
p->write_byte(addr, val, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
smbus_log("SMBus: write_byte(%02X, %02X)\n", addr, val);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val)
|
||||
{
|
||||
smbus_t *p;
|
||||
int found = 0;
|
||||
|
||||
if (smbus[addr]) {
|
||||
p = smbus[addr];
|
||||
while(p) {
|
||||
if (p->write_byte_cmd) {
|
||||
p->write_byte_cmd(addr, cmd, val, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
smbus_log("SMBus: write_byte_cmd(%02X, %02X, %02X)\n", addr, cmd, val);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val)
|
||||
{
|
||||
smbus_t *p;
|
||||
int found = 0;
|
||||
|
||||
if (smbus[addr]) {
|
||||
p = smbus[addr];
|
||||
while(p) {
|
||||
if (p->write_word_cmd) {
|
||||
p->write_word_cmd(addr, cmd, val, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
smbus_log("SMBus: write_word_cmd(%02X, %02X, %04X)\n", addr, cmd, val);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
smbus_write_block_cmd(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len)
|
||||
{
|
||||
smbus_t *p;
|
||||
int found = 0;
|
||||
|
||||
p = smbus[addr];
|
||||
if (p) {
|
||||
while(p) {
|
||||
if (p->write_block_cmd) {
|
||||
p->write_block_cmd(addr, cmd, data, len, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
smbus_log("SMBus: write_block_cmd(%02X, %02X, %02X)\n", addr, cmd, len);
|
||||
|
||||
return;
|
||||
}
|
@@ -25,7 +25,7 @@
|
||||
#include <86box/io.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/timer.h>
|
||||
#include <86box/smbus.h>
|
||||
#include <86box/i2c.h>
|
||||
#include <86box/smbus_piix4.h>
|
||||
|
||||
|
||||
@@ -59,22 +59,28 @@ smbus_piix4_read(uint16_t addr, void *priv)
|
||||
case 0x00:
|
||||
ret = dev->stat;
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
dev->index = 0; /* reading from this resets the block data index */
|
||||
ret = dev->ctl;
|
||||
break;
|
||||
|
||||
case 0x03:
|
||||
ret = dev->cmd;
|
||||
break;
|
||||
|
||||
case 0x04:
|
||||
ret = dev->addr;
|
||||
break;
|
||||
|
||||
case 0x05:
|
||||
ret = dev->data0;
|
||||
break;
|
||||
|
||||
case 0x06:
|
||||
ret = dev->data1;
|
||||
break;
|
||||
|
||||
case 0x07:
|
||||
ret = dev->data[dev->index++];
|
||||
if (dev->index >= SMBUS_PIIX4_BLOCK_DATA_SIZE)
|
||||
@@ -82,7 +88,7 @@ smbus_piix4_read(uint16_t addr, void *priv)
|
||||
break;
|
||||
}
|
||||
|
||||
smbus_piix4_log("SMBus PIIX4: read(%02x) = %02x\n", addr, ret);
|
||||
smbus_piix4_log("SMBus PIIX4: read(%02X) = %02x\n", addr, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -95,7 +101,7 @@ smbus_piix4_write(uint16_t addr, uint8_t val, void *priv)
|
||||
uint8_t smbus_addr, smbus_read, prev_stat;
|
||||
uint16_t temp;
|
||||
|
||||
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;
|
||||
@@ -107,6 +113,7 @@ smbus_piix4_write(uint16_t addr, uint8_t val, void *priv)
|
||||
dev->stat &= ~smbus_addr;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
dev->ctl = val & ~(0x40); /* START always reads 0 */
|
||||
if (val & 0x02) { /* cancel an in-progress command if KILL is set */
|
||||
@@ -118,49 +125,53 @@ smbus_piix4_write(uint16_t addr, uint8_t val, void *priv)
|
||||
}
|
||||
if (val & 0x40) { /* dispatch command if START is set */
|
||||
smbus_addr = (dev->addr >> 1);
|
||||
if (!smbus_has_device(smbus_addr)) {
|
||||
if (!i2c_has_device(i2c_smbus, smbus_addr)) {
|
||||
/* raise DEV_ERR if no device is at this address */
|
||||
dev->next_stat = 0x4;
|
||||
break;
|
||||
}
|
||||
smbus_read = (dev->addr & 0x01);
|
||||
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) {
|
||||
case 0x0: /* quick R/W */
|
||||
if (smbus_read)
|
||||
i2c_read_quick(i2c_smbus, smbus_addr);
|
||||
else
|
||||
i2c_write_quick(i2c_smbus, smbus_addr);
|
||||
break;
|
||||
|
||||
case 0x1: /* byte R/W */
|
||||
if (smbus_read)
|
||||
dev->data0 = smbus_read_byte(smbus_addr);
|
||||
dev->data0 = i2c_read_byte(i2c_smbus, smbus_addr);
|
||||
else
|
||||
smbus_write_byte(smbus_addr, dev->data0);
|
||||
i2c_write_byte(i2c_smbus, smbus_addr, dev->data0);
|
||||
break;
|
||||
|
||||
case 0x2: /* byte data R/W */
|
||||
if (smbus_read)
|
||||
dev->data0 = smbus_read_byte_cmd(smbus_addr, dev->cmd);
|
||||
dev->data0 = i2c_read_byte_cmd(i2c_smbus, smbus_addr, dev->cmd);
|
||||
else
|
||||
smbus_write_byte_cmd(smbus_addr, dev->cmd, dev->data0);
|
||||
i2c_write_byte_cmd(i2c_smbus, smbus_addr, dev->cmd, dev->data0);
|
||||
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);
|
||||
temp = i2c_read_word_cmd(i2c_smbus, smbus_addr, dev->cmd);
|
||||
dev->data0 = temp;
|
||||
dev->data1 = temp >> 8;
|
||||
} else {
|
||||
temp = ((dev->data1 << 8) | dev->data0);
|
||||
smbus_write_word_cmd(smbus_addr, dev->cmd, temp);
|
||||
temp = (dev->data1 << 8) | dev->data0;
|
||||
i2c_write_word_cmd(i2c_smbus, smbus_addr, dev->cmd, temp);
|
||||
}
|
||||
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);
|
||||
dev->data0 = i2c_read_block_cmd(i2c_smbus, smbus_addr, dev->cmd, dev->data, SMBUS_PIIX4_BLOCK_DATA_SIZE);
|
||||
else
|
||||
smbus_write_block_cmd(smbus_addr, dev->cmd, dev->data, dev->data0);
|
||||
i2c_write_block_cmd(i2c_smbus, smbus_addr, dev->cmd, dev->data, dev->data0);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -170,18 +181,23 @@ smbus_piix4_write(uint16_t addr, uint8_t val, void *priv)
|
||||
}
|
||||
}
|
||||
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,7 +205,7 @@ 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 a status register update was given, dispatch it after 10us to ensure nothing breaks */
|
||||
if (dev->next_stat) {
|
||||
dev->stat = 0x1; /* raise HOST_BUSY while waiting */
|
||||
timer_disable(&dev->response_timer);
|
||||
@@ -228,7 +244,8 @@ 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();
|
||||
i2c_smbus = i2c_addbus("smbus_piix4");
|
||||
|
||||
timer_add(&dev->response_timer, smbus_piix4_response, dev, 0);
|
||||
|
||||
return dev;
|
||||
@@ -240,6 +257,9 @@ smbus_piix4_close(void *priv)
|
||||
{
|
||||
smbus_piix4_t *dev = (smbus_piix4_t *) priv;
|
||||
|
||||
i2c_removebus(i2c_smbus);
|
||||
i2c_smbus = NULL;
|
||||
|
||||
free(dev);
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
uint8_t as99127f_smbus_addr;
|
||||
uint8_t as99127f_i2c_addr;
|
||||
} lm75_t;
|
||||
|
||||
|
||||
|
89
src/include/86box/i2c.h
Normal file
89
src/include/86box/i2c.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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 void i2c_sethandler(void *bus_handle, uint8_t base, int size,
|
||||
void (*read_quick)(void *bus, uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte)(void *bus, uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte_cmd)(void *bus, uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint16_t (*read_word_cmd)(void *bus, uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint8_t (*read_block_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void (*write_quick)(void *bus, uint8_t addr, void *priv),
|
||||
void (*write_byte)(void *bus, uint8_t addr, uint8_t val, void *priv),
|
||||
void (*write_byte_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint8_t val, void *priv),
|
||||
void (*write_word_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint16_t val, void *priv),
|
||||
void (*write_block_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void *priv);
|
||||
|
||||
extern void i2c_removehandler(void *bus_handle, uint8_t base, int size,
|
||||
void (*read_quick)(void *bus, uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte)(void *bus, uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte_cmd)(void *bus, uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint16_t (*read_word_cmd)(void *bus, uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint8_t (*read_block_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void (*write_quick)(void *bus, uint8_t addr, void *priv),
|
||||
void (*write_byte)(void *bus, uint8_t addr, uint8_t val, void *priv),
|
||||
void (*write_byte_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint8_t val, void *priv),
|
||||
void (*write_word_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint16_t val, void *priv),
|
||||
void (*write_block_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void *priv);
|
||||
|
||||
extern void i2c_handler(int set, void *bus_handle, uint8_t base, int size,
|
||||
void (*read_quick)(void *bus, uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte)(void *bus, uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte_cmd)(void *bus, uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint16_t (*read_word_cmd)(void *bus, uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint8_t (*read_block_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void (*write_quick)(void *bus, uint8_t addr, void *priv),
|
||||
void (*write_byte)(void *bus, uint8_t addr, uint8_t val, void *priv),
|
||||
void (*write_byte_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint8_t val, void *priv),
|
||||
void (*write_word_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint16_t val, void *priv),
|
||||
void (*write_block_cmd)(void *bus, uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void *priv);
|
||||
|
||||
extern uint8_t i2c_has_device(void *bus_handle, uint8_t addr);
|
||||
extern void i2c_read_quick(void *bus_handle, uint8_t addr);
|
||||
extern uint8_t i2c_read_byte(void *bus_handle, uint8_t addr);
|
||||
extern uint8_t i2c_read_byte_cmd(void *bus_handle, uint8_t addr, uint8_t cmd);
|
||||
extern uint16_t i2c_read_word_cmd(void *bus_handle, uint8_t addr, uint8_t cmd);
|
||||
extern uint8_t i2c_read_block_cmd(void *bus_handle, uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len);
|
||||
extern void i2c_write_quick(void *bus_handle, uint8_t addr);
|
||||
extern void i2c_write_byte(void *bus_handle, uint8_t addr, uint8_t val);
|
||||
extern void i2c_write_byte_cmd(void *bus_handle, uint8_t addr, uint8_t cmd, uint8_t val);
|
||||
extern void i2c_write_word_cmd(void *bus_handle, uint8_t addr, uint8_t cmd, uint16_t val);
|
||||
extern void i2c_write_block_cmd(void *bus_handle, uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len);
|
||||
|
||||
/* i2c_gpio.c */
|
||||
extern void *i2c_gpio_init(char *bus_name);
|
||||
extern void i2c_gpio_close(void *dev_handle);
|
||||
extern void i2c_gpio_set(void *dev_handle, uint8_t scl, uint8_t sda);
|
||||
extern uint8_t i2c_gpio_get_scl(void *dev_handle);
|
||||
extern uint8_t i2c_gpio_get_sda(void *dev_handle);
|
||||
extern void *i2c_gpio_get_bus();
|
||||
|
||||
|
||||
#endif /*EMU_I2C_H*/
|
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
||||
* running old operating systems and software designed for IBM
|
||||
* PC systems and compatibles from 1981 through fairly recent
|
||||
* system designs based on the PCI bus.
|
||||
*
|
||||
* This file is part of the 86Box distribution.
|
||||
*
|
||||
* Definitions for the SMBus handler.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: RichardG, <richardg867@gmail.com>
|
||||
*
|
||||
* Copyright 2020 RichardG.
|
||||
*/
|
||||
#ifndef EMU_SMBUS_H
|
||||
# define EMU_SMBUS_H
|
||||
|
||||
|
||||
extern void smbus_init(void);
|
||||
|
||||
extern void smbus_sethandler(uint8_t base, int size,
|
||||
uint8_t (*read_byte)(uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint16_t (*read_word_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint8_t (*read_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void (*write_byte)(uint8_t addr, uint8_t val, void *priv),
|
||||
void (*write_byte_cmd)(uint8_t addr, uint8_t cmd, uint8_t val, void *priv),
|
||||
void (*write_word_cmd)(uint8_t addr, uint8_t cmd, uint16_t val, void *priv),
|
||||
void (*write_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void *priv);
|
||||
|
||||
extern void smbus_removehandler(uint8_t base, int size,
|
||||
uint8_t (*read_byte)(uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint16_t (*read_word_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint8_t (*read_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void (*write_byte)(uint8_t addr, uint8_t val, void *priv),
|
||||
void (*write_byte_cmd)(uint8_t addr, uint8_t cmd, uint8_t val, void *priv),
|
||||
void (*write_word_cmd)(uint8_t addr, uint8_t cmd, uint16_t val, void *priv),
|
||||
void (*write_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void *priv);
|
||||
|
||||
extern void smbus_handler(int set, uint8_t base, int size,
|
||||
uint8_t (*read_byte)(uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint16_t (*read_word_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint8_t (*read_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void (*write_byte)(uint8_t addr, uint8_t val, void *priv),
|
||||
void (*write_byte_cmd)(uint8_t addr, uint8_t cmd, uint8_t val, void *priv),
|
||||
void (*write_word_cmd)(uint8_t addr, uint8_t cmd, uint16_t val, void *priv),
|
||||
void (*write_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void *priv);
|
||||
|
||||
extern uint8_t smbus_has_device(uint8_t addr);
|
||||
extern uint8_t smbus_read_byte(uint8_t addr);
|
||||
extern uint8_t smbus_read_byte_cmd(uint8_t addr, uint8_t cmd);
|
||||
extern uint16_t smbus_read_word_cmd(uint8_t addr, uint8_t cmd);
|
||||
extern uint8_t smbus_read_block_cmd(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len);
|
||||
extern void smbus_write_byte(uint8_t addr, uint8_t val);
|
||||
extern void smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val);
|
||||
extern void smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val);
|
||||
extern void smbus_write_block_cmd(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len);
|
||||
|
||||
|
||||
#endif /*EMU_SMBUS_H*/
|
@@ -1,4 +1,24 @@
|
||||
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);
|
||||
|
||||
#endif /*EMU_VID_DDC_H*/
|
||||
|
472
src/mem/spd.c
472
src/mem/spd.c
@@ -23,7 +23,7 @@
|
||||
#define HAVE_STDARG_H
|
||||
#include <86box/86box.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/smbus.h>
|
||||
#include <86box/i2c.h>
|
||||
#include <86box/spd.h>
|
||||
#include <86box/version.h>
|
||||
#include <86box/machine.h>
|
||||
@@ -37,11 +37,6 @@ spd_t *spd_devices[SPD_MAX_SLOTS];
|
||||
uint8_t spd_data[SPD_MAX_SLOTS][SPD_DATA_SIZE];
|
||||
|
||||
|
||||
static uint8_t spd_read_byte(uint8_t addr, void *priv);
|
||||
static uint8_t spd_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv);
|
||||
static void spd_write_byte(uint8_t addr, uint8_t val, void *priv);
|
||||
|
||||
|
||||
#ifdef ENABLE_SPD_LOG
|
||||
int spd_do_log = ENABLE_SPD_LOG;
|
||||
|
||||
@@ -52,9 +47,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
|
||||
@@ -63,15 +58,7 @@ spd_log(const char *fmt, ...)
|
||||
|
||||
|
||||
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_read_byte_cmd(void *bus, uint8_t addr, uint8_t cmd, void *priv)
|
||||
{
|
||||
spd_t *dev = (spd_t *) priv;
|
||||
uint8_t ret = *(spd_data[dev->slot] + cmd);
|
||||
@@ -80,26 +67,33 @@ spd_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv)
|
||||
}
|
||||
|
||||
|
||||
uint16_t
|
||||
spd_read_word_cmd(uint8_t addr, uint8_t cmd, void *priv)
|
||||
uint8_t
|
||||
spd_read_byte(void *bus, uint8_t addr, void *priv)
|
||||
{
|
||||
return (spd_read_byte_cmd(addr, cmd + 1, priv) << 8) | spd_read_byte_cmd(addr, cmd, priv);
|
||||
spd_t *dev = (spd_t *) priv;
|
||||
return spd_read_byte_cmd(bus, addr, dev->addr_register, priv);
|
||||
}
|
||||
|
||||
|
||||
uint16_t
|
||||
spd_read_word_cmd(void *bus, uint8_t addr, uint8_t cmd, void *priv)
|
||||
{
|
||||
return (spd_read_byte_cmd(bus, addr, cmd + 1, priv) << 8) | spd_read_byte_cmd(bus, addr, cmd, priv);
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
spd_read_block_cmd(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv)
|
||||
spd_read_block_cmd(void *bus, 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);
|
||||
}
|
||||
for (uint8_t i = cmd; (i < len) && (i < SPD_DATA_SIZE); i++)
|
||||
data[read++] = spd_read_byte_cmd(bus, addr, i, priv);
|
||||
return read;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
spd_write_byte(uint8_t addr, uint8_t val, void *priv)
|
||||
spd_write_byte(void *bus, uint8_t addr, uint8_t val, void *priv)
|
||||
{
|
||||
spd_t *dev = (spd_t *) priv;
|
||||
dev->addr_register = val;
|
||||
@@ -111,12 +105,12 @@ spd_close(void *priv)
|
||||
{
|
||||
spd_t *dev = (spd_t *) priv;
|
||||
|
||||
spd_log("SPD: closing slot %d (SMBus %02Xh)\n", dev->slot, SPD_BASE_ADDR + dev->slot);
|
||||
spd_log("SPD: closing slot %d (SMBus %02X)\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);
|
||||
i2c_removehandler(i2c_smbus, SPD_BASE_ADDR + dev->slot, 1,
|
||||
NULL, spd_read_byte, spd_read_byte_cmd, spd_read_word_cmd, spd_read_block_cmd,
|
||||
NULL, spd_write_byte, NULL, NULL, NULL,
|
||||
dev);
|
||||
|
||||
spd_present = 0;
|
||||
|
||||
@@ -129,12 +123,12 @@ spd_init(const device_t *info)
|
||||
{
|
||||
spd_t *dev = spd_devices[info->local];
|
||||
|
||||
spd_log("SPD: initializing slot %d (SMBus %02Xh)\n", dev->slot, SPD_BASE_ADDR + dev->slot);
|
||||
spd_log("SPD: initializing slot %d (SMBus %02X)\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);
|
||||
i2c_sethandler(i2c_smbus, SPD_BASE_ADDR + dev->slot, 1,
|
||||
NULL, spd_read_byte, spd_read_byte_cmd, spd_read_word_cmd, spd_read_block_cmd,
|
||||
NULL, spd_write_byte, NULL, NULL, NULL,
|
||||
dev);
|
||||
|
||||
spd_present = 1;
|
||||
|
||||
@@ -147,7 +141,7 @@ log2_ui16(uint16_t i)
|
||||
{
|
||||
uint8_t ret = 0;
|
||||
while ((i >>= 1))
|
||||
ret++;
|
||||
ret++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -170,63 +164,63 @@ spd_populate(uint16_t *vslots, uint8_t slot_count, uint16_t total_size, uint16_t
|
||||
/* 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 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;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
vslot = slot_count - 1;
|
||||
do {
|
||||
asym = (1 << log2_ui16(MIN(total_size, vslots[vslot])));
|
||||
if (vslots[vslot] + asym <= max_module_size) {
|
||||
vslots[vslot] += asym;
|
||||
total_size -= asym;
|
||||
}
|
||||
} while ((vslot-- > 0) && total_size);
|
||||
}
|
||||
|
||||
if (total_size) /* still not enough */
|
||||
spd_log("SPD: not enough RAM slots (%d) to cover memory (%d MB short)\n", slot_count, total_size);
|
||||
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 */
|
||||
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 (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 */
|
||||
|
||||
/* 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 vslot */
|
||||
next_empty_vslot = 0;
|
||||
for (i = vslot + 1; i < slot_count && !next_empty_vslot; i++) {
|
||||
if (!vslots[i])
|
||||
next_empty_vslot = i;
|
||||
}
|
||||
if (!next_empty_vslot)
|
||||
break; /* no empty vslots left */
|
||||
|
||||
/* split the module into its own vslot and the next empty vslot */
|
||||
spd_log("SPD: splitting vslot %d (%d MB) into %d and %d (%d MB each)\n", vslot, vslots[vslot], vslot, next_empty_vslot, (vslots[vslot] >> 1));
|
||||
vslots[vslot] = vslots[next_empty_vslot] = (vslots[vslot] >> 1);
|
||||
split = 1;
|
||||
break;
|
||||
}
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* sort vslots by descending capacity if any were split */
|
||||
if (split)
|
||||
qsort(vslots, slot_count, sizeof(uint16_t), comp_ui16_rev);
|
||||
/* sort vslots by descending capacity if any were split */
|
||||
if (split)
|
||||
qsort(vslots, slot_count, sizeof(uint16_t), comp_ui16_rev);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,27 +236,27 @@ spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size)
|
||||
|
||||
/* 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 */
|
||||
slot_count = 0;
|
||||
for (slot = 0; slot < SPD_MAX_SLOTS; slot++) {
|
||||
vslots[slot] = 0;
|
||||
if (slot_mask & (1 << slot)) {
|
||||
slot_count++;
|
||||
}
|
||||
vslots[slot] = 0;
|
||||
if (slot_mask & (1 << slot)) {
|
||||
slot_count++;
|
||||
}
|
||||
}
|
||||
|
||||
/* populate vslots */
|
||||
@@ -271,134 +265,134 @@ spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size)
|
||||
/* 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 */
|
||||
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;
|
||||
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_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];
|
||||
spd_devices[slot] = (spd_t *) malloc(sizeof(spd_t));
|
||||
memset(spd_devices[slot], 0, sizeof(spd_t));
|
||||
spd_devices[slot]->info = info;
|
||||
spd_devices[slot]->slot = slot;
|
||||
spd_devices[slot]->size = vslots[vslot];
|
||||
|
||||
/* determine the second row size, from which the first row size can be obtained */
|
||||
asym = (vslots[vslot] - (1 << log2_ui16(vslots[vslot]))); /* separate the powers of 2 */
|
||||
if (!asym) /* is the module asymmetric? */
|
||||
asym = (vslots[vslot] >> 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_devices[slot]->row1 = (vslots[vslot] - asym);
|
||||
spd_devices[slot]->row2 = asym;
|
||||
spd_devices[slot]->row1 = vslots[vslot] - asym;
|
||||
spd_devices[slot]->row2 = asym;
|
||||
|
||||
spd_log("SPD: registering slot %d = vslot %d = %d MB (%d/%d)\n", slot, vslot, vslots[vslot], spd_devices[slot]->row1, spd_devices[slot]->row2);
|
||||
spd_log("SPD: registering slot %d = 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_data[slot];
|
||||
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_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 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", 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;
|
||||
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_data[slot][i];
|
||||
for (i = 0; i < 129; i++)
|
||||
edo_data->checksum2 += spd_data[slot][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_data[slot];
|
||||
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_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->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", 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->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_data[slot][i];
|
||||
for (i = 0; i < 129; i++)
|
||||
sdram_data->checksum2 += spd_data[slot][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++;
|
||||
device_add(info);
|
||||
vslot++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -411,49 +405,49 @@ spd_write_drbs(uint8_t *regs, uint8_t reg_min, uint8_t reg_max, uint8_t drb_unit
|
||||
|
||||
/* 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(vslots, 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);
|
||||
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_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);
|
||||
}
|
||||
|
||||
/* 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("DRB[%d] = %d MB (%02Xh raw)\n", row, size, regs[drb]);
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
} mach64_t;
|
||||
|
||||
static video_timings_t timing_mach64_isa = {VIDEO_ISA, 3, 3, 6, 5, 5, 10};
|
||||
@@ -1740,7 +1743,7 @@ uint8_t mach64_ext_readb(uint32_t addr, void *p)
|
||||
mach64_t *mach64 = (mach64_t *)p;
|
||||
uint8_t gpio_state;
|
||||
|
||||
uint8_t ret;
|
||||
uint8_t ret = 0xff;
|
||||
if (!(addr & 0x400))
|
||||
{
|
||||
mach64_log("nmach64_ext_readb: addr=%04x\n", addr);
|
||||
@@ -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;
|
||||
@@ -2381,7 +2384,7 @@ void mach64_ext_writeb(uint32_t addr, uint8_t val, void *p)
|
||||
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);
|
||||
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");
|
||||
ddc_init(i2c_gpio_get_bus(mach64->i2c));
|
||||
|
||||
return mach64;
|
||||
}
|
||||
|
@@ -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
|
||||
@@ -14,321 +11,122 @@
|
||||
*
|
||||
*
|
||||
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
|
||||
* 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)*/
|
||||
typedef struct {
|
||||
uint8_t addr_register;
|
||||
} ddc_t;
|
||||
|
||||
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,
|
||||
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 */
|
||||
47, 30, /* Manufacturer week and year */
|
||||
0x01, 0x03, /* EDID version (1.3) */
|
||||
|
||||
/*Detailed mode descriptions*/
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x08, /* Analog input, separate sync */
|
||||
34, 0, /* Landscape, 4:3 */
|
||||
0, /* Gamma */
|
||||
0x08, /* RGB colour */
|
||||
0x81, 0xf1, 0xa3, 0x57, 0x53, 0x9f, 0x27, 0x0a, 0x50, /* Chromaticity */
|
||||
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
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,
|
||||
|
||||
0x00, 0x00, 0x00, 0x00, 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, /*No extensions*/
|
||||
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
|
||||
};
|
||||
|
||||
/*This should probably be split off into a separate I2C module*/
|
||||
enum
|
||||
{
|
||||
TRANSMITTER_MONITOR = 1,
|
||||
TRANSMITTER_HOST = -1
|
||||
};
|
||||
|
||||
enum
|
||||
uint8_t
|
||||
ddc_read_byte_cmd(void *bus, uint8_t addr, uint8_t cmd, void *priv)
|
||||
{
|
||||
I2C_IDLE = 0,
|
||||
I2C_RECEIVE,
|
||||
I2C_RECEIVE_WAIT,
|
||||
I2C_TRANSMIT_START,
|
||||
I2C_TRANSMIT,
|
||||
I2C_ACKNOWLEDGE,
|
||||
I2C_TRANSACKNOWLEDGE,
|
||||
I2C_TRANSMIT_WAIT
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROM_IDLE = 0,
|
||||
PROM_RECEIVEADDR,
|
||||
PROM_RECEIVEDATA,
|
||||
PROM_SENDDATA,
|
||||
PROM_INVALID
|
||||
};
|
||||
|
||||
static struct
|
||||
{
|
||||
int clock, data;
|
||||
int state;
|
||||
int last_data;
|
||||
int pos;
|
||||
int transmit;
|
||||
uint8_t byte;
|
||||
} i2c;
|
||||
|
||||
static struct
|
||||
{
|
||||
int state;
|
||||
int addr;
|
||||
int rw;
|
||||
} prom;
|
||||
|
||||
static void prom_stop(void)
|
||||
{
|
||||
// pclog("prom_stop()\n");
|
||||
prom.state = PROM_IDLE;
|
||||
i2c.transmit = TRANSMITTER_HOST;
|
||||
return edid_data[cmd & 0x7f];
|
||||
}
|
||||
|
||||
static void prom_next_byte(void)
|
||||
|
||||
uint8_t
|
||||
ddc_read_byte(void *bus, uint8_t addr, void *priv)
|
||||
{
|
||||
// pclog("prom_next_byte(%d)\n", prom.addr);
|
||||
i2c.byte = edid_data[(prom.addr++) & 0x7F];
|
||||
ddc_t *dev = (ddc_t *) priv;
|
||||
return ddc_read_byte_cmd(bus, addr, dev->addr_register++, priv);
|
||||
}
|
||||
|
||||
static void prom_write(uint8_t byte)
|
||||
|
||||
uint16_t
|
||||
ddc_read_word_cmd(void *bus, uint8_t addr, uint8_t cmd, void *priv)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
return (ddc_read_byte_cmd(bus, addr, cmd + 1, priv) << 8) | ddc_read_byte_cmd(bus, addr, cmd, priv);
|
||||
}
|
||||
|
||||
void ddc_i2c_change(int new_clock, int new_data)
|
||||
|
||||
uint8_t
|
||||
ddc_read_block_cmd(void *bus, uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv)
|
||||
{
|
||||
// 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;
|
||||
uint8_t read = 0;
|
||||
for (uint8_t i = cmd; (i < len) && (i < 0x80); i++)
|
||||
data[read++] = ddc_read_byte_cmd(bus, addr, i, priv);
|
||||
return read;
|
||||
}
|
||||
|
||||
int ddc_read_clock(void)
|
||||
|
||||
void
|
||||
ddc_write_byte(void *bus, uint8_t addr, uint8_t val, void *priv)
|
||||
{
|
||||
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;
|
||||
ddc_t *dev = (ddc_t *) priv;
|
||||
dev->addr_register = val;
|
||||
}
|
||||
|
||||
void ddc_init(void)
|
||||
|
||||
void
|
||||
ddc_init(void *i2c)
|
||||
{
|
||||
int c;
|
||||
uint8_t checksum = 0;
|
||||
ddc_t *dev = (ddc_t *) malloc(sizeof(ddc_t));
|
||||
memset(dev, 0, sizeof(ddc_t));
|
||||
|
||||
for (c = 0; c < 127; c++)
|
||||
checksum += edid_data[c];
|
||||
edid_data[127] = 256 - checksum;
|
||||
uint8_t checksum = 0;
|
||||
for (int c = 0; c < 127; c++)
|
||||
checksum += edid_data[c];
|
||||
edid_data[127] = 256 - checksum;
|
||||
|
||||
i2c.clock = 1;
|
||||
i2c.data = 1;
|
||||
i2c_sethandler(i2c, 0x50, 1,
|
||||
NULL, ddc_read_byte, ddc_read_byte_cmd, ddc_read_word_cmd, ddc_read_block_cmd,
|
||||
NULL, ddc_write_byte, NULL, NULL, NULL,
|
||||
dev);
|
||||
}
|
||||
|
@@ -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>
|
||||
@@ -286,6 +287,8 @@ typedef struct virge_t
|
||||
uint8_t subsys_stat, subsys_cntl, advfunc_cntl;
|
||||
|
||||
uint8_t serialport;
|
||||
|
||||
void *i2c;
|
||||
} virge_t;
|
||||
|
||||
static video_timings_t timing_diamond_stealth3d_2000_vlb = {VIDEO_BUS, 2, 2, 3, 28, 28, 45};
|
||||
@@ -894,9 +897,9 @@ 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;
|
||||
}
|
||||
@@ -1180,7 +1183,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 +3848,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");
|
||||
ddc_init(i2c_gpio_get_bus(virge->i2c));
|
||||
|
||||
return virge;
|
||||
}
|
||||
|
@@ -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;
|
||||
} banshee_t;
|
||||
|
||||
enum
|
||||
@@ -682,7 +685,7 @@ 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, !!(val & VIDSERIAL_DDC_DCK_W), !!(val & VIDSERIAL_DDC_DDA_W));
|
||||
break;
|
||||
|
||||
case Video_vidScreenSize:
|
||||
@@ -915,9 +918,9 @@ 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))
|
||||
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))
|
||||
ret |= VIDSERIAL_DDC_DDA_R;
|
||||
ret = ret & ~(VIDSERIAL_I2C_SCK_R | VIDSERIAL_I2C_SDA_R);
|
||||
if (banshee->vidSerialParallelPort & VIDSERIAL_I2C_SCK_W)
|
||||
@@ -2625,7 +2628,8 @@ 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("ddc_voodoo_banshee");
|
||||
ddc_init(i2c_gpio_get_bus(banshee->i2c));
|
||||
|
||||
video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_banshee);
|
||||
|
||||
@@ -2672,6 +2676,7 @@ static void banshee_close(void *p)
|
||||
|
||||
voodoo_card_close(banshee->voodoo);
|
||||
svga_close(&banshee->svga);
|
||||
i2c_gpio_close(banshee->i2c);
|
||||
|
||||
free(banshee);
|
||||
}
|
||||
|
@@ -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_gpio.o smbus_piix4.o \
|
||||
keyboard.o \
|
||||
keyboard_xt.o keyboard_at.o \
|
||||
mouse.o \
|
||||
|
Reference in New Issue
Block a user