# This file describes eFuses fields and registers for ESP32 chip
#
# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
#
# SPDX-License-Identifier: GPL-2.0-or-later

import copy
from dataclasses import dataclass
import os

import yaml

from ..mem_definition_base import (
    EfuseBlocksBase,
    EfuseFieldsBase,
    EfuseRegistersBase,
    Field,
)


@dataclass(frozen=True)
class EfuseDefineRegisters(EfuseRegistersBase):
    EFUSE_MEM_SIZE = 0x011C + 4

    # EFUSE registers & command/conf values
    DR_REG_EFUSE_BASE = 0x3FF5A000
    EFUSE_REG_CONF = DR_REG_EFUSE_BASE + 0x0FC
    EFUSE_CONF_WRITE = 0x5A5A
    EFUSE_CONF_READ = 0x5AA5
    EFUSE_REG_CMD = DR_REG_EFUSE_BASE + 0x104
    EFUSE_CMD_OP_MASK = 0x3
    EFUSE_CMD_WRITE = 0x2
    EFUSE_CMD_READ = 0x1

    # 3/4 Coding scheme warnings registers
    EFUSE_REG_DEC_STATUS = DR_REG_EFUSE_BASE + 0x11C
    EFUSE_REG_DEC_STATUS_MASK = 0xFFF

    # Coding Scheme
    EFUSE_CODING_SCHEME_WORD = 6
    EFUSE_CODING_SCHEME_MASK = 0x3

    # Efuse clock control
    EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x118
    EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x0F8
    EFUSE_DAC_CLK_DIV_MASK = 0xFF
    EFUSE_CLK_SEL0_MASK = 0x00FF
    EFUSE_CLK_SEL1_MASK = 0xFF00

    EFUSE_CLK_SETTINGS = {
        # APB freq: clk_sel0, clk_sel1, dac_clk_div
        # Taken from TRM chapter "eFuse Controller": Timing Configuration
        # 80 is here for completeness only as esptool never sets an 80MHz APB clock
        26: (250, 255, 52),
        40: (160, 255, 80),
        80: (80, 128, 100),
    }

    DR_REG_SYSCON_BASE = 0x3FF66000
    APB_CTL_DATE_ADDR = DR_REG_SYSCON_BASE + 0x7C
    APB_CTL_DATE_V = 0x1
    APB_CTL_DATE_S = 31

    EFUSE_BLK0_RDATA3_REG = DR_REG_EFUSE_BASE + 0x00C
    EFUSE_RD_CHIP_VER_REV1 = 1 << 15

    EFUSE_BLK0_RDATA5_REG = DR_REG_EFUSE_BASE + 0x014
    EFUSE_RD_CHIP_VER_REV2 = 1 << 20


class EfuseDefineBlocks(EfuseBlocksBase):
    __base_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE
    # List of efuse blocks
    # fmt: off
    BLOCKS = [
        # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose
        ("BLOCK0",    [],                                     0, __base_regs + 0x000, __base_regs + 0x01C, None, None, 7, None),
        ("BLOCK1",    ["flash_encryption"],                   1, __base_regs + 0x038, __base_regs + 0x098, 7,    0,    8, None),
        ("BLOCK2",    ["secure_boot_v1", "secure_boot_v2"],   2, __base_regs + 0x058, __base_regs + 0x0B8, 8,    1,    8, None),
        ("BLOCK3",    [],                                     3, __base_regs + 0x078, __base_regs + 0x0D8, 9,    2,    8, None),
    ]
    # fmt: on

    def get_burn_block_data_names(self):
        list_of_names = []
        for block in self.BLOCKS:
            blk = self.get(block)
            if blk.name:
                list_of_names.append(blk.name)
        return list_of_names


class EfuseDefineFields(EfuseFieldsBase):
    def __init__(self, extend_efuse_table) -> None:
        self.EFUSES = []
        # if MAC_VERSION is set "1", these efuse fields are in BLOCK3:
        self.CUSTOM_MAC = []
        # The len of fields depends on coding scheme: for CODING_SCHEME_NONE
        self.KEYBLOCKS_256 = []
        # The len of fields depends on coding scheme: for CODING_SCHEME_34
        self.KEYBLOCKS_192 = []
        # if BLK3_PART_RESERVE is set, these efuse fields are in BLOCK3:
        self.ADC_CALIBRATION = []

        self.CALC = []

        dir_name = os.path.dirname(os.path.abspath(__file__))
        dir_name, file_name = os.path.split(dir_name)
        file_name = file_name + ".yaml"
        dir_name, _ = os.path.split(dir_name)
        efuse_file = os.path.join(dir_name, "efuse_defs", file_name)
        with open(f"{efuse_file}") as r_file:
            e_desc = yaml.safe_load(r_file)
        super().__init__(e_desc, extend_efuse_table)

        for i, efuse in enumerate(self.ALL_EFUSES):
            if efuse.name == "BLOCK1" or efuse.name == "BLOCK2":
                self.KEYBLOCKS_256.append(efuse)
                BLOCK = copy.deepcopy(efuse)
                BLOCK.type = "bytes:24"
                BLOCK.bit_len = 24 * 8
                self.KEYBLOCKS_192.append(BLOCK)
                self.ALL_EFUSES[i] = None

            elif efuse.name == "MAC_VERSION":
                # A field from BLOCK3, It is used as a template
                BLOCK3 = copy.deepcopy(efuse)
                BLOCK3.name = "BLOCK3"
                BLOCK3.block = 3
                BLOCK3.word = 0
                BLOCK3.pos = 0
                BLOCK3.bit_len = 32 * 8
                BLOCK3.type = "bytes:32"
                BLOCK3.category = "security"
                BLOCK3.class_type = "keyblock"
                BLOCK3.description = "Variable Block 3"
                self.KEYBLOCKS_256.append(BLOCK3)

                BLOCK3 = copy.deepcopy(BLOCK3)
                BLOCK3.type = "bytes:24"
                BLOCK3.bit_len = 24 * 8
                self.KEYBLOCKS_192.append(BLOCK3)

            elif efuse.category == "calibration" and efuse.block == 3:
                self.ADC_CALIBRATION.append(efuse)
                self.ALL_EFUSES[i] = None

            elif efuse.name in ["CUSTOM_MAC_CRC", "CUSTOM_MAC"]:
                self.CUSTOM_MAC.append(efuse)
                self.ALL_EFUSES[i] = None

            elif efuse.category == "spi pad":
                efuse.class_type = "spipin"

        self.CALC.append(
            Field(
                name="WAFER_VERSION_MAJOR",
                block=0,
                bit_len=3,
                type="uint",
                category="identity",
                class_type="wafer",
                description="calc WAFER VERSION MAJOR from CHIP_VER_REV1 and CHIP_VER_REV2 and apb_ctl_date (read only)",
            )
        )

        self.CALC.append(
            Field(
                name="PKG_VERSION",
                block=0,
                bit_len=4,
                type="uint",
                category="identity",
                class_type="pkg",
                description="calc Chip package = CHIP_PACKAGE_4BIT << 3 + CHIP_PACKAGE (read only)",
            )
        )

        for efuse in self.ALL_EFUSES:
            if efuse is not None:
                self.EFUSES.append(efuse)

        self.ALL_EFUSES = []
