Add Aspeed PCC driver support

Add support for Aspeed Post Code Capture driver.

In order to fetch post code from Aspeed PCC driver, please set
`snoop-device` and `post-code-bytes` as below.
- snoop-device = aspeed-lpc-pcc
- post-code-bytes = 8

Tested on Harma system.

Change-Id: I5806362d2a54d1ed7b7f821d512f79cbc3effd38
Signed-off-by: Cosmo Chou <cosmo.chou@quantatw.com>
Signed-off-by: Potin Lai <potin.lai@quantatw.com>
diff --git a/main.cpp b/main.cpp
index e977ce0..d84a5aa 100644
--- a/main.cpp
+++ b/main.cpp
@@ -44,6 +44,7 @@
 
 static size_t codeSize = 1; /* Size of each POST code in bytes */
 static bool verbose = false;
+static std::function<bool(uint64_t&, ssize_t)> procPostCode;
 
 static void usage(const char* name)
 {
@@ -122,6 +123,75 @@
 }
 
 /*
+ * Split input code into multiple 2 bytes PCC code, If the PCC code prefix
+ * matches the check code, store each PCC code in aspeedPCCBuffer, or clear
+ * aspeedPCCBuffer if the prefix does not match.
+ *
+ * Each PCC code contains one byte of port number (MSB) and another byte of
+ * partial postcode (LSB). To get a complete postcode, the PCC code should
+ * followed the sequence of 0x40AA, 0x41BB, 0x42CC & 0x43DD. When
+ * aspeedPCCBuffer contains enough PCC codes, the postcode will be assigned as
+ * 0xDDCCBBAA.
+ */
+bool aspeedPCC(uint64_t& code, ssize_t readb)
+{
+    // Size of data coming from the PCC hardware
+    constexpr size_t pccSize = sizeof(uint16_t);
+    // Required PCC count of a full postcode, if codeSize is 8 bytes, it means
+    // it require 4 PCC codes in correct sequence to get a complete postcode.
+    const size_t fullPostPCCCount = codeSize / pccSize;
+    // A PCC buffer for storing PCC code in sequence.
+    static std::vector<uint16_t> aspeedPCCBuffer;
+    constexpr uint16_t firstPCCPortNumber = 0x4000;
+    constexpr uint16_t pccPortNumberMask = 0xFF00;
+    constexpr uint16_t pccPostCodeMask = 0x00FF;
+    constexpr uint8_t byteShift = 8;
+
+    uint16_t* codePtr = reinterpret_cast<uint16_t*>(&code);
+
+    for (size_t i = 0; i < (readb / pccSize); i++)
+    {
+        uint16_t checkCode = firstPCCPortNumber +
+                             ((aspeedPCCBuffer.size() % fullPostPCCCount)
+                              << byteShift);
+
+        if (checkCode == (codePtr[i] & pccPortNumberMask))
+        {
+            aspeedPCCBuffer.emplace_back(codePtr[i]);
+        }
+        else
+        {
+            aspeedPCCBuffer.clear();
+
+            // keep the PCC code if codePtr[i] matches with 0x40XX as first PCC
+            // code in buffer.
+            if ((codePtr[i] & pccPortNumberMask) == firstPCCPortNumber)
+            {
+                aspeedPCCBuffer.emplace_back(codePtr[i]);
+            }
+        }
+    }
+
+    if (aspeedPCCBuffer.size() < fullPostPCCCount)
+    {
+        // not receive full postcode yet.
+        return false;
+    }
+
+    // Remove the prefix bytes and combine the partial postcodes together.
+    code = 0;
+    for (size_t i = 0; i < fullPostPCCCount; i++)
+    {
+        code |= static_cast<uint64_t>(aspeedPCCBuffer[i] & pccPostCodeMask)
+                << (byteShift * i);
+    }
+    aspeedPCCBuffer.erase(aspeedPCCBuffer.begin(),
+                          aspeedPCCBuffer.begin() + fullPostPCCCount);
+
+    return true;
+}
+
+/*
  * Callback handling IO event from the POST code fd. i.e. there is new
  * POST code available to read.
  */
@@ -133,6 +203,11 @@
 
     while ((readb = read(postFd, &code, codeSize)) > 0)
     {
+        if (procPostCode && procPostCode(code, readb) == false)
+        {
+            return;
+        }
+
         code = le64toh(code);
         if (verbose)
         {
@@ -243,6 +318,10 @@
                 break;
             }
             case 'd':
+                if (std::string(optarg) == "/dev/aspeed-lpc-pcc")
+                {
+                    procPostCode = aspeedPCC;
+                }
 
                 postFd = open(optarg, O_NONBLOCK);
                 if (postFd < 0)