#include <unistd.h>

#include <chrono>
#include <fstream>
#include <gpiod.hpp>
#include <phosphor-logging/log.hpp>
#include <registration.hpp>
#include <system_error>
#include <thread>

namespace openpower
{
namespace misc
{

constexpr auto cfamResetPath = "/sys/class/fsi-master/fsi0/device/cfam_reset";

using namespace phosphor::logging;

/**
 * @brief Reset the CFAM using the appropriate GPIO
 * @return void
 */
void cfamReset()
{

    // First look if system supports kernel sysfs based cfam reset
    // If it does then write a 1 and let the kernel handle the reset
    std::ofstream file;
    file.open(cfamResetPath);
    if (!file)
    {
        log<level::DEBUG>("system does not support kernel cfam reset, default "
                          "to using libgpiod");
    }
    else
    {
        // Write a 1 to have kernel toggle the reset
        file << "1";
        file.close();
        log<level::DEBUG>("cfam reset via sysfs complete");
        return;
    }

    // No kernel support so toggle gpio from userspace
    const std::string cfamReset = {"cfam-reset"};
    auto line = gpiod::find_line(cfamReset);
    if (!line)
    {
        log<level::ERR>("failed to find cfam-reset line");
        throw std::system_error(ENODEV, std::system_category());
    }

    // Configure this app to own the gpio while doing the reset
    gpiod::line_request conf;
    conf.consumer = "cfamReset";
    conf.request_type = gpiod::line_request::DIRECTION_OUTPUT;
    line.request(conf);

    // Put chips into reset
    line.set_value(0);

    // Sleep one second to ensure reset processed
    using namespace std::chrono_literals;
    std::this_thread::sleep_for(1s);

    // Take chips out of reset
    line.set_value(1);
}

REGISTER_PROCEDURE("cfamReset", cfamReset)

} // namespace misc
} // namespace openpower