binutils-gdb/sim/mn10300/dv-mn103tim.c

824 lines
19 KiB
C

/* This file is part of the program GDB, the GNU debugger.
Copyright (C) 1998 Free Software Foundation, Inc.
Contributed by Cygnus Solutions.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "sim-main.h"
#include "hw-main.h"
/* DEVICE
mn103tim - mn103002 timers (8 and 16 bit)
DESCRIPTION
Implements the mn103002 8 and 16 bit timers as described in the mn103002 user guide.
PROPERTIES
reg = <8bit-timers-addr> <8bit-timers-size> <16bit-timers-addr> <16bit-timers-size>
BUGS
*/
/* The timers' register address blocks */
struct mn103tim_block {
unsigned_word base;
unsigned_word bound;
};
enum { TIMER8_BLOCK, TIMER16_BLOCK, NR_TIMER_BLOCKS };
enum timer_register_types {
FIRST_MODE_REG = 0,
TM0MD = FIRST_MODE_REG,
TM1MD,
TM2MD,
TM3MD,
TM4MD,
TM5MD,
TM6MD,
LAST_MODE_REG = TM6MD,
FIRST_BASE_REG,
TM0BR = FIRST_BASE_REG,
TM1BR,
TM2BR,
TM3BR,
TM4BR,
TM5BR,
LAST_BASE_REG = TM5BR,
FIRST_COUNTER,
TM0BC = FIRST_COUNTER,
TM1BC,
TM2BC,
TM3BC,
TM4BC,
TM5BC,
TM6BC,
LAST_COUNTER = TM6BC,
TM6MDA,
TM6MDB,
TM6CA,
TM6CB,
};
/* Don't include timer 6 because it's handled specially. */
#define NR_8BIT_TIMERS 4
#define NR_16BIT_TIMERS 2
#define NR_TIMERS 6
typedef struct _mn10300_timer {
unsigned32 div_ratio, start, base;
unsigned8 mode;
struct hw_event *event;
} mn10300_timer;
struct mn103tim {
struct mn103tim_block block[NR_TIMER_BLOCKS];
mn10300_timer timer[NR_TIMERS];
/* treat timer 6 registers specially. */
unsigned16 tm6md, tm6bc, tm6mca, tm6mcb;
unsigned8 tm6mda, tm6mdb; /* compare/capture mode regs for timer 6 */
struct hw_event *event6;
};
/* output port ID's */
/* for mn103002 */
enum {
TIMER0_UFLOW,
TIMER1_UFLOW,
TIMER2_UFLOW,
TIMER3_UFLOW,
TIMER4_UFLOW,
TIMER5_UFLOW,
TIMER6_UFLOW,
TIMER6_CMPA,
TIMER6_CMPB,
};
static const struct hw_port_descriptor mn103tim_ports[] = {
{ "timer-0-underflow", TIMER0_UFLOW, 0, output_port, },
{ "timer-1-underflow", TIMER1_UFLOW, 0, output_port, },
{ "timer-2-underflow", TIMER2_UFLOW, 0, output_port, },
{ "timer-3-underflow", TIMER3_UFLOW, 0, output_port, },
{ "timer-4-underflow", TIMER4_UFLOW, 0, output_port, },
{ "timer-5-underflow", TIMER5_UFLOW, 0, output_port, },
{ "timer-6-underflow", TIMER6_UFLOW, 0, output_port, },
{ "timer-6-compare-a", TIMER6_CMPA, 0, output_port, },
{ "timer-6-compare-b", TIMER6_CMPB, 0, output_port, },
{ NULL, },
};
#define bits2to5_mask 0x3c
#define load_mask 0x40
#define count_mask 0x80
#define count_and_load_mask (load_mask | count_mask)
#define clock_mask 0x03
#define clk_ioclk 0x00
#define clk_cascaded 0x03
/* Finish off the partially created hw device. Attach our local
callbacks. Wire up our port names etc */
static hw_io_read_buffer_method mn103tim_io_read_buffer;
static hw_io_write_buffer_method mn103tim_io_write_buffer;
static void
attach_mn103tim_regs (struct hw *me,
struct mn103tim *timers)
{
int i;
if (hw_find_property (me, "reg") == NULL)
hw_abort (me, "Missing \"reg\" property");
for (i = 0; i < NR_TIMER_BLOCKS; i++)
{
unsigned_word attach_address;
int attach_space;
unsigned attach_size;
reg_property_spec reg;
if (!hw_find_reg_array_property (me, "reg", i, &reg))
hw_abort (me, "\"reg\" property must contain three addr/size entries");
hw_unit_address_to_attach_address (hw_parent (me),
&reg.address,
&attach_space,
&attach_address,
me);
timers->block[i].base = attach_address;
hw_unit_size_to_attach_size (hw_parent (me),
&reg.size,
&attach_size, me);
timers->block[i].bound = attach_address + (attach_size - 1);
hw_attach_address (hw_parent (me),
0,
attach_space, attach_address, attach_size,
me);
}
}
static void
mn103tim_finish (struct hw *me)
{
struct mn103tim *timers;
int i;
timers = HW_ZALLOC (me, struct mn103tim);
set_hw_data (me, timers);
set_hw_io_read_buffer (me, mn103tim_io_read_buffer);
set_hw_io_write_buffer (me, mn103tim_io_write_buffer);
set_hw_ports (me, mn103tim_ports);
/* Attach ourself to our parent bus */
attach_mn103tim_regs (me, timers);
/* Initialize the timers */
for ( i=0; i < NR_TIMERS; ++i )
{
timers->timer[i].event = NULL;
timers->timer[i].mode = 0x00;
timers->timer[i].base = 0;
timers->timer[i].div_ratio = 0;
timers->timer[i].start = 0;
}
timers->tm6md = 0x0000;
timers->tm6bc = 0x0000;
timers->tm6mca = 0x0000;
timers->tm6mcb = 0x0000;
timers->tm6mda = 0x00;
timers->tm6mdb = 0x00;
}
/* read and write */
static int
decode_addr (struct hw *me,
struct mn103tim *timers,
unsigned_word address)
{
unsigned_word offset;
offset = address - timers->block[0].base;
switch (offset)
{
case 0x00: return TM0MD;
case 0x01: return TM1MD;
case 0x02: return TM2MD;
case 0x03: return TM3MD;
case 0x10: return TM0BR;
case 0x11: return TM1BR;
case 0x12: return TM2BR;
case 0x13: return TM3BR;
case 0x20: return TM0BC;
case 0x21: return TM1BC;
case 0x22: return TM2BC;
case 0x23: return TM3BC;
case 0x80: return TM4MD;
case 0x82: return TM5MD;
case 0x84: return TM6MD;
case 0x90: return TM4BR;
case 0x92: return TM5BR;
case 0xa0: return TM4BC;
case 0xa2: return TM5BC;
case 0xa4: return TM6BC;
case 0xb4: return TM6MDA;
case 0xb5: return TM6MDB;
case 0xc4: return TM6CA;
case 0xd4: return TM6CB;
default:
{
hw_abort (me, "bad address");
return -1;
}
}
}
static void
read_mode_reg (struct hw *me,
struct mn103tim *timers,
int timer_nr,
void *dest,
unsigned nr_bytes)
{
unsigned16 val16;
unsigned32 val32;
switch ( nr_bytes )
{
case 1:
/* Accessing 1 byte is ok for all mode registers. */
*(unsigned8*)dest = timers->timer[timer_nr].mode;
break;
case 2:
if ( timer_nr == 6 )
{
*(unsigned16 *)dest = timers->tm6md;
}
else if ( timer_nr == 0 || timer_nr == 2 )
{
val16 = (timers->timer[timer_nr].mode << 8)
| timers->timer[timer_nr+1].mode;
*(unsigned16*)dest = val16;
}
else
{
hw_abort (me, "bad read size of 2 bytes to TM%dMD.", timer_nr);
}
break;
case 4:
if ( timer_nr == 0 )
{
val32 = (timers->timer[0].mode << 24 )
| (timers->timer[1].mode << 16)
| (timers->timer[2].mode << 8)
| timers->timer[3].mode;
*(unsigned32*)dest = val32;
}
else
{
hw_abort (me, "bad read size of 4 bytes to TM%dMD.", timer_nr);
}
break;
default:
hw_abort (me, "bad read size of %d bytes to TM%dMD.",
nr_bytes, timer_nr);
}
}
static void
read_base_reg (struct hw *me,
struct mn103tim *timers,
int timer_nr,
void *dest,
unsigned nr_bytes)
{
unsigned16 val16;
unsigned32 val32;
/* Check nr_bytes: accesses of 1, 2 and 4 bytes allowed depending on timer. */
switch ( nr_bytes )
{
case 1:
/* Reading 1 byte is ok for all registers. */
if ( timer_nr < NR_8BIT_TIMERS )
{
*(unsigned8*)dest = timers->timer[timer_nr].base;
}
break;
case 2:
if ( timer_nr == 1 || timer_nr == 3 )
{
hw_abort (me, "bad read size of 2 bytes to TM%dBR.", timer_nr);
}
else
{
if ( timer_nr < NR_8BIT_TIMERS )
{
val16 = (timers->timer[timer_nr].base<<8)
| timers->timer[timer_nr+1].base;
}
else
{
val16 = timers->timer[timer_nr].base;
}
*(unsigned16*)dest = val16;
}
break;
case 4:
if ( timer_nr == 0 )
{
val32 = (timers->timer[0].base << 24) | (timers->timer[1].base << 16)
| (timers->timer[2].base << 8) | timers->timer[3].base;
*(unsigned32*)dest = val32;
}
else if ( timer_nr == 4 )
{
val32 = (timers->timer[4].base << 16) | timers->timer[5].base;
*(unsigned32*)dest = val32;
}
else
{
hw_abort (me, "bad read size of 4 bytes to TM%dBR.", timer_nr);
}
break;
default:
hw_abort (me, "bad read size must of %d bytes to TM%dBR.",
nr_bytes, timer_nr);
}
}
static void
read_counter (struct hw *me,
struct mn103tim *timers,
int timer_nr,
void *dest,
unsigned nr_bytes)
{
unsigned32 val;
if ( NULL == timers->timer[timer_nr].event )
{
/* Timer is not counting, use value in base register. */
val = timers->timer[timer_nr].base;
}
else
{
/* ticks left = start time + div ratio - curr time */
/* Cannot use base register because it can be written during counting and it
doesn't affect counter until underflow occurs. */
val = timers->timer[timer_nr].start + timers->timer[timer_nr].div_ratio
- hw_event_queue_time(me);
}
switch (nr_bytes) {
case 1:
*(unsigned8 *)dest = val;
break;
case 2:
*(unsigned16 *)dest = val;
break;
case 4:
*(unsigned32 *)dest = val;
break;
default:
hw_abort(me, "bad read size for reading counter");
}
}
static unsigned
mn103tim_io_read_buffer (struct hw *me,
void *dest,
int space,
unsigned_word base,
unsigned nr_bytes)
{
struct mn103tim *timers = hw_data (me);
enum timer_register_types timer_reg;
HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
timer_reg = decode_addr (me, timers, base);
/* It can be either a mode register, a base register or a binary counter. */
/* Check in that order. */
if ( timer_reg >= FIRST_MODE_REG && timer_reg <= LAST_MODE_REG )
{
read_mode_reg(me, timers, timer_reg-FIRST_MODE_REG, dest, nr_bytes);
}
else if ( timer_reg <= LAST_BASE_REG )
{
read_base_reg(me, timers, timer_reg-FIRST_BASE_REG, dest, nr_bytes);
}
else if ( timer_reg <= LAST_COUNTER )
{
read_counter(me, timers, timer_reg-FIRST_COUNTER, dest, nr_bytes);
}
else
{
hw_abort(me, "invalid timer register address.");
}
return nr_bytes;
}
static void
do_counter_event (struct hw *me,
void *data)
{
struct mn103tim *timers = hw_data(me);
int timer_nr = (int) data;
/* Check if counting is still enabled. */
if ( (timers->timer[timer_nr].mode & count_mask) != 0 )
{
/* Generate an interrupt for the timer underflow (TIMERn_UFLOW). */
hw_port_event (me, timer_nr /*uflow_port[timer_nr]*/, 1 /* level */);
/* Schedule next timeout. */
timers->timer[timer_nr].start = hw_event_queue_time(me);
/* FIX: Check if div_ ratio has changed and if it's now 0. */
timers->timer[timer_nr].event
= hw_event_queue_schedule (me, timers->timer[timer_nr].div_ratio,
do_counter_event, (void *)timer_nr);
}
}
static void
write_base_reg (struct hw *me,
struct mn103tim *timers,
int timer_nr,
const void *source,
unsigned nr_bytes)
{
unsigned i;
const unsigned8 *buf8 = source;
const unsigned16 *buf16 = source;
unsigned8 mode_val;
/* If TMnCNE == 0 (counting is off), writing to the base register
(TMnBR) causes a simultaneous write to the counter reg (TMnBC).
Else, the TMnBC is reloaded with the value from TMnBR when
underflow occurs. Since the counter register is not explicitly
maintained, this functionality is handled in read_counter. */
mode_val = timers->timer[timer_nr].mode;
/* Check nr_bytes: write of 1, 2 or 4 bytes allowed depending on timer. */
switch ( nr_bytes )
{
case 1:
/* Storing 1 byte is ok for all registers. */
timers->timer[timer_nr].base = buf8[0];
break;
case 2:
if ( timer_nr == 1 || timer_nr == 3 )
{
hw_abort (me, "bad write size of 2 bytes to TM%dBR.", timer_nr);
}
else
{
if ( timer_nr < NR_8BIT_TIMERS )
{
timers->timer[timer_nr].base = buf8[0];
timers->timer[timer_nr+1].base = buf8[1];
}
else
{
timers->timer[timer_nr].base = buf16[0];
}
}
break;
case 4:
if ( timer_nr == 0 )
{
ASSERT(0);
timers->timer[0].base = buf8[0];
timers->timer[1].base = buf8[1];
timers->timer[2].base = buf8[2];
timers->timer[3].base = buf8[3];
}
else if ( timer_nr == 4 )
{
timers->timer[4].base = buf16[0];
timers->timer[5].base = buf16[1];
}
else
{
hw_abort (me, "bad write size of 4 bytes to TM%dBR.", timer_nr);
}
break;
default:
hw_abort (me, "bad write size must of %d bytes to TM%dBR.",
nr_bytes, timer_nr);
}
}
static void
write_8bit_mode_reg (struct hw *me,
struct mn103tim *timers,
int timer_nr,
const void *source,
unsigned nr_bytes)
/* for timers 0 to 3 */
{
unsigned i;
unsigned8 mode_val, next_mode_val;
unsigned32 div_ratio;
if ( nr_bytes != 1 )
{
hw_abort (me, "bad write size of %d bytes to TM%dMD.", nr_bytes, timer_nr);
}
mode_val = *(unsigned8 *)source;
timers->timer[timer_nr].mode = mode_val;
if ( ( mode_val & count_and_load_mask ) == count_and_load_mask )
{
hw_abort(me, "Cannot load base reg and start counting simultaneously.");
}
if ( ( mode_val & bits2to5_mask ) != 0 )
{
hw_abort(me, "Cannot write to bits 2 to 5 of mode register");
}
if ( mode_val & count_mask )
{
/* - de-schedule any previous event. */
/* - add new event to queue to start counting. */
/* - assert that counter == base reg? */
/* For cascaded timers, */
if ( (mode_val & clock_mask) == clk_cascaded )
{
if ( timer_nr == 0 )
{
hw_abort(me, "Timer 0 cannot be cascaded.");
}
}
else
{
div_ratio = timers->timer[timer_nr].base;
/* Check for cascading. */
next_mode_val = timers->timer[timer_nr+1].mode;
if ( ( next_mode_val & clock_mask ) == clk_cascaded )
{
/* Check that CNE is on. */
if ( ( next_mode_val & count_mask ) == 0 )
{
hw_abort (me, "cascaded timer not ready for counting");
}
ASSERT(timers->timer[timer_nr+1].event == NULL);
ASSERT(timers->timer[timer_nr+1].div_ratio == 0);
div_ratio = div_ratio | (timers->timer[timer_nr+1].base << 8);
}
timers->timer[timer_nr].div_ratio = div_ratio;
if ( NULL != timers->timer[timer_nr].event )
{
hw_event_queue_deschedule (me, timers->timer[timer_nr].event);
timers->timer[timer_nr].event = NULL;
}
if ( div_ratio > 0 )
{
/* Set start time. */
timers->timer[timer_nr].start = hw_event_queue_time(me);
timers->timer[timer_nr].event
= hw_event_queue_schedule(me, div_ratio,
do_counter_event,
(void *)(timer_nr));
}
}
}
else
{
/* Turn off counting */
if ( NULL != timers->timer[timer_nr].event )
{
ASSERT((timers->timer[timer_nr].mode & clock_mask) != clk_cascaded);
hw_event_queue_deschedule (me, timers->timer[timer_nr].event);
timers->timer[timer_nr].event = NULL;
}
else
{
if ( (timers->timer[timer_nr].mode & clock_mask) == clk_cascaded )
{
ASSERT(timers->timer[timer_nr].event == NULL);
}
}
}
}
static void
write_16bit_mode_reg (struct hw *me,
struct mn103tim *timers,
int timer_nr,
const void *source,
unsigned nr_bytes)
/* for timers 4 and 5, not 6 */
{
unsigned i;
unsigned8 mode_val, next_mode_val;
unsigned32 div_ratio;
if ( nr_bytes != 1 )
{
hw_abort (me, "bad write size of %d bytes to TM%dMD.", nr_bytes, timer_nr);
}
mode_val = *(unsigned8 *)source;
timers->timer[timer_nr].mode = mode_val;
if ( ( mode_val & count_and_load_mask ) == count_and_load_mask )
{
hw_abort(me, "Cannot load base reg and start counting simultaneously.");
}
if ( ( mode_val & bits2to5_mask ) != 0 )
{
hw_abort(me, "Cannot write to bits 2 to 5 of mode register");
}
if ( mode_val & count_mask )
{
/* - de-schedule any previous event. */
/* - add new event to queue to start counting. */
/* - assert that counter == base reg? */
/* For cascaded timers, */
if ( (mode_val & clock_mask) == clk_cascaded )
{
if ( timer_nr == 4 )
{
hw_abort(me, "Timer 4 cannot be cascaded.");
}
}
else
{
div_ratio = timers->timer[timer_nr].base;
/* Check for cascading. */
next_mode_val = timers->timer[timer_nr+1].mode;
if ( ( next_mode_val & clock_mask ) == clk_cascaded )
{
/* Check that CNE is on. */
if ( ( next_mode_val & count_mask ) == 0 )
{
hw_abort (me, "cascaded timer not ready for counting");
}
ASSERT(timers->timer[timer_nr+1].event == NULL);
ASSERT(timers->timer[timer_nr+1].div_ratio == 0);
div_ratio = div_ratio | (timers->timer[timer_nr+1].base << 16);
}
timers->timer[timer_nr].div_ratio = div_ratio;
if ( NULL != timers->timer[timer_nr].event )
{
hw_event_queue_deschedule (me, timers->timer[timer_nr].event);
timers->timer[timer_nr].event = NULL;
}
if ( div_ratio > 0 )
{
/* Set start time. */
timers->timer[timer_nr].start = hw_event_queue_time(me);
timers->timer[timer_nr].event
= hw_event_queue_schedule(me, div_ratio, do_counter_event,
(void *)(timer_nr));
}
}
}
else
{
/* Turn off counting */
if ( NULL != timers->timer[timer_nr].event )
{
ASSERT((timers->timer[timer_nr].mode & clock_mask) != clk_cascaded);
hw_event_queue_deschedule (me, timers->timer[timer_nr].event);
timers->timer[timer_nr].event = NULL;
}
else
{
if ( (timers->timer[timer_nr].mode & clock_mask) == clk_cascaded )
{
ASSERT(timers->timer[timer_nr].event == NULL);
}
}
}
}
static unsigned
mn103tim_io_write_buffer (struct hw *me,
const void *source,
int space,
unsigned_word base,
unsigned nr_bytes)
{
struct mn103tim *timers = hw_data (me);
enum timer_register_types timer_reg;
HW_TRACE ((me, "write to 0x%08lx length %d with 0x%x", (long) base,
(int) nr_bytes, *(unsigned32 *)source));
timer_reg = decode_addr (me, timers, base);
/* It can be either a mode register, a base register or a binary counter. */
/* Check in that order. */
if ( timer_reg <= LAST_MODE_REG )
{
if ( timer_reg > 3 )
{
write_16bit_mode_reg(me, timers, timer_reg-FIRST_MODE_REG,
source, nr_bytes);
}
else
{
write_8bit_mode_reg(me, timers, timer_reg-FIRST_MODE_REG,
source, nr_bytes);
}
}
else if ( timer_reg <= LAST_BASE_REG )
{
write_base_reg(me, timers, timer_reg-FIRST_BASE_REG, source, nr_bytes);
}
else if ( timer_reg <= LAST_COUNTER )
{
hw_abort(me, "cannot write to counter");
}
else
{
hw_abort(me, "invalid reg type");
}
return nr_bytes;
}
const struct hw_descriptor dv_mn103tim_descriptor[] = {
{ "mn103tim", mn103tim_finish, },
{ NULL },
};