/*
 * Copyright (c) 2018-2019, NVIDIA CORPORATION.  All rights reserved.
 *
 * NVIDIA CORPORATION and its licensors retain all intellectual property
 * and proprietary rights in and to this software, related documentation
 * and any modifications thereto.  Any use, reproduction, disclosure or
 * distribution of this software and related documentation without an express
 * license agreement from NVIDIA CORPORATION is strictly prohibited.
 */

#include "CNvMI2CAddrMgr.hpp"
#include <vector>
#include <algorithm>

namespace nvsipl
{

//! Constructor
CNvMI2CAddrMgr::CNvMI2CAddrMgr()
{
   /*! These addresses are reserved by I2C spec */
    reservedI2CAddr.push_back(0x0); // General call/START byte
    reservedI2CAddr.push_back(0x1); // CBUS address
    reservedI2CAddr.push_back(0x2); // Reserved for different bus format
    reservedI2CAddr.push_back(0x3); // Reserved for future purposes
    reservedI2CAddr.push_back(0x4); // Hs-mode master code
    reservedI2CAddr.push_back(0x5); // Hs-mode master code
    reservedI2CAddr.push_back(0x6); // Hs-mode master code
    reservedI2CAddr.push_back(0x7); // Hs-mode master code
    reservedI2CAddr.push_back(0x78); // 10-bit slave addressing
    reservedI2CAddr.push_back(0x79); // 10-bit slave addressing
    reservedI2CAddr.push_back(0x7A); // 10-bit slave addressing
    reservedI2CAddr.push_back(0x7B); // 10-bit slave addressing
    reservedI2CAddr.push_back(0x7C); // Reserved for future purposes
    reservedI2CAddr.push_back(0x7D); // Reserved for future purposes
    reservedI2CAddr.push_back(0x7E); // Reserved for future purposes
    reservedI2CAddr.push_back(0x7F); // Reserved for future purposes

    /*! Additional reserved address can be added here */
    reservedI2CAddr.push_back(0x58); //Device at 0x58 on i2c bus 2 of E3550.

    //TODO: Add reservedI2CAddr depending on module/platform.
}

void CNvMI2CAddrMgr::RegisterNativeI2CAddr(uint8_t nativeI2CAddr)
{
    if(std::find(reservedI2CAddr.begin(), reservedI2CAddr.end(), nativeI2CAddr) == reservedI2CAddr.end()) {
        reservedI2CAddr.push_back(nativeI2CAddr);
    } else {
        LOG_INFO("Native I2C address already registered\n");
    }
}

//! Auto-gen i2c address based on the native address
uint8_t CNvMI2CAddrMgr::GenerateI2CAddr(uint8_t nativeI2CAddr)
{
    uint8_t generatedI2CAddr = nativeI2CAddr;

    if (reservedI2CAddr.size() == 128u) {
        LOG_ERR("No available I2C address\n");
        return 0;
    }

    while (std::find(reservedI2CAddr.begin(), reservedI2CAddr.end(), generatedI2CAddr) != reservedI2CAddr.end()) {
        // Address is present in the table
        if (generatedI2CAddr == 127u) {
            generatedI2CAddr = 0u;
        } else {
            generatedI2CAddr++;
        }
    }

    reservedI2CAddr.push_back(generatedI2CAddr);
    LOG_INFO("Generated I2C address 0x%x for native I2C address 0x%x\n", generatedI2CAddr, nativeI2CAddr);
    return generatedI2CAddr;
}

} // end of namespace nvsipl
