summaryrefslogtreecommitdiffstats
path: root/src/ssx/ppc405/ppc405_core.c
blob: e1beea2ade3ba4306d5288b996589d67efabd103 (plain)
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
277
278
/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/ssx/ppc405/ppc405_core.c $                                */
/*                                                                        */
/* OpenPOWER OnChipController Project                                     */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2014,2016                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
//-----------------------------------------------------------------------------
// *! (C) Copyright International Business Machines Corp. 2014
// *! All Rights Reserved -- Property of IBM
// *! *** IBM Confidential ***
//-----------------------------------------------------------------------------

/// \file ppc405_core.c
/// \brief The final bits of SSX runtime code required to complete the PPC405
/// port.
///
/// The entry points in this file are considered 'core' routines that will
/// always be present during runtime in any SSX application.

#define __PPC405_CORE_C__

#include "ssx.h"

// Even though the external timebase is only a 32 bit register, we emulate
// a 64 bit timebase by keeping the upper 32 bits in SRAM.
volatile SsxTimebase  ppc405_64bit_ext_timebase = 0;

#if APPCFG_USE_EXT_TIMEBASE_FOR_TRACE
typedef union
{
    struct
    {
        uint32_t tbu;
        uint32_t tbl;
    };
    SsxTimebase tb64;
} SsxExtTimebase;


SsxTimebase ssx_ext_timebase_get(void)
{
    SsxExtTimebase              snapshot;
    volatile SsxExtTimebase*     cur_tb = (SsxExtTimebase*)&ppc405_64bit_ext_timebase;
    uint32_t                    tbr;
    uint32_t                    high;

    //read our in-memory timebase accumulator.
    //NOTE: 64 bit reads are not atomic on the ppc405.  This means that the
    //accumulator can be updated between reading the upper 32 bits and lower
    //32 bits.  It's ok if only the lower 32 bits changed, but if the upper
    //32 bits changed, then we will report the wrong time stamp.  Therefore,
    //we check the upper 32 bits after reading the lower 32 bits to make sure
    //it hasn't changed.
    do
    {
        snapshot.tbu = cur_tb->tbu;
        snapshot.tbl = cur_tb->tbl;
        high = cur_tb->tbu;
    }
    while(snapshot.tbu != high);

    //Now read the external timebase register
    tbr = in32(OCB_OTBR);

    //Check if we need to increment the upper 32 bits
    if(tbr < snapshot.tbl)
    {
        snapshot.tbu++;
    }

    snapshot.tbl = tbr;
    return snapshot.tb64;
}

#endif /* APPCFG_USE_EXT_TIMEBASE_FOR_TRACE */

/// Get the 64-bit timebase following the PowerPC protocol
///
/// Note that the only way to guarantee that the value returned is the value
/// \e right \e now is to call this API from a critical section.

SsxTimebase
ssx_timebase_get(void)
{
    Uint64 tb;
    uint32_t high;

    do
    {
        tb.word[0] = mftbu();
        tb.word[1] = mftb();
        high       = mftbu();
    }
    while (high != tb.word[0]);

    return tb.value;
}


/// Set the 64-bit timebase in an SSX_CRITICAL critical section
///
/// It is assumed that the caller knows what they are doing; e.g., is aware of
/// what may happen when time warps as a result of this call.

void
ssx_timebase_set(SsxTimebase timebase)
{
    SsxMachineContext ctx;
    Uint64 tb;

    tb.value = timebase;

    ssx_critical_section_enter(SSX_CRITICAL, &ctx);

    mttbl(0);
    mttbu(tb.word[0]);
    mttbl(tb.word[1]);

    ssx_critical_section_exit(&ctx);
}


/// Enable interrupt preemption
///
/// This API can only be called from an interrupt context.  Threads will
/// always be preempted by interrupts unless they explicitly disable
/// interrupts with the \c ssx_interrupt_disable() API. It is legal to call
/// this API redundantly.
///
/// Be careful when enabling interrupt handler preemption that the interrupt
/// being handled does not/can not trigger again, as this could rapidly lead
/// to stack overflows.
///
/// Return values other then SSX_OK (0) are errors; see \ref ssx_errors
///
/// \retval 0 Successful completion
///
/// \retval -SSX_ILLEGAL_CONTEXT The API call was not made from an interrupt
/// context.

int
ssx_interrupt_preemption_enable()
{
    if (SSX_ERROR_CHECK_API)
    {
        SSX_ERROR_UNLESS_ANY_INTERRUPT_CONTEXT();
    }

    if (__ssx_kernel_context_noncritical_interrupt())
    {
        wrteei(1);
    }
    else
    {
        or_msr(MSR_CE);
    }

    return SSX_OK;
}


/// Disable interrupt preemption
///
/// This API can only be called from an interrupt context.  Threads will
/// always be preempted by interrupts unless they explicitly disable
/// interrupts with the \c ssx_interrupt_disable() API.  It is legal to call
/// this API redundantly.
///
/// Return values other then SSX_OK (0) are errors; see \ref ssx_errors
///
/// \retval 0 Successful completion
///
/// \retval -SSX_ILLEGAL_CONTEXT The API call was not made from an interrupt
/// context.

int
ssx_interrupt_preemption_disable()
{
    if (SSX_ERROR_CHECK_API)
    {
        SSX_ERROR_UNLESS_ANY_INTERRUPT_CONTEXT();
    }

    if (__ssx_kernel_context_noncritical_interrupt())
    {
        wrteei(0);
    }
    else
    {
        andc_msr(MSR_CE);
    }

    return SSX_OK;
}


#if SSX_TIMER_SUPPORT

// The tickless kernel timer mechanism for PPC405
//
// This routine must be called from an SSX_NONCRITICAL critical section.
//
// Tickless timeouts are provided by programming the PIT timer based on when
// the next timeout will occur.  If the timeout is for the end of time there's
// nothing to do - SSX does not use auto-reload mode so no more PIT interrupts
// will be arriving.  Otherwise, if the timeout is longer than the 32-bit PIT
// timer can handle, we simply schedule the timeout for 2**32 - 1 and
// __ssx_timer_handler() will keep rescheduling it until it finally occurs.
// If the \a timeout is in the past, we schedule the PIT interrupt for 1 tick
// in the future in accordance with the SSX specification.

void
__ssx_schedule_hardware_timeout(SsxTimebase timeout)
{
    SsxTimebase now;
    uint32_t pit;

    if (timeout != SSX_TIMEBASE_MAX)
    {

        now = ssx_timebase_get();

        if (timeout <= now)
        {
            pit = 1;
        }
        else if ((timeout - now) > 0xffffffff)
        {
            pit = 0xffffffff;
        }
        else
        {
            pit = timeout - now;
        }

        mtspr(SPRN_PIT, pit);

#if APPCFG_USE_EXT_TIMEBASE_FOR_TRACE
        ppc405_64bit_ext_timebase = ssx_ext_timebase_get();
#endif /* APPCFG_USE_EXT_TIMEBASE_FOR_TRACE */
    }
}


// Cancel the PPC405 tickless kernel timeout
//
// This routine must be called from an SSX_NONCRITICAL critical section.  SSX
// does not use auto-reload mode of the PIT, so simply writing the PIT with 0
// effectively cancels the timer.

void
__ssx_cancel_hardware_timeout()
{
    mtspr(SPRN_PIT, 0);
}


#endif  /* SSX_TIMER_SUPPORT */

#undef __PPC405_CORE_C__
OpenPOWER on IntegriCloud