summaryrefslogtreecommitdiffstats
path: root/src/occ_gpe0/firdata/pnor_mboxdd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/occ_gpe0/firdata/pnor_mboxdd.c')
-rw-r--r--src/occ_gpe0/firdata/pnor_mboxdd.c404
1 files changed, 404 insertions, 0 deletions
diff --git a/src/occ_gpe0/firdata/pnor_mboxdd.c b/src/occ_gpe0/firdata/pnor_mboxdd.c
new file mode 100644
index 0000000..463e950
--- /dev/null
+++ b/src/occ_gpe0/firdata/pnor_mboxdd.c
@@ -0,0 +1,404 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/occ_405/firdata/pnor_mboxdd.c $ */
+/* */
+/* OpenPOWER OnChipController Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2017 */
+/* [+] International Business Machines Corp. */
+/* */
+/* */
+/* Licensed under the Apache License, Version 2.0 (the "License"); */
+/* you may not use this file except in compliance with the License. */
+/* You may obtain a copy of the License at */
+/* */
+/* http://www.apache.org/licenses/LICENSE-2.0 */
+/* */
+/* Unless required by applicable law or agreed to in writing, software */
+/* distributed under the License is distributed on an "AS IS" BASIS, */
+/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */
+/* implied. See the License for the specific language governing */
+/* permissions and limitations under the License. */
+/* */
+/* IBM_PROLOG_END_TAG */
+
+/**
+ * @file ast_mboxdd.c
+ *
+ * @brief Implementation of the PNOR access code on top of AST MBOX protocol
+ */
+
+/*****************************************************************************/
+// I n c l u d e s
+/*****************************************************************************/
+#include <native.h>
+#include <norflash.h>
+#include <pnor_mboxdd.h>
+#include <lpc.h>
+
+errorHndl_t hwInit(pnorMbox_t* i_pnorMbox)
+{
+ errorHndl_t l_err = NO_ERROR;
+ uint8_t* l_data;
+ int i;
+ do
+ {
+ //Current window starts closed
+ i_pnorMbox->iv_curWindowOpen = false;
+ i_pnorMbox->iv_mbox.iv_mboxMsgSeq = 1;
+ l_err = initializeMbox();
+ if (l_err)
+ {
+ TRAC_ERR("initializeMbox failed rc:0x%x", l_err);
+ break;
+ }
+
+ //Send message to BMC Mbox to get MBOX info
+ // This message gets the MBOX protocol version
+ mboxMessage_t l_getInfoMsg;
+ l_data = (uint8_t*)(&l_getInfoMsg);
+ for (i = 0; i < BMC_MBOX_DATA_REGS; i++)
+ {
+ l_data[i] = 0;
+ }
+
+ l_getInfoMsg.iv_cmd = MBOX_C_GET_MBOX_INFO;
+ put8(&l_getInfoMsg, 0, 2);
+ l_err = doMessage(&i_pnorMbox->iv_mbox, &l_getInfoMsg);
+ if (l_err)
+ {
+ TRAC_ERR("doMessage to ping BMC failed with rc=0x%x", l_err);
+ break;
+ }
+
+ i_pnorMbox->iv_protocolVersion = get8(&l_getInfoMsg, 0);
+ if (i_pnorMbox->iv_protocolVersion == 1)
+ {
+ i_pnorMbox->iv_blockShift = 12;
+ i_pnorMbox->iv_readWindowSize = get16(&l_getInfoMsg, 1)
+ << i_pnorMbox->iv_blockShift;
+ i_pnorMbox->iv_writeWindowSize = get16(&l_getInfoMsg, 3)
+ << i_pnorMbox->iv_blockShift;
+ }
+ else
+ {
+ i_pnorMbox->iv_blockShift = get8(&l_getInfoMsg, 5);
+ }
+
+ //Now get the size of the flash
+ mboxMessage_t l_getSizeMsg;
+ l_getSizeMsg.iv_cmd = MBOX_C_GET_FLASH_INFO;
+ l_err = doMessage(&i_pnorMbox->iv_mbox, &l_getSizeMsg);
+ if (l_err)
+ {
+ TRAC_ERR("doMessage failed to get flash size rc=0x%x", l_err);
+ break;
+ }
+
+ if (i_pnorMbox->iv_protocolVersion == 1)
+ {
+ i_pnorMbox->iv_flashSize = get32(&l_getSizeMsg, 0);
+ i_pnorMbox->iv_flashEraseSize = get32(&l_getSizeMsg, 4);
+ }
+ else
+ {
+ i_pnorMbox->iv_flashSize = get16(&l_getSizeMsg, 0)
+ << i_pnorMbox->iv_blockShift;
+ i_pnorMbox->iv_flashEraseSize = get16(&l_getSizeMsg, 2)
+ << i_pnorMbox->iv_blockShift;
+ }
+ } while (0);
+ return l_err;
+}
+
+
+errorHndl_t readFlash(pnorMbox_t* i_pnorMbox,
+ uint32_t i_addr,
+ size_t i_size,
+ void* o_data)
+{
+ errorHndl_t l_err = NO_ERROR;
+
+ do
+ {
+ // Ensure we are operating on a 4-byte boundary
+ if ((i_size % 4 != 0) && (i_addr % 4 != 0))
+ {
+ TRAC_ERR("readFlash: not on 4-byte boundary");
+ return FAIL;
+ }
+
+ while (i_size)
+ {
+ uint32_t l_lpcAddr;
+ size_t l_chunkLen;
+
+ l_err = adjustMboxWindow(i_pnorMbox,
+ false,
+ i_addr,
+ i_size,
+ &l_lpcAddr,
+ &l_chunkLen);
+
+ if (l_err)
+ {
+ break;
+ }
+
+ //Directly access LPC to do read/write as BMC is setup now
+ l_err = lpc_read(LPC_TRANS_FW, l_lpcAddr, o_data, l_chunkLen);
+
+ if (l_err)
+ {
+ break;
+ }
+
+ i_addr += l_chunkLen;
+ i_size -= l_chunkLen;
+ o_data = (char*)o_data + l_chunkLen;
+ }
+
+ if(l_err)
+ {
+ break;
+ }
+
+ }
+ while(0);
+
+ return l_err;
+}
+
+errorHndl_t writeFlash(pnorMbox_t* i_pnorMbox,
+ uint32_t i_addr,
+ size_t i_size,
+ void* i_data)
+{
+ errorHndl_t l_err = NO_ERROR;
+
+ do
+ {
+ // Ensure we are operating on a 4-byte boundary
+ if (i_size % 4 != 0)
+ {
+ TRAC_ERR("writeFlash: not on 4-byte boundary");
+ return FAIL;
+ }
+
+
+ errorHndl_t l_flushErr = NO_ERROR;
+
+ while (i_size)
+ {
+ uint32_t l_lpcAddr;
+ size_t l_chunkLen;
+
+ l_err = adjustMboxWindow(i_pnorMbox,
+ true,
+ i_addr,
+ i_size,
+ &l_lpcAddr,
+ &l_chunkLen);
+
+ if (l_err)
+ {
+ break;
+ }
+
+ //Directly do LPC access to space pointed to by BMC
+ l_err = lpc_write(LPC_TRANS_FW, l_lpcAddr, i_data, l_chunkLen);
+
+ if (l_err)
+ {
+ break;
+ }
+
+ //Tell BMC to push data from LPC space into PNOR
+ l_err = writeDirty(i_pnorMbox, i_addr, l_chunkLen);
+
+ if (l_err)
+ {
+ break;
+ }
+
+ i_addr += l_chunkLen;
+ i_size -= l_chunkLen;
+ i_data = (char*)i_data + l_chunkLen;
+ }
+
+ /* We flush whether we had an error or not.
+ *
+ * NOTE: It would help the daemon a lot if we moved that out of here
+ * and instead had a single flush call over a series of writes.
+ */
+ l_flushErr = writeFlush(i_pnorMbox);
+
+ if ( l_err == NO_ERROR && l_flushErr )
+ {
+ l_err = l_flushErr;
+ }
+
+ if( l_err )
+ {
+ i_size = 0;
+ }
+
+ if(l_err)
+ {
+ break;
+ }
+
+ }
+ while(0);
+
+ return l_err;
+}
+
+errorHndl_t adjustMboxWindow(pnorMbox_t* i_pnorMbox,
+ bool i_isWrite, uint32_t i_reqAddr,
+ size_t i_reqSize, uint32_t *o_lpcAddr,
+ size_t *o_chunkLen)
+{
+ errorHndl_t l_err = NO_ERROR;
+ uint32_t l_pos, l_wSize, l_reqSize;
+
+ do
+ {
+ /*
+ * Handle the case where the window is already opened, is of
+ * the right type and contains the requested address.
+ */
+ uint32_t l_wEnd = i_pnorMbox->iv_curWindowOffset +
+ i_pnorMbox->iv_curWindowSize;
+
+ /* A read request can be serviced by a write window */
+ if (i_pnorMbox->iv_curWindowOpen &&
+ (i_pnorMbox->iv_curWindowWrite || !i_isWrite) &&
+ i_reqAddr >= i_pnorMbox->iv_curWindowOffset && i_reqAddr < l_wEnd)
+ {
+ size_t l_gap = (l_wEnd - i_reqAddr);
+
+ *o_lpcAddr = i_pnorMbox->iv_curWindowLpcOffset +
+ (i_reqAddr - i_pnorMbox->iv_curWindowOffset);
+ if (i_reqSize <= l_gap)
+ {
+ *o_chunkLen = i_reqSize;
+ }
+ else
+ {
+ *o_chunkLen = l_gap;
+ }
+ return NO_ERROR;
+ }
+
+ /*
+ * We need a window change, mark it closed first
+ */
+ i_pnorMbox->iv_curWindowOpen = false;
+
+ /*
+ * Then open the new one at the right position. The required
+ * alignment differs between protocol versions
+ */
+ if (i_pnorMbox->iv_protocolVersion == 1)
+ {
+ l_wSize = i_isWrite ? i_pnorMbox->iv_writeWindowSize
+ : i_pnorMbox->iv_readWindowSize;
+ l_pos = i_reqAddr & ~(l_wSize - 1);
+ l_reqSize = 0;
+ }
+ else
+ {
+ uint32_t l_blockMask = (1u << i_pnorMbox->iv_blockShift) - 1;
+ l_wSize = 0;
+ l_pos = i_reqAddr & ~l_blockMask;
+ l_reqSize = (((i_reqAddr + i_reqSize) + l_blockMask) & ~l_blockMask)
+ - l_pos;
+ }
+
+ mboxMessage_t winMsg;
+ if (i_isWrite)
+ {
+ winMsg.iv_cmd = MBOX_C_CREATE_WRITE_WINDOW;
+ }
+ else
+ {
+ winMsg.iv_cmd = MBOX_C_CREATE_READ_WINDOW;
+ }
+
+ put16(&winMsg, 0, l_pos >> i_pnorMbox->iv_blockShift);
+ put16(&winMsg, 2, l_reqSize >> i_pnorMbox->iv_blockShift);
+ l_err = doMessage(&i_pnorMbox->iv_mbox, &winMsg);
+
+ if (l_err)
+ {
+ break;
+ }
+
+ i_pnorMbox->iv_curWindowLpcOffset =
+ (get16(&winMsg,0)) << i_pnorMbox->iv_blockShift;
+
+ if (i_pnorMbox->iv_protocolVersion == 1)
+ {
+ i_pnorMbox->iv_curWindowOffset = l_pos;
+ i_pnorMbox->iv_curWindowLpcOffset =
+ (get16(&winMsg,0)) << i_pnorMbox->iv_blockShift;
+ i_pnorMbox->iv_curWindowSize = l_wSize;
+ }
+ else
+ {
+ i_pnorMbox->iv_curWindowLpcOffset = (get16(&winMsg,0))
+ << i_pnorMbox->iv_blockShift;
+ i_pnorMbox->iv_curWindowSize = (get16(&winMsg,2))
+ << i_pnorMbox->iv_blockShift;
+ i_pnorMbox->iv_curWindowOffset = (get16(&winMsg,4))
+ << i_pnorMbox->iv_blockShift;
+ }
+
+ i_pnorMbox->iv_curWindowOpen = true;
+ i_pnorMbox->iv_curWindowWrite = i_isWrite;
+
+ }
+ while (true);
+
+ return l_err;
+}
+
+errorHndl_t writeDirty(pnorMbox_t* i_pnorMbox, uint32_t i_addr, size_t i_size)
+{
+ /* To pass a correct "size" for both protocol versions, we
+ * calculate the block-aligned start and end.
+ */
+ uint32_t l_blockMask = (1u << i_pnorMbox->iv_blockShift) - 1;
+ uint32_t l_start = i_addr & ~l_blockMask;
+ uint32_t l_end = ((i_addr + i_size) + l_blockMask) & ~l_blockMask;
+
+ mboxMessage_t dirtyMsg;
+ dirtyMsg.iv_cmd = MBOX_C_MARK_WRITE_DIRTY;
+
+ if (i_pnorMbox->iv_protocolVersion == 1)
+ {
+ put16(&dirtyMsg, 0, i_addr >> i_pnorMbox->iv_blockShift);
+ put32(&dirtyMsg, 2, l_end - l_start);
+ }
+ else
+ {
+ put16(&dirtyMsg, 0, (i_addr - i_pnorMbox->iv_curWindowOffset)
+ >> i_pnorMbox->iv_blockShift);
+ put16(&dirtyMsg, 2, (l_end - l_start) >> i_pnorMbox->iv_blockShift);
+ }
+
+ return doMessage(&i_pnorMbox->iv_mbox, &dirtyMsg);
+}
+
+errorHndl_t writeFlush(pnorMbox_t* i_pnorMbox)
+{
+ mboxMessage_t flushMsg;
+ flushMsg.iv_cmd = MBOX_C_WRITE_FLUSH;
+
+ put16(&flushMsg, 0, 0);
+ put32(&flushMsg, 2, 0);
+
+ return doMessage(&i_pnorMbox->iv_mbox, &flushMsg);
+}
OpenPOWER on IntegriCloud