1890 lines
71 KiB
C
1890 lines
71 KiB
C
/*
|
|
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
|
* running old operating systems and software designed for IBM
|
|
* PC systems and compatibles from 1981 through fairly recent
|
|
* system designs based on the PCI bus.
|
|
*
|
|
* This file is part of the 86Box distribution.
|
|
*
|
|
* Implementation of the SMC FDC37C932FR and FDC37C935 Super
|
|
* I/O Chips.
|
|
*
|
|
*
|
|
*
|
|
* Authors: Miran Grca, <mgrca8@gmail.com>
|
|
*
|
|
* Copyright 2016-2018 Miran Grca.
|
|
*/
|
|
#include <inttypes.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <wchar.h>
|
|
#include <86box/86box.h>
|
|
#include <86box/io.h>
|
|
#include <86box/timer.h>
|
|
#include <86box/device.h>
|
|
#include <86box/pci.h>
|
|
#include <86box/pic.h>
|
|
#include <86box/lpt.h>
|
|
#include <86box/serial.h>
|
|
#include <86box/hdc.h>
|
|
#include <86box/hdc_ide.h>
|
|
#include <86box/fdd.h>
|
|
#include <86box/fdc.h>
|
|
#include <86box/keyboard.h>
|
|
#include <86box/machine.h>
|
|
#include <86box/nvr.h>
|
|
#include <86box/apm.h>
|
|
#include <86box/access_bus.h>
|
|
#include <86box/acpi.h>
|
|
#include <86box/plat.h>
|
|
#include <86box/plat_unused.h>
|
|
#include <86box/video.h>
|
|
#include <86box/sio.h>
|
|
#include "cpu.h"
|
|
|
|
typedef struct fdc37c93x_t {
|
|
uint8_t chip_id;
|
|
uint8_t is_apm;
|
|
uint8_t is_compaq;
|
|
uint8_t has_nvr;
|
|
uint8_t max_ld;
|
|
uint8_t tries;
|
|
uint8_t port_370;
|
|
uint8_t gpio_reg;
|
|
uint8_t gpio_regs[256];
|
|
uint8_t gpio_pulldn[8];
|
|
uint8_t auxio_reg;
|
|
uint8_t regs[48];
|
|
uint8_t alt_regs[3][8];
|
|
uint8_t ld_regs[11][256];
|
|
uint16_t kbc_type;
|
|
uint16_t superio_base;
|
|
uint16_t fdc_base;
|
|
uint16_t lpt_base;
|
|
uint16_t nvr_pri_base;
|
|
uint16_t nvr_sec_base;
|
|
uint16_t kbc_base;
|
|
uint16_t gpio_base; /* Set to EA */
|
|
uint16_t auxio_base;
|
|
uint16_t uart_base[2];
|
|
int locked;
|
|
int cur_reg;
|
|
fdc_t *fdc;
|
|
access_bus_t *access_bus;
|
|
nvr_t *nvr;
|
|
acpi_t *acpi;
|
|
void *kbc;
|
|
serial_t *uart[2];
|
|
} fdc37c93x_t;
|
|
|
|
static void fdc37c93x_write(uint16_t port, uint8_t val, void *priv);
|
|
static uint8_t fdc37c93x_read(uint16_t port, void *priv);
|
|
|
|
static uint8_t gp_func_regs[8][8] = { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, /* GP00-GP07 */
|
|
{ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7 }, /* GP10-GP17 */
|
|
{ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef }, /* GP20-GP27 */
|
|
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, /* GP30-GP37 */
|
|
{ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7 }, /* GP40-GP47 */
|
|
{ 0xc8, 0xc9, 0xff, 0xcb, 0xcc, 0xff, 0xff, 0xff }, /* GP50-GP57 */
|
|
{ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7 }, /* GP60-GP67 */
|
|
{ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf } }; /* GP70-GP77 */
|
|
|
|
static uint16_t
|
|
make_port_superio(const fdc37c93x_t *dev)
|
|
{
|
|
const uint16_t r0 = dev->regs[0x26];
|
|
const uint16_t r1 = dev->regs[0x27];
|
|
|
|
const uint16_t p = (r1 << 8) + r0;
|
|
|
|
return p;
|
|
}
|
|
|
|
static uint16_t
|
|
make_port(const fdc37c93x_t *dev, const uint8_t ld)
|
|
{
|
|
const uint16_t r0 = dev->ld_regs[ld][0x60];
|
|
const uint16_t r1 = dev->ld_regs[ld][0x61];
|
|
|
|
const uint16_t p = (r0 << 8) + r1;
|
|
|
|
return p;
|
|
}
|
|
|
|
static uint16_t
|
|
make_port_sec(const fdc37c93x_t *dev, const uint8_t ld)
|
|
{
|
|
const uint16_t r0 = dev->ld_regs[ld][0x62];
|
|
const uint16_t r1 = dev->ld_regs[ld][0x63];
|
|
|
|
const uint16_t p = (r0 << 8) + r1;
|
|
|
|
return p;
|
|
}
|
|
|
|
static uint8_t
|
|
fdc37c93x_auxio_read(UNUSED(uint16_t port), void *priv)
|
|
{
|
|
const fdc37c93x_t *dev = (fdc37c93x_t *) priv;
|
|
|
|
return dev->auxio_reg;
|
|
}
|
|
|
|
static void
|
|
fdc37c93x_auxio_write(UNUSED(uint16_t port), uint8_t val, void *priv)
|
|
{
|
|
fdc37c93x_t *dev = (fdc37c93x_t *) priv;
|
|
|
|
dev->auxio_reg = val;
|
|
}
|
|
|
|
static __inline uint8_t
|
|
fdc37c93x_do_read_gp(fdc37c93x_t *dev, int reg, int bit)
|
|
{
|
|
/* Update bit 2 on the Acer V35N according to the selected graphics card type. */
|
|
if ((reg == 2) && (strstr(machine_get_internal_name(), "acer") != NULL))
|
|
dev->gpio_pulldn[reg] = (dev->gpio_pulldn[reg] & 0xfb) | (video_is_mda() ? 0x00 : 0x04);
|
|
|
|
return dev->gpio_regs[reg] & dev->gpio_pulldn[reg] & (1 << bit);
|
|
}
|
|
|
|
static __inline uint8_t
|
|
fdc37c93x_do_read_alt(const fdc37c93x_t *dev, int alt, int reg, int bit)
|
|
{
|
|
return dev->alt_regs[alt][reg] & (1 << bit);
|
|
}
|
|
|
|
static uint8_t
|
|
fdc37c93x_read_gp(const fdc37c93x_t *dev, int reg, int bit)
|
|
{
|
|
uint8_t gp_reg = gp_func_regs[reg][bit];
|
|
uint8_t gp_func_reg = dev->ld_regs[0x08][gp_reg];
|
|
uint8_t gp_func;
|
|
uint8_t ret = 1 << bit;
|
|
|
|
if (gp_func_reg & 0x01) switch (reg) {
|
|
default:
|
|
/* Do nothing, this GP does not exist. */
|
|
break;
|
|
case 1:
|
|
switch (bit) {
|
|
default:
|
|
gp_func = (gp_func_reg >> 3) & 0x01;
|
|
if (gp_func == 0x00)
|
|
ret = fdc37c93x_do_read_gp((fdc37c93x_t *) dev, reg, bit);
|
|
else
|
|
ret = fdc37c93x_do_read_alt(dev, 0, reg, bit);
|
|
break;
|
|
case 1:
|
|
gp_func = (gp_func_reg >> 3) & 0x03;
|
|
if (!(gp_func & 0x01)) {
|
|
if (gp_func & 0x02)
|
|
ret = fdc37c93x_do_read_alt(dev, 0, reg, bit);
|
|
else
|
|
ret = fdc37c93x_do_read_gp((fdc37c93x_t *) dev, reg, bit);
|
|
}
|
|
break;
|
|
case 3:
|
|
gp_func = (gp_func_reg >> 3) & 0x01;
|
|
if (gp_func == 0x01)
|
|
/* TODO: Write to power LED if it's ever implemented. */
|
|
ret = fdc37c93x_do_read_alt(dev, 0, reg, bit);
|
|
else
|
|
ret = fdc37c93x_do_read_gp((fdc37c93x_t *) dev, reg, bit);
|
|
break;
|
|
case 6:
|
|
gp_func = (gp_func_reg >> 3) & 0x03;
|
|
switch (gp_func) {
|
|
case 0:
|
|
ret = fdc37c93x_do_read_gp((fdc37c93x_t *) dev, reg, bit);
|
|
break;
|
|
case 1 ... 3:
|
|
ret = fdc37c93x_do_read_alt(dev, gp_func - 1, reg, bit);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 2:
|
|
switch (bit) {
|
|
default:
|
|
gp_func = (gp_func_reg >> 3) & 0x01;
|
|
if (gp_func == 0x00)
|
|
ret = fdc37c93x_do_read_gp((fdc37c93x_t *) dev, reg, bit);
|
|
else
|
|
ret = fdc37c93x_do_read_alt(dev, 0, reg, bit);
|
|
break;
|
|
case 0:
|
|
gp_func = (gp_func_reg >> 3) & 0x03;
|
|
switch (gp_func) {
|
|
case 0:
|
|
ret = fdc37c93x_do_read_gp((fdc37c93x_t *) dev, reg, bit);
|
|
break;
|
|
case 1:
|
|
ret = fdc37c93x_do_read_alt(dev, 0, reg, bit);
|
|
break;
|
|
case 2:
|
|
ret = kbc_at_read_p(dev->kbc, 2, 0x01) ? (1 << bit) : 0x00;
|
|
break;
|
|
}
|
|
break;
|
|
case 1: case 2:
|
|
gp_func = (gp_func_reg >> 3) & 0x03;
|
|
switch (gp_func) {
|
|
case 0:
|
|
ret = fdc37c93x_do_read_gp((fdc37c93x_t *) dev, reg, bit);
|
|
break;
|
|
case 1: case 2:
|
|
ret = fdc37c93x_do_read_alt(dev, gp_func - 1, reg, bit);
|
|
break;
|
|
}
|
|
break;
|
|
case 5:
|
|
gp_func = (gp_func_reg >> 3) & 0x01;
|
|
if (gp_func == 0x00)
|
|
ret = fdc37c93x_do_read_gp((fdc37c93x_t *) dev, reg, bit);
|
|
else
|
|
ret = kbc_at_read_p(dev->kbc, 2, 0x02) ? (1 << bit) : 0x00;
|
|
break;
|
|
case 6: case 7:
|
|
/* Do nothing, these bits do not exist. */
|
|
break;
|
|
}
|
|
break;
|
|
case 4:
|
|
gp_func = (gp_func_reg >> 3) & 0x03;
|
|
switch (bit) {
|
|
default:
|
|
switch (gp_func) {
|
|
case 0:
|
|
ret = fdc37c93x_do_read_alt(dev, 0, reg, bit);
|
|
break;
|
|
case 1:
|
|
ret = fdc37c93x_do_read_gp((fdc37c93x_t *) dev, reg, bit);
|
|
break;
|
|
}
|
|
break;
|
|
case 0: case 1:
|
|
switch (gp_func) {
|
|
case 0:
|
|
ret = fdc_get_media_id(dev->fdc, bit ^ 1) ? (1 << bit) : 0x00;
|
|
break;
|
|
case 1:
|
|
ret = fdc37c93x_do_read_gp((fdc37c93x_t *) dev, reg, bit);
|
|
break;
|
|
}
|
|
break;
|
|
case 6:
|
|
switch (gp_func) {
|
|
case 0:
|
|
ret = fdc37c93x_do_read_alt(dev, 0, reg, bit);
|
|
break;
|
|
case 1:
|
|
ret = fdc37c93x_do_read_gp((fdc37c93x_t *) dev, reg, bit);
|
|
break;
|
|
case 2:
|
|
/* TODO: Write to power LED if it's ever implemented. */
|
|
ret = fdc37c93x_do_read_alt(dev, 1, reg, bit);
|
|
break;
|
|
case 3:
|
|
ret = fdc37c93x_do_read_alt(dev, 2, reg, bit);
|
|
break;
|
|
}
|
|
break;
|
|
case 7:
|
|
switch (gp_func) {
|
|
case 0:
|
|
ret = fdc37c93x_do_read_alt(dev, 0, reg, bit);
|
|
break;
|
|
case 1:
|
|
ret = fdc37c93x_do_read_gp((fdc37c93x_t *) dev, reg, bit);
|
|
break;
|
|
case 2:
|
|
ret = fdc37c93x_do_read_alt(dev, 1, reg, bit);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 5:
|
|
gp_func = (gp_func_reg >> 3) & 0x03;
|
|
switch (bit) {
|
|
default:
|
|
break;
|
|
case 0: case 3: case 4:
|
|
switch (gp_func) {
|
|
case 0:
|
|
ret = fdc37c93x_do_read_alt(dev, 0, reg, bit);
|
|
break;
|
|
case 1:
|
|
ret = fdc37c93x_do_read_gp((fdc37c93x_t *) dev, reg, bit);
|
|
break;
|
|
}
|
|
break;
|
|
case 1:
|
|
switch (gp_func) {
|
|
case 0:
|
|
ret = fdc37c93x_do_read_alt(dev, 0, reg, bit);
|
|
break;
|
|
case 1:
|
|
ret = fdc37c93x_do_read_gp((fdc37c93x_t *) dev, reg, bit);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 6:
|
|
gp_func = (gp_func_reg >> 3) & 0x03;
|
|
switch (bit) {
|
|
default:
|
|
switch (gp_func) {
|
|
case 0:
|
|
ret = fdc37c93x_do_read_alt(dev, 0, reg, bit);
|
|
break;
|
|
case 1:
|
|
ret = fdc37c93x_do_read_gp((fdc37c93x_t *) dev, reg, bit);
|
|
break;
|
|
case 2:
|
|
ret = kbc_at_read_p(dev->kbc, 1, 1 << bit);
|
|
break;
|
|
}
|
|
break;
|
|
case 0:
|
|
switch (gp_func) {
|
|
case 0:
|
|
ret = fdc37c93x_do_read_alt(dev, 0, reg, bit);
|
|
break;
|
|
case 1:
|
|
ret = fdc37c93x_do_read_gp((fdc37c93x_t *) dev, reg, bit);
|
|
break;
|
|
case 2:
|
|
/* TODO: Write to power LED if it's ever implemented. */
|
|
ret = fdc37c93x_do_read_alt(dev, 1, reg, bit);
|
|
break;
|
|
}
|
|
break;
|
|
case 1:
|
|
switch (gp_func) {
|
|
case 0:
|
|
ret = fdc37c93x_do_read_alt(dev, 0, reg, bit);
|
|
break;
|
|
case 1:
|
|
ret = fdc37c93x_do_read_gp((fdc37c93x_t *) dev, reg, bit);
|
|
break;
|
|
case 2:
|
|
ret = fdc37c93x_do_read_alt(dev, 1, reg, bit);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 7:
|
|
gp_func = (gp_func_reg >> 3) & 0x03;
|
|
switch (bit) {
|
|
default:
|
|
switch (gp_func) {
|
|
case 0:
|
|
ret = fdc37c93x_do_read_alt(dev, 0, reg, bit);
|
|
break;
|
|
case 1:
|
|
ret = fdc37c93x_do_read_gp((fdc37c93x_t *) dev, reg, bit);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (gp_func_reg & 0x02)
|
|
ret ^= (1 << bit);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static __inline void
|
|
fdc37c93x_do_write_gp(fdc37c93x_t *dev, int reg, int bit, int set)
|
|
{
|
|
dev->gpio_regs[reg] = (dev->gpio_regs[reg] & ~(1 << bit)) |
|
|
(set << bit);
|
|
}
|
|
|
|
static __inline void
|
|
fdc37c93x_do_write_alt(fdc37c93x_t *dev, int alt, int reg, int bit, int set)
|
|
{
|
|
dev->alt_regs[alt][reg] = (dev->alt_regs[alt][reg] & ~(1 << bit)) |
|
|
(set << bit);
|
|
}
|
|
|
|
static void
|
|
fdc37c93x_write_gp(fdc37c93x_t *dev, int reg, int bit, int set)
|
|
{
|
|
uint8_t gp_func_reg = dev->ld_regs[0x08][gp_func_regs[reg][bit]];
|
|
uint8_t gp_func;
|
|
|
|
if (gp_func_reg & 0x02)
|
|
set = !set;
|
|
|
|
if (!(gp_func_reg & 0x01)) switch (reg) {
|
|
default:
|
|
/* Do nothing, this GP does not exist. */
|
|
break;
|
|
case 1:
|
|
switch (bit) {
|
|
default:
|
|
gp_func = (gp_func_reg >> 3) & 0x01;
|
|
if (gp_func == 0x00)
|
|
fdc37c93x_do_write_gp(dev, reg, bit, set);
|
|
else
|
|
fdc37c93x_do_write_alt(dev, 0, reg, bit, set);
|
|
break;
|
|
case 1:
|
|
gp_func = (gp_func_reg >> 3) & 0x03;
|
|
if (!(gp_func & 0x01)) {
|
|
if (gp_func & 0x02) {
|
|
set ? picint(1 << 13) : picintc(1 << 13);
|
|
fdc37c93x_do_write_alt(dev, 0, reg, bit, set);
|
|
} else
|
|
fdc37c93x_do_write_gp(dev, reg, bit, set);
|
|
}
|
|
break;
|
|
case 3:
|
|
gp_func = (gp_func_reg >> 3) & 0x01;
|
|
if (gp_func == 0x01)
|
|
/* TODO: Write to power LED if it's ever implemented. */
|
|
fdc37c93x_do_write_alt(dev, 0, reg, bit, set);
|
|
else
|
|
fdc37c93x_do_write_gp(dev, reg, bit, set);
|
|
break;
|
|
case 6:
|
|
gp_func = (gp_func_reg >> 3) & 0x03;
|
|
switch (gp_func) {
|
|
case 0:
|
|
fdc37c93x_do_write_gp(dev, reg, bit, set);
|
|
break;
|
|
case 1 ... 3:
|
|
fdc37c93x_do_write_alt(dev, gp_func - 1, reg, bit, set);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 2:
|
|
switch (bit) {
|
|
default:
|
|
gp_func = (gp_func_reg >> 3) & 0x01;
|
|
if (gp_func == 0x00)
|
|
fdc37c93x_do_write_gp(dev, reg, bit, set);
|
|
else
|
|
fdc37c93x_do_write_alt(dev, 0, reg, bit, set);
|
|
break;
|
|
case 0:
|
|
gp_func = (gp_func_reg >> 3) & 0x03;
|
|
switch (gp_func) {
|
|
case 0:
|
|
fdc37c93x_do_write_gp(dev, reg, bit, set);
|
|
break;
|
|
case 1:
|
|
fdc37c93x_do_write_alt(dev, 0, reg, bit, set);
|
|
break;
|
|
case 2:
|
|
kbc_at_write_p(dev->kbc, 2, 0xfe, set);
|
|
break;
|
|
}
|
|
break;
|
|
case 1: case 2:
|
|
gp_func = (gp_func_reg >> 3) & 0x03;
|
|
switch (gp_func) {
|
|
case 0:
|
|
fdc37c93x_do_write_gp(dev, reg, bit, set);
|
|
break;
|
|
case 1: case 2:
|
|
fdc37c93x_do_write_alt(dev, gp_func - 1, reg, bit, set);
|
|
break;
|
|
}
|
|
break;
|
|
case 5:
|
|
gp_func = (gp_func_reg >> 3) & 0x01;
|
|
if (gp_func == 0x00)
|
|
fdc37c93x_do_write_gp(dev, reg, bit, set);
|
|
else
|
|
kbc_at_write_p(dev->kbc, 2, 0xfd, set << 1);
|
|
break;
|
|
case 6: case 7:
|
|
/* Do nothing, these bits do not exist. */
|
|
break;
|
|
}
|
|
break;
|
|
case 4:
|
|
gp_func = (gp_func_reg >> 3) & 0x03;
|
|
switch (bit) {
|
|
default:
|
|
switch (gp_func) {
|
|
case 0:
|
|
fdc37c93x_do_write_alt(dev, 0, reg, bit, set);
|
|
break;
|
|
case 1:
|
|
fdc37c93x_do_write_gp(dev, reg, bit, set);
|
|
break;
|
|
}
|
|
break;
|
|
case 0: case 1:
|
|
switch (gp_func) {
|
|
case 0:
|
|
fdc_set_media_id(dev->fdc, bit ^ 1, set);
|
|
break;
|
|
case 1:
|
|
fdc37c93x_do_write_gp(dev, reg, bit, set);
|
|
break;
|
|
}
|
|
break;
|
|
case 6:
|
|
switch (gp_func) {
|
|
case 0:
|
|
fdc37c93x_do_write_alt(dev, 0, reg, bit, set);
|
|
break;
|
|
case 1:
|
|
fdc37c93x_do_write_gp(dev, reg, bit, set);
|
|
break;
|
|
case 2:
|
|
/* TODO: Write to power LED if it's ever implemented. */
|
|
fdc37c93x_do_write_alt(dev, 1, reg, bit, set);
|
|
break;
|
|
case 3:
|
|
fdc37c93x_do_write_alt(dev, 2, reg, bit, set);
|
|
break;
|
|
}
|
|
break;
|
|
case 7:
|
|
switch (gp_func) {
|
|
case 0:
|
|
fdc37c93x_do_write_alt(dev, 0, reg, bit, set);
|
|
break;
|
|
case 1:
|
|
fdc37c93x_do_write_gp(dev, reg, bit, set);
|
|
break;
|
|
case 2:
|
|
fdc37c93x_do_write_alt(dev, 1, reg, bit, set);
|
|
if (!set)
|
|
smi_raise();
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 5:
|
|
gp_func = (gp_func_reg >> 3) & 0x03;
|
|
switch (bit) {
|
|
default:
|
|
break;
|
|
case 0: case 3: case 4:
|
|
switch (gp_func) {
|
|
case 0:
|
|
fdc37c93x_do_write_alt(dev, 0, reg, bit, set);
|
|
break;
|
|
case 1:
|
|
fdc37c93x_do_write_gp(dev, reg, bit, set);
|
|
break;
|
|
}
|
|
break;
|
|
case 1:
|
|
switch (gp_func) {
|
|
case 0:
|
|
fdc37c93x_do_write_alt(dev, 0, reg, bit, set);
|
|
if (set)
|
|
plat_power_off();
|
|
break;
|
|
case 1:
|
|
fdc37c93x_do_write_gp(dev, reg, bit, set);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 6:
|
|
gp_func = (gp_func_reg >> 3) & 0x03;
|
|
switch (bit) {
|
|
default:
|
|
switch (gp_func) {
|
|
case 0:
|
|
fdc37c93x_do_write_alt(dev, 0, reg, bit, set);
|
|
break;
|
|
case 1:
|
|
fdc37c93x_do_write_gp(dev, reg, bit, set);
|
|
break;
|
|
case 2:
|
|
kbc_at_write_p(dev->kbc, 1, ~(1 << bit), set << bit);
|
|
break;
|
|
}
|
|
break;
|
|
case 0:
|
|
switch (gp_func) {
|
|
case 0:
|
|
fdc37c93x_do_write_alt(dev, 0, reg, bit, set);
|
|
break;
|
|
case 1:
|
|
fdc37c93x_do_write_gp(dev, reg, bit, set);
|
|
break;
|
|
case 2:
|
|
/* TODO: Write to power LED if it's ever implemented. */
|
|
fdc37c93x_do_write_alt(dev, 1, reg, bit, set);
|
|
break;
|
|
}
|
|
break;
|
|
case 1:
|
|
switch (gp_func) {
|
|
case 0:
|
|
fdc37c93x_do_write_alt(dev, 0, reg, bit, set);
|
|
break;
|
|
case 1:
|
|
fdc37c93x_do_write_gp(dev, reg, bit, set);
|
|
break;
|
|
case 2:
|
|
fdc37c93x_do_write_alt(dev, 1, reg, bit, set);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 7:
|
|
gp_func = (gp_func_reg >> 3) & 0x03;
|
|
switch (bit) {
|
|
default:
|
|
switch (gp_func) {
|
|
case 0:
|
|
fdc37c93x_do_write_alt(dev, 0, reg, bit, set);
|
|
break;
|
|
case 1:
|
|
fdc37c93x_do_write_gp(dev, reg, bit, set);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static uint8_t
|
|
fdc37c93x_gpio_read(uint16_t port, void *priv)
|
|
{
|
|
const fdc37c93x_t *dev = (fdc37c93x_t *) priv;
|
|
uint8_t ret = 0xff;
|
|
|
|
if (dev->locked) {
|
|
if (dev->is_compaq)
|
|
ret = fdc37c93x_read(port & 0x0001, priv);
|
|
} else if (port & 0x0001) switch (dev->gpio_reg) {
|
|
case 0x01: case 0x02:
|
|
ret = 0x00;
|
|
for (uint8_t i = 0; i < 8; i++)
|
|
ret |= fdc37c93x_read_gp(dev, dev->gpio_reg, i);
|
|
break;
|
|
case 0x03:
|
|
ret = dev->ld_regs[0x08][0xf4];
|
|
break;
|
|
case 0x04 ... 0x07:
|
|
if (dev->chip_id >= FDC37C93X_FR) {
|
|
ret = 0x00;
|
|
for (uint8_t i = 0; i < 8; i++)
|
|
ret |= fdc37c93x_read_gp(dev, dev->gpio_reg, i);
|
|
}
|
|
break;
|
|
case 0x08 ... 0x0f:
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
ret = dev->ld_regs[0x08][0xb0 + dev->gpio_reg - 0x08];
|
|
break;
|
|
} else
|
|
ret = dev->gpio_reg;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
fdc37c93x_gpio_write(uint16_t port, uint8_t val, void *priv)
|
|
{
|
|
fdc37c93x_t *dev = (fdc37c93x_t *) priv;
|
|
|
|
if (dev->locked) {
|
|
if (dev->is_compaq)
|
|
fdc37c93x_write(port & 0x0001, val, priv);
|
|
} else if (port & 0x0001) switch (dev->gpio_reg) {
|
|
case 0x01: case 0x02:
|
|
for (uint8_t i = 0; i < 8; i++)
|
|
fdc37c93x_write_gp(dev, dev->gpio_reg, i, val & (1 << i));
|
|
break;
|
|
case 0x03:
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
dev->ld_regs[0x08][0xf4] = val & 0xef;
|
|
else
|
|
dev->ld_regs[0x08][0xf4] = val & 0x0f;
|
|
break;
|
|
case 0x04 ... 0x07:
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
for (uint8_t i = 0; i < 8; i++)
|
|
fdc37c93x_write_gp(dev, dev->gpio_reg, i, val & (1 << i));
|
|
break;
|
|
case 0x08: case 0x0a:
|
|
case 0x0c: case 0x0e:
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
dev->ld_regs[0x08][0xb0 + dev->gpio_reg - 0x08] = val;
|
|
break;
|
|
case 0x09:
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
dev->ld_regs[0x08][0xb0 + dev->gpio_reg - 0x08] = val & 0xd3;
|
|
break;
|
|
case 0x0b:
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
dev->ld_regs[0x08][0xb0 + dev->gpio_reg - 0x08] = val & 0x17;
|
|
break;
|
|
case 0x0d:
|
|
if (dev->chip_id == FDC37C93X_APM)
|
|
dev->ld_regs[0x08][0xb0 + dev->gpio_reg - 0x08] = val;
|
|
else if (dev->chip_id == FDC37C93X_FR)
|
|
dev->ld_regs[0x08][0xb0 + dev->gpio_reg - 0x08] = val & 0xbf;
|
|
break;
|
|
case 0x0f:
|
|
if (dev->chip_id == FDC37C93X_APM)
|
|
dev->ld_regs[0x08][0xb0 + dev->gpio_reg - 0x08] = val & 0x7f;
|
|
else if (dev->chip_id == FDC37C93X_FR)
|
|
dev->ld_regs[0x08][0xb0 + dev->gpio_reg - 0x08] = val & 0x3f;
|
|
break;
|
|
} else
|
|
dev->gpio_reg = val;
|
|
}
|
|
|
|
static void
|
|
fdc37c93x_superio_handler(fdc37c93x_t *dev)
|
|
{
|
|
if (!dev->is_compaq) {
|
|
if (dev->superio_base != 0x0000)
|
|
io_removehandler(dev->superio_base, 0x0002,
|
|
fdc37c93x_read, NULL, NULL, fdc37c93x_write, NULL, NULL, dev);
|
|
dev->superio_base = make_port_superio(dev);
|
|
if (dev->superio_base != 0x0000)
|
|
io_sethandler(dev->superio_base, 0x0002,
|
|
fdc37c93x_read, NULL, NULL, fdc37c93x_write, NULL, NULL, dev);
|
|
}
|
|
}
|
|
|
|
static void
|
|
fdc37c93x_fdc_handler(fdc37c93x_t *dev)
|
|
{
|
|
const uint8_t global_enable = !!(dev->regs[0x22] & (1 << 0));
|
|
const uint8_t local_enable = !!dev->ld_regs[0][0x30];
|
|
const uint16_t old_base = dev->fdc_base;
|
|
|
|
dev->fdc_base = 0x0000;
|
|
|
|
if (global_enable && local_enable)
|
|
dev->fdc_base = make_port(dev, 0) & 0xfff8;
|
|
|
|
if (dev->fdc_base != old_base) {
|
|
if ((old_base >= 0x0100) && (old_base <= 0x0ff8))
|
|
fdc_remove(dev->fdc);
|
|
|
|
if ((dev->fdc_base >= 0x0100) && (dev->fdc_base <= 0x0ff8))
|
|
fdc_set_base(dev->fdc, dev->fdc_base);
|
|
}
|
|
}
|
|
|
|
static void
|
|
fdc37c93x_lpt_handler(fdc37c93x_t *dev)
|
|
{
|
|
const uint8_t global_enable = !!(dev->regs[0x22] & (1 << 3));
|
|
const uint8_t local_enable = !!dev->ld_regs[3][0x30];
|
|
uint8_t lpt_irq = dev->ld_regs[3][0x70];
|
|
const uint16_t old_base = dev->lpt_base;
|
|
|
|
if (lpt_irq > 15)
|
|
lpt_irq = 0xff;
|
|
|
|
dev->lpt_base = 0x0000;
|
|
|
|
if (global_enable && local_enable)
|
|
dev->lpt_base = make_port(dev, 3) & 0xfffc;
|
|
|
|
if (dev->lpt_base != old_base) {
|
|
if ((old_base >= 0x0100) && (old_base <= 0x0ffc))
|
|
lpt1_remove();
|
|
|
|
if ((dev->lpt_base >= 0x0100) && (dev->lpt_base <= 0x0ffc))
|
|
lpt1_setup(dev->lpt_base);
|
|
}
|
|
|
|
lpt1_irq(lpt_irq);
|
|
}
|
|
|
|
static void
|
|
fdc37c93x_serial_handler(fdc37c93x_t *dev, const int uart)
|
|
{
|
|
const uint8_t uart_no = 4 + uart;
|
|
const uint8_t global_enable = !!(dev->regs[0x22] & (1 << uart_no));
|
|
const uint8_t local_enable = !!dev->ld_regs[uart_no][0x30];
|
|
const uint16_t old_base = dev->uart_base[uart];
|
|
|
|
dev->uart_base[uart] = 0x0000;
|
|
|
|
if (global_enable && local_enable)
|
|
dev->uart_base[uart] = make_port(dev, uart_no) & 0xfff8;
|
|
|
|
if (dev->uart_base[uart] != old_base) {
|
|
if ((old_base >= 0x0100) && (old_base <= 0x0ff8))
|
|
serial_remove(dev->uart[uart]);
|
|
|
|
if ((dev->uart_base[uart] >= 0x0100) && (dev->uart_base[uart] <= 0x0ff8))
|
|
serial_setup(dev->uart[uart], dev->uart_base[uart], dev->ld_regs[uart_no][0x70]);
|
|
}
|
|
|
|
/*
|
|
TODO: If UART 2's own IRQ pin is also enabled when shared,
|
|
it should also be asserted.
|
|
*/
|
|
if ((dev->chip_id >= FDC37C93X_FR) && (dev->ld_regs[4][0xf0] & 0x80))
|
|
serial_irq(dev->uart[uart], dev->ld_regs[4][0x70]);
|
|
else
|
|
serial_irq(dev->uart[uart], dev->ld_regs[uart_no][0x70]);
|
|
}
|
|
|
|
static void
|
|
fdc37c93x_nvr_pri_handler(const fdc37c93x_t *dev)
|
|
{
|
|
uint8_t local_enable = !!dev->ld_regs[6][0x30];
|
|
|
|
if (dev->chip_id != 0x02)
|
|
local_enable &= ((dev->ld_regs[6][0xf0] & 0x90) != 0x80);
|
|
|
|
nvr_at_handler(0, 0x70, dev->nvr);
|
|
if (local_enable)
|
|
nvr_at_handler(1, 0x70, dev->nvr);
|
|
}
|
|
|
|
static void
|
|
fdc37c93x_nvr_sec_handler(fdc37c93x_t *dev)
|
|
{
|
|
uint8_t local_enable = !!dev->ld_regs[6][0x30];
|
|
const uint16_t old_base = dev->nvr_sec_base;
|
|
|
|
local_enable &= (((dev->ld_regs[6][0xf0] & 0xe0) == 0x80) ||
|
|
((dev->ld_regs[6][0xf0] & 0xe0) == 0xe0));
|
|
|
|
dev->nvr_sec_base = 0x0000;
|
|
|
|
if (local_enable)
|
|
dev->nvr_sec_base = make_port_sec(dev, 6) & 0xfffe;
|
|
|
|
if (dev->nvr_sec_base != old_base) {
|
|
if ((old_base > 0x0000) && (old_base <= 0x0ffe))
|
|
nvr_at_sec_handler(0, dev->nvr_sec_base, dev->nvr);
|
|
|
|
/* Datasheet erratum: First it says minimum address is 0x0100, but later implies that it's 0x0000
|
|
and that default is 0x0070, same as (unrelocatable) primary NVR. */
|
|
if ((dev->nvr_sec_base > 0x0000) && (dev->nvr_sec_base <= 0x0ffe))
|
|
nvr_at_sec_handler(1, dev->nvr_sec_base, dev->nvr);
|
|
}
|
|
}
|
|
|
|
static void
|
|
fdc37c93x_kbc_handler(fdc37c93x_t *dev)
|
|
{
|
|
const uint8_t local_enable = !!dev->ld_regs[7][0x30];
|
|
const uint16_t old_base = dev->kbc_base;
|
|
|
|
dev->kbc_base = local_enable ? 0x0060 : 0x0000;
|
|
|
|
if (dev->kbc_base != old_base)
|
|
kbc_at_handler(local_enable, dev->kbc);
|
|
}
|
|
|
|
static void
|
|
fdc37c93x_auxio_handler(fdc37c93x_t *dev)
|
|
{
|
|
const uint8_t local_enable = !!dev->ld_regs[8][0x30];
|
|
const uint16_t old_base = dev->auxio_base;
|
|
|
|
if (local_enable)
|
|
dev->auxio_base = make_port(dev, 8);
|
|
else
|
|
dev->auxio_base = 0x0000;
|
|
|
|
if (dev->auxio_base != old_base) {
|
|
if ((old_base >= 0x0100) && (old_base <= 0x0fff))
|
|
io_removehandler(old_base, 0x0001,
|
|
fdc37c93x_auxio_read, NULL, NULL, fdc37c93x_auxio_write, NULL, NULL, dev);
|
|
|
|
if ((dev->auxio_base >= 0x0100) && (dev->auxio_base <= 0x0fff))
|
|
io_sethandler(dev->auxio_base, 0x0001,
|
|
fdc37c93x_auxio_read, NULL, NULL, fdc37c93x_auxio_write, NULL, NULL, dev);
|
|
}
|
|
}
|
|
|
|
static void
|
|
fdc37c93x_gpio_handler(fdc37c93x_t *dev)
|
|
{
|
|
const uint8_t local_enable = !!(dev->regs[0x03] & 0x80) ||
|
|
(dev->is_compaq && dev->locked);
|
|
const uint16_t old_base = dev->gpio_base;
|
|
|
|
dev->gpio_base = 0x0000;
|
|
|
|
if (local_enable) switch (dev->regs[0x03] & 0x03) {
|
|
default:
|
|
break;
|
|
case 0:
|
|
dev->gpio_base = 0x00e0;
|
|
break;
|
|
case 1:
|
|
dev->gpio_base = 0x00e2;
|
|
break;
|
|
case 2:
|
|
dev->gpio_base = 0x00e4;
|
|
break;
|
|
case 3:
|
|
dev->gpio_base = 0x00ea; /* Default */
|
|
break;
|
|
}
|
|
|
|
if (dev->gpio_base != old_base) {
|
|
if (old_base != 0x0000)
|
|
io_removehandler(old_base, 0x0002,
|
|
fdc37c93x_gpio_read, NULL, NULL, fdc37c93x_gpio_write, NULL, NULL, dev);
|
|
|
|
if (dev->gpio_base > 0x0000)
|
|
io_sethandler(dev->gpio_base, 0x0002,
|
|
fdc37c93x_gpio_read, NULL, NULL, fdc37c93x_gpio_write, NULL, NULL, dev);
|
|
}
|
|
}
|
|
|
|
static void
|
|
fdc37c93x_access_bus_handler(fdc37c93x_t *dev)
|
|
{
|
|
const uint8_t global_enable = !!(dev->regs[0x22] & (1 << 6));
|
|
const uint8_t local_enable = !!dev->ld_regs[9][0x30];
|
|
const uint16_t ld_port = dev->access_bus->base = make_port(dev, 9);
|
|
|
|
access_bus_handler(dev->access_bus, global_enable && local_enable, ld_port);
|
|
}
|
|
|
|
static void
|
|
fdc37c93x_acpi_handler(fdc37c93x_t *dev)
|
|
{
|
|
uint16_t ld_port;
|
|
const uint8_t local_enable = !!dev->ld_regs[0x0a][0x30];
|
|
const uint8_t sci_irq = dev->ld_regs[0x0a][0x70];
|
|
|
|
acpi_update_io_mapping(dev->acpi, 0x0000, local_enable);
|
|
if (local_enable) {
|
|
ld_port = make_port(dev, 0x0a) & 0xFFF0;
|
|
if ((ld_port >= 0x0100) && (ld_port <= 0x0FF0))
|
|
acpi_update_io_mapping(dev->acpi, ld_port, local_enable);
|
|
}
|
|
|
|
acpi_update_aux_io_mapping(dev->acpi, 0x0000, local_enable);
|
|
if (local_enable) {
|
|
ld_port = make_port_sec(dev, 0x0a) & 0xFFF8;
|
|
if ((ld_port >= 0x0100) && (ld_port <= 0x0FF8))
|
|
acpi_update_aux_io_mapping(dev->acpi, ld_port, local_enable);
|
|
}
|
|
|
|
acpi_set_irq_line(dev->acpi, sci_irq);
|
|
}
|
|
|
|
static void
|
|
fdc37c93x_state_change(fdc37c93x_t *dev, const uint8_t locked)
|
|
{
|
|
dev->locked = locked;
|
|
fdc_3f1_enable(dev->fdc, !locked);
|
|
fdc37c93x_gpio_handler(dev);
|
|
}
|
|
|
|
static void
|
|
fdc37c93x_write(uint16_t port, uint8_t val, void *priv)
|
|
{
|
|
fdc37c93x_t *dev = (fdc37c93x_t *) priv;
|
|
uint8_t index = !(port & 1);
|
|
uint8_t valxor;
|
|
|
|
if (port == 0x00fb) {
|
|
fdc37c93x_state_change(dev, 1);
|
|
dev->tries = 0;
|
|
} else if (port == 0x00f9)
|
|
fdc37c93x_state_change(dev, 0);
|
|
else if (index) {
|
|
if ((!dev->is_compaq) && (val == 0x55) && !dev->locked) {
|
|
if (dev->tries) {
|
|
fdc37c93x_state_change(dev, 1);
|
|
dev->tries = 0;
|
|
} else
|
|
dev->tries++;
|
|
} else if (dev->locked) {
|
|
if ((!dev->is_compaq) && (val == 0xaa))
|
|
fdc37c93x_state_change(dev, 0);
|
|
else
|
|
dev->cur_reg = val;
|
|
} else if ((!dev->is_compaq) && dev->tries)
|
|
dev->tries = 0;
|
|
} else if (dev->locked) {
|
|
if (dev->cur_reg < 0x30) {
|
|
valxor = val ^ dev->regs[dev->cur_reg];
|
|
|
|
switch (dev->cur_reg) {
|
|
case 0x02:
|
|
dev->regs[dev->cur_reg] = val;
|
|
if (val == 0x02)
|
|
fdc37c93x_state_change(dev, 0);
|
|
break;
|
|
case 0x03:
|
|
dev->regs[dev->cur_reg] = val & 0x83;
|
|
break;
|
|
case 0x07: case 0x26:
|
|
case 0x2e ... 0x2f:
|
|
dev->regs[dev->cur_reg] = val;
|
|
break;
|
|
case 0x22:
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
dev->regs[dev->cur_reg] = val & 0x7f;
|
|
else
|
|
dev->regs[dev->cur_reg] = val & 0x6f;
|
|
|
|
if (valxor & 0x01)
|
|
fdc37c93x_fdc_handler(dev);
|
|
if (valxor & 0x08)
|
|
fdc37c93x_lpt_handler(dev);
|
|
if (valxor & 0x10)
|
|
fdc37c93x_serial_handler(dev, 0);
|
|
if (valxor & 0x20)
|
|
fdc37c93x_serial_handler(dev, 1);
|
|
if ((dev->chip_id >= FDC37C93X_FR) && (valxor & 0x40))
|
|
fdc37c93x_access_bus_handler(dev);
|
|
break;
|
|
case 0x23:
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
dev->regs[dev->cur_reg] = val & 0x7f;
|
|
else
|
|
dev->regs[dev->cur_reg] = val & 0x6f;
|
|
break;
|
|
case 0x24:
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
dev->regs[dev->cur_reg] = val & 0xcf;
|
|
else
|
|
dev->regs[dev->cur_reg] = val & 0xcc;
|
|
|
|
if ((dev->chip_id >= FDC37C93X_FR) && (valxor & 0x01)) {
|
|
serial_set_clock_src(dev->uart[0], (val & 0x01) ?
|
|
48000000.0 : 24000000.0);
|
|
serial_set_clock_src(dev->uart[1], (val & 0x01) ?
|
|
48000000.0 : 24000000.0);
|
|
}
|
|
break;
|
|
case 0x27:
|
|
if (dev->chip_id >= FDC37C93X_FR) {
|
|
dev->regs[dev->cur_reg] = val;
|
|
|
|
fdc37c93x_superio_handler(dev);
|
|
}
|
|
break;
|
|
case 0x28:
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
dev->regs[dev->cur_reg] = val & 0x1f;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
} else {
|
|
valxor = val ^ dev->ld_regs[dev->regs[7]][dev->cur_reg];
|
|
|
|
if ((dev->regs[7] <= dev->max_ld) && ((dev->regs[7] != 0x08) ||
|
|
(dev->cur_reg < 0xb0) || (dev->cur_reg > 0xdf) ||
|
|
(dev->chip_id >= FDC37C93X_FR))) switch (dev->regs[7]) {
|
|
case 0x00: /* FDD */
|
|
switch (dev->cur_reg) {
|
|
case 0x30:
|
|
case 0x60: case 0x61:
|
|
case 0x70:
|
|
case 0x74:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val;
|
|
|
|
if ((dev->cur_reg == 0x30) && (val & 0x01))
|
|
dev->regs[0x22] |= 0x01;
|
|
if (valxor)
|
|
fdc37c93x_fdc_handler(dev);
|
|
break;
|
|
case 0xf0:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0x1f;
|
|
|
|
if (valxor & 0x01)
|
|
fdc_update_enh_mode(dev->fdc, val & 0x01);
|
|
if (valxor & 0x10)
|
|
fdc_set_swap(dev->fdc, (val & 0x10) >> 4);
|
|
break;
|
|
case 0xf1:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0xfc;
|
|
|
|
if (valxor & 0x0c)
|
|
fdc_update_densel_force(dev->fdc, (val & 0xc) >> 2);
|
|
break;
|
|
case 0xf2:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val;
|
|
|
|
if (valxor & 0xc0)
|
|
fdc_update_rwc(dev->fdc, 3, (val & 0xc0) >> 6);
|
|
if (valxor & 0x30)
|
|
fdc_update_rwc(dev->fdc, 2, (val & 0x30) >> 4);
|
|
if (valxor & 0x0c)
|
|
fdc_update_rwc(dev->fdc, 1, (val & 0x0c) >> 2);
|
|
if (valxor & 0x03)
|
|
fdc_update_rwc(dev->fdc, 0, (val & 0x03));
|
|
break;
|
|
case 0xf4 ... 0xf7:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0x5b;
|
|
|
|
if (valxor & 0x18)
|
|
fdc_update_drvrate(dev->fdc, dev->cur_reg - 0xf4,
|
|
(val & 0x18) >> 3);
|
|
break;
|
|
}
|
|
break;
|
|
case 0x01: /* IDE1 */
|
|
switch (dev->cur_reg) {
|
|
case 0x30:
|
|
case 0x60: case 0x61:
|
|
case 0x62: case 0x63:
|
|
case 0x70:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val;
|
|
|
|
if ((dev->cur_reg == 0x30) && (val & 0x01))
|
|
dev->regs[0x22] |= 0x02;
|
|
break;
|
|
case 0xf0: case 0xf1:
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0x1f;
|
|
else if (dev->cur_reg == 0xf0)
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val;
|
|
break;
|
|
}
|
|
break;
|
|
case 0x02: /* IDE2 */
|
|
switch (dev->cur_reg) {
|
|
case 0x30:
|
|
case 0x60: case 0x61:
|
|
case 0x62: case 0x63:
|
|
case 0x70:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val;
|
|
|
|
if ((dev->cur_reg == 0x30) && (val & 0x01))
|
|
dev->regs[0x22] |= 0x04;
|
|
break;
|
|
case 0xf0:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0x01;
|
|
break;
|
|
}
|
|
break;
|
|
case 0x03: /* Parallel Port */
|
|
switch (dev->cur_reg) {
|
|
case 0x30:
|
|
case 0x60: case 0x61:
|
|
case 0x70:
|
|
case 0x74:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val;
|
|
|
|
if ((dev->cur_reg == 0x30) && (val & 0x01))
|
|
dev->regs[0x22] |= 0x08;
|
|
if (valxor)
|
|
fdc37c93x_lpt_handler(dev);
|
|
break;
|
|
case 0xf0:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val;
|
|
break;
|
|
case 0xf1:
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0x03;
|
|
break;
|
|
}
|
|
break;
|
|
case 0x04: /* Serial port 1 */
|
|
switch (dev->cur_reg) {
|
|
case 0x30:
|
|
case 0x60: case 0x61:
|
|
case 0x70:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val;
|
|
|
|
if ((dev->cur_reg == 0x30) && (val & 0x01))
|
|
dev->regs[0x22] |= 0x10;
|
|
if (valxor)
|
|
fdc37c93x_serial_handler(dev, 0);
|
|
break;
|
|
/* TODO: Bit 0 = MIDI Mode, Bit 1 = High speed. */
|
|
case 0xf0:
|
|
if (dev->chip_id >= FDC37C93X_FR) {
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0x83;
|
|
} else
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0x03;
|
|
|
|
if (valxor & 0x83) {
|
|
fdc37c93x_serial_handler(dev, 0);
|
|
fdc37c93x_serial_handler(dev, 1);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 0x05: /* Serial port 2 */
|
|
switch (dev->cur_reg) {
|
|
case 0x30:
|
|
case 0x60: case 0x61:
|
|
case 0x62: case 0x63:
|
|
case 0x70:
|
|
case 0x74:
|
|
if (((dev->cur_reg != 0x62) && (dev->cur_reg != 0x63)) ||
|
|
(dev->chip_id == FDC37C93X_FR)) {
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val;
|
|
|
|
if ((dev->cur_reg == 0x30) && (val & 0x01))
|
|
dev->regs[0x22] |= 0x20;
|
|
if (valxor)
|
|
fdc37c93x_serial_handler(dev, 1);
|
|
}
|
|
break;
|
|
/* TODO: Bit 0 = MIDI Mode, Bit 1 = High speed. */
|
|
case 0xf0:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0x03;
|
|
|
|
if (valxor & 0x03) {
|
|
fdc37c93x_serial_handler(dev, 0);
|
|
fdc37c93x_serial_handler(dev, 1);
|
|
}
|
|
break;
|
|
case 0xf1:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0x7f;
|
|
break;
|
|
case 0xf2:
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val;
|
|
break;
|
|
}
|
|
break;
|
|
case 0x06: /* RTC */
|
|
switch (dev->cur_reg) {
|
|
case 0x30:
|
|
// case 0x60: case 0x61:
|
|
case 0x62: case 0x63:
|
|
case 0x70:
|
|
if (((dev->cur_reg != 0x62) && (dev->cur_reg != 0x63)) ||
|
|
(dev->chip_id >= FDC37C93X_FR)) {
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val;
|
|
|
|
if (valxor) {
|
|
fdc37c93x_nvr_pri_handler(dev);
|
|
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
fdc37c93x_nvr_sec_handler(dev);
|
|
}
|
|
}
|
|
break;
|
|
case 0xf0:
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val;
|
|
else
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0x8f;
|
|
|
|
if (valxor) {
|
|
nvr_lock_set(0x80, 0x20, !!(dev->ld_regs[6][dev->cur_reg] & 0x01), dev->nvr);
|
|
nvr_lock_set(0xa0, 0x20, !!(dev->ld_regs[6][dev->cur_reg] & 0x02), dev->nvr);
|
|
nvr_lock_set(0xc0, 0x20, !!(dev->ld_regs[6][dev->cur_reg] & 0x04), dev->nvr);
|
|
nvr_lock_set(0xe0, 0x20, !!(dev->ld_regs[6][dev->cur_reg] & 0x08), dev->nvr);
|
|
if (dev->ld_regs[6][dev->cur_reg] & 0x80) {
|
|
if (dev->chip_id == FDC37C93X_NORMAL)
|
|
nvr_bank_set(0, 1, dev->nvr);
|
|
else switch ((dev->ld_regs[6][dev->cur_reg] >> 4) & 0x07) {
|
|
case 0x00:
|
|
default:
|
|
nvr_bank_set(0, 0xff, dev->nvr);
|
|
nvr_bank_set(1, 1, dev->nvr);
|
|
break;
|
|
case 0x01:
|
|
nvr_bank_set(0, 0, dev->nvr);
|
|
nvr_bank_set(1, 1, dev->nvr);
|
|
break;
|
|
case 0x02: case 0x04:
|
|
nvr_bank_set(0, 0xff, dev->nvr);
|
|
nvr_bank_set(1, 0xff, dev->nvr);
|
|
break;
|
|
case 0x03: case 0x05:
|
|
nvr_bank_set(0, 0, dev->nvr);
|
|
nvr_bank_set(1, 0xff, dev->nvr);
|
|
break;
|
|
case 0x06:
|
|
nvr_bank_set(0, 0xff, dev->nvr);
|
|
nvr_bank_set(1, 2, dev->nvr);
|
|
break;
|
|
case 0x07:
|
|
nvr_bank_set(0, 0, dev->nvr);
|
|
nvr_bank_set(1, 2, dev->nvr);
|
|
break;
|
|
}
|
|
} else {
|
|
nvr_bank_set(0, 0, dev->nvr);
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
nvr_bank_set(1, 0xff, dev->nvr);
|
|
}
|
|
|
|
fdc37c93x_nvr_pri_handler(dev);
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
fdc37c93x_nvr_sec_handler(dev);
|
|
}
|
|
break;
|
|
case 0xf1:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0x1f;
|
|
break;
|
|
case 0xf2: case 0xf3:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val;
|
|
break;
|
|
case 0xf4:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0x83;
|
|
break;
|
|
}
|
|
break;
|
|
case 0x07: /* Keyboard */
|
|
switch (dev->cur_reg) {
|
|
case 0x30:
|
|
case 0x70: case 0x71:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val;
|
|
|
|
if (valxor)
|
|
fdc37c93x_kbc_handler(dev);
|
|
break;
|
|
case 0xf0:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0x87;
|
|
break;
|
|
}
|
|
break;
|
|
case 0x08: /* Aux. I/O */
|
|
switch (dev->cur_reg) {
|
|
case 0x30:
|
|
case 0x60: case 0x61:
|
|
case 0x62: case 0x63:
|
|
case 0x70:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val;
|
|
|
|
if (valxor)
|
|
fdc37c93x_auxio_handler(dev);
|
|
break;
|
|
case 0xb0: case 0xb2:
|
|
case 0xb4: case 0xb6:
|
|
case 0xe0: case 0xe1:
|
|
case 0xe9: case 0xf2:
|
|
case 0xf3:
|
|
case 0xc0 ... 0xc9:
|
|
case 0xcb ... 0xcc:
|
|
case 0xd0 ... 0xdf:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val;
|
|
break;
|
|
case 0xb1:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0xd3;
|
|
break;
|
|
case 0xb3:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0x17;
|
|
break;
|
|
case 0xb5:
|
|
if (dev->chip_id == FDC37C93X_APM)
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val;
|
|
else
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0xbf;
|
|
break;
|
|
case 0xb7:
|
|
if (dev->chip_id == FDC37C93X_APM)
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0x7f;
|
|
else
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0x3f;
|
|
break;
|
|
case 0xb8:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0x3f;
|
|
break;
|
|
case 0x18:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0x18;
|
|
break;
|
|
case 0xe2 ... 0xe5:
|
|
case 0xe7:
|
|
case 0xeb ... 0xed:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0x0f;
|
|
break;
|
|
case 0xe6: case 0xe8:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0x1f;
|
|
break;
|
|
case 0xea:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0x9f;
|
|
break;
|
|
case 0xef:
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0xf8;
|
|
break;
|
|
case 0xf1:
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0x83;
|
|
else
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0x03;
|
|
break;
|
|
case 0xf4:
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0xef;
|
|
else
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val & 0x0f;
|
|
break;
|
|
case 0xf6:
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
for (uint8_t i = 0; i < 8; i++)
|
|
fdc37c93x_write_gp(dev, 1, i, val & (1 << i));
|
|
break;
|
|
case 0xf7:
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
for (uint8_t i = 0; i < 8; i++)
|
|
fdc37c93x_write_gp(dev, 2, i, val & (1 << i));
|
|
break;
|
|
case 0xf8:
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
for (uint8_t i = 0; i < 8; i++)
|
|
fdc37c93x_write_gp(dev, 4, i, val & (1 << i));
|
|
break;
|
|
case 0xf9:
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
for (uint8_t i = 0; i < 8; i++)
|
|
fdc37c93x_write_gp(dev, 5, i, val & (1 << i));
|
|
break;
|
|
case 0xfa:
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
for (uint8_t i = 0; i < 8; i++)
|
|
fdc37c93x_write_gp(dev, 6, i, val & (1 << i));
|
|
break;
|
|
case 0xfb:
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
for (uint8_t i = 0; i < 8; i++)
|
|
fdc37c93x_write_gp(dev, 7, i, val & (1 << i));
|
|
break;
|
|
}
|
|
break;
|
|
case 0x09: /* Access.Bus */
|
|
switch (dev->cur_reg) {
|
|
case 0x30:
|
|
case 0x60: case 0x61:
|
|
case 0x70:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val;
|
|
|
|
if ((dev->cur_reg == 0x30) && (val & 0x01))
|
|
dev->regs[0x22] |= 0x40;
|
|
if (valxor)
|
|
fdc37c93x_access_bus_handler(dev);
|
|
break;
|
|
}
|
|
break;
|
|
case 0x0a: /* ACPI */
|
|
switch (dev->cur_reg) {
|
|
case 0x30:
|
|
case 0x60: case 0x61:
|
|
case 0x62: case 0x63:
|
|
case 0x70:
|
|
dev->ld_regs[dev->regs[7]][dev->cur_reg] = val;
|
|
|
|
if (valxor)
|
|
fdc37c93x_acpi_handler(dev);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static uint8_t
|
|
fdc37c93x_read(uint16_t port, void *priv)
|
|
{
|
|
fdc37c93x_t *dev = (fdc37c93x_t *) priv;
|
|
uint8_t index = (port & 1) ? 0 : 1;
|
|
uint8_t ret = 0xff;
|
|
|
|
/* Compaq Presario 4500: Unlock at FB, Register at EA, Data at EB, Lock at F9. */
|
|
if ((port == 0xea) || (port == 0xf9) || (port == 0xfb))
|
|
index = 1;
|
|
else if (port == 0xeb)
|
|
index = 0;
|
|
|
|
if (dev->locked) {
|
|
if (index)
|
|
ret = dev->cur_reg;
|
|
else {
|
|
if (dev->cur_reg < 0x30) {
|
|
if (dev->cur_reg == 0x20)
|
|
ret = dev->chip_id;
|
|
else
|
|
ret = dev->regs[dev->cur_reg];
|
|
} else if (dev->regs[7] <= dev->max_ld) {
|
|
if ((dev->regs[7] == 0x00) && (dev->cur_reg == 0xf2))
|
|
ret = (fdc_get_rwc(dev->fdc, 0) | (fdc_get_rwc(dev->fdc, 1) << 2) |
|
|
(fdc_get_rwc(dev->fdc, 2) << 4) | (fdc_get_rwc(dev->fdc, 3) << 6));
|
|
else if ((dev->regs[7] != 0x06) || (dev->cur_reg != 0xf3))
|
|
ret = dev->ld_regs[dev->regs[7]][dev->cur_reg];
|
|
else if ((dev->regs[7] == 0x08) && (dev->cur_reg >= 0xf6) &&
|
|
(dev->cur_reg <= 0xfb) &&
|
|
(dev->chip_id >= FDC37C93X_FR)) switch (dev->cur_reg) {
|
|
case 0xf6:
|
|
if (dev->chip_id >= FDC37C93X_FR) {
|
|
ret = 0x00;
|
|
for (uint8_t i = 0; i < 8; i++)
|
|
ret |= fdc37c93x_read_gp(dev, 1, i);
|
|
}
|
|
break;
|
|
case 0xf7:
|
|
if (dev->chip_id >= FDC37C93X_FR) {
|
|
ret = 0x00;
|
|
for (uint8_t i = 0; i < 8; i++)
|
|
ret |= fdc37c93x_read_gp(dev, 2, i);
|
|
}
|
|
break;
|
|
case 0xf8:
|
|
if (dev->chip_id >= FDC37C93X_FR) {
|
|
ret = 0x00;
|
|
for (uint8_t i = 0; i < 8; i++)
|
|
ret |= fdc37c93x_read_gp(dev, 4, i);
|
|
}
|
|
break;
|
|
case 0xf9:
|
|
if (dev->chip_id >= FDC37C93X_FR) {
|
|
ret = 0x00;
|
|
for (uint8_t i = 0; i < 8; i++)
|
|
ret |= fdc37c93x_read_gp(dev, 5, i);
|
|
}
|
|
break;
|
|
case 0xfa:
|
|
if (dev->chip_id >= FDC37C93X_FR) {
|
|
ret = 0x00;
|
|
for (uint8_t i = 0; i < 8; i++)
|
|
ret |= fdc37c93x_read_gp(dev, 6, i);
|
|
}
|
|
break;
|
|
case 0xfb:
|
|
if (dev->chip_id >= FDC37C93X_FR) {
|
|
ret = 0x00;
|
|
for (uint8_t i = 0; i < 8; i++)
|
|
ret |= fdc37c93x_read_gp(dev, 7, i);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
fdc37c93x_reset(fdc37c93x_t *dev)
|
|
{
|
|
memset(dev->regs, 0x00, sizeof(dev->regs));
|
|
|
|
dev->regs[0x03] = 0x03;
|
|
dev->regs[0x20] = dev->chip_id;
|
|
dev->regs[0x21] = 0x01;
|
|
dev->regs[0x22] = 0x39;
|
|
dev->regs[0x24] = 0x04;
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
dev->regs[0x26] = dev->port_370 ? 0x70 : 0xf0;
|
|
dev->regs[0x27] = 0x03;
|
|
|
|
for (uint8_t i = 0; i <= 0x0a; i++)
|
|
memset(dev->ld_regs[i], 0x00, 256);
|
|
|
|
/* Logical device 0: FDD */
|
|
dev->ld_regs[0x00][0x30] = 0x00;
|
|
dev->ld_regs[0x00][0x60] = 0x03;
|
|
dev->ld_regs[0x00][0x61] = 0xf0;
|
|
dev->ld_regs[0x00][0x70] = 0x06;
|
|
dev->ld_regs[0x00][0x74] = 0x02;
|
|
dev->ld_regs[0x00][0xf0] = 0x0e;
|
|
dev->ld_regs[0x00][0xf2] = 0xff;
|
|
|
|
/* Logical device 1: IDE1 */
|
|
dev->ld_regs[0x01][0x30] = 0x00;
|
|
dev->ld_regs[0x01][0x60] = 0x01;
|
|
dev->ld_regs[0x01][0x61] = 0xf0;
|
|
dev->ld_regs[0x01][0x62] = 0x03;
|
|
dev->ld_regs[0x01][0x63] = 0xf6;
|
|
dev->ld_regs[0x01][0x70] = 0x0e;
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
dev->ld_regs[0x01][0xf0] = 0x0c;
|
|
|
|
/* Logical device 2: IDE2 */
|
|
dev->ld_regs[0x02][0x30] = 0x00;
|
|
dev->ld_regs[0x02][0x60] = 0x01;
|
|
dev->ld_regs[0x02][0x61] = 0x70;
|
|
dev->ld_regs[0x02][0x62] = 0x03;
|
|
dev->ld_regs[0x02][0x63] = 0x76;
|
|
dev->ld_regs[0x02][0x70] = 0x0f;
|
|
|
|
/* Logical device 3: Parallel Port */
|
|
dev->ld_regs[0x03][0x30] = 0x00;
|
|
dev->ld_regs[0x03][0x60] = 0x03;
|
|
dev->ld_regs[0x03][0x61] = 0x78;
|
|
dev->ld_regs[0x03][0x70] = 0x07;
|
|
dev->ld_regs[0x03][0x74] = 0x04;
|
|
dev->ld_regs[0x03][0xf0] = 0x3c;
|
|
|
|
/* Logical device 4: Serial Port 1 */
|
|
dev->ld_regs[0x04][0x30] = 0x00;
|
|
dev->ld_regs[0x04][0x60] = 0x03;
|
|
dev->ld_regs[0x04][0x61] = 0xf8;
|
|
dev->ld_regs[0x04][0x70] = 0x04;
|
|
dev->ld_regs[0x04][0xf0] = 0x03;
|
|
serial_irq(dev->uart[0], dev->ld_regs[4][0x70]);
|
|
|
|
/* Logical device 5: Serial Port 2 */
|
|
dev->ld_regs[0x05][0x30] = 0x00;
|
|
dev->ld_regs[0x05][0x60] = 0x02;
|
|
dev->ld_regs[0x05][0x61] = 0xf8;
|
|
dev->ld_regs[0x05][0x70] = 0x03;
|
|
dev->ld_regs[0x05][0x74] = 0x04;
|
|
dev->ld_regs[0x05][0xf1] = 0x02;
|
|
if (dev->chip_id >= FDC37C93X_FR)
|
|
dev->ld_regs[0x05][0xf2] = 0x03;
|
|
serial_irq(dev->uart[1], dev->ld_regs[5][0x70]);
|
|
|
|
/* Logical device 6: RTC */
|
|
dev->ld_regs[0x06][0x30] = 0x00;
|
|
dev->ld_regs[0x06][0x63] = (dev->has_nvr) ? 0x70 : 0x00;
|
|
dev->ld_regs[0x06][0xf0] = 0x00;
|
|
dev->ld_regs[0x06][0xf4] = 0x03;
|
|
|
|
/* Logical device 7: Keyboard */
|
|
dev->ld_regs[0x07][0x30] = 0x00;
|
|
dev->ld_regs[0x07][0x61] = 0x60;
|
|
dev->ld_regs[0x07][0x70] = 0x01;
|
|
|
|
/* Logical device 8: Auxiliary I/O */
|
|
dev->ld_regs[0x08][0x30] = 0x00;
|
|
dev->ld_regs[0x08][0x60] = 0x00;
|
|
dev->ld_regs[0x08][0x61] = 0x00;
|
|
if (dev->chip_id >= FDC37C93X_FR) {
|
|
dev->ld_regs[0x08][0xb1] = 0x80;
|
|
dev->ld_regs[0x08][0xc0] = 0x01;
|
|
dev->ld_regs[0x08][0xc1] = 0x01;
|
|
dev->ld_regs[0x08][0xc5] = 0x01;
|
|
dev->ld_regs[0x08][0xc6] = 0x01;
|
|
dev->ld_regs[0x08][0xc7] = 0x01;
|
|
dev->ld_regs[0x08][0xc8] = 0x01;
|
|
dev->ld_regs[0x08][0xc9] = 0x80;
|
|
dev->ld_regs[0x08][0xcb] = 0x01;
|
|
dev->ld_regs[0x08][0xcc] = 0x01;
|
|
memset(&(dev->ld_regs[0x08][0xd0]), 0x01, 16);
|
|
}
|
|
memset(&(dev->ld_regs[0x08][0xe0]), 0x01, 14);
|
|
|
|
/* Logical device 9: ACCESS.bus */
|
|
if (dev->chip_id >= FDC37C93X_FR) {
|
|
dev->ld_regs[0x09][0x30] = 0x00;
|
|
dev->ld_regs[0x09][0x60] = 0x00;
|
|
dev->ld_regs[0x09][0x61] = 0x00;
|
|
}
|
|
|
|
/* Logical device A: ACPI */
|
|
if (dev->chip_id == FDC37C93X_APM) {
|
|
dev->ld_regs[0x0a][0x30] = 0x00;
|
|
dev->ld_regs[0x0a][0x60] = 0x00;
|
|
dev->ld_regs[0x0a][0x61] = 0x00;
|
|
}
|
|
|
|
fdc37c93x_gpio_handler(dev);
|
|
fdc37c93x_lpt_handler(dev);
|
|
fdc37c93x_serial_handler(dev, 0);
|
|
fdc37c93x_serial_handler(dev, 1);
|
|
fdc37c93x_auxio_handler(dev);
|
|
if (dev->is_apm || (dev->chip_id == 0x03))
|
|
fdc37c93x_access_bus_handler(dev);
|
|
if (dev->is_apm)
|
|
fdc37c93x_acpi_handler(dev);
|
|
|
|
fdc_reset(dev->fdc);
|
|
fdc37c93x_fdc_handler(dev);
|
|
|
|
if (dev->has_nvr) {
|
|
fdc37c93x_nvr_pri_handler(dev);
|
|
fdc37c93x_nvr_sec_handler(dev);
|
|
nvr_bank_set(0, 0, dev->nvr);
|
|
nvr_bank_set(1, 0xff, dev->nvr);
|
|
|
|
nvr_lock_set(0x80, 0x20, 0, dev->nvr);
|
|
nvr_lock_set(0xa0, 0x20, 0, dev->nvr);
|
|
nvr_lock_set(0xc0, 0x20, 0, dev->nvr);
|
|
nvr_lock_set(0xe0, 0x20, 0, dev->nvr);
|
|
}
|
|
|
|
fdc37c93x_kbc_handler(dev);
|
|
|
|
if (dev->chip_id != 0x02)
|
|
fdc37c93x_superio_handler(dev);
|
|
|
|
if (dev->chip_id >= FDC37C93X_FR) {
|
|
serial_set_clock_src(dev->uart[0], 24000000.0);
|
|
serial_set_clock_src(dev->uart[1], 24000000.0);
|
|
}
|
|
|
|
memset(dev->gpio_regs, 0xff, 256);
|
|
memset(dev->gpio_pulldn, 0xff, 8);
|
|
|
|
/* Acer V62X requires bit 0 to be clear to not be stuck in "clear password" mode. */
|
|
if (!strcmp(machine_get_internal_name(), "vectra54")) {
|
|
dev->gpio_pulldn[1] = 0x40;
|
|
|
|
/*
|
|
HP Vectra VL/5 Series 4 GPIO
|
|
(TODO: Find how multipliers > 3.0 are defined):
|
|
|
|
Bit 6: 1 = can boot, 0 = no;
|
|
Bit 7, 1 = multiplier (00 = 2.5, 01 = 2.0,
|
|
10 = 3.0, 11 = 1.5);
|
|
Bit 5, 4 = bus speed (00 = 50 MHz, 01 = 66 MHz,
|
|
10 = 60 MHz, 11 = ????):
|
|
Bit 7, 5, 4, 1: 0000 = 125 MHz, 0010 = 166 MHz,
|
|
0100 = 150 MHz, 0110 = ??? MHz;
|
|
0001 = 100 MHz, 0011 = 133 MHz,
|
|
0101 = 120 MHz, 0111 = ??? MHz;
|
|
1000 = 150 MHz, 1010 = 200 MHz,
|
|
1100 = 180 MHz, 1110 = ??? MHz;
|
|
1001 = 75 MHz, 1011 = 100 MHz,
|
|
1101 = 90 MHz, 1111 = ??? MHz
|
|
*/
|
|
if (cpu_busspeed <= 40000000)
|
|
dev->gpio_pulldn[1] |= 0x30;
|
|
else if ((cpu_busspeed > 40000000) && (cpu_busspeed <= 50000000))
|
|
dev->gpio_pulldn[1] |= 0x00;
|
|
else if ((cpu_busspeed > 50000000) && (cpu_busspeed <= 60000000))
|
|
dev->gpio_pulldn[1] |= 0x20;
|
|
else if (cpu_busspeed > 60000000)
|
|
dev->gpio_pulldn[1] |= 0x10;
|
|
|
|
if (cpu_dmulti <= 1.5)
|
|
dev->gpio_pulldn[1] |= 0x82;
|
|
else if ((cpu_dmulti > 1.5) && (cpu_dmulti <= 2.0))
|
|
dev->gpio_pulldn[1] |= 0x02;
|
|
else if ((cpu_dmulti > 2.0) && (cpu_dmulti <= 2.5))
|
|
dev->gpio_pulldn[1] |= 0x00;
|
|
else if (cpu_dmulti > 2.5)
|
|
dev->gpio_pulldn[1] |= 0x80;
|
|
} else if (!strcmp(machine_get_internal_name(), "acerv62x"))
|
|
dev->gpio_pulldn[1] = 0xfe;
|
|
else
|
|
dev->gpio_pulldn[1] = (dev->chip_id == 0x30) ? 0xff : 0xfd;
|
|
|
|
if (strstr(machine_get_internal_name(), "acer") != NULL)
|
|
/* Bit 2 on the Acer V35N is the text/graphics toggle, bits 1 and 3 = ????. */
|
|
dev->gpio_pulldn[2] = 0x10;
|
|
|
|
dev->locked = 0;
|
|
}
|
|
|
|
static void
|
|
fdc37c93x_close(void *priv)
|
|
{
|
|
fdc37c93x_t *dev = (fdc37c93x_t *) priv;
|
|
|
|
free(dev);
|
|
}
|
|
|
|
static void *
|
|
fdc37c93x_init(const device_t *info)
|
|
{
|
|
fdc37c93x_t *dev = (fdc37c93x_t *) calloc(1, sizeof(fdc37c93x_t));
|
|
|
|
dev->fdc = device_add(&fdc_at_smc_device);
|
|
|
|
dev->uart[0] = device_add_inst(&ns16550_device, 1);
|
|
dev->uart[1] = device_add_inst(&ns16550_device, 2);
|
|
|
|
dev->chip_id = info->local & FDC37C93X_CHIP_ID;
|
|
dev->kbc_type = info->local & FDC37C93X_KBC;
|
|
|
|
dev->is_apm = (dev->chip_id == FDC37C93X_APM);
|
|
dev->is_compaq = (dev->kbc_type == FDC37C931);
|
|
|
|
dev->has_nvr = !(info->local & FDC37C93X_NO_NVR);
|
|
dev->port_370 = !!(info->local & FDC37C93X_370);
|
|
|
|
if (dev->has_nvr) {
|
|
dev->nvr = device_add(&amstrad_megapc_nvr_device);
|
|
|
|
nvr_bank_set(0, 0, dev->nvr);
|
|
nvr_bank_set(1, 0xff, dev->nvr);
|
|
}
|
|
|
|
dev->max_ld = 8;
|
|
|
|
if (dev->chip_id >= FDC37C93X_FR) {
|
|
dev->access_bus = device_add(&access_bus_device);
|
|
dev->max_ld++;
|
|
}
|
|
|
|
if (dev->chip_id == FDC37C93X_APM) {
|
|
dev->acpi = device_add(&acpi_smc_device);
|
|
dev->max_ld++;
|
|
}
|
|
|
|
if (dev->is_compaq) {
|
|
io_sethandler(0x0f9, 0x0001,
|
|
fdc37c93x_read, NULL, NULL, fdc37c93x_write, NULL, NULL, dev);
|
|
io_sethandler(0x0fb, 0x0001,
|
|
fdc37c93x_read, NULL, NULL, fdc37c93x_write, NULL, NULL, dev);
|
|
}
|
|
|
|
switch (dev->kbc_type) {
|
|
case FDC37C931:
|
|
dev->kbc = device_add(&keyboard_ps2_compaq_device);
|
|
break;
|
|
case FDC37C932:
|
|
dev->kbc = device_add(&keyboard_ps2_intel_ami_pci_device);
|
|
break;
|
|
case FDC37C933:
|
|
default:
|
|
dev->kbc = device_add(&keyboard_ps2_pci_device);
|
|
break;
|
|
case FDC37C935:
|
|
dev->kbc = device_add(&keyboard_ps2_phoenix_device);
|
|
break;
|
|
case FDC37C937:
|
|
dev->kbc = device_add(&keyboard_ps2_phoenix_pci_device);
|
|
break;
|
|
}
|
|
|
|
/* Set the defaults here so the ports can be removed by fdc37c93x_reset(). */
|
|
dev->fdc_base = 0x03f0;
|
|
dev->lpt_base = 0x0378;
|
|
dev->uart_base[0] = 0x03f8;
|
|
dev->uart_base[1] = 0x02f8;
|
|
dev->nvr_pri_base = 0x0070;
|
|
dev->nvr_sec_base = 0x0070;
|
|
dev->kbc_base = 0x0060;
|
|
dev->gpio_base = 0x00ea;
|
|
|
|
fdc37c93x_reset(dev);
|
|
|
|
if (dev->chip_id == 0x02) {
|
|
io_sethandler(0x03f0, 0x0002,
|
|
fdc37c93x_read, NULL, NULL, fdc37c93x_write, NULL, NULL, dev);
|
|
io_sethandler(0x0370, 0x0002,
|
|
fdc37c93x_read, NULL, NULL, fdc37c93x_write, NULL, NULL, dev);
|
|
}
|
|
|
|
return dev;
|
|
}
|
|
|
|
const device_t fdc37c93x_device = {
|
|
.name = "SMC FDC37C93x Super I/O",
|
|
.internal_name = "fdc37c93x",
|
|
.flags = 0,
|
|
.local = 0,
|
|
.init = fdc37c93x_init,
|
|
.close = fdc37c93x_close,
|
|
.reset = NULL,
|
|
.available = NULL,
|
|
.speed_changed = NULL,
|
|
.force_redraw = NULL,
|
|
.config = NULL
|
|
};
|