Add code to access P9 CFAM registers

Change-Id: Idd98d016f0d6a246b516b4850e887991c771ae16
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
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);
+}
+
+}
+}
+}