492 lines
17 KiB
C
492 lines
17 KiB
C
|
/*
|
||
|
* This file is part of the MicroPython project, http://micropython.org/
|
||
|
*
|
||
|
* The MIT License (MIT)
|
||
|
*
|
||
|
* Copyright (c) 2019 Adafruit Industries
|
||
|
* Copyright (c) 2023 Robert Hammelrath
|
||
|
*
|
||
|
* 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.
|
||
|
*
|
||
|
* Port of the Adafruit QSPIflash driver for SAMD devices
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include <stdint.h>
|
||
|
#include <string.h>
|
||
|
#include "py/obj.h"
|
||
|
#include "py/runtime.h"
|
||
|
#include "py/mphal.h"
|
||
|
#include "py/mperrno.h"
|
||
|
#include "modmachine.h"
|
||
|
#include "extmod/machine_spi.h"
|
||
|
#include "extmod/vfs.h"
|
||
|
#include "pin_af.h"
|
||
|
#include "clock_config.h"
|
||
|
#include "sam.h"
|
||
|
|
||
|
#ifdef MICROPY_HW_QSPIFLASH
|
||
|
|
||
|
#include "drivers/memory/external_flash_device.h"
|
||
|
|
||
|
// QSPI command codes
|
||
|
enum
|
||
|
{
|
||
|
QSPI_CMD_READ = 0x03,
|
||
|
QSPI_CMD_READ_4B = 0x13,
|
||
|
QSPI_CMD_QUAD_READ = 0x6B,// 1 line address, 4 line data
|
||
|
|
||
|
QSPI_CMD_READ_JEDEC_ID = 0x9f,
|
||
|
|
||
|
QSPI_CMD_PAGE_PROGRAM = 0x02,
|
||
|
QSPI_CMD_PAGE_PROGRAM_4B = 0x12,
|
||
|
QSPI_CMD_QUAD_PAGE_PROGRAM = 0x32, // 1 line address, 4 line data
|
||
|
|
||
|
QSPI_CMD_READ_STATUS = 0x05,
|
||
|
QSPI_CMD_READ_STATUS2 = 0x35,
|
||
|
|
||
|
QSPI_CMD_WRITE_STATUS = 0x01,
|
||
|
QSPI_CMD_WRITE_STATUS2 = 0x31,
|
||
|
|
||
|
QSPI_CMD_ENABLE_RESET = 0x66,
|
||
|
QSPI_CMD_RESET = 0x99,
|
||
|
|
||
|
QSPI_CMD_WRITE_ENABLE = 0x06,
|
||
|
QSPI_CMD_WRITE_DISABLE = 0x04,
|
||
|
|
||
|
QSPI_CMD_ERASE_SECTOR = 0x20,
|
||
|
QSPI_CMD_ERASE_SECTOR_4B = 0x21,
|
||
|
QSPI_CMD_ERASE_BLOCK = 0xD8,
|
||
|
QSPI_CMD_ERASE_CHIP = 0xC7,
|
||
|
|
||
|
QSPI_CMD_READ_SFDP_PARAMETER = 0x5A,
|
||
|
};
|
||
|
|
||
|
// QSPI flash pins are: CS=PB11, SCK=PB10, IO0-IO3=PA08, PA09, PA10 and PA11.
|
||
|
#define PIN_CS (43)
|
||
|
#define PIN_SCK (42)
|
||
|
#define PIN_IO0 (8)
|
||
|
#define PIN_IO1 (9)
|
||
|
#define PIN_IO2 (10)
|
||
|
#define PIN_IO3 (11)
|
||
|
|
||
|
#define PAGE_SIZE (256)
|
||
|
#define SECTOR_SIZE (4096)
|
||
|
|
||
|
typedef struct _samd_qspiflash_obj_t {
|
||
|
mp_obj_base_t base;
|
||
|
uint16_t pagesize;
|
||
|
uint16_t sectorsize;
|
||
|
uint32_t size;
|
||
|
uint8_t phase;
|
||
|
uint8_t polarity;
|
||
|
} samd_qspiflash_obj_t;
|
||
|
|
||
|
/// List of all possible flash devices used by Adafruit boards
|
||
|
static const external_flash_device possible_devices[] = {
|
||
|
MICROPY_HW_QSPIFLASH
|
||
|
};
|
||
|
|
||
|
#define EXTERNAL_FLASH_DEVICE_COUNT MP_ARRAY_SIZE(possible_devices)
|
||
|
static external_flash_device const *flash_device;
|
||
|
static external_flash_device generic_config = GENERIC;
|
||
|
extern const mp_obj_type_t samd_qspiflash_type;
|
||
|
|
||
|
// The QSPIflash object is a singleton
|
||
|
static samd_qspiflash_obj_t qspiflash_obj = { { &samd_qspiflash_type } };
|
||
|
|
||
|
// Turn off cache and invalidate all data in it.
|
||
|
static void samd_peripherals_disable_and_clear_cache(void) {
|
||
|
CMCC->CTRL.bit.CEN = 0;
|
||
|
while (CMCC->SR.bit.CSTS) {
|
||
|
}
|
||
|
CMCC->MAINT0.bit.INVALL = 1;
|
||
|
}
|
||
|
|
||
|
// Enable cache
|
||
|
static void samd_peripherals_enable_cache(void) {
|
||
|
CMCC->CTRL.bit.CEN = 1;
|
||
|
}
|
||
|
|
||
|
// Run a single QSPI instruction.
|
||
|
// Parameters are:
|
||
|
// - command instruction code
|
||
|
// - iframe iframe register value (configured by caller according to command code)
|
||
|
// - addr the address to read or write from. If the instruction doesn't require an address, this parameter is meaningless.
|
||
|
// - buffer pointer to the data to be written or stored depending on the type is Read or Write
|
||
|
// - size the number of bytes to read or write.
|
||
|
bool run_instruction(uint8_t command, uint32_t iframe, uint32_t addr, uint8_t *buffer, uint32_t size) {
|
||
|
|
||
|
samd_peripherals_disable_and_clear_cache();
|
||
|
|
||
|
uint8_t *qspi_mem = (uint8_t *)QSPI_AHB;
|
||
|
if (addr) {
|
||
|
qspi_mem += addr;
|
||
|
}
|
||
|
|
||
|
QSPI->INSTRCTRL.bit.INSTR = command;
|
||
|
QSPI->INSTRADDR.reg = addr;
|
||
|
QSPI->INSTRFRAME.reg = iframe;
|
||
|
|
||
|
// Dummy read of INSTRFRAME needed to synchronize.
|
||
|
// See Instruction Transmission Flow Diagram, figure 37.9, page 995
|
||
|
// and Example 4, page 998, section 37.6.8.5.
|
||
|
(volatile uint32_t)QSPI->INSTRFRAME.reg;
|
||
|
|
||
|
if (buffer && size) {
|
||
|
uint32_t const tfr_type = iframe & QSPI_INSTRFRAME_TFRTYPE_Msk;
|
||
|
if ((tfr_type == QSPI_INSTRFRAME_TFRTYPE_READ) || (tfr_type == QSPI_INSTRFRAME_TFRTYPE_READMEMORY)) {
|
||
|
memcpy(buffer, qspi_mem, size);
|
||
|
} else {
|
||
|
memcpy(qspi_mem, buffer, size);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
__asm volatile ("dsb");
|
||
|
__asm volatile ("isb");
|
||
|
|
||
|
QSPI->CTRLA.reg = QSPI_CTRLA_ENABLE | QSPI_CTRLA_LASTXFER;
|
||
|
while (!QSPI->INTFLAG.bit.INSTREND) {
|
||
|
}
|
||
|
QSPI->INTFLAG.reg = QSPI_INTFLAG_INSTREND;
|
||
|
|
||
|
samd_peripherals_enable_cache();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool run_command(uint8_t command) {
|
||
|
uint32_t iframe = QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | QSPI_INSTRFRAME_ADDRLEN_24BITS |
|
||
|
QSPI_INSTRFRAME_TFRTYPE_READ | QSPI_INSTRFRAME_INSTREN;
|
||
|
return run_instruction(command, iframe, 0, NULL, 0);
|
||
|
}
|
||
|
|
||
|
bool read_command(uint8_t command, uint8_t *response, uint32_t len) {
|
||
|
uint32_t iframe = QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | QSPI_INSTRFRAME_ADDRLEN_24BITS |
|
||
|
QSPI_INSTRFRAME_TFRTYPE_READ | QSPI_INSTRFRAME_INSTREN | QSPI_INSTRFRAME_DATAEN;
|
||
|
return run_instruction(command, iframe, 0, response, len);
|
||
|
}
|
||
|
|
||
|
bool read_memory_single(uint8_t command, uint32_t addr, uint8_t *response, uint32_t len) {
|
||
|
uint32_t iframe = QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | QSPI_INSTRFRAME_ADDRLEN_24BITS |
|
||
|
QSPI_INSTRFRAME_TFRTYPE_READ | QSPI_INSTRFRAME_INSTREN | QSPI_INSTRFRAME_ADDREN |
|
||
|
QSPI_INSTRFRAME_DATAEN | QSPI_INSTRFRAME_DUMMYLEN(8);
|
||
|
return run_instruction(command, iframe, addr, response, len);
|
||
|
}
|
||
|
|
||
|
bool write_command(uint8_t command, uint8_t const *data, uint32_t len) {
|
||
|
uint32_t iframe = QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | QSPI_INSTRFRAME_ADDRLEN_24BITS |
|
||
|
QSPI_INSTRFRAME_TFRTYPE_WRITE | QSPI_INSTRFRAME_INSTREN | (data != NULL ? QSPI_INSTRFRAME_DATAEN : 0);
|
||
|
return run_instruction(command, iframe, 0, (uint8_t *)data, len);
|
||
|
}
|
||
|
|
||
|
bool erase_command(uint8_t command, uint32_t address) {
|
||
|
// Sector Erase
|
||
|
uint32_t iframe = QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | QSPI_INSTRFRAME_ADDRLEN_24BITS |
|
||
|
QSPI_INSTRFRAME_TFRTYPE_WRITE | QSPI_INSTRFRAME_INSTREN | QSPI_INSTRFRAME_ADDREN;
|
||
|
return run_instruction(command, iframe, address, NULL, 0);
|
||
|
}
|
||
|
|
||
|
bool read_memory_quad(uint8_t command, uint32_t addr, uint8_t *data, uint32_t len) {
|
||
|
uint32_t iframe = QSPI_INSTRFRAME_WIDTH_QUAD_OUTPUT | QSPI_INSTRFRAME_ADDRLEN_24BITS |
|
||
|
QSPI_INSTRFRAME_TFRTYPE_READMEMORY | QSPI_INSTRFRAME_INSTREN | QSPI_INSTRFRAME_ADDREN | QSPI_INSTRFRAME_DATAEN |
|
||
|
/*QSPI_INSTRFRAME_CRMODE |*/ QSPI_INSTRFRAME_DUMMYLEN(8);
|
||
|
return run_instruction(command, iframe, addr, data, len);
|
||
|
}
|
||
|
|
||
|
bool write_memory_quad(uint8_t command, uint32_t addr, uint8_t *data, uint32_t len) {
|
||
|
uint32_t iframe = QSPI_INSTRFRAME_WIDTH_QUAD_OUTPUT | QSPI_INSTRFRAME_ADDRLEN_24BITS |
|
||
|
QSPI_INSTRFRAME_TFRTYPE_WRITEMEMORY | QSPI_INSTRFRAME_INSTREN | QSPI_INSTRFRAME_ADDREN | QSPI_INSTRFRAME_DATAEN;
|
||
|
return run_instruction(command, iframe, addr, data, len);
|
||
|
}
|
||
|
|
||
|
static uint8_t read_status(void) {
|
||
|
uint8_t r;
|
||
|
read_command(QSPI_CMD_READ_STATUS, &r, 1);
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
static uint8_t read_status2(void) {
|
||
|
uint8_t r;
|
||
|
read_command(QSPI_CMD_READ_STATUS2, &r, 1);
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
static bool write_enable(void) {
|
||
|
return run_command(QSPI_CMD_WRITE_ENABLE);
|
||
|
}
|
||
|
|
||
|
static void wait_for_flash_ready(void) {
|
||
|
// both WIP and WREN bit should be clear
|
||
|
while (read_status() & 0x03) {
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static uint8_t get_baud(int32_t freq_mhz) {
|
||
|
int baud = get_peripheral_freq() / (freq_mhz * 1000000) - 1;
|
||
|
if (baud < 1) {
|
||
|
baud = 1;
|
||
|
}
|
||
|
if (baud > 255) {
|
||
|
baud = 255;
|
||
|
}
|
||
|
return baud;
|
||
|
}
|
||
|
|
||
|
int get_sfdp_table(uint8_t *table, int maxlen) {
|
||
|
uint8_t header[16];
|
||
|
read_memory_single(QSPI_CMD_READ_SFDP_PARAMETER, 0, header, sizeof(header));
|
||
|
int len = MIN(header[11] * 4, maxlen);
|
||
|
int addr = header[12] + (header[13] << 8) + (header[14] << 16);
|
||
|
read_memory_single(QSPI_CMD_READ_SFDP_PARAMETER, addr, table, len);
|
||
|
return len;
|
||
|
}
|
||
|
|
||
|
STATIC mp_obj_t samd_qspiflash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
|
||
|
mp_arg_check_num(n_args, n_kw, 0, 0, false);
|
||
|
|
||
|
// The QSPI is a singleton
|
||
|
samd_qspiflash_obj_t *self = &qspiflash_obj;
|
||
|
self->phase = 0;
|
||
|
self->polarity = 0;
|
||
|
self->pagesize = PAGE_SIZE;
|
||
|
self->sectorsize = SECTOR_SIZE;
|
||
|
|
||
|
// Enable the device clock
|
||
|
MCLK->AHBMASK.reg |= MCLK_AHBMASK_QSPI;
|
||
|
MCLK->AHBMASK.reg |= MCLK_AHBMASK_QSPI_2X;
|
||
|
MCLK->APBCMASK.reg |= MCLK_APBCMASK_QSPI;
|
||
|
|
||
|
// Configure the pins.
|
||
|
mp_hal_set_pin_mux(PIN_CS, ALT_FCT_QSPI);
|
||
|
mp_hal_set_pin_mux(PIN_SCK, ALT_FCT_QSPI);
|
||
|
mp_hal_set_pin_mux(PIN_IO0, ALT_FCT_QSPI);
|
||
|
mp_hal_set_pin_mux(PIN_IO1, ALT_FCT_QSPI);
|
||
|
mp_hal_set_pin_mux(PIN_IO2, ALT_FCT_QSPI);
|
||
|
mp_hal_set_pin_mux(PIN_IO3, ALT_FCT_QSPI);
|
||
|
|
||
|
// Configure the QSPI interface
|
||
|
QSPI->CTRLA.bit.SWRST = 1;
|
||
|
mp_hal_delay_us(1000); // Maybe not required.
|
||
|
|
||
|
QSPI->CTRLB.reg = QSPI_CTRLB_MODE_MEMORY |
|
||
|
QSPI_CTRLB_CSMODE_NORELOAD |
|
||
|
QSPI_CTRLB_DATALEN_8BITS |
|
||
|
QSPI_CTRLB_CSMODE_LASTXFER;
|
||
|
// start with low 4Mhz, Mode 0
|
||
|
QSPI->BAUD.reg = QSPI_BAUD_BAUD(get_baud(4)) |
|
||
|
(self->phase << QSPI_BAUD_CPHA_Pos) |
|
||
|
(self->polarity << QSPI_BAUD_CPOL_Pos);
|
||
|
QSPI->CTRLA.bit.ENABLE = 1;
|
||
|
|
||
|
uint8_t jedec_ids[3];
|
||
|
read_command(QSPI_CMD_READ_JEDEC_ID, jedec_ids, sizeof(jedec_ids));
|
||
|
|
||
|
// Read the common sfdp table
|
||
|
// Check the device addr length, support of 1-1-4 mode and get the sector size
|
||
|
uint8_t sfdp_table[128];
|
||
|
int len = get_sfdp_table(sfdp_table, sizeof(sfdp_table));
|
||
|
if (len >= 29) {
|
||
|
self->sectorsize = 1 << sfdp_table[28];
|
||
|
bool addr4b = ((sfdp_table[2] >> 1) & 0x03) == 0x02;
|
||
|
bool supports_qspi_114 = (sfdp_table[2] & 0x40) != 0;
|
||
|
if (addr4b || !supports_qspi_114) {
|
||
|
mp_raise_ValueError(MP_ERROR_TEXT("QSPI mode not supported"));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check, if the flash device is known and get it's properties.
|
||
|
flash_device = NULL;
|
||
|
for (uint8_t i = 0; i < EXTERNAL_FLASH_DEVICE_COUNT; i++) {
|
||
|
const external_flash_device *possible_device = &possible_devices[i];
|
||
|
if (jedec_ids[0] == possible_device->manufacturer_id &&
|
||
|
jedec_ids[1] == possible_device->memory_type &&
|
||
|
jedec_ids[2] == possible_device->capacity) {
|
||
|
flash_device = possible_device;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If the flash device is not known, try generic config options
|
||
|
if (flash_device == NULL) {
|
||
|
if (jedec_ids[0] == 0xc2) { // Macronix devices
|
||
|
generic_config.quad_enable_bit_mask = 0x04;
|
||
|
generic_config.single_status_byte = true;
|
||
|
}
|
||
|
generic_config.total_size = 1 << jedec_ids[2];
|
||
|
flash_device = &generic_config;
|
||
|
}
|
||
|
|
||
|
self->size = flash_device->total_size;
|
||
|
|
||
|
// The write in progress bit should be low.
|
||
|
while (read_status() & 0x01) {
|
||
|
}
|
||
|
// The suspended write/erase bit should be low.
|
||
|
while (read_status2() & 0x80) {
|
||
|
}
|
||
|
run_command(QSPI_CMD_ENABLE_RESET);
|
||
|
run_command(QSPI_CMD_RESET);
|
||
|
// Wait 30us for the reset
|
||
|
mp_hal_delay_us(30);
|
||
|
// Speed up the frequency
|
||
|
QSPI->BAUD.bit.BAUD = get_baud(flash_device->max_clock_speed_mhz);
|
||
|
|
||
|
// Enable Quad Mode if available
|
||
|
uint8_t status = 0;
|
||
|
if (flash_device->quad_enable_bit_mask) {
|
||
|
// Verify that QSPI mode is enabled.
|
||
|
status = flash_device->single_status_byte ? read_status() : read_status2();
|
||
|
}
|
||
|
|
||
|
// Check the quad enable bit.
|
||
|
if ((status & flash_device->quad_enable_bit_mask) == 0) {
|
||
|
write_enable();
|
||
|
uint8_t full_status[2] = {0x00, flash_device->quad_enable_bit_mask};
|
||
|
|
||
|
if (flash_device->write_status_register_split) {
|
||
|
write_command(QSPI_CMD_WRITE_STATUS2, full_status + 1, 1);
|
||
|
} else if (flash_device->single_status_byte) {
|
||
|
write_command(QSPI_CMD_WRITE_STATUS, full_status + 1, 1);
|
||
|
} else {
|
||
|
write_command(QSPI_CMD_WRITE_STATUS, full_status, 2);
|
||
|
}
|
||
|
}
|
||
|
// Turn off writes in case this is a microcontroller only reset.
|
||
|
run_command(QSPI_CMD_WRITE_DISABLE);
|
||
|
wait_for_flash_ready();
|
||
|
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
STATIC mp_obj_t samd_qspiflash_read(samd_qspiflash_obj_t *self, uint32_t addr, uint8_t *dest, uint32_t len) {
|
||
|
if (len > 0) {
|
||
|
wait_for_flash_ready();
|
||
|
// Command 0x6B 1 line address, 4 line Data
|
||
|
// with Continuous Read Mode and Quad output mode, read memory type
|
||
|
read_memory_quad(QSPI_CMD_QUAD_READ, addr, dest, len);
|
||
|
}
|
||
|
|
||
|
return mp_const_none;
|
||
|
}
|
||
|
|
||
|
STATIC mp_obj_t samd_qspiflash_write(samd_qspiflash_obj_t *self, uint32_t addr, uint8_t *src, uint32_t len) {
|
||
|
uint32_t length = len;
|
||
|
uint32_t pos = 0;
|
||
|
uint8_t *buf = src;
|
||
|
|
||
|
while (pos < length) {
|
||
|
uint16_t maxsize = self->pagesize - pos % self->pagesize;
|
||
|
uint16_t size = (length - pos) > maxsize ? maxsize : length - pos;
|
||
|
|
||
|
wait_for_flash_ready();
|
||
|
write_enable();
|
||
|
write_memory_quad(QSPI_CMD_QUAD_PAGE_PROGRAM, addr, buf + pos, size);
|
||
|
|
||
|
addr += size;
|
||
|
pos += size;
|
||
|
}
|
||
|
|
||
|
return mp_const_none;
|
||
|
}
|
||
|
|
||
|
STATIC mp_obj_t samd_qspiflash_erase(uint32_t addr) {
|
||
|
wait_for_flash_ready();
|
||
|
write_enable();
|
||
|
erase_command(QSPI_CMD_ERASE_SECTOR, addr);
|
||
|
|
||
|
return mp_const_none;
|
||
|
}
|
||
|
|
||
|
STATIC mp_obj_t samd_qspiflash_readblocks(size_t n_args, const mp_obj_t *args) {
|
||
|
samd_qspiflash_obj_t *self = MP_OBJ_TO_PTR(args[0]);
|
||
|
uint32_t offset = (mp_obj_get_int(args[1]) * self->sectorsize);
|
||
|
mp_buffer_info_t bufinfo;
|
||
|
mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_WRITE);
|
||
|
if (n_args == 4) {
|
||
|
offset += mp_obj_get_int(args[3]);
|
||
|
}
|
||
|
|
||
|
// Read data to flash (adf4 API)
|
||
|
samd_qspiflash_read(self, offset, bufinfo.buf, bufinfo.len);
|
||
|
|
||
|
return mp_const_none;
|
||
|
}
|
||
|
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(samd_qspiflash_readblocks_obj, 3, 4, samd_qspiflash_readblocks);
|
||
|
|
||
|
STATIC mp_obj_t samd_qspiflash_writeblocks(size_t n_args, const mp_obj_t *args) {
|
||
|
samd_qspiflash_obj_t *self = MP_OBJ_TO_PTR(args[0]);
|
||
|
uint32_t offset = (mp_obj_get_int(args[1]) * self->sectorsize);
|
||
|
mp_buffer_info_t bufinfo;
|
||
|
mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ);
|
||
|
if (n_args == 3) {
|
||
|
samd_qspiflash_erase(offset);
|
||
|
// TODO check return value
|
||
|
} else {
|
||
|
offset += mp_obj_get_int(args[3]);
|
||
|
}
|
||
|
// Write data to flash (adf4 API)
|
||
|
samd_qspiflash_write(self, offset, bufinfo.buf, bufinfo.len);
|
||
|
// TODO check return value
|
||
|
return mp_const_none;
|
||
|
}
|
||
|
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(samd_qspiflash_writeblocks_obj, 3, 4, samd_qspiflash_writeblocks);
|
||
|
|
||
|
STATIC mp_obj_t samd_qspiflash_ioctl(mp_obj_t self_in, mp_obj_t cmd_in, mp_obj_t arg_in) {
|
||
|
samd_qspiflash_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||
|
mp_int_t cmd = mp_obj_get_int(cmd_in);
|
||
|
|
||
|
switch (cmd) {
|
||
|
case MP_BLOCKDEV_IOCTL_INIT:
|
||
|
return MP_OBJ_NEW_SMALL_INT(0);
|
||
|
case MP_BLOCKDEV_IOCTL_DEINIT:
|
||
|
return MP_OBJ_NEW_SMALL_INT(0);
|
||
|
case MP_BLOCKDEV_IOCTL_SYNC:
|
||
|
return MP_OBJ_NEW_SMALL_INT(0);
|
||
|
case MP_BLOCKDEV_IOCTL_BLOCK_COUNT:
|
||
|
return MP_OBJ_NEW_SMALL_INT(self->size / self->sectorsize);
|
||
|
case MP_BLOCKDEV_IOCTL_BLOCK_SIZE:
|
||
|
return MP_OBJ_NEW_SMALL_INT(self->sectorsize);
|
||
|
case MP_BLOCKDEV_IOCTL_BLOCK_ERASE: {
|
||
|
samd_qspiflash_erase(mp_obj_get_int(arg_in) * self->sectorsize);
|
||
|
// TODO check return value
|
||
|
return MP_OBJ_NEW_SMALL_INT(0);
|
||
|
}
|
||
|
default:
|
||
|
return mp_const_none;
|
||
|
}
|
||
|
}
|
||
|
STATIC MP_DEFINE_CONST_FUN_OBJ_3(samd_qspiflash_ioctl_obj, samd_qspiflash_ioctl);
|
||
|
|
||
|
STATIC const mp_rom_map_elem_t samd_qspiflash_locals_dict_table[] = {
|
||
|
{ MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&samd_qspiflash_readblocks_obj) },
|
||
|
{ MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&samd_qspiflash_writeblocks_obj) },
|
||
|
{ MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&samd_qspiflash_ioctl_obj) },
|
||
|
};
|
||
|
STATIC MP_DEFINE_CONST_DICT(samd_qspiflash_locals_dict, samd_qspiflash_locals_dict_table);
|
||
|
|
||
|
MP_DEFINE_CONST_OBJ_TYPE(
|
||
|
samd_qspiflash_type,
|
||
|
MP_QSTR_Flash,
|
||
|
MP_TYPE_FLAG_NONE,
|
||
|
make_new, samd_qspiflash_make_new,
|
||
|
locals_dict, &samd_qspiflash_locals_dict
|
||
|
);
|
||
|
|
||
|
#endif // MICROPY_HW_QSPI_FLASH
|