Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/master/libraries/AP_FlashIface/AP_FlashIface_JEDEC.cpp
Views: 1798
/*1* This file is free software: you can redistribute it and/or modify it2* under the terms of the GNU General Public License as published by the3* Free Software Foundation, either version 3 of the License, or4* (at your option) any later version.5*6* This file is distributed in the hope that it will be useful, but7* WITHOUT ANY WARRANTY; without even the implied warranty of8* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.9* See the GNU General Public License for more details.10*11* You should have received a copy of the GNU General Public License along12* with this program. If not, see <http://www.gnu.org/licenses/>.13*14* Code by15* Andy Piper16* Siddharth Bharat Purohit, Cubepilot Pty. Ltd.17*/18/*19Implements Common Flash Interface Driver based on following20standard published by JEDEC21* JEDEC Standard, JESD216D, Serial Flash Discoverable Parameters (SFDP)22*/2324#include <AP_HAL/AP_HAL.h>25#include "AP_FlashIface_JEDEC.h"26#include <AP_Math/AP_Math.h>27#ifdef HAL_BOOTLOADER_BUILD28#include <AP_HAL_ChibiOS/WSPIDevice.h>29#include "../../Tools/AP_Bootloader/support.h"30#else31extern const AP_HAL::HAL& hal;32#endif3334enum class SupportedDeviceType {35QuadSPI = 0,36OctoSPI = 137};3839struct supported_device {40const char* name;41uint8_t manufacturer_id;42uint8_t device_id;43SupportedDeviceType device_type;44};4546static const struct supported_device supported_devices[] = {47{"mt25q", 0x20, 0xBA, SupportedDeviceType::QuadSPI}, // https://www.mouser.in/datasheet/2/671/mict_s_a0003959700_1-2290909.pdf48{"w25q", 0xEF, 0x40, SupportedDeviceType::QuadSPI},49{"w25q-dtr", 0xEF, 0x70, SupportedDeviceType::QuadSPI},50};5152#ifdef HAL_BOOTLOADER_BUILD53#define DELAY_MILLIS(x) do { chThdSleepMilliseconds(x); } while(0)54#define DELAY_MICROS(x) do { chThdSleepMicroseconds(x); } while(0)55#else56#define DELAY_MILLIS(x) do { hal.scheduler->delay(x); } while(0)57#define DELAY_MICROS(x) do { hal.scheduler->delay_microseconds(x); } while(0)58#endif5960#define MAX_SUPPORTED_FLASH_SIZE 0x1FFFFFFUL61// Vendor Specific Constants62// Following Commands Sets were found here:63// * JEDEC Standard JESD251-A1, Addendum No. 1 to JESD251, Optional x4 Quad I/O64// With Data Strobe65/// NOTE: Except Read ID and Multiline Read ID, they seem to be66// constant across manufacturers, but can't find official standard on67// this.68#define CMD_READ_ID 0x9F69#define CMD_MULTILINE_READ_ID 0xAF70#define CMD_PAGE_PROGRAM 0x0271#define CMD_WRITE_DISABLE 0x0472#define CMD_READ_STATUS_1 0x0573#define CMD_READ_STATUS_2 0x3574#define CMD_MASS_ERASE 0xC775#define CMD_RESET_ENABLE 0x6676#define CMD_RESET_MEMORY 0x9977#define CMD_READ_SFDP 0x5A7879#define SFDP_MASK(lo, hi) (((1UL<<(hi)) - ((1UL<<(lo)))) + (1UL<<(hi)))80#define SFDP_GET_BITS(x, lo, hi) (((x) & SFDP_MASK(lo, hi)) >> (lo))81#define SFDP_GET_BIT(x, bit) ((x) & (1<<(bit)))8283#define SFDP_HDR_NUM_PARAMS(x) (SFDP_GET_BITS(x[1], 16, 19) + 1)84#define SFDP_HDR_PARAM_REV(x) SFDP_GET_BITS(x[1], 0, 15)85#define SFDP_PARAM_ID(x) ((SFDP_GET_BITS(x[0], 0, 3) << 8) | SFDP_GET_BITS(x[1], 24, 31))86#define SFDP_PARAM_DWORD_LEN(x) SFDP_GET_BITS(x[0], 24, 31)87#define SFDP_PARAM_POINTER(x) SFDP_GET_BITS(x[1], 0, 23)8889#define SFDP_REV_1_5 0x010590#define SFDP_REV_1_6 0x010691// quad enable for winbond92#define QUAD_ENABLE_B1R2 0x493// octo enable for winbond94#define OCTO_ENABLE_B3R2 0x19596//#define DEBUG9798#ifdef HAL_BOOTLOADER_BUILD99#ifdef DEBUG100#define Debug(fmt, args ...) do {uprintf("JEDEC: " fmt "\n", ## args);} while(0)101#else102#define Debug(fmt, args ...)103#endif104#define Msg_Print(fmt, args ...) do {uprintf("JEDEC: " fmt "\n", ## args);} while(0)105#else106#ifdef DEBUG107#define Debug(fmt, args ...) do {hal.console->printf("JEDEC: " fmt "\n", ## args);} while(0)108#else109#define Debug(fmt, args ...)110#endif111#define Msg_Print(fmt, args ...) do {hal.console->printf("JEDEC: " fmt "\n", ## args);} while(0)112#endif // #ifdef HAL_BOOTLOADER_BUILD113114#define MAX_READ_SIZE 1024UL115116#ifdef HAL_BOOTLOADER_BUILD117static ChibiOS::WSPIDeviceManager wspi;118#endif119120bool AP_FlashIface_JEDEC::init()121{122// Get device bus by name123_dev = nullptr;124for (uint8_t i = 0; i < ARRAY_SIZE(supported_devices); i++) {125#ifdef HAL_BOOTLOADER_BUILD126_dev = wspi.get_device(supported_devices[i].name);127#else128_dev = hal.wspi->get_device(supported_devices[i].name);129#endif130if (_dev) {131_dev_list_idx = i;132break;133}134}135136if (!_dev) {137AP_HAL::panic("Ext Flash Not Found!");138}139140DELAY_MILLIS(5); // required by w25q141// Reset Device involves trying to soft reset the chip142// as when system reboots the device might not have.143reset_device();144145DELAY_MICROS(30); // required by w25q146147// Detecting Device involves trying to read Device ID and matching148// with what we expect. Along with extracting info from SFDP149if (!detect_device()) {150Msg_Print("Failed to detect flash device: %s", supported_devices[_dev_list_idx].name);151return false;152}153154// Configuring Device involved setting chip to correct WSPI mode155// i.e. 1-4-4156if (!configure_device()) {157Msg_Print("Failed to config flash device: %s", supported_devices[_dev_list_idx].name);158return false;159}160161Msg_Print("Detected Flash Device: %s", supported_devices[_dev_list_idx].name);162return true;163}164165//////////////////////////////////////////////////////166////////////////// Internal Methods //////////////////167//////////////////////////////////////////////////////168169// reset chip to known default power on state170void AP_FlashIface_JEDEC::reset_device()171{172// Get chip out of XIP mode173AP_HAL::WSPIDevice::CommandHeader cmd;174#ifndef HAL_BOOTLOADER_BUILD // this is required in order to run jedec_test with a regular bootloader175_dev->get_semaphore()->take_blocking();176#endif177178/* Single line CMD_RESET_MEMORY command.*/179cmd.cmd = CMD_RESET_ENABLE;180cmd.cfg = AP_HAL::WSPI::CFG_CMD_MODE_ONE_LINE;181cmd.addr = 0;182cmd.alt = 0;183cmd.dummy = 0;184_dev->set_cmd_header(cmd);185_dev->transfer(nullptr, 0, nullptr, 0);186187188/* Single line N25Q_CMD_RESET_MEMORY command.*/189cmd.cmd = CMD_RESET_MEMORY;190cmd.cfg = AP_HAL::WSPI::CFG_CMD_MODE_ONE_LINE;191cmd.addr = 0;192cmd.alt = 0;193cmd.dummy = 0;194_dev->set_cmd_header(cmd);195_dev->transfer(nullptr, 0, nullptr, 0);196197// By now we are pretty sure the chip is reset198}199200// Does initial configuration to bring up and setup chip201bool AP_FlashIface_JEDEC::detect_device()202{203AP_HAL::WSPIDevice::CommandHeader cmd;204205{206uint8_t buf[3] {};207cmd.cmd = CMD_READ_ID;208cmd.cfg = AP_HAL::WSPI::CFG_CMD_MODE_ONE_LINE |209AP_HAL::WSPI::CFG_DATA_MODE_ONE_LINE;210cmd.addr = 0;211cmd.alt = 0;212cmd.dummy = 0;213214_dev->set_cmd_header(cmd);215if (!_dev->transfer(nullptr, 0, buf, sizeof(buf))) {216Debug("Failed to read Device ID");217return false;218}219220if (buf[0] != supported_devices[_dev_list_idx].manufacturer_id ||221buf[1] != supported_devices[_dev_list_idx].device_id) {222return false;223}224}225226// Read SFDP header to get information Ref. JESD216D 4 and 6.2227{228uint32_t sfdp_header[2];229230cmd.cmd = CMD_READ_SFDP;231cmd.cfg = AP_HAL::WSPI::CFG_CMD_MODE_ONE_LINE |232AP_HAL::WSPI::CFG_ADDR_MODE_ONE_LINE |233AP_HAL::WSPI::CFG_ADDR_SIZE_24 |234AP_HAL::WSPI::CFG_DATA_MODE_ONE_LINE;235cmd.addr = 0;236cmd.alt = 0;237cmd.dummy = 8; // 8 dummy cycles238_dev->set_cmd_header(cmd);239if (!_dev->transfer(nullptr, 0, (uint8_t*)sfdp_header, sizeof(sfdp_header))) {240Debug("SFDP Header read failed");241return false;242}243244// Read Signature245if (memcmp(sfdp_header, "SFDP", 4)) {246Debug("SFDP Bad Signature: 0x%lx", (unsigned long)sfdp_header[0]);247return false;248}249250// Read Num Param Headers251if (SFDP_HDR_NUM_PARAMS(sfdp_header) == 0) {252Debug("Unsupported number of param headers %ld", (unsigned long)SFDP_HDR_NUM_PARAMS(sfdp_header));253return false;254}255// Read Revision256_desc.param_rev = SFDP_HDR_PARAM_REV(sfdp_header);257if (_desc.param_rev != SFDP_REV_1_6 && _desc.param_rev != SFDP_REV_1_5) {258Debug("Unsupported revision %x", (unsigned int)_desc.param_rev);259return false;260}261}262263264// Read Param Header Ref. JESD216D 6.4.1 6.4.2265{266uint32_t param_header[2] {}; // read only first parameter header267// Immediately after 2 DWORDS of SFDP Header268cmd.addr = 2*sizeof(uint32_t);269_dev->set_cmd_header(cmd);270if (!_dev->transfer(nullptr, 0, (uint8_t*)param_header, sizeof(param_header))) {271Debug("Param header read failed");272return false;273}274275if (SFDP_PARAM_ID(param_header) != 0xFF) {276Debug("Only basic Param Table supported not %lx", (unsigned long)SFDP_PARAM_ID(param_header));277return false;278}279// Lets get the length of parameter table280_desc.param_table_len = MIN(SFDP_PARAM_DWORD_LEN(param_header), 20UL);281_desc.param_table_pointer = SFDP_PARAM_POINTER(param_header);282}283284// Read and parse the param table285{286uint32_t param_table[20] {};287cmd.addr = _desc.param_table_pointer;288_dev->set_cmd_header(cmd);289if (!_dev->transfer(nullptr, 0, (uint8_t*)param_table, _desc.param_table_len*sizeof(uint32_t))) {290Debug("Failed to read Parameter Table");291return false;292}293294// Flash Memory details Ref. JESD216D 6.4.5 6.4.14295if (SFDP_GET_BIT(param_table[1], 31)) {296Debug("Unsupported Flash Size");297return false;298}299_desc.flash_size = SFDP_GET_BITS(param_table[1], 0, 30)/8;300// But we only support 24bit (3Bytes) addressing right now301// So limit is upto 32MB addressing302if (_desc.flash_size >= MAX_SUPPORTED_FLASH_SIZE) {303_desc.flash_size = MAX_SUPPORTED_FLASH_SIZE;304}305_desc.page_size = 1UL<<SFDP_GET_BITS(param_table[10], 4, 7);306_desc.page_count = _desc.flash_size/_desc.page_size;307if (_desc.page_count == 0) {308Debug("Page size greater than flash size unsupported");309return false;310}311312// Erase Flash Memory details Ref. JESD216D 6.4.11 6.4.12313for (uint8_t i = 0; i < 4; i++) {314uint32_t size = 1UL<<SFDP_GET_BITS(param_table[7 + (i/2)], 0 + 16*(i%2), 7 + 16*(i%2));315uint8_t ins = SFDP_GET_BITS(param_table[7 + (i/2)], 8 + 16*(i%2), 15 + 16*(i%2));316if ((size-1) > 0) {317_desc.erase_type[i].size = size;318_desc.erase_type[i].ins = ins;319if (size > _desc.sector_size) {320_desc.sector_size = size;321}322if (size < _desc.min_erase_size) {323_desc.min_erase_size = size;324}325}326}327_desc.sector_count = _desc.flash_size/_desc.sector_size;328if (_desc.sector_count == 0) {329_desc.sector_count = 1;330}331// Read Erase Times 6.4.13332uint8_t timeout_mult = 2*(SFDP_GET_BITS(param_table[9], 0, 3) + 1);333for (uint8_t i = 0; i < 4; i++) {334if (_desc.erase_type[i].size) {335uint32_t unit = SFDP_GET_BITS(param_table[9], 9+(7*i), 10+(7*i));336uint8_t val = SFDP_GET_BITS(param_table[9], 4+(7*i), 8+(7*i));337if (unit == 0b00) {338unit = 1; //1ms339} else if (unit == 0b01) {340unit = 16; // 16ms341} else if (unit == 0b10) {342unit = 128; // 128ms343} else if (unit == 0b11) {344unit = 1000; // 1s345}346347_desc.erase_type[i].delay_ms = (val+1)*unit;348_desc.erase_type[i].timeout_ms = timeout_mult*_desc.erase_type[i].delay_ms;349}350}351// Mass Erase times 6.4.14352uint32_t unit = SFDP_GET_BITS(param_table[10], 29, 30);353if (unit == 0b00) {354unit = 16; // 16ms355} else if (unit == 0b01) {356unit = 256; // 256ms357} else if (unit == 0b10) {358unit = 4000; // 4s359} else if (unit == 0b11) {360unit = 64000; // 64s361}362_desc.mass_erase_delay_ms = (SFDP_GET_BITS(param_table[10], 24, 28) + 1)*unit;363_desc.mass_erase_timeout_ms = timeout_mult*_desc.mass_erase_delay_ms;364365// Setup Write Enable Instruction Ref. JESD216D 6.4.19366// If needed legacy support Ref. JESD216D 6.4.4 and implement that367if (SFDP_GET_BIT(param_table[15], 0) ||368SFDP_GET_BIT(param_table[15], 1)) {369_desc.write_enable_ins = 0x06;370} else if (SFDP_GET_BIT(param_table[15], 2)) {371_desc.write_enable_ins = 0x50;372} else if (SFDP_GET_BITS(param_table[15], 3, 6)) {373Debug("Unsupported Register Write Enable Config");374return false;375}376377// Setup Program timings Ref. JESD216D 6.4.14378// unit = SFDP_GET_BIT(param_table[10], 23)?1:8;379// _desc.add_byte_prog_delay_us = (SFDP_GET_BITS(19, 22) + 1) * unit;380// _desc.add_byte_prog_timeout_us = _desc.add_byte_prog_delay_us * timeout_mult;381// unit = SFDP_GET_BIT(param_table[10], 18)?1:8;382// _desc.first_byte_prog_delay_us = (SFDP_GET_BITS(14, 17) + 1) * unit;383// _desc.first_byte_prog_timeout_us = _desc.first_byte_prog_delay_us * timeout_mult;384385// Implement above code if more precise delay and timeouts are needed while programming386// otherwise fraction of page timings should be fine387timeout_mult = 2*(SFDP_GET_BITS(param_table[10], 0, 3) + 1);388unit = SFDP_GET_BIT(param_table[10], 13)?64:8;389_desc.page_prog_delay_us = (SFDP_GET_BITS(param_table[10], 8, 12) + 1) * unit;390_desc.page_prog_timeout_us = _desc.page_prog_delay_us * timeout_mult;391392uint8_t fast_read_dword = 2; // QuadSPI configuration dword393#if HAL_USE_OCTOSPI394// To use Octo SPI it must be both supported in hardware and configured395const bool octo_spi = supported_devices[_dev_list_idx].device_type == SupportedDeviceType::OctoSPI396&& SFDP_GET_BITS(param_table[16], 8, 15);397398if (octo_spi) {399fast_read_dword = 16;400}401#else402const bool octo_spi = false;403#endif404405// Configure Octo/Quad Mode Enable and Read Sequence, Ref. JESD216D 6.4.8 6.4.10 6.4.18, 6.4.20406if (!octo_spi && !SFDP_GET_BIT(param_table[0], 21)) {407Debug("1-4-4 mode unsupported");408return false;409}410411_desc.fast_read_ins = SFDP_GET_BITS(param_table[fast_read_dword], 8, 15);412// we get number of dummy clocks cycles needed, also include mode bits413_desc.fast_read_mode_clocks = SFDP_GET_BITS(param_table[fast_read_dword], 5, 7);414_desc.fast_read_dummy_cycles = SFDP_GET_BITS(param_table[fast_read_dword], 0, 4);415416if (_desc.fast_read_ins == 0) {417Debug("Fast read unsupported");418return false;419}420421if (!octo_spi) {422_desc.wide_mode_enable = SFDP_GET_BITS(param_table[14], 20, 22);423if (_desc.wide_mode_enable != 0b000 && _desc.wide_mode_enable != QUAD_ENABLE_B1R2) {424Debug("Unsupported Quad Enable Requirement 0x%x", _desc.wide_mode_enable);425return false;426}427if (SFDP_GET_BIT(param_table[14], 4) && _desc.wide_mode_enable != QUAD_ENABLE_B1R2) {428Debug("Unsupported Quad Enable Requirement: set QE bits");429return false;430}431}432#if HAL_USE_OCTOSPI433else {434_desc.wide_mode_enable = SFDP_GET_BITS(param_table[18], 20, 22);435if (_desc.wide_mode_enable != 0b000 && _desc.wide_mode_enable != OCTO_ENABLE_B3R2) {436Debug("Unsupported Octo Enable Requirement 0x%x", _desc.wide_mode_enable);437return false;438}439if (SFDP_GET_BIT(param_table[18], 5) && _desc.wide_mode_enable != OCTO_ENABLE_B3R2) {440Debug("Unsupported Quad Enable Requirement: set WREN bits");441return false;442}443}444#endif445446// if (!SFDP_GET_BIT(param_table[4], 4)) {447// Debug("Quad mode unsupported");448// return false;449// }450451452// Configure XIP mode Ref. JESD216D 6.4.18453if (SFDP_GET_BIT(param_table[14], 9)) {454_desc.is_xip_supported = true;455} else {456_desc.is_xip_supported = false;457}458if (_desc.is_xip_supported) {459if (SFDP_GET_BIT(param_table[14],16)) {460_desc.entry_method = AP_FlashIface_JEDEC::XIP_ENTRY_METHOD_1;461} else if (SFDP_GET_BIT(param_table[14],17)) {462_desc.entry_method = AP_FlashIface_JEDEC::XIP_ENTRY_METHOD_2;463} else {464Debug("Unsupported XIP enable sequence 0x%x", uint8_t(SFDP_GET_BITS(param_table[14],16, 19)));465}466}467468// Configure Status Polling Method Ref. JESD216D 6.4.17469if (SFDP_GET_BIT(param_table[13], 3)) {470_desc.legacy_status_polling = false;471_desc.status_read_ins = 0x70;472} else if (SFDP_GET_BIT(param_table[13], 2)) {473_desc.legacy_status_polling = true;474_desc.status_read_ins = CMD_READ_STATUS_1;475}476}477initialised = true;478return true;479}480481// Configures device to normal working state, currently 1-4-4 WSPI482bool AP_FlashIface_JEDEC::configure_device()483{484// Enable 1-4-4 mode485if (_desc.wide_mode_enable == QUAD_ENABLE_B1R2) {486uint8_t reg1 = 0, reg2 = 0;487if (!read_reg(CMD_READ_STATUS_1, reg1)) {488Debug("Failed reg1 read");489return false;490}491if (!read_reg(CMD_READ_STATUS_2, reg2)) {492Debug("Failed reg2 read");493return false;494}495write_enable();496wait_ready();497498AP_HAL::WSPIDevice::CommandHeader cmd {499.cmd = 0x01,500.cfg = AP_HAL::WSPI::CFG_CMD_MODE_ONE_LINE |501AP_HAL::WSPI::CFG_DATA_MODE_ONE_LINE,502.addr = 0,503.alt = 0,504.dummy = 0505};506507reg2 |= 0x2; // enable QE bit508uint8_t write_val[2] { reg1, reg2 };509_dev->set_cmd_header(cmd);510511if (!_dev->transfer(write_val, 2, nullptr, 0)) {512Debug("Failed QE write");513write_disable();514return false;515}516517write_disable();518519if (!read_reg(0x35, reg2) || (reg2 & 0x2) == 0) {520Debug("Failed to set QE bit");521return false;522}523524Debug("Device configured for 1-4-4 mode: QE bit 0x%x, fast read ins/cycles 0x%x/0x%x",525_desc.quad_mode_enable, _desc.fast_read_ins, _desc.fast_read_dummy_cycles);526527// Hurray! We are in wide mode528_wide_spi_mode = WSPIMode::QuadSPI;529}530#if HAL_USE_OCTOSPI531// Enable 1-8-8 mode532else if (_desc.wide_mode_enable == OCTO_ENABLE_B3R2) {533uint8_t reg2;534535AP_HAL::WSPIDevice::CommandHeader read_reg {536.cmd = 0x65,537.cfg = AP_HAL::WSPI::CFG_CMD_MODE_ONE_LINE |538AP_HAL::WSPI::CFG_DATA_MODE_ONE_LINE |539AP_HAL::WSPI::CFG_ADDR_MODE_ONE_LINE |540AP_HAL::WSPI::CFG_ADDR_SIZE_8,541.addr = 0x02,542.alt = 0,543.dummy = 8,544};545546_dev->set_cmd_header(read_reg);547if (!_dev->transfer(nullptr, 0, ®2, sizeof(reg2))) {548Debug("Failed reg2 read");549return false;550}551552write_enable();553wait_ready();554555reg2 |= 0x4; // enable OE bit556557if (!write_reg(0x31, reg2)) {558Debug("Failed OE write");559write_disable();560return false;561}562563write_disable();564565_dev->set_cmd_header(read_reg);566if (!_dev->transfer(nullptr, 0, ®2, sizeof(reg2)) || (reg2 & 0x4) == 0) {567Debug("Failed to set OE bit");568return false;569}570Debug("Device configured for 1-8-8 mode: OE bit 0x%x, fast read ins/cycles 0x%x/0x%x",571_desc.wide_mode_enable, _desc.fast_read_ins, _desc.fast_read_dummy_cycles);572573// Hurray! We are in wide mode574_wide_spi_mode = WSPIMode::OctoSPI;575}576#endif577return true;578}579580// Enables commands that modify flash data or settings581bool AP_FlashIface_JEDEC::write_enable()582{583if (_desc.write_enable_ins) {584wait_ready();585write_enable_called = true;586return send_cmd(_desc.write_enable_ins);587}588return true;589}590591// Disables commands that modify flash data or settings592bool AP_FlashIface_JEDEC::write_disable()593{594if (_desc.write_enable_ins) {595wait_ready();596write_enable_called = true;597return send_cmd(CMD_WRITE_DISABLE);598}599return true;600}601602// Read modify write register603bool AP_FlashIface_JEDEC::modify_reg(uint8_t read_ins, uint8_t write_ins,604uint8_t mask, uint8_t val)605{606// Read607uint8_t reg_val;608if (!read_reg(read_ins, reg_val)) {609return false;610}611612// Modify613reg_val = (reg_val & ~mask) | (val & mask);614615// Write616if (!write_reg(write_ins, reg_val)) {617return false;618}619return true;620}621622// reads a register value of chip using instruction623bool AP_FlashIface_JEDEC::read_reg(uint8_t read_ins, uint8_t &read_val)624{625AP_HAL::WSPIDevice::CommandHeader cmd;626cmd.cmd = read_ins;627cmd.cfg = AP_HAL::WSPI::CFG_CMD_MODE_ONE_LINE |628AP_HAL::WSPI::CFG_DATA_MODE_ONE_LINE;629cmd.addr = 0;630cmd.alt = 0;631cmd.dummy = 0;632_dev->set_cmd_header(cmd);633if (!_dev->transfer(nullptr, 0, &read_val, sizeof(read_val))) {634Debug("Failed Register Read");635return false;636}637return true;638}639640// sends instruction to write a register value in the chip641bool AP_FlashIface_JEDEC::write_reg(uint8_t write_ins, uint8_t write_val)642{643AP_HAL::WSPIDevice::CommandHeader cmd;644cmd.cmd = write_ins;645cmd.cfg = AP_HAL::WSPI::CFG_CMD_MODE_ONE_LINE |646AP_HAL::WSPI::CFG_DATA_MODE_ONE_LINE;647cmd.addr = 0;648cmd.alt = 0;649cmd.dummy = 0;650_dev->set_cmd_header(cmd);651if (!_dev->transfer(&write_val, 1, nullptr, 0)) {652Debug("Failed Register Write");653return false;654}655return true;656}657658// Sends WSPI command without data659bool AP_FlashIface_JEDEC::send_cmd(uint8_t ins)660{661AP_HAL::WSPIDevice::CommandHeader cmd;662cmd.cmd = ins;663cmd.cfg = AP_HAL::WSPI::CFG_CMD_MODE_ONE_LINE;664cmd.addr = 0;665cmd.alt = 0;666cmd.dummy = 0;667_dev->set_cmd_header(cmd);668if (!_dev->transfer(nullptr, 0, nullptr, 0)) {669Debug("Failed Register Write");670return false;671}672return true;673}674675//////////////////////////////////////////////////////676////////////////////PUBLIC METHODS////////////////////677//////////////////////////////////////////////////////678/**679* @details Sends command to erase the entire chips.680*681* @param[out] delay_ms Time to wait until next is_device_busy call682* @param[out] timeout_ms Time by which the erase should have timedout683*684* @return The operation status.685* @retval false if the operation failed.686* @retval true if the operation succeeded.687*688*/689bool AP_FlashIface_JEDEC::start_mass_erase(uint32_t &delay_ms, uint32_t &timeout_ms)690{691write_enable();692wait_ready();693694AP_HAL::WSPIDevice::CommandHeader cmd;695cmd.cmd = CMD_MASS_ERASE;696cmd.cfg = AP_HAL::WSPI::CFG_CMD_MODE_ONE_LINE;697cmd.addr = 0;698cmd.alt = 0;699cmd.dummy = 0;700_dev->set_cmd_header(cmd);701if (!_dev->transfer(nullptr, 0, nullptr, 0)) { // Command only702write_disable();703Debug("Failed to send erase command");704return false;705}706delay_ms = _desc.mass_erase_delay_ms;707timeout_ms = _desc.mass_erase_timeout_ms;708write_disable();709return true;710}711712/**713* @details Sends command to erase a sector of the chip.714*715* @param[in] sector Sector number to be erased716* @param[out] delay_ms Time to wait until next is_device_busy call717* @param[out] timeout_ms Time by which the erase should have timedout718*719* @return The operation status.720* @retval false if the operation failed.721* @retval true if the operation succeeded.722*723*/724bool AP_FlashIface_JEDEC::start_sector_erase(uint32_t sector, uint32_t &delay_ms, uint32_t &timeout_ms)725{726if (sector > _desc.sector_count) {727Debug("Invalid sector");728return false;729}730uint32_t erasing;731bool ret = start_erase_offset(_desc.sector_size*sector, _desc.sector_size, erasing, delay_ms, timeout_ms);732if (!ret || (erasing != _desc.sector_size)) {733Debug("Failed to erase sector");734return false;735}736return true;737}738739/**740* @details Tries to erase as much as possible starting from the offset741* until size. User needs to call this as many times as needed742* taking already erased bytes into account, until desired erase743* has taken place744*745* @param[in] offset address offset for erase746* @param[in] size size desired to be erased747* @param[out] erasing number of bytes erasing748* @param[out] delay_ms Time to wait until next is_device_busy call749* @param[out] timeout_ms Time by which the erase should have timedout750*751* @return The operation status.752* @retval false if the operation failed.753* @retval true if the operation succeeded.754*755*/756bool AP_FlashIface_JEDEC::start_erase_offset(uint32_t offset, uint32_t size, uint32_t &erasing,757uint32_t &delay_ms, uint32_t &timeout_ms)758{759uint8_t ins = 0;760uint32_t erase_size = 0;761erasing = 0;762// Find the maximum size we can erase763for (uint8_t i=0; i < 4; i++) {764if (_desc.erase_type[i].size == 0) {765continue;766}767if (_desc.erase_type[i].size < erase_size) {768// we already found a larger size we can erase769continue;770}771// check if we can find an instruction to match the erase req.772if ((size >= _desc.erase_type[i].size) && !(offset % _desc.erase_type[i].size)) {773erase_size = size;774ins = _desc.erase_type[i].ins;775delay_ms = _desc.erase_type[i].delay_ms;776timeout_ms = _desc.erase_type[i].timeout_ms;777}778}779if (erase_size == 0) {780Debug("Requested Erase size is too small");781return false;782}783if ((offset+erase_size) > _desc.flash_size) {784Debug("Requested erase overflows supported flash size");785return false;786}787// Start Erasing788write_enable();789wait_ready();790791AP_HAL::WSPIDevice::CommandHeader cmd;792cmd.cmd = ins;793cmd.cfg = AP_HAL::WSPI::CFG_CMD_MODE_ONE_LINE |794AP_HAL::WSPI::CFG_ADDR_MODE_ONE_LINE |795AP_HAL::WSPI::CFG_ADDR_SIZE_24;796cmd.addr = offset;797cmd.alt = 0;798cmd.dummy = 0;799_dev->set_cmd_header(cmd);800if (!_dev->transfer(nullptr, 0, nullptr, 0)) { // Command only801write_disable();802Debug("Failed to send erase command");803return false;804}805write_disable();806erasing = erase_size;807return true;808}809810811/**812* @details Check if selected sector is erased.813*814* @param[in] sector sector for which to check erase815* @return The operation status.816* @retval false if the operation failed.817* @retval true if the operation succeeded.818*819*/820bool AP_FlashIface_JEDEC::verify_sector_erase(uint32_t sector)821{822uint8_t buf[MAX_READ_SIZE] {}; // Read 1KB per read823for (uint32_t offset = _desc.sector_size*sector; offset < (_desc.sector_size*(sector+1)); offset+=sizeof(buf)) {824if (read(offset, buf, sizeof(buf))) {825for (uint16_t i = 0; i < sizeof(buf); i++) {826if (buf[i] != 0xFF) {827Debug("Found unerased byte %x @ offset %ld", (unsigned int)buf[i], (unsigned long)offset);828return false;829}830}831} else {832Debug("Read Failed");833return false;834}835}836return true;837}838839/**840* @details Sends command to start programming a page of the chip.841*842* @param[in] page Page number to be written to843* @param[in] data data to be written844* @param[out] delay_us Time to wait until next is_device_busy call845* @param[out] timeout_us Time after which the erase should be timedout,846* should be reset at every call.847* @return The operation status.848* @retval false if the operation failed.849* @retval true if the operation succeeded.850*851*/852bool AP_FlashIface_JEDEC::start_program_page(uint32_t page, const uint8_t* data,853uint32_t &delay_us, uint32_t &timeout_us)854{855if (page > _desc.page_count) {856Debug("Invalid Page");857return false;858}859uint32_t programming;860bool ret = start_program_offset(_desc.page_size*page, data, _desc.sector_size, programming, delay_us, timeout_us);861if (!ret || (programming != _desc.page_size)) {862Debug("Failed to program page");863return false;864}865return true;866}867868/**869* @details Tries to program as much as possible starting from the offset870* until size. User needs to call this as many times as needed871* taking already programmed bytes into account.872*873* @param[in] offset address offset for program874* @param[in] data data to be programmed875* @param[in] size size desired to be programmed876* @param[out] programming number of bytes programming, taking care of the limits877* @param[out] delay_us Time to wait until program typically finishes878* @param[out] timeout_us Time by which current program should have timedout.879* @return The operation status.880* @retval false if the operation failed.881* @retval true if the operation succeeded.882*883*/884bool AP_FlashIface_JEDEC::start_program_offset(uint32_t offset, const uint8_t* data, uint32_t size, uint32_t &programming,885uint32_t &delay_us, uint32_t &timeout_us)886{887if (size > _desc.page_size) {888// we can only program single889// page at the max in one go890size = _desc.page_size;891}892// Ensure we don't go beyond the page of offset, while writing893size = MIN(_desc.page_size - (offset % _desc.page_size), size);894895write_enable();896wait_ready();897898AP_HAL::WSPIDevice::CommandHeader cmd;899cmd.cmd = CMD_PAGE_PROGRAM;900cmd.cfg = AP_HAL::WSPI::CFG_CMD_MODE_ONE_LINE |901AP_HAL::WSPI::CFG_ADDR_MODE_ONE_LINE |902AP_HAL::WSPI::CFG_ADDR_SIZE_24 |903AP_HAL::WSPI::CFG_DATA_MODE_ONE_LINE;904cmd.addr = offset;905cmd.alt = 0;906cmd.dummy = 0;907_dev->set_cmd_header(cmd);908if (!_dev->transfer(data, size, nullptr, 0)) { // Command only909write_disable();910Debug("Failed to send program command");911return false;912}913write_disable();914programming = size;915// we are mostly going to program in chunks so this will do916delay_us = (_desc.page_prog_delay_us*size)/(_desc.page_size);917timeout_us = (_desc.page_prog_timeout_us*size)/(_desc.page_size);918return true;919}920921/**922* @details Read data from flash chip.923*924* @param[in] offset address offset from where to start the read925* @param[out] data data to be read from the device926* @param[in] size size of the data to be read927* @return The operation status.928* @retval false if the operation failed.929* @retval true if the operation succeeded.930*931*/932bool AP_FlashIface_JEDEC::read(uint32_t offset, uint8_t* data, uint32_t size)933{934if ((offset + size) > _desc.flash_size) {935// reading more than what exists936return false;937}938939wait_ready();940941uint32_t read_ptr, read_size;942for (read_ptr = offset; read_ptr < (offset+size); read_ptr+=MAX_READ_SIZE) {943read_size = MIN((offset+size) - read_ptr, MAX_READ_SIZE);944AP_HAL::WSPIDevice::CommandHeader cmd;945cmd.cmd = _desc.fast_read_ins;946cmd.addr = read_ptr;947cmd.alt = 0;948cmd.cfg = AP_HAL::WSPI::CFG_CMD_MODE_ONE_LINE |949AP_HAL::WSPI::CFG_ADDR_SIZE_24 |950AP_HAL::WSPI::CFG_ALT_SIZE_8;951if (_wide_spi_mode == WSPIMode::QuadSPI) {952cmd.cfg |= (AP_HAL::WSPI::CFG_ADDR_MODE_FOUR_LINES |953AP_HAL::WSPI::CFG_DATA_MODE_FOUR_LINES |954AP_HAL::WSPI::CFG_ALT_MODE_FOUR_LINES);955#if HAL_USE_OCTOSPI956} else if (_wide_spi_mode == WSPIMode::OctoSPI) {957cmd.cfg |= (AP_HAL::WSPI::CFG_ADDR_MODE_EIGHT_LINES |958AP_HAL::WSPI::CFG_DATA_MODE_EIGHT_LINES |959AP_HAL::WSPI::CFG_ALT_MODE_EIGHT_LINES);960#endif961}962963if (_desc.fast_read_mode_clocks == 1){964cmd.dummy = _desc.fast_read_dummy_cycles - 1;965} else {966cmd.dummy = _desc.fast_read_dummy_cycles;967}968_dev->set_cmd_header(cmd);969if (!_dev->transfer(nullptr, 0, &data[read_ptr-offset], read_size)) { // Command only970Debug("Failed to read flash");971return false;972}973}974return true;975}976977/**978* @details Check if the device is busy.979*980* @return device busy with last op.981*982* @retval false if the device is ready.983* @retval true if the device is busy.984*985*/986bool AP_FlashIface_JEDEC::is_device_busy()987{988// wait for peripheral to become free989while (_dev->is_busy()) {}990991uint8_t status;992read_reg(_desc.status_read_ins, status);993994if (_desc.legacy_status_polling) {995return (status & 0x1);996} else {997return !(status & 1<<7);998}999}10001001// wait for the chip to be ready for the next instruction1002void AP_FlashIface_JEDEC::wait_ready()1003{1004while (is_device_busy()) {1005}1006}10071008/**1009* @details Starts execution in place mode1010*1011* @return if successfully entered XIP mode.1012*1013* @retval false the device failed to enter XIP mode.1014* @retval true the device has entered XIP mode.1015*1016*/1017bool AP_FlashIface_JEDEC::start_xip_mode(void** addr)1018{1019wait_ready();10201021if (!_desc.is_xip_supported) {1022Debug("XIP mode unsupported on this chip");1023return false;1024}10251026bool success = false;10271028switch(_desc.entry_method) {1029case AP_FlashIface_JEDEC::XIP_ENTRY_METHOD_1:1030{1031// Set WSPI module for XIP mode1032AP_HAL::WSPIDevice::CommandHeader cmd;1033cmd.cmd = _desc.fast_read_ins; // generally 0xEB for 1-4-4 access1034cmd.alt = 0xF0; // add M0-7 bits in alt to make up 32-bit address phase, sec 8.2.11 W25Q64JV reference1035cmd.cfg = AP_HAL::WSPI::CFG_ADDR_SIZE_24 |1036AP_HAL::WSPI::CFG_CMD_MODE_ONE_LINE |1037AP_HAL::WSPI::CFG_ALT_SIZE_8;1038if (_wide_spi_mode == WSPIMode::QuadSPI) {1039cmd.cfg |= (AP_HAL::WSPI::CFG_ADDR_MODE_FOUR_LINES |1040AP_HAL::WSPI::CFG_DATA_MODE_FOUR_LINES |1041AP_HAL::WSPI::CFG_ALT_MODE_FOUR_LINES);1042#if HAL_USE_OCTOSPI1043} else if (_wide_spi_mode == WSPIMode::OctoSPI) {1044cmd.cfg |= (AP_HAL::WSPI::CFG_ADDR_MODE_EIGHT_LINES |1045AP_HAL::WSPI::CFG_DATA_MODE_EIGHT_LINES |1046AP_HAL::WSPI::CFG_ALT_MODE_EIGHT_LINES);1047#endif1048}1049cmd.addr = 0;1050cmd.dummy = _desc.fast_read_dummy_cycles;1051_dev->set_cmd_header(cmd);1052success = _dev->enter_xip_mode(addr);1053break;1054}1055case AP_FlashIface_JEDEC::XIP_ENTRY_METHOD_2:1056{1057// set configuration register to start 0-4-4 mode1058write_enable();1059wait_ready();10601061if (!modify_reg(0x85, 0x81, 1<<3, 0)) {1062Debug("Failed to configure chip for XIP");1063write_disable();1064return false;1065}1066// Set WSPI module for XIP mode1067AP_HAL::WSPIDevice::CommandHeader cmd;1068cmd.cmd = _desc.fast_read_ins;1069cmd.alt = 0;1070cmd.cfg = AP_HAL::WSPI::CFG_ADDR_SIZE_24 |1071AP_HAL::WSPI::CFG_CMD_MODE_ONE_LINE |1072AP_HAL::WSPI::CFG_ALT_SIZE_8 |1073AP_HAL::WSPI::CFG_SIOO;1074if (_wide_spi_mode == WSPIMode::QuadSPI) {1075cmd.cfg |= (AP_HAL::WSPI::CFG_ADDR_MODE_FOUR_LINES |1076AP_HAL::WSPI::CFG_DATA_MODE_FOUR_LINES |1077AP_HAL::WSPI::CFG_ALT_MODE_FOUR_LINES);1078#if HAL_USE_OCTOSPI1079} else if (_wide_spi_mode == WSPIMode::OctoSPI) {1080cmd.cfg |= (AP_HAL::WSPI::CFG_ADDR_MODE_EIGHT_LINES |1081AP_HAL::WSPI::CFG_DATA_MODE_EIGHT_LINES |1082AP_HAL::WSPI::CFG_ALT_MODE_EIGHT_LINES);1083#endif1084}1085cmd.addr = 0;1086// correct dummy bytes because of addition of alt bytes1087cmd.dummy = _desc.fast_read_dummy_cycles - 1;1088_dev->set_cmd_header(cmd);1089success = _dev->enter_xip_mode(addr);1090break;1091}1092default:1093{1094Debug("Unsupported XIP Entry Method");1095return false;1096}1097}1098// make sure that the flash is ready once we enter XIP1099DELAY_MICROS(100);1100return success;1101}11021103bool AP_FlashIface_JEDEC::stop_xip_mode()1104{1105return _dev->exit_xip_mode();1106}110711081109