197 lines
6.5 KiB
C
197 lines
6.5 KiB
C
#include "ibm.h"
|
|
|
|
#include "fdc.h"
|
|
#include "io.h"
|
|
#include "lpt.h"
|
|
#include "serial.h"
|
|
#include "w83977tf.h"
|
|
|
|
typedef struct winbond_t
|
|
{
|
|
int cur_reg;
|
|
int cur_device;
|
|
int index;
|
|
|
|
uint8_t unlock_key;
|
|
uint8_t key[2];
|
|
int enable_regs;
|
|
|
|
struct
|
|
{
|
|
int enable;
|
|
uint16_t addr;
|
|
int irq;
|
|
int dma;
|
|
} dev[11];
|
|
} winbond_t;
|
|
|
|
static winbond_t winbond_global;
|
|
|
|
#define DEV_FDC 0x0
|
|
#define DEV_LPT 0x1
|
|
#define DEV_COM1 0x2
|
|
#define DEV_COM2 0x3
|
|
#define DEV_KBC 0x5
|
|
#define DEV_GPIO 0x7
|
|
#define DEV_GPIO_II 0x8
|
|
#define DEV_GPIO_III 0x9
|
|
#define DEV_ACPI 0xa
|
|
|
|
#define REG_DEVICE 0x07
|
|
#define REG_DEVICE_ID 0x20
|
|
#define REG_DEVICE_REV 0x21
|
|
#define REG_ENABLE 0x30
|
|
#define REG_ADDRHI 0x60
|
|
#define REG_ADDRLO 0x61
|
|
#define REG_IRQ 0x70
|
|
#define REG_DMA 0x74
|
|
|
|
void winbond_write_reg(winbond_t *winbond, int index, uint8_t val)
|
|
{
|
|
if (index == REG_DEVICE)
|
|
winbond->cur_device = val & 0xf;
|
|
else
|
|
{
|
|
// pclog("Write winbond %02x [%02x] %02x\n", winbond->cur_reg, winbond->cur_device, val);
|
|
switch (index)
|
|
{
|
|
case REG_ENABLE:
|
|
if (winbond->cur_device <= DEV_ACPI)
|
|
winbond->dev[winbond->cur_device].enable = val;
|
|
break;
|
|
case REG_ADDRLO:
|
|
if (winbond->cur_device <= DEV_ACPI)
|
|
winbond->dev[winbond->cur_device].addr = (winbond->dev[winbond->cur_device].addr & 0xff00) | val;
|
|
break;
|
|
case REG_ADDRHI:
|
|
if (winbond->cur_device <= DEV_ACPI)
|
|
winbond->dev[winbond->cur_device].addr = (winbond->dev[winbond->cur_device].addr & 0x00ff) | (val << 8);
|
|
break;
|
|
case REG_IRQ:
|
|
if (winbond->cur_device <= DEV_ACPI)
|
|
winbond->dev[winbond->cur_device].irq = val;
|
|
break;
|
|
case REG_DMA:
|
|
if (winbond->cur_device <= DEV_ACPI)
|
|
winbond->dev[winbond->cur_device].dma = val;
|
|
break;
|
|
}
|
|
|
|
switch (winbond->cur_device)
|
|
{
|
|
case DEV_FDC:
|
|
fdc_remove();
|
|
if (winbond->dev[DEV_FDC].enable & 1)
|
|
fdc_add();
|
|
break;
|
|
case DEV_COM1:
|
|
serial1_remove();
|
|
if (winbond->dev[DEV_COM1].enable & 1)
|
|
serial1_set(winbond->dev[DEV_COM1].addr, winbond->dev[DEV_COM1].irq);
|
|
break;
|
|
case DEV_COM2:
|
|
serial2_remove();
|
|
if (winbond->dev[DEV_COM2].enable & 1)
|
|
serial2_set(winbond->dev[DEV_COM2].addr, winbond->dev[DEV_COM2].irq);
|
|
break;
|
|
case DEV_LPT:
|
|
lpt1_remove();
|
|
lpt2_remove();
|
|
if (winbond->dev[DEV_LPT].enable & 1)
|
|
lpt1_init(winbond->dev[DEV_LPT].addr);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
uint8_t winbond_read_reg(winbond_t *winbond, int index)
|
|
{
|
|
// pclog("Read winbond %02x\n", winbond->cur_reg);
|
|
|
|
switch (index)
|
|
{
|
|
case REG_DEVICE:
|
|
return winbond->cur_device;
|
|
case REG_DEVICE_ID:
|
|
return 0x97;
|
|
case REG_DEVICE_REV:
|
|
return 0x73;
|
|
case REG_ENABLE:
|
|
return winbond->dev[winbond->cur_device].enable;
|
|
case REG_ADDRLO:
|
|
return winbond->dev[winbond->cur_device].addr & 0xff;
|
|
case REG_ADDRHI:
|
|
return winbond->dev[winbond->cur_device].addr >> 8;
|
|
case REG_IRQ:
|
|
return winbond->dev[winbond->cur_device].irq;
|
|
case REG_DMA:
|
|
return winbond->dev[winbond->cur_device].dma;
|
|
}
|
|
|
|
return 0xff;
|
|
}
|
|
|
|
static void winbond_write_3fx(uint16_t port, uint8_t val, void *p)
|
|
{
|
|
winbond_t *winbond = (winbond_t *)p;
|
|
|
|
// pclog("winbond_write: port=%04x val=%02x\n", port, val);
|
|
|
|
switch (port)
|
|
{
|
|
case 0x3f0: /*Enable/index register*/
|
|
if (val == 0xaa)
|
|
{
|
|
fdc_3f1_enable(1);
|
|
winbond->enable_regs = 0;
|
|
}
|
|
else if (!winbond->enable_regs)
|
|
{
|
|
winbond->key[1] = winbond->key[0];
|
|
winbond->key[0] = val;
|
|
winbond->enable_regs = (winbond->key[0] == winbond->unlock_key && winbond->key[1] == winbond->unlock_key);
|
|
/*The FDC conflicts with the 3fx config registers, so disable
|
|
it when enabling config registers*/
|
|
if (winbond->enable_regs)
|
|
fdc_3f1_enable(0);
|
|
}
|
|
else
|
|
winbond->index = val;
|
|
break;
|
|
case 0x3f1: /*Data*/
|
|
if (winbond->enable_regs)
|
|
winbond_write_reg(winbond, winbond->index, val);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static uint8_t winbond_read_3fx(uint16_t port, void *p)
|
|
{
|
|
winbond_t *winbond = (winbond_t *)p;
|
|
uint8_t ret = 0xff;
|
|
|
|
switch (port)
|
|
{
|
|
case 0x3f0: /*Index*/
|
|
if (winbond->enable_regs)
|
|
ret = winbond->index;
|
|
break;
|
|
|
|
case 0x3f1: /*Data*/
|
|
if (winbond->enable_regs)
|
|
ret = winbond_read_reg(winbond, winbond->index);
|
|
break;
|
|
}
|
|
|
|
// pclog("winbond_read: port=%04x val=%02x\n", port, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void w83977tf_init(uint16_t base, uint8_t key)
|
|
{
|
|
memset(&winbond_global, 0, sizeof(winbond_t));
|
|
|
|
winbond_global.unlock_key = key;
|
|
io_sethandler(base, 0x0002, winbond_read_3fx, NULL, NULL, winbond_write_3fx, NULL, NULL, &winbond_global);
|
|
}
|