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;
+}
+}
+}