summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/m68k/mvme167/clock/ckinit.c
blob: 021725d992202c11cc0a1ec4bc8ead2c5dcce24c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
/*  ckinit.c
 *
 *  Implementation of the Clock_control() and Clock_initialize() functions
 *  prototyped in rtems/c/src/lib/include/clockdrv.h.
 *
 *  This port does not allow the application to select which timer on the
 *  MVME167 to use for the clock, nor does it allow the application to
 *  configure the clock. The clock uses the VMEchip2 Tick Timer #2. This
 *  timer is set up to raise a MC680x0 level-6 interrupt every 1 ms. The
 *  interrupt vector is 0x69.
 *
 *  All page references are to the MVME166/MVME167/MVME187 Single Board
 *  Computer Programmer's Reference Guide (MVME187PG/D2) with the April
 *  1993 supplements/addenda (MVME187PG/D2A1).
 *
 *  COPYRIGHT (c) 1989-1999.
 *  On-Line Applications Research Corporation (OAR).
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.OARcorp.com/rtems/license.html.
 *
 *  Modifications of respective RTEMS files:
 *  Copyright (c) 1998, National Research Council of Canada
 *
 *  $Id$
 */
 
#include <stdlib.h>
#include <bsp.h>
#include <rtems/libio.h>

#define MS_COUNT          1000              /* T2's countdown constant (1 ms) */
#define CLOCK_INT_LEVEL   6                 /* T2's interrupt level */
#define CLOCK_VECTOR (VBR0 * 0x10 + 0x9)    /* T2 is vector $X9 (p. 2-71)*/

/*
 *  These are declared in rtems/c/src/lib/include/clockdrv.h
 *  In other BSPs, rtems_clock_major is set to the largest possible value
 *  (which is almost certainly greater than the number of I/O devices) to
 *  indicate that this device has not been initialized yet. The actual
 *  device number is supplied during initialization. We do not do that.
 *
 *  Initialized data ends up the the .data section. This causes two problems:
 *  1) the .data section is no longer ROMable because we need to write into
 *  it. 2) The initial value is correct only after a download. On subsequent
 *  program restarts, the value is not re-initialized but left to whatever it
 *  was when the previous run terminated or aborted. If we depend on some
 *  global variable value, we must initialize that value explicitly in code
 *  at boot time.
 */
rtems_device_major_number rtems_clock_major;
rtems_device_minor_number rtems_clock_minor;

/*
 *  Clock_driver_ticks is a monotonically increasing counter of the number of
 *  VMEchip2 timer #2 ticks since the driver was initialized.
 */
volatile rtems_unsigned32 Clock_driver_ticks;


/* 
 *  Clock_isrs is the number of clock ISRs until the next invocation of the
 *  RTEMS clock tick routine. This clock tick device driver gets an interrupt
 *  once a millisecond and counts down until the length of time between the
 *  user configured microseconds per tick has passed. This allows the clock
 *  device to "tick" faster than the kernel clock. Of course, the kernel clock
 *  cannot tick faster than the hardware clock. Therefore, the kernel clock
 *  ticks cannot occur more frequently than every 1 millisecond.
 */
rtems_unsigned32 Clock_isrs;


/*
 *  Records the previous clock ISR (should be NULL)
 */
rtems_isr_entry  Old_ticker;


/*
 *  Called when the kernel exits.
 */
void clock_exit( void );


/*
 *  VMEchip2_T2_isr
 *
 *  C ISR Handler. Increment the number of internal ticks. If it is time for a
 *  kernel clock tick (if Clock_isrs == 1), call rtems_clock_tick() to signal
 *  the event and reset the Clock_isrs counter; else, just decrement it.
 *
 *  Input parameters:
 *    vector number
 *
 *  Output parameters: NONE
 *
 *  Return values: NONE
 */
rtems_isr VMEchip2_T2_isr(
  rtems_vector_number vector
)
{
  char overflow;                /* Content of overflow counter */
  long i;
  long ct;                      /* Number of T2 ticks per RTEMS ticks */
  
  ct = BSP_Configuration.microseconds_per_tick / 1000;
  
  /*
   *  May have missed interrupts, so should look at the overflow counter.
   */
  lcsr->intr_clear |= 0x02000000;   /* Clear the interrupt */
  overflow = (lcsr->board_ctl >> 12) & 0xF;
  lcsr->board_ctl |= 0x400;         /* Reset overflow counter */
  
  /* Attempt to protect against one more period */
  if ( overflow == 0 )
    overflow = 16;

  Clock_driver_ticks += overflow;   /* One or more internal ticks */

  if ( Clock_isrs <= overflow ) {
    /* If its time for kernel clock ticks, signal the events to RTEMS */
    for( i = overflow - Clock_isrs; i >= 0; i -= ct ) {
      rtems_clock_tick();
    }
    /* Reset the counter */
    Clock_isrs = (rtems_unsigned32)-i;
  }
  else 
    Clock_isrs -= overflow;
}


/*
 *  VMEchip2_T2_initialize
 *
 *  Initialize the VMEchip2 Tick Timer #2.
 *
 *  THE VMECHIP2 PRESCALER REGISTER IS ASSUMED TO BE SET! 
 *  The prescaler is used by all VMEchip2 timers, including the VMEbus grant
 *  timeout counter, the DMAC time off timer, the DMAC timer on timer, and the
 *  VMEbus global timeout timer. The prescaler value is normally set by the
 *  boot ROM to provide a 1 MHz clock to the timers. For a 25 MHz MVME167, the
 *  prescaler value should be 0xE7 (page 2-63).
 *
 *  Input parameters: NONE
 *
 *  Output paremeters: NONE
 *
 *  Return values: NONE
 */
void VMEchip2_T2_initialize( void )
{
  Clock_driver_ticks = 0;
  Clock_isrs = BSP_Configuration.microseconds_per_tick / 1000;

  lcsr->intr_ena &= 0xFDFFFFFF;   /* Disable tick timer 2 interrupt */
  lcsr->intr_clear = 0x02000000;  /* Clear tick timer 2 interrupt */
  lcsr->intr_level[0] =           /* Set tick timer 2 interrupt level */
      (lcsr->intr_level[0] & 0xFFFFFF0F ) | (CLOCK_INT_LEVEL << 4);
  lcsr->timer_cmp_2 = MS_COUNT;   /* Period in compare register */
  lcsr->timer_cnt_2 = 0;          /* Clear tick timer 2 counter */
  Old_ticker =                    /* Install C ISR */
      (rtems_isr_entry) set_vector( VMEchip2_T2_isr, CLOCK_VECTOR, 1 );
  lcsr->board_ctl |= 0x700;       /* Start tick timer 2, reset-on-compare, */
                                  /*  and clear tick timer 2 overflow counter */
  lcsr->intr_ena |= 0x02000000;   /* Enable tick timer 2 interrupt */
  lcsr->vector_base |= 0x00800000;/* Unmask VMEchip2 interrupts */
  atexit( clock_exit );           /* Turn off T2 interrupts when we exit */
}


/*
 *  clock_exit
 *
 *  This routine stops the VMEchip2 T2 timer, disables its interrupt, and
 *  re-install the old interrupt vectors.
 *
 *  Input parameters:   NONE
 *
 *  Output parameters:  NONE
 *
 *  Return values:      NONE
 *
 */
void clock_exit( void )
{
  lcsr->board_ctl &= 0xFFFFFEFF;  /* Stop tick timer 2 */
  lcsr->intr_ena &= 0xFDFFFFFF;   /* Disable tick timer 2 interrupt */
  lcsr->intr_clear = 0x02000000;  /* Clear tick timer 2 interrupt */

  set_vector( Old_ticker, CLOCK_VECTOR, 1 );
}


/*
 *  Clock_initialize()
 *  prototyped in rtems/c/src/lib/include/clockdrv.h.
 *
 *  Input parameters:
 *     major - console device major number
 *     minor - console device minor number
 *             ALWAYS 0 IN VERSION 3.6.0 OF RTEMS!
 *             Probably provided for symmetry with the other I/O calls.
 *     arg   - pointer to optional device driver arguments
 *             ALWAYS NULL IN VERSION 3.6.0 OF RTEMS!
 *
 *  Output paremeters: NONE
 *
 *  Return values:
 *     rtems_device_driver status code
 */ 
rtems_device_driver Clock_initialize(
  rtems_device_major_number major,
  rtems_device_minor_number minor,
  void *pargp
)
{
  VMEchip2_T2_initialize();
 
  /*
   *  Make major/minor avail to others such as shared memory driver
   */
  rtems_clock_major = major;
  rtems_clock_minor = minor;
 
  return RTEMS_SUCCESSFUL;
}


/*
 *  Clock_control().
 *  Prototyped in rtems/c/src/lib/include/clockdrv.h
 *
 *  Input parameters:
 *    major - clock device major number
 *    minor - clock device minor number
 *    parg  - pointer to optional device driver arguments
 *
 *  Output parameters:  NONE
 *
 *  Return values:
 *    rtems_device_driver status code
 */
rtems_device_driver Clock_control(
  rtems_device_major_number major,
  rtems_device_minor_number minor,
  void *pargp)
{
    rtems_unsigned32 isrlevel;
    rtems_libio_ioctl_args_t *args = pargp;
 
    if ( args == 0 )
        goto done;
 
    /*
     * This is hokey, but until we get a defined interface
     * to do this, it will just be this simple...
     */
    if ( args->command == rtems_build_name('I', 'S', 'R', ' ') )
    {
      VMEchip2_T2_isr( CLOCK_VECTOR );
    }
    else if ( args->command == rtems_build_name('N', 'E', 'W', ' ') )
    {
      rtems_interrupt_disable( isrlevel );
      set_vector( args->buffer, CLOCK_VECTOR, 1 );
      rtems_interrupt_enable( isrlevel );
    }
 
done:
    return RTEMS_SUCCESSFUL;
}