diff options
Diffstat (limited to 'src/ssx/occhw/occhw_async.h')
-rw-r--r-- | src/ssx/occhw/occhw_async.h | 1514 |
1 files changed, 1514 insertions, 0 deletions
diff --git a/src/ssx/occhw/occhw_async.h b/src/ssx/occhw/occhw_async.h new file mode 100644 index 0000000..e2bbf0a --- /dev/null +++ b/src/ssx/occhw/occhw_async.h @@ -0,0 +1,1514 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/ssx/occhw/occhw_async.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] 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 */ +#ifndef __OCCHW_ASYNC_H__ +#define __OCCHW_ASYNC_H__ + +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file occhw_async.h +/// \brief Support for asynchronous request/callback mechanisms +/// +/// The data structures defined here provide a 'C' implementation of multiple +/// single-inheritance class hierarchies. The 'subclasses' always include the +/// 'superclass' as the initial element of the structure, allowing subclass +/// pointers to be safely cast to the superclass, and vice-versa (assuming +/// that the subclass is known). One benefit of this approach is that it +/// allows code sharing between requests targeting GPE, +/// PBA-BCDE, PBA-BCUE and the deferred callback queue. +/// +/// The 'class hierarchy' : +/// +/// SsxDeque +/// AsyncRequest +/// GpeRequest +/// BceRequest +/// OcbRequest +/// Pbaxrequest +/// +/// AsyncQueue +/// GpeQueue +/// BceQueue +/// OcbQueue +/// PbaxQueue +/// + +#include "ipc_async_cmd.h" +#include "occhw_xir_dump.h" + +// OCCHW Execution engines for the purposes of the generic request mechanism. + +#define ASYNC_ENGINE_ANONYMOUS 0x00 + +#define ASYNC_ENGINE_GPE 0x10 +#define ASYNC_ENGINE_GPE0 0x10 +#define ASYNC_ENGINE_GPE1 0x11 +#define ASYNC_ENGINE_GPE2 0x12 +#define ASYNC_ENGINE_GPE3 0x13 + +#define ASYNC_ENGINE_BCE 0x20 +#define ASYNC_ENGINE_BCDE 0x20 +#define ASYNC_ENGINE_BCUE 0x21 + +// Indirect channel 3 is now back online to support push/pull queues in P9. +#define ASYNC_ENGINE_OCB 0x40 +#define ASYNC_ENGINE_OCB_PUSH0 0x41 +#define ASYNC_ENGINE_OCB_PUSH1 0x42 +#define ASYNC_ENGINE_OCB_PUSH2 0x43 +#define ASYNC_ENGINE_OCB_PUSH3 0x44 +#define ASYNC_ENGINE_OCB_PULL0 0x45 +#define ASYNC_ENGINE_OCB_PULL1 0x46 +#define ASYNC_ENGINE_OCB_PULL2 0x47 +#define ASYNC_ENGINE_OCB_PULL3 0x48 + +#define ASYNC_ENGINE_PBAX 0x80 +#define ASYNC_ENGINE_PBAX_PUSH0 0x81 +#define ASYNC_ENGINE_PBAX_PUSH1 0x82 + + +#ifndef __ASSEMBLER__ + +typedef uint8_t AsyncEngine; + +#endif /* __ASSEMBLER__ */ + + +//////////////////////////////////////////////////////////////////////////// +// FFDC Structures +//////////////////////////////////////////////////////////////////////////// + +#ifndef __ASSEMBLER__ + +/// FFDC from the PLB (OCI) arbiter +/// +/// The PLB arbiter records errors from all OCI masters, including the address +/// of the transaction that caused the error. The PLB arbiter is set up to +/// 'lock' the first error data until the lock is reset. This structure is +/// included in all of the unit FFDC structures for peripherals that can +/// master on the OCI (SLW/GPE/OCB/PBA). Note that OCI errors from the 405 +/// core will cause an immediate machine check exception. + +typedef struct { + + /// PLB arbiter Error Address Register + /// + /// This is the address of the last PLB timeout or other error recorded in + /// the PEAR. This is an error for the unit in question only if the + /// \a mine data member is non-zero. + ocb_oear_t oear; + + /// PLB arbiter Error Status Register + /// + /// The PESR at the time of the error. + ocb_oesr_t oesr; + + /// Is the unit in question responsible for the error address recorded in + /// the PEARL? + int mine; + +} OciFfdc; + +void +oci_ffdc(OciFfdc* ffdc, int master_id); + +#endif // __ASSEMBLER__ + + +//////////////////////////////////////////////////////////////////////////// +// AsyncRequest +//////////////////////////////////////////////////////////////////////////// + +/// \defgroup async_request_states ASYNC Request States +/// +/// Request states for async requests are group into 5 logical states for +/// error handling and timeout handling purposes. +/// +/// - Queued Group : Queued requests are still waiting to execute in the device queue, +/// so these requests can be cancelled simply by removing them from the queue. +/// +/// - Running Group : Running requests are running on the hardware, so killing +/// these jobs may require a forced stop and restart of the device. +/// +/// - Callback Group : By specification callbacks are always run if provided, +/// even if a job dies or is timed out. This includes post-processing only +/// work like unblocking blocked threads. These processes are not interrupted. +/// +/// - Idle Group : These jobs are no longer in process, and their states are +/// final. +/// +/// Only idle requests can be (re)scheduled. Only jobs in the Queued or +/// Running states can time out. Once the job has moved to the Callback group +/// it is considered complete as far as timeout processing is concerned. +/// +/// @{ + +/// The request is waiting in the queue +#define ASYNC_REQUEST_STATE_QUEUED 0x10 + + +/// The request is running on the hardware +#define ASYNC_REQUEST_STATE_RUNNING 0x20 + + +/// The request is complete but a deferred callback has yet to run +#define ASYNC_REQUEST_STATE_CALLBACK_QUEUED 0x40 + +/// The request callback is running +#define ASYNC_REQUEST_STATE_CALLBACK_RUNNING 0x41 + +/// Thread unblocking and/or timeout cancellation has been deferred to a +/// noncritical interrupt context. +#define ASYNC_REQUEST_STATE_POSTPROCESSING 0x42 + + +/// The request has been initialized but never run +#define ASYNC_REQUEST_STATE_INITIALIZED 0x80 + +/// The request failed due to an error signalled by hardware +#define ASYNC_REQUEST_STATE_FAILED 0x81 + +/// The request completed normally +#define ASYNC_REQUEST_STATE_COMPLETE 0x82 + +/// The application cancelled the request +#define ASYNC_REQUEST_STATE_CANCELLED 0x83 + +/// The request timed out +#define ASYNC_REQUEST_STATE_TIMED_OUT 0x84 + + +#define ASYNC_REQUEST_QUEUED_GROUP 0x10 +#define ASYNC_REQUEST_RUNNING_GROUP 0x20 +#define ASYNC_REQUEST_CALLBACK_GROUP 0x40 +#define ASYNC_REQUEST_IDLE_GROUP 0x80 + +/// @} + + +/// \defgroup async_request_options ASYNC Request Options +/// +/// These are the option flags for the \a options field of the AsyncRequest. +/// These are generic options applicable to all requests. +/// +/// @{ + +#define ASYNC_CALLBACK_IMMEDIATE 0x0001 +#define ASYNC_CALLBACK_DEFERRED 0x0002 +#define ASYNC_CALLBACK_NONCRITICAL 0x0004 +#define ASYNC_CALLBACK_OPTIONS 0x0007 + +#define ASYNC_REQUEST_PRIORITY 0x0008 +#define ASYNC_CALLBACK_PRIORITY 0x0010 + +#define ASYNC_REQUEST_BLOCKING 0x0020 + +#define ASYNC_GENERIC_OPTIONS 0x003f + +/// @} + +#ifndef __ASSEMBLER__ + +struct AsyncRequest; +struct AsyncQueue; + +typedef int (*AsyncRunMethod)(struct AsyncRequest *request); + +typedef int (*AsyncErrorMethod)(struct AsyncRequest *request); + +typedef int (*AsyncRequestCallback)(void *); + +/// A generic request for the asynchronous request drivers +/// +/// Note: Normally the application will not explicitly manipulate this +/// structure, but will instead manipulate derived structures. +/// +/// A request is queued for a particular engine, with a callback to be +/// executed once the request has been processed by the engine. The \a +/// callback may be NULL, but at a minimum the requestor can observe the \a +/// state of the request to determine the state of request processing. If a +/// deferred callback is requested, then once processing is done the generic +/// handler will requeue the request in a callback queue. +/// +/// The \a run_method is a "pure virtual" function of this class. The \a +/// run_method encapsulates the procedure for starting a job on the +/// engine. The \a run_method may make assumptions about and use data +/// contained in the AsyncQueue class used to queue the requests. +/// +/// The \a error_method is also "pure virtual" function of this class. The \a +/// error_method encapsulates the procedure for collecting FFDC and preparing +/// the engine to run the next job. The \a error_method may make assumptions +/// about and use data contained in the AsyncQueue class used to queue the +/// requests. If the \a error_method returns a non-0 code then the error is +/// considered fatal and the queue stops. + +typedef struct AsyncRequest { + + /// Generic queue management - the "base class" + SsxDeque deque; + + /// The time the job was started on the hardware. + /// + /// For jobs requiring multiple passes on the hardware, e.g., BCE jobs + /// that move more data than the PBA supports with a single pass, this + /// time is the time the first hardware pass started. In this case the + /// interval time computed as the difference with \a start_time includes + /// interrupt handling overhead required to process the completion/restart + /// of intermediate passes. + /// + /// This timestamp is inserted into the request by the generic routine + /// async_request_run(). + SsxTimebase start_time; + + /// The time the job finished on the hardware. + /// + /// This timestamp is inserted into the request by the generic routine + /// async_handler(). + SsxTimebase end_time; + + /// A semaphore for thread-mode requests to block on if desired. + SsxSemaphore sem; + + /// A timer used for timeout management + SsxTimer timer; + + /// The engine queue the request is/was scheduled on + struct AsyncQueue *queue; + + /// The "virtual" run method for the class + AsyncRunMethod run_method; + + /// The "virtual" error handler method for the class + AsyncErrorMethod error_method; + + /// The routine Called (or deferred) with the \a arg parameter once + /// the engine has completed the request. + /// + /// The callback may be NULL (0), in which case the \a arg parameter + /// is ignored, and the only status available to the requestor is the + /// request \a state. + AsyncRequestCallback callback; + + /// The argument of the callback + void *arg; + + /// The timeout value + /// + /// AsyncRequest objects with \a timeout other than SSX_WAIT_FOREVER are + /// governed by a private watchdog timer that will cancel a queued job or + /// kill a running job if the hardware operation does not complete before + /// it times out. + SsxInterval timeout; + + /// The return value of the callback (if any) is stored here + /// + /// The success or failure of the callback is recorded here; it is not + /// recorded in the \a state variable. If there is no callback this field + /// will always read as 0 at job completion. + int callback_rc; + + /// Options controlling request processing + uint8_t options; + + /// The current state of the request + /// + /// This field is declared volatile because applications may poll this + /// field which is set asynchronously by the async interrupt handlers, and + /// optimizing compilers transform polling a variable into an infinite + /// loop if the variable is not set on the initial test! + volatile uint8_t state; + + /// The state of the request when the request was aborted + /// + /// This field is valid when the state of the request is read as + /// ASYNC_REQUEST_STATE_FAILED, ASYNC_REQUEST_STATE_CANCELLED, or + /// ASYNC_REQUEST_STATE_TIMED_OUT. This field records the state of the + /// job when it was forceably cancelled or killed either due to the + /// application's request or due to a hardware error or timeout. + uint8_t abort_state; + + /// The completion state of the request. + /// + /// This is the state that will be reported when the request completes and + /// the callback has been run. Normally this is + /// ASYNC_REQUEST_STATE_COMPLETE. However the specification requires that + /// even jobs that terminate due to an errors, timeouts or being cancelled + /// or killed must run their callbacks. In this case this variable will be + /// set to ASYNC_REQUEST_STATE_FAILED, ASYNC_REQUEST_STATE_TIMED_OUT, + /// ASYNC_REQUEST_STATE_CANCELLED or ASYNC_REQUEST_STATE_KILLED + /// respectively. + uint8_t completion_state; + +} AsyncRequest; + + +int +async_request_create(AsyncRequest *request, + struct AsyncQueue *queue, + AsyncRunMethod run_method, + AsyncErrorMethod error_method, + SsxInterval timeout, + AsyncRequestCallback callback, + void *arg, + int options); + +void +async_request_finalize(AsyncRequest* request); + + +/// Check that an asynchronous request is idle +/// +/// \param request An initialized request +/// +/// A request is considered idle if it is not attached to any of the +/// asynchronous request queues, \e or the request has terminated with an +/// error. This includes requests that have never been scheduled, have +/// completed or been cancelled or killed. Only idle requests can be +/// rescheduled. +/// +/// \retval 0 The request is not idle +/// +/// \retval 1 The request is idle + +static inline int +async_request_is_idle(AsyncRequest *request) +{ + return (request->state & ASYNC_REQUEST_IDLE_GROUP) != 0; +} + + +/// Check an asynchronous request for successful completion +/// +/// \param request A request that had been previosuly scheduled +/// +/// Note that a request is not considered complete until both the engine job +/// has finshed without error and any callback has run to completion. Thus +/// jobs that have error-ed out, been cancelled or killed will be idle (and +/// rescheduleable), but not complete. +/// +/// \retval 0 The request has not yet completed successfully +/// +/// \retval 1 The request has completed successfully. + +static inline int +async_request_completed(AsyncRequest *request) +{ + return (request->state == ASYNC_REQUEST_STATE_COMPLETE); +} + + +int +async_request_timestamps_get(AsyncRequest* request, + SsxTimebase* start_time, + SsxTimebase* end_time); + +int +async_request_latency(AsyncRequest* request, SsxTimebase* latency); + + +void +async_request_printk(AsyncRequest *request); + + +#endif /* __ASSEMBLER__ */ + + +//////////////////////////////////////////////////////////////////////////// +// AsyncQueue +//////////////////////////////////////////////////////////////////////////// + +#define ASYNC_QUEUE_STATE_IDLE 0x00 +#define ASYNC_QUEUE_STATE_RUNNING 0x01 +#define ASYNC_QUEUE_STATE_ERROR 0x02 + +#ifndef __ASSEMBLER__ + +/// A generic asynchronous request queue +/// +/// Note: This structure is normally not manipulated directly by application +/// code, but only by the device drivers. +/// +/// Request queues support 2 priorities - Normal and High. Normal priority +/// requests are queued FIFO order, while high-priority requests are queued +/// LIFO order. Requests queued with high priority always execute before any +/// queued with normal priority, however there is no concept of preemption of +/// a currently running request. +/// +/// Because high-priority requests are queued in LIFO order, multiple +/// high-priority jobs will run in the reverse order from which they were +/// enqueued. + +typedef struct AsyncQueue { + + /// The sentinel of the request queue + SsxDeque deque; + + /// The currently running request, or NULL (0) + AsyncRequest* current; + + /// The engine associated with the queue. + AsyncEngine engine; + + /// The state of the queue + uint8_t state; + +} AsyncQueue; + + +int +async_queue_create(AsyncQueue* queue, AsyncEngine engine); + +void +async_handler(AsyncQueue* queue); + +int +async_request_schedule(AsyncRequest* request); + +void +async_error_handler(AsyncQueue* queue, uint8_t completion_state); + +#endif /* __ASSEMBLER__ */ + + +//////////////////////////////////////////////////////////////////////////// +// Async Initialization +//////////////////////////////////////////////////////////////////////////// + +#ifndef __ASSEMBLER__ + +void +async_edge_handler_setup(SsxIrqHandler handler, + void *arg, + SsxIrqId irq, + int priority); + +void +async_level_handler_setup(SsxIrqHandler handler, + void *arg, + SsxIrqId irq, + int priority, + int polarity); + +void +async_callbacks_initialize(SsxIrqId irq); + + +#endif // __ASSEMBLER__ + +//////////////////////////////////////////////////////////////////////////// +// GpeRequest +//////////////////////////////////////////////////////////////////////////// + +#ifndef __ASSEMBLER__ + +struct GpeQueue; + +/// GPE FFDC +typedef struct { + uint32_t func_id; + int32_t ipc_rc; + int xir_dump_rc; + occhw_xir_dump_t xir_dump; +} GpeFfdc; + +/// A request to run a GPE command +/// +/// A GPE request extends the generic AsyncRequest request by the addition of +/// several fields required for running a job on a GPE +/// including the program parameter for the routine. +/// +/// As long as the request is known to be idle the application is free to +/// change the \a parameter value between executions of the GPE command, +/// e.g., to do ping-pong buffer management. None of the other fields should +/// be directly modified by the application. + +typedef struct { + + /// The generic request + AsyncRequest request; + + /// Error information collected by the 405 + GpeFfdc ffdc; + + /// The targeted IPC function ID for this request + ipc_func_enum_t targeted_func_id; + + /// A pointer to any command data that is used by the GPE or + /// returned by the GPE. + void* cmd_data; + +} GpeRequest; + +int +gpe_run_method(AsyncRequest* request); + +int +gpe_error_method(AsyncRequest* request); + +int +gpe_request_create(GpeRequest* request, + struct GpeQueue* queue, + ipc_func_enum_t func_id, + void* cmd_data, + SsxInterval timeout, + AsyncRequestCallback callback, + void *arg, + int options); + + +/// See async_request_schedule() for documentation. +static inline int +gpe_request_schedule(GpeRequest* request) +{ + return async_request_schedule((AsyncRequest *)request); +} + +//////////////////////////////////////////////////////////////////////////// +// GpeQueue +//////////////////////////////////////////////////////////////////////////// + + +/// A GPE engine queue +/// +/// A GPE queue consists of a generic AsyncQueue to manage jobs on the +/// engine. + +typedef struct GpeQueue { + + /// The generic request queue - the "base class" + AsyncQueue queue; + + /// The IPC target_id + uint8_t ipc_target_id; + + /// Pointer to an IPC command message + ipc_async_cmd_t *ipc_cmd; + +} GpeQueue; + + +int +gpe_queue_create(GpeQueue *queue, + int engine); + +#endif /* ASSEMBLER */ + +//////////////////////////////////////////////////////////////////////////// +// PBA FFDC Structures +//////////////////////////////////////////////////////////////////////////// + +#ifndef __ASSEMBLER__ + +/// Common FFDC collected for each PBA subunit in the event that a particular +/// subunit is implicated in an error. This structure contains generic +/// data that may be useful in diagnosing any PBA error. Instances of this +/// structure are embedded in higher-level structures for the PBA bridge, BCE +/// engines, and PBAX mechanism. + +typedef struct { + + /// FFDC from the PLB (OCI) arbiter + OciFfdc oci_ffdc; + + /// The PBA MODE register + pba_mode_t mode; + + /// The PBA FIR + pba_fir_t fir; + + /// PBA Error Report 0 + pba_errpt0_t errpt0; + + /// PBA Error Report 1 + pba_errpt1_t errpt1; + + /// PBA Error Report 2 + pba_errpt2_t errpt2; + + /// PBA Read Buffer Valid Status + pba_rbufvaln_t rbufval[PBA_READ_BUFFERS]; + + /// PBA Write Buffer Valid Status + pba_wbufvaln_t wbufval[PBA_WRITE_BUFFERS]; + + /// PBA BARs + pba_barn_t bar[PBA_BARS]; + + /// PBA BAR masks + pba_barmskn_t barmsk[PBA_BARS]; + + /// Error status - non-0 if this structure contains error data + int error; + +} PbaCommonFfdc; + + +/// FFDC collected for generic PBA bridge errors +/// +/// These types of errors will almost certainly be attributable to the +/// PORE-SLW or PORE-GPE, since the OCC is normally not allowed to perform +/// direct bridge operations after OCC initialization. This structure extends +/// the common PBA FFDC with information on how the slaves were programmed. + +typedef struct { + + /// Common FFDC + PbaCommonFfdc common; + + /// PBA Slave reset register + pba_slvrst_t slvrst; + + /// PBA Slave control registers + pba_slvctln_t slvctl[PBA_SLAVES]; + +} PbaBridgeFfdc; + + +/// FFDC collected for the PBA BCUE/BCDE +/// +/// The BCDE/BCUE control and status registers have identical layouts. +/// Similar to the way the drivers are coded, the BCDE register forms are used +/// to declare the structures. + +typedef struct { + + /// Common FFDC + PbaCommonFfdc common; + + /// BCE control register + pba_bcde_ctl_t ctl; + + /// BCE setup register + pba_bcde_set_t set; + + /// BCE PowerBus setup register + pba_bcde_pbadr_t pbadr; + + /// BCE status register + pba_bcde_stat_t stat; + +} BceFfdc; + + +/// FFDC collected for PBAX send errors + +typedef struct { + + /// Common FFDC + PbaCommonFfdc common; + + /// PBAX configuration register + pba_xcfg_t xcfg; + + /// PBAX send transaction register + pba_xsndtx_t xsndtx; + + /// PBAX send status register + pba_xsndstat_t xsndstat; + +} PbaxSendFfdc; + + +/// FFDC collected for PBAX receive errors + +typedef struct { + + /// Common FFDC + PbaCommonFfdc common; + + /// PBAX configuration register + pba_xcfg_t xcfg; + + /// PBAX receive status register + pba_xrcvstat_t xrcvstat; + + /// PBAX push base registers + pba_xshbrn_t xshbrn[PBAX_QUEUES]; + + /// PBAX push control/status registers + pba_xshcsn_t xshcsn[PBAX_QUEUES]; + +} PbaxReceiveFfdc; + + +/// A single global structure containing FFDC for all PBA functions + +typedef struct { + + /// FFDC for the generic bridge + PbaBridgeFfdc bridge; + + /// FFDC for the BCDE + BceFfdc bcde; + + /// FFDC for the BCUE + BceFfdc bcue; + + /// FFDC for PBAX send + PbaxSendFfdc pbax_send; + + /// FFDC for PBAX receive + PbaxReceiveFfdc pbax_receive; + +} PbaUnitFfdc; + +#endif // __ASSEMBLER__ + +//////////////////////////////////////////////////////////////////////////// +// BceRequest +//////////////////////////////////////////////////////////////////////////// + +#ifndef __ASSEMBLER__ + +struct BceQueue; + +/// A request to move data through the PBA BCUE or BCDE +/// +/// From a programming and control perspective the Block Copy Download Engine +/// (BCDE) and Upload Engine (BCUE) are identical - they simply move a given +/// number of 128-byte cache lines from/to system memory to/from SRAM. +/// +/// Although the PBA BCE hardware can only move a maximum of 4KB at a time +/// through the channel, the software layer allows any amount of data to be +/// moved as a single request. + +typedef struct { + + /// The generic request + AsyncRequest request; + + /// FFDC collected in the event of an error in the request + BceFfdc ffdc; + + /// Initial bridge address for read (BCDE) or write (BCUE) data. This + /// field is not modified by the drivers. + uint32_t bridge_address; + + /// Initial OCI address for read (BCUE) or write (BCDE) data. This + /// field is not modified by the drivers. + uint32_t oci_address; + + /// Number of bytes to move. This field is not modified by the drivers. + /// + /// Note that the PBA moves data in sets of 128 bytes (the PowerBus + /// cache line size). This field will always be a multiple of 128. + size_t bytes; + + /// The next bridge address for read (BCDE) or write (BCUE) data. This + /// field is modified by the drivers as each maximum 4K block is copied. + uint32_t next_bridge_address; + + /// Initial OCI address for read (BCUE) or write (BCDE) data. This + /// field is modified by the drivers as each maximum 4K block is copied. + uint32_t next_oci_address; + + /// The number of bytes remaining to be moved. + size_t remaining; + + /// The extended address, as a 64-bit PowerBus address + /// + /// Bits 23:36 of the field define bits 23:36 of the final PowerBus + /// address, subject to the final mask selection. This field is cleared by + /// the default constructor, and must be explicitly set if an extended + /// address is needed. + pba_extended_address_t extended_address; + +} BceRequest; + +int +bce_request_create(BceRequest *request, + struct BceQueue *queue, + uint32_t bridge_address, + uint32_t oci_address, + size_t bytes, + SsxInterval timeout, + AsyncRequestCallback callback, + void *arg, + int options); + +#endif /* __ASSEMBLER__ */ + + +//////////////////////////////////////////////////////////////////////////// +// BceQueue +//////////////////////////////////////////////////////////////////////////// + +// NB: This assignment ordering is assumed by static initialization code. +// These constants are used as array indices. + +#define BCE_ENGINE_BCDE 0 +#define BCE_ENGINE_BCUE 1 + +#define BCE_ENGINES 2 + +#ifndef __ASSEMBLER__ + + +/// A PBA Block Copy Engine queue +/// +/// A PBA block copy request queue consists of a generic AsyncQueue to manage +/// jobs on the engine and the PBA engine id. The BCDE and BCUE can use the +/// same driver because their control interfaces are identical. + +typedef struct BceQueue { + + /// The generic request queue + AsyncQueue queue; + + /// The engine id of the BCE engine for control register address lookup + int engine; + +} BceQueue; + + +int +bce_queue_create(BceQueue *queue, + int engine); + + +int +bce_request_schedule(BceRequest *request); + +#endif /* __ASSEMBLER__ */ + + +//////////////////////////////////////////////////////////////////////////// +// PbaxRequest +//////////////////////////////////////////////////////////////////////////// + +#ifndef __ASSEMBLER__ + +struct PbaxQueue; + +/// A request to move data through a PBAX circular queue +/// +/// The PBAX circular queues are modeled after the OCB circular queues. The +/// queues are named and documented in the hardware specifications from the +/// perspective of the PowerBus as "PUSH" queues. Here we name and document +/// the queues from the perspective of OCC firmware as "READ" queues. Note +/// that there are no PBAX "WRITE" queues. The PBAX write mechanism handles +/// only a single datum at a time and has no interrupt. PBAX writes are +/// handled by the pbax_send() and _pbax_send() APIs. +/// +/// The PBAX queues support data buffers of from 8 to 256 bytes with an 8-byte +/// granularity. The PBAX hardware requires that the data buffers be aligned +/// to a power-of-2 boundary greater than or equal to the buffer size. +/// The async drivers allow specification of any multiple of 8 bytes to read +/// and handle all of the queue and split-request management. +/// +/// Note : As long as the PbaxRequest is idle, the application is free to +/// directly change the \a data and \a bytes fields to read/write different +/// numbers of bytes to/from different locations the next time the request is +/// scheduled. +/// +/// The PBAX queues support both 'lazy' and 'aggressive' interrupt protocols. +/// The aggressive protocols generate interrupts whenever read data is +/// available. The lazy protocols require a full read buffer to trigger an +/// interrupt. A lazy read protocol with a buffer size > 1 demands that +/// communcations always consist of a known number of doublewords equal to the +/// buffer size. For free-form communications an aggressive read protocol must +/// be used. + +typedef struct { + + /// The generic request + AsyncRequest request; + + /// Initial pointer to the data area to hold read data. + /// + /// This field is not modified by the drivers. The application may modify + /// this field any time the PbaxRequest is idle. + uint64_t *data; + + /// Number of bytes to move, which must be a multiple of 8. + /// + /// This field is not modified by the drivers. The application may modify + /// this field any time the PbaxRequest is idle. + size_t bytes; + + /// Pointer to where to put the next read data. + /// + /// The application should never modify this field. + void* current; + + /// Number of bytes remaining to be moved. + /// + /// The application should never modify this field. + size_t remaining; + +} PbaxRequest; + + +int +pbax_request_create(PbaxRequest *request, + struct PbaxQueue *queue, + uint64_t *data, + size_t bytes, + SsxInterval timeout, + AsyncRequestCallback callback, + void *arg, + int options); + + +int +pbax_request_schedule(PbaxRequest *request); + +#endif /* __ASSEMBLER__ */ + + +//////////////////////////////////////////////////////////////////////////// +// PbaxQueue +//////////////////////////////////////////////////////////////////////////// + +// NB: This assignment ordering is assumed by static initialization code in +// occhw_async.c - these constants are used as array indices. + +#define PBAX_ENGINE_PUSH0 0 +#define PBAX_ENGINE_PUSH1 1 + +#define PBAX_ENGINES 2 + +#ifndef __ASSEMBLER__ + +extern const SsxAddress pba_xshcsn[]; +extern const SsxAddress pba_xshbrn[]; +extern const SsxAddress pba_xshincn[]; + + +/// A PBAX Circular buffer queue +/// +/// A PBAX circular buffer queue consists of a generic AsyncQueue to manage +/// jobs on the engine, a pointer to the data area and the PBAX engine id. + +typedef struct PbaxQueue { + + /// The generic request queue + AsyncQueue queue; + + /// The base of the circular queue data area + /// + /// This data area must satisfy stringent alignment constraints; See the + /// documentation for PbaxRequest. + uint64_t *cq_base; + + /// The number of 8-byte entries in the queue. + /// + /// This is included here to simplify APIs; The length is also encoded in + /// the PBAXSHCSn register for the queue. + size_t cq_entries; + + /// The interrupt protocol implemented by the queue. + /// + /// This will either be PBAX_INTERRUPT_PROTOCOL_LAZY or + /// PBAX_INTERRUPT_PROTOCOL_AGGRESSIVE. + int protocol; + + /// The engine id for lookup of OCI control register addresses. + int engine; + + /// The IRQ associated with normal completion on the engine + /// + /// \todo Due to header reference ordering we can't define this as SsxIrqId + uint8_t irq; + +} PbaxQueue; + + +int +pbax_queue_create(PbaxQueue *queue, + int engine, + uint64_t *cq_base, + size_t cq_entries, + int protocol); + +int +pbax_queue_disable(PbaxQueue *queue); + +int +pbax_queue_enable(PbaxQueue *queue); + +int +pbax_read(PbaxQueue* queue, void* buf, size_t bytes, size_t* read); + +#endif /* __ASSEMBLER__ */ + + +//////////////////////////////////////////////////////////////////////////// +// OcbRequest +//////////////////////////////////////////////////////////////////////////// + +#ifndef __ASSEMBLER__ + +/// OCB FFDC +/// +/// This structure is used to store FFDC in two cases. 1) When an error can be +/// reasonably attributed to an OCB channel under the control of OCC, and 2) +/// general errors signalled by the OCB direct bridge. Due to the myriad ways +/// that OCB channels can be used the FFDC for OCB direct bridge errors is +/// stored globally rather than with particular requests or queues. +/// +/// The OcbFfdc includes the PLB arbiter PEARL and PESR register state. +/// This state is only collected if the error status indicates an OCI timeout +/// for this channel. +/// +/// Any error reasonably attributable to a channel will capture the FFDC and +/// then disable the channel since any further communication through the +/// channel must be considered compromised. + +typedef struct { + + /// FFDC from the OCI (PLB) arbiter + OciFfdc oci_ffdc; + + /// A copy of the OCB OCC_LFIR register at the time of the error + ocb_occlfir_t fir; + + /// A copy of the OCB Control/Status register for the channel at the time + /// of the error. + /// + /// This field will be set to 0 for generic OCB bridge errors + ocb_ocbcsrn_t csr; + + /// A copy of the OCB Stream Push Base [n] Register at the time of the + /// failure + /// + /// This field will be set to 0 for generic OCB bridge errors + ocb_ocbshbrn_t shbr; + + /// A copy of the OCB Stream Push Control/Status [n] Register at the time + /// of the failure + /// + /// This field will be set to 0 for generic OCB bridge errors + ocb_ocbshcsn_t shcs; + + /// A copy of the OCB Stream Pull Base [n] Register at the time of the + /// failure + /// + /// This field will be set to 0 for generic OCB bridge errors + ocb_ocbslbrn_t slbr; + + /// A copy of the OCB Stream Pull Control/Status [n] Register at the time + /// of the failure + /// + /// This field will be set to 0 for generic OCB bridge errors + ocb_ocbslcsn_t slcs; + + /// Is this record valid (does it contain captured error information)? + int error; + +} OcbFfdc; + + +/// OCB Unit FFDC +/// +/// Contains FFDC structures for each channel and the direct bridge + +typedef struct { + + OcbFfdc channel[OCB_INDIRECT_CHANNELS]; + OcbFfdc bridge; + +} OcbUnitFfdc; + + +/// Global FFDC for OCB + +extern OcbUnitFfdc G_ocb_ffdc; + + +struct OcbQueue; + +/// A request to move data through an OCB circular queue +/// +/// The OCB circular queues are named and documented in the hardware specs +/// from the perspective of the PIB - "PUSH" and "PULL" queues. Here we name +/// and document the queues from the perspective of OCC firmware - "READ" and +/// "WRITE" queues. The request is generic and becomes either a read or write +/// request simply by which queue it is scheduled on. +/// +/// The circular queues only support 8-byte granularity, require that the data +/// areas be 8- or 32-byte aligned, and only support up to 256 byte +/// queues. However the async drivers allow specification of any multiple of 8 +/// bytes to read or write, and handle all of the queue and split-request +/// management. +/// +/// Note : As long as the OcbRequest is idle, the application is free to +/// directly change the \a data and \a bytes fields to read/write different +/// numbers of bytes to/from different locations the next time the request is +/// scheduled. +/// +/// OCB requests always complete in an interrupt context. The OCB queues +/// support both 'lazy' and 'aggressive' interrupt protocols. The aggressive +/// protocols generate interrupts whenever read data is available or write +/// space is available. The lazy protocols require a full read buffer or +/// empty write buffer to trigger an interrupt. A lazy read protocol with a +/// buffer size > 1 demands that communcations always consist of a known +/// number of doublewords. For free-form communications an aggressive read +/// protocol must be used. On average, an aggressive write protocol will +/// shorten blocking periods of writers, and can guarantee that data is in the +/// queue whenever data is avilable. Lazy write protocols will be preferred +/// when reducing interrupt overhead is more important than reducing blocking +/// time. +/// +/// Completion of write requests does not guarantee that the communication +/// partner has received the data, but simply that the data has been queued +/// for reception and the caller may reuse/refill the data buffer if required. + +typedef struct { + + /// The generic request + AsyncRequest request; + + /// Initial pointer to the data to be moved for write data, or the + /// data area to hold read data. This field is not modified by the drivers. + uint64_t *data; + + /// Number of bytes to move. This field is not modified by the drivers. + size_t bytes; + + /// Pointer to data yet to be moved for write, or where to put the next + /// read data. + uint64_t* current; + + /// Number of bytes remaining to be moved. + size_t remaining; + +} OcbRequest; + + +int +ocb_request_create(OcbRequest *request, + struct OcbQueue *queue, + uint64_t *data, + size_t bytes, + SsxInterval timeout, + AsyncRequestCallback callback, + void *arg, + int options); + + +int +ocb_request_schedule(OcbRequest *request); + +#endif /* __ASSEMBLER__ */ + + +//////////////////////////////////////////////////////////////////////////// +// OcbQueue +//////////////////////////////////////////////////////////////////////////// + +// NB: This assignment ordering is assumed by static initialization code in +// occhw_async.c - these constants are used as array indices. The code also +// assumes this ordering for the access of G_ocb_ocbsesn[], and for +// determining whether the engine is a PUSH or PULL queue. +// Note: push/pull queues for channel 3 have been deleted + +#define OCB_ENGINE_PUSH0 0 +#define OCB_ENGINE_PULL0 1 +#define OCB_ENGINE_PUSH1 2 +#define OCB_ENGINE_PULL1 3 +#define OCB_ENGINE_PUSH2 4 +#define OCB_ENGINE_PULL2 5 +#define OCB_ENGINE_PUSH3 6 +#define OCB_ENGINE_PULL3 7 + +#define OCB_ENGINES 8 + +#ifndef __ASSEMBLER__ + +/// An OCB Circular buffer queue +/// +/// A OCB circular buffer queue consists of a generic AsyncQueue to manage +/// jobs on the engine, a pointer to the data area and the OCB engine id. + +typedef struct OcbQueue { + + /// The generic request queue + AsyncQueue queue; + + /// The base of the circular queue data area - must be 8-byte aligned + uint64_t *cq_base; + + /// The length of the queue in terms of the number of 8-byte entries in + /// the queue. + /// + /// This is for informational purposes only. The length is also encoded + /// in the OCBSxCSn register for the queue. + size_t cq_length; + + /// The engine id for lookup of OCI control register addresses. + int engine; + + /// The IRQ associated with normal completion on the engine + /// + /// \todo Due to header reference ordering we can't define this as SsxIrqId + uint8_t irq; + +} OcbQueue; + + +int +ocb_queue_create(OcbQueue *queue, + int engine, + uint64_t *cq_base, + size_t cq_length, + int protocol); + +#endif /* __ASSEMBLER__ */ + + +//////////////////////////////////////////////////////////////////////////// +// Miscellaneous/Initialization +//////////////////////////////////////////////////////////////////////////// + +// Error codes + +#define ASYNC_INVALID_OBJECT_REQUEST 0x00279600 +#define ASYNC_INVALID_OBJECT_SCHEDULE 0x00279601 +#define ASYNC_INVALID_OBJECT_OCB_REQUEST 0x00279602 +#define ASYNC_INVALID_OBJECT_OCB_QUEUE 0x00279603 +#define ASYNC_INVALID_OBJECT_PBAX_REQUEST 0x00279604 +#define ASYNC_INVALID_OBJECT_PBAX_DISABLE 0x00279605 +#define ASYNC_INVALID_OBJECT_PBAX_QUEUE 0x00279606 +#define ASYNC_INVALID_OBJECT_BCE_REQUEST 0x00279607 +#define ASYNC_INVALID_OBJECT_BCE_QUEUE 0x00279608 +#define ASYNC_INVALID_OBJECT_GPE_REQUEST 0x00279609 +#define ASYNC_INVALID_OBJECT_GPE_QUEUE 0x0027960a +#define ASYNC_INVALID_ARGUMENT 0x00279610 +#define ASYNC_INVALID_ARGUMENT_OCB_READ 0x00279611 +#define ASYNC_INVALID_ARGUMENT_OCB_WRITE 0x00279612 +#define ASYNC_INVALID_ARGUMENT_OCB_QUEUE 0x00279613 +#define ASYNC_INVALID_ARGUMENT_OCB_QUEUE2 0x00279614 +#define ASYNC_INVALID_ARGUMENT_OCB_REQUEST 0x00279615 +#define ASYNC_INVALID_ARGUMENT_OCB_SCHEDULE 0x00279616 +#define ASYNC_INVALID_ARGUMENT_BCE_SCHEDULE 0x00279617 +#define ASYNC_INVALID_ARGUMENT_PBAX_READ 0x00279618 +#define ASYNC_INVALID_ARGUMENT_PBAX_REQUEST 0x00279619 +#define ASYNC_INVALID_ARGUMENT_PBAX_SCHEDULE 0x0027961a +#define ASYNC_INVALID_ARGUMENT_PBAX_QUEUE 0x0027961b +#define ASYNC_INVALID_ARGUMENT_GPE_REQUEST 0x0027961c +#define ASYNC_INVALID_ENGINE_OCB 0x0027961f +#define ASYNC_INVALID_ENGINE_PBAX 0x00279620 +#define ASYNC_INVALID_ENGINE_BCE 0x00279621 +#define ASYNC_INVALID_ENGINE_GPE 0x00279622 +#define ASYNC_INVALID_OPTIONS 0x00279624 +#define ASYNC_INVALID_ASSIGNMENT 0x00279625 +#define ASYNC_CALLBACK_PROTOCOL_UNSPECIFIED 0x00279626 +#define ASYNC_REQUEST_NOT_IDLE 0x00279627 +#define ASYNC_REQUEST_COMPLETE 0x00279629 +#define ASYNC_INVALID_TIMESTAMPS 0x0027962a +#define ASYNC_OCB_ERROR_READ_OLD 0x0027962b +#define ASYNC_OCB_ERROR_READ_NEW 0x0027962c +#define ASYNC_OCB_ERROR_WRITE_OLD 0x0027962d +#define ASYNC_OCB_ERROR_WRITE_NEW 0x0027962e +#define ASYNC_PBAX_ERROR_OLD 0x0027962f +#define ASYNC_PBAX_ERROR_NEW 0x00279630 +#define ASYNC_REQUEST_NOT_COMPLETE 0x00279631 + +// Panic codes + +#define ASYNC_GPE_FIXED_INVARIANT 0x00279633 +#define ASYNC_PHANTOM_INTERRUPT 0x00279634 +#define ASYNC_PHANTOM_INTERRUPT_OCB 0x00279635 +#define ASYNC_PHANTOM_INTERRUPT_BCE 0x00279636 +#define ASYNC_PHANTOM_INTERRUPT_PBAX 0x00279637 +#define ASYNC_SCOM_ERROR 0x00279638 +#define ASYNC_TIMEOUT_BUG 0x00279639 +#define ASYNC_INVALID_STATE 0x0027963a +#define ASYNC_PHANTOM_ERROR 0x0027963b +#define ASYNC_BUG_GPE_AT_CREATE 0x0027963c + +//////////////////////////////////////////////////////////////////////////// +// Global Data and Constants +//////////////////////////////////////////////////////////////////////////// + +#ifndef __ASSEMBLER__ + +// GPE Queues + +extern GpeQueue G_async_gpe_queue0; +extern GpeQueue G_async_gpe_queue1; +extern GpeQueue G_async_gpe_queue2; +extern GpeQueue G_async_gpe_queue3; + + +// OCB Queues and FFDC + +// OCB circular queue naming is confusing, because PUSH/PULL are defined from +// the perspective of the PIB. This driver uses the terms read/write +// respectively and represents the hardware from the perspective of code +// running on OCC. + +// OCB read/write queue lengths are defined in terms of doublewords, and must +// be in a small defined range. Two meaningful interrupt protocols are +// possible for a queue: 'LAZY' allows read/write queues to fill/empty +// respectively before an interrupt is signalled. 'AGRESSIVE' signals an +// interrupt whenever there is data to be read or a free slot to put write +// data. + +// In a production system the lengths and protocols will likely vary based on +// the particular application and the characteristics of the communication +// partner. By default the queues take on their maximum lengths. Read queues +// use the aggressive protocol which allows free-form communication. Write +// queues use lazy protoclols which reduces interrupt overhead. Different +// effective lengths and the interrupt protocols can be changed later if +// desired - assuming the queues are idle when the changes are made. + +#define OCB_PUSH_PULL_LENGTH_MIN 1 +#define OCB_PUSH_PULL_LENGTH_MAX 32 + +#define OCB_INTERRUPT_PROTOCOL_LAZY 0 +#define OCB_INTERRUPT_PROTOCOL_AGGRESSIVE 1 + +#define OCB_READ0_LENGTH OCB_PUSH_PULL_LENGTH_MAX +#define OCB_READ1_LENGTH OCB_PUSH_PULL_LENGTH_MAX +#define OCB_READ2_LENGTH OCB_PUSH_PULL_LENGTH_MAX +#define OCB_READ3_LENGTH OCB_PUSH_PULL_LENGTH_MAX + +#define OCB_WRITE0_LENGTH OCB_PUSH_PULL_LENGTH_MAX +#define OCB_WRITE1_LENGTH OCB_PUSH_PULL_LENGTH_MAX +#define OCB_WRITE2_LENGTH OCB_PUSH_PULL_LENGTH_MAX +#define OCB_WRITE3_LENGTH OCB_PUSH_PULL_LENGTH_MAX + +#define OCB_READ0_PROTOCOL OCB_INTERRUPT_PROTOCOL_AGGRESSIVE +#define OCB_READ1_PROTOCOL OCB_INTERRUPT_PROTOCOL_AGGRESSIVE +#define OCB_READ2_PROTOCOL OCB_INTERRUPT_PROTOCOL_AGGRESSIVE +#define OCB_READ3_PROTOCOL OCB_INTERRUPT_PROTOCOL_AGGRESSIVE + +#define OCB_WRITE0_PROTOCOL OCB_INTERRUPT_PROTOCOL_LAZY +#define OCB_WRITE1_PROTOCOL OCB_INTERRUPT_PROTOCOL_LAZY +#define OCB_WRITE2_PROTOCOL OCB_INTERRUPT_PROTOCOL_LAZY +#define OCB_WRITE3_PROTOCOL OCB_INTERRUPT_PROTOCOL_LAZY + +extern OcbUnitFfdc G_ocb_ffdc; + +extern OcbQueue G_ocb_read_queue[]; +extern OcbQueue G_ocb_write_queue[]; + +extern uint64_t G_ocb_read0_buffer[]; +extern uint64_t G_ocb_read1_buffer[]; +extern uint64_t G_ocb_read2_buffer[]; +extern uint64_t G_ocb_read3_buffer[]; + +extern uint64_t G_ocb_write0_buffer[]; +extern uint64_t G_ocb_write1_buffer[]; +extern uint64_t G_ocb_write2_buffer[]; +extern uint64_t G_ocb_write3_buffer[]; + + +// PBA Queues + +/// Block Copy Upload (OCI -> PowerBus) Engine job queue; +extern BceQueue G_pba_bcue_queue; + +/// Block Copy Download Engine (PowerBus -> OCI) job queue; +extern BceQueue G_pba_bcde_queue; + + +// PBAX Queues + +// PBAX circular queue naming is confusing, because "PUSH" is defined from the +// perspective of the PowerBus. This driver uses the term "READ" and +// represents the hardware from the perspective of code running on OCC. + +// PBAX read queue lengths are defined in terms of 8-byte doublewords, and +// must be in a small defined range. Two meaningful interrupt protocols are +// possible for a queue: 'LAZY' allows read queues to fill before an interrupt +// is signalled. 'AGRESSIVE' signals an interrupt whenever there is data to +// be read. + +// In a production system the lengths and protocols will likely vary based on +// the particular application and the characteristics of the communication +// partner. By default the queues take on their maximum lengths. Read queues +// use the aggressive protocol which allows free-form communication. Different +// effective lengths and the interrupt protocols can be changed later if +// desired - assuming the queues are idle when the changes are made. + +#define PBAX_PUSH_LENGTH_MIN 1 +#define PBAX_PUSH_LENGTH_MAX 32 + +#define PBAX_INTERRUPT_PROTOCOL_LAZY 0 +#define PBAX_INTERRUPT_PROTOCOL_AGGRESSIVE 1 + +#define PBAX_READ0_LENGTH PBAX_PUSH_LENGTH_MAX +#define PBAX_READ1_LENGTH PBAX_PUSH_LENGTH_MAX + +#define PBAX_READ0_PROTOCOL PBAX_INTERRUPT_PROTOCOL_AGGRESSIVE +#define PBAX_READ1_PROTOCOL PBAX_INTERRUPT_PROTOCOL_AGGRESSIVE + +/// Job queues for the PBAX circular buffers +extern PbaxQueue G_pbax_read_queue[]; + + +// PBAX read buffers must be cache-line aligned since they are invalidated, +// and furthermore must be aligned to the next higher power-of-two of their +// lengths. Some minor efficiencies could probably be obtained by a policy +// that buffers be held in non-cacheable storage (and subsequent modifications +// to the device driver code). +// +// Due to linker restrictions, only initialized data areas can be aligned. + + +#define _PBAX_ALIGN_(length) \ + (((length) <= 4) ? 32 : \ + (((length) <= 8) ? 64 : \ + (((length) <= 16) ? 128 : 256))) + +#define PBAX_CQ_READ_BUFFER(buffer, length) \ + uint64_t buffer[length] \ + __attribute__ ((aligned (_PBAX_ALIGN_(length)))) = {0} + + +extern uint64_t G_pbax_read0_buffer[]; +extern uint64_t G_pbax_read1_buffer[]; + + +// Initialization APIs + +void +async_gpe_initialize(GpeQueue *queue, int engine); + +void +async_bce_initialize(BceQueue *queue, int engine, SsxIrqId irq); + + +void +async_ocb_initialize(OcbQueue *queue, int engine, + uint64_t *buffer, size_t length, int protocol); + + +void +async_pbax_initialize(PbaxQueue *queue, int engine, SsxIrqId irq, + uint64_t *buffer, size_t length, int protocol); + + +void +async_initialize(); + +#endif /* __ASSEMBLER__ */ + +#endif /* __OCCHW_ASYNC_H__ */ |