# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, # Espressif Systems (Shanghai) CO LTD, other contributors as noted. # # SPDX-License-Identifier: GPL-2.0-or-later from ..loader import ESPLoader from ..util import FatalError, NotSupportedError class ESP8266ROM(ESPLoader): """Access class for ESP8266 ROM bootloader""" CHIP_NAME = "ESP8266" IS_STUB = False CHIP_DETECT_MAGIC_VALUE = [0xFFF0C101] # OTP ROM addresses ESP_OTP_MAC0 = 0x3FF00050 ESP_OTP_MAC1 = 0x3FF00054 ESP_OTP_MAC3 = 0x3FF0005C SPI_REG_BASE = 0x60000200 SPI_USR_OFFS = 0x1C SPI_USR1_OFFS = 0x20 SPI_USR2_OFFS = 0x24 SPI_MOSI_DLEN_OFFS = None SPI_MISO_DLEN_OFFS = None SPI_W0_OFFS = 0x40 UART_CLKDIV_REG = 0x60000014 XTAL_CLK_DIVIDER = 2 FLASH_SIZES = { "512KB": 0x00, "256KB": 0x10, "1MB": 0x20, "2MB": 0x30, "4MB": 0x40, "2MB-c1": 0x50, "4MB-c1": 0x60, "8MB": 0x80, "16MB": 0x90, } FLASH_FREQUENCY = { "80m": 0xF, "40m": 0x0, "26m": 0x1, "20m": 0x2, } BOOTLOADER_FLASH_OFFSET = 0 MEMORY_MAP = [ [0x3FF00000, 0x3FF00010, "DPORT"], [0x3FFE8000, 0x40000000, "DRAM"], [0x40100000, 0x40108000, "IRAM"], [0x40201010, 0x402E1010, "IROM"], ] UF2_FAMILY_ID = 0x7EAB61ED def get_efuses(self): # Return the 128 bits of ESP8266 efuse as a single Python integer result = self.read_reg(0x3FF0005C) << 96 result |= self.read_reg(0x3FF00058) << 64 result |= self.read_reg(0x3FF00054) << 32 result |= self.read_reg(0x3FF00050) return result def _get_flash_size(self, efuses): # rX_Y = EFUSE_DATA_OUTX[Y] r0_4 = (efuses & (1 << 4)) != 0 r3_25 = (efuses & (1 << 121)) != 0 r3_26 = (efuses & (1 << 122)) != 0 r3_27 = (efuses & (1 << 123)) != 0 if r0_4 and not r3_25: if not r3_27 and not r3_26: return 1 elif not r3_27 and r3_26: return 2 if not r0_4 and r3_25: if not r3_27 and not r3_26: return 2 elif not r3_27 and r3_26: return 4 return -1 def get_chip_description(self): efuses = self.get_efuses() is_8285 = ( efuses & ((1 << 4) | 1 << 80) ) != 0 # One or the other efuse bit is set for ESP8285 if is_8285: flash_size = self._get_flash_size(efuses) max_temp = ( efuses & (1 << 5) ) != 0 # This efuse bit identifies the max flash temperature chip_name = { 1: "ESP8285H08" if max_temp else "ESP8285N08", 2: "ESP8285H16" if max_temp else "ESP8285N16", }.get(flash_size, "ESP8285") return chip_name return "ESP8266EX" def get_chip_features(self): features = ["WiFi"] if "ESP8285" in self.get_chip_description(): features += ["Embedded Flash"] return features def flash_spi_attach(self, hspi_arg): if self.IS_STUB: super(ESP8266ROM, self).flash_spi_attach(hspi_arg) else: # ESP8266 ROM has no flash_spi_attach command in serial protocol, # but flash_begin will do it self.flash_begin(0, 0) def flash_set_parameters(self, size): # not implemented in ROM, but OK to silently skip for ROM if self.IS_STUB: super(ESP8266ROM, self).flash_set_parameters(size) def chip_id(self): """ Read Chip ID from efuse - the equivalent of the SDK system_get_chip_id() func """ id0 = self.read_reg(self.ESP_OTP_MAC0) id1 = self.read_reg(self.ESP_OTP_MAC1) return (id0 >> 24) | ((id1 & 0xFFFFFF) << 8) def read_mac(self, mac_type="BASE_MAC"): """Read MAC from OTP ROM""" if mac_type != "BASE_MAC": return None mac0 = self.read_reg(self.ESP_OTP_MAC0) mac1 = self.read_reg(self.ESP_OTP_MAC1) mac3 = self.read_reg(self.ESP_OTP_MAC3) if mac3 != 0: oui = ((mac3 >> 16) & 0xFF, (mac3 >> 8) & 0xFF, mac3 & 0xFF) elif ((mac1 >> 16) & 0xFF) == 0: oui = (0x18, 0xFE, 0x34) elif ((mac1 >> 16) & 0xFF) == 1: oui = (0xAC, 0xD0, 0x74) else: raise FatalError("Unknown OUI") return oui + ((mac1 >> 8) & 0xFF, mac1 & 0xFF, (mac0 >> 24) & 0xFF) def get_erase_size(self, offset, size): """Calculate an erase size given a specific size in bytes. Provides a workaround for the bootloader erase bug.""" sectors_per_block = 16 sector_size = self.FLASH_SECTOR_SIZE num_sectors = (size + sector_size - 1) // sector_size start_sector = offset // sector_size head_sectors = sectors_per_block - (start_sector % sectors_per_block) if num_sectors < head_sectors: head_sectors = num_sectors if num_sectors < 2 * head_sectors: return (num_sectors + 1) // 2 * sector_size else: return (num_sectors - head_sectors) * sector_size def get_flash_voltage(self): pass # not supported on ESP8266 def override_vddsdio(self, new_voltage): raise NotSupportedError(self, "Overriding VDDSDIO") def check_spi_connection(self, spi_connection): raise NotSupportedError(self, "Setting --spi-connection") def get_secure_boot_enabled(self): return False # ESP8266 doesn't have security features class ESP8266StubLoader(ESP8266ROM): """Access class for ESP8266 stub loader, runs on top of ROM.""" FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c IS_STUB = True def __init__(self, rom_loader): self.secure_download_mode = rom_loader.secure_download_mode self._port = rom_loader._port self._trace_enabled = rom_loader._trace_enabled self.cache = rom_loader.cache self.flush_input() # resets _slip_reader def get_erase_size(self, offset, size): return size # stub doesn't have same size bug as ROM loader ESP8266ROM.STUB_CLASS = ESP8266StubLoader