#include "sim-main.h"
#include "hw-main.h"
static void deliver_tx3904tmr_tick (struct hw *me, void *data);
enum
{
TCR_REG = 0,
TISR_REG = 1,
CPRA_REG = 2,
CPRB_REG = 3,
ITMR_REG = 4,
CCDR_REG = 8,
PMGR_REG = 12,
WTMR_REG = 16,
TRR_REG = 60
};
enum
{
RESET_PORT,
INT_PORT,
FF_PORT
};
static const struct hw_port_descriptor tx3904tmr_ports[] =
{
{ "int", INT_PORT, 0, output_port, },
{ "ff", FF_PORT, 0, output_port, },
{ "reset", RESET_PORT, 0, input_port, },
{ NULL, },
};
struct tx3904tmr {
address_word base_address;
unsigned_4 clock_ticks, ext_ticks;
signed_8 last_ticks;
signed_8 roundoff_ticks;
int ff;
struct hw_event* event;
unsigned_4 tcr;
#define GET_TCR_TCE(c) (((c)->tcr & 0x80) >> 7)
#define GET_TCR_CCDE(c) (((c)->tcr & 0x40) >> 6)
#define GET_TCR_CRE(c) (((c)->tcr & 0x20) >> 5)
#define GET_TCR_CCS(c) (((c)->tcr & 0x04) >> 2)
#define GET_TCR_TMODE(c) (((c)->tcr & 0x03) >> 0)
unsigned_4 tisr;
#define SET_TISR_TWIS(c) ((c)->tisr |= 0x08)
#define SET_TISR_TPIBS(c) ((c)->tisr |= 0x04)
#define SET_TISR_TPIAS(c) ((c)->tisr |= 0x02)
#define SET_TISR_TIIS(c) ((c)->tisr |= 0x01)
unsigned_4 cpra;
unsigned_4 cprb;
unsigned_4 itmr;
#define GET_ITMR_TIIE(c) (((c)->itmr & 0x8000) >> 15)
#define SET_ITMR_TIIE(c,v) BLIT32((c)->itmr, 15, (v) ? 1 : 0)
#define GET_ITMR_TZCE(c) (((c)->itmr & 0x0001) >> 0)
#define SET_ITMR_TZCE(c,v) BLIT32((c)->itmr, 0, (v) ? 1 : 0)
unsigned_4 ccdr;
#define GET_CCDR_CDR(c) (((c)->ccdr & 0x07) >> 0)
unsigned_4 pmgr;
#define GET_PMGR_TPIBE(c) (((c)->pmgr & 0x8000) >> 15)
#define SET_PMGR_TPIBE(c,v) BLIT32((c)->pmgr, 15, (v) ? 1 : 0)
#define GET_PMGR_TPIAE(c) (((c)->pmgr & 0x4000) >> 14)
#define SET_PMGR_TPIAE(c,v) BLIT32((c)->pmgr, 14, (v) ? 1 : 0)
#define GET_PMGR_FFI(c) (((c)->pmgr & 0x0001) >> 0)
#define SET_PMGR_FFI(c,v) BLIT32((c)->pmgr, 0, (v) ? 1 : 0)
unsigned_4 wtmr;
#define GET_WTMR_TWIE(c) (((c)->wtmr & 0x8000) >> 15)
#define SET_WTMR_TWIE(c,v) BLIT32((c)->wtmr, 15, (v) ? 1 : 0)
#define GET_WTMR_WDIS(c) (((c)->wtmr & 0x0080) >> 7)
#define SET_WTMR_WDIS(c,v) BLIT32((c)->wtmr, 7, (v) ? 1 : 0)
#define GET_WTMR_TWC(c) (((c)->wtmr & 0x0001) >> 0)
#define SET_WTMR_TWC(c,v) BLIT32((c)->wtmr, 0, (v) ? 1 : 0)
unsigned_4 trr;
};
static hw_io_read_buffer_method tx3904tmr_io_read_buffer;
static hw_io_write_buffer_method tx3904tmr_io_write_buffer;
static hw_port_event_method tx3904tmr_port_event;
static void
attach_tx3904tmr_regs (struct hw *me,
struct tx3904tmr *controller)
{
unsigned_word attach_address;
int attach_space;
unsigned attach_size;
reg_property_spec reg;
if (hw_find_property (me, "reg") == NULL)
hw_abort (me, "Missing \"reg\" property");
if (!hw_find_reg_array_property (me, "reg", 0, ®))
hw_abort (me, "\"reg\" property must contain one addr/size entry");
hw_unit_address_to_attach_address (hw_parent (me),
®.address,
&attach_space,
&attach_address,
me);
hw_unit_size_to_attach_size (hw_parent (me),
®.size,
&attach_size, me);
hw_attach_address (hw_parent (me), 0,
attach_space, attach_address, attach_size,
me);
if(hw_find_property(me, "clock") != NULL)
controller->clock_ticks = (unsigned_4) hw_find_integer_property(me, "clock");
if(hw_find_property(me, "ext") != NULL)
controller->ext_ticks = (unsigned_4) hw_find_integer_property(me, "ext");
controller->base_address = attach_address;
}
static void
tx3904tmr_finish (struct hw *me)
{
struct tx3904tmr *controller;
controller = HW_ZALLOC (me, struct tx3904tmr);
set_hw_data (me, controller);
set_hw_io_read_buffer (me, tx3904tmr_io_read_buffer);
set_hw_io_write_buffer (me, tx3904tmr_io_write_buffer);
set_hw_ports (me, tx3904tmr_ports);
set_hw_port_event (me, tx3904tmr_port_event);
controller->clock_ticks = 1;
controller->ext_ticks = 100;
attach_tx3904tmr_regs (me, controller);
controller->tcr =
controller->itmr =
controller->ccdr =
controller->pmgr =
controller->wtmr =
controller->tisr =
controller->trr = 0;
controller->cpra = controller->cprb = 0x00FFFFFF;
controller->ff = 0;
controller->last_ticks = controller->roundoff_ticks = 0;
controller->event = NULL;
}
static void
tx3904tmr_port_event (struct hw *me,
int my_port,
struct hw *source,
int source_port,
int level)
{
struct tx3904tmr *controller = hw_data (me);
switch (my_port)
{
case RESET_PORT:
{
HW_TRACE ((me, "reset"));
controller->ff = GET_PMGR_FFI(controller);
controller->tcr =
controller->itmr =
controller->ccdr =
controller->pmgr =
controller->wtmr =
controller->tisr =
controller->trr = 0;
controller->cpra = controller->cprb = 0x00FFFFFF;
controller->last_ticks = controller->roundoff_ticks = 0;
if(controller->event != NULL)
hw_event_queue_deschedule(me, controller->event);
controller->event = NULL;
break;
}
default:
hw_abort (me, "Event on unknown port %d", my_port);
break;
}
}
static unsigned
tx3904tmr_io_read_buffer (struct hw *me,
void *dest,
int space,
unsigned_word base,
unsigned nr_bytes)
{
struct tx3904tmr *controller = hw_data (me);
unsigned byte;
HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
for (byte = 0; byte < nr_bytes; byte++)
{
address_word address = base + byte;
int reg_number = (address - controller->base_address) / 4;
int reg_offset = 3 - (address - controller->base_address) % 4;
unsigned_4 register_value;
switch (reg_number)
{
case TCR_REG: register_value = controller->tcr; break;
case TISR_REG: register_value = controller->tisr; break;
case CPRA_REG: register_value = controller->cpra; break;
case CPRB_REG: register_value = controller->cprb; break;
case ITMR_REG: register_value = controller->itmr; break;
case CCDR_REG: register_value = controller->ccdr; break;
case PMGR_REG: register_value = controller->pmgr; break;
case WTMR_REG: register_value = controller->wtmr; break;
case TRR_REG: register_value = controller->trr; break;
default: register_value = 0;
}
memcpy ((char*) dest + byte, ((char*)& register_value)+reg_offset, 1);
}
return nr_bytes;
}
static unsigned
tx3904tmr_io_write_buffer (struct hw *me,
const void *source,
int space,
unsigned_word base,
unsigned nr_bytes)
{
struct tx3904tmr *controller = hw_data (me);
unsigned byte;
HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));
for (byte = 0; byte < nr_bytes; byte++)
{
address_word address = base + byte;
unsigned_1 write_byte = ((const char*) source)[byte];
int reg_number = (address - controller->base_address) / 4;
int reg_offset = 3 - (address - controller->base_address) % 4;
switch (reg_number)
{
case TCR_REG:
if(reg_offset == 0)
{
controller->tcr = (unsigned_4) (write_byte & 0xef);
if(GET_TCR_TCE(controller) == 0 &&
GET_TCR_CRE(controller) == 1)
controller->trr = 0;
}
break;
case ITMR_REG:
if(reg_offset == 1)
{
SET_ITMR_TIIE(controller, write_byte & 0x80);
}
else if(reg_offset == 0)
{
SET_ITMR_TZCE(controller, write_byte & 0x01);
}
break;
case CCDR_REG:
if(reg_offset == 0)
{
controller->ccdr = write_byte & 0x07;
}
break;
case PMGR_REG:
if(reg_offset == 1)
{
SET_PMGR_TPIBE(controller, write_byte & 0x80);
SET_PMGR_TPIAE(controller, write_byte & 0x40);
}
else if(reg_offset == 0)
{
SET_PMGR_FFI(controller, write_byte & 0x01);
}
break;
case WTMR_REG:
if(reg_offset == 1)
{
SET_WTMR_TWIE(controller, write_byte & 0x80);
}
else if(reg_offset == 0)
{
SET_WTMR_WDIS(controller, write_byte & 0x80);
SET_WTMR_TWC(controller, write_byte & 0x01);
}
break;
case TISR_REG:
if(reg_offset == 0)
{
if(controller->tisr != 0)
{
hw_port_event(me, INT_PORT, 0);
}
controller->tisr = 0;
}
break;
case CPRA_REG:
if(reg_offset < 3)
{
MBLIT32(controller->cpra, (reg_offset*8)+7, (reg_offset*8), write_byte);
}
break;
case CPRB_REG:
if(reg_offset < 3)
{
MBLIT32(controller->cprb, (reg_offset*8)+7, (reg_offset*8), write_byte);
}
break;
default:
HW_TRACE ((me, "write to illegal register %d", reg_number));
}
}
hw_event_queue_schedule(me, 1, deliver_tx3904tmr_tick, NULL);
return nr_bytes;
}
static void
deliver_tx3904tmr_tick (struct hw *me,
void *data)
{
struct tx3904tmr *controller = hw_data (me);
SIM_DESC sd = hw_system (me);
signed_8 this_ticks = sim_events_time(sd);
signed_8 warp;
signed_8 divisor;
signed_8 quotient, remainder;
if(controller->last_ticks != 0)
warp = this_ticks - controller->last_ticks + controller->roundoff_ticks;
else
{
controller->last_ticks = this_ticks;
warp = controller->roundoff_ticks;
}
if(controller->event != NULL)
hw_event_queue_deschedule(me, controller->event);
controller->event = NULL;
switch((int) GET_TCR_TMODE(controller))
{
case 0:
if(GET_TCR_TCE(controller) == 0 ||
controller->trr == controller->cpra)
return;
break;
case 1:
if(GET_TCR_TCE(controller) == 0)
return;
break;
case 2:
if(GET_TCR_TCE(controller) == 0 &&
GET_WTMR_WDIS(controller) == 1)
return;
break;
case 3:
return;
}
if(GET_TCR_CCS(controller) == 0)
{
if(GET_TCR_CCDE(controller))
divisor = controller->clock_ticks * (1 << (1 + GET_CCDR_CDR(controller)));
else
divisor = controller->clock_ticks;
}
else
{
divisor = controller->ext_ticks;
}
quotient = warp / divisor;
remainder = warp % divisor;
controller->roundoff_ticks = remainder;
controller->last_ticks = this_ticks;
while(quotient > 0)
{
unsigned_4 next_trr = (controller->trr + 1) % (1 << 24);
quotient --;
switch((int) GET_TCR_TMODE(controller))
{
case 0:
{
if(controller->trr == controller->cpra ||
next_trr == controller->cpra)
{
if(controller->trr == controller->cpra)
next_trr = controller->cpra;
SET_TISR_TIIS(controller);
if(GET_ITMR_TIIE(controller) &&
next_trr != controller->trr)
{
hw_port_event(me, INT_PORT, 1);
}
if(GET_ITMR_TZCE(controller))
{
next_trr = 0;
}
}
}
break;
case 1:
{
if(next_trr == controller->cpra)
{
controller->ff ^= 1;
hw_port_event(me, FF_PORT, controller->ff);
SET_TISR_TPIAS(controller);
if(GET_PMGR_TPIAE(controller))
{
hw_port_event(me, INT_PORT, 1);
}
}
else if(next_trr == controller->cprb)
{
controller->ff ^= 1;
hw_port_event(me, FF_PORT, controller->ff);
SET_TISR_TPIBS(controller);
if(GET_PMGR_TPIBE(controller))
{
hw_port_event(me, INT_PORT, 1);
}
next_trr = 0;
}
}
break;
case 2:
{
if(next_trr == controller->cpra)
{
SET_TISR_TWIS(controller);
if(GET_WTMR_TWIE(controller))
{
hw_port_event(me, INT_PORT, 1);
}
next_trr = 0;
}
}
break;
case 3:
default:
}
controller->trr = next_trr;
}
controller->event = hw_event_queue_schedule(me, divisor*3/4, deliver_tx3904tmr_tick, NULL);
}
const struct hw_descriptor dv_tx3904tmr_descriptor[] = {
{ "tx3904tmr", tx3904tmr_finish, },
{ NULL },
};