You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

135 lines
3.9 KiB

# SPDX-FileCopyrightText: 2020 Foundation Devices, Inc. <hello@foundationdevices.com>
# SPDX-License-Identifier: GPL-3.0-or-later
#
# SPDX-FileCopyrightText: 2018 Coinkite, Inc. <coldcardwallet.com>
# SPDX-License-Identifier: GPL-3.0-only
#
# (c) Copyright 2018 by Coinkite Inc. This file is part of Coldcard <coldcardwallet.com>
# and is covered by GPLv3 license found in COPYING.
#
# sflash.py - SPI Flash on rev D and up boards. Simple serial SPI flash on SPI2 port.
#
# see also ../external/micropython/drivers/memory/spiflash.c
# but not using that, because:
# - not exposed as python objects
# - it wants to waste 4k on a buffer
#
# Layout for project:
# - 384k PSBT incoming (MAX_TXN_LEN)
# - 384k PSBT outgoing (MAX_TXN_LEN)
# - 128k nvram settings (32 slots of 4k each)
#
# During firmware updates, entire flash, starting at zero may be used.
#
import machine
CMD_WRSR = const(0x01)
CMD_WRITE = const(0x02)
CMD_READ = const(0x03)
CMD_FAST_READ = const(0x0b)
CMD_RDSR = const(0x05)
CMD_WREN = const(0x06)
CMD_RDCR = const(0x35)
CMD_RD_DEVID = const(0x9f)
CMD_SEC_ERASE = const(0x20)
CMD_BLK_ERASE = const(0xd8)
CMD_CHIP_ERASE = const(0xc7)
CMD_C4READ = const(0xeb)
class SPIFlash:
# must write with this page size granulatity
PAGE_SIZE = 256
# must erase with one of these size granulatity!
SECTOR_SIZE = 4096
BLOCK_SIZE = 65536
def __init__(self):
from machine import Pin
self.spi = machine.SPI(4, baudrate=8000000)
self.cs = Pin('SF_CS', Pin.OUT)
def cmd(self, cmd, addr=None, complete=True, pad=False):
if addr is not None:
buf = bytes([cmd, (addr>>16) & 0xff, (addr >> 8) & 0xff, addr & 0xff])
else:
buf = bytes([cmd])
if pad:
buf = buf + b'\0'
self.cs.low()
self.spi.write(buf)
if complete:
self.cs.high()
def read(self, address, buf, cmd=CMD_FAST_READ):
# random read (fast mode, because why wouldn't we?!)
self.cmd(cmd, address, complete=False, pad=True)
self.spi.readinto(buf)
self.cs.high()
def write(self, address, buf):
# 'page program', must already be erased
assert 1 <= len(buf) <= 256 # "max 256"
assert address & ~0xff == (address+len(buf)-1) & ~0xff # "page boundary"
self.cmd(CMD_WREN)
self.cmd(CMD_WRITE, address, complete=False)
self.spi.write(buf)
self.cs.high()
def read_reg(self, cmd, length=3):
# read register
rv = bytearray(length)
self.cmd(cmd, 0, complete=False)
self.spi.readinto(rv)
self.cs.high()
return rv
def is_busy(self):
# return status of WIP = Write In Progress bit
r = self.read_reg(CMD_RDSR, 1)
return bool(r[0] & 0x01)
def wait_done(self):
# wait until write done; could be fancier
while 1:
if not self.is_busy():
return
def chip_erase(self):
# can take up to 6 seconds, so poll is_busy()
self.cmd(CMD_WREN)
self.cmd(CMD_CHIP_ERASE)
def sector_erase(self, address):
# erase 4k. 40-200ms delay; poll is_busy()
assert address % 4096 == 0 # "not sector start"
self.cmd(CMD_WREN)
self.cmd(CMD_SEC_ERASE, address)
def block_erase(self, address):
# erase 64k at once
assert address % 65536 == 0 # "not block start"
self.cmd(CMD_WREN)
self.cmd(CMD_BLK_ERASE, address)
def wipe_most(self):
# erase everything except settings: takes 5 seconds at least
from nvstore import SLOTS
end = SLOTS[0]
from common import dis
dis.fullscreen("Cleanup...")
for addr in range(0, end, self.BLOCK_SIZE):
self.block_erase(addr)
dis.progress_bar_show(addr/end)
while self.is_busy():
pass
# EOF