summaryrefslogtreecommitdiffstats
path: root/src/occ_405/lock
diff options
context:
space:
mode:
authorChris Cain <cjcain@us.ibm.com>2016-01-21 08:08:22 -0600
committerChristopher J. Cain <cjcain@us.ibm.com>2016-03-01 10:04:07 -0500
commit0b38bc9d5f8a7ab2fe466a3a432ade97eb5f8971 (patch)
tree6238bd93a590b4b38e69fbc1dd6513b1bb7955de /src/occ_405/lock
parent8d6b58d82125223c07fa0c4fa795c0de52eefd2a (diff)
downloadtalos-occ-0b38bc9d5f8a7ab2fe466a3a432ade97eb5f8971.tar.gz
talos-occ-0b38bc9d5f8a7ab2fe466a3a432ade97eb5f8971.zip
Implement I2C locking with host
Change-Id: I9e99e799e0df442bebef473360ca87d564f5ddaf RTC: 140545 Reviewed-on: http://ralgit01.raleigh.ibm.com/gerrit1/12898 Tested-by: FSP CI Jenkins Reviewed-by: Martha Broyles <mbroyles@us.ibm.com> Reviewed-by: William A. Bryan <wilbryan@us.ibm.com> Reviewed-by: Christopher J. Cain <cjcain@us.ibm.com>
Diffstat (limited to 'src/occ_405/lock')
-rwxr-xr-xsrc/occ_405/lock/lock.c404
-rw-r--r--src/occ_405/lock/lock.h54
2 files changed, 458 insertions, 0 deletions
diff --git a/src/occ_405/lock/lock.c b/src/occ_405/lock/lock.c
new file mode 100755
index 0000000..0e75a93
--- /dev/null
+++ b/src/occ_405/lock/lock.c
@@ -0,0 +1,404 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/occ_405/dimm/dimm.c $ */
+/* */
+/* OpenPOWER OnChipController Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2011,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 */
+
+// TODO: Simics currently does not support the atomic _OR/_CLR OCI registers
+#define SIMICS_FLAG_ISSUE
+
+// Debug trace
+//#define LOCK_DEBUG
+#ifdef LOCK_DEBUG
+ #define LOCK_DBG(frmt,args...) \
+ TRAC_INFO(frmt,##args)
+#else
+ #define LOCK_DBG(frmt,args...)
+#endif
+
+#include <ssx.h>
+#include <occhw_async.h>
+#include <gpe_export.h>
+
+#include <trac_interface.h>
+#include <trac.h>
+#include <occ_common.h>
+#include <comp_ids.h>
+#include <occ_service_codes.h>
+#include "dimm.h"
+#include "dimm_service_codes.h"
+#include "lock.h"
+#include "common.h"
+#include "state.h"
+
+extern bool G_mem_monitoring_allowed;
+
+typedef enum
+{
+ LOCK_RELEASE = 0x00,
+ LOCK_ACQUIRE = 0x01
+} lockOperation_e;
+
+
+#ifdef DEBUG_LOCK_TESTING
+// DEBUG: Simulate I2C lock request from the host
+void host_i2c_lock_request()
+{
+ ocb_occflg_t occ_flags = {0};
+ TRAC_INFO("host_i2c_lock_request called (tick %d / %d)", CURRENT_TICK, DIMM_TICK);
+#ifdef SIMICS_FLAG_ISSUE
+ // NON-ATOMIC OPERATION
+ occ_flags.value = in32(OCB_OCCFLG);
+ occ_flags.fields.i2c_engine3_lock_host = 1;
+ out32(OCB_OCCFLG, occ_flags.value);
+#else
+ // TODO - OCB_OCCFLG_OR is currently not working in SIMICS 1/27/2016
+ occ_flags.fields.i2c_engine3_lock_host = 1;
+ TRAC_INFO("host_i2c_lock_request - writing %04X to _OR(0x%08X)", occ_flags.value, OCB_OCCFLG_OR);
+ out32(OCB_OCCFLG_OR, occ_flags.value);
+#endif
+
+ occ_flags.value = in32(OCB_OCCFLG);
+ //TRAC_INFO("host_i2c_lock_request - 0x%08X returned value=0x%04X", OCB_OCCFLG, occ_flags.value);
+ if (occ_flags.fields.i2c_engine3_lock_host != 1)
+ {
+ TRAC_INFO("ERROR: host_i2c_lock_request - host not locked! (0x%08X value=0x%04X)", OCB_OCCFLG, occ_flags.value);
+ occ_flags.fields.i2c_engine3_lock_host = 1;
+ occ_flags.fields.i2c_engine3_lock_occ = 1;
+ out32(OCB_OCCFLG, occ_flags.value);
+ occ_flags.value = in32(OCB_OCCFLG);
+ TRAC_INFO("host_i2c_lock_request - write+read 0x%08X returned value=0x%04X", OCB_OCCFLG, occ_flags.value);
+ }
+}
+
+// DEBUG: Simulate I2C lock release from host
+void host_i2c_lock_release()
+{
+ TRAC_INFO("host_i2c_lock_release called (tick %d / %d)", CURRENT_TICK, DIMM_TICK);
+
+ ocb_occmisc_t occmiscreg = {0};
+ ocb_occflg_t occ_flags = {0};
+#ifdef SIMICS_FLAG_ISSUE // NON-ATOMIC OPERATION
+ // clear external interrupt (so OCC can notify host when lock released)
+ occmiscreg.value = in32(OCB_OCCMISC);
+ occmiscreg.fields.core_ext_intr = 0;
+ out32(OCB_OCCMISC, occmiscreg.value);
+
+ // Clear the host request
+ occ_flags.value = in32(OCB_OCCFLG);
+ occ_flags.fields.i2c_engine3_lock_host = 0;
+ out32(OCB_OCCFLG, occ_flags.value);
+#else
+ // TODO - OCB_OCCFLG_CLR is currently not working in SIMICS 1/27/2016
+ // clear external interrupt (so OCC can notify host when lock released)
+ occmiscreg.fields.core_ext_intr = 1;
+ out32(OCB_OCCMISC_CLR, occmiscreg.value);
+
+ // Clear the host request
+ occ_flags.fields.i2c_engine3_lock_host = 1;
+ TRAC_INFO("host_i2c_lock_release - writing %04X to _CLR(0x%08X)", occ_flags.value, OCB_OCCFLG_CLR);
+ out32(OCB_OCCFLG_CLR, occ_flags.value);
+#endif
+
+ occ_flags.value = in32(OCB_OCCFLG);
+ //TRAC_INFO("host_i2c_lock_release - 0x%08X returned value=0x%04X", OCB_OCCFLG, occ_flags.value);
+ if (occ_flags.fields.i2c_engine3_lock_host != 0)
+ {
+ TRAC_INFO("ERROR: host_i2c_lock_release - host not released! (0x%08X value=0x%04X)", OCB_OCCFLG, occ_flags.value);
+ occ_flags.fields.i2c_engine3_lock_host = 0;
+ occ_flags.fields.i2c_engine3_lock_occ = 1;
+ out32(OCB_OCCFLG, occ_flags.value);
+ occ_flags.value = in32(OCB_OCCFLG);
+ TRAC_INFO("host_i2c_lock_release - write+read 0x%08X returned value=0x%04X", OCB_OCCFLG, occ_flags.value);
+ }
+}
+#endif
+
+
+// Update I2C log information for specified engine
+// i_op values:
+// LOC_ACQUIRE = OCC should take ownership of lock
+// LOC_RELEASE = OCC should release ownership of lock and notify host
+void update_i2c_lock(const lockOperation_e i_op, const uint8_t i_engine)
+{
+ ocb_occflg_t occ_flags = {0};
+
+#ifdef DEBUG_LOCK_TESTING
+ ocb_occflg_t flag;
+ flag.value = in32(OCB_OCCFLG);
+ if (LOCK_RELEASE == i_op)
+ {
+ LOCK_DBG("update_i2c_lock: I2C engine %d RELEASE - host=%d, occ=%d, dimmTick=%d",
+ i_engine, flag.fields.i2c_engine3_lock_host, flag.fields.i2c_engine3_lock_occ, DIMM_TICK);
+ }
+ else
+ {
+ LOCK_DBG("update_i2c_lock: I2C engine %d LOCK - host=%d, occ=%d, dimmTick=%d",
+ i_engine, flag.fields.i2c_engine3_lock_host, flag.fields.i2c_engine3_lock_occ, DIMM_TICK);
+ }
+#endif
+
+#ifdef SIMICS_FLAG_ISSUE
+ // TODO - OCB_OCCFLG_OR is currently not working in SIMICS 1/27/2016
+ // NON-ATOMIC OPERATION
+ occ_flags.value = in32(OCB_OCCFLG);
+ if (PIB_I2C_ENGINE_E == i_engine)
+ {
+ occ_flags.fields.i2c_engine3_lock_occ = i_op;
+ }
+ else if (PIB_I2C_ENGINE_D == i_engine)
+ {
+ occ_flags.fields.i2c_engine2_lock_occ = i_op;
+ }
+ else if (PIB_I2C_ENGINE_C == i_engine)
+ {
+ occ_flags.fields.i2c_engine1_lock_occ = i_op;
+ }
+#else
+ if (PIB_I2C_ENGINE_E == i_engine)
+ {
+ occ_flags.fields.i2c_engine3_lock_occ = 1;
+ }
+ else if (PIB_I2C_ENGINE_D == i_engine)
+ {
+ occ_flags.fields.i2c_engine2_lock_occ = 1;
+ }
+ else if (PIB_I2C_ENGINE_C == i_engine)
+ {
+ occ_flags.fields.i2c_engine1_lock_occ = 1;
+ }
+#endif
+
+ if (LOCK_RELEASE == i_op)
+ {
+#ifdef SIMICS_FLAG_ISSUE
+ out32(OCB_OCCFLG, occ_flags.value);
+#else
+ out32(OCB_OCCFLG_CLR, occ_flags.value);
+#endif
+
+ // OCC had the lock and host wants it, so send interrupt to host
+ notify_host(INTR_REASON_I2C_OWNERSHIP_CHANGE);
+
+ TRAC_IMP("update_i2c_lock: OCC has released lock for I2C engine %d", i_engine);
+ }
+ else // LOCK_ACQUIRE
+ {
+#ifdef SIMICS_FLAG_ISSUE
+ out32(OCB_OCCFLG, occ_flags.value);
+#else
+ out32(OCB_OCCFLG_OR, occ_flags.value);
+#endif
+
+ TRAC_IMP("update_i2c_lock: OCC has aquired lock for I2C engine %d", i_engine);
+ }
+
+} // end update_i2c_lock()
+
+
+// Release the OCC lock indefinitely
+// This should be called when OCC goes into safe mode or will be reset
+// to allow the host to use the specified I2C engines.
+// If no engine is specified, locks for all I2C engines will be released
+void occ_i2c_lock_release(const uint8_t i_engine)
+{
+ TRAC_INFO("occ_i2c_lock_release(engine %d) called", i_engine); // TODO DEBUG
+
+ if ((PIB_I2C_ENGINE_ALL == i_engine) ||
+ (PIB_I2C_ENGINE_E == i_engine) || (PIB_I2C_ENGINE_D == i_engine) || (PIB_I2C_ENGINE_C == i_engine))
+ {
+ ocb_occflg_t occ_flags;
+ occ_flags.value = in32(OCB_OCCFLG);
+
+ if ((PIB_I2C_ENGINE_E == i_engine) || (PIB_I2C_ENGINE_ALL == i_engine))
+ {
+ update_i2c_lock(LOCK_RELEASE, PIB_I2C_ENGINE_E);
+ }
+ if ((PIB_I2C_ENGINE_D == i_engine) || (PIB_I2C_ENGINE_ALL == i_engine))
+ {
+ update_i2c_lock(LOCK_RELEASE, PIB_I2C_ENGINE_D);
+ }
+ if ((PIB_I2C_ENGINE_C == i_engine) || (PIB_I2C_ENGINE_ALL == i_engine))
+ {
+ update_i2c_lock(LOCK_RELEASE, PIB_I2C_ENGINE_C);
+ }
+ }
+ else
+ {
+ INTR_TRAC_ERR("occ_i2c_lock_release: Invalid engine specified: 0x%02X", i_engine);
+ }
+
+} // end occ_i2c_lock_release()
+
+
+// Determine if the OCC currently owns the lock
+// Returns true if OCC owns the lock, else false
+bool occ_owns_i2c_lock(const ocb_occflg_t i_flags, const uint8_t i_engine)
+{
+ bool ownsLock = false;
+ if (PIB_I2C_ENGINE_E == i_engine)
+ {
+ ownsLock = i_flags.fields.i2c_engine3_lock_occ;
+ }
+ else if (PIB_I2C_ENGINE_D == i_engine)
+ {
+ ownsLock = i_flags.fields.i2c_engine2_lock_occ;
+ }
+ else if (PIB_I2C_ENGINE_C == i_engine)
+ {
+ ownsLock = i_flags.fields.i2c_engine1_lock_occ;
+ }
+ return ownsLock;
+}
+
+
+// Determine if the Host wants the i2c lock
+// Returns true if Host wants the lock, else false
+bool host_wants_i2c_lock(const ocb_occflg_t i_flags, const uint8_t i_engine)
+{
+ bool wantsLock = false;
+ if (PIB_I2C_ENGINE_E == i_engine)
+ {
+ wantsLock = i_flags.fields.i2c_engine3_lock_host;
+ }
+ else if (PIB_I2C_ENGINE_D == i_engine)
+ {
+ wantsLock = i_flags.fields.i2c_engine2_lock_host;
+ }
+ else if (PIB_I2C_ENGINE_C == i_engine)
+ {
+ wantsLock = i_flags.fields.i2c_engine1_lock_host;
+ }
+ return wantsLock;
+}
+
+
+// Check and update lock ownership for the specified i2c engine.
+// Returns true if OCC owns the lock, or false if host owns lock
+//
+// If host has requesed the i2c lock, it will be released and an external interrupt
+// will be generated/queued and function will return false.
+// If the host has not released the lock, function will return false.
+// If the host cleared its lock bit, OCC will take back ownership and return true.
+//
+bool check_and_update_i2c_lock(const uint8_t i_engine)
+{
+ bool occ_owns_lock = true;
+
+ if ((PIB_I2C_ENGINE_E == i_engine) ||
+ (PIB_I2C_ENGINE_D == i_engine) ||
+ (PIB_I2C_ENGINE_C == i_engine))
+ {
+ bool needRetry = false;
+ do
+ {
+ ocb_occflg_t occ_flags;
+ ocb_occflg_t original_occflags;
+ original_occflags.value = in32(OCB_OCCFLG);
+ occ_flags.value = original_occflags.value;
+
+ LOCK_DBG("check_and_update_i2c_lock: I2C engine %d - host=%d, occ=%d (dimmTick=%d)",
+ i_engine, original_occflags.fields.i2c_engine3_lock_host, original_occflags.fields.i2c_engine3_lock_occ, DIMM_TICK);
+ if (occ_owns_i2c_lock(original_occflags, i_engine))
+ {
+ if (host_wants_i2c_lock(original_occflags, i_engine))
+ {
+ // Host requested lock, clear the OCC lock and notify host
+ update_i2c_lock(LOCK_RELEASE, i_engine);
+ occ_owns_lock = false;
+ }
+ // else OCC already owns the lock
+ }
+ else
+ {
+ // OCC does not own the lock
+ occ_owns_lock = false;
+ if (false == host_wants_i2c_lock(original_occflags, i_engine))
+ {
+ // Host is not requesting the lock, acquire lock for OCC
+ update_i2c_lock(LOCK_ACQUIRE, i_engine);
+ occ_owns_lock = true;
+ }
+ // else Host still holds the lock
+ }
+
+ if ((occ_owns_lock) &&
+ (original_occflags.fields.i2c_engine1_lock_host == 0) &&
+ (original_occflags.fields.i2c_engine1_lock_occ == 0))
+ {
+ // If neither lock bit is set, we must read back the register to make
+ // sure the host did not set at same time (lock conflict)
+ ocb_occflg_t verify_occflags;
+ verify_occflags.value = in32(OCB_OCCFLG);
+ if (host_wants_i2c_lock(verify_occflags, i_engine))
+ {
+ // Host wrote their lock bit at same time, clear OCC lock and notify host
+ update_i2c_lock(LOCK_RELEASE, i_engine);
+ occ_owns_lock = false;
+ }
+ else
+ {
+ if (false == occ_owns_i2c_lock(verify_occflags, i_engine))
+ {
+ // ERROR - OCC OWNERSHIP BIT DID NOT GET SET
+ INTR_TRAC_ERR("check_and_update_i2c_lock: I2C lock bit did not get set (OCCFLAGS reg: 0x%08X)",
+ verify_occflags.value);
+
+ if (needRetry)
+ {
+ // After one retry, log error and goto safe
+ /*
+ * @errortype
+ * @moduleid I2C_LOCK_UPDATE
+ * @reasoncode OCI_WRITE_FAILURE
+ * @userdata1 I2C engine number
+ * @userdata2 OCC Flags register
+ * @devdesc Invalid memory type detected
+ */
+ errlHndl_t err = createErrl(I2C_LOCK_UPDATE,
+ OCI_WRITE_FAILURE,
+ OCC_NO_EXTENDED_RC,
+ ERRL_SEV_PREDICTIVE,
+ NULL,
+ DEFAULT_TRACE_SIZE,
+ i_engine,
+ verify_occflags.value);
+ REQUEST_RESET(err);
+ occ_owns_lock = false;
+ break;
+ }
+ needRetry = true;
+ }
+ // else verify succeeded (OCC owns lock)
+ }
+ }
+ } while (needRetry);
+ }
+ else
+ {
+ // Invalid engine
+ INTR_TRAC_ERR("check_and_update_i2c_lock: Invalid engine specified: 0x%02X", i_engine);
+ }
+
+ return occ_owns_lock;
+
+} // end check_and_update_i2c_lock()
diff --git a/src/occ_405/lock/lock.h b/src/occ_405/lock/lock.h
new file mode 100644
index 0000000..dc570f2
--- /dev/null
+++ b/src/occ_405/lock/lock.h
@@ -0,0 +1,54 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/occ_405/lock/lock.h $ */
+/* */
+/* OpenPOWER OnChipController Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2011,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 _LOCK_H
+#define _LOCK_H
+
+#include <occ_common.h>
+#include <dimm.h>
+
+
+// TODO: remove testing code once SIMICS_FLAG_ISSUE removed
+//#define DEBUG_LOCK_TESTING
+
+
+
+// Release the OCC lock indefinitely
+// This should be called when OCC goes into safe mode or will be reset
+// to allow the host to use the specified I2C engines.
+// Use PIB_I2C_ENGINE_ALL, if locks for all I2C engines should be released
+void occ_i2c_lock_release(const uint8_t i_engine);
+
+
+// Check and update lock ownership for the specified i2c engine
+// If host has requesed lock, and there is no other outstanding interrupt
+// release the lock, generate and external interrupt and return false.
+// If the host has not released the lock, set ownership back to OCC and
+// return true.
+//
+// Returns true if OCC owns the lock, or false if host owns lock
+bool check_and_update_i2c_lock(const uint8_t i_engine);
+
+#endif //_LOCK_H
OpenPOWER on IntegriCloud