-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.
local log = require 'mc.logging'

-- 定义寄存器值和温度值的对应关系表
local  VAL32_TABLE <const> = {
    [1] = 0x3e,     [2] = 0x4a,     [3] = 0x5b,     [4] = 0x6a,     [5] = 0x7b,
    [6] = 0x99,     [7] = 0x9c,     [8] = 0xae,     [9] = 0xbd,     [10] = 0xce,
    [11] = 0xe3,    [12] = 0xf8,    [13] = 0x109,   [14] = 0x11e,   [15] = 0x12f,
    [16] = 0x143,   [17] = 0x157,   [18] = 0x16e,   [19] = 0x187,   [20] = 0x19f,
    [21] = 0x1b7,   [22] = 0x1cf,   [23] = 0x1e9,   [24] = 0x202,   [25] = 0x218,
    [26] = 0x23d,   [27] = 0x259,   [28] = 0x275,   [29] = 0x295,   [30] = 0x2b5,
    [31] = 0x2d7,   [32] = 0x2fb,   [33] = 0x31F
}

local TEMP_TABLE <const> = {
    [1] = -31.2,    [2] = -25.8,    [3] = -21.5,    [4] = -17.1,    [5] = -12.2,
    [6] = -7.7,     [7] = -2.6,     [8] = 2,        [9] = 7,        [10] = 12,
    [11] = 17,      [12] = 22,      [13] = 27,      [14] = 32,      [15] = 37,
    [16] = 42,      [17] = 47,      [18] = 52,      [19] = 57,      [20] = 62,
    [21] = 67,      [22] = 72,      [23] = 77,      [24] = 82,      [25] = 87,
    [26] = 92,      [27] = 98,      [28] = 103,     [29] = 109,     [30] = 114.5,
    [31] = 120,     [32] = 125,     [33] = 130
}

-- 根据厂商sdk，有效数据为bit[9:0]
local VALID_MASK <const> = 0x3ff

local function round(num)
    if num >= 0 then
        return math.floor(num)
    else
        return math.ceil(num)
    end
end

local function find_temperature_by_reg(sensor_value32)
    local pos = 1

    -- 找到寄存器值所在区间
    if sensor_value32 >= VAL32_TABLE[1] then
        for i = 1, #VAL32_TABLE - 1 do
            if sensor_value32 >= VAL32_TABLE[i] and sensor_value32 < VAL32_TABLE[i + 1] then
                break
            end
            pos = i
        end
    end

    -- 确定区间边界
    local low_reg = VAL32_TABLE[pos]
    local high_reg = VAL32_TABLE[pos + 1]
    local low_temp = TEMP_TABLE[pos]
    local high_temp = TEMP_TABLE[pos + 1]

    -- 线性插值计算温度
    local sensor0_temp = low_temp + (sensor_value32 - low_reg) * (high_temp - low_temp) / (high_reg - low_reg)

    return round(sensor0_temp)
end

local function get_retimer_prop(protocol, args)
    local ok, msg = protocol:retimer_read(args)
    if not ok then
        log:error('fail to read retimer, error: %s', msg)
        return false
    end
    return true, msg
end

local function get_retimer_temp(protocol, argument)
    -- 基于CS81532的SDK手册，获取温度需要先给ena_reg地址写入0
    local term = 0
    local ok, msg = protocol:retimer_write({ addr = argument.ena_reg, data = term, use_pec = argument.use_pec })
    if not ok then
        log:error('fail to turn off the retimer sensor, fail to write ctl_reg')
        return false, nil
    end

    ok, _ = protocol:retimer_write({ addr = argument.ena_reg, data = argument.change_mode, use_pec = argument.use_pec })
    if not ok then
        log:error('fail to read retimer temperature, fail to change the sensor to obtain temperature mode.')
        return false, nil
    end

    ok, _ = protocol:retimer_write({ addr = argument.ena_reg, data = argument.get_temp, use_pec = argument.use_pec })
    if not ok then
        log:error('fail to read retimer temperature, fail to enable the sensor to obtain temperature')
        return false, nil
    end
    
    local args = { addr = argument.sts_reg, use_pec = argument.use_pec }
    -- 根据SDK，直到第16位为1，刷新valid bit才成功
    ok, msg = protocol:retimer_loop_read(args, function(msg)
        return ((msg >> 16) & 0x1) == 1
    end)
    if not ok then
        log:error('fail to read retimer temperature, retimer_loop_read fail: %s',msg)
        return false, nil
    end

    -- 数据采集完成后需要再读一遍寄存器获取有效数字
    ok, msg = protocol:retimer_read(args)
    if not ok then
        log:error('fail to read retimer temperature, final retimer read fail: %s', msg)
        return false, nil
    end

    -- 采集数据完毕后需要关闭sensor
    ok, _ = protocol:retimer_write({ addr = argument.ena_reg, data = term, use_pec = argument.use_pec })
    if not ok then 
        log:error('fail to turn off the retimer sensor, fail to write ctl_reg')
        return false, nil
    end

    msg = msg & VALID_MASK
    local temperature = find_temperature_by_reg(msg)

    return true, temperature
end

local function get_retimer_max_temp(protocol, args)
    -- sensor 0
    local ok0, temp_0 = get_retimer_temp(protocol, args['0'])
    -- sensor 1
    local ok1, temp_1 = get_retimer_temp(protocol, args['1'])
    if not ok0 and not ok1 then
        log:error('sensor 0 and sensor 1 temperature read fail')
        return false, nil
    end

    temp_0 = temp_0 and temp_0 or 0
    temp_1 = temp_1 and temp_1 or 0
    return true, {temp_0 = temp_0, temp_1 = temp_1}
end

local function parse_version(msg)
    local str = string.format("%08X", msg)
    -- 由于user_ver在定义时写在高6位，需要右移两位获取正确值
    local user_ver = tonumber(str:sub(1, 2), 16) >> 2
    -- 版本号格式: ver1.ver2.ver3.user_ver
    -- user_ver用于区分不同retimer固件类型
    return string.format("%s.%s.%s.%s", str:sub(3, 4), str:sub(5, 6), str:sub(7, 8), string.format("%02X", user_ver))
end

local function parse_temperature(msg)
    return (msg.temp_0 > msg.temp_1) and msg.temp_0 or msg.temp_1
end

-- retimer的动态属性，需要轮询获取
local PARAMETERS_DYNAMIC<const> = {
    ['TemperatureCelsius'] = {
        fun = get_retimer_max_temp,
        args = {
            ['0'] = {ctl_reg = 0x01006000, sts_reg = 0x01006010,
                        ena_reg = 0x0100600C, change_mode = 0x00000f00,
                        get_temp = 0x00000101, use_pec = true},
            ['1'] = {ctl_reg = 0x01006004, sts_reg = 0x01006014,
                        ena_reg = 0x0100600C, change_mode = 0x00000f00,
                        get_temp = 0x00000101, use_pec = true}
        },
        callback = parse_temperature
    }
}

-- retimer的固有属性，仅需上电后获取一次
local PARAMETERS_INHERENT<const> = {
    ['FirmwareVersion'] = {
        fun = get_retimer_prop,
        args = { addr = 0x01050004, use_pec = true },
        callback = parse_version
    },
}

return {
    PARAMETERS_DYNAMIC = PARAMETERS_DYNAMIC,
    PARAMETERS_INHERENT = PARAMETERS_INHERENT,
    DEFAULT_VALUE = {
        ['TemperatureCelsius'] = 0
    },
    FAILURE_VALUE = {
        ['TemperatureCelsius'] = 0,
        ['FirmwareVersion'] = "N/A"
    }
}