summaryrefslogtreecommitdiffstats
path: root/pk/ppe42/ppe42_core.c
blob: b49b88556972891569e7751d6980b3bca2a7a09d (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
//-----------------------------------------------------------------------------
// *! (C) Copyright International Business Machines Corp. 2014
// *! All Rights Reserved -- Property of IBM
// *! *** IBM Confidential ***
//-----------------------------------------------------------------------------

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

#define __PPE42_CORE_C__

#include "pk.h"

typedef union
{
    uint64_t    value;
    struct
    {
        uint32_t dec_start;
        uint32_t dec_change_tag;
    };
}ppe42_timebase_data_t;

ppe42_timebase_data_t ppe42_tb_data = {0};
PkTimebase  ppe42_64bit_timebase = 0;


/// 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 pk_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 PK_OK (0) are errors; see \ref pk_errors
///
/// \retval 0 Successful completion
///
/// \retval -PK_ILLEGAL_CONTEXT The API call was not made from an interrupt
/// context. 

int
pk_interrupt_preemption_enable()
{
    if (PK_ERROR_CHECK_API) {
        PK_ERROR_UNLESS_ANY_INTERRUPT_CONTEXT();
    }

    wrteei(1);

    return PK_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 pk_interrupt_disable() API.  It is legal to call
/// this API redundantly.
///
/// Return values other then PK_OK (0) are errors; see \ref pk_errors
///
/// \retval 0 Successful completion
///
/// \retval -PK_ILLEGAL_CONTEXT The API call was not made from an interrupt
/// context. 

int
pk_interrupt_preemption_disable()
{
    if (PK_ERROR_CHECK_API) {
        PK_ERROR_UNLESS_ANY_INTERRUPT_CONTEXT();
    }

    wrteei(0);

    return PK_OK;
}
        

#if PK_TIMER_SUPPORT

// The tickless kernel timer mechanism for PPE42
//
// This routine must be called from a 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 - PK 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
// __pk_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 PK specification.

#ifndef APPCFG_USE_EXT_TIMEBASE
void
__pk_schedule_hardware_timeout(PkTimebase timeout)
{
    PkTimebase       now;
    uint32_t         new_dec;
    uint32_t         dec;

    if (timeout != PK_TIMEBASE_MAX) {

        now = pk_timebase_get();

        if (timeout <= now) {
            new_dec = 1;
        } else if ((timeout - now) > 0xffff0000) {
            new_dec = 0xffff0000;
        } else {
            new_dec = timeout - now;
        }

        //read and write the DEC back-to-back so that we lose as little time
        //as possible
        dec = mfspr(SPRN_DEC);
        mtspr(SPRN_DEC, new_dec);

        //update our 64bit accumulator with how much time has advanced since
        //we last changed it.
        ppe42_64bit_timebase += ppe42_tb_data.dec_start - dec;

        //update our start time so we know how much time has advanced since
        //this update of the accumulator
        ppe42_tb_data.dec_start = new_dec;
        ppe42_tb_data.dec_change_tag++;
    }
}

#else

void
__pk_schedule_hardware_timeout(PkTimebase timeout)
{
    PkTimebase       now;
    PkTimebase       diff;
    uint32_t         new_dec;

    if (timeout != PK_TIMEBASE_MAX) {

        now = pk_timebase_get();

        //update our 64bit accumulator with the current snapshot
        ppe42_64bit_timebase = now;

        if (timeout <= now)
        {
            new_dec = 1;
        }
        else
        {
            //FIXME: We have to multiply the difference by 16
            //to workaround missing support for selecting the
            //external dec_timer clock source for the decrementer.
            diff = (timeout - now) << 4;

            if (diff > 0xfffffffful)
            {
                new_dec = 0xffffffff;
            }
            else
            {
                new_dec = diff;
            }
        }

        mtspr(SPRN_DEC, new_dec);

    }
}

#endif  /* APPCFG_USE_EXT_TIMEBASE */

#endif  /* PK_TIMER_SUPPORT */

#undef __PPE42_CORE_C__
OpenPOWER on IntegriCloud