summaryrefslogtreecommitdiffstats
path: root/src/ssx/occhw/occhw_scom.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ssx/occhw/occhw_scom.c')
-rw-r--r--src/ssx/occhw/occhw_scom.c424
1 files changed, 424 insertions, 0 deletions
diff --git a/src/ssx/occhw/occhw_scom.c b/src/ssx/occhw/occhw_scom.c
new file mode 100644
index 0000000..743d0dc
--- /dev/null
+++ b/src/ssx/occhw/occhw_scom.c
@@ -0,0 +1,424 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/ssx/occhw/occhw_scom.c $ */
+/* */
+/* 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 */
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2014
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file occhw_scom.c
+/// \brief procedures and support for scom operations
+///
+/// <b> SCOM Operations </b>
+///
+/// The maximum latency of a PIB operation has a hard upper
+/// bound derived from the hardware implementation. The putscom()/getscom()
+/// drivers here take advantage of this upper bound and implement tight
+/// timeouts, enforced by polling the timebase while waiting for the SCOM
+/// operations to complete.
+///
+/// The latencies are small enough and so well understood that the
+/// getscom()/putscom() procedures operate in SSX_CRITICAL critical
+/// sections. There should be no problem for thread-based procedures to be
+/// written using getscom()/putscom() directly - in fact for short procedures
+/// it may be less overhead to use getscom()/putscom() than queuing a PORE-GPE
+/// program. All mainline procedures used by hard real-time code should
+/// remain as PORE-GPE programs however.
+///
+/// SCOM operations return non-zero error codes that may or may not indicate
+/// an actual error, depending on which SCOM is being accessed. This error
+/// code (or 0 for success) is returned as the value of getscom()/putscom().
+/// The error severity increases with the severity of the error:
+/// \code
+///
+/// #define PCB_ERROR_NONE 0
+/// #define PCB_ERROR_RESOURCE_OCCUPIED 1
+/// #define PCB_ERROR_CHIPLET_OFFLINE 2
+/// #define PCB_ERROR_PARTIAL_GOOD 3
+/// #define PCB_ERROR_ADDRESS_ERROR 4
+/// #define PCB_ERROR_CLOCK_ERROR 5
+/// #define PCB_ERROR_PACKET_ERROR 6
+/// #define PCB_ERROR_TIMEOUT 7
+/// \endcode
+///
+/// The default configuration variable SCOM_ERROR_LIMIT defines the maximum
+/// error code that will be returned - error codes above the limit (plus hard
+/// timeouts and other protocol errors) cause an immediate kernel panic. In
+/// the event of a non-0 error code, getscom() always sets the returned data
+/// to 0.
+///
+/// In addition to getscom()/putscom() that implement the above defined error
+/// protocols, the raw APIs _getscom()/_putscom() are also available and
+/// allow the application full control over timeouts and error handling on a
+/// SCOM-by-SCOM basis.
+///
+/// \bug Modify getscom/putscom to return the SSX error codes rather than
+/// 1-7.
+///
+/// \bug Implement and use a generic poll_with_timeout(f, arg, t)
+
+#include "ssx.h"
+#include "occhw_scom.h"
+#include "occhw_shared_data.h"
+
+////////////////////////////////////////////////////////////////////////////
+// SCOM
+////////////////////////////////////////////////////////////////////////////
+
+// Common SCOM polling loop with software timeout. The PMC is always polled
+// at least twice to guarantee that we always poll once after a timeout.
+
+static int
+poll_scom(SsxInterval timeout)
+{
+ SsxTimebase start;
+ int timed_out;
+ int rc;
+
+ start = ssx_timebase_get();
+ timed_out = 0;
+ do {
+ rc = ssx_irq_status_get(OCCHW_IRQ_IPI_SCOM);
+ if (!rc) {
+ break;
+ }
+ if (timed_out) {
+ rc = -SCOM_TIMEOUT_ERROR;
+ break;
+ }
+ timed_out =
+ ((timeout != SSX_WAIT_FOREVER) &&
+ ((ssx_timebase_get() - start) > timeout));
+ } while (1);
+ return rc;
+}
+
+
+/// A raw getscom() through the PMC OCI/PIB bridge
+///
+/// \param address A standard 32-bit SCOM address, including multicast
+/// addresses.
+///
+/// \param data Points to a container for the returned data.
+///
+/// \param timeout The software timeout as an SSX interval (timebase ticks),
+/// or the special value SSX_WAIT_FOREVER to indicate no software timeout.
+///
+/// This routine executes in an SSX_CRITICAL critical section.
+///
+/// Unlike most other APIs, this API returns both positive and negative error
+/// codes, as well as the 0 code for success. In the event of PCB errors, the
+/// returned \a data is obtained from the PMC O2P data registers. In the
+/// event of non-PCB errors, the caller \a data is not modified.
+///
+/// If the transaction experiences a software timeout (controlled by the \a
+/// timeout parameter) or a protocol error, the PMC PIB master will be left in
+/// a state in which it is illegal to perform further SCOM access through the
+/// PMC until the ongoing transaction is finished.
+///
+/// \retval 0 Success
+///
+///\ retval 1-7 A PCB error code. See \c pcb_common.h
+///
+/// \retval -SCOM_TIMEOUT_ERROR The software timeout specified by the \a
+/// timeout parameter expired before the transaction completed.
+///
+/// retval -SCOM_PROTOCOL_ERROR_GETSCOM_BUSY The PMC SCOM engine was busy when
+/// the call was made.
+
+int
+_getscom(uint32_t address, uint64_t *data, SsxInterval timeout)
+{
+ SsxMachineContext ctx;
+ int rc;
+ occhw_scom_cmd_t *scom_cmd = &OSD_PTR->scom_cmd;
+ occhw_scom_status_t scom_status;
+
+ do
+ {
+ if(address & OCCHW_SCOM_READ_MASK)
+ {
+ rc = -SCOM_INVALID_ADDRESS;
+ break;
+ }
+
+ ssx_critical_section_enter(SSX_CRITICAL, &ctx);
+
+ // Check for a transaction already ongoing
+ rc = ssx_irq_status_get(OCCHW_IRQ_IPI_SCOM);
+ if (rc) {
+ ssx_critical_section_exit(&ctx);
+ rc = -SCOM_PROTOCOL_ERROR_GETSCOM_BUSY;
+ break;
+ }
+
+ // Setup the write. The 'read' bit is set in the address.
+ scom_cmd->scom_status.status32 = OCCHW_SCOM_PENDING;
+ scom_cmd->scom_addr = address | OCCHW_SCOM_READ_MASK;
+
+ // Notify the GPE (by raising an interrupt) that a request is pending
+ ssx_irq_status_set(OCCHW_IRQ_IPI_SCOM, 1);
+
+ // Poll until completed or timed out
+ rc = poll_scom(timeout);
+
+ // Extract the data and status out of the scom command block
+ *data = scom_cmd->scom_data;
+ scom_status.status32 = scom_cmd->scom_status.status32;
+
+ ssx_critical_section_exit(&ctx);
+
+ if(!rc)
+ {
+ //check that the GPE updated the scom status. Normally,
+ //the gpe won't clear the interrupt until it has updated
+ //the status field. The exception is if the GPE gets
+ //reset.
+ if(scom_status.status32 == OCCHW_SCOM_PENDING)
+ {
+ rc = -SCOM_PROTOCOL_ERROR_GETSCOM_RST;
+ }
+ else
+ {
+ //The SIBRC field of the MSR is where we get the status for
+ //the last scom operation.
+ rc = scom_status.sibrc;
+ }
+ }
+
+ }while(0);
+ return rc;
+}
+
+
+/// getscom() through the PMC OCI/PIB bridge
+///
+/// \param address A standard 32-bit SCOM address, including multicast
+/// addresses.
+///
+/// \param data Points to a container for the returned data.
+///
+/// This routine executes in an SSX_CRITICAL critical section.
+///
+/// Unlike most other APIs, this API returns positive error
+/// codes, as well as the 0 code for success. In the event of PCB errors, the
+/// returned \a data is set to 0.
+///
+/// If the transaction experiences a software timeout (controlled by the \a
+/// timeout parameter), a protocol error, or a PCB error greater than the
+/// configuration constant SCOM_ERROR_LIMIT this routine causes a kernel
+/// panic. This may leave the PMC PIB master in a state in which it is illegal
+/// to perform further SCOM access through the PMC (until the ongoing
+/// transaction is finished.)
+///
+/// \retval 0 Success
+///
+///\ retval 1-7 A PCB error code. See \c pcb_common.h
+
+int
+getscom(uint32_t address, uint64_t *data)
+{
+ int rc;
+
+ rc = _getscom(address, data, SCOM_TIMEOUT);
+ if (rc == 0) {
+ return 0;
+ }
+
+ if ((rc > 0) && (rc <= SCOM_ERROR_LIMIT)) {
+ *data = 0;
+ } else {
+
+ //printk("getscom(0x%08x, %p) : Failed with error %d\n",
+ // address, data, rc);
+
+ if (rc > 0) {
+ switch (rc) {
+ case 1: SSX_PANIC(SCOM_PCB_ERROR_1_GETSCOM); break;
+ case 2: SSX_PANIC(SCOM_PCB_ERROR_2_GETSCOM); break;
+ case 3: SSX_PANIC(SCOM_PCB_ERROR_3_GETSCOM); break;
+ case 4: SSX_PANIC(SCOM_PCB_ERROR_4_GETSCOM); break;
+ case 5: SSX_PANIC(SCOM_PCB_ERROR_5_GETSCOM); break;
+ case 6: SSX_PANIC(SCOM_PCB_ERROR_6_GETSCOM); break;
+ default: SSX_PANIC(SCOM_PCB_ERROR_7_GETSCOM); break;
+ }
+ } else if (rc == -SCOM_TIMEOUT_ERROR) {
+ SSX_PANIC(SCOM_TIMEOUT_ERROR_GETSCOM);
+ } else {
+ SSX_PANIC(SCOM_PROTOCOL_ERROR_GETSCOM);
+ }
+ }
+
+ return rc;
+}
+
+
+/// A raw putscom() through the PMC OCI/PIB bridge
+///
+/// \param address A standard 32-bit SCOM address, including multicast
+/// addresses.
+///
+/// \param data The SCOM write data
+///
+/// \param timeout The software timeout as an SSX interval (timebase ticks),
+/// or the special value SSX_WAIT_FOREVER to indicate no timeout.
+///
+/// This routine executes in an SSX_CRITICAL critical section.
+///
+/// Unlike most other APIs, this API returns both positive and negative error
+/// codes, as well as the 0 code for success.
+///
+/// If the transaction experiences a software timeout (controlled by the \a
+/// timeout parameter) or a protocol error, the PMC PIB master will be left in
+/// a state in which it is illegal to perform further SCOM access through the
+/// PMC until the ongoing transaction is finished.
+///
+/// \retval 0 Success
+///
+/// \retval 1-7 A PCB error code. See \c pcb_common.h
+///
+/// \retval -SCOM_TIMEOUT The software timeout specified by the \a timeout
+/// parameter expired before the transaction completed.
+///
+/// \retval -SCOM_PROTOCOL_ERROR_PUTSCOM_BUSY The PMC SCOM engine was busy when
+/// the call was made.
+
+int
+_putscom(uint32_t address, uint64_t data, SsxInterval timeout)
+{
+ SsxMachineContext ctx;
+ int rc;
+ occhw_scom_cmd_t *scom_cmd = &OSD_PTR->scom_cmd;
+ occhw_scom_status_t scom_status;
+
+ do
+ {
+ if(address & OCCHW_SCOM_READ_MASK)
+ {
+ rc = -SCOM_INVALID_ADDRESS;
+ break;
+ }
+
+ ssx_critical_section_enter(SSX_CRITICAL, &ctx);
+
+ // Check for a transaction already ongoing
+ rc = ssx_irq_status_get(OCCHW_IRQ_IPI_SCOM);
+ if (rc) {
+ ssx_critical_section_exit(&ctx);
+ rc = -SCOM_PROTOCOL_ERROR_PUTSCOM_BUSY;
+ break;
+ }
+
+ // Setup the write. The 'read' bit is cleared in the address.
+ scom_cmd->scom_status.status32 = OCCHW_SCOM_PENDING;
+ scom_cmd->scom_addr = address;
+ scom_cmd->scom_data = data;
+
+ // Notify the GPE (by raising an interrupt) that a request is pending
+ ssx_irq_status_set(OCCHW_IRQ_IPI_SCOM, 1);
+
+ // Poll until completed or timed out
+ rc = poll_scom(timeout);
+ scom_status.status32 = scom_cmd->scom_status.status32;
+
+ ssx_critical_section_exit(&ctx);
+
+ if(!rc)
+ {
+ //check that the GPE updated the scom status. Normally,
+ //the gpe won't clear the interrupt until it has updated
+ //the status field. The exception is if the GPE gets
+ //reset.
+ if(scom_status.status32 == OCCHW_SCOM_PENDING)
+ {
+ rc = -SCOM_PROTOCOL_ERROR_PUTSCOM_RST;
+ }
+ else
+ {
+ //The SIBRC field of the MSR is where we get the status for
+ //the last scom operation.
+ rc = scom_status.sibrc;
+ }
+ }
+ }while(0);
+ return rc;
+}
+
+
+/// putscom() through the PMC OCI/PIB bridge
+///
+/// \param address A standard 32-bit SCOM address, including multicast
+/// addresses.
+///
+/// \param data The SCOM write data.
+///
+/// This routine executes in an SSX_CRITICAL critical section.
+///
+/// Unlike most other APIs, this API returns positive error
+/// codes, as well as the 0 code for success.
+///
+/// If the transaction experiences a software timeout (controlled by the \a
+/// timeout parameter), a protocol error, or a PCB error greater than the
+/// configuration constant SCOM_ERROR_LIMIT this routine causes a kernel
+/// panic. This may leave the PMC PIB master in a state in which it is illegal
+/// to perform further SCOM access through the PMC (until the ongoing
+/// transaction is finished.)
+///
+/// \retval 0 Success
+///
+/// \retval 1-7 A PCB error code. See \c pcb_common.h
+
+int
+putscom(uint32_t address, uint64_t data)
+{
+ int rc;
+
+ rc = _putscom(address, data, SCOM_TIMEOUT);
+
+ if ((rc == 0) || ((rc > 0) && (rc <= SCOM_ERROR_LIMIT))) {
+ return rc;
+ }
+
+ //printk("putscom(0x%08x, 0x%016llx) : Failed with error %d\n",
+ // address, data, rc);
+
+ if (rc > 0) {
+ switch (rc) {
+ case 1: SSX_PANIC(SCOM_PCB_ERROR_1_PUTSCOM); break;
+ case 2: SSX_PANIC(SCOM_PCB_ERROR_2_PUTSCOM); break;
+ case 3: SSX_PANIC(SCOM_PCB_ERROR_3_PUTSCOM); break;
+ case 4: SSX_PANIC(SCOM_PCB_ERROR_4_PUTSCOM); break;
+ case 5: SSX_PANIC(SCOM_PCB_ERROR_5_PUTSCOM); break;
+ case 6: SSX_PANIC(SCOM_PCB_ERROR_6_PUTSCOM); break;
+ default: SSX_PANIC(SCOM_PCB_ERROR_7_PUTSCOM); break;
+ }
+ } else if (rc == -SCOM_TIMEOUT_ERROR) {
+ SSX_PANIC(SCOM_TIMEOUT_ERROR_PUTSCOM);
+ } else {
+ SSX_PANIC(SCOM_PROTOCOL_ERROR_PUTSCOM);
+ }
+
+ return rc;
+}
OpenPOWER on IntegriCloud