118c42b0fSMatt Spinler /** 218c42b0fSMatt Spinler * Copyright © 2020 IBM Corporation 318c42b0fSMatt Spinler * 418c42b0fSMatt Spinler * Licensed under the Apache License, Version 2.0 (the "License"); 518c42b0fSMatt Spinler * you may not use this file except in compliance with the License. 618c42b0fSMatt Spinler * You may obtain a copy of the License at 718c42b0fSMatt Spinler * 818c42b0fSMatt Spinler * http://www.apache.org/licenses/LICENSE-2.0 918c42b0fSMatt Spinler * 1018c42b0fSMatt Spinler * Unless required by applicable law or agreed to in writing, software 1118c42b0fSMatt Spinler * distributed under the License is distributed on an "AS IS" BASIS, 1218c42b0fSMatt Spinler * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1318c42b0fSMatt Spinler * See the License for the specific language governing permissions and 1418c42b0fSMatt Spinler * limitations under the License. 1518c42b0fSMatt Spinler */ 1618c42b0fSMatt Spinler #include "device_callouts.hpp" 1718c42b0fSMatt Spinler 1818c42b0fSMatt Spinler #include "paths.hpp" 1918c42b0fSMatt Spinler 2018c42b0fSMatt Spinler #include <fstream> 2118c42b0fSMatt Spinler #include <phosphor-logging/log.hpp> 2218c42b0fSMatt Spinler #include <regex> 2318c42b0fSMatt Spinler 2418c42b0fSMatt Spinler namespace openpower::pels::device_callouts 2518c42b0fSMatt Spinler { 2618c42b0fSMatt Spinler 2718c42b0fSMatt Spinler constexpr auto debugFilePath = "/etc/phosphor-logging/"; 2818c42b0fSMatt Spinler constexpr auto calloutFileSuffix = "_dev_callouts.json"; 2918c42b0fSMatt Spinler 3018c42b0fSMatt Spinler namespace fs = std::filesystem; 3118c42b0fSMatt Spinler using namespace phosphor::logging; 3218c42b0fSMatt Spinler 3318c42b0fSMatt Spinler namespace util 3418c42b0fSMatt Spinler { 3518c42b0fSMatt Spinler 3618c42b0fSMatt Spinler fs::path getJSONFilename(const std::vector<std::string>& compatibleList) 3718c42b0fSMatt Spinler { 3818c42b0fSMatt Spinler auto basePath = getPELReadOnlyDataPath(); 3918c42b0fSMatt Spinler fs::path fullPath; 4018c42b0fSMatt Spinler 4118c42b0fSMatt Spinler // Find an entry in the list of compatible system names that 4218c42b0fSMatt Spinler // matches a filename we have. 4318c42b0fSMatt Spinler 4418c42b0fSMatt Spinler for (const auto& name : compatibleList) 4518c42b0fSMatt Spinler { 4618c42b0fSMatt Spinler fs::path filename = name + calloutFileSuffix; 4718c42b0fSMatt Spinler 4818c42b0fSMatt Spinler // Check the debug path first 4918c42b0fSMatt Spinler fs::path path{fs::path{debugFilePath} / filename}; 5018c42b0fSMatt Spinler 5118c42b0fSMatt Spinler if (fs::exists(path)) 5218c42b0fSMatt Spinler { 5318c42b0fSMatt Spinler log<level::INFO>("Found device callout debug file"); 5418c42b0fSMatt Spinler fullPath = path; 5518c42b0fSMatt Spinler break; 5618c42b0fSMatt Spinler } 5718c42b0fSMatt Spinler 5818c42b0fSMatt Spinler path = basePath / filename; 5918c42b0fSMatt Spinler 6018c42b0fSMatt Spinler if (fs::exists(path)) 6118c42b0fSMatt Spinler { 6218c42b0fSMatt Spinler fullPath = path; 6318c42b0fSMatt Spinler break; 6418c42b0fSMatt Spinler } 6518c42b0fSMatt Spinler } 6618c42b0fSMatt Spinler 6718c42b0fSMatt Spinler if (fullPath.empty()) 6818c42b0fSMatt Spinler { 6918c42b0fSMatt Spinler throw std::invalid_argument( 7018c42b0fSMatt Spinler "No JSON dev path callout file for this system"); 7118c42b0fSMatt Spinler } 7218c42b0fSMatt Spinler 7318c42b0fSMatt Spinler return fullPath; 7418c42b0fSMatt Spinler } 7518c42b0fSMatt Spinler 7618c42b0fSMatt Spinler /** 7718c42b0fSMatt Spinler * @brief Reads the callout JSON into an object based on the 7818c42b0fSMatt Spinler * compatible system names list. 7918c42b0fSMatt Spinler * 8018c42b0fSMatt Spinler * @param[in] compatibleList - The list of compatible names for this 8118c42b0fSMatt Spinler * system. 8218c42b0fSMatt Spinler * 8318c42b0fSMatt Spinler * @return nlohmann::json - The JSON object 8418c42b0fSMatt Spinler */ 8518c42b0fSMatt Spinler nlohmann::json loadJSON(const std::vector<std::string>& compatibleList) 8618c42b0fSMatt Spinler { 8718c42b0fSMatt Spinler auto filename = getJSONFilename(compatibleList); 8818c42b0fSMatt Spinler std::ifstream file{filename}; 8918c42b0fSMatt Spinler return nlohmann::json::parse(file); 9018c42b0fSMatt Spinler } 9118c42b0fSMatt Spinler 92*44c0a643SMatt Spinler std::tuple<size_t, uint8_t> getI2CSearchKeys(const std::string& devPath) 93*44c0a643SMatt Spinler { 94*44c0a643SMatt Spinler std::smatch match; 95*44c0a643SMatt Spinler 96*44c0a643SMatt Spinler // Look for i2c-A/A-00BB 97*44c0a643SMatt Spinler // where A = bus number and BB = address 98*44c0a643SMatt Spinler std::regex regex{"i2c-[0-9]+/([0-9]+)-00([0-9a-f]{2})"}; 99*44c0a643SMatt Spinler 100*44c0a643SMatt Spinler regex_search(devPath, match, regex); 101*44c0a643SMatt Spinler 102*44c0a643SMatt Spinler if (match.size() != 3) 103*44c0a643SMatt Spinler { 104*44c0a643SMatt Spinler std::string msg = "Could not get I2C bus and address from " + devPath; 105*44c0a643SMatt Spinler throw std::invalid_argument{msg.c_str()}; 106*44c0a643SMatt Spinler } 107*44c0a643SMatt Spinler 108*44c0a643SMatt Spinler size_t bus = std::stoul(match[1].str(), nullptr, 0); 109*44c0a643SMatt Spinler 110*44c0a643SMatt Spinler // An I2C bus on a CFAM has everything greater than the 10s digit 111*44c0a643SMatt Spinler // as the CFAM number, so strip it off. Like: 112*44c0a643SMatt Spinler // 112 = cfam1 bus 12 113*44c0a643SMatt Spinler // 1001 = cfam10 bus 1 114*44c0a643SMatt Spinler bus = bus % 100; 115*44c0a643SMatt Spinler 116*44c0a643SMatt Spinler uint8_t address = std::stoul(match[2].str(), nullptr, 16); 117*44c0a643SMatt Spinler 118*44c0a643SMatt Spinler return {bus, address}; 119*44c0a643SMatt Spinler } 120*44c0a643SMatt Spinler 121*44c0a643SMatt Spinler std::string getFSISearchKeys(const std::string& devPath) 122*44c0a643SMatt Spinler { 123*44c0a643SMatt Spinler std::string links; 124*44c0a643SMatt Spinler std::smatch match; 125*44c0a643SMatt Spinler auto search = devPath; 126*44c0a643SMatt Spinler 127*44c0a643SMatt Spinler // Look for slave@XX: 128*44c0a643SMatt Spinler // where XX = link number in hex 129*44c0a643SMatt Spinler std::regex regex{"slave@([0-9a-f]{2}):"}; 130*44c0a643SMatt Spinler 131*44c0a643SMatt Spinler // Find all links in the path and separate them with hyphens. 132*44c0a643SMatt Spinler while (regex_search(search, match, regex)) 133*44c0a643SMatt Spinler { 134*44c0a643SMatt Spinler // Convert to an int first to handle a hex number like "0a" 135*44c0a643SMatt Spinler // though in reality there won't be more than links 0 - 9. 136*44c0a643SMatt Spinler auto linkNum = std::stoul(match[1].str(), nullptr, 16); 137*44c0a643SMatt Spinler links += std::to_string(linkNum) + '-'; 138*44c0a643SMatt Spinler 139*44c0a643SMatt Spinler search = match.suffix(); 140*44c0a643SMatt Spinler } 141*44c0a643SMatt Spinler 142*44c0a643SMatt Spinler if (links.empty()) 143*44c0a643SMatt Spinler { 144*44c0a643SMatt Spinler std::string msg = "Could not get FSI links from " + devPath; 145*44c0a643SMatt Spinler throw std::invalid_argument{msg.c_str()}; 146*44c0a643SMatt Spinler } 147*44c0a643SMatt Spinler 148*44c0a643SMatt Spinler // Remove the trailing '-' 149*44c0a643SMatt Spinler links.pop_back(); 150*44c0a643SMatt Spinler 151*44c0a643SMatt Spinler return links; 152*44c0a643SMatt Spinler } 153*44c0a643SMatt Spinler 154*44c0a643SMatt Spinler std::tuple<std::string, std::tuple<size_t, uint8_t>> 155*44c0a643SMatt Spinler getFSII2CSearchKeys(const std::string& devPath) 156*44c0a643SMatt Spinler { 157*44c0a643SMatt Spinler // This combines the FSI and i2C search keys 158*44c0a643SMatt Spinler 159*44c0a643SMatt Spinler auto links = getFSISearchKeys(devPath); 160*44c0a643SMatt Spinler auto busAndAddr = getI2CSearchKeys(devPath); 161*44c0a643SMatt Spinler 162*44c0a643SMatt Spinler return {std::move(links), std::move(busAndAddr)}; 163*44c0a643SMatt Spinler } 164*44c0a643SMatt Spinler 165*44c0a643SMatt Spinler size_t getSPISearchKeys(const std::string& devPath) 166*44c0a643SMatt Spinler { 167*44c0a643SMatt Spinler std::smatch match; 168*44c0a643SMatt Spinler 169*44c0a643SMatt Spinler // Look for spi_master/spiX/ where X is the SPI bus/port number 170*44c0a643SMatt Spinler // Note: This doesn't distinguish between multiple chips on 171*44c0a643SMatt Spinler // the same port as no need for it yet. 172*44c0a643SMatt Spinler std::regex regex{"spi_master/spi(\\d+)/"}; 173*44c0a643SMatt Spinler 174*44c0a643SMatt Spinler regex_search(devPath, match, regex); 175*44c0a643SMatt Spinler 176*44c0a643SMatt Spinler if (match.size() != 2) 177*44c0a643SMatt Spinler { 178*44c0a643SMatt Spinler std::string msg = "Could not get SPI bus from " + devPath; 179*44c0a643SMatt Spinler throw std::invalid_argument{msg.c_str()}; 180*44c0a643SMatt Spinler } 181*44c0a643SMatt Spinler 182*44c0a643SMatt Spinler size_t port = std::stoul(match[1].str()); 183*44c0a643SMatt Spinler 184*44c0a643SMatt Spinler return port; 185*44c0a643SMatt Spinler } 186*44c0a643SMatt Spinler 187*44c0a643SMatt Spinler std::tuple<std::string, size_t> getFSISPISearchKeys(const std::string& devPath) 188*44c0a643SMatt Spinler { 189*44c0a643SMatt Spinler 190*44c0a643SMatt Spinler // Combine the FSI and SPI search keys. 191*44c0a643SMatt Spinler auto links = getFSISearchKeys(devPath); 192*44c0a643SMatt Spinler auto bus = getSPISearchKeys(devPath); 193*44c0a643SMatt Spinler 194*44c0a643SMatt Spinler return {std::move(links), std::move(bus)}; 195*44c0a643SMatt Spinler } 196*44c0a643SMatt Spinler 19718c42b0fSMatt Spinler std::vector<device_callouts::Callout> 19818c42b0fSMatt Spinler calloutI2C(size_t i2cBus, uint8_t i2cAddress, 19918c42b0fSMatt Spinler const nlohmann::json& calloutJSON) 20018c42b0fSMatt Spinler { 20118c42b0fSMatt Spinler // TODO 20218c42b0fSMatt Spinler return {}; 20318c42b0fSMatt Spinler } 20418c42b0fSMatt Spinler 20518c42b0fSMatt Spinler std::vector<device_callouts::Callout> findCallouts(const std::string& devPath, 20618c42b0fSMatt Spinler const nlohmann::json& json) 20718c42b0fSMatt Spinler { 20818c42b0fSMatt Spinler std::vector<Callout> callouts; 209a307089cSMatt Spinler fs::path path; 21018c42b0fSMatt Spinler 211a307089cSMatt Spinler // Gives the /sys/devices/platform/ path 212a307089cSMatt Spinler try 213a307089cSMatt Spinler { 214a307089cSMatt Spinler path = fs::canonical(devPath); 215a307089cSMatt Spinler } 216a307089cSMatt Spinler catch (const fs::filesystem_error& e) 217a307089cSMatt Spinler { 218a307089cSMatt Spinler // Path not there, still try to do the callout 219a307089cSMatt Spinler path = devPath; 220a307089cSMatt Spinler } 221a307089cSMatt Spinler 222a307089cSMatt Spinler switch (util::getCalloutType(path)) 223a307089cSMatt Spinler { 224a307089cSMatt Spinler case util::CalloutType::i2c: 225a307089cSMatt Spinler // callouts = calloutI2CUsingPath(errnoValue, path, json); 226a307089cSMatt Spinler break; 227a307089cSMatt Spinler case util::CalloutType::fsi: 228a307089cSMatt Spinler // callouts = calloutFSI(errnoValue, path, json); 229a307089cSMatt Spinler break; 230a307089cSMatt Spinler case util::CalloutType::fsii2c: 231a307089cSMatt Spinler // callouts = calloutFSII2C(errnoValue, path, json); 232a307089cSMatt Spinler break; 233a307089cSMatt Spinler case util::CalloutType::fsispi: 234a307089cSMatt Spinler // callouts = calloutFSISPI(errnoValue, path, json); 235a307089cSMatt Spinler break; 236a307089cSMatt Spinler default: 237a307089cSMatt Spinler std::string msg = 238a307089cSMatt Spinler "Could not get callout type from device path: " + path.string(); 239a307089cSMatt Spinler throw std::invalid_argument{msg.c_str()}; 240a307089cSMatt Spinler break; 241a307089cSMatt Spinler } 24218c42b0fSMatt Spinler 24318c42b0fSMatt Spinler return callouts; 24418c42b0fSMatt Spinler } 24518c42b0fSMatt Spinler 246a307089cSMatt Spinler CalloutType getCalloutType(const std::string& devPath) 247a307089cSMatt Spinler { 248a307089cSMatt Spinler if ((devPath.find("fsi-master") != std::string::npos) && 249a307089cSMatt Spinler (devPath.find("i2c-") != std::string::npos)) 250a307089cSMatt Spinler { 251a307089cSMatt Spinler return CalloutType::fsii2c; 252a307089cSMatt Spinler } 253a307089cSMatt Spinler 254a307089cSMatt Spinler if ((devPath.find("fsi-master") != std::string::npos) && 255a307089cSMatt Spinler (devPath.find("spi") != std::string::npos)) 256a307089cSMatt Spinler { 257a307089cSMatt Spinler return CalloutType::fsispi; 258a307089cSMatt Spinler } 259a307089cSMatt Spinler 260a307089cSMatt Spinler // Treat anything else FSI related as plain FSI 261a307089cSMatt Spinler if (devPath.find("fsi-master") != std::string::npos) 262a307089cSMatt Spinler { 263a307089cSMatt Spinler return CalloutType::fsi; 264a307089cSMatt Spinler } 265a307089cSMatt Spinler 266a307089cSMatt Spinler if (devPath.find("i2c-bus/i2c-") != std::string::npos) 267a307089cSMatt Spinler { 268a307089cSMatt Spinler return CalloutType::i2c; 269a307089cSMatt Spinler } 270a307089cSMatt Spinler 271a307089cSMatt Spinler return CalloutType::unknown; 272a307089cSMatt Spinler } 273a307089cSMatt Spinler 27418c42b0fSMatt Spinler } // namespace util 27518c42b0fSMatt Spinler 27618c42b0fSMatt Spinler std::vector<Callout> getCallouts(const std::string& devPath, 27718c42b0fSMatt Spinler const std::vector<std::string>& compatibleList) 27818c42b0fSMatt Spinler { 27918c42b0fSMatt Spinler auto json = util::loadJSON(compatibleList); 28018c42b0fSMatt Spinler return util::findCallouts(devPath, json); 28118c42b0fSMatt Spinler } 28218c42b0fSMatt Spinler 28318c42b0fSMatt Spinler std::vector<Callout> 28418c42b0fSMatt Spinler getI2CCallouts(size_t i2cBus, uint8_t i2cAddress, 28518c42b0fSMatt Spinler const std::vector<std::string>& compatibleList) 28618c42b0fSMatt Spinler { 28718c42b0fSMatt Spinler auto json = util::loadJSON(compatibleList); 28818c42b0fSMatt Spinler return util::calloutI2C(i2cBus, i2cAddress, json); 28918c42b0fSMatt Spinler } 29018c42b0fSMatt Spinler 29118c42b0fSMatt Spinler } // namespace openpower::pels::device_callouts 292