tools: implement pci support for Nuvoton

Note: burn_my_bmc will detect the PCI device at runtime
      as Aspeed vs Nuvoton
Tested: Verified this works for a Nuvoton BMC.

Signed-off-by: Medad CChien <ctcchien@nuvoton.com>
Change-Id: Ifb253dee301f4d39582269f7d3e9b4f423fdfde3
diff --git a/tools/p2a.cpp b/tools/p2a.cpp
index b1f8672..5146c85 100644
--- a/tools/p2a.cpp
+++ b/tools/p2a.cpp
@@ -52,35 +52,56 @@
     PciDevice result;
     PciFilter filter;
     bool found = false;
-    pciaddr_t bar1;
+    pciaddr_t bar;
     bool returnValue = false;
     int inputFd = -1;
     ipmi_flash::PciConfigResponse pciResp;
     std::int64_t fileSize;
-    const std::uint32_t p2aLength = aspeedP2aOffset;
     std::unique_ptr<std::uint8_t[]> readBuffer;
 
-    filter.vid = aspeedVendorId;
-    filter.did = aspeedDeviceId;
+    std::uint16_t pciDeviceVID;
+    std::uint16_t pciDeviceDID;
+    std::uint32_t p2aOffset;
+    std::uint32_t p2aLength;
 
-    /* Find the ASPEED PCI device entry we want. */
-    auto output = pci->getPciDevices(filter);
-    for (const auto& d : output)
+    for (auto device : PCIDeviceList)
     {
-        std::fprintf(stderr, "[0x%x 0x%x] ", d.vid, d.did);
+        filter.vid = device.VID;
+        filter.did = device.DID;
 
-        /* Verify it's a memory-based bar -- we want bar1. */
-        bar1 = d.bars[1];
-        if ((bar1 & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)
+        /* Find the PCI device entry we want. */
+        auto output = pci->getPciDevices(filter);
+        for (const auto& d : output)
         {
-            /* We want it to not be IO-based access. */
-            continue;
+            std::fprintf(stderr, "[0x%x 0x%x] \n", d.vid, d.did);
+
+            /* Verify it's a memory-based bar. */
+            bar = d.bars[device.bar];
+
+            if ((bar & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)
+            {
+                /* We want it to not be IO-based access. */
+                continue;
+            }
+
+            /* For now capture the entire device even if we're only using BAR0
+             */
+            result = d;
+            found = true;
+            break;
         }
 
-        /* For now capture the entire device even if we're only using BAR1 */
-        result = d;
-        found = true;
-        break;
+        if (found)
+        {
+            std::fprintf(stderr, "Find [0x%x 0x%x] \n", device.VID, device.DID);
+            std::fprintf(stderr, "bar%u[0x%x] \n", device.bar,
+                         (unsigned int)bar);
+            pciDeviceVID = device.VID;
+            pciDeviceDID = device.DID;
+            p2aOffset = device.Offset;
+            p2aLength = device.Length;
+            break;
+        }
     }
 
     if (!found)
@@ -93,27 +114,31 @@
     /* We sent the open command before this, so the window should be open and
      * the bridge enabled on the BMC.
      */
-    std::uint32_t value;
-    if (!io->read(bar1 + aspeedP2aConfig, sizeof(value), &value))
+    if (pciDeviceVID == aspeedPciDeviceInfo.VID &&
+        pciDeviceDID == aspeedPciDeviceInfo.DID)
     {
-        std::fprintf(stderr, "PCI config read failed\n");
-        return false;
-    }
-
-    if (0 == (value & p2ABridgeEnabled))
-    {
-        std::fprintf(stderr, "Bridge not enabled - Enabling from host\n");
-
-        value |= p2ABridgeEnabled;
-        if (!io->write(bar1 + aspeedP2aConfig, sizeof(value), &value))
+        std::uint32_t value;
+        if (!io->read(bar + aspeedP2aConfig, sizeof(value), &value))
         {
-            std::fprintf(stderr, "PCI config write failed\n");
+            std::fprintf(stderr, "PCI config read failed\n");
             return false;
         }
-    }
 
-    /* From this point down we need to disable the bridge. */
-    std::fprintf(stderr, "The bridge is enabled!\n");
+        if (0 == (value & p2ABridgeEnabled))
+        {
+            std::fprintf(stderr, "Bridge not enabled - Enabling from host\n");
+
+            value |= p2ABridgeEnabled;
+            if (!io->write(bar + aspeedP2aConfig, sizeof(value), &value))
+            {
+                std::fprintf(stderr, "PCI config write failed\n");
+                return false;
+            }
+        }
+
+        /* From this point down we need to disable the bridge. */
+        std::fprintf(stderr, "The bridge is enabled!\n");
+    }
 
     /* Read the configuration via blobs metadata (stat). */
     ipmiblob::StatResponse stat = blob->getStat(session);
@@ -127,13 +152,17 @@
     std::memcpy(&pciResp, stat.metadata.data(), sizeof(pciResp));
     std::fprintf(stderr, "Received address: 0x%x\n", pciResp.address);
 
-    /* Configure the mmio to point there. */
-    if (!io->write(bar1 + aspeedP2aBridge, sizeof(pciResp.address),
-                   &pciResp.address))
+    if (pciDeviceVID == aspeedPciDeviceInfo.VID &&
+        pciDeviceDID == aspeedPciDeviceInfo.DID)
     {
-        // Failed to set it up, so fall back.
-        std::fprintf(stderr, "Failed to update the bridge address\n");
-        goto exit;
+        /* Configure the mmio to point there. */
+        if (!io->write(bar + aspeedP2aBridge, sizeof(pciResp.address),
+                       &pciResp.address))
+        {
+            // Failed to set it up, so fall back.
+            std::fprintf(stderr, "Failed to update the bridge address\n");
+            goto exit;
+        }
     }
 
     /* For data blocks in 64kb, stage data, and send blob write command. */
@@ -170,19 +199,18 @@
             bytesRead = sys->read(inputFd, readBuffer.get(), p2aLength);
             if (bytesRead > 0)
             {
-                /* TODO: Will likely need to store an rv somewhere to know
-                 * when we're exiting from failure.
+                /* TODO: Will likely need to store an rv somewhere to know when
+                 * we're exiting from failure.
                  */
-                if (!io->write(bar1 + aspeedP2aOffset, bytesRead,
-                               readBuffer.get()))
+                if (!io->write(bar + p2aOffset, bytesRead, readBuffer.get()))
                 {
                     std::fprintf(stderr,
                                  "Failed to write to region in memory!\n");
                     goto exit;
                 }
 
-                /* Ok, so the data is staged, now send the blob write with
-                 * the details.
+                /* Ok, so the data is staged, now send the blob write with the
+                 * details.
                  */
                 struct ipmi_flash::ExtChunkHdr chunk;
                 chunk.length = bytesRead;
@@ -205,8 +233,12 @@
     returnValue = true;
 
 exit:
-    /* disable PCI bridge. */
-    disablePciBridge(io, bar1);
+    /* disable ASPEED PCI bridge. */
+    if (pciDeviceVID == aspeedPciDeviceInfo.VID &&
+        pciDeviceDID == aspeedPciDeviceInfo.DID)
+    {
+        disablePciBridge(io, bar);
+    }
 
     /* close input file. */
     if (inputFd != -1)
diff --git a/tools/p2a.hpp b/tools/p2a.hpp
index d1a303e..84c0952 100644
--- a/tools/p2a.hpp
+++ b/tools/p2a.hpp
@@ -8,14 +8,21 @@
 
 #include <cstdint>
 #include <ipmiblob/blob_interface.hpp>
+#include <vector>
 
-constexpr std::uint16_t aspeedVendorId = 0x1a03;
-constexpr std::uint16_t aspeedDeviceId = 0x2000;
-constexpr std::size_t aspeedP2aOffset = 0x10000;
 constexpr std::size_t aspeedP2aConfig = 0x0f000;
 constexpr std::size_t aspeedP2aBridge = 0x0f004;
 constexpr std::uint32_t p2ABridgeEnabled = 0x1;
 
+struct PciDeviceInfo
+{
+    std::uint16_t VID;
+    std::uint16_t DID;
+    std::size_t Offset;
+    std::size_t Length;
+    std::uint16_t bar;
+};
+
 namespace host_tool
 {
 
@@ -42,6 +49,12 @@
     PciUtilInterface* pci;
     ProgressInterface* progress;
     const internal::Sys* sys;
-};
 
+    constexpr struct PciDeviceInfo static aspeedPciDeviceInfo = {
+        0x1a03, 0x2000, 0x10000, 0x10000, 1};
+    constexpr struct PciDeviceInfo static nuvotonPciDeviceInfo = {
+        0x1050, 0x0750, 0x0, 0x4000, 0};
+    const std::vector<PciDeviceInfo> PCIDeviceList = {aspeedPciDeviceInfo,
+                                                      nuvotonPciDeviceInfo};
+};
 } // namespace host_tool