diff --git a/ports/atmel-samd/Makefile b/ports/atmel-samd/Makefile index 041f1d1ad7..a5d1fd7c0e 100644 --- a/ports/atmel-samd/Makefile +++ b/ports/atmel-samd/Makefile @@ -252,7 +252,10 @@ ifeq ($(INTERNAL_FLASH_FILESYSTEM),1) SRC_C += internal_flash.c endif ifeq ($(SPI_FLASH_FILESYSTEM),1) -SRC_C += spi_flash.c +SRC_C += external_flash/external_flash.c external_flash/spi_flash.c +endif +ifeq ($(QSPI_FLASH_FILESYSTEM),1) +SRC_C += external_flash/external_flash.c external_flash/qspi_flash.c endif SRC_COMMON_HAL = \ diff --git a/ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.h b/ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.h index fdb96c06c3..cc09d7f790 100644 --- a/ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.h +++ b/ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.h @@ -38,7 +38,7 @@ #define SPEAKER_ENABLE_PIN (&pin_PA30) -#include "spi_flash.h" +#include "external_flash/external_flash.h" // If you change this, then make sure to update the linker scripts as well to // make sure you don't overwrite code. @@ -47,8 +47,8 @@ #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - CIRCUITPY_INTERNAL_NVM_SIZE) -#include "flash_S25FL216K.h" -#include "flash_GD25Q16C.h" +#include "external_flash/devices/S25FL216K.h" +#include "external_flash/devices/GD25Q16C.h" #define CALIBRATE_CRYSTALLESS 1 diff --git a/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.h b/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.h index 448c6c52bf..378b0891ad 100644 --- a/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.h +++ b/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.h @@ -31,7 +31,7 @@ #define MICROPY_PORT_B ( 0 ) #define MICROPY_PORT_C ( 0 ) -#include "spi_flash.h" +#include "external_flash/external_flash.h" // If you change this, then make sure to update the linker scripts as well to // make sure you don't overwrite code. @@ -40,5 +40,5 @@ #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - CIRCUITPY_INTERNAL_NVM_SIZE) -#include "flash_S25FL216K.h" -#include "flash_GD25Q16C.h" +#include "external_flash/devices/S25FL216K.h" +#include "external_flash/devices/GD25Q16C.h" diff --git a/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.h b/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.h index 02ce5e1cdc..b5ed682962 100644 --- a/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.h +++ b/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.h @@ -33,7 +33,7 @@ #define MICROPY_PORT_B ( 0 ) #define MICROPY_PORT_C ( 0 ) -#include "spi_flash.h" +#include "external_flash/external_flash.h" // If you change this, then make sure to update the linker scripts as well to // make sure you don't overwrite code. @@ -42,4 +42,4 @@ #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - CIRCUITPY_INTERNAL_NVM_SIZE) -#include "flash_S25FL064L.h" +#include "external_flash/devices/S25FL064L.h" diff --git a/ports/atmel-samd/boards/itsybitsy_m0/mpconfigboard.h b/ports/atmel-samd/boards/itsybitsy_m0/mpconfigboard.h index f41059ee4f..15a9922211 100644 --- a/ports/atmel-samd/boards/itsybitsy_m0/mpconfigboard.h +++ b/ports/atmel-samd/boards/itsybitsy_m0/mpconfigboard.h @@ -33,7 +33,7 @@ #define MICROPY_PORT_B (PORT_PB22 | PORT_PB23 | PORT_PB03 ) #define MICROPY_PORT_C (0) -#include "spi_flash.h" +#include "external_flash/external_flash.h" // If you change this, then make sure to update the linker scripts as well to // make sure you don't overwrite code. @@ -41,6 +41,6 @@ #define CIRCUITPY_INTERNAL_NVM_SIZE 0 #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - CIRCUITPY_INTERNAL_NVM_SIZE) -//#include "flash_S25FL216K.h" -#include "flash_W25Q80DV.h" -//#include "flash_GD25Q16C.h" +//#include "external_flash/devices/S25FL216K.h" +#include "external_flash/devices/W25Q80DV.h" +//#include "external_flash/devices/GD25Q16C.h" diff --git a/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.h b/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.h index 023beb6883..70e830620d 100644 --- a/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.h +++ b/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.h @@ -34,7 +34,7 @@ #define MICROPY_PORT_B (PORT_PB03 | PORT_PB22 | PORT_PB23) #define MICROPY_PORT_C (0) -#include "spi_flash.h" +#include "external_flash/external_flash.h" // If you change this, then make sure to update the linker scripts as well to // make sure you don't overwrite code. @@ -43,5 +43,5 @@ #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - CIRCUITPY_INTERNAL_NVM_SIZE) -#include "flash_S25FL216K.h" -#include "flash_GD25Q16C.h" +#include "external_flash/devices/S25FL216K.h" +#include "external_flash/devices/GD25Q16C.h" diff --git a/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.h b/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.h index 1af18ecbc2..b8f14e6253 100644 --- a/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.h +++ b/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.h @@ -33,14 +33,14 @@ #define SPI_FLASH_DIPO 3 // same as MISO pad // These are pins not to reset. -#define MICROPY_PORT_A (PORT_PA27) -#define MICROPY_PORT_B (PORT_PB06 | PORT_PB08 | PORT_PB09 | PORT_PB10 | PORT_PB11 | PORT_PB17) +#define MICROPY_PORT_A (PORT_PA08 | PORT_PA09 | PORT_PA10 | PORT_PA11 | PORT_PA27) +#define MICROPY_PORT_B (PORT_PB10 | PORT_PB11 | PORT_PB17) #define MICROPY_PORT_C (0) #define MICROPY_PORT_D (0) #define AUTORESET_DELAY_MS 500 -#include "spi_flash.h" +#include "external_flash/external_flash.h" // If you change this, then make sure to update the linker scripts as well to // make sure you don't overwrite code @@ -49,5 +49,5 @@ #define BOARD_FLASH_SIZE (FLASH_SIZE - 0x4000 - CIRCUITPY_INTERNAL_NVM_SIZE) -#include "flash_S25FL216K.h" -#include "flash_GD25Q16C.h" +#include "external_flash/devices/S25FL216K.h" +#include "external_flash/devices/GD25Q16C.h" diff --git a/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.mk b/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.mk index 0f96532396..1d59ed9664 100644 --- a/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.mk @@ -4,7 +4,7 @@ USB_PID = 0x8021 USB_PRODUCT = "Metro M4 Express" USB_MANUFACTURER = "Adafruit Industries LLC" -SPI_FLASH_FILESYSTEM = 1 +QSPI_FLASH_FILESYSTEM = 1 CHIP_VARIANT = SAMD51J19A CHIP_FAMILY = samd51 diff --git a/ports/atmel-samd/boards/trinket_m0_haxpress/mpconfigboard.h b/ports/atmel-samd/boards/trinket_m0_haxpress/mpconfigboard.h index 2a5792a63b..62bca9a413 100644 --- a/ports/atmel-samd/boards/trinket_m0_haxpress/mpconfigboard.h +++ b/ports/atmel-samd/boards/trinket_m0_haxpress/mpconfigboard.h @@ -40,7 +40,7 @@ #define CALIBRATE_CRYSTALLESS 1 -#include "spi_flash.h" +#include "external_flash/external_flash.h" // If you change this, then make sure to update the linker scripts as well to // make sure you don't overwrite code. @@ -48,5 +48,4 @@ #define CIRCUITPY_INTERNAL_NVM_SIZE 0 #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - CIRCUITPY_INTERNAL_NVM_SIZE) -#include "flash_W25Q32BV.h" - +#include "external_flash/devices/W25Q32BV.h" diff --git a/ports/atmel-samd/boards/ugame10/mpconfigboard.h b/ports/atmel-samd/boards/ugame10/mpconfigboard.h index 16a792f421..d0d9296a40 100644 --- a/ports/atmel-samd/boards/ugame10/mpconfigboard.h +++ b/ports/atmel-samd/boards/ugame10/mpconfigboard.h @@ -33,7 +33,7 @@ #define CALIBRATE_CRYSTALLESS 1 -#include "spi_flash.h" +#include "external_flash/external_flash.h" // If you change this, then make sure to update the linker scripts as well to // make sure you don't overwrite code. @@ -41,5 +41,5 @@ #define CIRCUITPY_INTERNAL_NVM_SIZE 0 #define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - CIRCUITPY_INTERNAL_NVM_SIZE) -//#include "flash_W25Q32BV.h" -#include "flash_S25FL216K.h" +//#include "external_flash/devices/W25Q32BV.h" +#include "external_flash/devices/S25FL216K.h" diff --git a/ports/atmel-samd/external_flash/common_commands.h b/ports/atmel-samd/external_flash/common_commands.h new file mode 100644 index 0000000000..fc05e70de7 --- /dev/null +++ b/ports/atmel-samd/external_flash/common_commands.h @@ -0,0 +1,41 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_COMMON_COMMANDS_H +#define MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_COMMON_COMMANDS_H + +#define CMD_READ_JEDEC_ID 0x9f +#define CMD_READ_DATA 0x03 +#define CMD_SECTOR_ERASE 0x20 +// #define CMD_SECTOR_ERASE CMD_READ_JEDEC_ID +#define CMD_DISABLE_WRITE 0x04 +#define CMD_ENABLE_WRITE 0x06 +#define CMD_PAGE_PROGRAM 0x02 +// #define CMD_PAGE_PROGRAM CMD_READ_JEDEC_ID +#define CMD_READ_STATUS 0x05 +#define CMD_WRITE_STATUS_BYTE1 0x01 +#define CMD_QUAD_READ 0x6b + +#endif // MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_COMMON_COMMANDS_H diff --git a/ports/atmel-samd/boards/flash_AT25DF081A.h b/ports/atmel-samd/external_flash/devices/AT25DF081A.h similarity index 100% rename from ports/atmel-samd/boards/flash_AT25DF081A.h rename to ports/atmel-samd/external_flash/devices/AT25DF081A.h diff --git a/ports/atmel-samd/boards/flash_GD25Q16C.h b/ports/atmel-samd/external_flash/devices/GD25Q16C.h similarity index 100% rename from ports/atmel-samd/boards/flash_GD25Q16C.h rename to ports/atmel-samd/external_flash/devices/GD25Q16C.h diff --git a/ports/atmel-samd/boards/flash_S25FL064L.h b/ports/atmel-samd/external_flash/devices/S25FL064L.h similarity index 100% rename from ports/atmel-samd/boards/flash_S25FL064L.h rename to ports/atmel-samd/external_flash/devices/S25FL064L.h diff --git a/ports/atmel-samd/external_flash/devices/S25FL116K.h b/ports/atmel-samd/external_flash/devices/S25FL116K.h new file mode 100644 index 0000000000..4928bd04e2 --- /dev/null +++ b/ports/atmel-samd/external_flash/devices/S25FL116K.h @@ -0,0 +1,54 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_ATMEL_SAMD_BOARD_FLASH_S25FL116K_H +#define MICROPY_INCLUDED_ATMEL_SAMD_BOARD_FLASH_S25FL116K_H + +// Settings for the Cypress (was Spansion) S25FL116K 2MiB SPI flash. +// Datasheet: http://www.cypress.com/file/196886/download + +// The total flash size in bytes. +#define SPI_FLASH_TOTAL_SIZE (1 << 21) // 2 MiB + +// The size of the smallest erase unit thats erased with command 0x20. +#define SPI_FLASH_ERASE_SIZE (1 << 12) // 4 KiB + +// The size of a page that is programmed with page program command 0x02. +#define SPI_FLASH_PAGE_SIZE (256) // 256 bytes + +// These are the first three response bytes to the JEDEC ID command 0x9f that is +// used to confirm we're talking to the flash we expect. +#ifndef SPI_FLASH_JEDEC_MANUFACTURER +#define SPI_FLASH_JEDEC_MANUFACTURER 0x01 +#define SPI_FLASH_SECTOR_PROTECTION false +#else +#define SPI_FLASH_JEDEC_MANUFACTURER_2 0x01 +#define SPI_FLASH_SECTOR_PROTECTION_2 false +#endif +#define SPI_FLASH_JEDEC_MEMORY_TYPE 0x40 +#define SPI_FLASH_JEDEC_CAPACITY 0x15 + +#endif // MICROPY_INCLUDED_ATMEL_SAMD_BOARD_FLASH_S25FL216K_H diff --git a/ports/atmel-samd/boards/flash_S25FL216K.h b/ports/atmel-samd/external_flash/devices/S25FL216K.h similarity index 100% rename from ports/atmel-samd/boards/flash_S25FL216K.h rename to ports/atmel-samd/external_flash/devices/S25FL216K.h diff --git a/ports/atmel-samd/boards/flash_W25Q32BV.h b/ports/atmel-samd/external_flash/devices/W25Q32BV.h similarity index 100% rename from ports/atmel-samd/boards/flash_W25Q32BV.h rename to ports/atmel-samd/external_flash/devices/W25Q32BV.h diff --git a/ports/atmel-samd/boards/flash_W25Q80DV.h b/ports/atmel-samd/external_flash/devices/W25Q80DV.h similarity index 100% rename from ports/atmel-samd/boards/flash_W25Q80DV.h rename to ports/atmel-samd/external_flash/devices/W25Q80DV.h diff --git a/ports/atmel-samd/spi_flash.c b/ports/atmel-samd/external_flash/external_flash.c similarity index 66% rename from ports/atmel-samd/spi_flash.c rename to ports/atmel-samd/external_flash/external_flash.c index 55a00dcba3..d3a2b4bae6 100644 --- a/ports/atmel-samd/spi_flash.c +++ b/ports/atmel-samd/external_flash/external_flash.c @@ -23,11 +23,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "spi_flash.h" +#include "external_flash.h" #include #include +#include "external_flash/spi_flash_api.h" +#include "external_flash/common_commands.h" #include "extmod/vfs.h" #include "extmod/vfs_fat.h" #include "py/misc.h" @@ -46,17 +48,6 @@ #define NO_SECTOR_LOADED 0xFFFFFFFF -#define CMD_READ_JEDEC_ID 0x9f -#define CMD_READ_DATA 0x03 -#define CMD_SECTOR_ERASE 0x20 -// #define CMD_SECTOR_ERASE CMD_READ_JEDEC_ID -#define CMD_DISABLE_WRITE 0x04 -#define CMD_ENABLE_WRITE 0x06 -#define CMD_PAGE_PROGRAM 0x02 -// #define CMD_PAGE_PROGRAM CMD_READ_JEDEC_ID -#define CMD_READ_STATUS 0x05 -#define CMD_WRITE_STATUS_BYTE1 0x01 - static bool spi_flash_is_initialised = false; struct spi_m_sync_descriptor spi_flash_desc; @@ -71,46 +62,20 @@ static uint32_t dirty_mask; // Address of the scratch flash sector. #define SCRATCH_SECTOR (SPI_FLASH_TOTAL_SIZE - SPI_FLASH_ERASE_SIZE) -// Enable the flash over SPI. -static void flash_enable(void) { - gpio_set_pin_level(SPI_FLASH_CS_PIN, false); -} - -// Disable the flash over SPI. -static void flash_disable(void) { - gpio_set_pin_level(SPI_FLASH_CS_PIN, true); -} - // Wait until both the write enable and write in progress bits have cleared. static bool wait_for_flash_ready(void) { - uint8_t read_status_request[2] = {CMD_READ_STATUS, 0x00}; - uint8_t read_status_response[2] = {0x00, 0x00}; - struct spi_xfer read_status_xfer = {read_status_request, read_status_response, 2}; - int32_t status; + uint8_t read_status_response[1] = {0x00}; + bool ok = true; // Both the write enable and write in progress bits should be low. do { - flash_enable(); - status = spi_m_sync_transfer(&spi_flash_desc, &read_status_xfer); - flash_disable(); - } while (status >= 0 && (read_status_response[1] & 0x3) != 0); - return status >= 0; // status is number of chars read or a negative error code. + ok = spi_flash_read_command(CMD_READ_STATUS, read_status_response, 1); + } while (ok && (read_status_response[0] & 0x3) != 0); + return ok; } // Turn on the write enable bit so we can program and erase the flash. static bool write_enable(void) { - flash_enable(); - uint8_t enable_write_request[1] = {CMD_ENABLE_WRITE}; - struct spi_xfer enable_write_xfer = {enable_write_request, 0, 1}; - int32_t status = spi_m_sync_transfer(&spi_flash_desc, &enable_write_xfer); - flash_disable(); - return status >= 0; // status is number of chars read or a negative error code. -} - -// Pack the low 24 bits of the address into a uint8_t array. -static void address_to_bytes(uint32_t address, uint8_t* bytes) { - bytes[0] = (address >> 16) & 0xff; - bytes[1] = (address >> 8) & 0xff; - bytes[2] = address & 0xff; + return spi_flash_command(CMD_ENABLE_WRITE); } // Read data_length's worth of bytes starting at address into data. @@ -121,19 +86,7 @@ static bool read_flash(uint32_t address, uint8_t* data, uint32_t data_length) { if (!wait_for_flash_ready()) { return false; } - // We can read as much as we want sequentially. - uint8_t read_data_request[4] = {CMD_READ_DATA, 0x00, 0x00, 0x00}; - struct spi_xfer read_data_xfer = {read_data_request, 0, 4}; - // Write the SPI flash read address into the bytes following the command byte. - address_to_bytes(address, read_data_request + 1); - flash_enable(); - int32_t status = spi_m_sync_transfer(&spi_flash_desc, &read_data_xfer); - struct spi_xfer read_data_buffer_xfer = {0, data, data_length}; - if (status >= 0) { - status = spi_m_sync_transfer(&spi_flash_desc, &read_data_buffer_xfer); - } - flash_disable(); - return status >= 0; + return spi_flash_read_data(address, data, data_length); } // Writes data_length's worth of bytes starting at address from data. Assumes @@ -162,29 +115,9 @@ static bool write_flash(uint32_t address, const uint8_t* data, uint32_t data_len if (!wait_for_flash_ready() || !write_enable()) { return false; } - int32_t status; - #ifdef SPI_FLASH_SECTOR_PROTECTION - // Print out the protection status. - // uint8_t protect_check[5] = {0x3C, 0x00, 0x00, 0x00, 0x00}; - // address_to_bytes(address + bytes_written, protect_check + 1); - // flash_enable(); - // status = spi_write_buffer_wait(&spi_flash_desc, protect_check, 5); - // flash_disable(); - #endif - - flash_enable(); - uint8_t page_program_request[4] = {CMD_PAGE_PROGRAM, 0x00, 0x00, 0x00}; - // Write the SPI flash write address into the bytes following the command byte. - address_to_bytes(address + bytes_written, page_program_request + 1); - struct spi_xfer page_program_xfer = {page_program_request, 0, 4}; - status = spi_m_sync_transfer(&spi_flash_desc, &page_program_xfer); - if (status >= 0) { - struct spi_xfer write_data_buffer_xfer = {(uint8_t*) data + bytes_written, 0, SPI_FLASH_PAGE_SIZE}; - status = spi_m_sync_transfer(&spi_flash_desc, &write_data_buffer_xfer); - } - flash_disable(); - if (status < 0) { + if (!spi_flash_write_data(address + bytes_written, (uint8_t*) data + bytes_written, + SPI_FLASH_PAGE_SIZE)) { return false; } } @@ -228,13 +161,8 @@ static bool erase_sector(uint32_t sector_address) { return false; } - uint8_t erase_request[4] = {CMD_SECTOR_ERASE, 0x00, 0x00, 0x00}; - address_to_bytes(sector_address, erase_request + 1); - struct spi_xfer erase_xfer = {erase_request, 0, 4}; - flash_enable(); - int32_t status = spi_m_sync_transfer(&spi_flash_desc, &erase_xfer); - flash_disable(); - return status >= 0; + spi_flash_sector_command(CMD_SECTOR_ERASE, sector_address); + return true; } // Sector is really 24 bits. @@ -252,45 +180,12 @@ static bool copy_block(uint32_t src_address, uint32_t dest_address) { return true; } -void spi_flash_init(void) { +void external_flash_init(void) { if (spi_flash_is_initialised) { return; } - samd_peripherals_sercom_clock_init(SPI_FLASH_SERCOM, SPI_FLASH_SERCOM_INDEX); - - // Set up with defaults, then change. - spi_m_sync_init(&spi_flash_desc, SPI_FLASH_SERCOM); - - hri_sercomspi_write_CTRLA_DOPO_bf(SPI_FLASH_SERCOM, SPI_FLASH_DOPO); - hri_sercomspi_write_CTRLA_DIPO_bf(SPI_FLASH_SERCOM, SPI_FLASH_DIPO); - - gpio_set_pin_direction(SPI_FLASH_SCK_PIN, GPIO_DIRECTION_OUT); - gpio_set_pin_pull_mode(SPI_FLASH_SCK_PIN, GPIO_PULL_OFF); - gpio_set_pin_function(SPI_FLASH_SCK_PIN, SPI_FLASH_SCK_PIN_FUNCTION); - - gpio_set_pin_direction(SPI_FLASH_MOSI_PIN, GPIO_DIRECTION_OUT); - gpio_set_pin_pull_mode(SPI_FLASH_MOSI_PIN, GPIO_PULL_OFF); - gpio_set_pin_function(SPI_FLASH_MOSI_PIN, SPI_FLASH_MOSI_PIN_FUNCTION); - - gpio_set_pin_direction(SPI_FLASH_MISO_PIN, GPIO_DIRECTION_IN); - gpio_set_pin_pull_mode(SPI_FLASH_MISO_PIN, GPIO_PULL_OFF); - gpio_set_pin_function(SPI_FLASH_MISO_PIN, SPI_FLASH_MISO_PIN_FUNCTION); - - hri_sercomspi_write_CTRLA_DOPO_bf(SPI_FLASH_SERCOM, SPI_FLASH_DOPO); - hri_sercomspi_write_CTRLA_DIPO_bf(SPI_FLASH_SERCOM, SPI_FLASH_DIPO); - - spi_m_sync_set_baudrate(&spi_flash_desc, samd_peripherals_spi_baudrate_to_baud_reg_value(SPI_FLASH_BAUDRATE)); - - gpio_set_pin_direction(SPI_FLASH_CS_PIN, GPIO_DIRECTION_OUT); - // There's already a pull-up on the board. - gpio_set_pin_pull_mode(SPI_FLASH_CS_PIN, GPIO_PULL_OFF); - gpio_set_pin_function(SPI_FLASH_CS_PIN, GPIO_PIN_FUNCTION_OFF); - - // Set CS high (disabled). - flash_disable(); - - spi_m_sync_enable(&spi_flash_desc); + spi_flash_init(); // Activity LED for flash writes. #ifdef MICROPY_HW_LED_MSC @@ -300,20 +195,17 @@ void spi_flash_init(void) { gpio_set_pin_level(MICROPY_HW_LED_MSC, false); #endif - uint8_t jedec_id_request[4] = {CMD_READ_JEDEC_ID, 0x00, 0x00, 0x00}; - uint8_t jedec_id_response[4] = {0x00, 0x00, 0x00, 0x00}; - struct spi_xfer jedec_id_xfer = { jedec_id_request, jedec_id_response, 4 }; - flash_enable(); - spi_m_sync_transfer(&spi_flash_desc, &jedec_id_xfer); - flash_disable(); - uint8_t manufacturer = jedec_id_response[1]; - if ((jedec_id_response[1] == SPI_FLASH_JEDEC_MANUFACTURER + uint8_t jedec_id_response[3] = {0x00, 0x00, 0x00}; + spi_flash_read_command(CMD_READ_JEDEC_ID, jedec_id_response, 3); + + uint8_t manufacturer = jedec_id_response[0]; + if ((jedec_id_response[0] == SPI_FLASH_JEDEC_MANUFACTURER #ifdef SPI_FLASH_JEDEC_MANUFACTURER_2 - || jedec_id_response[1] == SPI_FLASH_JEDEC_MANUFACTURER_2 + || jedec_id_response[0] == SPI_FLASH_JEDEC_MANUFACTURER_2 #endif ) && - jedec_id_response[2] == SPI_FLASH_JEDEC_MEMORY_TYPE && - jedec_id_response[3] == SPI_FLASH_JEDEC_CAPACITY) { + jedec_id_response[1] == SPI_FLASH_JEDEC_MEMORY_TYPE && + jedec_id_response[2] == SPI_FLASH_JEDEC_CAPACITY) { spi_flash_is_initialised = true; } else { // Unknown flash chip! @@ -329,19 +221,12 @@ void spi_flash_init(void) { write_enable(); // Turn off sector protection - uint8_t disable_protect_request[2] = {CMD_WRITE_STATUS_BYTE1, 0x00}; - struct spi_xfer disable_protect_xfer = { disable_protect_request, 0, 4 }; - flash_enable(); - spi_m_sync_transfer(&spi_flash_desc, &disable_protect_xfer); - flash_disable(); + uint8_t data[1] = {0x00}; + spi_flash_write_command(CMD_WRITE_STATUS_BYTE1, data, 1); } // Turn off writes in case this is a microcontroller only reset. - uint8_t disable_write_request[1] = {CMD_DISABLE_WRITE}; - struct spi_xfer disable_write_xfer = { disable_write_request, 0, 1 }; - flash_enable(); - spi_m_sync_transfer(&spi_flash_desc, &disable_write_xfer); - flash_disable(); + spi_flash_command(CMD_DISABLE_WRITE); wait_for_flash_ready(); @@ -353,12 +238,12 @@ void spi_flash_init(void) { } // The size of each individual block. -uint32_t spi_flash_get_block_size(void) { +uint32_t external_flash_get_block_size(void) { return FILESYSTEM_BLOCK_SIZE; } // The total number of available blocks. -uint32_t spi_flash_get_block_count(void) { +uint32_t external_flash_get_block_count(void) { // We subtract one erase sector size because we may use it as a staging area // for writes. return SPI_FLASH_PART1_START_BLOCK + (SPI_FLASH_TOTAL_SIZE - SPI_FLASH_ERASE_SIZE) / FILESYSTEM_BLOCK_SIZE; @@ -510,12 +395,12 @@ static void spi_flash_flush_keep_cache(bool keep_cache) { // External flash function used. If called externally we assume we won't need // the cache after. -void spi_flash_flush(void) { +void external_flash_flush(void) { spi_flash_flush_keep_cache(false); } void flash_flush(void) { - spi_flash_flush(); + external_flash_flush(); } // Builds a partition entry for the MBR. @@ -557,7 +442,7 @@ static void build_partition(uint8_t *buf, int boot, int type, } static int32_t convert_block_to_flash_addr(uint32_t block) { - if (SPI_FLASH_PART1_START_BLOCK <= block && block < spi_flash_get_block_count()) { + if (SPI_FLASH_PART1_START_BLOCK <= block && block < external_flash_get_block_count()) { // a block in partition 1 block -= SPI_FLASH_PART1_START_BLOCK; return block * FILESYSTEM_BLOCK_SIZE; @@ -566,7 +451,7 @@ static int32_t convert_block_to_flash_addr(uint32_t block) { return -1; } -bool spi_flash_read_block(uint8_t *dest, uint32_t block) { +bool external_flash_read_block(uint8_t *dest, uint32_t block) { if (block == 0) { // Fake the MBR so we can decide on our own partition table for (int i = 0; i < 446; i++) { @@ -575,7 +460,7 @@ bool spi_flash_read_block(uint8_t *dest, uint32_t block) { build_partition(dest + 446, 0, 0x01 /* FAT12 */, SPI_FLASH_PART1_START_BLOCK, - spi_flash_get_block_count() - SPI_FLASH_PART1_START_BLOCK); + external_flash_get_block_count() - SPI_FLASH_PART1_START_BLOCK); build_partition(dest + 462, 0, 0, 0, 0); build_partition(dest + 478, 0, 0, 0, 0); build_partition(dest + 494, 0, 0, 0, 0); @@ -618,7 +503,7 @@ bool spi_flash_read_block(uint8_t *dest, uint32_t block) { } } -bool spi_flash_write_block(const uint8_t *data, uint32_t block) { +bool external_flash_write_block(const uint8_t *data, uint32_t block) { if (block < SPI_FLASH_PART1_START_BLOCK) { // Fake writing below the flash partition. return true; @@ -670,18 +555,18 @@ bool spi_flash_write_block(const uint8_t *data, uint32_t block) { } } -mp_uint_t spi_flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks) { +mp_uint_t external_flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks) { for (size_t i = 0; i < num_blocks; i++) { - if (!spi_flash_read_block(dest + i * FILESYSTEM_BLOCK_SIZE, block_num + i)) { + if (!external_flash_read_block(dest + i * FILESYSTEM_BLOCK_SIZE, block_num + i)) { return 1; // error } } return 0; // success } -mp_uint_t spi_flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks) { +mp_uint_t external_flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks) { for (size_t i = 0; i < num_blocks; i++) { - if (!spi_flash_write_block(src + i * FILESYSTEM_BLOCK_SIZE, block_num + i)) { + if (!external_flash_write_block(src + i * FILESYSTEM_BLOCK_SIZE, block_num + i)) { return 1; // error } } @@ -694,58 +579,58 @@ mp_uint_t spi_flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_ // Expose the flash as an object with the block protocol. // there is a singleton Flash object -STATIC const mp_obj_base_t spi_flash_obj = {&spi_flash_type}; +STATIC const mp_obj_base_t external_flash_obj = {&external_flash_type}; -STATIC mp_obj_t spi_flash_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { +STATIC mp_obj_t external_flash_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { // check arguments mp_arg_check_num(n_args, n_kw, 0, 0, false); // return singleton object - return (mp_obj_t)&spi_flash_obj; + return (mp_obj_t)&external_flash_obj; } -STATIC mp_obj_t spi_flash_obj_readblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) { +STATIC mp_obj_t external_flash_obj_readblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_WRITE); - mp_uint_t ret = spi_flash_read_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FILESYSTEM_BLOCK_SIZE); + mp_uint_t ret = external_flash_read_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FILESYSTEM_BLOCK_SIZE); return MP_OBJ_NEW_SMALL_INT(ret); } -STATIC MP_DEFINE_CONST_FUN_OBJ_3(spi_flash_obj_readblocks_obj, spi_flash_obj_readblocks); +STATIC MP_DEFINE_CONST_FUN_OBJ_3(external_flash_obj_readblocks_obj, external_flash_obj_readblocks); -STATIC mp_obj_t spi_flash_obj_writeblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) { +STATIC mp_obj_t external_flash_obj_writeblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ); - mp_uint_t ret = spi_flash_write_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FILESYSTEM_BLOCK_SIZE); + mp_uint_t ret = external_flash_write_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FILESYSTEM_BLOCK_SIZE); return MP_OBJ_NEW_SMALL_INT(ret); } -STATIC MP_DEFINE_CONST_FUN_OBJ_3(spi_flash_obj_writeblocks_obj, spi_flash_obj_writeblocks); +STATIC MP_DEFINE_CONST_FUN_OBJ_3(external_flash_obj_writeblocks_obj, external_flash_obj_writeblocks); -STATIC mp_obj_t spi_flash_obj_ioctl(mp_obj_t self, mp_obj_t cmd_in, mp_obj_t arg_in) { +STATIC mp_obj_t external_flash_obj_ioctl(mp_obj_t self, mp_obj_t cmd_in, mp_obj_t arg_in) { mp_int_t cmd = mp_obj_get_int(cmd_in); switch (cmd) { - case BP_IOCTL_INIT: spi_flash_init(); return MP_OBJ_NEW_SMALL_INT(0); - case BP_IOCTL_DEINIT: spi_flash_flush(); return MP_OBJ_NEW_SMALL_INT(0); // TODO properly - case BP_IOCTL_SYNC: spi_flash_flush(); return MP_OBJ_NEW_SMALL_INT(0); - case BP_IOCTL_SEC_COUNT: return MP_OBJ_NEW_SMALL_INT(spi_flash_get_block_count()); - case BP_IOCTL_SEC_SIZE: return MP_OBJ_NEW_SMALL_INT(spi_flash_get_block_size()); + case BP_IOCTL_INIT: external_flash_init(); return MP_OBJ_NEW_SMALL_INT(0); + case BP_IOCTL_DEINIT: external_flash_flush(); return MP_OBJ_NEW_SMALL_INT(0); // TODO properly + case BP_IOCTL_SYNC: external_flash_flush(); return MP_OBJ_NEW_SMALL_INT(0); + case BP_IOCTL_SEC_COUNT: return MP_OBJ_NEW_SMALL_INT(external_flash_get_block_count()); + case BP_IOCTL_SEC_SIZE: return MP_OBJ_NEW_SMALL_INT(external_flash_get_block_size()); default: return mp_const_none; } } -STATIC MP_DEFINE_CONST_FUN_OBJ_3(spi_flash_obj_ioctl_obj, spi_flash_obj_ioctl); +STATIC MP_DEFINE_CONST_FUN_OBJ_3(external_flash_obj_ioctl_obj, external_flash_obj_ioctl); -STATIC const mp_rom_map_elem_t spi_flash_obj_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&spi_flash_obj_readblocks_obj) }, - { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&spi_flash_obj_writeblocks_obj) }, - { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&spi_flash_obj_ioctl_obj) }, +STATIC const mp_rom_map_elem_t external_flash_obj_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&external_flash_obj_readblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&external_flash_obj_writeblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&external_flash_obj_ioctl_obj) }, }; -STATIC MP_DEFINE_CONST_DICT(spi_flash_obj_locals_dict, spi_flash_obj_locals_dict_table); +STATIC MP_DEFINE_CONST_DICT(external_flash_obj_locals_dict, external_flash_obj_locals_dict_table); -const mp_obj_type_t spi_flash_type = { +const mp_obj_type_t external_flash_type = { { &mp_type_type }, .name = MP_QSTR_SPIFlash, - .make_new = spi_flash_obj_make_new, - .locals_dict = (mp_obj_t)&spi_flash_obj_locals_dict, + .make_new = external_flash_obj_make_new, + .locals_dict = (mp_obj_t)&external_flash_obj_locals_dict, }; void flash_init_vfs(fs_user_mount_t *vfs) { @@ -753,12 +638,12 @@ void flash_init_vfs(fs_user_mount_t *vfs) { vfs->flags |= FSUSER_NATIVE | FSUSER_HAVE_IOCTL; vfs->fatfs.drv = vfs; vfs->fatfs.part = 1; // flash filesystem lives on first partition - vfs->readblocks[0] = (mp_obj_t)&spi_flash_obj_readblocks_obj; - vfs->readblocks[1] = (mp_obj_t)&spi_flash_obj; - vfs->readblocks[2] = (mp_obj_t)spi_flash_read_blocks; // native version - vfs->writeblocks[0] = (mp_obj_t)&spi_flash_obj_writeblocks_obj; - vfs->writeblocks[1] = (mp_obj_t)&spi_flash_obj; - vfs->writeblocks[2] = (mp_obj_t)spi_flash_write_blocks; // native version - vfs->u.ioctl[0] = (mp_obj_t)&spi_flash_obj_ioctl_obj; - vfs->u.ioctl[1] = (mp_obj_t)&spi_flash_obj; + vfs->readblocks[0] = (mp_obj_t)&external_flash_obj_readblocks_obj; + vfs->readblocks[1] = (mp_obj_t)&external_flash_obj; + vfs->readblocks[2] = (mp_obj_t)external_flash_read_blocks; // native version + vfs->writeblocks[0] = (mp_obj_t)&external_flash_obj_writeblocks_obj; + vfs->writeblocks[1] = (mp_obj_t)&external_flash_obj; + vfs->writeblocks[2] = (mp_obj_t)external_flash_write_blocks; // native version + vfs->u.ioctl[0] = (mp_obj_t)&external_flash_obj_ioctl_obj; + vfs->u.ioctl[1] = (mp_obj_t)&external_flash_obj; } diff --git a/ports/atmel-samd/spi_flash.h b/ports/atmel-samd/external_flash/external_flash.h similarity index 69% rename from ports/atmel-samd/spi_flash.h rename to ports/atmel-samd/external_flash/external_flash.h index e4bf8a5224..5a43b10c84 100644 --- a/ports/atmel-samd/spi_flash.h +++ b/ports/atmel-samd/external_flash/external_flash.h @@ -23,8 +23,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#ifndef MICROPY_INCLUDED_ATMEL_SAMD_SPI_FLASH_H -#define MICROPY_INCLUDED_ATMEL_SAMD_SPI_FLASH_H +#ifndef MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_H +#define MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_H #include @@ -40,21 +40,21 @@ #define SPI_FLASH_SYSTICK_MASK (0x1ff) // 512ms #define SPI_FLASH_IDLE_TICK(tick) (((tick) & SPI_FLASH_SYSTICK_MASK) == 2) -void spi_flash_init(void); -uint32_t spi_flash_get_block_size(void); -uint32_t spi_flash_get_block_count(void); -void spi_flash_irq_handler(void); -void spi_flash_flush(void); -bool spi_flash_read_block(uint8_t *dest, uint32_t block); -bool spi_flash_write_block(const uint8_t *src, uint32_t block); +void external_flash_init(void); +uint32_t external_flash_get_block_size(void); +uint32_t external_flash_get_block_count(void); +void external_flash_irq_handler(void); +void external_flash_flush(void); +bool external_flash_read_block(uint8_t *dest, uint32_t block); +bool external_flash_write_block(const uint8_t *src, uint32_t block); // these return 0 on success, non-zero on error -mp_uint_t spi_flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks); -mp_uint_t spi_flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks); +mp_uint_t external_flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks); +mp_uint_t external_flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks); -extern const struct _mp_obj_type_t spi_flash_type; +extern const struct _mp_obj_type_t external_flash_type; struct _fs_user_mount_t; void flash_init_vfs(struct _fs_user_mount_t *vfs); -#endif // MICROPY_INCLUDED_ATMEL_SAMD_SPI_FLASH_H +#endif // MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_H diff --git a/ports/atmel-samd/external_flash/qspi_flash.c b/ports/atmel-samd/external_flash/qspi_flash.c new file mode 100644 index 0000000000..d9dc3bf01e --- /dev/null +++ b/ports/atmel-samd/external_flash/qspi_flash.c @@ -0,0 +1,196 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016, 2017 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "spi_flash_api.h" + +#include +#include + +#include "external_flash/common_commands.h" + +#include "atmel_start_pins.h" +#include "hal_gpio.h" + +bool spi_flash_command(uint8_t command) { + QSPI->INSTRCTRL.bit.INSTR = command; + + QSPI->INSTRFRAME.reg = QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | + QSPI_INSTRFRAME_ADDRLEN_24BITS | + QSPI_INSTRFRAME_TFRTYPE_READ | + QSPI_INSTRFRAME_INSTREN; + + QSPI->CTRLA.reg = QSPI_CTRLA_ENABLE | QSPI_CTRLA_LASTXFER; + + while( !QSPI->INTFLAG.bit.INSTREND ); + + QSPI->INTFLAG.reg = QSPI_INTFLAG_INSTREND; + + return true; +} + +bool spi_flash_read_command(uint8_t command, uint8_t* response, uint32_t length) { + QSPI->INSTRCTRL.bit.INSTR = command; + + QSPI->INSTRFRAME.reg = QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | + QSPI_INSTRFRAME_ADDRLEN_24BITS | + QSPI_INSTRFRAME_TFRTYPE_READ | + QSPI_INSTRFRAME_INSTREN | + QSPI_INSTRFRAME_DATAEN; + + memcpy(response, (uint8_t *) QSPI_AHB, length); + + QSPI->CTRLA.reg = QSPI_CTRLA_ENABLE | QSPI_CTRLA_LASTXFER; + + while( !QSPI->INTFLAG.bit.INSTREND ); + + QSPI->INTFLAG.reg = QSPI_INTFLAG_INSTREND; + + return true; +} + +bool spi_flash_write_command(uint8_t command, uint8_t* data, uint32_t length) { + QSPI->INSTRCTRL.bit.INSTR = command; + + QSPI->INSTRFRAME.reg = QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | + QSPI_INSTRFRAME_ADDRLEN_24BITS | + QSPI_INSTRFRAME_TFRTYPE_WRITE | + QSPI_INSTRFRAME_INSTREN; + + if (data != NULL) { + QSPI->INSTRFRAME.bit.DATAEN = true; + + memcpy((uint8_t *) QSPI_AHB, data, length); + } + + QSPI->CTRLA.reg = QSPI_CTRLA_ENABLE | QSPI_CTRLA_LASTXFER; + + while( !QSPI->INTFLAG.bit.INSTREND ); + + QSPI->INTFLAG.reg = QSPI_INTFLAG_INSTREND; + + return true; +} + +bool spi_flash_sector_command(uint8_t command, uint32_t address) { + QSPI->INSTRCTRL.bit.INSTR = command; + QSPI->INSTRADDR.bit.ADDR = address; + + QSPI->INSTRFRAME.reg = QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | + QSPI_INSTRFRAME_ADDRLEN_24BITS | + QSPI_INSTRFRAME_TFRTYPE_WRITE | + QSPI_INSTRFRAME_INSTREN | + QSPI_INSTRFRAME_ADDREN; + + QSPI->CTRLA.reg = QSPI_CTRLA_ENABLE | QSPI_CTRLA_LASTXFER; + + while( !QSPI->INTFLAG.bit.INSTREND ); + + QSPI->INTFLAG.reg = QSPI_INTFLAG_INSTREND; + + return true; +} + +bool spi_flash_write_data(uint32_t address, uint8_t* data, uint32_t length) { + QSPI->INSTRCTRL.bit.INSTR = CMD_PAGE_PROGRAM; + uint32_t mode = QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI; + + QSPI->INSTRFRAME.reg = mode | + QSPI_INSTRFRAME_ADDRLEN_24BITS | + QSPI_INSTRFRAME_TFRTYPE_WRITEMEMORY | + QSPI_INSTRFRAME_INSTREN | + QSPI_INSTRFRAME_ADDREN | + QSPI_INSTRFRAME_DATAEN; + + memcpy(((uint8_t *) QSPI_AHB) + address, data, length); + + QSPI->CTRLA.reg = QSPI_CTRLA_ENABLE | QSPI_CTRLA_LASTXFER; + + while( !QSPI->INTFLAG.bit.INSTREND ); + + QSPI->INTFLAG.reg = QSPI_INTFLAG_INSTREND; + + return true; +} + +bool spi_flash_read_data(uint32_t address, uint8_t* data, uint32_t length) { + QSPI->INSTRCTRL.bit.INSTR = CMD_QUAD_READ; + uint32_t mode = QSPI_INSTRFRAME_WIDTH_QUAD_OUTPUT; + + QSPI->INSTRFRAME.reg = mode | + QSPI_INSTRFRAME_ADDRLEN_24BITS | + QSPI_INSTRFRAME_TFRTYPE_READMEMORY | + QSPI_INSTRFRAME_INSTREN | + QSPI_INSTRFRAME_ADDREN | + QSPI_INSTRFRAME_DATAEN | + QSPI_INSTRFRAME_DUMMYLEN(8); + + memcpy(data, ((uint8_t *) QSPI_AHB) + address, length); + + QSPI->CTRLA.reg = QSPI_CTRLA_ENABLE | QSPI_CTRLA_LASTXFER; + + while( !QSPI->INTFLAG.bit.INSTREND ); + + QSPI->INTFLAG.reg = QSPI_INTFLAG_INSTREND; + + return true; +} + + +void spi_flash_init(void) { + MCLK->APBCMASK.bit.QSPI_ = true; + MCLK->AHBMASK.bit.QSPI_ = true; + MCLK->AHBMASK.bit.QSPI_2X_ = false; // Only true if we are doing DDR. + + QSPI->CTRLA.reg = QSPI_CTRLA_SWRST; + // We don't need to wait because we're running as fast as the CPU. + + QSPI->BAUD.bit.BAUD = 10; + QSPI->CTRLB.reg = QSPI_CTRLB_MODE_MEMORY | + QSPI_CTRLB_DATALEN_8BITS | + QSPI_CTRLB_CSMODE_LASTXFER; + + QSPI->CTRLA.bit.ENABLE = 1; + + // The QSPI is only connected to one set of pins in the SAMD51 so we can hard code it. + uint32_t pins[6] = {PIN_PA08, PIN_PA09, PIN_PA10, PIN_PA11, PIN_PB10, PIN_PB11}; + for (uint8_t i = 0; i < 6; i++) { + gpio_set_pin_direction(pins[i], GPIO_DIRECTION_IN); + gpio_set_pin_pull_mode(pins[i], GPIO_PULL_OFF); + gpio_set_pin_function(pins[i], GPIO_PIN_FUNCTION_H); + } + + // Verify that QSPI mode is enabled. + uint8_t status; + spi_flash_read_command(0x35, &status, 1); + + if ((status & 0x2) == 0) { + uint8_t full_status[3] = { 0, status | 0x2, 0x70}; + spi_flash_command(CMD_ENABLE_WRITE); + spi_flash_write_command(0x01, full_status, 3); + } + + asm("nop"); +} diff --git a/ports/atmel-samd/external_flash/spi_flash.c b/ports/atmel-samd/external_flash/spi_flash.c new file mode 100644 index 0000000000..4b63132eea --- /dev/null +++ b/ports/atmel-samd/external_flash/spi_flash.c @@ -0,0 +1,139 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016, 2017 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "spi_flash_api.h" + +#include +#include + +#include "external_flash/common_commands.h" +#include "peripherals.h" + +#include "hal_gpio.h" +#include "hal_spi_m_sync.h" + +struct spi_m_sync_descriptor spi_flash_desc; + +// Enable the flash over SPI. +static void flash_enable(void) { + gpio_set_pin_level(SPI_FLASH_CS_PIN, false); +} + +// Disable the flash over SPI. +static void flash_disable(void) { + gpio_set_pin_level(SPI_FLASH_CS_PIN, true); +} + +static bool transfer(uint8_t* command, uint32_t command_length, uint8_t* data_in, uint8_t* data_out, uint32_t data_length) { + struct spi_xfer xfer = { command, NULL, command_length }; + flash_enable(); + int32_t status = spi_m_sync_transfer(&spi_flash_desc, &xfer); + if (status >= 0 && !(data_in == NULL && data_out == NULL)) { + struct spi_xfer data_xfer = {data_in, data_out, data_length}; + status = spi_m_sync_transfer(&spi_flash_desc, &data_xfer); + } + flash_disable(); + return status >= 0; +} + +static bool transfer_command(uint8_t command, uint8_t* data_in, uint8_t* data_out, uint32_t data_length) { + return transfer(&command, 1, data_in, data_out, data_length); +} + +bool spi_flash_command(uint8_t command) { + return transfer_command(command, NULL, NULL, 0); +} + +bool spi_flash_read_command(uint8_t command, uint8_t* data, uint32_t data_length) { + return transfer_command(command, NULL, data, data_length); +} + +bool spi_flash_write_command(uint8_t command, uint8_t* data, uint32_t data_length) { + return transfer_command(command, data, NULL, data_length); +} + +// Pack the low 24 bits of the address into a uint8_t array. +static void address_to_bytes(uint32_t address, uint8_t* bytes) { + bytes[0] = (address >> 16) & 0xff; + bytes[1] = (address >> 8) & 0xff; + bytes[2] = address & 0xff; +} + +bool spi_flash_sector_command(uint8_t command, uint32_t address) { + uint8_t request[4] = {command, 0x00, 0x00, 0x00}; + address_to_bytes(address, request + 1); + return transfer(request, 4, NULL, NULL, 0); +} + +bool spi_flash_write_data(uint32_t address, uint8_t* data, uint32_t data_length) { + uint8_t request[4] = {CMD_PAGE_PROGRAM, 0x00, 0x00, 0x00}; + // Write the SPI flash write address into the bytes following the command byte. + address_to_bytes(address, request + 1); + return transfer(request, 4, data, NULL, data_length); +} + +bool spi_flash_read_data(uint32_t address, uint8_t* data, uint32_t data_length) { + uint8_t request[4] = {CMD_READ_DATA, 0x00, 0x00, 0x00}; + // Write the SPI flash write address into the bytes following the command byte. + address_to_bytes(address, request + 1); + return transfer(request, 4, NULL, data, data_length); +} + +void spi_flash_init(void) { + samd_peripherals_sercom_clock_init(SPI_FLASH_SERCOM, SPI_FLASH_SERCOM_INDEX); + + // Set up with defaults, then change. + spi_m_sync_init(&spi_flash_desc, SPI_FLASH_SERCOM); + + hri_sercomspi_write_CTRLA_DOPO_bf(SPI_FLASH_SERCOM, SPI_FLASH_DOPO); + hri_sercomspi_write_CTRLA_DIPO_bf(SPI_FLASH_SERCOM, SPI_FLASH_DIPO); + + gpio_set_pin_direction(SPI_FLASH_SCK_PIN, GPIO_DIRECTION_OUT); + gpio_set_pin_pull_mode(SPI_FLASH_SCK_PIN, GPIO_PULL_OFF); + gpio_set_pin_function(SPI_FLASH_SCK_PIN, SPI_FLASH_SCK_PIN_FUNCTION); + + gpio_set_pin_direction(SPI_FLASH_MOSI_PIN, GPIO_DIRECTION_OUT); + gpio_set_pin_pull_mode(SPI_FLASH_MOSI_PIN, GPIO_PULL_OFF); + gpio_set_pin_function(SPI_FLASH_MOSI_PIN, SPI_FLASH_MOSI_PIN_FUNCTION); + + gpio_set_pin_direction(SPI_FLASH_MISO_PIN, GPIO_DIRECTION_IN); + gpio_set_pin_pull_mode(SPI_FLASH_MISO_PIN, GPIO_PULL_OFF); + gpio_set_pin_function(SPI_FLASH_MISO_PIN, SPI_FLASH_MISO_PIN_FUNCTION); + + hri_sercomspi_write_CTRLA_DOPO_bf(SPI_FLASH_SERCOM, SPI_FLASH_DOPO); + hri_sercomspi_write_CTRLA_DIPO_bf(SPI_FLASH_SERCOM, SPI_FLASH_DIPO); + + spi_m_sync_set_baudrate(&spi_flash_desc, samd_peripherals_spi_baudrate_to_baud_reg_value(SPI_FLASH_BAUDRATE)); + + gpio_set_pin_direction(SPI_FLASH_CS_PIN, GPIO_DIRECTION_OUT); + // There's already a pull-up on the board. + gpio_set_pin_pull_mode(SPI_FLASH_CS_PIN, GPIO_PULL_OFF); + gpio_set_pin_function(SPI_FLASH_CS_PIN, GPIO_PIN_FUNCTION_OFF); + + // Set CS high (disabled). + flash_disable(); + + spi_m_sync_enable(&spi_flash_desc); +} diff --git a/ports/atmel-samd/external_flash/spi_flash_api.h b/ports/atmel-samd/external_flash/spi_flash_api.h new file mode 100644 index 0000000000..b858ff0ea0 --- /dev/null +++ b/ports/atmel-samd/external_flash/spi_flash_api.h @@ -0,0 +1,42 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_ATMEL_SAMD_SPI_FLASH_H +#define MICROPY_INCLUDED_ATMEL_SAMD_SPI_FLASH_H + +#include +#include + +// This API is implemented for both normal SPI peripherals and QSPI peripherals. + +bool spi_flash_command(uint8_t command); +bool spi_flash_read_command(uint8_t command, uint8_t* response, uint32_t length); +bool spi_flash_write_command(uint8_t command, uint8_t* data, uint32_t length); +bool spi_flash_sector_command(uint8_t command, uint32_t address); +bool spi_flash_write_data(uint32_t address, uint8_t* data, uint32_t data_length); +bool spi_flash_read_data(uint32_t address, uint8_t* data, uint32_t data_length); +void spi_flash_init(void); + +#endif // MICROPY_INCLUDED_ATMEL_SAMD_SPI_FLASH_H