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
6from ..loader import ESPLoader
7from ..util import FatalError, NotSupportedError
8
9
10class ESP8266ROM(ESPLoader):
11    """Access class for ESP8266 ROM bootloader"""
12
13    CHIP_NAME = "ESP8266"
14    IS_STUB = False
15
16    CHIP_DETECT_MAGIC_VALUE = [0xFFF0C101]
17
18    # OTP ROM addresses
19    ESP_OTP_MAC0 = 0x3FF00050
20    ESP_OTP_MAC1 = 0x3FF00054
21    ESP_OTP_MAC3 = 0x3FF0005C
22
23    SPI_REG_BASE = 0x60000200
24    SPI_USR_OFFS = 0x1C
25    SPI_USR1_OFFS = 0x20
26    SPI_USR2_OFFS = 0x24
27    SPI_MOSI_DLEN_OFFS = None
28    SPI_MISO_DLEN_OFFS = None
29    SPI_W0_OFFS = 0x40
30
31    UART_CLKDIV_REG = 0x60000014
32
33    XTAL_CLK_DIVIDER = 2
34
35    FLASH_SIZES = {
36        "512KB": 0x00,
37        "256KB": 0x10,
38        "1MB": 0x20,
39        "2MB": 0x30,
40        "4MB": 0x40,
41        "2MB-c1": 0x50,
42        "4MB-c1": 0x60,
43        "8MB": 0x80,
44        "16MB": 0x90,
45    }
46
47    FLASH_FREQUENCY = {
48        "80m": 0xF,
49        "40m": 0x0,
50        "26m": 0x1,
51        "20m": 0x2,
52    }
53
54    BOOTLOADER_FLASH_OFFSET = 0
55
56    MEMORY_MAP = [
57        [0x3FF00000, 0x3FF00010, "DPORT"],
58        [0x3FFE8000, 0x40000000, "DRAM"],
59        [0x40100000, 0x40108000, "IRAM"],
60        [0x40201010, 0x402E1010, "IROM"],
61    ]
62
63    UF2_FAMILY_ID = 0x7EAB61ED
64
65    def get_efuses(self):
66        # Return the 128 bits of ESP8266 efuse as a single Python integer
67        result = self.read_reg(0x3FF0005C) << 96
68        result |= self.read_reg(0x3FF00058) << 64
69        result |= self.read_reg(0x3FF00054) << 32
70        result |= self.read_reg(0x3FF00050)
71        return result
72
73    def _get_flash_size(self, efuses):
74        # rX_Y = EFUSE_DATA_OUTX[Y]
75        r0_4 = (efuses & (1 << 4)) != 0
76        r3_25 = (efuses & (1 << 121)) != 0
77        r3_26 = (efuses & (1 << 122)) != 0
78        r3_27 = (efuses & (1 << 123)) != 0
79
80        if r0_4 and not r3_25:
81            if not r3_27 and not r3_26:
82                return 1
83            elif not r3_27 and r3_26:
84                return 2
85        if not r0_4 and r3_25:
86            if not r3_27 and not r3_26:
87                return 2
88            elif not r3_27 and r3_26:
89                return 4
90        return -1
91
92    def get_chip_description(self):
93        efuses = self.get_efuses()
94        is_8285 = (
95            efuses & ((1 << 4) | 1 << 80)
96        ) != 0  # One or the other efuse bit is set for ESP8285
97        if is_8285:
98            flash_size = self._get_flash_size(efuses)
99            max_temp = (
100                efuses & (1 << 5)
101            ) != 0  # This efuse bit identifies the max flash temperature
102            chip_name = {
103                1: "ESP8285H08" if max_temp else "ESP8285N08",
104                2: "ESP8285H16" if max_temp else "ESP8285N16",
105            }.get(flash_size, "ESP8285")
106            return chip_name
107        return "ESP8266EX"
108
109    def get_chip_features(self):
110        features = ["WiFi"]
111        if "ESP8285" in self.get_chip_description():
112            features += ["Embedded Flash"]
113        return features
114
115    def flash_spi_attach(self, hspi_arg):
116        if self.IS_STUB:
117            super(ESP8266ROM, self).flash_spi_attach(hspi_arg)
118        else:
119            # ESP8266 ROM has no flash_spi_attach command in serial protocol,
120            # but flash_begin will do it
121            self.flash_begin(0, 0)
122
123    def flash_set_parameters(self, size):
124        # not implemented in ROM, but OK to silently skip for ROM
125        if self.IS_STUB:
126            super(ESP8266ROM, self).flash_set_parameters(size)
127
128    def chip_id(self):
129        """
130        Read Chip ID from efuse - the equivalent of the SDK system_get_chip_id() func
131        """
132        id0 = self.read_reg(self.ESP_OTP_MAC0)
133        id1 = self.read_reg(self.ESP_OTP_MAC1)
134        return (id0 >> 24) | ((id1 & 0xFFFFFF) << 8)
135
136    def read_mac(self, mac_type="BASE_MAC"):
137        """Read MAC from OTP ROM"""
138        if mac_type != "BASE_MAC":
139            return None
140        mac0 = self.read_reg(self.ESP_OTP_MAC0)
141        mac1 = self.read_reg(self.ESP_OTP_MAC1)
142        mac3 = self.read_reg(self.ESP_OTP_MAC3)
143        if mac3 != 0:
144            oui = ((mac3 >> 16) & 0xFF, (mac3 >> 8) & 0xFF, mac3 & 0xFF)
145        elif ((mac1 >> 16) & 0xFF) == 0:
146            oui = (0x18, 0xFE, 0x34)
147        elif ((mac1 >> 16) & 0xFF) == 1:
148            oui = (0xAC, 0xD0, 0x74)
149        else:
150            raise FatalError("Unknown OUI")
151        return oui + ((mac1 >> 8) & 0xFF, mac1 & 0xFF, (mac0 >> 24) & 0xFF)
152
153    def get_erase_size(self, offset, size):
154        """Calculate an erase size given a specific size in bytes.
155
156        Provides a workaround for the bootloader erase bug."""
157
158        sectors_per_block = 16
159        sector_size = self.FLASH_SECTOR_SIZE
160        num_sectors = (size + sector_size - 1) // sector_size
161        start_sector = offset // sector_size
162
163        head_sectors = sectors_per_block - (start_sector % sectors_per_block)
164        if num_sectors < head_sectors:
165            head_sectors = num_sectors
166
167        if num_sectors < 2 * head_sectors:
168            return (num_sectors + 1) // 2 * sector_size
169        else:
170            return (num_sectors - head_sectors) * sector_size
171
172    def get_flash_voltage(self):
173        pass  # not supported on ESP8266
174
175    def override_vddsdio(self, new_voltage):
176        raise NotSupportedError(self, "Overriding VDDSDIO")
177
178    def check_spi_connection(self, spi_connection):
179        raise NotSupportedError(self, "Setting --spi-connection")
180
181    def get_secure_boot_enabled(self):
182        return False  # ESP8266 doesn't have security features
183
184
185class ESP8266StubLoader(ESP8266ROM):
186    """Access class for ESP8266 stub loader, runs on top of ROM."""
187
188    FLASH_WRITE_SIZE = 0x4000  # matches MAX_WRITE_BLOCK in stub_loader.c
189    IS_STUB = True
190
191    def __init__(self, rom_loader):
192        self.secure_download_mode = rom_loader.secure_download_mode
193        self._port = rom_loader._port
194        self._trace_enabled = rom_loader._trace_enabled
195        self.cache = rom_loader.cache
196        self.flush_input()  # resets _slip_reader
197
198    def get_erase_size(self, offset, size):
199        return size  # stub doesn't have same size bug as ROM loader
200
201
202ESP8266ROM.STUB_CLASS = ESP8266StubLoader
203