source: rtems/c/src/lib/libbsp/powerpc/shared/clock/clock.c @ 8bb00ac

4.115
Last change on this file since 8bb00ac was 8bb00ac, checked in by Sebastian Huber <sebastian.huber@…>, on 04/02/12 at 09:32:11

bsps: More accurate PowerPC clock driver

The clock driver used previously the bsp_clicks_per_usec value. For a
33333333Hz time base frequency this leads to a relative error of one per
cent for example due to integer truncation.

  • Property mode set to 100644
File size: 6.1 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup powerpc_shared
5 *
6 * @brief Source file for a clock driver.
7 */
8
9/*
10 * Copyright (c) 2008-2012 embedded brains GmbH.  All rights reserved.
11 *
12 *  embedded brains GmbH
13 *  Obere Lagerstr. 30
14 *  82178 Puchheim
15 *  Germany
16 *  <rtems@embedded-brains.de>
17 *
18 * The license and distribution terms for this file may be
19 * found in the file LICENSE in this distribution or at
20 * http://www.rtems.com/license/LICENSE.
21 */
22
23#include <rtems.h>
24#include <rtems/clockdrv.h>
25
26#include <libcpu/powerpc-utility.h>
27#include <bsp/vectors.h>
28
29#define RTEMS_STATUS_CHECKS_USE_PRINTK
30
31#include <rtems/status-checks.h>
32
33/*
34 * This variable must be defined in the BSP and valid before clock driver
35 * initialization.
36 */
37extern uint32_t bsp_time_base_frequency;
38
39#define PPC_CLOCK_DECREMENTER_MAX UINT32_MAX
40
41volatile uint32_t Clock_driver_ticks = 0;
42
43rtems_device_major_number rtems_clock_major = UINT32_MAX;
44
45rtems_device_minor_number rtems_clock_minor = UINT32_MAX;
46
47static uint32_t ppc_clock_decrementer_value = PPC_CLOCK_DECREMENTER_MAX;
48
49static uint32_t ppc_clock_next_time_base;
50
51static uint64_t ppc_clock_factor;
52
53static void ppc_clock_no_tick(void)
54{
55        /* Do nothing */
56}
57
58static void (*ppc_clock_tick)(void) = ppc_clock_no_tick;
59
60static int ppc_clock_exception_handler( BSP_Exception_frame *frame, unsigned number)
61{
62        uint32_t delta = ppc_clock_decrementer_value;
63        uint32_t next = ppc_clock_next_time_base;
64        uint32_t dec = 0;
65        uint32_t now = 0;
66        uint32_t msr = 0;
67
68        do {
69                /* Increment clock ticks */
70                Clock_driver_ticks += 1;
71
72                /* Enable external exceptions */
73                msr = ppc_external_exceptions_enable();
74
75                /* Call clock ticker  */
76                ppc_clock_tick();
77
78                /* Restore machine state */
79                ppc_external_exceptions_disable( msr);
80
81                /* Next time base */
82                next += delta;
83
84                /* Current time */
85                now = ppc_time_base();
86
87                /* New decrementer value */
88                dec = next - now;
89        } while (dec > delta);
90
91        /* Set decrementer */
92        ppc_set_decrementer_register( dec);
93
94        /* Expected next time base */
95        ppc_clock_next_time_base = next;
96
97        return 0;
98}
99
100static int ppc_clock_exception_handler_first( BSP_Exception_frame *frame, unsigned number)
101{
102        /* We have to clear the first pending decrementer exception this way */
103
104        if (ppc_decrementer_register() >= 0x80000000) {
105                ppc_clock_exception_handler( frame, number);
106        }
107
108        ppc_exc_set_handler( ASM_DEC_VECTOR, ppc_clock_exception_handler);
109
110        return 0;
111}
112
113static int ppc_clock_exception_handler_booke( BSP_Exception_frame *frame, unsigned number)
114{
115        uint32_t msr;
116
117        /* Acknowledge decrementer request */
118        PPC_SET_SPECIAL_PURPOSE_REGISTER( BOOKE_TSR, BOOKE_TSR_DIS);
119
120        /* Increment clock ticks */
121        Clock_driver_ticks += 1;
122
123        /* Enable external exceptions */
124        msr = ppc_external_exceptions_enable();
125
126        /* Call clock ticker  */
127        ppc_clock_tick();
128
129        /* Restore machine state */
130        ppc_external_exceptions_disable( msr);
131
132        return 0;
133}
134
135static int ppc_clock_exception_handler_e300( BSP_Exception_frame *frame, unsigned number)
136{
137        uint32_t msr;
138
139        /* Increment clock ticks */
140        Clock_driver_ticks += 1;
141
142        /* Enable external exceptions */
143        msr = ppc_external_exceptions_enable();
144
145        /* Call clock ticker  */
146        ppc_clock_tick();
147
148        /* Restore machine state */
149        ppc_external_exceptions_disable( msr);
150
151        return 0;
152}
153
154static uint32_t ppc_clock_nanoseconds_since_last_tick(void)
155{
156        uint64_t k = ppc_clock_factor;
157        uint32_t c = ppc_decrementer_register();
158        uint32_t i = ppc_clock_decrementer_value + 1;
159
160        return (uint32_t) (((i - c) * k) >> 32);
161}
162
163void Clock_exit(void)
164{
165        /* Set the decrementer to the maximum value */
166        ppc_set_decrementer_register( PPC_CLOCK_DECREMENTER_MAX);
167
168        /* Use default clock handler */
169        ppc_clock_tick = ppc_clock_no_tick;
170}
171
172rtems_device_driver Clock_initialize( rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
173{
174        uint64_t frequency = bsp_time_base_frequency;
175        uint64_t us_per_tick = rtems_configuration_get_microseconds_per_tick();
176        uint32_t interval = (uint32_t) ((frequency * us_per_tick) / 1000000);
177
178        /* Current CPU type */
179        ppc_cpu_id_t cpu_type = get_ppc_cpu_type();
180
181        /* Make major/minor available to others such as shared memory driver */
182        rtems_clock_major = major;
183        rtems_clock_minor = minor;
184
185        /*
186         * Set default ticker.
187         *
188         * The function rtems_clock_tick() returns a status code.  This value
189         * will be discarded since the RTEMS documentation claims that it is
190         * always successful.
191         */
192        ppc_clock_tick = (void (*)(void)) rtems_clock_tick;
193
194        /* Set the decrementer to the maximum value */
195        ppc_set_decrementer_register( PPC_CLOCK_DECREMENTER_MAX);
196
197        /* Factor for nano seconds extension */
198        ppc_clock_factor = (1000000000ULL << 32) / frequency;
199
200        /* Decrementer value */
201        ppc_clock_decrementer_value = interval - 1;
202
203        /* Check decrementer value */
204        if (ppc_clock_decrementer_value == 0) {
205                ppc_clock_decrementer_value = PPC_CLOCK_DECREMENTER_MAX;
206                RTEMS_SYSLOG_ERROR( "decrementer value would be zero, will be set to maximum value instead\n");
207        }
208
209        /* Set the nanoseconds since last tick handler */
210        rtems_clock_set_nanoseconds_extension( ppc_clock_nanoseconds_since_last_tick);
211
212        if (ppc_cpu_is_bookE()) {
213                /* Set decrementer auto-reload value */
214                PPC_SET_SPECIAL_PURPOSE_REGISTER( BOOKE_DECAR, ppc_clock_decrementer_value);
215
216                /* Install exception handler */
217                ppc_exc_set_handler( ASM_BOOKE_DEC_VECTOR, ppc_clock_exception_handler_booke);
218
219                /* Enable decrementer and auto-reload */
220                PPC_SET_SPECIAL_PURPOSE_REGISTER_BITS( BOOKE_TCR, BOOKE_TCR_DIE | BOOKE_TCR_ARE);
221        } else if (cpu_type == PPC_e300c2 || cpu_type == PPC_e300c3) {
222                /* TODO: Not tested for e300c2 */
223
224                /* Enable auto-reload */
225                PPC_SET_SPECIAL_PURPOSE_REGISTER_BITS( HID0, 0x00000040);
226
227                /* Install exception handler */
228                ppc_exc_set_handler( ASM_DEC_VECTOR, ppc_clock_exception_handler_e300);
229        } else {
230                /* Here the decrementer value is actually the interval */
231                ++ppc_clock_decrementer_value;
232
233                /* Initialize next time base */
234                ppc_clock_next_time_base = ppc_time_base() + ppc_clock_decrementer_value;
235
236                /* Install exception handler */
237                ppc_exc_set_handler( ASM_DEC_VECTOR, ppc_clock_exception_handler_first);
238        }
239
240        /* Set the decrementer value */
241        ppc_set_decrementer_register( ppc_clock_decrementer_value);
242
243        return RTEMS_SUCCESSFUL;
244}
Note: See TracBrowser for help on using the repository browser.