Merge pull request #891 from richardg867/master

Genesys Logic GL518SM hardware monitor
This commit is contained in:
Miran Grča
2020-07-03 03:08:05 +02:00
committed by GitHub
7 changed files with 331 additions and 34 deletions

278
src/device/hwm_gl518sm.c Normal file
View File

@@ -0,0 +1,278 @@
/*
* 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 Genesys Logic GL518SM hardware monitoring 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/io.h>
#include "cpu.h"
#include <86box/smbus.h>
#include <86box/hwm.h>
#define CLAMP(a, min, max) (((a)< (min)) ? (min) : (((a) > (max)) ? (max) : (a)))
#define GL518SM_RPM_TO_REG(r, d) ((r) ? CLAMP(480000 / (r * d), 1, 255) : 0)
#define GL518SM_VOLTAGE_TO_REG(v) (((v) / 19) & 0xff)
#define GL518SM_VDD_TO_REG(v) ((((v) * 4) / 95) & 0xff)
typedef struct {
uint32_t local;
hwm_values_t *values;
uint16_t regs[32];
uint8_t addr_register;
uint8_t smbus_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 uint16_t gl518sm_read(gl518sm_t *dev, uint8_t reg);
static void gl518sm_smbus_write_byte(uint8_t addr, uint8_t val, void *priv);
static void gl518sm_smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val, void *priv);
static void gl518sm_smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val, void *priv);
static uint8_t gl518sm_write(gl518sm_t *dev, uint8_t reg, uint16_t val);
static void gl518sm_reset(gl518sm_t *dev);
#define ENABLE_GL518SM_LOG 1
#ifdef ENABLE_GL518SM_LOG
int gl518sm_do_log = ENABLE_GL518SM_LOG;
static void
gl518sm_log(const char *fmt, ...)
{
va_list ap;
if (gl518sm_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
#define gl518sm_log(fmt, ...)
#endif
static void
gl518sm_remap(gl518sm_t *dev, uint8_t addr)
{
gl518sm_log("GL518SM: remapping to SMBus %02Xh\n", addr);
smbus_removehandler(dev->smbus_addr, 1,
gl518sm_smbus_read_byte, gl518sm_smbus_read_byte_cmd, gl518sm_smbus_read_word_cmd, NULL,
gl518sm_smbus_write_byte, gl518sm_smbus_write_byte_cmd, gl518sm_smbus_write_word_cmd, NULL,
dev);
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);
dev->smbus_addr = addr;
}
static uint8_t
gl518sm_smbus_read_byte(uint8_t addr, void *priv)
{
gl518sm_t *dev = (gl518sm_t *) priv;
return gl518sm_read(dev, dev->addr_register);
}
static uint8_t
gl518sm_smbus_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv)
{
gl518sm_t *dev = (gl518sm_t *) priv;
return gl518sm_read(dev, cmd);
}
static uint16_t
gl518sm_smbus_read_word_cmd(uint8_t addr, uint8_t cmd, void *priv)
{
gl518sm_t *dev = (gl518sm_t *) priv;
return gl518sm_read(dev, cmd);
}
static uint16_t
gl518sm_read(gl518sm_t *dev, uint8_t reg)
{
uint16_t ret = dev->regs[reg & 0x1f];
switch (reg) {
case 0x07: case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c:
/* two-byte registers: leave as-is */
break;
default:
/* single-byte registers: duplicate low byte to high byte (real hardware behavior unknown) */
ret |= (ret << 8);
break;
}
gl518sm_log("GL518SM: read(%02X) = %04X\n", reg, ret);
return ret;
}
static void
gl518sm_smbus_write_byte(uint8_t addr, uint8_t val, void *priv)
{
gl518sm_t *dev = (gl518sm_t *) priv;
dev->addr_register = val;
}
static void
gl518sm_smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val, void *priv)
{
gl518sm_t *dev = (gl518sm_t *) priv;
gl518sm_write(dev, cmd, val);
}
static void
gl518sm_smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val, void *priv)
{
gl518sm_t *dev = (gl518sm_t *) priv;
gl518sm_write(dev, cmd, val);
}
static uint8_t
gl518sm_write(gl518sm_t *dev, uint8_t reg, uint16_t val)
{
gl518sm_log("GL518SM: write(%02X, %04X)\n", reg, val);
switch (reg) {
case 0x00: case 0x01: case 0x04: case 0x07: case 0x0d: case 0x12: case 0x13: case 0x14: case 0x15:
/* read-only registers */
return 0;
case 0x0a:
dev->regs[0x13] = (val & 0xff);
break;
case 0x03:
dev->regs[reg] = (val & 0xfc);
if (val & 0x80) /* Init */
gl518sm_reset(dev);
break;
case 0x0f:
dev->regs[reg] = (val & 0xf8);
/* update fan values to match the new divisor */
dev->regs[0x07] = (GL518SM_RPM_TO_REG(dev->values->fans[0], 1 << ((dev->regs[0x0f] >> 6) & 0x3)) << 8);
dev->regs[0x07] |= GL518SM_RPM_TO_REG(dev->values->fans[1], 1 << ((dev->regs[0x0f] >> 4) & 0x3));
break;
case 0x11:
dev->regs[reg] = (val & 0x7f);
break;
default:
dev->regs[reg] = val;
break;
}
return 1;
}
static void
gl518sm_reset(gl518sm_t *dev)
{
memset(dev->regs, 0, sizeof(dev->regs));
dev->regs[0x00] = 0x80;
dev->regs[0x01] = 0x80; /* revision 0x80 can read all voltages */
dev->regs[0x04] = ((dev->values->temperatures[0] + 119) & 0xff);
dev->regs[0x05] = 0xc7;
dev->regs[0x06] = 0xc2;
dev->regs[0x07] = ((GL518SM_RPM_TO_REG(dev->values->fans[0], 8) << 8) | GL518SM_RPM_TO_REG(dev->values->fans[1], 8));
dev->regs[0x08] = 0x6464;
dev->regs[0x09] = 0xdac5;
dev->regs[0x0a] = 0xdac5;
dev->regs[0x0b] = 0xdac5;
dev->regs[0x0c] = 0xdac5;
/* AOpen System Monitor requires an approximate voltage offset of 13 at least on 3.3V (voltages[2]) */
dev->regs[0x0d] = 13 + GL518SM_VOLTAGE_TO_REG(dev->values->voltages[2]);
dev->regs[0x0f] = 0xf8;
dev->regs[0x13] = 13 + GL518SM_VOLTAGE_TO_REG(dev->values->voltages[1]);
dev->regs[0x14] = 13 + GL518SM_VOLTAGE_TO_REG(dev->values->voltages[0]);
dev->regs[0x15] = 13 + GL518SM_VDD_TO_REG(5000);
}
static void
gl518sm_close(void *priv)
{
gl518sm_t *dev = (gl518sm_t *) priv;
gl518sm_remap(dev, 0);
free(dev);
}
static void *
gl518sm_init(const device_t *info)
{
gl518sm_t *dev = (gl518sm_t *) malloc(sizeof(gl518sm_t));
memset(dev, 0, sizeof(gl518sm_t));
dev->local = info->local;
dev->values = hwm_get_values();
gl518sm_reset(dev);
gl518sm_remap(dev, dev->local & 0x7f);
return dev;
}
const device_t gl518sm_2c_device = {
"Genesys Logic GL518SM Hardware Monitor",
DEVICE_ISA,
0x2c,
gl518sm_init, gl518sm_close, NULL,
NULL, NULL, NULL,
NULL
};
const device_t gl518sm_2d_device = {
"Genesys Logic GL518SM Hardware Monitor",
DEVICE_ISA,
0x2d,
gl518sm_init, gl518sm_close, NULL,
NULL, NULL, NULL,
NULL
};

View File

@@ -60,19 +60,21 @@ lm75_log(const char *fmt, ...)
void void
lm75_remap(lm75_t *dev) lm75_remap(lm75_t *dev, uint8_t addr)
{ {
lm75_log("LM75: remapping to SMBus %02Xh\n", dev->smbus_addr); lm75_log("LM75: remapping to SMBus %02Xh\n", addr);
smbus_removehandler(dev->smbus_addr, 1, smbus_removehandler(dev->smbus_addr, 1,
lm75_smbus_read_byte, lm75_smbus_read_byte_cmd, lm75_smbus_read_word_cmd, NULL, 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, lm75_smbus_write_byte, lm75_smbus_write_byte_cmd, lm75_smbus_write_word_cmd, NULL,
dev); dev);
if (dev->smbus_addr) smbus_sethandler(dev->smbus_addr, 1, if (addr < 0x80) smbus_sethandler(addr, 1,
lm75_smbus_read_byte, lm75_smbus_read_byte_cmd, lm75_smbus_read_word_cmd, NULL, 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, lm75_smbus_write_byte, lm75_smbus_write_byte_cmd, lm75_smbus_write_word_cmd, NULL,
dev); dev);
dev->smbus_addr = addr;
} }
@@ -128,7 +130,7 @@ lm75_read(lm75_t *dev, uint8_t reg)
/* The AS99127F hardware monitor uses the addresses of its LM75 devices /* The AS99127F hardware monitor uses the addresses of its LM75 devices
to access some of its proprietary registers. Pass this operation on to to access some of its proprietary registers. Pass this operation on to
the main monitor address through an internal SMBus call, if necessary. */ the main monitor address through an internal SMBus call, if necessary. */
if ((reg > 0x7) && ((reg & 0xf8) != 0x50) && (dev->as99127f_smbus_addr)) if ((reg > 0x7) && ((reg & 0xf8) != 0x50) && (dev->as99127f_smbus_addr < 0x80))
ret = smbus_read_byte_cmd(dev->as99127f_smbus_addr, reg); ret = smbus_read_byte_cmd(dev->as99127f_smbus_addr, reg);
else else
ret = dev->regs[reg & 0x7]; ret = dev->regs[reg & 0x7];
@@ -191,7 +193,7 @@ lm75_write(lm75_t *dev, uint8_t reg, uint8_t val)
/* The AS99127F hardware monitor uses the addresses of its LM75 devices /* The AS99127F hardware monitor uses the addresses of its LM75 devices
to access some of its proprietary registers. Pass this operation on to to access some of its proprietary registers. Pass this operation on to
the main monitor address through an internal SMBus call, if necessary. */ the main monitor address through an internal SMBus call, if necessary. */
if ((reg > 0x7) && ((reg & 0xf8) != 0x50) && (dev->as99127f_smbus_addr)) { if ((reg > 0x7) && ((reg & 0xf8) != 0x50) && (dev->as99127f_smbus_addr < 0x80)) {
smbus_write_byte_cmd(dev->as99127f_smbus_addr, reg, val); smbus_write_byte_cmd(dev->as99127f_smbus_addr, reg, val);
return 1; return 1;
} }
@@ -216,7 +218,7 @@ lm75_reset(lm75_t *dev)
dev->regs[0x3] = 0x4b; dev->regs[0x3] = 0x4b;
dev->regs[0x5] = 0x50; dev->regs[0x5] = 0x50;
lm75_remap(dev); lm75_remap(dev, dev->local & 0x7f);
} }
@@ -224,6 +226,9 @@ static void
lm75_close(void *priv) lm75_close(void *priv)
{ {
lm75_t *dev = (lm75_t *) priv; lm75_t *dev = (lm75_t *) priv;
lm75_remap(dev, 0);
free(dev); free(dev);
} }
@@ -237,8 +242,7 @@ lm75_init(const device_t *info)
dev->local = info->local; dev->local = info->local;
dev->values = hwm_get_values(); dev->values = hwm_get_values();
dev->smbus_addr = dev->local; dev->as99127f_smbus_addr = 0x80;
dev->as99127f_smbus_addr = 0x00;
lm75_reset(dev); lm75_reset(dev);

View File

@@ -91,24 +91,26 @@ lm78_log(const char *fmt, ...)
static void static void
lm78_remap(lm78_t *dev) lm78_remap(lm78_t *dev, uint8_t addr)
{ {
lm75_t *lm75; lm75_t *lm75;
if (!(dev->local & LM78_SMBUS)) return; if (!(dev->local & LM78_SMBUS)) return;
lm78_log("LM78: remapping to SMBus %02Xh\n", dev->smbus_addr); lm78_log("LM78: remapping to SMBus %02Xh\n", addr);
smbus_removehandler(dev->smbus_addr, 1, smbus_removehandler(dev->smbus_addr, 1,
lm78_smbus_read_byte, lm78_smbus_read_byte_cmd, lm78_smbus_read_word_cmd, NULL, 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, lm78_smbus_write_byte, lm78_smbus_write_byte_cmd, lm78_smbus_write_word_cmd, NULL,
dev); dev);
if (dev->smbus_addr) smbus_sethandler(dev->smbus_addr, 1, if (addr < 0x80) smbus_sethandler(addr, 1,
lm78_smbus_read_byte, lm78_smbus_read_byte_cmd, lm78_smbus_read_word_cmd, NULL, 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, lm78_smbus_write_byte, lm78_smbus_write_byte_cmd, lm78_smbus_write_word_cmd, NULL,
dev); dev);
dev->smbus_addr = addr;
if (dev->local & LM78_AS99127F) { if (dev->local & LM78_AS99127F) {
/* Store the main SMBus address on the LM75 devices to ensure reads/writes /* Store the main SMBus address on the LM75 devices to ensure reads/writes
to the AS99127F's proprietary registers are passed through to this side. */ to the AS99127F's proprietary registers are passed through to this side. */
@@ -291,10 +293,8 @@ lm78_write(lm78_t *dev, uint8_t reg, uint8_t val, uint8_t bank)
switch (reg) { switch (reg) {
case 0x40: case 0x40:
if (val & 0x80) { if (val & 0x80) /* INITIALIZATION bit resets all registers except main SMBus address */
/* INITIALIZATION bit resets all registers except main SMBus address */
lm78_reset(dev, 1); lm78_reset(dev, 1);
}
break; break;
case 0x47: case 0x47:
/* update FAN1/FAN2 values to match the new divisor */ /* update FAN1/FAN2 values to match the new divisor */
@@ -303,19 +303,15 @@ lm78_write(lm78_t *dev, uint8_t reg, uint8_t val, uint8_t bank)
break; break;
case 0x48: case 0x48:
/* set main SMBus address */ /* set main SMBus address */
if (dev->local & LM78_SMBUS) { if (dev->local & LM78_SMBUS)
dev->smbus_addr = (dev->regs[0x48] & 0x7f); lm78_remap(dev, dev->regs[0x48] & 0x7f);
lm78_remap(dev);
}
break; break;
case 0x49: case 0x49:
if (!(dev->local & LM78_WINBOND)) { if (!(dev->local & LM78_WINBOND)) {
if (val & 0x20) { if (val & 0x20) /* Chip Reset bit (LM78 only) resets all registers */
/* Chip Reset bit (LM78 only) resets all registers */
lm78_reset(dev, 0); lm78_reset(dev, 0);
} else { else
dev->regs[0x49] = 0x40; dev->regs[0x49] = 0x40;
}
} else { } else {
dev->regs[0x49] &= 0x01; dev->regs[0x49] &= 0x01;
} }
@@ -328,10 +324,9 @@ lm78_write(lm78_t *dev, uint8_t reg, uint8_t val, uint8_t bank)
if (!lm75) if (!lm75)
continue; continue;
if (dev->regs[0x4a] & (0x08 * (0x10 * i))) /* DIS_T2 and DIS_T3 bit disable those interfaces */ if (dev->regs[0x4a] & (0x08 * (0x10 * i))) /* DIS_T2 and DIS_T3 bit disable those interfaces */
lm75->smbus_addr = 0x00; lm75_remap(lm75, 0x80);
else else
lm75->smbus_addr = (0x48 + ((dev->regs[0x4a] >> (i * 4)) & 0x7)); lm75_remap(lm75, 0x48 + ((dev->regs[0x4a] >> (i * 4)) & 0x7));
lm75_remap(lm75);
} }
} }
break; break;
@@ -428,7 +423,7 @@ lm78_reset(lm78_t *dev, uint8_t initialization)
dev->regs[0x49] = 0x40; dev->regs[0x49] = 0x40;
} }
lm78_remap(dev); lm78_remap(dev, dev->smbus_addr);
} }

View File

@@ -102,9 +102,9 @@ smbus_piix4_write(uint16_t addr, uint8_t val, void *priv)
switch (addr - dev->io_base) { switch (addr - dev->io_base) {
case 0x00: case 0x00:
/* some status bits are reset by writing 1 to them */ /* some status bits are reset by writing 1 to them */
for (smbus_addr = 0x02; smbus_addr <= 0x10; smbus_addr = smbus_addr << 1) { for (smbus_addr = 0x02; smbus_addr <= 0x10; smbus_addr <<= 1) {
if (val & smbus_addr) if (val & smbus_addr)
dev->stat = dev->stat & ~smbus_addr; dev->stat &= ~smbus_addr;
} }
break; break;
case 0x02: case 0x02:
@@ -150,7 +150,7 @@ smbus_piix4_write(uint16_t addr, uint8_t val, void *priv)
dev->data0 = (temp & 0xFF); dev->data0 = (temp & 0xFF);
dev->data1 = (temp >> 8); dev->data1 = (temp >> 8);
} else { } else {
temp = (dev->data1 << 8) | dev->data0; temp = ((dev->data1 << 8) | dev->data0);
smbus_write_word_cmd(smbus_addr, dev->cmd, temp); smbus_write_word_cmd(smbus_addr, dev->cmd, temp);
} }
dev->next_stat = 0x2; dev->next_stat = 0x2;
@@ -216,7 +216,7 @@ smbus_piix4_remap(smbus_piix4_t *dev, uint16_t new_io_base, uint8_t enable)
dev->io_base = new_io_base; dev->io_base = new_io_base;
smbus_piix4_log("SMBus PIIX4: remap to %04Xh\n", dev->io_base); smbus_piix4_log("SMBus PIIX4: remap to %04Xh\n", dev->io_base);
if (enable && (dev->io_base != 0x0000)) if ((enable) && (dev->io_base != 0x0000))
io_sethandler(dev->io_base, 0x10, smbus_piix4_read, NULL, NULL, smbus_piix4_write, NULL, NULL, dev); io_sethandler(dev->io_base, 0x10, smbus_piix4_read, NULL, NULL, smbus_piix4_write, NULL, NULL, dev);
} }

View File

@@ -43,7 +43,7 @@ typedef struct {
extern void hwm_set_values(hwm_values_t new_values); extern void hwm_set_values(hwm_values_t new_values);
extern hwm_values_t* hwm_get_values(); extern hwm_values_t* hwm_get_values();
extern void lm75_remap(lm75_t *dev); extern void lm75_remap(lm75_t *dev, uint8_t addr);
extern uint8_t lm75_read(lm75_t *dev, uint8_t reg); extern uint8_t lm75_read(lm75_t *dev, uint8_t reg);
extern uint8_t lm75_write(lm75_t *dev, uint8_t reg, uint8_t val); extern uint8_t lm75_write(lm75_t *dev, uint8_t reg, uint8_t val);
@@ -56,5 +56,8 @@ extern const device_t w83781d_device;
extern const device_t as99127f_device; extern const device_t as99127f_device;
extern const device_t as99127f_rev2_device; extern const device_t as99127f_rev2_device;
extern const device_t gl518sm_2c_device;
extern const device_t gl518sm_2d_device;
#endif /*EMU_HWM_H*/ #endif /*EMU_HWM_H*/

View File

@@ -365,13 +365,30 @@ machine_at_ax6bc_init(const machine_t *model)
pci_register_slot(0x09, PCI_CARD_NORMAL, 4, 1, 2, 3); pci_register_slot(0x09, PCI_CARD_NORMAL, 4, 1, 2, 3);
pci_register_slot(0x0D, PCI_CARD_NORMAL, 4, 1, 2, 3); pci_register_slot(0x0D, PCI_CARD_NORMAL, 4, 1, 2, 3);
pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 1, 2, 3, 4); pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 1, 2, 3, 4);
pci_register_slot(0x01, PCI_CARD_NORMAL, 1, 2, 3, 4); pci_register_slot(0x01, PCI_CARD_NORMAL, 1, 2, 3, 4);
device_add(&i440bx_device); device_add(&i440bx_device);
device_add(&piix4e_device); device_add(&piix4e_device);
device_add(&keyboard_ps2_pci_device); device_add(&keyboard_ps2_pci_device);
device_add(&w83977tf_device); device_add(&w83977tf_device);
device_add(&sst_flash_29ee020_device); device_add(&sst_flash_29ee020_device);
spd_register(SPD_TYPE_SDRAM, 0x7, 256); spd_register(SPD_TYPE_SDRAM, 0x7, 256);
hwm_values_t machine_hwm = {
{ /* fan speeds */
3000, /* System */
3000 /* CPU */
}, { /* temperatures */
30 /* CPU */
}, { /* voltages */
2050, /* VCORE (2.05V by default) */
RESISTOR_DIVIDER(12000, 150, 47), /* +12V (15K/4.7K divider suggested in the GL518SM datasheet) */
3300 /* +3.3V */
}
};
if (model->cpu[cpu_manufacturer].cpus[cpu_effective].cpu_type == CPU_PENTIUM2)
machine_hwm.voltages[0] = 2800; /* set higher VCORE (2.8V) for Klamath */
hwm_set_values(machine_hwm);
device_add(&gl518sm_2d_device);
return ret; return ret;
} }

View File

@@ -592,7 +592,7 @@ MCHOBJ := machine.o machine_table.o \
m_at_socket4_5.o m_at_socket7_s7.o m_at_sockets7.o \ m_at_socket4_5.o m_at_socket7_s7.o m_at_sockets7.o \
m_at_socket8.o m_at_slot1.o m_at_slot2.o m_at_socket370.o m_at_socket8.o m_at_slot1.o m_at_slot2.o m_at_socket370.o
DEVOBJ := bugger.o hwm.o hwm_lm75.o hwm_lm78.o ibm_5161.o isamem.o isartc.o lpt.o postcard.o serial.o \ DEVOBJ := bugger.o hwm.o hwm_lm75.o hwm_lm78.o hwm_gl518sm.o ibm_5161.o isamem.o isartc.o lpt.o postcard.o serial.o \
smbus.o smbus_piix4.o \ smbus.o smbus_piix4.o \
keyboard.o \ keyboard.o \
keyboard_xt.o keyboard_at.o \ keyboard_xt.o keyboard_at.o \