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