1 /**
2  * Copyright (C) 2017 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "cfam_access.hpp"
17 
18 #include "targeting.hpp"
19 
20 #include <unistd.h>
21 
22 #include <phosphor-logging/elog-errors.hpp>
23 #include <phosphor-logging/elog.hpp>
24 #include <xyz/openbmc_project/Common/Device/error.hpp>
25 #include <xyz/openbmc_project/Common/File/error.hpp>
26 
27 namespace openpower
28 {
29 namespace cfam
30 {
31 namespace access
32 {
33 
34 constexpr auto cfamRegSize = 4;
35 
36 using namespace openpower::targeting;
37 using namespace openpower::util;
38 namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error;
39 namespace device_error = sdbusplus::xyz::openbmc_project::Common::Device::Error;
40 
41 /**
42  * Converts the CFAM register address used by the calling
43  * code (because that's how it is in the spec) to the address
44  * required by the device driver.
45  */
makeOffset(cfam_address_t address)46 static inline cfam_address_t makeOffset(cfam_address_t address)
47 {
48     return (address & 0xFC00) | ((address & 0x03FF) << 2);
49 }
50 
writeReg(const std::unique_ptr<Target> & target,cfam_address_t address,cfam_data_t data)51 void writeReg(const std::unique_ptr<Target>& target, cfam_address_t address,
52               cfam_data_t data)
53 {
54     using namespace phosphor::logging;
55     int rc = lseek(target->getCFAMFD(), makeOffset(address), SEEK_SET);
56     if (rc < 0)
57     {
58         log<level::ERR>("Failed seeking on a processor CFAM",
59                         entry("CFAM_ADDRESS=0x%X", address));
60 
61         using metadata = xyz::openbmc_project::Common::File::Seek;
62 
63         elog<file_error::Seek>(metadata::OFFSET(makeOffset(address)),
64                                metadata::WHENCE(SEEK_SET),
65                                metadata::ERRNO(errno),
66                                metadata::PATH(target->getCFAMPath().c_str()));
67     }
68 
69     data = htobe32(data);
70 
71     rc = write(target->getCFAMFD(), &data, cfamRegSize);
72     if (rc < 0)
73     {
74         using metadata = xyz::openbmc_project::Common::Device::WriteFailure;
75 
76         elog<device_error::WriteFailure>(
77             metadata::CALLOUT_ERRNO(errno),
78             metadata::CALLOUT_DEVICE_PATH(target->getCFAMPath().c_str()));
79     }
80 }
81 
readReg(const std::unique_ptr<Target> & target,cfam_address_t address)82 cfam_data_t readReg(const std::unique_ptr<Target>& target,
83                     cfam_address_t address)
84 {
85     using namespace phosphor::logging;
86 
87     cfam_data_t data = 0;
88 
89     int rc = lseek(target->getCFAMFD(), makeOffset(address), SEEK_SET);
90     if (rc < 0)
91     {
92         log<level::ERR>("Failed seeking on a processor CFAM",
93                         entry("CFAM_ADDRESS=0x%X", address));
94 
95         using metadata = xyz::openbmc_project::Common::File::Seek;
96 
97         elog<file_error::Seek>(metadata::OFFSET(makeOffset(address)),
98                                metadata::WHENCE(SEEK_SET),
99                                metadata::ERRNO(errno),
100                                metadata::PATH(target->getCFAMPath().c_str()));
101     }
102 
103     rc = read(target->getCFAMFD(), &data, cfamRegSize);
104     if (rc < 0)
105     {
106         using metadata = xyz::openbmc_project::Common::Device::ReadFailure;
107 
108         elog<device_error::ReadFailure>(
109             metadata::CALLOUT_ERRNO(errno),
110             metadata::CALLOUT_DEVICE_PATH(target->getCFAMPath().c_str()));
111     }
112 
113     return be32toh(data);
114 }
115 
writeRegWithMask(const std::unique_ptr<Target> & target,cfam_address_t address,cfam_data_t data,cfam_mask_t mask)116 void writeRegWithMask(const std::unique_ptr<Target>& target,
117                       cfam_address_t address, cfam_data_t data,
118                       cfam_mask_t mask)
119 {
120     cfam_data_t readData = readReg(target, address);
121 
122     readData &= ~mask;
123     readData |= (data & mask);
124 
125     writeReg(target, address, readData);
126 }
127 
128 } // namespace access
129 } // namespace cfam
130 } // namespace openpower
131