summaryrefslogtreecommitdiffstats
path: root/src/ssx/pgp/pgp_ocb.c
blob: e39475e3c187fd8741a05cd45a4efed6e2b50ce7 (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
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
// $Id: pgp_ocb.c,v 1.2 2014/02/03 01:30:35 daviddu Exp $
// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/ssx/pgp/pgp_ocb.c,v $
//-----------------------------------------------------------------------------
// *! (C) Copyright International Business Machines Corp. 2013
// *! All Rights Reserved -- Property of IBM
// *! *** IBM Confidential ***
//-----------------------------------------------------------------------------

/// \file pgp_ocb.c
/// \brief OCB-related drivers for PgP

#include "ssx.h"

//use compile-time default in case the OCB timer is never used -- grm
unsigned int g_ocb_timer_divider = OCB_TIMER_DIVIDER_DEFAULT;

/// Reset an OCB timer
///
/// \param timer A valid OCB timer index
///
/// \param auto_reload A non-0 value indicates to run the timer in auto-reload
/// mode. 
///
/// \param timeout_ns The timeout specified in nanoseconds. The actual timeout
/// will be rounded down to the underlying timer tick, with a minimum 1 tick
/// timeout. However if the \a timeout_ns argument is explicity 0, then the
/// timer wil be initialized with a 0 timeout, effectively disabling the
/// timer.
///
/// Reseting an OCB timer means rewriting the timer control register with a
/// potentially new auto-reload enable and new timeout value. This API also
/// clears any pending timer interrupt status.
///
/// \retval 0 Success
///
/// \retval -OCB_INVALID_ARGUMENT_TIMER Causes include illegal timer 
/// numbers and illegal or unrepresntable timeouts.

// Note that OCB_TIMER_FREQUENCY_HZ is in the range of 1-2 MHz.

int
ocb_timer_reset(int timer,
                int auto_reload,
                int timeout_ns)
{
    ocb_otrn_t otr;
    int ticks;

    //printk("ocb_timer_reset(%d, %d, %d)\n", 
    //       timer, auto_reload, timeout_ns);

    if (timeout_ns != 0) {
        ticks = MAX(1, timeout_ns / (1000000000 / OCB_TIMER_FREQUENCY_HZ));
    } else {
        ticks = 0;
    }

    if (SSX_ERROR_CHECK_API) {
        SSX_ERROR_IF((timer < 0) ||
                     (timer >= OCB_TIMERS) ||
                     (timeout_ns < 0) ||
                     (ticks != ((uint16_t)ticks)),
                     OCB_INVALID_ARGUMENT_TIMER);
    }

    otr.value = 0;

    otr.fields.timeout = 1;
    otr.fields.control = 1;
    otr.fields.auto_reload = (auto_reload != 0);
    otr.fields.timer = ticks;

    out32(OCB_OTRN(timer), otr.value);

    return 0;
}


/// Set up an OCB timer and interrupt handler
///
/// \param timer A valid OCB timer index
///
/// \param auto_reload A non-0 value indicates to run the timer in auto-reload
/// mode. 
///
/// \param timeout_ns The timeout specified in nanoseconds. The actual timeout
/// will rounded down to the underlying timer tick, with a minimum 1 tick
/// timeout. However if the \a timeout_ns argument is explicity 0, then the
/// timer wil be initialized with a 0 timeout, effectively disabling the
/// timer.
///
/// \param handler The interrupt handler for the timer interrupt
///
/// \param arg The private argument of the interrupt handler
///
/// \param priority The SSX/PPC405 interrupt priority to assign to the
/// interrupt. 
///
/// This API sets up and starts the timer and unmasks the timer
/// interrupt. Once set up, the timer can be reset using ocb_timer_reset(). As
/// a fine point of the specification, if a timer interrupt is already pending
/// when this API is invoked then that interrupt will be cleared.  Only the
/// next interrupt (corresponding to the new programming) will be serviced by
/// the newly installed handler. 
///
/// Note that the interrupt handler is responsible for clearing the timer
/// interrupt status. An API ocb_timer_status_clear() is available for this.
///
/// \retval 0 Success
///
/// \retval -OCB_INVALID_ARGUMENT_TIMER Causes include illegal timer 
/// numbers and illegal or unrepresntable timeouts.
///
/// Other errors may be returned by the underlying calls to SSX routines that
/// set up interrupts.

int
ocb_timer_setup(int timer, 
                int auto_reload,
                int timeout_ns,
                SsxIrqHandler handler,
                void *arg,
                int priority)
{
    int rc;
    tpc_hpr2_t l_hpr2;

    do
    {
        //Read Hang Pulse Register 2 to get the log base 2 of the ocb clock divider -- grm
        rc = getscom(TPC_HPR2, &l_hpr2.value);
        if(rc)
        {
            break;
        }

        g_ocb_timer_divider = 1 << l_hpr2.fields.hang_pulse_reg;

        //printk("ocb_timer_setup(%d, %d, %d, %p, %p, %d)\n",
        //       timer, auto_reload, timeout_ns,
        //       handler, arg, priority);
    
        ssx_irq_disable(PGP_IRQ_OCC_TIMER0 + timer);

        ssx_irq_setup(PGP_IRQ_OCC_TIMER0 + timer,
                  SSX_IRQ_POLARITY_ACTIVE_HIGH,
                  SSX_IRQ_TRIGGER_LEVEL_SENSITIVE);

        ssx_irq_handler_set(PGP_IRQ_OCC_TIMER0 + timer,
                        handler,
                        arg,
                        priority);

        rc = ocb_timer_reset(timer, auto_reload, timeout_ns);

        ssx_irq_enable(PGP_IRQ_OCC_TIMER0 + timer);
    }while(0);

    return rc;
}


/// Generate an core interrupt via the PSI Host Bridge
///
/// Setting OCB_OCCMISC.core_ext_int to 1 causes a wire to pulse to the PSI
/// Host Bridge to allow the presentation of an external interrupt to a core
/// thread.  The core thread to be interrupted is controlled by the XIVR - OCC
/// register, SCOM 02010916.  Normally the hypervisor will set up the PSI Host
/// Bridge. This procedure allows OCC to send an interrupt to the hypervisor.
///
/// \retval 0 Success

// The interrupt is generated by causing a 0->1 transation on
// OCB_OCCMISC.core_ext_intr. This is implemented here using the WAND/WOR
// forms of the register.

int
ocb_core_interrupt()
{
    ocb_occmisc_t oo;

    oo.value = 0;
    oo.fields.core_ext_intr = 1;
    out32(OCB_OCCMISC_AND, ~oo.value);
    out32(OCB_OCCMISC_OR, oo.value);

    return 0;
}


/// Procedure to setup a linear window on an indirect channel
///
///
/// Since the linear window access is restricted to the SRAM region of
/// OCI space, Bits 0:4 of the base parameter are don't care and will be
/// overwritten with b'11000'
///
///
/// \todo double check SRAM region restriction 
///
/// \param channel The indirect channel to use, in the range 0..2.
///
/// \param base The 32-bit PowerBus base address where the block starts.  This
/// address must be aligned to the \a log_size.
///
/// \param log_size The base 2 logarithm of the block size, in bytes. The
/// minimum size is 8B (2**3), the maximum size is 32KB (2**15)
///
///
/// \retval 0 Success
///
/// \retval OCB_INVALID_ARGUMENT_LW_INIT One or more of the parameter 
/// restrictions were violated.
///
/// \retval OCB_SCOM_ERROR An attempt to write a PBA SCOM register to set up
/// the BARs produced a non-zero return code.

int
ocb_linear_window_initialize(int channel, uint32_t base, int log_size)
{
    uint32_t mask ;
    ocb_ocblwcrn_t ocblwcrn;
    ocb_ocblwsbrn_t ocblwsbrn;

    // create mask for checking
    mask = (0x1ull << log_size) - 1;

    if (SSX_ERROR_CHECK_API) {
        SSX_ERROR_IF((channel < 0) || 
                     (channel > 2) ||
                     (log_size < OCB_LW_LOG_SIZE_MIN) || 
                     (log_size > OCB_LW_LOG_SIZE_MAX) ||
                     ((base & mask) != 0),
                     OCB_INVALID_ARGUMENT_LW_INIT);
    }

    // now invert mask for use in ocb linear window setup
    mask = ~mask;


    // Configure OCB Linear Write Control Register    
    ocblwcrn.fields.linear_window_enable = 1;
    // base 13:28 (16 bits) 
    ocblwcrn.fields.linear_window_bar = (base >> 3) & 0xFFFF;
    // mask 17:28 (12 bits)
    ocblwcrn.fields.linear_window_mask = (mask >> 3) & 0xFFF;
    out32(OCB_OCBLWCRN(channel), ocblwcrn.value);

    // Configure OCB Linear Window Write Base Register
    ocblwsbrn.fields.linear_window_region = 3;  // SRAM only
    // \todo:  Are there constants for the OCI regions?
        // base 2:9 (8 bits)
    ocblwsbrn.fields.linear_window_base = (base >> 19) & 0xFF;
    out32(OCB_OCBLWSBRN(channel), ocblwsbrn.value);

    return 0 ;
}

/// Procedure to disable a linear window on an indirect channel
///
/// This procedure will disable the linear window while maintaining
/// the linear_window_bar and linear_window_mask settings
///
/// \param channel The indirect channel to disable, in the range 0..2.
///
/// \retval 0 Success
///
/// \retval OCB_INVALID_ARGUMENT_LW_DISABLE One or more of the parameter 
/// restrictions were violated.
///

int
ocb_linear_window_disable(int channel)
{
    ocb_ocblwcrn_t ocblwcrn;
 
    if (SSX_ERROR_CHECK_API) {
        SSX_ERROR_IF((channel < 0) || 
                     (channel > 2),
                     OCB_INVALID_ARGUMENT_LW_DISABLE);
    }

    ocblwcrn.value = in32(OCB_OCBLWCRN(channel));
    // Configure OCB Linear Write Control Register    
    ocblwcrn.fields.linear_window_enable = 0;
    out32(OCB_OCBLWCRN(channel), ocblwcrn.value);

    return 0 ;
}


/// Procedure for setting up untrusted mode for an indirect channel
///
/// Note that the OCC FW is expected to enable the channel for FSP
/// access.  As an untrusted master, the FSP cannot configure this
/// in a chip running in trusted mode.  The SBE is considered a trusted
/// master.
///
///
/// \param channel The indirect channel to use, in the range 0..2
/// Note that this bit is not used for indirect channel 3.
///
///
/// \param allow_untrusted Enable untrusted PIB masters 
/// access to the indirect channel being configured.  If allow_untrusted is
/// not enabled and the chip is running in trusted mode, then any untrusted
/// PIB master will get an offline return code when attempting to write
/// the indirect channel.  0 = Disable, 1 = Enable
///
/// \retval 0 Success
///
/// \retval OCB_INVALID_ARGUMENT_UNTRUST One or more of the parameter 
/// restrictions were violated.
///


int
ocb_allow_untrusted_initialize(int channel, int allow_untrusted)
{
    ocb_ocbicrn_t ocbicrn;

    if (SSX_ERROR_CHECK_API) {
        SSX_ERROR_IF((channel < 0) || 
                     (channel > 2) ||
                     (allow_untrusted < 0) ||
                     (allow_untrusted > 1),
                     OCB_INVALID_ARGUMENT_UNTRUST);
    }

    // Configure allow_unsecure_pib_masters bit
    ocbicrn.fields.allow_unsecure_pib_masters = allow_untrusted;
    out32(OCB_OCBICRN(channel), ocbicrn.value);  

    return 0 ;
}
OpenPOWER on IntegriCloud