|
| 1 | +# SPDX-License-Identifier: GPL-2.0-only |
| 2 | +# Copyright 2006 David Anderson <[email protected]> |
| 3 | + |
| 4 | +import math |
| 5 | + |
| 6 | +from importlib.resources import read_binary |
| 7 | + |
| 8 | +from . import resources |
| 9 | + |
| 10 | +# Mnemonics for the addresses of the various registers used by the flash |
| 11 | +# controller. |
| 12 | +PMC_MCKR = 0xFFFFFC30 # Master clock register |
| 13 | +MC_FMR = 0xFFFFFF60 # Flash mode register |
| 14 | +MC_FCR = 0xFFFFFF64 # Flash control register |
| 15 | +MC_FSR = 0xFFFFFF68 # Flash status register |
| 16 | + |
| 17 | +# The addresses in ram used by the flash driver. |
| 18 | +FLASH_DRIVER_ADDR = 0x202000 |
| 19 | +FLASH_TARGET_BLOCK_NUM_ADDR = 0x202300 |
| 20 | +FLASH_BLOCK_DATA_ADDR = 0x202100 |
| 21 | + |
| 22 | +# The region lock control bits are non-volatile bits, which have special |
| 23 | +# timing requirements compared to the 'regular' flash memory. The |
| 24 | +# following constants define the timing settings for lock bits and for |
| 25 | +# normal flash memory. |
| 26 | +# |
| 27 | +# FMCN = 0x5, FWS = 0x1 |
| 28 | +FLASH_REGION_LOCK_SETTING = (0x5 << 16) | (0x1 << 8) |
| 29 | +# FMCN = 0x34, FWS = 0x1 |
| 30 | +FLASH_MEMORY_WRITE_SETTING = (0x34 << 16) | (0x1 << 8) |
| 31 | + |
| 32 | +# The base command to unlock a flash region, and a helper to generate the |
| 33 | +# correct region number. |
| 34 | +FLASH_REGION_UNLOCK_CMD = (0x5A << 24) | (0x4) |
| 35 | + |
| 36 | + |
| 37 | +def _unlock_region(region_num): |
| 38 | + # The unlock command must specify the page number of any page within |
| 39 | + # the region that you want to unlock. Since each region is 64 pages |
| 40 | + # long, we just multiply the page number by 64 to get into the |
| 41 | + # correct region. |
| 42 | + return FLASH_REGION_UNLOCK_CMD | ((64 * region_num) << 8) |
| 43 | + |
| 44 | + |
| 45 | +class MissingFlashDriverFile(Exception): |
| 46 | + """Could not find the flash driver firmware image.""" |
| 47 | + |
| 48 | + |
| 49 | +class InvalidFirmwareImage(Exception): |
| 50 | + """The given firmware image cannot be written.""" |
| 51 | + |
| 52 | + |
| 53 | +class FlashController(object): |
| 54 | + def __init__(self, brick): |
| 55 | + self._brick = brick |
| 56 | + |
| 57 | + def _wait_for_flash(self): |
| 58 | + """Wait for the flash controller to become ready.""" |
| 59 | + status = self._brick.read_word(MC_FSR) |
| 60 | + while not (status & 0x1): |
| 61 | + status = self._brick.read_word(MC_FSR) |
| 62 | + |
| 63 | + def _unlock_regions(self): |
| 64 | + status = self._brick.read_word(MC_FSR) |
| 65 | + if (status & 0xFFFF0000) == 0: |
| 66 | + return |
| 67 | + |
| 68 | + self._brick.write_word(MC_FMR, FLASH_REGION_LOCK_SETTING) |
| 69 | + for i in range(16): |
| 70 | + mask = 1 << (16 + i) |
| 71 | + if status & mask: |
| 72 | + self._brick.write_word(MC_FCR, _unlock_region(i)) |
| 73 | + self._wait_for_flash() |
| 74 | + self._brick.write_word(MC_FMR, FLASH_MEMORY_WRITE_SETTING) |
| 75 | + |
| 76 | + def _prepare_flash(self): |
| 77 | + # Switch to the PLL clock with a /2 prescaler. The timing |
| 78 | + # configurations we use are only valid for that configuration. |
| 79 | + # |
| 80 | + # TODO: The atmel tool does this as well, but according to the |
| 81 | + # specs this is horribly wrong: one should absolutely not switch |
| 82 | + # clock sources and prescalers in one go, much less switch to |
| 83 | + # the PLL without configuring it and stabilizing it first. What |
| 84 | + # the hell is this? |
| 85 | + self._brick.write_word(PMC_MCKR, 0x7) |
| 86 | + |
| 87 | + # Set the correct timings for flash writes. |
| 88 | + self._brick.write_word(MC_FMR, FLASH_MEMORY_WRITE_SETTING) |
| 89 | + |
| 90 | + # Check/unlock all flash regions. |
| 91 | + self._unlock_regions() |
| 92 | + |
| 93 | + # Send the flash driver to the brick. |
| 94 | + driver = read_binary(resources, resources.FLASH_DRIVER) |
| 95 | + self._brick.write_buffer(FLASH_DRIVER_ADDR, driver) |
| 96 | + |
| 97 | + def flash(self, firmware): |
| 98 | + self._prepare_flash() |
| 99 | + |
| 100 | + num_pages = int(math.ceil(len(firmware) / 256)) |
| 101 | + if num_pages > 1024: |
| 102 | + raise InvalidFirmwareImage("The firmware image must be smaller than 256kB") |
| 103 | + |
| 104 | + for page_num in range(num_pages): |
| 105 | + self._brick.write_word(FLASH_TARGET_BLOCK_NUM_ADDR, page_num) |
| 106 | + self._brick.write_buffer( |
| 107 | + FLASH_BLOCK_DATA_ADDR, firmware[page_num * 256 : (page_num + 1) * 256] |
| 108 | + ) |
| 109 | + self._brick.jump(FLASH_DRIVER_ADDR) |
0 commit comments