RTC auf true
components/storage/blockdevice/COMPONENT_SPIF/SPIFBlockDevice.cpp@2:7aab896b1a3b, 2019-03-13 (annotated)
- Committer:
- kevman
- Date:
- Wed Mar 13 11:03:24 2019 +0000
- Revision:
- 2:7aab896b1a3b
2019-03-13
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
kevman | 2:7aab896b1a3b | 1 | /* mbed Microcontroller Library |
kevman | 2:7aab896b1a3b | 2 | * Copyright (c) 2018 ARM Limited |
kevman | 2:7aab896b1a3b | 3 | * |
kevman | 2:7aab896b1a3b | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
kevman | 2:7aab896b1a3b | 5 | * you may not use this file except in compliance with the License. |
kevman | 2:7aab896b1a3b | 6 | * You may obtain a copy of the License at |
kevman | 2:7aab896b1a3b | 7 | * |
kevman | 2:7aab896b1a3b | 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
kevman | 2:7aab896b1a3b | 9 | * |
kevman | 2:7aab896b1a3b | 10 | * Unless required by applicable law or agreed to in writing, software |
kevman | 2:7aab896b1a3b | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
kevman | 2:7aab896b1a3b | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
kevman | 2:7aab896b1a3b | 13 | * See the License for the specific language governing permissions and |
kevman | 2:7aab896b1a3b | 14 | * limitations under the License. |
kevman | 2:7aab896b1a3b | 15 | */ |
kevman | 2:7aab896b1a3b | 16 | |
kevman | 2:7aab896b1a3b | 17 | #include "SPIFBlockDevice.h" |
kevman | 2:7aab896b1a3b | 18 | #include "mbed_critical.h" |
kevman | 2:7aab896b1a3b | 19 | |
kevman | 2:7aab896b1a3b | 20 | #include <string.h> |
kevman | 2:7aab896b1a3b | 21 | #include "mbed_wait_api.h" |
kevman | 2:7aab896b1a3b | 22 | |
kevman | 2:7aab896b1a3b | 23 | #include "mbed_trace.h" |
kevman | 2:7aab896b1a3b | 24 | #define TRACE_GROUP "SPIF" |
kevman | 2:7aab896b1a3b | 25 | using namespace mbed; |
kevman | 2:7aab896b1a3b | 26 | |
kevman | 2:7aab896b1a3b | 27 | /* Default SPIF Parameters */ |
kevman | 2:7aab896b1a3b | 28 | /****************************/ |
kevman | 2:7aab896b1a3b | 29 | #define SPIF_DEFAULT_READ_SIZE 1 |
kevman | 2:7aab896b1a3b | 30 | #define SPIF_DEFAULT_PROG_SIZE 1 |
kevman | 2:7aab896b1a3b | 31 | #define SPIF_DEFAULT_PAGE_SIZE 256 |
kevman | 2:7aab896b1a3b | 32 | #define SPIF_DEFAULT_SE_SIZE 4096 |
kevman | 2:7aab896b1a3b | 33 | #define SPI_MAX_STATUS_REGISTER_SIZE 2 |
kevman | 2:7aab896b1a3b | 34 | #ifndef UINT64_MAX |
kevman | 2:7aab896b1a3b | 35 | #define UINT64_MAX -1 |
kevman | 2:7aab896b1a3b | 36 | #endif |
kevman | 2:7aab896b1a3b | 37 | #define SPI_NO_ADDRESS_COMMAND UINT64_MAX |
kevman | 2:7aab896b1a3b | 38 | // Status Register Bits |
kevman | 2:7aab896b1a3b | 39 | #define SPIF_STATUS_BIT_WIP 0x1 //Write In Progress |
kevman | 2:7aab896b1a3b | 40 | #define SPIF_STATUS_BIT_WEL 0x2 // Write Enable Latch |
kevman | 2:7aab896b1a3b | 41 | |
kevman | 2:7aab896b1a3b | 42 | /* SFDP Header Parsing */ |
kevman | 2:7aab896b1a3b | 43 | /***********************/ |
kevman | 2:7aab896b1a3b | 44 | #define SPIF_SFDP_HEADER_SIZE 8 |
kevman | 2:7aab896b1a3b | 45 | #define SPIF_PARAM_HEADER_SIZE 8 |
kevman | 2:7aab896b1a3b | 46 | |
kevman | 2:7aab896b1a3b | 47 | /* Basic Parameters Table Parsing */ |
kevman | 2:7aab896b1a3b | 48 | /**********************************/ |
kevman | 2:7aab896b1a3b | 49 | #define SFDP_DEFAULT_BASIC_PARAMS_TABLE_SIZE_BYTES 64 /* 16 DWORDS */ |
kevman | 2:7aab896b1a3b | 50 | //READ Instruction support according to BUS Configuration |
kevman | 2:7aab896b1a3b | 51 | #define SPIF_BASIC_PARAM_TABLE_FAST_READ_SUPPORT_BYTE 2 |
kevman | 2:7aab896b1a3b | 52 | #define SPIF_BASIC_PARAM_TABLE_QPI_READ_SUPPORT_BYTE 16 |
kevman | 2:7aab896b1a3b | 53 | #define SPIF_BASIC_PARAM_TABLE_222_READ_INST_BYTE 23 |
kevman | 2:7aab896b1a3b | 54 | #define SPIF_BASIC_PARAM_TABLE_122_READ_INST_BYTE 15 |
kevman | 2:7aab896b1a3b | 55 | #define SPIF_BASIC_PARAM_TABLE_112_READ_INST_BYTE 13 |
kevman | 2:7aab896b1a3b | 56 | #define SPIF_BASIC_PARAM_TABLE_PAGE_SIZE_BYTE 40 |
kevman | 2:7aab896b1a3b | 57 | // Address Length |
kevman | 2:7aab896b1a3b | 58 | #define SPIF_ADDR_SIZE_3_BYTES 3 |
kevman | 2:7aab896b1a3b | 59 | // Erase Types Params |
kevman | 2:7aab896b1a3b | 60 | #define SPIF_BASIC_PARAM_ERASE_TYPE_1_BYTE 29 |
kevman | 2:7aab896b1a3b | 61 | #define SPIF_BASIC_PARAM_ERASE_TYPE_2_BYTE 31 |
kevman | 2:7aab896b1a3b | 62 | #define SPIF_BASIC_PARAM_ERASE_TYPE_3_BYTE 33 |
kevman | 2:7aab896b1a3b | 63 | #define SPIF_BASIC_PARAM_ERASE_TYPE_4_BYTE 35 |
kevman | 2:7aab896b1a3b | 64 | #define SPIF_BASIC_PARAM_ERASE_TYPE_1_SIZE_BYTE 28 |
kevman | 2:7aab896b1a3b | 65 | #define SPIF_BASIC_PARAM_ERASE_TYPE_2_SIZE_BYTE 30 |
kevman | 2:7aab896b1a3b | 66 | #define SPIF_BASIC_PARAM_ERASE_TYPE_3_SIZE_BYTE 32 |
kevman | 2:7aab896b1a3b | 67 | #define SPIF_BASIC_PARAM_ERASE_TYPE_4_SIZE_BYTE 34 |
kevman | 2:7aab896b1a3b | 68 | #define SPIF_BASIC_PARAM_4K_ERASE_TYPE_BYTE 1 |
kevman | 2:7aab896b1a3b | 69 | |
kevman | 2:7aab896b1a3b | 70 | // Erase Types Per Region BitMask |
kevman | 2:7aab896b1a3b | 71 | #define ERASE_BITMASK_TYPE4 0x08 |
kevman | 2:7aab896b1a3b | 72 | #define ERASE_BITMASK_TYPE1 0x01 |
kevman | 2:7aab896b1a3b | 73 | #define ERASE_BITMASK_NONE 0x00 |
kevman | 2:7aab896b1a3b | 74 | #define ERASE_BITMASK_ALL 0x0F |
kevman | 2:7aab896b1a3b | 75 | |
kevman | 2:7aab896b1a3b | 76 | #define IS_MEM_READY_MAX_RETRIES 10000 |
kevman | 2:7aab896b1a3b | 77 | |
kevman | 2:7aab896b1a3b | 78 | enum spif_default_instructions { |
kevman | 2:7aab896b1a3b | 79 | SPIF_NOP = 0x00, // No operation |
kevman | 2:7aab896b1a3b | 80 | SPIF_PP = 0x02, // Page Program data |
kevman | 2:7aab896b1a3b | 81 | SPIF_READ = 0x03, // Read data |
kevman | 2:7aab896b1a3b | 82 | SPIF_SE = 0x20, // 4KB Sector Erase |
kevman | 2:7aab896b1a3b | 83 | SPIF_SFDP = 0x5a, // Read SFDP |
kevman | 2:7aab896b1a3b | 84 | SPIF_WRSR = 0x01, // Write Status/Configuration Register |
kevman | 2:7aab896b1a3b | 85 | SPIF_WRDI = 0x04, // Write Disable |
kevman | 2:7aab896b1a3b | 86 | SPIF_RDSR = 0x05, // Read Status Register |
kevman | 2:7aab896b1a3b | 87 | SPIF_WREN = 0x06, // Write Enable |
kevman | 2:7aab896b1a3b | 88 | SPIF_RSTEN = 0x66, // Reset Enable |
kevman | 2:7aab896b1a3b | 89 | SPIF_RST = 0x99, // Reset |
kevman | 2:7aab896b1a3b | 90 | SPIF_RDID = 0x9f, // Read Manufacturer and JDEC Device ID |
kevman | 2:7aab896b1a3b | 91 | }; |
kevman | 2:7aab896b1a3b | 92 | |
kevman | 2:7aab896b1a3b | 93 | // Mutex is used for some SPI Driver commands that must be done sequentially with no other commands in between |
kevman | 2:7aab896b1a3b | 94 | // e.g. (1)Set Write Enable, (2)Program, (3)Wait Memory Ready |
kevman | 2:7aab896b1a3b | 95 | SingletonPtr<PlatformMutex> SPIFBlockDevice::_mutex; |
kevman | 2:7aab896b1a3b | 96 | |
kevman | 2:7aab896b1a3b | 97 | // Local Function |
kevman | 2:7aab896b1a3b | 98 | static unsigned int local_math_power(int base, int exp); |
kevman | 2:7aab896b1a3b | 99 | |
kevman | 2:7aab896b1a3b | 100 | //*********************** |
kevman | 2:7aab896b1a3b | 101 | // SPIF Block Device APIs |
kevman | 2:7aab896b1a3b | 102 | //*********************** |
kevman | 2:7aab896b1a3b | 103 | SPIFBlockDevice::SPIFBlockDevice( |
kevman | 2:7aab896b1a3b | 104 | PinName mosi, PinName miso, PinName sclk, PinName csel, int freq) |
kevman | 2:7aab896b1a3b | 105 | : _spi(mosi, miso, sclk), _cs(csel), _device_size_bytes(0), _is_initialized(false), _init_ref_count(0) |
kevman | 2:7aab896b1a3b | 106 | { |
kevman | 2:7aab896b1a3b | 107 | _address_size = SPIF_ADDR_SIZE_3_BYTES; |
kevman | 2:7aab896b1a3b | 108 | // Initial SFDP read tables are read with 8 dummy cycles |
kevman | 2:7aab896b1a3b | 109 | // Default Bus Setup 1_1_1 with 0 dummy and mode cycles |
kevman | 2:7aab896b1a3b | 110 | _read_dummy_and_mode_cycles = 8; |
kevman | 2:7aab896b1a3b | 111 | _write_dummy_and_mode_cycles = 0; |
kevman | 2:7aab896b1a3b | 112 | _dummy_and_mode_cycles = _read_dummy_and_mode_cycles; |
kevman | 2:7aab896b1a3b | 113 | |
kevman | 2:7aab896b1a3b | 114 | _min_common_erase_size = 0; |
kevman | 2:7aab896b1a3b | 115 | _regions_count = 1; |
kevman | 2:7aab896b1a3b | 116 | _region_erase_types_bitfield[0] = ERASE_BITMASK_NONE; |
kevman | 2:7aab896b1a3b | 117 | |
kevman | 2:7aab896b1a3b | 118 | if (SPIF_BD_ERROR_OK != _spi_set_frequency(freq)) { |
kevman | 2:7aab896b1a3b | 119 | tr_error("ERROR: SPI Set Frequency Failed"); |
kevman | 2:7aab896b1a3b | 120 | } |
kevman | 2:7aab896b1a3b | 121 | |
kevman | 2:7aab896b1a3b | 122 | _cs = 1; |
kevman | 2:7aab896b1a3b | 123 | } |
kevman | 2:7aab896b1a3b | 124 | |
kevman | 2:7aab896b1a3b | 125 | int SPIFBlockDevice::init() |
kevman | 2:7aab896b1a3b | 126 | { |
kevman | 2:7aab896b1a3b | 127 | uint8_t vendor_device_ids[4]; |
kevman | 2:7aab896b1a3b | 128 | size_t data_length = 3; |
kevman | 2:7aab896b1a3b | 129 | int status = SPIF_BD_ERROR_OK; |
kevman | 2:7aab896b1a3b | 130 | uint32_t basic_table_addr = 0; |
kevman | 2:7aab896b1a3b | 131 | size_t basic_table_size = 0; |
kevman | 2:7aab896b1a3b | 132 | uint32_t sector_map_table_addr = 0; |
kevman | 2:7aab896b1a3b | 133 | size_t sector_map_table_size = 0; |
kevman | 2:7aab896b1a3b | 134 | spif_bd_error spi_status = SPIF_BD_ERROR_OK; |
kevman | 2:7aab896b1a3b | 135 | |
kevman | 2:7aab896b1a3b | 136 | _mutex->lock(); |
kevman | 2:7aab896b1a3b | 137 | |
kevman | 2:7aab896b1a3b | 138 | if (!_is_initialized) { |
kevman | 2:7aab896b1a3b | 139 | _init_ref_count = 0; |
kevman | 2:7aab896b1a3b | 140 | } |
kevman | 2:7aab896b1a3b | 141 | |
kevman | 2:7aab896b1a3b | 142 | _init_ref_count++; |
kevman | 2:7aab896b1a3b | 143 | |
kevman | 2:7aab896b1a3b | 144 | if (_init_ref_count != 1) { |
kevman | 2:7aab896b1a3b | 145 | goto exit_point; |
kevman | 2:7aab896b1a3b | 146 | } |
kevman | 2:7aab896b1a3b | 147 | |
kevman | 2:7aab896b1a3b | 148 | // Soft Reset |
kevman | 2:7aab896b1a3b | 149 | if (-1 == _reset_flash_mem()) { |
kevman | 2:7aab896b1a3b | 150 | tr_error("ERROR: init - Unable to initialize flash memory, tests failed\n"); |
kevman | 2:7aab896b1a3b | 151 | status = SPIF_BD_ERROR_DEVICE_ERROR; |
kevman | 2:7aab896b1a3b | 152 | goto exit_point; |
kevman | 2:7aab896b1a3b | 153 | } else { |
kevman | 2:7aab896b1a3b | 154 | tr_info("INFO: Initialize flash memory OK\n"); |
kevman | 2:7aab896b1a3b | 155 | } |
kevman | 2:7aab896b1a3b | 156 | |
kevman | 2:7aab896b1a3b | 157 | |
kevman | 2:7aab896b1a3b | 158 | /* Read Manufacturer ID (1byte), and Device ID (2bytes)*/ |
kevman | 2:7aab896b1a3b | 159 | spi_status = _spi_send_general_command(SPIF_RDID, SPI_NO_ADDRESS_COMMAND, NULL, 0, (char *)vendor_device_ids, |
kevman | 2:7aab896b1a3b | 160 | data_length); |
kevman | 2:7aab896b1a3b | 161 | if (spi_status != SPIF_BD_ERROR_OK) { |
kevman | 2:7aab896b1a3b | 162 | tr_error("ERROR: init - Read Vendor ID Failed"); |
kevman | 2:7aab896b1a3b | 163 | status = SPIF_BD_ERROR_DEVICE_ERROR; |
kevman | 2:7aab896b1a3b | 164 | goto exit_point; |
kevman | 2:7aab896b1a3b | 165 | } |
kevman | 2:7aab896b1a3b | 166 | |
kevman | 2:7aab896b1a3b | 167 | switch (vendor_device_ids[0]) { |
kevman | 2:7aab896b1a3b | 168 | case 0xbf: |
kevman | 2:7aab896b1a3b | 169 | // SST devices come preset with block protection |
kevman | 2:7aab896b1a3b | 170 | // enabled for some regions, issue write disable instruction to clear |
kevman | 2:7aab896b1a3b | 171 | _set_write_enable(); |
kevman | 2:7aab896b1a3b | 172 | _spi_send_general_command(SPIF_WRDI, SPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0); |
kevman | 2:7aab896b1a3b | 173 | break; |
kevman | 2:7aab896b1a3b | 174 | } |
kevman | 2:7aab896b1a3b | 175 | |
kevman | 2:7aab896b1a3b | 176 | //Synchronize Device |
kevman | 2:7aab896b1a3b | 177 | if (false == _is_mem_ready()) { |
kevman | 2:7aab896b1a3b | 178 | tr_error("ERROR: init - _is_mem_ready Failed"); |
kevman | 2:7aab896b1a3b | 179 | status = SPIF_BD_ERROR_READY_FAILED; |
kevman | 2:7aab896b1a3b | 180 | goto exit_point; |
kevman | 2:7aab896b1a3b | 181 | } |
kevman | 2:7aab896b1a3b | 182 | |
kevman | 2:7aab896b1a3b | 183 | /**************************** Parse SFDP Header ***********************************/ |
kevman | 2:7aab896b1a3b | 184 | if (0 != _sfdp_parse_sfdp_headers(basic_table_addr, basic_table_size, sector_map_table_addr, sector_map_table_size)) { |
kevman | 2:7aab896b1a3b | 185 | tr_error("ERROR: init - Parse SFDP Headers Failed"); |
kevman | 2:7aab896b1a3b | 186 | status = SPIF_BD_ERROR_PARSING_FAILED; |
kevman | 2:7aab896b1a3b | 187 | goto exit_point; |
kevman | 2:7aab896b1a3b | 188 | } |
kevman | 2:7aab896b1a3b | 189 | |
kevman | 2:7aab896b1a3b | 190 | |
kevman | 2:7aab896b1a3b | 191 | /**************************** Parse Basic Parameters Table ***********************************/ |
kevman | 2:7aab896b1a3b | 192 | if (0 != _sfdp_parse_basic_param_table(basic_table_addr, basic_table_size)) { |
kevman | 2:7aab896b1a3b | 193 | tr_error("ERROR: init - Parse Basic Param Table Failed"); |
kevman | 2:7aab896b1a3b | 194 | status = SPIF_BD_ERROR_PARSING_FAILED; |
kevman | 2:7aab896b1a3b | 195 | goto exit_point; |
kevman | 2:7aab896b1a3b | 196 | } |
kevman | 2:7aab896b1a3b | 197 | |
kevman | 2:7aab896b1a3b | 198 | /**************************** Parse Sector Map Table ***********************************/ |
kevman | 2:7aab896b1a3b | 199 | _region_size_bytes[0] = |
kevman | 2:7aab896b1a3b | 200 | _device_size_bytes; // If there's no region map, we have a single region sized the entire device size |
kevman | 2:7aab896b1a3b | 201 | _region_high_boundary[0] = _device_size_bytes - 1; |
kevman | 2:7aab896b1a3b | 202 | |
kevman | 2:7aab896b1a3b | 203 | if ((sector_map_table_addr != 0) && (0 != sector_map_table_size)) { |
kevman | 2:7aab896b1a3b | 204 | tr_info("INFO: init - Parsing Sector Map Table - addr: 0x%lxh, Size: %d", sector_map_table_addr, |
kevman | 2:7aab896b1a3b | 205 | sector_map_table_size); |
kevman | 2:7aab896b1a3b | 206 | if (0 != _sfdp_parse_sector_map_table(sector_map_table_addr, sector_map_table_size)) { |
kevman | 2:7aab896b1a3b | 207 | tr_error("ERROR: init - Parse Sector Map Table Failed"); |
kevman | 2:7aab896b1a3b | 208 | status = SPIF_BD_ERROR_PARSING_FAILED; |
kevman | 2:7aab896b1a3b | 209 | goto exit_point; |
kevman | 2:7aab896b1a3b | 210 | } |
kevman | 2:7aab896b1a3b | 211 | } |
kevman | 2:7aab896b1a3b | 212 | |
kevman | 2:7aab896b1a3b | 213 | // Configure BUS Mode to 1_1_1 for all commands other than Read |
kevman | 2:7aab896b1a3b | 214 | // Dummy And Mode Cycles Back default 0 |
kevman | 2:7aab896b1a3b | 215 | _dummy_and_mode_cycles = _write_dummy_and_mode_cycles; |
kevman | 2:7aab896b1a3b | 216 | _is_initialized = true; |
kevman | 2:7aab896b1a3b | 217 | |
kevman | 2:7aab896b1a3b | 218 | exit_point: |
kevman | 2:7aab896b1a3b | 219 | _mutex->unlock(); |
kevman | 2:7aab896b1a3b | 220 | |
kevman | 2:7aab896b1a3b | 221 | return status; |
kevman | 2:7aab896b1a3b | 222 | } |
kevman | 2:7aab896b1a3b | 223 | |
kevman | 2:7aab896b1a3b | 224 | |
kevman | 2:7aab896b1a3b | 225 | int SPIFBlockDevice::deinit() |
kevman | 2:7aab896b1a3b | 226 | { |
kevman | 2:7aab896b1a3b | 227 | spif_bd_error status = SPIF_BD_ERROR_OK; |
kevman | 2:7aab896b1a3b | 228 | |
kevman | 2:7aab896b1a3b | 229 | _mutex->lock(); |
kevman | 2:7aab896b1a3b | 230 | |
kevman | 2:7aab896b1a3b | 231 | if (!_is_initialized) { |
kevman | 2:7aab896b1a3b | 232 | _init_ref_count = 0; |
kevman | 2:7aab896b1a3b | 233 | goto exit_point; |
kevman | 2:7aab896b1a3b | 234 | } |
kevman | 2:7aab896b1a3b | 235 | |
kevman | 2:7aab896b1a3b | 236 | _init_ref_count--; |
kevman | 2:7aab896b1a3b | 237 | |
kevman | 2:7aab896b1a3b | 238 | if (_init_ref_count) { |
kevman | 2:7aab896b1a3b | 239 | goto exit_point; |
kevman | 2:7aab896b1a3b | 240 | } |
kevman | 2:7aab896b1a3b | 241 | |
kevman | 2:7aab896b1a3b | 242 | // Disable Device for Writing |
kevman | 2:7aab896b1a3b | 243 | status = _spi_send_general_command(SPIF_WRDI, SPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0); |
kevman | 2:7aab896b1a3b | 244 | if (status != SPIF_BD_ERROR_OK) { |
kevman | 2:7aab896b1a3b | 245 | tr_error("ERROR: Write Disable failed"); |
kevman | 2:7aab896b1a3b | 246 | } |
kevman | 2:7aab896b1a3b | 247 | _is_initialized = false; |
kevman | 2:7aab896b1a3b | 248 | |
kevman | 2:7aab896b1a3b | 249 | exit_point: |
kevman | 2:7aab896b1a3b | 250 | _mutex->unlock(); |
kevman | 2:7aab896b1a3b | 251 | |
kevman | 2:7aab896b1a3b | 252 | return status; |
kevman | 2:7aab896b1a3b | 253 | } |
kevman | 2:7aab896b1a3b | 254 | |
kevman | 2:7aab896b1a3b | 255 | int SPIFBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) |
kevman | 2:7aab896b1a3b | 256 | { |
kevman | 2:7aab896b1a3b | 257 | if (!_is_initialized) { |
kevman | 2:7aab896b1a3b | 258 | return BD_ERROR_DEVICE_ERROR; |
kevman | 2:7aab896b1a3b | 259 | } |
kevman | 2:7aab896b1a3b | 260 | |
kevman | 2:7aab896b1a3b | 261 | int status = SPIF_BD_ERROR_OK; |
kevman | 2:7aab896b1a3b | 262 | tr_info("INFO Read - Inst: 0x%xh", _read_instruction); |
kevman | 2:7aab896b1a3b | 263 | _mutex->lock(); |
kevman | 2:7aab896b1a3b | 264 | |
kevman | 2:7aab896b1a3b | 265 | // Set Dummy Cycles for Specific Read Command Mode |
kevman | 2:7aab896b1a3b | 266 | _dummy_and_mode_cycles = _read_dummy_and_mode_cycles; |
kevman | 2:7aab896b1a3b | 267 | |
kevman | 2:7aab896b1a3b | 268 | status = _spi_send_read_command(_read_instruction, static_cast<uint8_t *>(buffer), addr, size); |
kevman | 2:7aab896b1a3b | 269 | |
kevman | 2:7aab896b1a3b | 270 | // Set Dummy Cycles for all other command modes |
kevman | 2:7aab896b1a3b | 271 | _dummy_and_mode_cycles = _write_dummy_and_mode_cycles; |
kevman | 2:7aab896b1a3b | 272 | |
kevman | 2:7aab896b1a3b | 273 | _mutex->unlock(); |
kevman | 2:7aab896b1a3b | 274 | return status; |
kevman | 2:7aab896b1a3b | 275 | } |
kevman | 2:7aab896b1a3b | 276 | |
kevman | 2:7aab896b1a3b | 277 | int SPIFBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size) |
kevman | 2:7aab896b1a3b | 278 | { |
kevman | 2:7aab896b1a3b | 279 | if (!_is_initialized) { |
kevman | 2:7aab896b1a3b | 280 | return BD_ERROR_DEVICE_ERROR; |
kevman | 2:7aab896b1a3b | 281 | } |
kevman | 2:7aab896b1a3b | 282 | |
kevman | 2:7aab896b1a3b | 283 | bool program_failed = false; |
kevman | 2:7aab896b1a3b | 284 | int status = SPIF_BD_ERROR_OK; |
kevman | 2:7aab896b1a3b | 285 | uint32_t offset = 0; |
kevman | 2:7aab896b1a3b | 286 | uint32_t chunk = 0; |
kevman | 2:7aab896b1a3b | 287 | |
kevman | 2:7aab896b1a3b | 288 | tr_debug("DEBUG: program - Buff: 0x%lxh, addr: %llu, size: %llu", (uint32_t)buffer, addr, size); |
kevman | 2:7aab896b1a3b | 289 | |
kevman | 2:7aab896b1a3b | 290 | while (size > 0) { |
kevman | 2:7aab896b1a3b | 291 | |
kevman | 2:7aab896b1a3b | 292 | // Write on _page_size_bytes boundaries (Default 256 bytes a page) |
kevman | 2:7aab896b1a3b | 293 | offset = addr % _page_size_bytes; |
kevman | 2:7aab896b1a3b | 294 | chunk = (offset + size < _page_size_bytes) ? size : (_page_size_bytes - offset); |
kevman | 2:7aab896b1a3b | 295 | |
kevman | 2:7aab896b1a3b | 296 | _mutex->lock(); |
kevman | 2:7aab896b1a3b | 297 | |
kevman | 2:7aab896b1a3b | 298 | //Send WREN |
kevman | 2:7aab896b1a3b | 299 | if (_set_write_enable() != 0) { |
kevman | 2:7aab896b1a3b | 300 | tr_error("ERROR: Write Enabe failed\n"); |
kevman | 2:7aab896b1a3b | 301 | program_failed = true; |
kevman | 2:7aab896b1a3b | 302 | status = SPIF_BD_ERROR_WREN_FAILED; |
kevman | 2:7aab896b1a3b | 303 | goto exit_point; |
kevman | 2:7aab896b1a3b | 304 | } |
kevman | 2:7aab896b1a3b | 305 | |
kevman | 2:7aab896b1a3b | 306 | _spi_send_program_command(_prog_instruction, buffer, addr, chunk); |
kevman | 2:7aab896b1a3b | 307 | |
kevman | 2:7aab896b1a3b | 308 | buffer = static_cast<const uint8_t *>(buffer) + chunk; |
kevman | 2:7aab896b1a3b | 309 | addr += chunk; |
kevman | 2:7aab896b1a3b | 310 | size -= chunk; |
kevman | 2:7aab896b1a3b | 311 | |
kevman | 2:7aab896b1a3b | 312 | if (false == _is_mem_ready()) { |
kevman | 2:7aab896b1a3b | 313 | tr_error("ERROR: Device not ready after write, failed\n"); |
kevman | 2:7aab896b1a3b | 314 | program_failed = true; |
kevman | 2:7aab896b1a3b | 315 | status = SPIF_BD_ERROR_READY_FAILED; |
kevman | 2:7aab896b1a3b | 316 | goto exit_point; |
kevman | 2:7aab896b1a3b | 317 | } |
kevman | 2:7aab896b1a3b | 318 | _mutex->unlock(); |
kevman | 2:7aab896b1a3b | 319 | } |
kevman | 2:7aab896b1a3b | 320 | |
kevman | 2:7aab896b1a3b | 321 | exit_point: |
kevman | 2:7aab896b1a3b | 322 | if (program_failed) { |
kevman | 2:7aab896b1a3b | 323 | _mutex->unlock(); |
kevman | 2:7aab896b1a3b | 324 | } |
kevman | 2:7aab896b1a3b | 325 | |
kevman | 2:7aab896b1a3b | 326 | return status; |
kevman | 2:7aab896b1a3b | 327 | } |
kevman | 2:7aab896b1a3b | 328 | |
kevman | 2:7aab896b1a3b | 329 | int SPIFBlockDevice::erase(bd_addr_t addr, bd_size_t in_size) |
kevman | 2:7aab896b1a3b | 330 | { |
kevman | 2:7aab896b1a3b | 331 | if (!_is_initialized) { |
kevman | 2:7aab896b1a3b | 332 | return BD_ERROR_DEVICE_ERROR; |
kevman | 2:7aab896b1a3b | 333 | } |
kevman | 2:7aab896b1a3b | 334 | |
kevman | 2:7aab896b1a3b | 335 | int type = 0; |
kevman | 2:7aab896b1a3b | 336 | uint32_t offset = 0; |
kevman | 2:7aab896b1a3b | 337 | uint32_t chunk = 4096; |
kevman | 2:7aab896b1a3b | 338 | int cur_erase_inst = _erase_instruction; |
kevman | 2:7aab896b1a3b | 339 | int size = (int)in_size; |
kevman | 2:7aab896b1a3b | 340 | bool erase_failed = false; |
kevman | 2:7aab896b1a3b | 341 | int status = SPIF_BD_ERROR_OK; |
kevman | 2:7aab896b1a3b | 342 | // Find region of erased address |
kevman | 2:7aab896b1a3b | 343 | int region = _utils_find_addr_region(addr); |
kevman | 2:7aab896b1a3b | 344 | // Erase Types of selected region |
kevman | 2:7aab896b1a3b | 345 | uint8_t bitfield = _region_erase_types_bitfield[region]; |
kevman | 2:7aab896b1a3b | 346 | |
kevman | 2:7aab896b1a3b | 347 | tr_info("DEBUG: erase - addr: %llu, in_size: %llu", addr, in_size); |
kevman | 2:7aab896b1a3b | 348 | |
kevman | 2:7aab896b1a3b | 349 | if ((addr + in_size) > _device_size_bytes) { |
kevman | 2:7aab896b1a3b | 350 | tr_error("ERROR: erase exceeds flash device size"); |
kevman | 2:7aab896b1a3b | 351 | return SPIF_BD_ERROR_INVALID_ERASE_PARAMS; |
kevman | 2:7aab896b1a3b | 352 | } |
kevman | 2:7aab896b1a3b | 353 | |
kevman | 2:7aab896b1a3b | 354 | if (((addr % get_erase_size(addr)) != 0) || (((addr + in_size) % get_erase_size(addr + in_size - 1)) != 0)) { |
kevman | 2:7aab896b1a3b | 355 | tr_error("ERROR: invalid erase - unaligned address and size"); |
kevman | 2:7aab896b1a3b | 356 | return SPIF_BD_ERROR_INVALID_ERASE_PARAMS; |
kevman | 2:7aab896b1a3b | 357 | } |
kevman | 2:7aab896b1a3b | 358 | |
kevman | 2:7aab896b1a3b | 359 | // For each iteration erase the largest section supported by current region |
kevman | 2:7aab896b1a3b | 360 | while (size > 0) { |
kevman | 2:7aab896b1a3b | 361 | |
kevman | 2:7aab896b1a3b | 362 | // iterate to find next Largest erase type ( a. supported by region, b. smaller than size) |
kevman | 2:7aab896b1a3b | 363 | // find the matching instruction and erase size chunk for that type. |
kevman | 2:7aab896b1a3b | 364 | type = _utils_iterate_next_largest_erase_type(bitfield, size, (unsigned int)addr, _region_high_boundary[region]); |
kevman | 2:7aab896b1a3b | 365 | cur_erase_inst = _erase_type_inst_arr[type]; |
kevman | 2:7aab896b1a3b | 366 | offset = addr % _erase_type_size_arr[type]; |
kevman | 2:7aab896b1a3b | 367 | chunk = ((offset + size) < _erase_type_size_arr[type]) ? size : (_erase_type_size_arr[type] - offset); |
kevman | 2:7aab896b1a3b | 368 | |
kevman | 2:7aab896b1a3b | 369 | tr_debug("DEBUG: erase - addr: %llu, size:%d, Inst: 0x%xh, chunk: %lu , ", |
kevman | 2:7aab896b1a3b | 370 | addr, size, cur_erase_inst, chunk); |
kevman | 2:7aab896b1a3b | 371 | tr_debug("DEBUG: erase - Region: %d, Type:%d", |
kevman | 2:7aab896b1a3b | 372 | region, type); |
kevman | 2:7aab896b1a3b | 373 | |
kevman | 2:7aab896b1a3b | 374 | _mutex->lock(); |
kevman | 2:7aab896b1a3b | 375 | |
kevman | 2:7aab896b1a3b | 376 | if (_set_write_enable() != 0) { |
kevman | 2:7aab896b1a3b | 377 | tr_error("ERROR: SPI Erase Device not ready - failed"); |
kevman | 2:7aab896b1a3b | 378 | erase_failed = true; |
kevman | 2:7aab896b1a3b | 379 | status = SPIF_BD_ERROR_READY_FAILED; |
kevman | 2:7aab896b1a3b | 380 | goto exit_point; |
kevman | 2:7aab896b1a3b | 381 | } |
kevman | 2:7aab896b1a3b | 382 | |
kevman | 2:7aab896b1a3b | 383 | _spi_send_erase_command(cur_erase_inst, addr, size); |
kevman | 2:7aab896b1a3b | 384 | |
kevman | 2:7aab896b1a3b | 385 | addr += chunk; |
kevman | 2:7aab896b1a3b | 386 | size -= chunk; |
kevman | 2:7aab896b1a3b | 387 | |
kevman | 2:7aab896b1a3b | 388 | if ((size > 0) && (addr > _region_high_boundary[region])) { |
kevman | 2:7aab896b1a3b | 389 | // erase crossed to next region |
kevman | 2:7aab896b1a3b | 390 | region++; |
kevman | 2:7aab896b1a3b | 391 | bitfield = _region_erase_types_bitfield[region]; |
kevman | 2:7aab896b1a3b | 392 | } |
kevman | 2:7aab896b1a3b | 393 | |
kevman | 2:7aab896b1a3b | 394 | if (false == _is_mem_ready()) { |
kevman | 2:7aab896b1a3b | 395 | tr_error("ERROR: SPI After Erase Device not ready - failed\n"); |
kevman | 2:7aab896b1a3b | 396 | erase_failed = true; |
kevman | 2:7aab896b1a3b | 397 | status = SPIF_BD_ERROR_READY_FAILED; |
kevman | 2:7aab896b1a3b | 398 | goto exit_point; |
kevman | 2:7aab896b1a3b | 399 | } |
kevman | 2:7aab896b1a3b | 400 | |
kevman | 2:7aab896b1a3b | 401 | _mutex->unlock(); |
kevman | 2:7aab896b1a3b | 402 | } |
kevman | 2:7aab896b1a3b | 403 | |
kevman | 2:7aab896b1a3b | 404 | exit_point: |
kevman | 2:7aab896b1a3b | 405 | if (erase_failed) { |
kevman | 2:7aab896b1a3b | 406 | _mutex->unlock(); |
kevman | 2:7aab896b1a3b | 407 | } |
kevman | 2:7aab896b1a3b | 408 | |
kevman | 2:7aab896b1a3b | 409 | return status; |
kevman | 2:7aab896b1a3b | 410 | } |
kevman | 2:7aab896b1a3b | 411 | |
kevman | 2:7aab896b1a3b | 412 | bd_size_t SPIFBlockDevice::get_read_size() const |
kevman | 2:7aab896b1a3b | 413 | { |
kevman | 2:7aab896b1a3b | 414 | // Assuming all devices support 1byte read granularity |
kevman | 2:7aab896b1a3b | 415 | return SPIF_DEFAULT_READ_SIZE; |
kevman | 2:7aab896b1a3b | 416 | } |
kevman | 2:7aab896b1a3b | 417 | |
kevman | 2:7aab896b1a3b | 418 | bd_size_t SPIFBlockDevice::get_program_size() const |
kevman | 2:7aab896b1a3b | 419 | { |
kevman | 2:7aab896b1a3b | 420 | // Assuming all devices support 1byte program granularity |
kevman | 2:7aab896b1a3b | 421 | return SPIF_DEFAULT_PROG_SIZE; |
kevman | 2:7aab896b1a3b | 422 | } |
kevman | 2:7aab896b1a3b | 423 | |
kevman | 2:7aab896b1a3b | 424 | bd_size_t SPIFBlockDevice::get_erase_size() const |
kevman | 2:7aab896b1a3b | 425 | { |
kevman | 2:7aab896b1a3b | 426 | // return minimal erase size supported by all regions (0 if none exists) |
kevman | 2:7aab896b1a3b | 427 | return _min_common_erase_size; |
kevman | 2:7aab896b1a3b | 428 | } |
kevman | 2:7aab896b1a3b | 429 | |
kevman | 2:7aab896b1a3b | 430 | // Find minimal erase size supported by the region to which the address belongs to |
kevman | 2:7aab896b1a3b | 431 | bd_size_t SPIFBlockDevice::get_erase_size(bd_addr_t addr) |
kevman | 2:7aab896b1a3b | 432 | { |
kevman | 2:7aab896b1a3b | 433 | // Find region of current address |
kevman | 2:7aab896b1a3b | 434 | int region = _utils_find_addr_region(addr); |
kevman | 2:7aab896b1a3b | 435 | |
kevman | 2:7aab896b1a3b | 436 | unsigned int min_region_erase_size = _min_common_erase_size; |
kevman | 2:7aab896b1a3b | 437 | int8_t type_mask = ERASE_BITMASK_TYPE1; |
kevman | 2:7aab896b1a3b | 438 | int i_ind = 0; |
kevman | 2:7aab896b1a3b | 439 | |
kevman | 2:7aab896b1a3b | 440 | if (region != -1) { |
kevman | 2:7aab896b1a3b | 441 | type_mask = 0x01; |
kevman | 2:7aab896b1a3b | 442 | |
kevman | 2:7aab896b1a3b | 443 | for (i_ind = 0; i_ind < 4; i_ind++) { |
kevman | 2:7aab896b1a3b | 444 | // loop through erase types bitfield supported by region |
kevman | 2:7aab896b1a3b | 445 | if (_region_erase_types_bitfield[region] & type_mask) { |
kevman | 2:7aab896b1a3b | 446 | |
kevman | 2:7aab896b1a3b | 447 | min_region_erase_size = _erase_type_size_arr[i_ind]; |
kevman | 2:7aab896b1a3b | 448 | break; |
kevman | 2:7aab896b1a3b | 449 | } |
kevman | 2:7aab896b1a3b | 450 | type_mask = type_mask << 1; |
kevman | 2:7aab896b1a3b | 451 | } |
kevman | 2:7aab896b1a3b | 452 | |
kevman | 2:7aab896b1a3b | 453 | if (i_ind == 4) { |
kevman | 2:7aab896b1a3b | 454 | tr_error("ERROR: no erase type was found for region addr"); |
kevman | 2:7aab896b1a3b | 455 | } |
kevman | 2:7aab896b1a3b | 456 | } |
kevman | 2:7aab896b1a3b | 457 | |
kevman | 2:7aab896b1a3b | 458 | return (bd_size_t)min_region_erase_size; |
kevman | 2:7aab896b1a3b | 459 | } |
kevman | 2:7aab896b1a3b | 460 | |
kevman | 2:7aab896b1a3b | 461 | bd_size_t SPIFBlockDevice::size() const |
kevman | 2:7aab896b1a3b | 462 | { |
kevman | 2:7aab896b1a3b | 463 | if (!_is_initialized) { |
kevman | 2:7aab896b1a3b | 464 | return 0; |
kevman | 2:7aab896b1a3b | 465 | } |
kevman | 2:7aab896b1a3b | 466 | |
kevman | 2:7aab896b1a3b | 467 | return _device_size_bytes; |
kevman | 2:7aab896b1a3b | 468 | } |
kevman | 2:7aab896b1a3b | 469 | |
kevman | 2:7aab896b1a3b | 470 | int SPIFBlockDevice::get_erase_value() const |
kevman | 2:7aab896b1a3b | 471 | { |
kevman | 2:7aab896b1a3b | 472 | return 0xFF; |
kevman | 2:7aab896b1a3b | 473 | } |
kevman | 2:7aab896b1a3b | 474 | |
kevman | 2:7aab896b1a3b | 475 | /***************************************************/ |
kevman | 2:7aab896b1a3b | 476 | /*********** SPI Driver API Functions **************/ |
kevman | 2:7aab896b1a3b | 477 | /***************************************************/ |
kevman | 2:7aab896b1a3b | 478 | spif_bd_error SPIFBlockDevice::_spi_set_frequency(int freq) |
kevman | 2:7aab896b1a3b | 479 | { |
kevman | 2:7aab896b1a3b | 480 | _spi.frequency(freq); |
kevman | 2:7aab896b1a3b | 481 | return SPIF_BD_ERROR_OK; |
kevman | 2:7aab896b1a3b | 482 | } |
kevman | 2:7aab896b1a3b | 483 | |
kevman | 2:7aab896b1a3b | 484 | spif_bd_error SPIFBlockDevice::_spi_send_read_command(int read_inst, uint8_t *buffer, bd_addr_t addr, bd_size_t size) |
kevman | 2:7aab896b1a3b | 485 | { |
kevman | 2:7aab896b1a3b | 486 | uint32_t dummy_bytes = _dummy_and_mode_cycles / 8; |
kevman | 2:7aab896b1a3b | 487 | int dummy_byte = 0; |
kevman | 2:7aab896b1a3b | 488 | |
kevman | 2:7aab896b1a3b | 489 | // csel must go low for the entire command (Inst, Address and Data) |
kevman | 2:7aab896b1a3b | 490 | _cs = 0; |
kevman | 2:7aab896b1a3b | 491 | |
kevman | 2:7aab896b1a3b | 492 | // Write 1 byte Instruction |
kevman | 2:7aab896b1a3b | 493 | _spi.write(read_inst); |
kevman | 2:7aab896b1a3b | 494 | |
kevman | 2:7aab896b1a3b | 495 | // Write Address (can be either 3 or 4 bytes long) |
kevman | 2:7aab896b1a3b | 496 | for (int address_shift = ((_address_size - 1) * 8); address_shift >= 0; address_shift -= 8) { |
kevman | 2:7aab896b1a3b | 497 | _spi.write((addr >> address_shift) & 0xFF); |
kevman | 2:7aab896b1a3b | 498 | } |
kevman | 2:7aab896b1a3b | 499 | |
kevman | 2:7aab896b1a3b | 500 | // Write Dummy Cycles Bytes |
kevman | 2:7aab896b1a3b | 501 | for (uint32_t i = 0; i < dummy_bytes; i++) { |
kevman | 2:7aab896b1a3b | 502 | _spi.write(dummy_byte); |
kevman | 2:7aab896b1a3b | 503 | } |
kevman | 2:7aab896b1a3b | 504 | |
kevman | 2:7aab896b1a3b | 505 | // Read Data |
kevman | 2:7aab896b1a3b | 506 | for (bd_size_t i = 0; i < size; i++) { |
kevman | 2:7aab896b1a3b | 507 | buffer[i] = _spi.write(0); |
kevman | 2:7aab896b1a3b | 508 | } |
kevman | 2:7aab896b1a3b | 509 | |
kevman | 2:7aab896b1a3b | 510 | // csel back to high |
kevman | 2:7aab896b1a3b | 511 | _cs = 1; |
kevman | 2:7aab896b1a3b | 512 | return SPIF_BD_ERROR_OK; |
kevman | 2:7aab896b1a3b | 513 | } |
kevman | 2:7aab896b1a3b | 514 | |
kevman | 2:7aab896b1a3b | 515 | spif_bd_error SPIFBlockDevice::_spi_send_program_command(int prog_inst, const void *buffer, bd_addr_t addr, |
kevman | 2:7aab896b1a3b | 516 | bd_size_t size) |
kevman | 2:7aab896b1a3b | 517 | { |
kevman | 2:7aab896b1a3b | 518 | // Send Program (write) command to device driver |
kevman | 2:7aab896b1a3b | 519 | uint32_t dummy_bytes = _dummy_and_mode_cycles / 8; |
kevman | 2:7aab896b1a3b | 520 | int dummy_byte = 0; |
kevman | 2:7aab896b1a3b | 521 | uint8_t *data = (uint8_t *)buffer; |
kevman | 2:7aab896b1a3b | 522 | |
kevman | 2:7aab896b1a3b | 523 | // csel must go low for the entire command (Inst, Address and Data) |
kevman | 2:7aab896b1a3b | 524 | _cs = 0; |
kevman | 2:7aab896b1a3b | 525 | |
kevman | 2:7aab896b1a3b | 526 | // Write 1 byte Instruction |
kevman | 2:7aab896b1a3b | 527 | _spi.write(prog_inst); |
kevman | 2:7aab896b1a3b | 528 | |
kevman | 2:7aab896b1a3b | 529 | // Write Address (can be either 3 or 4 bytes long) |
kevman | 2:7aab896b1a3b | 530 | for (int address_shift = ((_address_size - 1) * 8); address_shift >= 0; address_shift -= 8) { |
kevman | 2:7aab896b1a3b | 531 | _spi.write((addr >> address_shift) & 0xFF); |
kevman | 2:7aab896b1a3b | 532 | } |
kevman | 2:7aab896b1a3b | 533 | |
kevman | 2:7aab896b1a3b | 534 | // Write Dummy Cycles Bytes |
kevman | 2:7aab896b1a3b | 535 | for (uint32_t i = 0; i < dummy_bytes; i++) { |
kevman | 2:7aab896b1a3b | 536 | _spi.write(dummy_byte); |
kevman | 2:7aab896b1a3b | 537 | } |
kevman | 2:7aab896b1a3b | 538 | |
kevman | 2:7aab896b1a3b | 539 | // Write Data |
kevman | 2:7aab896b1a3b | 540 | for (bd_size_t i = 0; i < size; i++) { |
kevman | 2:7aab896b1a3b | 541 | _spi.write(data[i]); |
kevman | 2:7aab896b1a3b | 542 | } |
kevman | 2:7aab896b1a3b | 543 | |
kevman | 2:7aab896b1a3b | 544 | // csel back to high |
kevman | 2:7aab896b1a3b | 545 | _cs = 1; |
kevman | 2:7aab896b1a3b | 546 | |
kevman | 2:7aab896b1a3b | 547 | return SPIF_BD_ERROR_OK; |
kevman | 2:7aab896b1a3b | 548 | } |
kevman | 2:7aab896b1a3b | 549 | |
kevman | 2:7aab896b1a3b | 550 | spif_bd_error SPIFBlockDevice::_spi_send_erase_command(int erase_inst, bd_addr_t addr, bd_size_t size) |
kevman | 2:7aab896b1a3b | 551 | { |
kevman | 2:7aab896b1a3b | 552 | tr_info("INFO: Erase Inst: 0x%xh, addr: %llu, size: %llu", erase_inst, addr, size); |
kevman | 2:7aab896b1a3b | 553 | addr = (((int)addr) & 0x00FFF000); |
kevman | 2:7aab896b1a3b | 554 | _spi_send_general_command(erase_inst, addr, NULL, 0, NULL, 0); |
kevman | 2:7aab896b1a3b | 555 | return SPIF_BD_ERROR_OK; |
kevman | 2:7aab896b1a3b | 556 | } |
kevman | 2:7aab896b1a3b | 557 | |
kevman | 2:7aab896b1a3b | 558 | spif_bd_error SPIFBlockDevice::_spi_send_general_command(int instruction, bd_addr_t addr, char *tx_buffer, |
kevman | 2:7aab896b1a3b | 559 | size_t tx_length, char *rx_buffer, size_t rx_length) |
kevman | 2:7aab896b1a3b | 560 | { |
kevman | 2:7aab896b1a3b | 561 | // Send a general command Instruction to driver |
kevman | 2:7aab896b1a3b | 562 | uint32_t dummy_bytes = _dummy_and_mode_cycles / 8; |
kevman | 2:7aab896b1a3b | 563 | uint8_t dummy_byte = 0x00; |
kevman | 2:7aab896b1a3b | 564 | |
kevman | 2:7aab896b1a3b | 565 | // csel must go low for the entire command (Inst, Address and Data) |
kevman | 2:7aab896b1a3b | 566 | _cs = 0; |
kevman | 2:7aab896b1a3b | 567 | |
kevman | 2:7aab896b1a3b | 568 | // Write 1 byte Instruction |
kevman | 2:7aab896b1a3b | 569 | _spi.write(instruction); |
kevman | 2:7aab896b1a3b | 570 | |
kevman | 2:7aab896b1a3b | 571 | // Reading SPI Bus registers does not require Flash Address |
kevman | 2:7aab896b1a3b | 572 | if (addr != SPI_NO_ADDRESS_COMMAND) { |
kevman | 2:7aab896b1a3b | 573 | // Write Address (can be either 3 or 4 bytes long) |
kevman | 2:7aab896b1a3b | 574 | for (int address_shift = ((_address_size - 1) * 8); address_shift >= 0; address_shift -= 8) { |
kevman | 2:7aab896b1a3b | 575 | _spi.write((addr >> address_shift) & 0xFF); |
kevman | 2:7aab896b1a3b | 576 | } |
kevman | 2:7aab896b1a3b | 577 | |
kevman | 2:7aab896b1a3b | 578 | // Write Dummy Cycles Bytes |
kevman | 2:7aab896b1a3b | 579 | for (uint32_t i = 0; i < dummy_bytes; i++) { |
kevman | 2:7aab896b1a3b | 580 | _spi.write(dummy_byte); |
kevman | 2:7aab896b1a3b | 581 | } |
kevman | 2:7aab896b1a3b | 582 | } |
kevman | 2:7aab896b1a3b | 583 | |
kevman | 2:7aab896b1a3b | 584 | // Read/Write Data |
kevman | 2:7aab896b1a3b | 585 | _spi.write(tx_buffer, (int)tx_length, rx_buffer, (int)rx_length); |
kevman | 2:7aab896b1a3b | 586 | |
kevman | 2:7aab896b1a3b | 587 | // csel back to high |
kevman | 2:7aab896b1a3b | 588 | _cs = 1; |
kevman | 2:7aab896b1a3b | 589 | |
kevman | 2:7aab896b1a3b | 590 | return SPIF_BD_ERROR_OK; |
kevman | 2:7aab896b1a3b | 591 | } |
kevman | 2:7aab896b1a3b | 592 | |
kevman | 2:7aab896b1a3b | 593 | /*********************************************************/ |
kevman | 2:7aab896b1a3b | 594 | /********** SFDP Parsing and Detection Functions *********/ |
kevman | 2:7aab896b1a3b | 595 | /*********************************************************/ |
kevman | 2:7aab896b1a3b | 596 | int SPIFBlockDevice::_sfdp_parse_sector_map_table(uint32_t sector_map_table_addr, size_t sector_map_table_size) |
kevman | 2:7aab896b1a3b | 597 | { |
kevman | 2:7aab896b1a3b | 598 | uint8_t sector_map_table[SFDP_DEFAULT_BASIC_PARAMS_TABLE_SIZE_BYTES]; /* Up To 16 DWORDS = 64 Bytes */ |
kevman | 2:7aab896b1a3b | 599 | uint32_t tmp_region_size = 0; |
kevman | 2:7aab896b1a3b | 600 | int i_ind = 0; |
kevman | 2:7aab896b1a3b | 601 | int prev_boundary = 0; |
kevman | 2:7aab896b1a3b | 602 | // Default set to all type bits 1-4 are common |
kevman | 2:7aab896b1a3b | 603 | int min_common_erase_type_bits = ERASE_BITMASK_ALL; |
kevman | 2:7aab896b1a3b | 604 | |
kevman | 2:7aab896b1a3b | 605 | |
kevman | 2:7aab896b1a3b | 606 | spif_bd_error status = _spi_send_read_command(SPIF_SFDP, sector_map_table, sector_map_table_addr /*address*/, |
kevman | 2:7aab896b1a3b | 607 | sector_map_table_size); |
kevman | 2:7aab896b1a3b | 608 | if (status != SPIF_BD_ERROR_OK) { |
kevman | 2:7aab896b1a3b | 609 | tr_error("ERROR: init - Read SFDP First Table Failed"); |
kevman | 2:7aab896b1a3b | 610 | return -1; |
kevman | 2:7aab896b1a3b | 611 | } |
kevman | 2:7aab896b1a3b | 612 | |
kevman | 2:7aab896b1a3b | 613 | // Currently we support only Single Map Descriptor |
kevman | 2:7aab896b1a3b | 614 | if (!((sector_map_table[0] & 0x3) == 0x03) && (sector_map_table[1] == 0x0)) { |
kevman | 2:7aab896b1a3b | 615 | tr_error("ERROR: Sector Map - Supporting Only Single! Map Descriptor (not map commands)"); |
kevman | 2:7aab896b1a3b | 616 | return -1; |
kevman | 2:7aab896b1a3b | 617 | } |
kevman | 2:7aab896b1a3b | 618 | |
kevman | 2:7aab896b1a3b | 619 | _regions_count = sector_map_table[2] + 1; |
kevman | 2:7aab896b1a3b | 620 | if (_regions_count > SPIF_MAX_REGIONS) { |
kevman | 2:7aab896b1a3b | 621 | tr_error("ERROR: Supporting up to %d regions, current setup to %d regions - fail", |
kevman | 2:7aab896b1a3b | 622 | SPIF_MAX_REGIONS, _regions_count); |
kevman | 2:7aab896b1a3b | 623 | return -1; |
kevman | 2:7aab896b1a3b | 624 | } |
kevman | 2:7aab896b1a3b | 625 | |
kevman | 2:7aab896b1a3b | 626 | // Loop through Regions and set for each one: size, supported erase types, high boundary offset |
kevman | 2:7aab896b1a3b | 627 | // Calculate minimum Common Erase Type for all Regions |
kevman | 2:7aab896b1a3b | 628 | for (i_ind = 0; i_ind < _regions_count; i_ind++) { |
kevman | 2:7aab896b1a3b | 629 | tmp_region_size = ((*((uint32_t *)§or_map_table[(i_ind + 1) * 4])) >> 8) & 0x00FFFFFF; // bits 9-32 |
kevman | 2:7aab896b1a3b | 630 | _region_size_bytes[i_ind] = (tmp_region_size + 1) * 256; // Region size is 0 based multiple of 256 bytes; |
kevman | 2:7aab896b1a3b | 631 | _region_erase_types_bitfield[i_ind] = sector_map_table[(i_ind + 1) * 4] & 0x0F; // bits 1-4 |
kevman | 2:7aab896b1a3b | 632 | min_common_erase_type_bits &= _region_erase_types_bitfield[i_ind]; |
kevman | 2:7aab896b1a3b | 633 | _region_high_boundary[i_ind] = (_region_size_bytes[i_ind] - 1) + prev_boundary; |
kevman | 2:7aab896b1a3b | 634 | prev_boundary = _region_high_boundary[i_ind] + 1; |
kevman | 2:7aab896b1a3b | 635 | } |
kevman | 2:7aab896b1a3b | 636 | |
kevman | 2:7aab896b1a3b | 637 | // Calc minimum Common Erase Size from min_common_erase_type_bits |
kevman | 2:7aab896b1a3b | 638 | uint8_t type_mask = ERASE_BITMASK_TYPE1; |
kevman | 2:7aab896b1a3b | 639 | for (i_ind = 0; i_ind < 4; i_ind++) { |
kevman | 2:7aab896b1a3b | 640 | if (min_common_erase_type_bits & type_mask) { |
kevman | 2:7aab896b1a3b | 641 | _min_common_erase_size = _erase_type_size_arr[i_ind]; |
kevman | 2:7aab896b1a3b | 642 | break; |
kevman | 2:7aab896b1a3b | 643 | } |
kevman | 2:7aab896b1a3b | 644 | type_mask = type_mask << 1; |
kevman | 2:7aab896b1a3b | 645 | } |
kevman | 2:7aab896b1a3b | 646 | |
kevman | 2:7aab896b1a3b | 647 | if (i_ind == 4) { |
kevman | 2:7aab896b1a3b | 648 | // No common erase type was found between regions |
kevman | 2:7aab896b1a3b | 649 | _min_common_erase_size = 0; |
kevman | 2:7aab896b1a3b | 650 | } |
kevman | 2:7aab896b1a3b | 651 | |
kevman | 2:7aab896b1a3b | 652 | return 0; |
kevman | 2:7aab896b1a3b | 653 | } |
kevman | 2:7aab896b1a3b | 654 | |
kevman | 2:7aab896b1a3b | 655 | int SPIFBlockDevice::_sfdp_parse_basic_param_table(uint32_t basic_table_addr, size_t basic_table_size) |
kevman | 2:7aab896b1a3b | 656 | { |
kevman | 2:7aab896b1a3b | 657 | uint8_t param_table[SFDP_DEFAULT_BASIC_PARAMS_TABLE_SIZE_BYTES]; /* Up To 16 DWORDS = 64 Bytes */ |
kevman | 2:7aab896b1a3b | 658 | //memset(param_table, 0, SFDP_DEFAULT_BASIC_PARAMS_TABLE_SIZE_BYTES); |
kevman | 2:7aab896b1a3b | 659 | |
kevman | 2:7aab896b1a3b | 660 | spif_bd_error status = _spi_send_read_command(SPIF_SFDP, param_table, basic_table_addr /*address*/, |
kevman | 2:7aab896b1a3b | 661 | basic_table_size); |
kevman | 2:7aab896b1a3b | 662 | if (status != SPIF_BD_ERROR_OK) { |
kevman | 2:7aab896b1a3b | 663 | tr_error("ERROR: init - Read SFDP First Table Failed"); |
kevman | 2:7aab896b1a3b | 664 | return -1; |
kevman | 2:7aab896b1a3b | 665 | } |
kevman | 2:7aab896b1a3b | 666 | |
kevman | 2:7aab896b1a3b | 667 | // Check address size, currently only supports 3byte addresses |
kevman | 2:7aab896b1a3b | 668 | if ((param_table[2] & 0x4) != 0 || (param_table[7] & 0x80) != 0) { |
kevman | 2:7aab896b1a3b | 669 | tr_error("ERROR: init - verify 3byte addressing Failed"); |
kevman | 2:7aab896b1a3b | 670 | return -1; |
kevman | 2:7aab896b1a3b | 671 | } |
kevman | 2:7aab896b1a3b | 672 | |
kevman | 2:7aab896b1a3b | 673 | // Get device density (stored in bits - 1) |
kevman | 2:7aab896b1a3b | 674 | uint32_t density_bits = ( |
kevman | 2:7aab896b1a3b | 675 | (param_table[7] << 24) | |
kevman | 2:7aab896b1a3b | 676 | (param_table[6] << 16) | |
kevman | 2:7aab896b1a3b | 677 | (param_table[5] << 8) | |
kevman | 2:7aab896b1a3b | 678 | param_table[4]); |
kevman | 2:7aab896b1a3b | 679 | _device_size_bytes = (density_bits + 1) / 8; |
kevman | 2:7aab896b1a3b | 680 | |
kevman | 2:7aab896b1a3b | 681 | // Set Default read/program/erase Instructions |
kevman | 2:7aab896b1a3b | 682 | _read_instruction = SPIF_READ; |
kevman | 2:7aab896b1a3b | 683 | _prog_instruction = SPIF_PP; |
kevman | 2:7aab896b1a3b | 684 | _erase_instruction = SPIF_SE; |
kevman | 2:7aab896b1a3b | 685 | |
kevman | 2:7aab896b1a3b | 686 | // Set Page Size (SPI write must be done on Page limits) |
kevman | 2:7aab896b1a3b | 687 | _page_size_bytes = _sfdp_detect_page_size(param_table, basic_table_size); |
kevman | 2:7aab896b1a3b | 688 | |
kevman | 2:7aab896b1a3b | 689 | // Detect and Set Erase Types |
kevman | 2:7aab896b1a3b | 690 | _sfdp_detect_erase_types_inst_and_size(param_table, basic_table_size, _erase4k_inst, _erase_type_inst_arr, |
kevman | 2:7aab896b1a3b | 691 | _erase_type_size_arr); |
kevman | 2:7aab896b1a3b | 692 | _erase_instruction = _erase4k_inst; |
kevman | 2:7aab896b1a3b | 693 | |
kevman | 2:7aab896b1a3b | 694 | // Detect and Set fastest Bus mode (default 1-1-1) |
kevman | 2:7aab896b1a3b | 695 | _sfdp_detect_best_bus_read_mode(param_table, basic_table_size, _read_instruction); |
kevman | 2:7aab896b1a3b | 696 | |
kevman | 2:7aab896b1a3b | 697 | return 0; |
kevman | 2:7aab896b1a3b | 698 | } |
kevman | 2:7aab896b1a3b | 699 | |
kevman | 2:7aab896b1a3b | 700 | int SPIFBlockDevice::_sfdp_parse_sfdp_headers(uint32_t &basic_table_addr, size_t &basic_table_size, |
kevman | 2:7aab896b1a3b | 701 | uint32_t §or_map_table_addr, size_t §or_map_table_size) |
kevman | 2:7aab896b1a3b | 702 | { |
kevman | 2:7aab896b1a3b | 703 | uint8_t sfdp_header[16]; |
kevman | 2:7aab896b1a3b | 704 | uint8_t param_header[SPIF_SFDP_HEADER_SIZE]; |
kevman | 2:7aab896b1a3b | 705 | size_t data_length = SPIF_SFDP_HEADER_SIZE; |
kevman | 2:7aab896b1a3b | 706 | bd_addr_t addr = 0x0; |
kevman | 2:7aab896b1a3b | 707 | |
kevman | 2:7aab896b1a3b | 708 | // Set 1-1-1 bus mode for SFDP header parsing |
kevman | 2:7aab896b1a3b | 709 | // Initial SFDP read tables are read with 8 dummy cycles |
kevman | 2:7aab896b1a3b | 710 | _read_dummy_and_mode_cycles = 8; |
kevman | 2:7aab896b1a3b | 711 | _dummy_and_mode_cycles = 8; |
kevman | 2:7aab896b1a3b | 712 | |
kevman | 2:7aab896b1a3b | 713 | spif_bd_error status = _spi_send_read_command(SPIF_SFDP, sfdp_header, addr /*address*/, data_length); |
kevman | 2:7aab896b1a3b | 714 | if (status != SPIF_BD_ERROR_OK) { |
kevman | 2:7aab896b1a3b | 715 | tr_error("ERROR: init - Read SFDP Failed"); |
kevman | 2:7aab896b1a3b | 716 | return -1; |
kevman | 2:7aab896b1a3b | 717 | } |
kevman | 2:7aab896b1a3b | 718 | |
kevman | 2:7aab896b1a3b | 719 | // Verify SFDP signature for sanity |
kevman | 2:7aab896b1a3b | 720 | // Also check that major/minor version is acceptable |
kevman | 2:7aab896b1a3b | 721 | if (!(memcmp(&sfdp_header[0], "SFDP", 4) == 0 && sfdp_header[5] == 1)) { |
kevman | 2:7aab896b1a3b | 722 | tr_error("ERROR: init - _verify SFDP signature and version Failed"); |
kevman | 2:7aab896b1a3b | 723 | return -1; |
kevman | 2:7aab896b1a3b | 724 | } else { |
kevman | 2:7aab896b1a3b | 725 | tr_info("INFO: init - verified SFDP Signature and version Successfully"); |
kevman | 2:7aab896b1a3b | 726 | } |
kevman | 2:7aab896b1a3b | 727 | |
kevman | 2:7aab896b1a3b | 728 | // Discover Number of Parameter Headers |
kevman | 2:7aab896b1a3b | 729 | int number_of_param_headers = (int)(sfdp_header[6]) + 1; |
kevman | 2:7aab896b1a3b | 730 | tr_debug("DEBUG: number of Param Headers: %d", number_of_param_headers); |
kevman | 2:7aab896b1a3b | 731 | |
kevman | 2:7aab896b1a3b | 732 | |
kevman | 2:7aab896b1a3b | 733 | addr += SPIF_SFDP_HEADER_SIZE; |
kevman | 2:7aab896b1a3b | 734 | data_length = SPIF_PARAM_HEADER_SIZE; |
kevman | 2:7aab896b1a3b | 735 | |
kevman | 2:7aab896b1a3b | 736 | // Loop over Param Headers and parse them (currently supported Basic Param Table and Sector Region Map Table) |
kevman | 2:7aab896b1a3b | 737 | for (int i_ind = 0; i_ind < number_of_param_headers; i_ind++) { |
kevman | 2:7aab896b1a3b | 738 | |
kevman | 2:7aab896b1a3b | 739 | status = _spi_send_read_command(SPIF_SFDP, param_header, addr, data_length); |
kevman | 2:7aab896b1a3b | 740 | if (status != SPIF_BD_ERROR_OK) { |
kevman | 2:7aab896b1a3b | 741 | tr_error("ERROR: init - Read Param Table %d Failed", i_ind + 1); |
kevman | 2:7aab896b1a3b | 742 | return -1; |
kevman | 2:7aab896b1a3b | 743 | } |
kevman | 2:7aab896b1a3b | 744 | |
kevman | 2:7aab896b1a3b | 745 | // The SFDP spec indicates the standard table is always at offset 0 |
kevman | 2:7aab896b1a3b | 746 | // in the parameter headers, we check just to be safe |
kevman | 2:7aab896b1a3b | 747 | if (param_header[2] != 1) { |
kevman | 2:7aab896b1a3b | 748 | tr_error("ERROR: Param Table %d - Major Version should be 1!", i_ind + 1); |
kevman | 2:7aab896b1a3b | 749 | return -1; |
kevman | 2:7aab896b1a3b | 750 | } |
kevman | 2:7aab896b1a3b | 751 | |
kevman | 2:7aab896b1a3b | 752 | if ((param_header[0] == 0) && (param_header[7] == 0xFF)) { |
kevman | 2:7aab896b1a3b | 753 | // Found Basic Params Table: LSB=0x00, MSB=0xFF |
kevman | 2:7aab896b1a3b | 754 | tr_debug("DEBUG: Found Basic Param Table at Table: %d", i_ind + 1); |
kevman | 2:7aab896b1a3b | 755 | basic_table_addr = ((param_header[6] << 16) | (param_header[5] << 8) | (param_header[4])); |
kevman | 2:7aab896b1a3b | 756 | // Supporting up to 64 Bytes Table (16 DWORDS) |
kevman | 2:7aab896b1a3b | 757 | basic_table_size = ((param_header[3] * 4) < SFDP_DEFAULT_BASIC_PARAMS_TABLE_SIZE_BYTES) ? (param_header[3] * 4) : 64; |
kevman | 2:7aab896b1a3b | 758 | |
kevman | 2:7aab896b1a3b | 759 | } else if ((param_header[0] == 81) && (param_header[7] == 0xFF)) { |
kevman | 2:7aab896b1a3b | 760 | // Found Sector Map Table: LSB=0x81, MSB=0xFF |
kevman | 2:7aab896b1a3b | 761 | tr_debug("DEBUG: Found Sector Map Table at Table: %d", i_ind + 1); |
kevman | 2:7aab896b1a3b | 762 | sector_map_table_addr = ((param_header[6] << 16) | (param_header[5] << 8) | (param_header[4])); |
kevman | 2:7aab896b1a3b | 763 | sector_map_table_size = param_header[3] * 4; |
kevman | 2:7aab896b1a3b | 764 | |
kevman | 2:7aab896b1a3b | 765 | } |
kevman | 2:7aab896b1a3b | 766 | addr += SPIF_PARAM_HEADER_SIZE; |
kevman | 2:7aab896b1a3b | 767 | |
kevman | 2:7aab896b1a3b | 768 | } |
kevman | 2:7aab896b1a3b | 769 | return 0; |
kevman | 2:7aab896b1a3b | 770 | } |
kevman | 2:7aab896b1a3b | 771 | |
kevman | 2:7aab896b1a3b | 772 | unsigned int SPIFBlockDevice::_sfdp_detect_page_size(uint8_t *basic_param_table_ptr, int basic_param_table_size) |
kevman | 2:7aab896b1a3b | 773 | { |
kevman | 2:7aab896b1a3b | 774 | unsigned int page_size = SPIF_DEFAULT_PAGE_SIZE; |
kevman | 2:7aab896b1a3b | 775 | |
kevman | 2:7aab896b1a3b | 776 | if (basic_param_table_size > SPIF_BASIC_PARAM_TABLE_PAGE_SIZE_BYTE) { |
kevman | 2:7aab896b1a3b | 777 | // Page Size is specified by 4 Bits (N), calculated by 2^N |
kevman | 2:7aab896b1a3b | 778 | int page_to_power_size = ((int)basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_PAGE_SIZE_BYTE]) >> 4; |
kevman | 2:7aab896b1a3b | 779 | page_size = local_math_power(2, page_to_power_size); |
kevman | 2:7aab896b1a3b | 780 | tr_debug("DEBUG: Detected Page Size: %d", page_size); |
kevman | 2:7aab896b1a3b | 781 | } else { |
kevman | 2:7aab896b1a3b | 782 | tr_debug("DEBUG: Using Default Page Size: %d", page_size); |
kevman | 2:7aab896b1a3b | 783 | } |
kevman | 2:7aab896b1a3b | 784 | return page_size; |
kevman | 2:7aab896b1a3b | 785 | } |
kevman | 2:7aab896b1a3b | 786 | |
kevman | 2:7aab896b1a3b | 787 | int SPIFBlockDevice::_sfdp_detect_erase_types_inst_and_size(uint8_t *basic_param_table_ptr, int basic_param_table_size, |
kevman | 2:7aab896b1a3b | 788 | int &erase4k_inst, |
kevman | 2:7aab896b1a3b | 789 | int *erase_type_inst_arr, unsigned int *erase_type_size_arr) |
kevman | 2:7aab896b1a3b | 790 | { |
kevman | 2:7aab896b1a3b | 791 | erase4k_inst = 0xff; |
kevman | 2:7aab896b1a3b | 792 | bool found_4Kerase_type = false; |
kevman | 2:7aab896b1a3b | 793 | uint8_t bitfield = 0x01; |
kevman | 2:7aab896b1a3b | 794 | |
kevman | 2:7aab896b1a3b | 795 | // Erase 4K Inst is taken either from param table legacy 4K erase or superseded by erase Instruction for type of size 4K |
kevman | 2:7aab896b1a3b | 796 | erase4k_inst = basic_param_table_ptr[SPIF_BASIC_PARAM_4K_ERASE_TYPE_BYTE]; |
kevman | 2:7aab896b1a3b | 797 | |
kevman | 2:7aab896b1a3b | 798 | if (basic_param_table_size > SPIF_BASIC_PARAM_ERASE_TYPE_1_SIZE_BYTE) { |
kevman | 2:7aab896b1a3b | 799 | // Loop Erase Types 1-4 |
kevman | 2:7aab896b1a3b | 800 | for (int i_ind = 0; i_ind < 4; i_ind++) { |
kevman | 2:7aab896b1a3b | 801 | erase_type_inst_arr[i_ind] = 0xff; //0xFF default for unsupported type |
kevman | 2:7aab896b1a3b | 802 | erase_type_size_arr[i_ind] = local_math_power(2, |
kevman | 2:7aab896b1a3b | 803 | basic_param_table_ptr[SPIF_BASIC_PARAM_ERASE_TYPE_1_SIZE_BYTE + 2 * i_ind]); // Size given as 2^N |
kevman | 2:7aab896b1a3b | 804 | tr_info("DEBUG: Erase Type(A) %d - Inst: 0x%xh, Size: %d", (i_ind + 1), erase_type_inst_arr[i_ind], |
kevman | 2:7aab896b1a3b | 805 | erase_type_size_arr[i_ind]); |
kevman | 2:7aab896b1a3b | 806 | if (erase_type_size_arr[i_ind] > 1) { |
kevman | 2:7aab896b1a3b | 807 | // if size==1 type is not supported |
kevman | 2:7aab896b1a3b | 808 | erase_type_inst_arr[i_ind] = basic_param_table_ptr[SPIF_BASIC_PARAM_ERASE_TYPE_1_BYTE + 2 * i_ind]; |
kevman | 2:7aab896b1a3b | 809 | |
kevman | 2:7aab896b1a3b | 810 | if ((erase_type_size_arr[i_ind] < _min_common_erase_size) || (_min_common_erase_size == 0)) { |
kevman | 2:7aab896b1a3b | 811 | //Set default minimal common erase for singal region |
kevman | 2:7aab896b1a3b | 812 | _min_common_erase_size = erase_type_size_arr[i_ind]; |
kevman | 2:7aab896b1a3b | 813 | } |
kevman | 2:7aab896b1a3b | 814 | |
kevman | 2:7aab896b1a3b | 815 | // SFDP standard requires 4K Erase type to exist and its instruction to be identical to legacy field erase instruction |
kevman | 2:7aab896b1a3b | 816 | if (erase_type_size_arr[i_ind] == 4096) { |
kevman | 2:7aab896b1a3b | 817 | found_4Kerase_type = true; |
kevman | 2:7aab896b1a3b | 818 | if (erase4k_inst != erase_type_inst_arr[i_ind]) { |
kevman | 2:7aab896b1a3b | 819 | //Verify 4KErase Type is identical to Legacy 4K erase type specified in Byte 1 of Param Table |
kevman | 2:7aab896b1a3b | 820 | erase4k_inst = erase_type_inst_arr[i_ind]; |
kevman | 2:7aab896b1a3b | 821 | tr_warning("WARNING: _detectEraseTypesInstAndSize - Default 4K erase Inst is different than erase type Inst for 4K"); |
kevman | 2:7aab896b1a3b | 822 | |
kevman | 2:7aab896b1a3b | 823 | } |
kevman | 2:7aab896b1a3b | 824 | } |
kevman | 2:7aab896b1a3b | 825 | _region_erase_types_bitfield[0] |= bitfield; // If there's no region map, set region "0" types bitfield as defualt; |
kevman | 2:7aab896b1a3b | 826 | } |
kevman | 2:7aab896b1a3b | 827 | |
kevman | 2:7aab896b1a3b | 828 | tr_info("INFO: Erase Type %d - Inst: 0x%xh, Size: %d", (i_ind + 1), erase_type_inst_arr[i_ind], |
kevman | 2:7aab896b1a3b | 829 | erase_type_size_arr[i_ind]); |
kevman | 2:7aab896b1a3b | 830 | bitfield = bitfield << 1; |
kevman | 2:7aab896b1a3b | 831 | } |
kevman | 2:7aab896b1a3b | 832 | } |
kevman | 2:7aab896b1a3b | 833 | |
kevman | 2:7aab896b1a3b | 834 | if (false == found_4Kerase_type) { |
kevman | 2:7aab896b1a3b | 835 | tr_warning("WARNING: Couldn't find Erase Type for 4KB size"); |
kevman | 2:7aab896b1a3b | 836 | } |
kevman | 2:7aab896b1a3b | 837 | return 0; |
kevman | 2:7aab896b1a3b | 838 | } |
kevman | 2:7aab896b1a3b | 839 | |
kevman | 2:7aab896b1a3b | 840 | int SPIFBlockDevice::_sfdp_detect_best_bus_read_mode(uint8_t *basic_param_table_ptr, int basic_param_table_size, |
kevman | 2:7aab896b1a3b | 841 | int &read_inst) |
kevman | 2:7aab896b1a3b | 842 | { |
kevman | 2:7aab896b1a3b | 843 | do { |
kevman | 2:7aab896b1a3b | 844 | |
kevman | 2:7aab896b1a3b | 845 | // TBD - SPIF Dual Read Modes Require SPI driver support |
kevman | 2:7aab896b1a3b | 846 | /* |
kevman | 2:7aab896b1a3b | 847 | uint8_t examined_byte; |
kevman | 2:7aab896b1a3b | 848 | |
kevman | 2:7aab896b1a3b | 849 | if (basic_param_table_size > SPIF_BASIC_PARAM_TABLE_QPI_READ_SUPPORT_BYTE) { |
kevman | 2:7aab896b1a3b | 850 | examined_byte = basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_QPI_READ_SUPPORT_BYTE]; |
kevman | 2:7aab896b1a3b | 851 | if (examined_byte & 0x01) { |
kevman | 2:7aab896b1a3b | 852 | // Fast Read 2-2-2 Supported |
kevman | 2:7aab896b1a3b | 853 | read_inst = basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_222_READ_INST_BYTE]; |
kevman | 2:7aab896b1a3b | 854 | _read_dummy_and_mode_cycles = (basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_222_READ_INST_BYTE - 1] >> 5) |
kevman | 2:7aab896b1a3b | 855 | + (basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_222_READ_INST_BYTE - 1] & 0x1F); |
kevman | 2:7aab896b1a3b | 856 | tr_info("\nDEBUG: Read Bus Mode set to 2-2-2, Instruction: 0x%xh", read_inst); |
kevman | 2:7aab896b1a3b | 857 | break; |
kevman | 2:7aab896b1a3b | 858 | } |
kevman | 2:7aab896b1a3b | 859 | } |
kevman | 2:7aab896b1a3b | 860 | examined_byte = basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_FAST_READ_SUPPORT_BYTE]; |
kevman | 2:7aab896b1a3b | 861 | if (examined_byte & 0x20) { |
kevman | 2:7aab896b1a3b | 862 | // Fast Read 1-2-2 Supported |
kevman | 2:7aab896b1a3b | 863 | read_inst = basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_122_READ_INST_BYTE]; |
kevman | 2:7aab896b1a3b | 864 | _read_dummy_and_mode_cycles = (basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_122_READ_INST_BYTE - 1] >> 5) |
kevman | 2:7aab896b1a3b | 865 | + (basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_122_READ_INST_BYTE - 1] & 0x1F); |
kevman | 2:7aab896b1a3b | 866 | tr_debug("\nDEBUG: Read Bus Mode set to 1-2-2, Instruction: 0x%xh", read_inst); |
kevman | 2:7aab896b1a3b | 867 | break; |
kevman | 2:7aab896b1a3b | 868 | } |
kevman | 2:7aab896b1a3b | 869 | if (examined_byte & 0x01) { |
kevman | 2:7aab896b1a3b | 870 | // Fast Read 1-1-2 Supported |
kevman | 2:7aab896b1a3b | 871 | read_inst = basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_112_READ_INST_BYTE]; |
kevman | 2:7aab896b1a3b | 872 | _read_dummy_and_mode_cycles = (basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_112_READ_INST_BYTE - 1] >> 5) |
kevman | 2:7aab896b1a3b | 873 | + (basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_112_READ_INST_BYTE - 1] & 0x1F); |
kevman | 2:7aab896b1a3b | 874 | tr_debug("\nDEBUG: Read Bus Mode set to 1-1-2, Instruction: 0x%xh", _read_instruction); |
kevman | 2:7aab896b1a3b | 875 | break; |
kevman | 2:7aab896b1a3b | 876 | } |
kevman | 2:7aab896b1a3b | 877 | */ |
kevman | 2:7aab896b1a3b | 878 | _read_dummy_and_mode_cycles = 0; |
kevman | 2:7aab896b1a3b | 879 | tr_debug("\nDEBUG: Read Bus Mode set to 1-1-1, Instruction: 0x%xh", read_inst); |
kevman | 2:7aab896b1a3b | 880 | } while (false); |
kevman | 2:7aab896b1a3b | 881 | |
kevman | 2:7aab896b1a3b | 882 | return 0; |
kevman | 2:7aab896b1a3b | 883 | } |
kevman | 2:7aab896b1a3b | 884 | |
kevman | 2:7aab896b1a3b | 885 | int SPIFBlockDevice::_reset_flash_mem() |
kevman | 2:7aab896b1a3b | 886 | { |
kevman | 2:7aab896b1a3b | 887 | // Perform Soft Reset of the Device prior to initialization |
kevman | 2:7aab896b1a3b | 888 | int status = 0; |
kevman | 2:7aab896b1a3b | 889 | char status_value[2] = {0}; |
kevman | 2:7aab896b1a3b | 890 | tr_info("INFO: _reset_flash_mem:\n"); |
kevman | 2:7aab896b1a3b | 891 | //Read the Status Register from device |
kevman | 2:7aab896b1a3b | 892 | if (SPIF_BD_ERROR_OK == _spi_send_general_command(SPIF_RDSR, SPI_NO_ADDRESS_COMMAND, NULL, 0, status_value, 1)) { |
kevman | 2:7aab896b1a3b | 893 | // store received values in status_value |
kevman | 2:7aab896b1a3b | 894 | tr_debug("DEBUG: Reading Status Register Success: value = 0x%x\n", (int)status_value[0]); |
kevman | 2:7aab896b1a3b | 895 | } else { |
kevman | 2:7aab896b1a3b | 896 | tr_debug("ERROR: Reading Status Register failed\n"); |
kevman | 2:7aab896b1a3b | 897 | status = -1; |
kevman | 2:7aab896b1a3b | 898 | } |
kevman | 2:7aab896b1a3b | 899 | |
kevman | 2:7aab896b1a3b | 900 | if (0 == status) { |
kevman | 2:7aab896b1a3b | 901 | //Send Reset Enable |
kevman | 2:7aab896b1a3b | 902 | if (SPIF_BD_ERROR_OK == _spi_send_general_command(SPIF_RSTEN, SPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0)) { |
kevman | 2:7aab896b1a3b | 903 | // store received values in status_value |
kevman | 2:7aab896b1a3b | 904 | tr_debug("DEBUG: Sending RSTEN Success\n"); |
kevman | 2:7aab896b1a3b | 905 | } else { |
kevman | 2:7aab896b1a3b | 906 | tr_error("ERROR: Sending RSTEN failed\n"); |
kevman | 2:7aab896b1a3b | 907 | status = -1; |
kevman | 2:7aab896b1a3b | 908 | } |
kevman | 2:7aab896b1a3b | 909 | |
kevman | 2:7aab896b1a3b | 910 | if (0 == status) { |
kevman | 2:7aab896b1a3b | 911 | //Send Reset |
kevman | 2:7aab896b1a3b | 912 | if (SPIF_BD_ERROR_OK == _spi_send_general_command(SPIF_RST, SPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0)) { |
kevman | 2:7aab896b1a3b | 913 | // store received values in status_value |
kevman | 2:7aab896b1a3b | 914 | tr_debug("DEBUG: Sending RST Success\n"); |
kevman | 2:7aab896b1a3b | 915 | } else { |
kevman | 2:7aab896b1a3b | 916 | tr_error("ERROR: Sending RST failed\n"); |
kevman | 2:7aab896b1a3b | 917 | status = -1; |
kevman | 2:7aab896b1a3b | 918 | } |
kevman | 2:7aab896b1a3b | 919 | _is_mem_ready(); |
kevman | 2:7aab896b1a3b | 920 | } |
kevman | 2:7aab896b1a3b | 921 | } |
kevman | 2:7aab896b1a3b | 922 | |
kevman | 2:7aab896b1a3b | 923 | return status; |
kevman | 2:7aab896b1a3b | 924 | } |
kevman | 2:7aab896b1a3b | 925 | |
kevman | 2:7aab896b1a3b | 926 | bool SPIFBlockDevice::_is_mem_ready() |
kevman | 2:7aab896b1a3b | 927 | { |
kevman | 2:7aab896b1a3b | 928 | // Check Status Register Busy Bit to Verify the Device isn't Busy |
kevman | 2:7aab896b1a3b | 929 | char status_value[2]; |
kevman | 2:7aab896b1a3b | 930 | int retries = 0; |
kevman | 2:7aab896b1a3b | 931 | bool mem_ready = true; |
kevman | 2:7aab896b1a3b | 932 | |
kevman | 2:7aab896b1a3b | 933 | do { |
kevman | 2:7aab896b1a3b | 934 | wait_ms(1); |
kevman | 2:7aab896b1a3b | 935 | retries++; |
kevman | 2:7aab896b1a3b | 936 | //Read the Status Register from device |
kevman | 2:7aab896b1a3b | 937 | if (SPIF_BD_ERROR_OK != _spi_send_general_command(SPIF_RDSR, SPI_NO_ADDRESS_COMMAND, NULL, 0, status_value, |
kevman | 2:7aab896b1a3b | 938 | 1)) { // store received values in status_value |
kevman | 2:7aab896b1a3b | 939 | tr_error("ERROR: Reading Status Register failed\n"); |
kevman | 2:7aab896b1a3b | 940 | } |
kevman | 2:7aab896b1a3b | 941 | } while ((status_value[0] & SPIF_STATUS_BIT_WIP) != 0 && retries < IS_MEM_READY_MAX_RETRIES); |
kevman | 2:7aab896b1a3b | 942 | |
kevman | 2:7aab896b1a3b | 943 | if ((status_value[0] & SPIF_STATUS_BIT_WIP) != 0) { |
kevman | 2:7aab896b1a3b | 944 | tr_error("ERROR: _is_mem_ready FALSE\n"); |
kevman | 2:7aab896b1a3b | 945 | mem_ready = false; |
kevman | 2:7aab896b1a3b | 946 | } |
kevman | 2:7aab896b1a3b | 947 | return mem_ready; |
kevman | 2:7aab896b1a3b | 948 | } |
kevman | 2:7aab896b1a3b | 949 | |
kevman | 2:7aab896b1a3b | 950 | int SPIFBlockDevice::_set_write_enable() |
kevman | 2:7aab896b1a3b | 951 | { |
kevman | 2:7aab896b1a3b | 952 | // Check Status Register Busy Bit to Verify the Device isn't Busy |
kevman | 2:7aab896b1a3b | 953 | char status_value[2]; |
kevman | 2:7aab896b1a3b | 954 | int status = -1; |
kevman | 2:7aab896b1a3b | 955 | |
kevman | 2:7aab896b1a3b | 956 | do { |
kevman | 2:7aab896b1a3b | 957 | if (SPIF_BD_ERROR_OK != _spi_send_general_command(SPIF_WREN, SPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0)) { |
kevman | 2:7aab896b1a3b | 958 | tr_error("ERROR:Sending WREN command FAILED\n"); |
kevman | 2:7aab896b1a3b | 959 | break; |
kevman | 2:7aab896b1a3b | 960 | } |
kevman | 2:7aab896b1a3b | 961 | |
kevman | 2:7aab896b1a3b | 962 | if (false == _is_mem_ready()) { |
kevman | 2:7aab896b1a3b | 963 | tr_error("ERROR: Device not ready, write failed"); |
kevman | 2:7aab896b1a3b | 964 | break; |
kevman | 2:7aab896b1a3b | 965 | } |
kevman | 2:7aab896b1a3b | 966 | |
kevman | 2:7aab896b1a3b | 967 | memset(status_value, 0, 2); |
kevman | 2:7aab896b1a3b | 968 | if (SPIF_BD_ERROR_OK != _spi_send_general_command(SPIF_RDSR, SPI_NO_ADDRESS_COMMAND, NULL, 0, status_value, |
kevman | 2:7aab896b1a3b | 969 | 1)) { // store received values in status_value |
kevman | 2:7aab896b1a3b | 970 | tr_error("ERROR: Reading Status Register failed\n"); |
kevman | 2:7aab896b1a3b | 971 | break; |
kevman | 2:7aab896b1a3b | 972 | } |
kevman | 2:7aab896b1a3b | 973 | |
kevman | 2:7aab896b1a3b | 974 | if ((status_value[0] & SPIF_STATUS_BIT_WEL) == 0) { |
kevman | 2:7aab896b1a3b | 975 | tr_error("ERROR: _set_write_enable failed\n"); |
kevman | 2:7aab896b1a3b | 976 | break; |
kevman | 2:7aab896b1a3b | 977 | } |
kevman | 2:7aab896b1a3b | 978 | status = 0; |
kevman | 2:7aab896b1a3b | 979 | } while (false); |
kevman | 2:7aab896b1a3b | 980 | return status; |
kevman | 2:7aab896b1a3b | 981 | } |
kevman | 2:7aab896b1a3b | 982 | |
kevman | 2:7aab896b1a3b | 983 | /*********************************************/ |
kevman | 2:7aab896b1a3b | 984 | /************* Utility Functions *************/ |
kevman | 2:7aab896b1a3b | 985 | /*********************************************/ |
kevman | 2:7aab896b1a3b | 986 | int SPIFBlockDevice::_utils_find_addr_region(bd_size_t offset) |
kevman | 2:7aab896b1a3b | 987 | { |
kevman | 2:7aab896b1a3b | 988 | //Find the region to which the given offset belong to |
kevman | 2:7aab896b1a3b | 989 | if ((offset > _device_size_bytes) || (_regions_count == 0)) { |
kevman | 2:7aab896b1a3b | 990 | return -1; |
kevman | 2:7aab896b1a3b | 991 | } |
kevman | 2:7aab896b1a3b | 992 | |
kevman | 2:7aab896b1a3b | 993 | if (_regions_count == 1) { |
kevman | 2:7aab896b1a3b | 994 | return 0; |
kevman | 2:7aab896b1a3b | 995 | } |
kevman | 2:7aab896b1a3b | 996 | |
kevman | 2:7aab896b1a3b | 997 | for (int i_ind = _regions_count - 2; i_ind >= 0; i_ind--) { |
kevman | 2:7aab896b1a3b | 998 | |
kevman | 2:7aab896b1a3b | 999 | if (offset > _region_high_boundary[i_ind]) { |
kevman | 2:7aab896b1a3b | 1000 | return (i_ind + 1); |
kevman | 2:7aab896b1a3b | 1001 | } |
kevman | 2:7aab896b1a3b | 1002 | } |
kevman | 2:7aab896b1a3b | 1003 | return -1; |
kevman | 2:7aab896b1a3b | 1004 | |
kevman | 2:7aab896b1a3b | 1005 | } |
kevman | 2:7aab896b1a3b | 1006 | |
kevman | 2:7aab896b1a3b | 1007 | int SPIFBlockDevice::_utils_iterate_next_largest_erase_type(uint8_t &bitfield, int size, int offset, int boundry) |
kevman | 2:7aab896b1a3b | 1008 | { |
kevman | 2:7aab896b1a3b | 1009 | // Iterate on all supported Erase Types of the Region to which the offset belong to. |
kevman | 2:7aab896b1a3b | 1010 | // Iterates from highest type to lowest |
kevman | 2:7aab896b1a3b | 1011 | uint8_t type_mask = ERASE_BITMASK_TYPE4; |
kevman | 2:7aab896b1a3b | 1012 | int i_ind = 0; |
kevman | 2:7aab896b1a3b | 1013 | int largest_erase_type = 0; |
kevman | 2:7aab896b1a3b | 1014 | for (i_ind = 3; i_ind >= 0; i_ind--) { |
kevman | 2:7aab896b1a3b | 1015 | if (bitfield & type_mask) { |
kevman | 2:7aab896b1a3b | 1016 | largest_erase_type = i_ind; |
kevman | 2:7aab896b1a3b | 1017 | if ((size > (int)(_erase_type_size_arr[largest_erase_type])) && |
kevman | 2:7aab896b1a3b | 1018 | ((boundry - offset) > (int)(_erase_type_size_arr[largest_erase_type]))) { |
kevman | 2:7aab896b1a3b | 1019 | break; |
kevman | 2:7aab896b1a3b | 1020 | } else { |
kevman | 2:7aab896b1a3b | 1021 | bitfield &= ~type_mask; |
kevman | 2:7aab896b1a3b | 1022 | } |
kevman | 2:7aab896b1a3b | 1023 | } |
kevman | 2:7aab896b1a3b | 1024 | type_mask = type_mask >> 1; |
kevman | 2:7aab896b1a3b | 1025 | } |
kevman | 2:7aab896b1a3b | 1026 | |
kevman | 2:7aab896b1a3b | 1027 | if (i_ind == 4) { |
kevman | 2:7aab896b1a3b | 1028 | tr_error("ERROR: no erase type was found for current region addr"); |
kevman | 2:7aab896b1a3b | 1029 | } |
kevman | 2:7aab896b1a3b | 1030 | return largest_erase_type; |
kevman | 2:7aab896b1a3b | 1031 | |
kevman | 2:7aab896b1a3b | 1032 | } |
kevman | 2:7aab896b1a3b | 1033 | |
kevman | 2:7aab896b1a3b | 1034 | /*********************************************/ |
kevman | 2:7aab896b1a3b | 1035 | /************** Local Functions **************/ |
kevman | 2:7aab896b1a3b | 1036 | /*********************************************/ |
kevman | 2:7aab896b1a3b | 1037 | static unsigned int local_math_power(int base, int exp) |
kevman | 2:7aab896b1a3b | 1038 | { |
kevman | 2:7aab896b1a3b | 1039 | // Integer X^Y function, used to calculate size fields given in 2^N format |
kevman | 2:7aab896b1a3b | 1040 | int result = 1; |
kevman | 2:7aab896b1a3b | 1041 | while (exp) { |
kevman | 2:7aab896b1a3b | 1042 | result *= base; |
kevman | 2:7aab896b1a3b | 1043 | exp--; |
kevman | 2:7aab896b1a3b | 1044 | } |
kevman | 2:7aab896b1a3b | 1045 | return result; |
kevman | 2:7aab896b1a3b | 1046 | } |
kevman | 2:7aab896b1a3b | 1047 | |
kevman | 2:7aab896b1a3b | 1048 |