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
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
|
/* IBM_PROLOG_BEGIN_TAG */
/* This is an automatically generated prolog. */
/* */
/* $Source: src/ssx/occhw/occhw_pba.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 */
//-----------------------------------------------------------------------------
// *! (C) Copyright International Business Machines Corp. 2014
// *! All Rights Reserved -- Property of IBM
// *! *** IBM Confidential ***
//-----------------------------------------------------------------------------
/// \file occhw_pba.c
/// \brief procedures for pba setup and operation.
#include "ssx.h"
#include "occhw_pba.h"
#include "occhw_scom.h"
#include "occhw_common.h"
#include "polling.h"
// Internal API to set up a PBA BAR
static int
pba_bar_set(int idx, uint64_t pb_base, uint8_t pb_scope)
{
int rc ;
pba_barn_t bar ;
bar.fields.cmd_scope = pb_scope ;
bar.fields.addr = pb_base >> PBA_LOG_SIZE_MIN ;
rc = putscom(PBA_BARN(idx), bar.value);
return rc ;
}
// Internal API to set up a PBA BAR Mask. The mask covers bits 23:43, address
// bits 44:63 are always allowed.
static int
pba_barmask_set(int idx, uint64_t mask)
{
int rc ;
pba_barmskn_t barmask ;
barmask.value = mask & 0x000001FFFFF00000ull;
rc = putscom(PBA_BARMSKN(idx), barmask.value);
return rc ;
}
/// Procedure to allocate a PowerBus address block using the bridge.
///
/// \param idx The BAR set to use, in the range 0..3.
///
/// \param base The 50-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 1MB (2**20), the maximum size is 2TB (2**41)
///
/// This is a validation/test procedure only, since setting up the PBA BARs is
/// normally reserved to pHyp and FSP. If the MMU is enabled and the PBA
/// mapping will be referenced by the 405, then an MMU mapping will also have
/// to be created (separately) for the block.
///
/// This procedure is not the complete setup for large memory areas. Memory
/// areas that require the extended address will have to set that up
/// separately.
///
/// \retval 0 Success
///
/// \retval PBA_INVALID_ARGUMENT_BARSET One or more of the parameter
/// restrictions were violated.
///
/// \retval PBA_SCOM_ERROR1 or PBA_SCOM_ERROR2 An attempt to write a PBA SCOM
/// register to set up the BARs produced a non-zero return code.
int
pba_barset_initialize(int idx, uint64_t base, int log_size)
{
uint64_t mask ;
mask = (0x1ull << log_size) - 1;
if (SSX_ERROR_CHECK_API)
{
SSX_ERROR_IF((idx < 0) ||
(idx > 3) ||
(log_size < PBA_LOG_SIZE_MIN) ||
(log_size > PBA_LOG_SIZE_MAX) ||
((base & mask) != 0),
PBA_INVALID_ARGUMENT_BARSET);
}
if (pba_bar_set(idx, base, PBA_POWERBUS_COMMAND_SCOPE_DEFAULT))
{
SSX_ERROR(PBA_SCOM_ERROR1);
}
if (pba_barmask_set(idx, mask))
{
SSX_ERROR(PBA_SCOM_ERROR2);
}
return 0 ;
}
// polling() function for PBA Slave reset
//
// Slave reset for PBA is a complex issue, especially in cases where the
// entity requesting the reset may be executing from main memory, i.e.
// continuing to read to or write from the slave being reset. To work around
// potential issues if the 405 is attempting to reset its own slave, the code
// that polls for reset is PowerBus cache-line aligned, and we re-hit the
// reset button each time we get an unsuccessful poll for the reset being
// done. This should guarantee that the slave will go to reset status as soon
// as any PowerBus blockages (if any) clear and the master stops either
// reading or writing the slave port. For details see HW228485.
int
_pba_slave_reset_poll(void* arg, int* done) ALIGNED_ATTRIBUTE(128);
int
_pba_slave_reset_poll(void* arg, int* done)
{
int id;
pba_slvrst_t psr;
id = (int)arg;
psr.value = 0;
psr.fields.set = PBA_SLVRST_SET(id);
out64(PBA_SLVRST, psr.value);
psr.value = in64(PBA_SLVRST);
*done = !(psr.fields.in_prog & PBA_SLVRST_IN_PROG(id));
return 0;
}
/// Reset a PBA slave with explicit timeout.
///
/// \param id A PBA slave id in the range 0..3
///
/// \param timeout A value of SsxInterval type. The special value
/// SSX_WAIT_FOREVER indicates no timeout.
///
/// \param sleep A value of SsxInterval type. Callers using the explicit
/// timeout form can request that the thread sleeps between polls; See
/// documentation for the polling() API.
///
/// This form of pba_slave_reset() gives the caller control over timeouts,
/// sleeping and error handling.
///
/// \retval 0 Success
///
/// \retval -PBA_INVALID_ARGUMENT_RESET The slave \a id parameter
/// is invalid.
///
/// \retval -PBA_SLVRST_TIMED_OUT1 The procedure timed out waiting for the PBA
/// to reset the slave.
int
_pba_slave_reset(int id, SsxInterval timeout, SsxInterval sleep)
{
int rc, closureRc;
if (SSX_ERROR_CHECK_API)
{
SSX_ERROR_IF((id < 0) || (id >= PBA_SLAVES),
PBA_INVALID_ARGUMENT_RESET);
}
rc = polling(&closureRc, _pba_slave_reset_poll, (void*)id, timeout, sleep);
if (rc == POLLING_TIMEOUT)
{
rc = -PBA_SLVRST_TIMED_OUT1;
}
return rc;
}
/// Reset a PBA slave with a default timeout
///
/// \param id A PBA slave id in the range 0..3
///
/// PBA slaves must be reset before being reprogrammed. Resetting a slave
/// also flushes any write buffers and invalidates any read buffers associated
/// with the slave. This procedure is for initialization/bringup/test only;
/// it has a non-deterministic run time due to the PowerBus so should not be
/// run as part of a hard real-time loop.
///
/// \retval 0 Success
///
/// \retval -SSX_INVALID_ARGUMENT_PBA_RESET The slave \a id parameter is
/// invalid.
///
/// \retval -PBA_SLVRST_TIMED_OUT2 The procedure timed out waiting for the PBA
/// to reset the slave.
int
pba_slave_reset(int id)
{
int rc;
rc = _pba_slave_reset(id, PBA_SLAVE_RESET_TIMEOUT, 0);
if (rc)
{
SSX_ERROR(PBA_SLVRST_TIMED_OUT2);
}
return rc;
}
/// Configure the PBAX mechanism
///
/// \param master If non-0, then this OCC will assume the role of the PBAX
/// master processor in the power domain. To avoid PBAX livelock there can
/// only be a single master in any power domain, and only the master is
/// allowed to issue PBAX broadcast transactions.
///
/// \param node The PBAX Node Id of this OCC
///
/// \param chip The PBAX Chip Id of this OCC
///
/// \param group_mask A bit mask indicating the broadcast group(s) (power
/// domain(s)) that this OCC belongs to.
///
/// This API sets up certain fields of the PBA_XCFG register according to the
/// parameters. Other fields in the register controlling hardware timeouts and
/// performance monitoring are unchanged.
///
/// \retval 0 Success
///
/// \retval -PBAX_INVALID_ARGUMENT_CONFIG One of the arguments is
/// not valid for some reason.
int
pbax_configure(int master, int group, int chip, int group_mask)
{
pba_xcfg_t pxc;
if (SSX_ERROR_CHECK_API)
{
SSX_ERROR_IF((group < 0) ||
(group >= PBAX_GROUPS) ||
(chip < 0) ||
(chip >= PBAX_CHIPS) ||
(group_mask < 0) ||
(group_mask > PBAX_GROUP_MASK_MAX),
PBAX_INVALID_ARGUMENT_CONFIG);
}
pxc.value = in64(PBA_XCFG);
pxc.fields.reservation_en = (master != 0);
pxc.fields.rcv_groupid = group;
pxc.fields.rcv_chipid = chip;
pxc.fields.rcv_brdcst_group = group_mask;
out64(PBA_XCFG, pxc.value);
return 0;
}
/// Create a PBAX abstract target
///
/// \param target An uninitialized or otherwise idle PbaxTarget object
///
/// \param type The transmission type, either PBAX_UNICAST or PBAX_BROADCAST
///
/// \param scope The PowerBus scope, either PBAX_GROUP or PBAX_SYSTEM
///
/// \param queue The receive queue index on the target, either 0 or 1
///
/// \param node The PBAX Node Id of the target
///
/// \param chip_or_group Either the PBAX Chip Id of the target (for unicast),
/// or the PBAX Group Id of the target (for broadcast)
///
/// Create an abstraction of a communication target for PBAX send operations,
/// for use with the _pbax_send() and _pbax_send() APIs. This API has no
/// knowledge of how the PBAX is configured, and therefore accepts all node,
/// chip and broadcast group ids as valid. However this API does assume that
/// all broadcast transactions are "real-time" and require a reservation.
///
/// \retval 0 Success
///
/// \retval -PBAX_INVALID_OBJECT The \a target parameter is NULL (0) or
/// otherwise invalid.
///
/// \retval -PBAX_INVALID_ARGUMENT_TARGET One or more of the arguments
/// is invalid.
int
pbax_target_create(PbaxTarget* target,
int type, int scope, int queue,
int group, int chip_or_group)
{
if (SSX_ERROR_CHECK_API)
{
SSX_ERROR_IF(target == 0, PBAX_INVALID_OBJECT);
SSX_ERROR_IF(((type != PBAX_UNICAST) && (type != PBAX_BROADCAST)) ||
((scope != PBAX_GROUP) && (scope != PBAX_SYSTEM)) ||
((queue < 0) || (queue >= PBAX_QUEUES)),
PBAX_INVALID_ARGUMENT_TARGET);
}
target->target.value = 0;
target->target.fields.snd_scope = scope;
target->target.fields.snd_qid = queue;
target->target.fields.snd_type = type;
target->target.fields.snd_reservation = (type == PBAX_BROADCAST);
target->target.fields.snd_groupid = group;
target->target.fields.snd_chipid = chip_or_group;
return 0;
}
/// Use PBAX to send 64 bits to a target with a caller-specified timeout
///
/// \param target An abstract PBAX target object
///
/// \param data The data to send
///
/// \param timeout The caller's timeout represented as an
/// SsxInterval. Use SSX_WAIT_FOREVER to indicate the caller is willing to
/// wait forever. A \a timeout of 0 indicates that the caller is not
/// willing to wait at all, and the call will either succeed immediately or
/// immediately return -PBAX_TIMEOUT.
///
/// The PBAX mechanism has a single outgoing channel that must be shared by
/// all processes that need to send messages using PBAX. Since messages sent
/// over PBAX may require an unknown amount of time to complete (due to
/// PowerBus traffic and blockages), this driver implements an agressive
/// policy that allows a sender to initiate a send transaction without waiting
/// for the final completion status. However this opens a window where a
/// subsequent writer may find the PBAX send mechanism already in use. Here
/// the new sender is obligated to busy-wait until the send mechanism is free,
/// as the PBAX send does not include an interrupt notification.
///
/// This form of the PBAX send operation accepts an explicit \a timeout value
/// as previously described. The timeout represents the amount of time that
/// the caller is willing to wait for a busy PBAX send mechanism to become
/// free. The PBAX hardware also includes mechanisms to time out the hardware
/// and modeably interrupt OCC in the event of lack of progress.
///
/// <em> This API does not operate in a critical section or enforce any
/// synchronization protocol. Synchronization and software timeout management
/// in the case of multiple senders is the responsibility of the
/// application. </em>
///
/// Unlike most other driver APIs this API can not be configured to "panic",
/// but instead always terminates with a 0 return code or an error status.
///
/// \retval 0 Success. Success only means that a transaction was sucessfully
/// initiated on an idle PBAX send machine.
///
/// \retval -PBAX_TIMEOUT The caller-specified timeout expired before the PBAX
/// send machine became free, but the PBAX send machine does not show error
/// status.
///
/// \retval -PBAX_SEND_ERROR The PBAXSNDSTAT.snd_error bit is asserted. It
/// is expected that this error will cause the PBA error interrupt to fire -
/// FFDC is collected by the interrupt handler.
int
_pbax_send(PbaxTarget* target, uint64_t data, SsxInterval timeout)
{
pba_xsndstat_t pss;
SsxTimebase start;
int rc, timed_out;
// The PBAX is always polled at least twice to guarantee that we always
// poll once after a timeout - unless the caller explicitly requested a 0
// timeout.
start = 0;
timed_out = 0;
do
{
pss.words.high_order = in32(PBA_XSNDSTAT);
if (pss.fields.snd_error)
{
rc = -PBAX_SEND_ERROR;
break;
}
if (!pss.fields.snd_in_progress)
{
rc = 0;
break;
}
if (start == 0)
{
start = ssx_timebase_get();
}
if ((timeout == 0) || timed_out)
{
rc = -PBAX_SEND_TIMEOUT;
break;
}
timed_out =
((timeout != SSX_WAIT_FOREVER) &&
((ssx_timebase_get() - start) > timeout));
}
while (1);
// Configure the send engine and initiate the write, which is kicked off
// by writing the high-order word of the send data register.
if (!rc)
{
out32(PBA_XSNDTX, target->target.words.high_order);
out32(PBA_XSNDDAT + 4, data >> 32);
out32(PBA_XSNDDAT, data & 0xffffffff);
}
return rc;
}
/// Use PBAX to send 64 bits to a target with a default timeout
///
/// \param target An abstract PBAX target object
///
/// \param data The data to send
///
/// This form of the PBAX send operation uses a default timeout, and may be
/// configured to panic in the event of timeouts or send errors. See
/// _pbax_send() for the specification of the underlying API.
///
/// <em> This API does not operate in a critical section or enforce any
/// synchronization protocol. Synchronization and software timeout management
/// in the case of multiple senders is the responsibility of the
/// application. </em>
///
/// \retval 0 Success. Success only means that a transaction was sucessfully
/// initiated on an idle PBAX send machine.
///
/// \retval -PBAX_TIMEOUT The caller-specified timeout expired before the PBAX
/// send machine became free.
///
/// \retval -PBAX_SEND_ERROR The PBAXSNDSTAT.snd_error bit is asserted.
int
pbax_send(PbaxTarget* target, uint64_t data)
{
int rc;
rc = _pbax_send(target, data, PBAX_SEND_DEFAULT_TIMEOUT);
if (rc)
{
if (rc == -PBAX_SEND_TIMEOUT)
{
SSX_ERROR(PBAX_SEND_TIMEOUT);
}
else
{
SSX_ERROR(PBAX_SEND_ERROR);
}
}
return rc;
}
|