-- 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.
-- You may obtain a copy of Mulan PSL v2 at:
--         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 CacheLocation = {
    -- Internal to the CPU
    Internal = 1,
    -- External to the CPU
    External = 2,
    -- Reserved
    Reserved = 3,
    -- Location Unknown
    Unknown = 4
}

local CacheOperationalMode = {
    -- Write Through
    WriteThrough = 0x00,
    -- Write Back
    WriteBack = 0x01,
    -- Varies with Memory Address
    VariesWithMemoryAddress = 0x02,
    -- Unknown Operational Mode
    Unknown = 0x03
}

local CacheConfiguaration = {}
CacheConfiguaration.__index = CacheConfiguaration

function CacheConfiguaration.new(v)
    return setmetatable({v}, CacheConfiguaration)
end

function CacheConfiguaration:cache_level()
    -- [bits 0, 1, 2] Cache Level – 1 through 8
    -- (For example, an L1 cache would use value
    -- 000b and an L3 cache would use 010b.
    return (self[1] & 0x0007) + 1
end

function CacheConfiguaration:cache_socketed()
    -- [bit 3] Cache Socketed
    --     1b - Socketed
    --     0b - Not socketed
    return (self[1] & 0x0008) == 0x0008
end

function CacheConfiguaration:location()
    -- [bits 5, 6] Location, relative to the CPU module:
    --     00b - Internal
    --     01b - External
    --     10b - Reserved
    --     11b - Unknown
    local v = self[1] & 0x0060
    if v == 0x0000 then
        return CacheLocation.Internal
    elseif v == 0x0020 then
        return CacheLocation.External
    elseif v == 0x0040 then
        return CacheLocation.Reserved
    elseif v == 0x0060 then
        return CacheLocation.Unknown
    end
    error('invalid location')
end

function CacheConfiguaration:enabled_at_boot()
    return (self[1] & 0x0080) == 0x0080
end

function CacheConfiguaration:operational_mode()
    local v = self[1] & 0x0300
    if v == 0x0000 then
        return CacheOperationalMode.WriteThrough
    elseif v == 0x0100 then
        return CacheOperationalMode.WriteBack
    elseif v == 0x0200 then
        return CacheOperationalMode.VariesWithMemoryAddress
    elseif v == 0x0300 then
        return CacheOperationalMode.Unknown
    end
    error('invalid operational_mode')
end

local SramTypes = {}
SramTypes.__index = SramTypes

function SramTypes.new(v)
    return setmetatable({v}, SramTypes)
end

function SramTypes:other()
    return (self[1] & 0x0001) == 0x0001
end

function SramTypes:unknown()
    return (self[1] & 0x0002) == 0x0002
end

function SramTypes:non_burst()
    return (self[1] & 0x0004) == 0x0004
end

function SramTypes:burst()
    return (self[1] & 0x0008) == 0x0008
end

function SramTypes:pipeline_burst()
    return (self[1] & 0x0010) == 0x0010
end

function SramTypes:synchronous()
    return (self[1] & 0x0020) == 0x0020
end

function SramTypes:asynchronous()
    return (self[1] & 0x0040) == 0x0040
end

local M = {}

function M.decode(type, length, handle, read_fields, strings)
    if length < 0x0F then
      return
    end

    local t = {}

    -- 2.0+
    if length >= 0x0F then
        local fields = read_fields('BHHHHH')
        t.socket_designation = strings[fields[1]]
        t.cache_configuration = CacheConfiguaration.new(fields[2])
        -- 为兼容V2的L3 Cache数据显示，还需要进一步处理
        -- value = (val_tmp & 0x8000) ? ((val_tmp & 0x7FFF) << 6) : val_tmp
        t.maximum_cache_size = fields[3]
        t.installed_size = fields[4]
        t.supported_sram_type = SramTypes.new(fields[5])
        t.current_sram_type = SramTypes.new(fields[6])
    end

    -- 2.1+
    if length >= 0x13 then
        local fields = read_fields('BBBB')
        t.cache_speed = fields[1]
        t.error_correction_type = fields[2]
        t.system_cache_type = fields[3]
        t.associativity = fields[4]
    end

    -- 3.1+
    if length >= 0x1B then
        local fields = read_fields('I4I4')
        t.maximum_cache_size_2 = fields[1]
        t.installed_cache_size_2 = fields[2]
    end
    return {Handle = handle, Length = length, Type = type, Strings = strings, Fields = t}
end

function M.get_alias_name_in_smbios(entry)
    return entry.Handle
end

M.CacheLocation = CacheLocation
M.CacheOperationalMode = CacheOperationalMode

return M
