Add code to access P9 CFAM registers

Change-Id: Idd98d016f0d6a246b516b4850e887991c771ae16
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index cc2374a..73ea204 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -5,6 +5,7 @@
 
 openpower_proc_control_SOURCES = \
 	proc_control.cpp \
+	cfam_access.cpp \
 	p9_procedures.cpp \
 	filedescriptor.cpp \
 	targeting.cpp
diff --git a/cfam_access.cpp b/cfam_access.cpp
new file mode 100644
index 0000000..2762e80
--- /dev/null
+++ b/cfam_access.cpp
@@ -0,0 +1,121 @@
+/**
+ * Copyright © 2017 IBM Corporation
+ *
+ * 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.
+ */
+#include <unistd.h>
+#include "filedescriptor.hpp"
+#include "cfam_access.hpp"
+#include "targeting.hpp"
+
+namespace openpower
+{
+namespace cfam
+{
+namespace access
+{
+
+constexpr auto cfamRegSize = 4;
+
+using namespace openpower::targeting;
+using namespace openpower::p9_util;
+
+/**
+ * Converts the CFAM register address used by the calling
+ * code (because that's how it is in the spec) to the address
+ * required by the device driver.
+ */
+static inline cfam_address_t makeOffset(cfam_address_t address)
+{
+    return (address & 0xFC00) | ((address & 0x03FF) << 2);
+}
+
+
+void writeReg(const std::unique_ptr<Target>& target,
+              cfam_address_t address,
+              cfam_data_t data)
+{
+    FileDescriptor fd(target->getPath());
+
+    int rc = lseek(fd(), makeOffset(address), SEEK_SET);
+    if (rc < 0)
+    {
+        //Future: use a different exception to create an error log
+        char msg[100];
+        sprintf(msg, "writeCFAMReg: Failed seek for address 0x%X, "
+                "processor %d.  errno = %d",
+                address, target->getPos(), errno);
+        throw std::runtime_error(msg);
+    }
+
+    rc = write(fd(), &data, cfamRegSize);
+    if (rc < 0)
+    {
+        //Future: use a different exception to create an error log
+        char msg[100];
+        sprintf(msg, "writeCFAMReg: Failed write to address 0x%X, "
+                "processor %d. errno = %d",
+                address, target->getPos(), errno);
+        throw std::runtime_error(msg);
+    }
+}
+
+
+cfam_data_t readReg(const std::unique_ptr<Target>& target,
+                    cfam_address_t address)
+{
+    FileDescriptor fd(target->getPath());
+    cfam_data_t data = 0;
+
+    int rc = lseek(fd(), makeOffset(address), SEEK_SET);
+    if (rc < 0)
+    {
+        //Future: use a different exception to create an error log
+        char msg[100];
+        sprintf(msg, "readCFAMReg: Failed seek for address 0x%X, "
+                "processor %d.  errno = %d",
+                address, target->getPos(), errno);
+        throw std::runtime_error(msg);
+    }
+
+    rc = read(fd(), &data, cfamRegSize);
+    if (rc < 0)
+    {
+        //Future: use a different exception to create an error log
+        char msg[100];
+        sprintf(msg, "readCFAMReg: Failed read for address 0x%X, "
+                "processor %d. errno = %d",
+                address, target->getPos(), errno);
+        throw std::runtime_error(msg);
+    }
+
+    return data;
+}
+
+
+void writeRegWithMask(const std::unique_ptr<Target>& target,
+                      cfam_address_t address,
+                      cfam_data_t data,
+                      cfam_mask_t mask)
+{
+    cfam_data_t readData = readReg(target, address);
+
+    readData &= ~mask;
+    readData |= (data & mask);
+
+    writeReg(target, address, readData);
+}
+
+}
+}
+}
diff --git a/cfam_access.hpp b/cfam_access.hpp
new file mode 100644
index 0000000..88dcc77
--- /dev/null
+++ b/cfam_access.hpp
@@ -0,0 +1,65 @@
+#pragma once
+
+#include <memory>
+#include "targeting.hpp"
+
+namespace openpower
+{
+namespace cfam
+{
+namespace access
+{
+
+using cfam_address_t = uint16_t;
+using cfam_data_t = uint32_t;
+using cfam_mask_t = uint32_t;
+
+/**
+ * @brief Writes a CFAM (Common FRU Access Macro) register in a P9.
+ *
+ * Throws an exception on error.
+ *
+ * @param[in] target - The Target to perform the operation on
+ * @param[in] address - The register address to write to
+ * @param[in] data - The data to write
+ */
+void writeReg(const std::unique_ptr<openpower::targeting::Target>& target,
+              cfam_address_t address,
+              cfam_data_t data);
+
+
+/**
+ * @brief Reads a CFAM (Common FRU Access Macro) register in a P9.
+ *
+ * Throws an exception on error.
+ *
+ * @param[in] target - The Target to perform the operation on
+ * @param[in] address - The register address to read
+ * @return - The register data
+ */
+cfam_data_t readReg(
+    const std::unique_ptr<openpower::targeting::Target>& target,
+    cfam_address_t address);
+
+
+/**
+ * @brief Writes a CFAM (Common FRU Access Macro) register in a P9
+ *        using a mask to specify the bits the modify.
+ *
+ * Only bits that are set in the mask parameter will be modified.
+ *
+ * Throws an exception on error.
+ *
+ * @param[in] target - The Target to perform the operation on
+ * @param[in] address - The register address to write to
+ * @param[in] data - The data to write
+ * @param[in] mask - The mask
+ */
+void writeRegWithMask(
+    const std::unique_ptr<openpower::targeting::Target>& target,
+    cfam_address_t address,
+    cfam_data_t data,
+    cfam_mask_t mask);
+}
+}
+}
diff --git a/p9_cfam.hpp b/p9_cfam.hpp
new file mode 100644
index 0000000..d8fa5ff
--- /dev/null
+++ b/p9_cfam.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+namespace openpower
+{
+namespace cfam
+{
+namespace p9
+{
+static constexpr uint16_t P9_FSI_A_SI1S            = 0x081C;
+static constexpr uint16_t P9_LL_MODE_REG           = 0x0840;
+static constexpr uint16_t P9_FSI2PIB_INTERRUPT     = 0x100B;
+static constexpr uint16_t P9_FSI2PIB_TRUE_MASK     = 0x100D;
+static constexpr uint16_t P9_CBS_CS                = 0x2801;
+static constexpr uint16_t P9_ROOT_CTRL0            = 0x2810;
+static constexpr uint16_t P9_PERV_CTRL0            = 0x281A;
+static constexpr uint16_t P9_SCRATCH_REGISTER_8    = 0x283F;
+static constexpr uint16_t P9_ROOT_CTRL8            = 0x2918;
+}
+}
+}