Files
86Box/src/device/hwm_lm75.c

259 lines
5.8 KiB
C
Raw Normal View History

2020-05-18 22:54:59 -03:00
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Emulation of the National Semiconductor LM75 temperature sensor chip.
*
*
*
* Author: RichardG, <richardg867@gmail.com>
*
* Copyright 2020 RichardG.
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define HAVE_STDARG_H
#include <wchar.h>
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/i2c.h>
2020-05-18 22:54:59 -03:00
#include <86box/hwm.h>
#define LM75_TEMP_TO_REG(t) ((t) << 8)
2020-11-20 19:23:14 -03:00
static void lm75_i2c_start(void *bus, uint8_t addr, void *priv);
static uint8_t lm75_i2c_read(void *bus, uint8_t addr, void *priv);
static uint8_t lm75_i2c_write(void *bus, uint8_t addr, uint8_t data, void *priv);
2020-05-18 22:54:59 -03:00
static void lm75_reset(lm75_t *dev);
#ifdef ENABLE_LM75_LOG
int lm75_do_log = ENABLE_LM75_LOG;
static void
lm75_log(const char *fmt, ...)
{
va_list ap;
if (lm75_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
#define lm75_log(fmt, ...)
#endif
void
2020-07-02 21:42:31 -03:00
lm75_remap(lm75_t *dev, uint8_t addr)
2020-05-18 22:54:59 -03:00
{
lm75_log("LM75: remapping to I2C %02Xh\n", addr);
2020-05-18 22:54:59 -03:00
2020-11-20 19:23:14 -03:00
if (dev->i2c_addr < 0x80)
i2c_removehandler(i2c_smbus, dev->i2c_addr, 1, lm75_i2c_start, lm75_i2c_read, lm75_i2c_write, NULL, dev);
2020-05-18 22:54:59 -03:00
2020-11-20 19:23:14 -03:00
if (addr < 0x80)
i2c_sethandler(i2c_smbus, addr, 1, lm75_i2c_start, lm75_i2c_read, lm75_i2c_write, NULL, dev);
2020-07-02 21:42:31 -03:00
dev->i2c_addr = addr;
2020-05-18 22:54:59 -03:00
}
2020-11-20 19:23:14 -03:00
static void
lm75_i2c_start(void *bus, uint8_t addr, void *priv)
2020-05-18 22:54:59 -03:00
{
lm75_t *dev = (lm75_t *) priv;
2020-11-20 19:23:14 -03:00
dev->i2c_state = 0;
2020-05-18 22:54:59 -03:00
}
static uint8_t
2020-11-20 19:23:14 -03:00
lm75_i2c_read(void *bus, uint8_t addr, void *priv)
2020-05-18 22:54:59 -03:00
{
lm75_t *dev = (lm75_t *) priv;
2020-11-20 19:23:14 -03:00
uint8_t ret = 0;
2020-05-18 22:54:59 -03:00
2020-11-20 19:23:14 -03:00
switch (dev->addr_register & 0x3) {
2020-10-30 20:41:15 -03:00
case 0x0: /* temperature */
2020-11-20 19:23:14 -03:00
ret = lm75_read(dev, (dev->i2c_state == 1) ? 0x1 : 0x0);
2020-10-30 20:41:15 -03:00
break;
2020-11-20 19:23:14 -03:00
2020-10-30 20:41:15 -03:00
case 0x1: /* configuration */
2020-11-20 19:23:14 -03:00
ret = lm75_read(dev, 0x2);
2020-10-30 20:41:15 -03:00
break;
2020-11-20 19:23:14 -03:00
2020-10-30 20:41:15 -03:00
case 0x2: /* Thyst */
2020-11-20 19:23:14 -03:00
ret = lm75_read(dev, (dev->i2c_state == 1) ? 0x4 : 0x3);
2020-10-30 20:41:15 -03:00
break;
case 0x3: /* Tos */
2020-11-20 19:23:14 -03:00
ret = lm75_read(dev, (dev->i2c_state == 1) ? 0x6 : 0x5);
2020-10-30 20:41:15 -03:00
break;
2020-05-18 22:54:59 -03:00
}
2020-11-20 19:23:14 -03:00
return ret;
2020-05-18 22:54:59 -03:00
}
uint8_t
lm75_read(lm75_t *dev, uint8_t reg)
{
uint8_t ret;
/* The AS99127F hardware monitor uses the addresses of its LM75 devices
to access some of its proprietary registers. Pass this operation on to
the main monitor address through an internal I2C call, if necessary. */
2020-11-20 19:23:14 -03:00
if ((reg > 0x7) && ((reg & 0xf8) != 0x50) && (dev->as99127f_i2c_addr < 0x80)) {
i2c_start(i2c_smbus, dev->as99127f_i2c_addr);
i2c_write(i2c_smbus, dev->as99127f_i2c_addr, reg);
ret = i2c_read(i2c_smbus, dev->as99127f_i2c_addr);
i2c_stop(i2c_smbus, dev->as99127f_i2c_addr);
} else if ((reg & 0x7) == 0x0) /* temperature high byte */
2020-10-30 20:41:15 -03:00
ret = LM75_TEMP_TO_REG(dev->values->temperatures[dev->local >> 8]) >> 8;
else if ((reg & 0x7) == 0x1) /* temperature low byte */
ret = LM75_TEMP_TO_REG(dev->values->temperatures[dev->local >> 8]);
2020-05-18 22:54:59 -03:00
else
2020-10-30 20:41:15 -03:00
ret = dev->regs[reg & 0x7];
2020-05-18 22:54:59 -03:00
lm75_log("LM75: read(%02X) = %02X\n", reg, ret);
2020-05-18 22:54:59 -03:00
return ret;
}
2020-11-20 19:23:14 -03:00
static uint8_t
lm75_i2c_write(void *bus, uint8_t addr, uint8_t data, void *priv)
2020-05-18 22:54:59 -03:00
{
lm75_t *dev = (lm75_t *) priv;
2020-11-20 19:23:14 -03:00
if ((dev->i2c_state > 2) || ((dev->i2c_state == 2) && ((dev->addr_register & 0x3) == 0x1))) {
return 0;
} else if (dev->i2c_state == 0) {
dev->addr_register = data;
return 1;
}
2020-05-18 22:54:59 -03:00
2020-11-20 19:23:14 -03:00
switch (dev->addr_register & 0x3) {
2020-10-30 20:41:15 -03:00
case 0x0: /* temperature */
2020-11-20 19:23:14 -03:00
lm75_write(dev, (dev->i2c_state == 1) ? 0x1 : 0x0, data);
2020-10-30 20:41:15 -03:00
break;
2020-11-20 19:23:14 -03:00
2020-10-30 20:41:15 -03:00
case 0x1: /* configuration */
2020-11-20 19:23:14 -03:00
lm75_write(dev, 0x2, data);
2020-10-30 20:41:15 -03:00
break;
2020-11-20 19:23:14 -03:00
2020-10-30 20:41:15 -03:00
case 0x2: /* Thyst */
2020-11-20 19:23:14 -03:00
lm75_write(dev, (dev->i2c_state == 1) ? 0x4 : 0x3, data);
2020-10-30 20:41:15 -03:00
break;
case 0x3: /* Tos */
2020-11-20 19:23:14 -03:00
lm75_write(dev, (dev->i2c_state == 1) ? 0x6 : 0x5, data);
2020-10-30 20:41:15 -03:00
break;
2020-05-18 22:54:59 -03:00
}
2020-11-20 19:23:14 -03:00
return 1;
2020-05-18 22:54:59 -03:00
}
uint8_t
lm75_write(lm75_t *dev, uint8_t reg, uint8_t val)
{
lm75_log("LM75: write(%02X, %02X)\n", reg, val);
2020-05-18 22:54:59 -03:00
/* The AS99127F hardware monitor uses the addresses of its LM75 devices
to access some of its proprietary registers. Pass this operation on to
the main monitor address through an internal I2C call, if necessary. */
if ((reg > 0x7) && ((reg & 0xf8) != 0x50) && (dev->as99127f_i2c_addr < 0x80)) {
2020-11-20 19:23:14 -03:00
i2c_start(i2c_smbus, dev->as99127f_i2c_addr);
i2c_write(i2c_smbus, dev->as99127f_i2c_addr, reg);
i2c_write(i2c_smbus, dev->as99127f_i2c_addr, val);
i2c_stop(i2c_smbus, dev->as99127f_i2c_addr);
2020-10-30 20:41:15 -03:00
return 1;
2020-05-18 22:54:59 -03:00
}
uint8_t reg_idx = (reg & 0x7);
if ((reg_idx <= 0x1) || (reg_idx == 0x7))
2020-10-30 20:41:15 -03:00
return 0; /* read-only registers */
2020-05-18 22:54:59 -03:00
dev->regs[reg_idx] = val;
return 1;
}
static void
lm75_reset(lm75_t *dev)
{
dev->regs[0x3] = 0x4b;
dev->regs[0x5] = 0x50;
2020-07-02 21:42:31 -03:00
lm75_remap(dev, dev->local & 0x7f);
2020-05-18 22:54:59 -03:00
}
static void
lm75_close(void *priv)
{
lm75_t *dev = (lm75_t *) priv;
2020-07-02 21:42:31 -03:00
lm75_remap(dev, 0);
2020-05-18 22:54:59 -03:00
free(dev);
}
static void *
lm75_init(const device_t *info)
{
lm75_t *dev = (lm75_t *) malloc(sizeof(lm75_t));
memset(dev, 0, sizeof(lm75_t));
dev->local = info->local;
2020-10-30 20:41:15 -03:00
/* Set default value. */
if (dev->local)
hwm_values.temperatures[dev->local >> 8] = 30;
dev->values = &hwm_values;
2020-05-18 22:54:59 -03:00
dev->as99127f_i2c_addr = 0x80;
2020-05-18 22:54:59 -03:00
lm75_reset(dev);
return dev;
}
/* LM75 on SMBus address 4Ah, reporting temperatures[1]. */
const device_t lm75_1_4a_device = {
"National Semiconductor LM75 Temperature Sensor",
2020-10-30 20:41:15 -03:00
DEVICE_ISA,
2020-05-18 22:54:59 -03:00
0x14a,
lm75_init, lm75_close, NULL,
WARNING: CONFIGS MIGHT PARTIALLY BREAK WHERE DEVICE NAMES HAVE CHANGED. Changes to device_t struct to accomodate the upcoming PCI IRQ arbitration rewrite; Added device.c/h API to obtain name from the device_t struct; Significant changes to win/win_settings.c to clean up the code a bit and fix bugs; Ported all the CPU and AudioPCI commits from PCem; Added an API call to allow ACPI soft power off to gracefully stop the emulator; Removed the Siemens PCD-2L from the Dev branch because it now works; Removed the Socket 5 HP Vectra from the Dev branch because it now works; Fixed the Compaq Presario and the Micronics Spitfire; Give the IBM PC330 its own list of 486 CPU so it can have DX2's with CPUID 0x470; SMM fixes; Rewrote the SYSENTER, SYSEXIT, SYSCALL, and SYSRET instructions; Changed IDE reset period to match the specification, fixes #929; The keyboard input and output ports are now forced in front of the queue when read, fixes a number of bugs, including the AMI Apollo hanging on soft reset; Added the Intel AN430TX but Dev branched because it does not work; The network code no longer drops packets if the emulated network card has failed to receive them (eg. when the buffer is full); Changes to PCI card adding and renamed some PCI slot types, also added proper AGP bridge slot types; USB UHCI emulation is no longer a stub (still doesn't fully work, but at least Windows XP chk with Debug no longer ASSERT's on it); Fixed NVR on the the SMC FDC37C932QF and APM variants; A number of fixes to Intel 4x0 chipsets, including fixing every register of the 440LX and 440EX; Some ACPI changes.
2020-11-16 00:01:21 +01:00
{ NULL }, NULL, NULL,
2020-05-18 22:54:59 -03:00
NULL
};
/* LM75 secondary/tertiary temperature sensors built into
the Winbond W83781D family. Not to be used stand-alone. */
const device_t lm75_w83781d_device = {
"Winbond W83781D Secondary Temperature Sensor",
2020-10-30 20:41:15 -03:00
DEVICE_ISA,
2020-05-18 22:54:59 -03:00
0,
lm75_init, lm75_close, NULL,
WARNING: CONFIGS MIGHT PARTIALLY BREAK WHERE DEVICE NAMES HAVE CHANGED. Changes to device_t struct to accomodate the upcoming PCI IRQ arbitration rewrite; Added device.c/h API to obtain name from the device_t struct; Significant changes to win/win_settings.c to clean up the code a bit and fix bugs; Ported all the CPU and AudioPCI commits from PCem; Added an API call to allow ACPI soft power off to gracefully stop the emulator; Removed the Siemens PCD-2L from the Dev branch because it now works; Removed the Socket 5 HP Vectra from the Dev branch because it now works; Fixed the Compaq Presario and the Micronics Spitfire; Give the IBM PC330 its own list of 486 CPU so it can have DX2's with CPUID 0x470; SMM fixes; Rewrote the SYSENTER, SYSEXIT, SYSCALL, and SYSRET instructions; Changed IDE reset period to match the specification, fixes #929; The keyboard input and output ports are now forced in front of the queue when read, fixes a number of bugs, including the AMI Apollo hanging on soft reset; Added the Intel AN430TX but Dev branched because it does not work; The network code no longer drops packets if the emulated network card has failed to receive them (eg. when the buffer is full); Changes to PCI card adding and renamed some PCI slot types, also added proper AGP bridge slot types; USB UHCI emulation is no longer a stub (still doesn't fully work, but at least Windows XP chk with Debug no longer ASSERT's on it); Fixed NVR on the the SMC FDC37C932QF and APM variants; A number of fixes to Intel 4x0 chipsets, including fixing every register of the 440LX and 440EX; Some ACPI changes.
2020-11-16 00:01:21 +01:00
{ NULL }, NULL, NULL,
2020-05-18 22:54:59 -03:00
NULL
};