summaryrefslogtreecommitdiffstats
path: root/src/ssx/occhw/occhw_ocb.c
blob: 813517ed78a430486352c3874387b2de8e2ee751 (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
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/ssx/occhw/occhw_ocb.c $                                   */
/*                                                                        */
/* OpenPOWER OnChipController Project                                     */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2015,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                                                     */

/// \file occhw_ocb.c
/// \brief OCB-related drivers for OCCHW

#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
// TEMP/TODO: Cannot do getscom from 405
// TODO: Need to discuss this getscom and whether it is necessary.
//        rc = getscom(TPC_HPR2, &l_hpr2.value);

        if(rc)
        {
            break;
        }
//TEMP/TODO: l_hpr2.fields.hang_pulse_reg is typically 9
//        g_ocb_timer_divider = 1 << l_hpr2.fields.hang_pulse_reg;
        g_ocb_timer_divider = 1 << 9;

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

        ssx_irq_disable(OCCHW_IRQ_OCC_TIMER0 + timer);

        ssx_irq_setup(OCCHW_IRQ_OCC_TIMER0 + timer,
                      SSX_IRQ_POLARITY_ACTIVE_HIGH,
                      SSX_IRQ_TRIGGER_LEVEL_SENSITIVE);

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

        rc = ocb_timer_reset(timer, auto_reload, timeout_ns);

        ssx_irq_enable(OCCHW_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_CLR, 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 > 3) ||
                     (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;


    // Enable LW mode
    ocblwcrn.fields.linear_window_enable = 1;
    // Select bits(12:28) of OCI addr for the LW bar
    ocblwcrn.fields.linear_window_bar = (base >> 3) & 0x1FFFF;
    ocblwcrn.fields.linear_window_mask = (mask >> 3) & 0xFFF;
    out32(OCB_OCBLWCRN(channel), ocblwcrn.value);

    // Configure LW region for SRAM access
    ocblwsbrn.fields.linear_window_region = 7;
    // Select bits(5:11) of OCI addr for the LW base
    ocblwsbrn.fields.linear_window_base = (base >> 20) & 0x7F;
    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 > 3),
                     OCB_INVALID_ARGUMENT_LW_DISABLE);
    }

    ocblwcrn.value = in32(OCB_OCBLWCRN(channel));
    // Disable LW mode
    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.
///


//NOTE: The OCBICR register seems to have gone away in P9 and we didn't ever
//      call this function in P8 so I'm removing this function for now. (grm)
#if 0
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 ;
}
#endif
OpenPOWER on IntegriCloud