From 72c911be972d63a5032fa02ecb74d173bf9ef34f Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 22 Nov 2017 23:07:21 -0500 Subject: [PATCH] Support SPI flash chips for CIRCUITPY, using non-DMA SPI for now. --- ports/atmel-samd/Makefile | 3 +- ports/atmel-samd/asf4 | 2 +- .../boards/feather_m0_express/mpconfigboard.h | 36 ++- .../feather_m0_express/mpconfigboard.mk | 3 +- .../boards/metro_m0_express/mpconfigboard.h | 35 ++- .../boards/metro_m0_express/mpconfigboard.mk | 3 +- .../atmel-samd/boards/metro_m0_express/pins.c | 1 - .../boards/metro_m4_express/mpconfigboard.h | 45 +++- .../boards/metro_m4_express/mpconfigboard.mk | 3 +- ports/atmel-samd/common-hal/busio/I2C.c | 2 +- ports/atmel-samd/common-hal/busio/SPI.c | 29 +- .../atmel-samd/common-hal/storage/__init__.c | 5 +- ports/atmel-samd/mpconfigport.h | 2 +- ports/atmel-samd/peripherals.c | 43 +++ ports/atmel-samd/peripherals.h | 5 + ports/atmel-samd/samd21_peripherals.c | 6 +- ports/atmel-samd/samd21_peripherals.h | 6 +- ports/atmel-samd/samd51_peripherals.c | 6 +- ports/atmel-samd/samd51_peripherals.h | 6 +- ports/atmel-samd/samd_peripherals.h | 36 +++ ports/atmel-samd/shared_dma.c | 33 ++- ports/atmel-samd/spi_flash.c | 253 ++++++++++-------- ports/atmel-samd/usb_mass_storage.c | 8 +- shared-bindings/storage/__init__.c | 2 +- 24 files changed, 361 insertions(+), 212 deletions(-) create mode 100644 ports/atmel-samd/peripherals.c create mode 100644 ports/atmel-samd/samd_peripherals.h diff --git a/ports/atmel-samd/Makefile b/ports/atmel-samd/Makefile index 3ac4cde9a8..486df28c23 100644 --- a/ports/atmel-samd/Makefile +++ b/ports/atmel-samd/Makefile @@ -218,6 +218,7 @@ SRC_C = \ mphalport.c \ reset.c \ $(CHIP_FAMILY)_peripherals.c \ + peripherals.c \ $(CHIP_FAMILY)_pins.c \ tick.c \ usb.c \ @@ -260,6 +261,7 @@ SRC_COMMON_HAL = \ microcontroller/Processor.c \ neopixel_write/__init__.c \ os/__init__.c \ + storage/__init__.c \ time/__init__.c \ # analogio/__init__.c \ analogio/AnalogIn.c \ @@ -276,7 +278,6 @@ SRC_COMMON_HAL = \ pulseio/PulseIn.c \ pulseio/PulseOut.c \ pulseio/PWMOut.c \ - storage/__init__.c \ touchio/__init__.c \ touchio/TouchIn.c \ usb_hid/__init__.c \ diff --git a/ports/atmel-samd/asf4 b/ports/atmel-samd/asf4 index 72f76894ba..0efc3407dd 160000 --- a/ports/atmel-samd/asf4 +++ b/ports/atmel-samd/asf4 @@ -1 +1 @@ -Subproject commit 72f76894ba08c9de2ec3ae231fb71daaf3eafb1e +Subproject commit 0efc3407dd97ef617a5655674a3516693897a961 diff --git a/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.h b/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.h index 83c6810619..448c6c52bf 100644 --- a/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.h +++ b/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.h @@ -3,25 +3,35 @@ #define MICROPY_HW_NEOPIXEL (&pin_PA06) -// Salae reads 12mhz which is the limit even though we set it to the safer 8mhz. +// Clock rates are off: Salae reads 12MHz which is the limit even though we set it to the safer 8MHz. #define SPI_FLASH_BAUDRATE (8000000) -#define SPI_FLASH_MUX_SETTING SPI_SIGNAL_MUX_SETTING_C -#define SPI_FLASH_PAD0_PINMUX PINMUX_PA08D_SERCOM2_PAD0 // MOSI -// Use default pinmux for the chip select since we manage it ourselves. -#define SPI_FLASH_PAD1_PINMUX PINMUX_PA09D_SERCOM2_PAD1 // SCK -#define SPI_FLASH_PAD2_PINMUX PINMUX_PA14C_SERCOM2_PAD2 // MISO -#define SPI_FLASH_PAD3_PINMUX PINMUX_UNUSED // SCK -#define SPI_FLASH_SERCOM SERCOM2 +#define SPI_FLASH_MOSI_PIN PIN_PA08 +#define SPI_FLASH_MISO_PIN PIN_PA14 +#define SPI_FLASH_SCK_PIN PIN_PA09 +#define SPI_FLASH_CS_PIN PIN_PA13 +#define SPI_FLASH_MOSI_PIN_FUNCTION PINMUX_PA08D_SERCOM2_PAD0 +#define SPI_FLASH_MISO_PIN_FUNCTION PINMUX_PA14C_SERCOM2_PAD2 +#define SPI_FLASH_SCK_PIN_FUNCTION PINMUX_PA09D_SERCOM2_PAD1 +#define SPI_FLASH_SERCOM SERCOM2 +#define SPI_FLASH_SERCOM_INDEX 2 +#define SPI_FLASH_MOSI_PAD 0 +#define SPI_FLASH_MISO_PAD 2 +#define SPI_FLASH_SCK_PAD 1 +// Transmit Data Pinout +// <0x0=>PAD[0,1]_DO_SCK +// <0x1=>PAD[2,3]_DO_SCK +// <0x2=>PAD[3,1]_DO_SCK +// <0x3=>PAD[0,3]_DO_SCK +#define SPI_FLASH_DOPO 0 +#define SPI_FLASH_DIPO 2 // same as MISO pad -#define SPI_FLASH_CS PIN_PA13 - -#define MICROPY_PORT_A (PORT_PA06 | PORT_PA08 | PORT_PA09 | PORT_PA14 | PORT_PA13 | PORT_PA14 | PORT_PA24 | PORT_PA25) +// These are pins not to reset. +#define MICROPY_PORT_A (PORT_PA06 | PORT_PA08 | PORT_PA09 | PORT_PA13 | PORT_PA14 | PORT_PA24 | PORT_PA25) #define MICROPY_PORT_B ( 0 ) #define MICROPY_PORT_C ( 0 ) -#include "internal_flash.h" -//#include "spi_flash.h" +#include "spi_flash.h" // If you change this, then make sure to update the linker scripts as well to // make sure you don't overwrite code. diff --git a/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk b/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk index 820ec4ecaf..5e3a974938 100644 --- a/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk @@ -4,8 +4,7 @@ USB_PID = 0x8023 USB_PRODUCT = "Feather M0 Express" USB_MANUFACTURER = "Adafruit Industries LLC" -#SPI_FLASH_FILESYSTEM = 1 -INTERNAL_FLASH_FILESYSTEM = 1 +SPI_FLASH_FILESYSTEM = 1 CHIP_VARIANT = SAMD21G18A CHIP_FAMILY = samd21 diff --git a/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.h b/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.h index e3b67de4b1..023beb6883 100644 --- a/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.h +++ b/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.h @@ -1,5 +1,3 @@ -#define USB_REPL - #define MICROPY_HW_BOARD_NAME "Adafruit Metro M0 Express" #define MICROPY_HW_MCU_NAME "samd21g18" @@ -8,24 +6,35 @@ #define MICROPY_HW_NEOPIXEL (&pin_PA30) -// Salae reads 12mhz which is the limit even though we set it to the safer 8mhz. +// Clock rates are off: Salae reads 12MHz which is the limit even though we set it to the safer 8MHz. #define SPI_FLASH_BAUDRATE (8000000) -#define SPI_FLASH_MUX_SETTING SPI_SIGNAL_MUX_SETTING_F -#define SPI_FLASH_PAD0_PINMUX PINMUX_UNUSED // CS -// Use default pinmux for the chip select since we manage it ourselves. -#define SPI_FLASH_PAD1_PINMUX PINMUX_PB03D_SERCOM5_PAD1 // MISO -#define SPI_FLASH_PAD2_PINMUX PINMUX_PB22D_SERCOM5_PAD2 // MOSI -#define SPI_FLASH_PAD3_PINMUX PINMUX_PB23D_SERCOM5_PAD3 // SCK -#define SPI_FLASH_CS PIN_PA13 -#define SPI_FLASH_SERCOM SERCOM5 +#define SPI_FLASH_MOSI_PIN PIN_PB22 +#define SPI_FLASH_MISO_PIN PIN_PB03 +#define SPI_FLASH_SCK_PIN PIN_PB23 +#define SPI_FLASH_CS_PIN PIN_PA13 +#define SPI_FLASH_MOSI_PIN_FUNCTION PINMUX_PB22D_SERCOM5_PAD2 +#define SPI_FLASH_MISO_PIN_FUNCTION PINMUX_PB03D_SERCOM5_PAD1 +#define SPI_FLASH_SCK_PIN_FUNCTION PINMUX_PB23D_SERCOM5_PAD3 +#define SPI_FLASH_SERCOM SERCOM5 +#define SPI_FLASH_SERCOM_INDEX 5 +#define SPI_FLASH_MOSI_PAD 2 +#define SPI_FLASH_MISO_PAD 1 +#define SPI_FLASH_SCK_PAD 3 +// Transmit Data Pinout +// <0x0=>PAD[0,1]_DO_SCK +// <0x1=>PAD[2,3]_DO_SCK +// <0x2=>PAD[3,1]_DO_SCK +// <0x3=>PAD[0,3]_DO_SCK +#define SPI_FLASH_DOPO 1 +#define SPI_FLASH_DIPO 1 // same as MISO pad +// These are pins not to reset. #define MICROPY_PORT_A (PORT_PA13 |PORT_PA24 | PORT_PA25 | PORT_PA27 | PORT_PA30 | PORT_PA31) #define MICROPY_PORT_B (PORT_PB03 | PORT_PB22 | PORT_PB23) #define MICROPY_PORT_C (0) -#include "internal_flash.h" -//#include "spi_flash.h" +#include "spi_flash.h" // If you change this, then make sure to update the linker scripts as well to // make sure you don't overwrite code. diff --git a/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk b/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk index 7d63321af9..d624b81911 100644 --- a/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk @@ -4,8 +4,7 @@ USB_PID = 0x8014 USB_PRODUCT = "Metro M0 Express" USB_MANUFACTURER = "Adafruit Industries LLC" -#SPI_FLASH_FILESYSTEM = 1 -INTERNAL_FLASH_FILESYSTEM = 1 +SPI_FLASH_FILESYSTEM = 1 CHIP_VARIANT = SAMD21G18A CHIP_FAMILY = samd21 diff --git a/ports/atmel-samd/boards/metro_m0_express/pins.c b/ports/atmel-samd/boards/metro_m0_express/pins.c index bafd0bc9f7..58510faabb 100644 --- a/ports/atmel-samd/boards/metro_m0_express/pins.c +++ b/ports/atmel-samd/boards/metro_m0_express/pins.c @@ -29,6 +29,5 @@ STATIC const mp_rom_map_elem_t board_global_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_PB11) }, { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_PB10) }, { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_PA12) }, - { MP_ROM_QSTR(MP_QSTR_FLASH_CS), MP_ROM_PTR(&pin_PA13) }, }; MP_DEFINE_CONST_DICT(board_module_globals, board_global_dict_table); diff --git a/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.h b/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.h index 34a6816076..1af18ecbc2 100644 --- a/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.h +++ b/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.h @@ -8,29 +8,46 @@ #define MICROPY_HW_NEOPIXEL (&pin_PB17) -#define SPI_FLASH_BAUDRATE (1000000) +#define SPI_FLASH_BAUDRATE (8000000) -// Rev F -#define SPI_FLASH_MUX_SETTING SPI_SIGNAL_MUX_SETTING_F -#define SPI_FLASH_PAD0_PINMUX PINMUX_PB08 // MOSI -// Use default pinmux for the chip select since we manage it ourselves. -#define SPI_FLASH_PAD1_PINMUX PINMUX_PB09 // SCK -#define SPI_FLASH_PAD2_PINMUX PINMUX_DEFAULT // CS -#define SPI_FLASH_PAD3_PINMUX PINMUX_PB11 // MISO -#define SPI_FLASH_CS PIN_PB10 -#define SPI_FLASH_SERCOM SERCOM5 +// Rev B: single channel SPI +// Rev C will be QSPI +#define SPI_FLASH_MOSI_PIN PIN_PB08 +#define SPI_FLASH_MISO_PIN PIN_PB11 +#define SPI_FLASH_SCK_PIN PIN_PB09 +#define SPI_FLASH_CS_PIN PIN_PB10 +#define SPI_FLASH_MOSI_PIN_FUNCTION PINMUX_PB08D_SERCOM4_PAD0 +#define SPI_FLASH_MISO_PIN_FUNCTION PINMUX_PB11D_SERCOM4_PAD3 +#define SPI_FLASH_SCK_PIN_FUNCTION PINMUX_PB09D_SERCOM4_PAD1 +#define SPI_FLASH_SERCOM SERCOM4 +#define SPI_FLASH_SERCOM_INDEX 4 +#define SPI_FLASH_MOSI_PAD 0 +#define SPI_FLASH_MISO_PAD 3 +#define SPI_FLASH_SCK_PAD 1 +// Transmit Data Pinout +// <0x0=>PAD[0,1]_DO_SCK +// <0x1=>PAD[2,3]_DO_SCK +// <0x2=>PAD[3,1]_DO_SCK +// <0x3=>PAD[0,3]_DO_SCK +#define SPI_FLASH_DOPO 0 +#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_PB11 | PORT_PB17) +#define MICROPY_PORT_B (PORT_PB06 | PORT_PB08 | PORT_PB09 | PORT_PB10 | PORT_PB11 | PORT_PB17) #define MICROPY_PORT_C (0) #define MICROPY_PORT_D (0) #define AUTORESET_DELAY_MS 500 -#include "internal_flash.h" +#include "spi_flash.h" // If you change this, then make sure to update the linker scripts as well to -// make sure you don't overwrite code. +// make sure you don't overwrite code +// #define CIRCUITPY_INTERNAL_NVM_SIZE 256 #define CIRCUITPY_INTERNAL_NVM_SIZE 0 -#define BOARD_FLASH_SIZE (FLASH_SIZE - 0x4000) +#define BOARD_FLASH_SIZE (FLASH_SIZE - 0x4000 - CIRCUITPY_INTERNAL_NVM_SIZE) + +#include "flash_S25FL216K.h" +#include "flash_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 194116545c..0369a7dd29 100644 --- a/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.mk @@ -4,8 +4,7 @@ USB_PID = 0x8021 USB_PRODUCT = "Metro M4 Express" USB_MANUFACTURER = "Adafruit Industries LLC" -#SPI_FLASH_FILESYSTEM = 1 -INTERNAL_FLASH_FILESYSTEM = 1 +SPI_FLASH_FILESYSTEM = 1 CHIP_VARIANT = SAMD51J19A CHIP_FAMILY = samd51 diff --git a/ports/atmel-samd/common-hal/busio/I2C.c b/ports/atmel-samd/common-hal/busio/I2C.c index 31b5767e79..4182c86513 100644 --- a/ports/atmel-samd/common-hal/busio/I2C.c +++ b/ports/atmel-samd/common-hal/busio/I2C.c @@ -72,7 +72,7 @@ void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, // Set up I2C clocks on sercom. - samd_peripheral_sercom_clock_init(sercom, sercom_index); + samd_peripherals_sercom_clock_init(sercom, sercom_index); if (i2c_m_sync_init(&self->i2c_desc, sercom) != ERR_NONE) { mp_raise_OSError(MP_EIO); diff --git a/ports/atmel-samd/common-hal/busio/SPI.c b/ports/atmel-samd/common-hal/busio/SPI.c index 21fed5d123..cb6a165def 100644 --- a/ports/atmel-samd/common-hal/busio/SPI.c +++ b/ports/atmel-samd/common-hal/busio/SPI.c @@ -37,17 +37,7 @@ #include "peripherals.h" #include "pins.h" -#include "shared_dma.h" - -// Convert frequency to clock-speed-dependent value. Return 0 if out of range. -static uint8_t baudrate_to_baud_reg_value(const uint32_t baudrate) { - uint32_t baud_reg_value = (uint32_t) (((float) PROTOTYPE_SERCOM_SPI_M_SYNC_CLOCK_FREQUENCY / - (2 * baudrate)) + 0.5f); - if (baud_reg_value > 0xff) { - return 0; - } - return (uint8_t) baud_reg_value; -} +//#include "shared_dma.h" void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t * clock, const mcu_pin_obj_t * mosi, @@ -78,7 +68,7 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, } clock_pinmux = PINMUX(clock->pin, (i == 0) ? MUX_C : MUX_D); clock_pad = clock->sercom[i].pad; - if (!samd_peripheral_valid_spi_clock_pad(clock_pad)) { + if (!samd_peripherals_valid_spi_clock_pad(clock_pad)) { continue; } for (int j = 0; j < NUM_SERCOMS_PER_PIN; j++) { @@ -86,7 +76,7 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, if(potential_sercom == mosi->sercom[j].sercom) { mosi_pinmux = PINMUX(mosi->pin, (j == 0) ? MUX_C : MUX_D); mosi_pad = mosi->sercom[j].pad; - dopo = samd_peripheral_get_spi_dopo(clock_pad, mosi_pad); + dopo = samd_peripherals_get_spi_dopo(clock_pad, mosi_pad); if (dopo > 0x3) { continue; // pad combination not possible } @@ -121,7 +111,7 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, } // Set up SPI clocks on SERCOM. - samd_peripheral_sercom_clock_init(sercom, sercom_index); + samd_peripherals_sercom_clock_init(sercom, sercom_index); if (spi_m_sync_init(&self->spi_desc, sercom) != ERR_NONE) { mp_raise_OSError(MP_EIO); @@ -132,7 +122,7 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, // Always start at 250khz which is what SD cards need. They are sensitive to // SPI bus noise before they are put into SPI mode. - uint8_t baud_value = baudrate_to_baud_reg_value(250000); + uint8_t baud_value = samd_peripherals_baudrate_to_baud_reg_value(250000); if (baud_value == 0) { mp_raise_RuntimeError("SPI initial baudrate out of range."); } @@ -142,6 +132,7 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, mp_raise_OSError(MP_EIO); } + gpio_set_pin_direction(clock->pin, GPIO_DIRECTION_OUT); gpio_set_pin_pull_mode(clock->pin, GPIO_PULL_OFF); gpio_set_pin_function(clock->pin, clock_pinmux); claim_pin(clock); @@ -150,6 +141,7 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, if (mosi_none) { self->MOSI_pin = NO_PIN; } else { + gpio_set_pin_direction(mosi->pin, GPIO_DIRECTION_OUT); gpio_set_pin_pull_mode(mosi->pin, GPIO_PULL_OFF); gpio_set_pin_function(mosi->pin, mosi_pinmux); self->MOSI_pin = mosi->pin; @@ -159,6 +151,7 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, if (miso_none) { self->MISO_pin = NO_PIN; } else { + gpio_set_pin_direction(miso->pin, GPIO_DIRECTION_IN); gpio_set_pin_pull_mode(miso->pin, GPIO_PULL_OFF); gpio_set_pin_function(miso->pin, miso_pinmux); self->MISO_pin = miso->pin; @@ -186,7 +179,7 @@ void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { bool common_hal_busio_spi_configure(busio_spi_obj_t *self, uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits) { - uint8_t baud_reg_value = baudrate_to_baud_reg_value(baudrate); + uint8_t baud_reg_value = samd_peripherals_baudrate_to_baud_reg_value(baudrate); if (baud_reg_value == 0) { mp_raise_ValueError("baudrate out of range"); } @@ -248,7 +241,7 @@ bool common_hal_busio_spi_write(busio_spi_obj_t *self, spi_m_sync_get_io_descriptor(&self->spi_desc, &spi_io); status = spi_io->write(spi_io, data, len); // } - return status > 0; // Status is number of chars read or an error code < 0. + return status >= 0; // Status is number of chars read or an error code < 0. } bool common_hal_busio_spi_read(busio_spi_obj_t *self, @@ -267,5 +260,5 @@ bool common_hal_busio_spi_read(busio_spi_obj_t *self, status = spi_io->read(spi_io, data, len); // } - return status > 0; // Status is number of chars read or an error code < 0. + return status >= 0; // Status is number of chars read or an error code < 0. } diff --git a/ports/atmel-samd/common-hal/storage/__init__.c b/ports/atmel-samd/common-hal/storage/__init__.c index 184184b8b3..c4cb831427 100644 --- a/ports/atmel-samd/common-hal/storage/__init__.c +++ b/ports/atmel-samd/common-hal/storage/__init__.c @@ -30,6 +30,7 @@ #include "py/mperrno.h" #include "py/runtime.h" #include "shared-bindings/storage/__init__.h" +#include "usb.h" extern volatile bool mp_msc_enabled; @@ -38,7 +39,9 @@ void common_hal_storage_remount(const char* mount_path, bool readonly) { mp_raise_OSError(MP_EINVAL); } - if (mp_msc_enabled) { + // TODO(dhalbert): is this is a good enough check? It checks for + // CDC enabled. There is no "MSC enabled" check. + if (usb_connected()) { mp_raise_RuntimeError("Cannot remount '/' when USB is active."); } diff --git a/ports/atmel-samd/mpconfigport.h b/ports/atmel-samd/mpconfigport.h index 1db2d5a67a..f15efca962 100644 --- a/ports/atmel-samd/mpconfigport.h +++ b/ports/atmel-samd/mpconfigport.h @@ -215,7 +215,6 @@ extern const struct _mp_obj_module_t usb_hid_module; // { MP_OBJ_NEW_QSTR(MP_QSTR_gamepad),(mp_obj_t)&gamepad_module }, // { MP_OBJ_NEW_QSTR(MP_QSTR_usb_hid),(mp_obj_t)&usb_hid_module }, -// { MP_OBJ_NEW_QSTR(MP_QSTR_storage), (mp_obj_t)&storage_module }, #define MICROPY_PORT_BUILTIN_MODULES \ @@ -226,6 +225,7 @@ extern const struct _mp_obj_module_t usb_hid_module; { MP_OBJ_NEW_QSTR(MP_QSTR_neopixel_write),(mp_obj_t)&neopixel_write_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_os), (mp_obj_t)&os_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_random), (mp_obj_t)&random_module }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_storage), (mp_obj_t)&storage_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_struct), (mp_obj_t)&struct_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_supervisor), (mp_obj_t)&supervisor_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_math), (mp_obj_t)&math_module }, \ diff --git a/ports/atmel-samd/peripherals.c b/ports/atmel-samd/peripherals.c new file mode 100644 index 0000000000..6de813cca7 --- /dev/null +++ b/ports/atmel-samd/peripherals.c @@ -0,0 +1,43 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Dan Halbert 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 "peripherals.h" + +#include "hpl_sercom_config.h" + +// Routines that are the same across all samd variants. + + +// Convert frequency to clock-speed-dependent value. Return 0 if out of range. +uint8_t samd_peripherals_baudrate_to_baud_reg_value(const uint32_t baudrate) { + uint32_t baud_reg_value = (uint32_t) (((float) PROTOTYPE_SERCOM_SPI_M_SYNC_CLOCK_FREQUENCY / + (2 * baudrate)) + 0.5f); + if (baud_reg_value > 0xff) { + return 0; + } + return (uint8_t) baud_reg_value; +} + diff --git a/ports/atmel-samd/peripherals.h b/ports/atmel-samd/peripherals.h index 03828ae748..95bc1ec53f 100644 --- a/ports/atmel-samd/peripherals.h +++ b/ports/atmel-samd/peripherals.h @@ -27,8 +27,13 @@ #ifndef MICROPY_INCLUDED_ATMEL_SAMD_PERIPHERALS_H #define MICROPY_INCLUDED_ATMEL_SAMD_PERIPHERALS_H +#include + #include "mpconfigport.h" +// Routines common across chip families. +uint8_t samd_peripherals_baudrate_to_baud_reg_value(const uint32_t baudrate); + #ifdef SAMD21 #include "samd21_peripherals.h" #endif diff --git a/ports/atmel-samd/samd21_peripherals.c b/ports/atmel-samd/samd21_peripherals.c index 0ddc3be528..484c787cb0 100644 --- a/ports/atmel-samd/samd21_peripherals.c +++ b/ports/atmel-samd/samd21_peripherals.c @@ -58,7 +58,7 @@ static const uint8_t SERCOMx_GCLK_ID_SLOW[] = { // Clock initialization as done in Atmel START. -void samd_peripheral_sercom_clock_init(Sercom* sercom, uint8_t sercom_index) { +void samd_peripherals_sercom_clock_init(Sercom* sercom, uint8_t sercom_index) { _pm_enable_bus_clock(PM_BUS_APBC, sercom); _gclk_enable_channel(SERCOMx_GCLK_ID_CORE[sercom_index], GCLK_CLKCTRL_GEN_GCLK0_Val); _gclk_enable_channel(SERCOMx_GCLK_ID_SLOW[sercom_index], GCLK_CLKCTRL_GEN_GCLK3_Val); @@ -70,7 +70,7 @@ void samd_peripheral_sercom_clock_init(Sercom* sercom, uint8_t sercom_index) { // <0x1=>PAD[2,3]_DO_SCK // <0x2=>PAD[3,1]_DO_SCK // <0x3=>PAD[0,3]_DO_SCK -uint8_t samd_peripheral_get_spi_dopo(uint8_t clock_pad, uint8_t mosi_pad) { +uint8_t samd_peripherals_get_spi_dopo(uint8_t clock_pad, uint8_t mosi_pad) { if (clock_pad == 1) { if (mosi_pad == 0) { return 0; @@ -87,6 +87,6 @@ uint8_t samd_peripheral_get_spi_dopo(uint8_t clock_pad, uint8_t mosi_pad) { return 255; } -bool samd_peripheral_valid_spi_clock_pad(uint8_t clock_pad) { +bool samd_peripherals_valid_spi_clock_pad(uint8_t clock_pad) { return clock_pad == 1 || clock_pad == 3; } diff --git a/ports/atmel-samd/samd21_peripherals.h b/ports/atmel-samd/samd21_peripherals.h index a317d3ddc7..7a44778916 100644 --- a/ports/atmel-samd/samd21_peripherals.h +++ b/ports/atmel-samd/samd21_peripherals.h @@ -29,8 +29,8 @@ #include "include/sam.h" -void samd_peripheral_sercom_clock_init(Sercom* sercom, uint8_t sercom_index); -uint8_t samd_peripheral_get_spi_dopo(uint8_t clock_pad, uint8_t mosi_pad); -bool samd_peripheral_valid_spi_clock_pad(uint8_t clock_pad); +void samd_peripherals_sercom_clock_init(Sercom* sercom, uint8_t sercom_index); +uint8_t samd_peripherals_get_spi_dopo(uint8_t clock_pad, uint8_t mosi_pad); +bool samd_peripherals_valid_spi_clock_pad(uint8_t clock_pad); #endif // MICROPY_INCLUDED_ATMEL_SAMD_SAMD21_PERIPHERALS_H diff --git a/ports/atmel-samd/samd51_peripherals.c b/ports/atmel-samd/samd51_peripherals.c index f1528f3c1c..e06f7f9df0 100644 --- a/ports/atmel-samd/samd51_peripherals.c +++ b/ports/atmel-samd/samd51_peripherals.c @@ -62,7 +62,7 @@ static const uint8_t SERCOMx_GCLK_ID_SLOW[] = { // Clock initialization as done in Atmel START. -void samd_peripheral_sercom_clock_init(Sercom* sercom, uint8_t sercom_index) { +void samd_peripherals_sercom_clock_init(Sercom* sercom, uint8_t sercom_index) { hri_gclk_write_PCHCTRL_reg(GCLK, SERCOMx_GCLK_ID_CORE[sercom_index], GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos)); @@ -113,7 +113,7 @@ void samd_peripheral_sercom_clock_init(Sercom* sercom, uint8_t sercom_index) { // <0x1=>PAD[2,3]_DO_SCK [RESERVED] // <0x2=>PAD[3,1]_DO_SCK // <0x3=>PAD[0,3]_DO_SCK [RESERVED] -uint8_t samd_peripheral_get_spi_dopo(uint8_t clock_pad, uint8_t mosi_pad) { +uint8_t samd_peripherals_get_spi_dopo(uint8_t clock_pad, uint8_t mosi_pad) { if (clock_pad != 1) { return 255; } @@ -126,6 +126,6 @@ uint8_t samd_peripheral_get_spi_dopo(uint8_t clock_pad, uint8_t mosi_pad) { return 255; } -bool samd_peripheral_valid_spi_clock_pad(uint8_t clock_pad) { +bool samd_peripherals_valid_spi_clock_pad(uint8_t clock_pad) { return clock_pad == 1; } diff --git a/ports/atmel-samd/samd51_peripherals.h b/ports/atmel-samd/samd51_peripherals.h index 3e8097c871..c47e3bee76 100644 --- a/ports/atmel-samd/samd51_peripherals.h +++ b/ports/atmel-samd/samd51_peripherals.h @@ -29,9 +29,9 @@ #include "sam.h" -void samd_peripheral_sercom_clock_init(Sercom* sercom, uint8_t sercom_index); -uint8_t samd_peripheral_get_spi_dopo(uint8_t clock_pad, uint8_t mosi_pad); -bool samd_peripheral_valid_spi_clock_pad(uint8_t clock_pad); +void samd_peripherals_sercom_clock_init(Sercom* sercom, uint8_t sercom_index); +uint8_t samd_peripherals_get_spi_dopo(uint8_t clock_pad, uint8_t mosi_pad); +bool samd_peripherals_valid_spi_clock_pad(uint8_t clock_pad); #endif // MICROPY_INCLUDED_ATMEL_SAMD_SAMD51_PERIPHERALS_H diff --git a/ports/atmel-samd/samd_peripherals.h b/ports/atmel-samd/samd_peripherals.h new file mode 100644 index 0000000000..7a44778916 --- /dev/null +++ b/ports/atmel-samd/samd_peripherals.h @@ -0,0 +1,36 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 by Dan Halbert 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_SAMD21_PERIPHERALS_H +#define MICROPY_INCLUDED_ATMEL_SAMD_SAMD21_PERIPHERALS_H + +#include "include/sam.h" + +void samd_peripherals_sercom_clock_init(Sercom* sercom, uint8_t sercom_index); +uint8_t samd_peripherals_get_spi_dopo(uint8_t clock_pad, uint8_t mosi_pad); +bool samd_peripherals_valid_spi_clock_pad(uint8_t clock_pad); + +#endif // MICROPY_INCLUDED_ATMEL_SAMD_SAMD21_PERIPHERALS_H diff --git a/ports/atmel-samd/shared_dma.c b/ports/atmel-samd/shared_dma.c index a2914c3cf2..f8cef0992c 100644 --- a/ports/atmel-samd/shared_dma.c +++ b/ports/atmel-samd/shared_dma.c @@ -43,28 +43,33 @@ void init_shared_dma(void) { struct dma_resource_config config; dma_get_config_defaults(&config); - // This allocates the lowest channel first so make sure the audio is first - // so it gets the highest priority. - config.peripheral_trigger = DAC_DMAC_ID_EMPTY; - config.trigger_action = DMA_TRIGGER_ACTION_BEAT; - config.event_config.input_action = DMA_EVENT_INPUT_TRIG; - config.event_config.event_output_enable = true; - dma_allocate(&audio_dma, &config); + // See asf4_conf/hpl_dmac_config.h for initial settings for DMA channels + // DMA Channel 0: audio, highest priority, + // normal transfer on input, DAC 0 empty is trigger source, trigger on each beat, beat is one byte + // output enable true. + // asf3 settings: + //config.peripheral_trigger = DAC_DMAC_ID_EMPTY; + //config.trigger_action = DMA_TRIGGER_ACTION_BEAT; + //config.event_config.input_action = DMA_EVENT_INPUT_TRIG; + //config.event_config.event_output_enable = true; + // Turn on the transfer complete interrupt so that the job_status changes to done. g_chan_interrupt_flag[audio_dma.channel_id] |= (1UL << DMA_CALLBACK_TRANSFER_DONE); // Prioritize the RX channel over the TX channel because TX can cause an RX // overflow. - dma_get_config_defaults(&config); - config.trigger_action = DMA_TRIGGER_ACTION_BEAT; - config.event_config.input_action = DMA_EVENT_INPUT_TRIG; + + // DMA Channel 1: rx channel, + // normal transfer on input, trigger on each beat, beat is one byte + //config.trigger_action = DMA_TRIGGER_ACTION_BEAT; + //config.event_config.input_action = DMA_EVENT_INPUT_TRIG; dma_allocate(&general_dma_rx, &config); g_chan_interrupt_flag[general_dma_rx.channel_id] |= (1UL << DMA_CALLBACK_TRANSFER_DONE); - dma_get_config_defaults(&config); - config.trigger_action = DMA_TRIGGER_ACTION_BEAT; - config.event_config.input_action = DMA_EVENT_INPUT_TRIG; - dma_allocate(&general_dma_tx, &config); + // DMA Channel 1: rx channel, + // normal transfer on input, trigger on each beat, beat is one byte + //config.trigger_action = DMA_TRIGGER_ACTION_BEAT; + //config.event_config.input_action = DMA_EVENT_INPUT_TRIG; g_chan_interrupt_flag[general_dma_tx.channel_id] |= (1UL << DMA_CALLBACK_TRANSFER_DONE); // Be sneaky and reuse the active descriptor memory. diff --git a/ports/atmel-samd/spi_flash.c b/ports/atmel-samd/spi_flash.c index 8191887e4b..a64c0ed331 100644 --- a/ports/atmel-samd/spi_flash.c +++ b/ports/atmel-samd/spi_flash.c @@ -28,20 +28,19 @@ #include #include -<<<<<<< HEAD -#include "asf/sam0/drivers/sercom/spi/spi.h" - #include "extmod/vfs.h" #include "extmod/vfs_fat.h" -======= ->>>>>>> WIP #include "py/gc.h" #include "py/obj.h" #include "py/runtime.h" #include "lib/oofatfs/ff.h" +#include "peripherals.h" +#include "supervisor/shared/rgb_led_status.h" -#include "rgb_led_status.h" -#include "shared_dma.h" +//#include "shared_dma.h" + +#include "hal_gpio.h" +#include "hal_spi_m_sync.h" #define SPI_FLASH_PART1_START_BLOCK (0x1) @@ -60,7 +59,7 @@ static bool spi_flash_is_initialised = false; -struct spi_module spi_flash_instance; +struct spi_m_sync_descriptor spi_flash_desc; // The currently cached sector in the cache, ram or flash based. static uint32_t current_sector; @@ -74,35 +73,37 @@ static uint32_t dirty_mask; // Enable the flash over SPI. static void flash_enable(void) { - port_pin_set_output_level(SPI_FLASH_CS, false); + gpio_set_pin_level(SPI_FLASH_CS_PIN, false); } // Disable the flash over SPI. static void flash_disable(void) { - port_pin_set_output_level(SPI_FLASH_CS, true); + 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 status_request[2] = {CMD_READ_STATUS, 0x00}; - uint8_t response[2] = {0x00, 0x01}; - enum status_code status = STATUS_OK; + 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; // Both the write enable and write in progress bits should be low. - while (status == STATUS_OK && (response[1] & 0x3) != 0) { + do { flash_enable(); - status = spi_transceive_buffer_wait(&spi_flash_instance, status_request, response, 2); + status = spi_m_sync_transfer(&spi_flash_desc, &read_status_xfer); flash_disable(); - } - return status == STATUS_OK; + } while (status >= 0 && (read_status_response[1] & 0x3) != 0); + return status >= 0; // status is number of chars read or a negative error code. } // Turn on the write enable bit so we can program and erase the flash. static bool write_enable(void) { flash_enable(); - uint8_t command = CMD_ENABLE_WRITE; - enum status_code status = spi_write_buffer_wait(&spi_flash_instance, &command, 1); + 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 == STATUS_OK; + 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. @@ -120,17 +121,19 @@ static bool read_flash(uint32_t address, uint8_t* data, uint32_t data_length) { if (!wait_for_flash_ready()) { return false; } - enum status_code status; // We can read as much as we want sequentially. - uint8_t read_request[4] = {CMD_READ_DATA, 0x00, 0x00, 0x00}; - address_to_bytes(address, read_request + 1); + 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(); - status = spi_write_buffer_wait(&spi_flash_instance, read_request, 4); - if (status == STATUS_OK) { - status = shared_dma_read(spi_flash_instance.hw, data, data_length, 0x00); + 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 == STATUS_OK; + return status >= 0; } // Writes data_length's worth of bytes starting at address from data. Assumes @@ -159,26 +162,29 @@ static bool write_flash(uint32_t address, const uint8_t* data, uint32_t data_len if (!wait_for_flash_ready() || !write_enable()) { return false; } - enum status_code status; + 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_instance, protect_check, 5); + // status = spi_write_buffer_wait(&spi_flash_desc, protect_check, 5); // flash_disable(); #endif flash_enable(); - uint8_t command[4] = {CMD_PAGE_PROGRAM, 0x00, 0x00, 0x00}; - address_to_bytes(address + bytes_written, command + 1); - status = spi_write_buffer_wait(&spi_flash_instance, command, 4); - if (status == STATUS_OK) { - status = shared_dma_write(spi_flash_instance.hw, data + bytes_written, SPI_FLASH_PAGE_SIZE); + 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 != STATUS_OK) { + if (status < 0) { return false; } } @@ -224,11 +230,11 @@ static bool erase_sector(uint32_t sector_address) { 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(); - enum status_code status = spi_write_buffer_wait(&spi_flash_instance, erase_request, 4); + int32_t status = spi_m_sync_transfer(&spi_flash_desc, &erase_xfer); flash_disable(); - return status == STATUS_OK; + return status >= 0; } // Sector is really 24 bits. @@ -247,82 +253,103 @@ static bool copy_block(uint32_t src_address, uint32_t dest_address) { } void spi_flash_init(void) { - if (!spi_flash_is_initialised) { - struct spi_config config_spi_master; - spi_get_config_defaults(&config_spi_master); - config_spi_master.mux_setting = SPI_FLASH_MUX_SETTING; - config_spi_master.pinmux_pad0 = SPI_FLASH_PAD0_PINMUX; - config_spi_master.pinmux_pad1 = SPI_FLASH_PAD1_PINMUX; - config_spi_master.pinmux_pad2 = SPI_FLASH_PAD2_PINMUX; - config_spi_master.pinmux_pad3 = SPI_FLASH_PAD3_PINMUX; - config_spi_master.mode_specific.master.baudrate = SPI_FLASH_BAUDRATE; - spi_init(&spi_flash_instance, SPI_FLASH_SERCOM, &config_spi_master); - spi_enable(&spi_flash_instance); - - // Manage chip select ourselves. - struct port_config pin_conf; - port_get_config_defaults(&pin_conf); - - pin_conf.direction = PORT_PIN_DIR_OUTPUT; - port_pin_set_config(SPI_FLASH_CS, &pin_conf); - flash_disable(); - - // Activity LED for flash writes. - #ifdef MICROPY_HW_LED_MSC - port_pin_set_config(MICROPY_HW_LED_MSC, &pin_conf); - port_pin_set_output_level(MICROPY_HW_LED_MSC, false); - #endif - - uint8_t jedec_id_request[4] = {CMD_READ_JEDEC_ID, 0x00, 0x00, 0x00}; - uint8_t response[4] = {0x00, 0x00, 0x00, 0x00}; - flash_enable(); - spi_transceive_buffer_wait(&spi_flash_instance, jedec_id_request, response, 4); - flash_disable(); - uint8_t manufacturer = response[1]; - if ((response[1] == SPI_FLASH_JEDEC_MANUFACTURER - #ifdef SPI_FLASH_JEDEC_MANUFACTURER_2 - || response[1] == SPI_FLASH_JEDEC_MANUFACTURER_2 - #endif - ) && - response[2] == SPI_FLASH_JEDEC_MEMORY_TYPE && - response[3] == SPI_FLASH_JEDEC_CAPACITY) { - spi_flash_is_initialised = true; - } else { - // Unknown flash chip! - spi_flash_is_initialised = false; - return; - } - - if ((manufacturer == SPI_FLASH_JEDEC_MANUFACTURER && SPI_FLASH_SECTOR_PROTECTION) - #ifdef SPI_FLASH_JEDEC_MANUFACTURER_2 - || (manufacturer == SPI_FLASH_JEDEC_MANUFACTURER_2 && SPI_FLASH_SECTOR_PROTECTION_2) - #endif - ) { - write_enable(); - - // Turn off sector protection - uint8_t disable_protect_request[2] = {CMD_WRITE_STATUS_BYTE1, 0x00}; - uint8_t disable_protect_response[2] = {0x00, 0x00}; - flash_enable(); - spi_transceive_buffer_wait(&spi_flash_instance, disable_protect_request, disable_protect_response, 2); - flash_disable(); - } - - // Turn off writes in case this is a microcontroller only reset. - uint8_t disable_write_request[1] = {CMD_DISABLE_WRITE}; - uint8_t disable_response[1] = {0x00}; - flash_enable(); - spi_transceive_buffer_wait(&spi_flash_instance, disable_write_request, disable_response, 1); - flash_disable(); - - wait_for_flash_ready(); - - current_sector = NO_SECTOR_LOADED; - dirty_mask = 0; - MP_STATE_VM(flash_ram_cache) = NULL; - - spi_flash_is_initialised = true; + 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_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); + + // Activity LED for flash writes. +#ifdef MICROPY_HW_LED_MSC + gpio_set_pin_function(SPI_FLASH_CS_PIN, GPIO_PIN_FUNCTION_OFF); + gpio_set_pin_direction(MICROPY_HW_LED_MSC, GPIO_DIRECTION_OUT); + // There's already a pull-up on the board. + 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 +#ifdef SPI_FLASH_JEDEC_MANUFACTURER_2 + || jedec_id_response[1] == SPI_FLASH_JEDEC_MANUFACTURER_2 +#endif + ) && + jedec_id_response[2] == SPI_FLASH_JEDEC_MEMORY_TYPE && + jedec_id_response[3] == SPI_FLASH_JEDEC_CAPACITY) { + spi_flash_is_initialised = true; + } else { + // Unknown flash chip! + spi_flash_is_initialised = false; + return; + } + + if ((manufacturer == SPI_FLASH_JEDEC_MANUFACTURER && SPI_FLASH_SECTOR_PROTECTION) +#ifdef SPI_FLASH_JEDEC_MANUFACTURER_2 + || (manufacturer == SPI_FLASH_JEDEC_MANUFACTURER_2 && SPI_FLASH_SECTOR_PROTECTION_2) +#endif + ) { + 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(); + } + + // 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(); + + wait_for_flash_ready(); + + current_sector = NO_SECTOR_LOADED; + dirty_mask = 0; + MP_STATE_VM(flash_ram_cache) = NULL; + + spi_flash_is_initialised = true; } // The size of each individual block. diff --git a/ports/atmel-samd/usb_mass_storage.c b/ports/atmel-samd/usb_mass_storage.c index 85c662e774..818736a782 100644 --- a/ports/atmel-samd/usb_mass_storage.c +++ b/ports/atmel-samd/usb_mass_storage.c @@ -271,7 +271,11 @@ int32_t usb_msc_xfer_done(uint8_t lun) { return ERR_NONE; } -// The start_read callback begins a read transaction which we accept but delay our response until the "main thread" calls usb_msc_background. Once it does, we read immediately from the drive into our cache and trigger the USB DMA to output the sector. Once the sector is transmitted, xfer_done will be called. +// The start_read callback begins a read transaction which we accept +// but delay our response until the "main thread" calls +// usb_msc_background. Once it does, we read immediately from the +// drive into our cache and trigger the USB DMA to output the +// sector. Once the sector is transmitted, xfer_done will be called. void usb_msc_background(void) { if (active_read && !usb_busy) { if (active_nblocks == 0) { @@ -290,7 +294,7 @@ void usb_msc_background(void) { fs_user_mount_t * vfs = get_vfs(active_lun); disk_write(vfs, sector_buffer, active_addr, 1); // Since by getting here we assume the mount is read-only to - // MicroPython lets update the cached FatFs sector if its the one + // MicroPython let's update the cached FatFs sector if it's the one // we just wrote. #if _MAX_SS != _MIN_SS if (vfs->ssize == FILESYSTEM_BLOCK_SIZE) { diff --git a/shared-bindings/storage/__init__.c b/shared-bindings/storage/__init__.c index 37d7b6976d..9c186a0519 100644 --- a/shared-bindings/storage/__init__.c +++ b/shared-bindings/storage/__init__.c @@ -38,7 +38,7 @@ //| //| .. module:: storage //| :synopsis: storage management -//| :platform: SAMD21 +//| :platform: SAMD21, SAMD51 //| //| The `storage` provides storage management functionality such as mounting and //| unmounting which is typically handled by the operating system hosting Python.