source: rtems/c/src/lib/libcpu/powerpc/mpc6xx/clock/c_clock.c @ 3fa48ee

4.104.114.9
Last change on this file since 3fa48ee was 3fa48ee, checked in by Till Straumann <strauman@…>, on Dec 1, 2007 at 12:22:30 AM

2007-11-30 Till Straumann <strauman@…>

  • mpc6xx/clock/c_clock.c, mpc6xx/clock/c_clock.h: added support for bookE/ppc405 style CPUs where the decrementer works slightly differently.
  • Property mode set to 100644
File size: 7.1 KB
Line 
1/*
2 *  Clock Tick Device Driver
3 *
4 *  This routine utilizes the Decrementer Register common to the PPC family.
5 *
6 *  The tick frequency is directly programmed to the configured number of
7 *  microseconds per tick.
8 *
9 *  COPYRIGHT (c) 1989-2007.
10 *  On-Line Applications Research Corporation (OAR).
11 *
12 *  The license and distribution terms for this file may in
13 *  the file LICENSE in this distribution or at
14 *  http://www.rtems.com/license/LICENSE.
15 *
16 *  Modified to support the MPC750.
17 *  Modifications Copyright (c) 1999 Eric Valette valette@crf.canon.fr
18 *
19 *  $Id$
20 */
21
22#include <rtems/system.h>
23#include <rtems.h>
24#include <rtems/libio.h>
25#include <stdlib.h>                     /* for atexit() */
26#include <assert.h>
27#include <libcpu/c_clock.h>
28#include <libcpu/cpuIdent.h>
29#include <libcpu/spr.h>
30#include <rtems/bspIo.h>                     /* for printk() */
31
32SPR_RW(BOOKE_TCR)
33SPR_RW(BOOKE_TSR)
34SPR_RW(BOOKE_DECAR)
35SPR_RW(DEC)
36
37extern int BSP_connect_clock_handler (void);
38
39/*
40 *  Clock ticks since initialization
41 */
42
43volatile uint32_t   Clock_driver_ticks;
44
45/*
46 *  This is the value programmed into the count down timer.
47 */
48
49uint32_t   Clock_Decrementer_value;
50
51/*
52 * These are set by clock driver during its init
53 */
54 
55rtems_device_major_number rtems_clock_major = ~0;
56rtems_device_minor_number rtems_clock_minor;
57
58void clockOff(void* unused)
59{
60rtems_interrupt_level l;
61
62  if ( ppc_cpu_is_bookE() ) {
63    rtems_interrupt_disable(l);
64    _write_BOOKE_TCR(_read_BOOKE_TCR() & ~BOOKE_TCR_DIE);
65    rtems_interrupt_enable(l);
66  } else {
67  /*
68   * Nothing to do as we cannot disable all interrupts and
69   * the decrementer interrupt enable is MSR_EE
70   */
71  }
72}
73
74void clockOn(void* unused)
75{
76rtems_interrupt_level l;
77
78  PPC_Set_decrementer( Clock_Decrementer_value );
79
80  if ( ppc_cpu_is_bookE() ) {
81    _write_BOOKE_DECAR( Clock_Decrementer_value );
82
83    rtems_interrupt_disable(l);
84
85    /* clear pending/stale irq */
86    _write_BOOKE_TSR( BOOKE_TSR_DIS );
87    /* enable */
88    _write_BOOKE_TCR( _read_BOOKE_TCR() | BOOKE_TCR_DIE );
89
90    rtems_interrupt_enable(l);
91
92  }
93}
94
95static void clockHandler(void)
96{
97    rtems_clock_tick();
98}
99
100static void (*clock_handler)(void);
101
102/*
103 *  Clock_isr
104 *
105 *  This is the clock tick interrupt handler.
106 *
107 *  Input parameters:
108 *    vector - vector number
109 *
110 *  Output parameters:  NONE
111 *
112 *  Return values:      NONE
113 *
114 */
115void clockIsr(void *unused)
116{
117int decr;
118  /*
119   *  The driver has seen another tick.
120   */
121  do {
122        register uint32_t flags;
123        rtems_interrupt_disable(flags);
124        asm volatile ("mfdec %0; add %0, %0, %1; mtdec %0":"=&r"(decr):"r"(Clock_Decrementer_value));
125        rtems_interrupt_enable(flags);
126
127        Clock_driver_ticks += 1;
128
129        /*
130         *  Real Time Clock counter/timer is set to automatically reload.
131         */
132        clock_handler();
133  } while ( decr < 0 );
134}
135
136/*
137 *  Clock_isr_bookE
138 *
139 *  This is the clock tick interrupt handler
140 *  for bookE CPUs. For efficiency reasons we
141 *  provide a separate handler rather than
142 *  checking the CPU type each time.
143 *
144 *  Input parameters:
145 *    vector - vector number
146 *
147 *  Output parameters:  NONE
148 *
149 *  Return values:      NONE
150 *
151 */
152void clockIsrBookE(void *unused)
153{
154  /* Note: TSR bit has already been cleared in the exception handler */
155
156  /*
157   *  The driver has seen another tick.
158   */
159
160  Clock_driver_ticks += 1;
161
162  /*
163   *  Real Time Clock counter/timer is set to automatically reload.
164   */
165  clock_handler();
166
167}
168
169int clockIsOn(void* unused)
170{
171uint32_t   msr_value;
172
173        _CPU_MSR_GET( msr_value );
174
175        if ( ppc_cpu_is_bookE() && ! (_read_BOOKE_TCR() & BOOKE_TCR_DIE) )
176                msr_value = 0;
177
178        if (msr_value & MSR_EE) return 1;
179
180        return 0;
181}
182
183
184/*
185 *  Clock_exit
186 *
187 *  This routine allows the clock driver to exit by masking the interrupt and
188 *  disabling the clock's counter.
189 *
190 *  Input parameters:   NONE
191 *
192 *  Output parameters:  NONE
193 *
194 *  Return values:      NONE
195 *
196 */
197
198void Clock_exit( void )
199{
200  (void) BSP_disconnect_clock_handler ();
201}
202 
203uint32_t Clock_driver_nanoseconds_since_last_tick(void)
204{
205  uint32_t clicks, tmp;
206
207  PPC_Get_decrementer( clicks ); 
208
209  /*
210   * Multiply by 1000 here separately from below so we do not overflow
211   * and get a negative value.
212   */
213  tmp = (Clock_Decrementer_value - clicks) * 1000;
214  tmp /= (BSP_bus_frequency/BSP_time_base_divisor);
215
216  return tmp * 1000;
217}
218
219/*
220 *  Clock_initialize
221 *
222 *  This routine initializes the clock driver.
223 *
224 *  Input parameters:
225 *    major - clock device major number
226 *    minor - clock device minor number
227 *    parg  - pointer to optional device driver arguments
228 *
229 *  Output parameters:  NONE
230 *
231 *  Return values:
232 *    rtems_device_driver status code
233 */
234rtems_device_driver Clock_initialize(
235  rtems_device_major_number major,
236  rtems_device_minor_number minor,
237  void *pargp
238)
239{
240rtems_interrupt_level l,tcr;
241
242  Clock_Decrementer_value = (BSP_bus_frequency/BSP_time_base_divisor)*
243            (rtems_configuration_get_microseconds_per_tick()/1000);
244
245  /* set the decrementer now, prior to installing the handler
246   * so no interrupts will happen in a while.
247   */
248  PPC_Set_decrementer( (unsigned)-1 );
249
250  /* On a bookE CPU the decrementer works differently. It doesn't
251   * count past zero but we can enable auto-reload :-)
252   */
253  if ( ppc_cpu_is_bookE() ) {
254
255    rtems_interrupt_disable(l);
256
257    tcr  = _read_BOOKE_TCR();
258        tcr |= BOOKE_TCR_ARE;
259        tcr &= ~BOOKE_TCR_DIE;
260    _write_BOOKE_TCR(tcr);
261
262    rtems_interrupt_enable(l);
263
264  }
265
266  /*
267   *  Set the nanoseconds since last tick handler
268   */
269  rtems_clock_set_nanoseconds_extension(
270    Clock_driver_nanoseconds_since_last_tick
271  );
272
273  /* if a decrementer exception was pending, it is cleared by
274   * executing the default (nop) handler at this point;
275   * The next exception will then be taken by our clock handler.
276   * Clock handler installation initializes the decrementer to
277   * the correct value.
278   */
279
280  clock_handler = clockHandler;
281  if (!BSP_connect_clock_handler ()) {
282    printk("Unable to initialize system clock\n");
283    rtems_fatal_error_occurred(1);
284  }
285  /* make major/minor avail to others such as shared memory driver */
286 
287  rtems_clock_major = major;
288  rtems_clock_minor = minor;
289
290  return RTEMS_SUCCESSFUL;
291} /* Clock_initialize */
292 
293/*
294 *  Clock_control
295 *
296 *  This routine is the clock device driver control entry point.
297 *
298 *  Input parameters:
299 *    major - clock device major number
300 *    minor - clock device minor number
301 *    parg  - pointer to optional device driver arguments
302 *
303 *  Output parameters:  NONE
304 *
305 *  Return values:
306 *    rtems_device_driver status code
307 */
308
309rtems_device_driver Clock_control(
310  rtems_device_major_number major,
311  rtems_device_minor_number minor,
312  void *pargp
313)
314{
315    rtems_libio_ioctl_args_t *args = pargp;
316 
317    if (args == 0)
318        goto done;
319 
320    Clock_Decrementer_value = (BSP_bus_frequency/BSP_time_base_divisor)*
321      (rtems_configuration_get_microseconds_per_tick()/1000);
322
323    if      (args->command == rtems_build_name('I', 'S', 'R', ' '))
324      clockHandler();
325    else if (args->command == rtems_build_name('N', 'E', 'W', ' '))
326    {
327        if (clock_handler == NULL)
328            Clock_initialize(major, minor, 0);
329        clock_handler = args->buffer;
330    }
331done:
332    return RTEMS_SUCCESSFUL;
333}
Note: See TracBrowser for help on using the repository browser.