1# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton,
2# Espressif Systems (Shanghai) CO LTD, other contributors as noted.
3#
4# SPDX-License-Identifier: GPL-2.0-or-later
5
6import struct
7import time
8from typing import Dict
9
10from .esp32c3 import ESP32C3ROM
11from ..loader import ESPLoader
12from ..util import FatalError
13
14
15class ESP32C2ROM(ESP32C3ROM):
16    CHIP_NAME = "ESP32-C2"
17    IMAGE_CHIP_ID = 12
18
19    IROM_MAP_START = 0x42000000
20    IROM_MAP_END = 0x42400000
21    DROM_MAP_START = 0x3C000000
22    DROM_MAP_END = 0x3C400000
23
24    # Magic value for ESP32C2 ECO0 , ECO1 and ECO4 respectively
25    CHIP_DETECT_MAGIC_VALUE = [0x6F51306F, 0x7C41A06F, 0x0C21E06F]
26
27    EFUSE_BASE = 0x60008800
28    EFUSE_BLOCK2_ADDR = EFUSE_BASE + 0x040
29    MAC_EFUSE_REG = EFUSE_BASE + 0x040
30
31    EFUSE_SECURE_BOOT_EN_REG = EFUSE_BASE + 0x30
32    EFUSE_SECURE_BOOT_EN_MASK = 1 << 21
33
34    EFUSE_SPI_BOOT_CRYPT_CNT_REG = EFUSE_BASE + 0x30
35    EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 18
36
37    EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_BASE + 0x30
38    EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 6
39
40    EFUSE_XTS_KEY_LENGTH_256_REG = EFUSE_BASE + 0x30
41    EFUSE_XTS_KEY_LENGTH_256 = 1 << 10
42
43    EFUSE_BLOCK_KEY0_REG = EFUSE_BASE + 0x60
44
45    EFUSE_RD_DIS_REG = EFUSE_BASE + 0x30
46    EFUSE_RD_DIS = 3
47
48    FLASH_FREQUENCY = {
49        "60m": 0xF,
50        "30m": 0x0,
51        "20m": 0x1,
52        "15m": 0x2,
53    }
54
55    MEMORY_MAP = [
56        [0x00000000, 0x00010000, "PADDING"],
57        [0x3C000000, 0x3C400000, "DROM"],
58        [0x3FCA0000, 0x3FCE0000, "DRAM"],
59        [0x3FC88000, 0x3FD00000, "BYTE_ACCESSIBLE"],
60        [0x3FF00000, 0x3FF50000, "DROM_MASK"],
61        [0x40000000, 0x40090000, "IROM_MASK"],
62        [0x42000000, 0x42400000, "IROM"],
63        [0x4037C000, 0x403C0000, "IRAM"],
64    ]
65
66    UF2_FAMILY_ID = 0x2B88D29C
67
68    KEY_PURPOSES: Dict[int, str] = {}
69
70    def get_pkg_version(self):
71        num_word = 1
72        return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 22) & 0x07
73
74    def get_chip_description(self):
75        chip_name = {
76            0: "ESP32-C2",
77            1: "ESP32-C2",
78        }.get(self.get_pkg_version(), "unknown ESP32-C2")
79        major_rev = self.get_major_chip_version()
80        minor_rev = self.get_minor_chip_version()
81        return f"{chip_name} (revision v{major_rev}.{minor_rev})"
82
83    def get_minor_chip_version(self):
84        num_word = 1
85        return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 16) & 0xF
86
87    def get_major_chip_version(self):
88        num_word = 1
89        return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 20) & 0x3
90
91    def get_flash_cap(self):
92        # ESP32-C2 doesn't have eFuse field FLASH_CAP.
93        # Can't get info about the flash chip.
94        return 0
95
96    def get_flash_vendor(self):
97        # ESP32-C2 doesn't have eFuse field FLASH_VENDOR.
98        # Can't get info about the flash chip.
99        return ""
100
101    def get_crystal_freq(self):
102        # The crystal detection algorithm of ESP32/ESP8266 works for ESP32-C2 as well.
103        return ESPLoader.get_crystal_freq(self)
104
105    def change_baud(self, baud):
106        rom_with_26M_XTAL = not self.IS_STUB and self.get_crystal_freq() == 26
107        if rom_with_26M_XTAL:
108            # The code is copied over from ESPLoader.change_baud().
109            # Probably this is just a temporary solution until the next chip revision.
110
111            # The ROM code thinks it uses a 40 MHz XTAL. Recompute the baud rate
112            # in order to trick the ROM code to set the correct baud rate for
113            # a 26 MHz XTAL.
114            false_rom_baud = baud * 40 // 26
115
116            print(f"Changing baud rate to {baud}")
117            self.command(
118                self.ESP_CHANGE_BAUDRATE, struct.pack("<II", false_rom_baud, 0)
119            )
120            print("Changed.")
121            self._set_port_baudrate(baud)
122            time.sleep(0.05)  # get rid of garbage sent during baud rate change
123            self.flush_input()
124        else:
125            ESPLoader.change_baud(self, baud)
126
127    def _post_connect(self):
128        # ESP32C2 ECO0 is no longer supported by the flasher stub
129        if not self.secure_download_mode and self.get_chip_revision() == 0:
130            self.stub_is_disabled = True
131            self.IS_STUB = False
132
133    """ Try to read (encryption key) and check if it is valid """
134
135    def is_flash_encryption_key_valid(self):
136        key_len_256 = (
137            self.read_reg(self.EFUSE_XTS_KEY_LENGTH_256_REG)
138            & self.EFUSE_XTS_KEY_LENGTH_256
139        )
140
141        word0 = self.read_reg(self.EFUSE_RD_DIS_REG) & self.EFUSE_RD_DIS
142        rd_disable = word0 == 3 if key_len_256 else word0 == 1
143
144        # reading of BLOCK3 is NOT ALLOWED so we assume valid key is programmed
145        if rd_disable:
146            return True
147        else:
148            # reading of BLOCK3 is ALLOWED so we will read and verify for non-zero.
149            # When chip has not generated AES/encryption key in BLOCK3,
150            # the contents will be readable and 0.
151            # If the flash encryption is enabled it is expected to have a valid
152            # non-zero key. We break out on first occurrence of non-zero value
153            key_word = [0] * 7 if key_len_256 else [0] * 3
154            for i in range(len(key_word)):
155                key_word[i] = self.read_reg(self.EFUSE_BLOCK_KEY0_REG + i * 4)
156                # key is non-zero so break & return
157                if key_word[i] != 0:
158                    return True
159            return False
160
161    def check_spi_connection(self, spi_connection):
162        if not set(spi_connection).issubset(set(range(0, 21))):
163            raise FatalError("SPI Pin numbers must be in the range 0-20.")
164
165
166class ESP32C2StubLoader(ESP32C2ROM):
167    """Access class for ESP32C2 stub loader, runs on top of ROM.
168
169    (Basically the same as ESP32StubLoader, but different base class.
170    Can possibly be made into a mixin.)
171    """
172
173    FLASH_WRITE_SIZE = 0x4000  # matches MAX_WRITE_BLOCK in stub_loader.c
174    STATUS_BYTES_LENGTH = 2  # same as ESP8266, different to ESP32 ROM
175    IS_STUB = True
176
177    def __init__(self, rom_loader):
178        self.secure_download_mode = rom_loader.secure_download_mode
179        self._port = rom_loader._port
180        self._trace_enabled = rom_loader._trace_enabled
181        self.cache = rom_loader.cache
182        self.flush_input()  # resets _slip_reader
183
184
185ESP32C2ROM.STUB_CLASS = ESP32C2StubLoader
186