Implement power control for x86 based platforms
This power control module provides the capability to
power on/off the host via gpio.
And provides some interfaces for system to contorl the system
power like:
setPowerState
getPowerState
Change-Id: Icd6530c42f2bc7c4d84062be786d25710b53f434
Signed-off-by: Kuiying Wang <kuiying.wang@intel.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..52780ba
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,8 @@
+cmake_minimum_required(VERSION 2.8.10 FATAL_ERROR)
+set(CMAKE_CXX_STANDARD 14)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/gpio/inc)
+
+add_subdirectory(gpio)
+add_subdirectory(power-control)
diff --git a/MAINTAINERS b/MAINTAINERS
new file mode 100644
index 0000000..50788a0
--- /dev/null
+++ b/MAINTAINERS
@@ -0,0 +1,46 @@
+How to use this list:
+ Find the most specific section entry (described below) that matches where
+ your change lives and add the reviewers (R) and maintainers (M) as
+ reviewers. You can use the same method to track down who knows a particular
+ code base best.
+
+ Your change/query may span multiple entries; that is okay.
+
+ If you do not find an entry that describes your request at all, someone
+ forgot to update this list; please at least file an issue or send an email
+ to a maintainer, but preferably you should just update this document.
+
+Description of section entries:
+
+ Section entries are structured according to the following scheme:
+
+ X: NAME <EMAIL_USERNAME@DOMAIN> <IRC_USERNAME!>
+ X: ...
+ .
+ .
+ .
+
+ Where REPO_NAME is the name of the repository within the OpenBMC GitHub
+ organization; FILE_PATH is a file path within the repository, possibly with
+ wildcards; X is a tag of one of the following types:
+
+ M: Denotes maintainer; has fields NAME <EMAIL_USERNAME@DOMAIN> <IRC_USERNAME!>;
+ if omitted from an entry, assume one of the maintainers from the
+ MAINTAINERS entry.
+ R: Denotes reviewer; has fields NAME <EMAIL_USERNAME@DOMAIN> <IRC_USERNAME!>;
+ these people are to be added as reviewers for a change matching the repo
+ path.
+ F: Denotes forked from an external repository; has fields URL.
+
+ Line comments are to be denoted "# SOME COMMENT" (typical shell style
+ comment); it is important to follow the correct syntax and semantics as we
+ may want to use automated tools with this file in the future.
+
+ A change cannot be added to an OpenBMC repository without a MAINTAINER's
+ approval; thus, a MAINTAINER should always be listed as a reviewer.
+
+START OF MAINTAINERS LIST
+-------------------------
+
+M: James Feist <james.feist@intel.com> <jfei!>
+M: Kuiying Wang <kuiying.wang@intel.com> <kuiyingw!>
\ No newline at end of file
diff --git a/cmake/Config.cmake.in b/cmake/Config.cmake.in
new file mode 100644
index 0000000..cf41347
--- /dev/null
+++ b/cmake/Config.cmake.in
@@ -0,0 +1,13 @@
+# - Config file for '@PROJECT_NAME@' package
+# It defines the following variables
+# @PROJECT_NAME_UPPERCASE@_INCLUDE_DIRS - include directories
+# @PROJECT_NAME_UPPERCASE@_LIBRARIES - libraries to link against
+
+# Include directory
+set(@PROJECT_NAME_UPPERCASE@_INCLUDE_DIRS "@INSTALL_INCLUDE_DIR@")
+
+# Import the exported targets
+include("@INSTALL_CMAKE_DIR@/@PROJECT_NAME@Targets.cmake")
+
+# Set the expected library variable
+set(@PROJECT_NAME_UPPERCASE@_LIBRARIES @LIBRARY_NAME@)
diff --git a/gpio/CMakeLists.txt b/gpio/CMakeLists.txt
new file mode 100644
index 0000000..bd51f3e
--- /dev/null
+++ b/gpio/CMakeLists.txt
@@ -0,0 +1,42 @@
+cmake_minimum_required(VERSION 2.8.10 FATAL_ERROR)
+project(chassisgpio CXX)
+set(CMAKE_CXX_STANDARD 14)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+set(LIBRARY_NAME "${PROJECT_NAME}")
+set(INSTALL_BIN_DIR ${CMAKE_INSTALL_PREFIX}/bin)
+set(INSTALL_LIB_DIR ${CMAKE_INSTALL_PREFIX}/lib)
+set(PROJECT_CMAKE_FILES ${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY})
+
+set(INSTALL_INCLUDE_DIR ${CMAKE_INSTALL_PREFIX}/include)
+set(DEF_INSTALL_CMAKE_DIR ${CMAKE_INSTALL_PREFIX}/lib/cmake)
+set(INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR})
+
+include(GNUInstallDirs)
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/inc)
+
+add_library(${PROJECT_NAME} SHARED src/gpio.cpp)
+
+set_target_properties(${PROJECT_NAME} PROPERTIES VERSION "0.1.0")
+set_target_properties(${PROJECT_NAME} PROPERTIES SOVERSION "0")
+
+install(TARGETS ${PROJECT_NAME}
+ EXPORT "${PROJECT_NAME}EXPORT"
+ RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin
+ LIBRARY DESTINATION "${INSTALL_LIB_DIR}" COMPONENT shlib
+ ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" COMPONENT stlib
+ COMPONENT dev)
+
+install(FILES "inc/gpio.hpp"
+ DESTINATION "${INSTALL_INCLUDE_DIR}/" )
+
+install(EXPORT "${PROJECT_NAME}EXPORT"
+ DESTINATION "lib/cmake"
+ FILE ${PROJECT_NAME}Targets.cmake)
+
+configure_file(${CMAKE_SOURCE_DIR}/cmake/Config.cmake.in
+ "${PROJECT_CMAKE_FILES}/${PROJECT_NAME}-config.cmake" @ONLY)
+
+install(FILES
+ "${PROJECT_CMAKE_FILES}/${PROJECT_NAME}-config.cmake"
+ DESTINATION "lib/cmake" COMPONENT dev)
diff --git a/gpio/inc/gpio.hpp b/gpio/inc/gpio.hpp
new file mode 100644
index 0000000..0c29381
--- /dev/null
+++ b/gpio/inc/gpio.hpp
@@ -0,0 +1,19 @@
+/*
+// Copyright (c) 2018 Intel 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.
+*/
+
+#pragma once
+int configGpio(const char *gpioName, int *fd, sdbusplus::bus::bus &bus);
+int closeGpio(int fd);
\ No newline at end of file
diff --git a/gpio/src/gpio.cpp b/gpio/src/gpio.cpp
new file mode 100644
index 0000000..de669eb
--- /dev/null
+++ b/gpio/src/gpio.cpp
@@ -0,0 +1,190 @@
+/*
+// Copyright (c) 2018 Intel 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 <experimental/filesystem>
+#include <fcntl.h>
+#include <fstream>
+#include <phosphor-logging/elog-errors.hpp>
+#include <unistd.h>
+#include <xyz/openbmc_project/Common/error.hpp>
+#include "gpio.hpp"
+
+const static constexpr char *SYSMGR_SERVICE = "org.openbmc.managers.System";
+const static constexpr char *SYSMGR_OBJ_PATH = "/org/openbmc/managers/System";
+const static constexpr char *SYSMGR_INTERFACE = "org.openbmc.managers.System";
+
+int closeGpio(int fd)
+{
+ if (fd > 0)
+ {
+ ::close(fd);
+ }
+ return 0;
+}
+
+int configGpio(const char *gpioName, int *fd, sdbusplus::bus::bus &bus)
+{
+ sdbusplus::message::message method = bus.new_method_call(
+ SYSMGR_SERVICE, SYSMGR_OBJ_PATH, SYSMGR_INTERFACE, "gpioInit");
+
+ method.append(gpioName);
+
+ sdbusplus::message::message result = bus.call(method);
+
+ if (result.is_method_error())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "configGPIO: bus call error!");
+ return -1;
+ }
+
+ int32_t gpioNum = -1;
+ std::string gpioDev;
+ std::string gpioDirection;
+
+ result.read(gpioDev, gpioNum, gpioDirection);
+
+ if (gpioDev.empty())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "configGPIO: gpioDev error!");
+ return -1;
+ }
+
+ if (gpioDirection.empty())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "configGPIO: gpioDirection error!");
+ return -1;
+ }
+
+ std::fstream stream;
+
+ stream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
+
+ std::string devPath =
+ gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
+
+ std::experimental::filesystem::path fullPath(devPath);
+
+ if (std::experimental::filesystem::exists(fullPath))
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "GPIO exported",
+ phosphor::logging::entry("PATH=%s", devPath.c_str()));
+ }
+ else
+ {
+ devPath = gpioDev + "/export";
+
+ stream.open(devPath, std::fstream::out);
+
+ if (!stream.good())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error in opening for write!",
+ phosphor::logging::entry("PATH=%s", devPath.c_str()),
+ phosphor::logging::entry("NUM=%d", gpioNum));
+ return -1;
+ }
+
+ stream << gpioNum;
+ stream.close();
+ }
+
+ if (gpioDirection == "out")
+ {
+ devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
+
+ uint32_t currentValue = 0;
+
+ stream.open(devPath, std::fstream::in);
+
+ if (!stream.good())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error in opening for read!",
+ phosphor::logging::entry("PATH=%s", devPath.c_str()));
+ return -1;
+ }
+
+ stream >> currentValue;
+ stream.close();
+
+ const char *direction = currentValue ? "high" : "low";
+
+ devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
+
+ stream.open(devPath, std::fstream::out);
+
+ if (!stream.good())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error in opening for write!");
+ return -1;
+ }
+
+ stream << direction;
+ stream.close();
+ }
+ else if (gpioDirection == "in")
+ {
+ devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/direction";
+
+ stream.open(devPath, std::fstream::out);
+
+ if (!stream.good())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error in opening for write!");
+ return -1;
+ }
+
+ stream << gpioDirection;
+ stream.close();
+ }
+ else if (gpioDirection == "both")
+ {
+
+ // For gpio configured as ‘both’, it is an interrupt pin and trigged on
+ // both rising and falling signals
+ devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/edge";
+
+ stream.open(devPath, std::fstream::out);
+
+ if (!stream.good())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error in opening for write!");
+ return -1;
+ }
+
+ stream << gpioDirection;
+ stream.close();
+ }
+
+ devPath = gpioDev + "/gpio" + std::to_string(gpioNum) + "/value";
+
+ *fd = ::open(devPath.c_str(), O_RDWR | O_NONBLOCK);
+
+ if (*fd < 0)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>("open error!");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/power-control/CMakeLists.txt b/power-control/CMakeLists.txt
new file mode 100644
index 0000000..282620d
--- /dev/null
+++ b/power-control/CMakeLists.txt
@@ -0,0 +1,48 @@
+cmake_minimum_required(VERSION 2.8.10 FATAL_ERROR)
+project(power-control CXX)
+set(CMAKE_CXX_STANDARD 14)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_RPATH}
+ ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR})
+set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
+include(GNUInstallDirs)
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/inc)
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+set(DBUS_OBJECT_NAME "xyz/openbmc_project/Chassis/Control/Power")
+set(DBUS_INTF_NAME "xyz.openbmc_project.Chassis.Control.Power")
+
+add_definitions(-DDBUS_OBJECT_NAME="/${DBUS_OBJECT_NAME}0")
+add_definitions(-DDBUS_INTF_NAME="${DBUS_INTF_NAME}")
+set(SRC_FILES
+ src/power_control.cpp
+ src/main.cpp
+)
+
+# import sdbusplus
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(SDBUSPLUSPLUS sdbusplus REQUIRED)
+include_directories(${SDBUSPLUSPLUS_INCLUDE_DIRS})
+link_directories(${SDBUSPLUSPLUS_LIBRARY_DIRS})
+find_program(SDBUSPLUSPLUS sdbus++)
+
+# import phosphor-logging
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(LOGGING phosphor-logging REQUIRED)
+include_directories(${LOGGING_INCLUDE_DIRS})
+link_directories(${LOGGING_LIBRARY_DIRS})
+
+# phosphor-dbus-interfaces
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(DBUSINTERFACE phosphor-dbus-interfaces REQUIRED)
+include_directories(${DBUSINTERFACE_INCLUDE_DIRS})
+link_directories(${DBUSINTERFACE_LIBRARY_DIRS})
+
+add_executable(${PROJECT_NAME} ${SRC_FILES})
+target_link_libraries(${PROJECT_NAME} ${DBUSINTERFACE_LIBRARIES}
+ chassisgpio )
+target_link_libraries(${PROJECT_NAME} "${SDBUSPLUSPLUS_LIBRARIES} -lstdc++fs -lphosphor_dbus")
+
+install (TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/power-control/inc/power_control.hpp b/power-control/inc/power_control.hpp
new file mode 100644
index 0000000..4983fb7
--- /dev/null
+++ b/power-control/inc/power_control.hpp
@@ -0,0 +1,169 @@
+/*
+// Copyright (c) 2018 Intel 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.
+*/
+
+#pragma once
+#include <fcntl.h>
+#include <linux/aspeed-lpc-sio.h>
+#include <unistd.h>
+#include <phosphor-logging/elog-errors.hpp>
+#include <xyz/openbmc_project/Chassis/Common/error.hpp>
+#include <xyz/openbmc_project/Chassis/Control/Power/server.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+#include "gpio.hpp"
+
+static constexpr size_t POLLING_INTERVAL_MS = 500;
+
+const static constexpr char* LPC_SIO_DEVPATH = "/dev/lpc-sio";
+const static constexpr char* PGOOD_PIN = "PGOOD";
+const static constexpr char* POWER_UP_PIN = "POWER_UP_PIN";
+
+const static constexpr size_t PCH_DEVICE_BUS_ADDRESS = 3;
+const static constexpr size_t PCH_DEVICE_SLAVE_ADDRESS = 0x44;
+const static constexpr size_t PCH_CMD_REGISTER = 0;
+const static constexpr size_t PCH_POWER_DOWN_CMD = 0x02;
+
+const static constexpr size_t POWER_UP_PIN_PULSE_TIME_MS = 200;
+
+using pwr_control =
+ sdbusplus::xyz::openbmc_project::Chassis::Control::server::Power;
+
+struct PowerControl : sdbusplus::server::object_t<pwr_control>
+{
+ PowerControl(sdbusplus::bus::bus& bus, const char* path,
+ phosphor::watchdog::EventPtr event,
+ sd_event_io_handler_t handler = PowerControl::EventHandler) :
+ sdbusplus::server::object_t<pwr_control>(bus, path),
+ bus(bus), callbackHandler(handler)
+ {
+ int ret = -1;
+ char buf = '0';
+
+ // config gpio
+ ret = configGpio(PGOOD_PIN, &pgood_fd, bus);
+ if (ret < 0)
+ {
+ throw std::runtime_error("failed to config PGOOD_PIN");
+ }
+
+ ret = configGpio(POWER_UP_PIN, &power_up_fd, bus);
+ if (ret < 0)
+ {
+ closeGpio(pgood_fd);
+ throw std::runtime_error("failed to config POWER_UP_PIN");
+ }
+
+ ret = sd_event_add_io(event.get(), nullptr, pgood_fd, EPOLLPRI,
+ callbackHandler, this);
+ if (ret < 0)
+ {
+ closeGpio(pgood_fd);
+ closeGpio(power_up_fd);
+ throw std::runtime_error("failed to add to event loop");
+ }
+
+ timer.start(std::chrono::duration_cast<std::chrono::microseconds>(
+ std::chrono::milliseconds(POLLING_INTERVAL_MS)));
+ timer.setEnabled<std::true_type>();
+ phosphor::logging::log<phosphor::logging::level::DEBUG>("Enable timer");
+ }
+
+ ~PowerControl()
+ {
+ closeGpio(pgood_fd);
+ closeGpio(power_up_fd);
+ }
+
+ static int EventHandler(sd_event_source* es, int fd, uint32_t revents,
+ void* userdata)
+ {
+ // For the first event, only set the initial status, do not emit signal
+ // since is it not triggered by the real gpio change
+ static bool first_event = true;
+ int n = -1;
+ char buf = '0';
+
+ if (!userdata)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "userdata null!");
+ return -1;
+ }
+
+ PowerControl* powercontrol = static_cast<PowerControl*>(userdata);
+
+ if (!powercontrol)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "null pointer!");
+ return -1;
+ }
+
+ n = ::lseek(fd, 0, SEEK_SET);
+ if (n < 0)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "lseek error!");
+ return n;
+ }
+
+ n = ::read(fd, &buf, sizeof(buf));
+ if (n < 0)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "read error!");
+ return n;
+ }
+
+ if (buf == '0')
+ {
+ powercontrol->state(0);
+ powercontrol->pgood(0);
+
+ if (first_event)
+ {
+ first_event = false;
+ }
+ else
+ {
+ powercontrol->powerLost();
+ }
+ }
+ else
+ {
+ powercontrol->state(1);
+ powercontrol->pgood(1);
+ if (first_event)
+ {
+ first_event = false;
+ }
+ else
+ {
+ powercontrol->powerGood();
+ }
+ }
+
+ return 0;
+ }
+
+ int32_t setPowerState(int32_t newState) override;
+ int32_t getPowerState() override;
+
+ private:
+ int power_up_fd;
+ int pgood_fd;
+ sdbusplus::bus::bus& bus;
+ sd_event_io_handler_t callbackHandler;
+};
diff --git a/power-control/src/main.cpp b/power-control/src/main.cpp
new file mode 100644
index 0000000..695666e
--- /dev/null
+++ b/power-control/src/main.cpp
@@ -0,0 +1,64 @@
+/*
+// Copyright (c) 2018 Intel 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 "power_control.hpp"
+
+int main(int argc, char* argv[])
+{
+ int ret = 0;
+
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "Start Chassis power control service...");
+
+ sd_event* event = nullptr;
+ ret = sd_event_default(&event);
+ if (ret < 0)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error creating a default sd_event handler");
+ return ret;
+ }
+ phosphor::watchdog::EventPtr eventP{event,
+ phosphor::watchdog::EventDeleter()};
+ event = nullptr;
+
+ sdbusplus::bus::bus bus = sdbusplus::bus::new_default();
+ sdbusplus::server::manager_t m{bus, DBUS_OBJECT_NAME};
+
+ bus.request_name(DBUS_INTF_NAME);
+
+ PowerControl powerControl{bus, DBUS_OBJECT_NAME, eventP};
+
+ auto now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(
+ std::chrono::system_clock::now());
+
+ try
+ {
+ bus.attach_event(eventP.get(), SD_EVENT_PRIORITY_NORMAL);
+ ret = sd_event_loop(eventP.get());
+ if (ret < 0)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error occurred during the sd_event_loop",
+ phosphor::logging::entry("RET=%d", ret));
+ }
+ }
+ catch (std::exception& e)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
+ return -1;
+ }
+ return 0;
+}
diff --git a/power-control/src/power_control.cpp b/power-control/src/power_control.cpp
new file mode 100644
index 0000000..74b64b8
--- /dev/null
+++ b/power-control/src/power_control.cpp
@@ -0,0 +1,82 @@
+/*
+// Copyright (c) 2018 Intel 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 "power_control.hpp"
+
+int32_t PowerControl::setPowerState(int32_t newState)
+{
+ int ret = 0;
+ int count = 0;
+ char buf = '0';
+
+ phosphor::logging::log<phosphor::logging::level::DEBUG>(
+ "setPowerState", phosphor::logging::entry("NEWSTATE=%d", newState));
+
+ if (state() == newState)
+ {
+ phosphor::logging::log<phosphor::logging::level::WARNING>(
+ "Same powerstate",
+ phosphor::logging::entry("NEWSTATE=%d", newState));
+ return 0;
+ }
+
+ ret = ::lseek(power_up_fd, 0, SEEK_SET);
+ if (ret < 0)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "lseek error!");
+ throw sdbusplus::xyz::openbmc_project::Chassis::Common::Error::
+ IOError();
+ }
+
+ /*
+ This power control just handle out pin "POWER_UP_PIN", change it
+ to low "0" form high "1" and set it back to high after over 200ms,
+ which will notify host (PCH) to switch power. Host to determine it
+ is power on or power off operation based on current power status.
+ For BMC (power control), just need to notify host (PCH) to switch
+ power, don't need to judge it should power on or off.
+ */
+ buf = '0';
+ ret = ::write(power_up_fd, &buf, sizeof(buf));
+ if (ret < 0)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "write error for setting 0 !");
+ throw sdbusplus::xyz::openbmc_project::Chassis::Common::Error::
+ IOError();
+ }
+
+ std::this_thread::sleep_for(
+ std::chrono::milliseconds(POWER_UP_PIN_PULSE_TIME_MS));
+
+ buf = '1';
+ ret = ::write(power_up_fd, &buf, sizeof(buf));
+ if (ret < 0)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "write error for setting 1 !");
+ throw sdbusplus::xyz::openbmc_project::Chassis::Common::Error::
+ IOError();
+ }
+
+ state(newState);
+ return 0;
+}
+
+int32_t PowerControl::getPowerState()
+{
+ return state();
+}