mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-12-25 22:43:50 +01:00
Merge pull request #15361 from benpicco/cpu/same54-qspi_spi
cpu/sam0_common: SPI: add support for QSPI in SPI mode
This commit is contained in:
commit
9a0243e062
@ -5,3 +5,8 @@ endif
|
||||
ifneq (,$(filter eui_provider,$(USEMODULE)))
|
||||
USEMODULE += at24mac
|
||||
endif
|
||||
|
||||
ifneq (,$(filter mtd,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_spi_on_qspi
|
||||
USEMODULE += mtd_spi_nor
|
||||
endif
|
||||
|
||||
@ -20,6 +20,38 @@
|
||||
|
||||
#include "board.h"
|
||||
#include "periph/gpio.h"
|
||||
#include "mtd_spi_nor.h"
|
||||
#include "timex.h"
|
||||
|
||||
#ifdef MODULE_MTD
|
||||
/* N25Q256A */
|
||||
static const mtd_spi_nor_params_t _same54_nor_params = {
|
||||
.opcode = &mtd_spi_nor_opcode_default,
|
||||
.wait_chip_erase = 240 * US_PER_SEC,
|
||||
.wait_64k_erase = 700 * US_PER_MS,
|
||||
.wait_sector_erase = 250 * US_PER_MS,
|
||||
.wait_chip_wake_up = 1 * US_PER_MS,
|
||||
.clk = MHZ(54),
|
||||
.flag = SPI_NOR_F_SECT_4K | SPI_NOR_F_SECT_64K,
|
||||
.spi = SPI_DEV(2),
|
||||
.mode = SPI_MODE_0,
|
||||
.cs = SAM0_QSPI_PIN_CS,
|
||||
.wp = SAM0_QSPI_PIN_DATA_2,
|
||||
.hold = SAM0_QSPI_PIN_DATA_3,
|
||||
.addr_width = 4,
|
||||
};
|
||||
|
||||
static mtd_spi_nor_t same54_nor_dev = {
|
||||
.base = {
|
||||
.driver = &mtd_spi_nor_driver,
|
||||
.page_size = 256,
|
||||
.pages_per_sector = 16,
|
||||
},
|
||||
.params = &_same54_nor_params,
|
||||
};
|
||||
|
||||
mtd_dev_t *mtd0 = (mtd_dev_t *)&same54_nor_dev;
|
||||
#endif /* MODULE_MTD */
|
||||
|
||||
void board_init(void)
|
||||
{
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
|
||||
#include "cpu.h"
|
||||
#include "at24mac.h"
|
||||
#include "mtd.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -83,6 +84,14 @@ static inline int _at24mac_get_eui48(const void *arg, eui48_t *addr)
|
||||
#define BTN0_MODE GPIO_IN_PU
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name MTD configuration
|
||||
* @{
|
||||
*/
|
||||
extern mtd_dev_t *mtd0;
|
||||
#define MTD_0 mtd0
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Xtimer configuration
|
||||
* @{
|
||||
|
||||
@ -251,7 +251,25 @@ static const spi_conf_t spi_config[] = {
|
||||
.tx_trigger = SERCOM6_DMAC_ID_TX,
|
||||
.rx_trigger = SERCOM6_DMAC_ID_RX,
|
||||
#endif
|
||||
}
|
||||
},
|
||||
#ifdef MODULE_PERIPH_SPI_ON_QSPI
|
||||
{ /* QSPI in SPI mode */
|
||||
.dev = QSPI,
|
||||
.miso_pin = SAM0_QSPI_PIN_DATA_1,
|
||||
.mosi_pin = SAM0_QSPI_PIN_DATA_0,
|
||||
.clk_pin = SAM0_QSPI_PIN_CLK,
|
||||
.miso_mux = SAM0_QSPI_MUX,
|
||||
.mosi_mux = SAM0_QSPI_MUX,
|
||||
.clk_mux = SAM0_QSPI_MUX,
|
||||
.miso_pad = SPI_PAD_MISO_0, /* unused */
|
||||
.mosi_pad = SPI_PAD_MOSI_0_SCK_1, /* unused */
|
||||
.gclk_src = SAM0_GCLK_MAIN, /* unused */
|
||||
#ifdef MODULE_PERIPH_DMA
|
||||
.tx_trigger = QSPI_DMAC_ID_TX,
|
||||
.rx_trigger = QSPI_DMAC_ID_RX,
|
||||
#endif
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
#define SPI_NUMOF ARRAY_SIZE(spi_config)
|
||||
|
||||
@ -383,7 +383,7 @@ typedef enum {
|
||||
* @brief SPI device configuration
|
||||
*/
|
||||
typedef struct {
|
||||
SercomSpi *dev; /**< pointer to the used SPI device */
|
||||
void *dev; /**< pointer to the used SPI device */
|
||||
gpio_t miso_pin; /**< used MISO pin */
|
||||
gpio_t mosi_pin; /**< used MOSI pin */
|
||||
gpio_t clk_pin; /**< used CLK pin */
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
|
||||
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
||||
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
@ -56,17 +57,79 @@ static DmacDescriptor DMA_DESCRIPTOR_ATTRS rx_desc[SPI_NUMOF];
|
||||
*/
|
||||
static inline SercomSpi *dev(spi_t bus)
|
||||
{
|
||||
return spi_config[bus].dev;
|
||||
return (SercomSpi *)spi_config[bus].dev;
|
||||
}
|
||||
|
||||
static inline bool _is_qspi(spi_t bus)
|
||||
{
|
||||
#ifdef MODULE_PERIPH_SPI_ON_QSPI
|
||||
return (void*)spi_config[bus].dev == (void*)QSPI;
|
||||
#else
|
||||
(void)bus;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void _qspi_clk(unsigned on)
|
||||
{
|
||||
#ifdef QSPI
|
||||
/* enable/disable QSPI clock */
|
||||
MCLK->APBCMASK.bit.QSPI_ = on;
|
||||
#else
|
||||
(void)on;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void poweron(spi_t bus)
|
||||
{
|
||||
sercom_clk_en(dev(bus));
|
||||
if (_is_qspi(bus)) {
|
||||
_qspi_clk(1);
|
||||
} else {
|
||||
sercom_clk_en(dev(bus));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void poweroff(spi_t bus)
|
||||
{
|
||||
sercom_clk_dis(dev(bus));
|
||||
if (_is_qspi(bus)) {
|
||||
_qspi_clk(0);
|
||||
} else {
|
||||
sercom_clk_dis(dev(bus));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void _reset(SercomSpi *dev)
|
||||
{
|
||||
dev->CTRLA.reg |= SERCOM_SPI_CTRLA_SWRST;
|
||||
while (dev->CTRLA.reg & SERCOM_SPI_CTRLA_SWRST) {}
|
||||
|
||||
#ifdef SERCOM_SPI_STATUS_SYNCBUSY
|
||||
while (dev->STATUS.bit.SYNCBUSY) {}
|
||||
#else
|
||||
while (dev->SYNCBUSY.bit.SWRST) {}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void _disable(SercomSpi *dev)
|
||||
{
|
||||
dev->CTRLA.reg = 0;
|
||||
|
||||
#ifdef SERCOM_SPI_STATUS_SYNCBUSY
|
||||
while (dev->STATUS.bit.SYNCBUSY) {}
|
||||
#else
|
||||
while (dev->SYNCBUSY.reg) {}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void _enable(SercomSpi *dev)
|
||||
{
|
||||
dev->CTRLA.bit.ENABLE = 1;
|
||||
|
||||
#ifdef SERCOM_SPI_STATUS_SYNCBUSY
|
||||
while (dev->STATUS.bit.SYNCBUSY) {}
|
||||
#else
|
||||
while (dev->SYNCBUSY.reg) {}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool _use_dma(spi_t bus)
|
||||
@ -80,6 +143,186 @@ static inline bool _use_dma(spi_t bus)
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void _init_dma(spi_t bus, const volatile void *reg_rx, volatile void *reg_tx)
|
||||
{
|
||||
if (!_use_dma(bus)) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef MODULE_PERIPH_DMA
|
||||
_dma_state[bus].rx_dma = dma_acquire_channel();
|
||||
_dma_state[bus].tx_dma = dma_acquire_channel();
|
||||
|
||||
dma_setup(_dma_state[bus].tx_dma,
|
||||
spi_config[bus].tx_trigger, 0, false);
|
||||
dma_setup(_dma_state[bus].rx_dma,
|
||||
spi_config[bus].rx_trigger, 1, true);
|
||||
|
||||
dma_prepare(_dma_state[bus].rx_dma, DMAC_BTCTRL_BEATSIZE_BYTE_Val,
|
||||
(void*)reg_rx, NULL, 1, 0);
|
||||
dma_prepare(_dma_state[bus].tx_dma, DMAC_BTCTRL_BEATSIZE_BYTE_Val,
|
||||
NULL, (void*)reg_tx, 0, 0);
|
||||
#else
|
||||
(void)reg_rx;
|
||||
(void)reg_tx;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief QSPI peripheral in SPI mode
|
||||
* @{
|
||||
*/
|
||||
#ifdef QSPI
|
||||
static void _init_qspi(spi_t bus)
|
||||
{
|
||||
/* reset the peripheral */
|
||||
QSPI->CTRLA.bit.SWRST = 1;
|
||||
|
||||
QSPI->CTRLB.reg = QSPI_CTRLB_MODE_SPI
|
||||
| QSPI_CTRLB_CSMODE_LASTXFER
|
||||
| QSPI_CTRLB_DATALEN_8BITS;
|
||||
|
||||
/* set up DMA channels */
|
||||
_init_dma(bus, &QSPI->RXDATA.reg, &QSPI->TXDATA.reg);
|
||||
}
|
||||
|
||||
static void _qspi_acquire(spi_mode_t mode, spi_clk_t clk)
|
||||
{
|
||||
/* datasheet says SCK = MCK / (BAUD + 1) */
|
||||
/* but BAUD = 0 does not work, assume SCK = MCK / BAUD */
|
||||
uint32_t baud = CLOCK_CORECLOCK > (2 * clk)
|
||||
? (CLOCK_CORECLOCK + clk - 1) / clk
|
||||
: 1;
|
||||
|
||||
/* bit order is reversed from SERCOM SPI */
|
||||
uint32_t _mode = (mode >> 1)
|
||||
| (mode << 1);
|
||||
_mode &= 0x3;
|
||||
|
||||
QSPI->CTRLA.bit.ENABLE = 1;
|
||||
QSPI->BAUD.reg = QSPI_BAUD_BAUD(baud) | _mode;
|
||||
}
|
||||
|
||||
static inline void _qspi_release(void)
|
||||
{
|
||||
QSPI->CTRLA.bit.ENABLE = 0;
|
||||
}
|
||||
|
||||
static void _qspi_blocking_transfer(const void *out, void *in, size_t len)
|
||||
{
|
||||
const uint8_t *out_buf = out;
|
||||
uint8_t *in_buf = in;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
uint8_t tmp = out_buf ? out_buf[i] : 0;
|
||||
|
||||
/* transmit byte on MOSI */
|
||||
QSPI->TXDATA.reg = tmp;
|
||||
|
||||
/* wait until byte has been sampled on MISO */
|
||||
while (QSPI->INTFLAG.bit.RXC == 0) {}
|
||||
|
||||
/* consume the byte */
|
||||
tmp = QSPI->RXDATA.reg;
|
||||
|
||||
if (in_buf) {
|
||||
in_buf[i] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else /* !QSPI */
|
||||
void _init_qspi(spi_t bus);
|
||||
void _qspi_acquire(spi_mode_t mode, spi_clk_t clk);
|
||||
void _qspi_release(void);
|
||||
void _qspi_blocking_transfer(const void *out, void *in, size_t len);
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief SERCOM peripheral in SPI mode
|
||||
* @{
|
||||
*/
|
||||
static void _init_spi(spi_t bus, SercomSpi *dev)
|
||||
{
|
||||
/* reset all device configuration */
|
||||
_reset(dev);
|
||||
|
||||
/* configure base clock */
|
||||
sercom_set_gen(dev, spi_config[bus].gclk_src);
|
||||
|
||||
/* enable receiver and configure character size to 8-bit
|
||||
* no synchronization needed, as SERCOM device is not enabled */
|
||||
dev->CTRLB.reg = SERCOM_SPI_CTRLB_CHSIZE(0) | SERCOM_SPI_CTRLB_RXEN;
|
||||
|
||||
/* set up DMA channels */
|
||||
_init_dma(bus, &dev->DATA.reg, &dev->DATA.reg);
|
||||
}
|
||||
|
||||
static void _spi_acquire(spi_t bus, spi_mode_t mode, spi_clk_t clk)
|
||||
{
|
||||
/* configure bus clock, in synchronous mode its calculated from
|
||||
* BAUD.reg = (f_ref / (2 * f_bus) - 1)
|
||||
* with f_ref := CLOCK_CORECLOCK as defined by the board
|
||||
* to mitigate the rounding error due to integer arithmetic, the
|
||||
* equation is modified to
|
||||
* BAUD.reg = ((f_ref + f_bus) / (2 * f_bus) - 1) */
|
||||
const uint8_t baud = ((sam0_gclk_freq(spi_config[bus].gclk_src) + clk) / (2 * clk) - 1);
|
||||
|
||||
/* configure device to be master and set mode and pads,
|
||||
*
|
||||
* NOTE: we could configure the pads already during spi_init, but for
|
||||
* efficiency reason we do that here, so we can do all in one single write
|
||||
* to the CTRLA register */
|
||||
const uint32_t ctrla = SERCOM_SPI_CTRLA_MODE(0x3) /* 0x3 -> master */
|
||||
| SERCOM_SPI_CTRLA_DOPO(spi_config[bus].mosi_pad)
|
||||
| SERCOM_SPI_CTRLA_DIPO(spi_config[bus].miso_pad)
|
||||
| (mode << SERCOM_SPI_CTRLA_CPHA_Pos);
|
||||
|
||||
/* first configuration or reconfiguration after altered device usage */
|
||||
if (dev(bus)->BAUD.reg != baud || dev(bus)->CTRLA.reg != ctrla) {
|
||||
/* disable the device */
|
||||
_disable(dev(bus));
|
||||
|
||||
dev(bus)->BAUD.reg = baud;
|
||||
dev(bus)->CTRLA.reg = ctrla;
|
||||
/* no synchronization needed here, the enable synchronization below
|
||||
* acts as a write-synchronization for both registers */
|
||||
}
|
||||
|
||||
/* finally enable the device */
|
||||
_enable(dev(bus));
|
||||
}
|
||||
|
||||
static inline void _spi_release(spi_t bus)
|
||||
{
|
||||
/* disable the device */
|
||||
_disable(dev(bus));
|
||||
}
|
||||
|
||||
static void _spi_blocking_transfer(spi_t bus, const void *out, void *in, size_t len)
|
||||
{
|
||||
const uint8_t *out_buf = out;
|
||||
uint8_t *in_buf = in;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
uint8_t tmp = (out_buf) ? out_buf[i] : 0;
|
||||
|
||||
/* transmit byte on MOSI */
|
||||
dev(bus)->DATA.reg = tmp;
|
||||
|
||||
/* wait until byte has been sampled on MISO */
|
||||
while (dev(bus)->INTFLAG.bit.RXC == 0) {}
|
||||
|
||||
/* consume the byte */
|
||||
tmp = dev(bus)->DATA.reg;
|
||||
|
||||
if (in_buf) {
|
||||
in_buf[i] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
/** @} */
|
||||
|
||||
void spi_init(spi_t bus)
|
||||
{
|
||||
/* make sure given bus is good */
|
||||
@ -94,35 +337,14 @@ void spi_init(spi_t bus)
|
||||
/* wake up device */
|
||||
poweron(bus);
|
||||
|
||||
/* reset all device configuration */
|
||||
dev(bus)->CTRLA.reg |= SERCOM_SPI_CTRLA_SWRST;
|
||||
while ((dev(bus)->CTRLA.reg & SERCOM_SPI_CTRLA_SWRST) ||
|
||||
(dev(bus)->SYNCBUSY.reg & SERCOM_SPI_SYNCBUSY_SWRST)) {}
|
||||
|
||||
/* configure base clock: using GLK GEN 0 */
|
||||
sercom_set_gen(dev(bus), spi_config[bus].gclk_src);
|
||||
|
||||
/* enable receiver and configure character size to 8-bit
|
||||
* no synchronization needed, as SERCOM device is not enabled */
|
||||
dev(bus)->CTRLB.reg = (SERCOM_SPI_CTRLB_CHSIZE(0) | SERCOM_SPI_CTRLB_RXEN);
|
||||
|
||||
#ifdef MODULE_PERIPH_DMA
|
||||
if (_use_dma(bus)) {
|
||||
_dma_state[bus].rx_dma = dma_acquire_channel();
|
||||
_dma_state[bus].tx_dma = dma_acquire_channel();
|
||||
dma_setup(_dma_state[bus].tx_dma,
|
||||
spi_config[bus].tx_trigger, 0, false);
|
||||
dma_setup(_dma_state[bus].rx_dma,
|
||||
spi_config[bus].rx_trigger, 1, true);
|
||||
dma_prepare(_dma_state[bus].rx_dma, DMAC_BTCTRL_BEATSIZE_BYTE_Val,
|
||||
(void*)&dev(bus)->DATA.reg, NULL, 1, 0);
|
||||
dma_prepare(_dma_state[bus].tx_dma, DMAC_BTCTRL_BEATSIZE_BYTE_Val,
|
||||
NULL, (void*)&dev(bus)->DATA.reg, 0, 0);
|
||||
if (_is_qspi(bus)) {
|
||||
_init_qspi(bus);
|
||||
} else {
|
||||
_init_spi(bus, dev(bus));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* put device back to sleep */
|
||||
poweroff(bus);
|
||||
|
||||
}
|
||||
|
||||
void spi_init_pins(spi_t bus)
|
||||
@ -150,46 +372,18 @@ int spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk)
|
||||
{
|
||||
(void)cs;
|
||||
|
||||
/* configure bus clock, in synchronous mode its calculated from
|
||||
* BAUD.reg = (f_ref / (2 * f_bus) - 1)
|
||||
* with f_ref := CLOCK_CORECLOCK as defined by the board
|
||||
* to mitigate the rounding error due to integer arithmetic, the
|
||||
* equation is modified to
|
||||
* BAUD.reg = ((f_ref + f_bus) / (2 * f_bus) - 1) */
|
||||
const uint8_t baud = ((sam0_gclk_freq(spi_config[bus].gclk_src) + clk) / (2 * clk) - 1);
|
||||
|
||||
/* configure device to be master and set mode and pads,
|
||||
*
|
||||
* NOTE: we could configure the pads already during spi_init, but for
|
||||
* efficiency reason we do that here, so we can do all in one single write
|
||||
* to the CTRLA register */
|
||||
const uint32_t ctrla = SERCOM_SPI_CTRLA_MODE(0x3) /* 0x3 -> master */
|
||||
| SERCOM_SPI_CTRLA_DOPO(spi_config[bus].mosi_pad)
|
||||
| SERCOM_SPI_CTRLA_DIPO(spi_config[bus].miso_pad)
|
||||
| (mode << SERCOM_SPI_CTRLA_CPHA_Pos);
|
||||
|
||||
/* get exclusive access to the device */
|
||||
mutex_lock(&locks[bus]);
|
||||
|
||||
/* power on the device */
|
||||
poweron(bus);
|
||||
|
||||
/* first configuration or reconfiguration after altered device usage */
|
||||
if (dev(bus)->BAUD.reg != baud || dev(bus)->CTRLA.reg != ctrla) {
|
||||
/* disable the device */
|
||||
dev(bus)->CTRLA.reg &= ~(SERCOM_SPI_CTRLA_ENABLE);
|
||||
while (dev(bus)->SYNCBUSY.reg & SERCOM_SPI_SYNCBUSY_ENABLE) {}
|
||||
|
||||
dev(bus)->BAUD.reg = baud;
|
||||
dev(bus)->CTRLA.reg = ctrla;
|
||||
/* no synchronization needed here, the enable synchronization below
|
||||
* acts as a write-synchronization for both registers */
|
||||
if (_is_qspi(bus)) {
|
||||
_qspi_acquire(mode, clk);
|
||||
} else {
|
||||
_spi_acquire(bus, mode, clk);
|
||||
}
|
||||
|
||||
/* finally enable the device */
|
||||
dev(bus)->CTRLA.reg |= SERCOM_SPI_CTRLA_ENABLE;
|
||||
while (dev(bus)->SYNCBUSY.reg & SERCOM_SPI_SYNCBUSY_ENABLE) {}
|
||||
|
||||
/* mux clk_pin to SPI peripheral */
|
||||
gpio_init_mux(spi_config[bus].clk_pin, spi_config[bus].clk_mux);
|
||||
|
||||
@ -202,9 +396,11 @@ void spi_release(spi_t bus)
|
||||
* and lead to unexpected current draw by SPI salves. */
|
||||
gpio_disable_mux(spi_config[bus].clk_pin);
|
||||
|
||||
/* disable the device */
|
||||
dev(bus)->CTRLA.reg &= ~(SERCOM_SPI_CTRLA_ENABLE);
|
||||
while (dev(bus)->SYNCBUSY.reg & SERCOM_SPI_SYNCBUSY_ENABLE) {}
|
||||
if (_is_qspi(bus)) {
|
||||
_qspi_release();
|
||||
} else {
|
||||
_spi_release(bus);
|
||||
}
|
||||
|
||||
/* power off the device */
|
||||
poweroff(bus);
|
||||
@ -215,17 +411,10 @@ void spi_release(spi_t bus)
|
||||
|
||||
static void _blocking_transfer(spi_t bus, const void *out, void *in, size_t len)
|
||||
{
|
||||
const uint8_t *out_buf = out;
|
||||
uint8_t *in_buf = in;
|
||||
|
||||
for (int i = 0; i < (int)len; i++) {
|
||||
uint8_t tmp = (out_buf) ? out_buf[i] : 0;
|
||||
dev(bus)->DATA.reg = tmp;
|
||||
while (!(dev(bus)->INTFLAG.reg & SERCOM_SPI_INTFLAG_RXC)) {}
|
||||
tmp = (uint8_t)dev(bus)->DATA.reg;
|
||||
if (in_buf) {
|
||||
in_buf[i] = tmp;
|
||||
}
|
||||
if (_is_qspi(bus)) {
|
||||
_qspi_blocking_transfer(out, in, len);
|
||||
} else {
|
||||
_spi_blocking_transfer(bus, out, in, len);
|
||||
}
|
||||
}
|
||||
|
||||
@ -273,6 +462,7 @@ static void _dma_transfer_regs(spi_t bus, uint8_t reg, const uint8_t *out,
|
||||
|
||||
_dma_execute(bus);
|
||||
}
|
||||
|
||||
void spi_transfer_regs(spi_t bus, spi_cs_t cs,
|
||||
uint8_t reg, const void *out, void *in, size_t len)
|
||||
{
|
||||
@ -303,7 +493,6 @@ uint8_t spi_transfer_reg(spi_t bus, spi_cs_t cs, uint8_t reg, uint8_t out)
|
||||
|
||||
#endif /* MODULE_PERIPH_DMA */
|
||||
|
||||
|
||||
void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont,
|
||||
const void *out, void *in, size_t len)
|
||||
{
|
||||
@ -312,6 +501,7 @@ void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont,
|
||||
if (cs != SPI_CS_UNDEF) {
|
||||
gpio_clear((gpio_t)cs);
|
||||
}
|
||||
|
||||
if (_use_dma(bus)) {
|
||||
#ifdef MODULE_PERIPH_DMA
|
||||
/* The DMA promises not to modify the const out data */
|
||||
|
||||
@ -14,6 +14,7 @@ config CPU_COMMON_SAMD5X
|
||||
select HAS_CPU_SAMD5X
|
||||
select HAS_PERIPH_GPIO_TAMPER_WAKE
|
||||
select HAS_PERIPH_HWRNG
|
||||
select HAS_PERIPH_SPI_ON_QSPI
|
||||
|
||||
config CPU_FAM_SAMD51
|
||||
bool
|
||||
|
||||
@ -4,5 +4,6 @@ FEATURES_PROVIDED += periph_hwrng
|
||||
FEATURES_PROVIDED += backup_ram
|
||||
FEATURES_PROVIDED += cortexm_mpu
|
||||
FEATURES_PROVIDED += periph_gpio_tamper_wake
|
||||
FEATURES_PROVIDED += periph_spi_on_qspi
|
||||
|
||||
include $(RIOTCPU)/sam0_common/Makefile.features
|
||||
|
||||
@ -160,6 +160,18 @@ struct sam0_aux_cfg_mapping {
|
||||
/* config words 5,6,7 */
|
||||
uint32_t user_pages[3]; /**< User pages */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief QSPI pins are fixed
|
||||
* @{
|
||||
*/
|
||||
#define SAM0_QSPI_PIN_CLK GPIO_PIN(PB, 10) /**< Clock */
|
||||
#define SAM0_QSPI_PIN_CS GPIO_PIN(PB, 11) /**< Chip Select */
|
||||
#define SAM0_QSPI_PIN_DATA_0 GPIO_PIN(PA, 8) /**< D0 / MOSI */
|
||||
#define SAM0_QSPI_PIN_DATA_1 GPIO_PIN(PA, 9) /**< D1 / MISO */
|
||||
#define SAM0_QSPI_PIN_DATA_2 GPIO_PIN(PA, 10) /**< D2 / WP */
|
||||
#define SAM0_QSPI_PIN_DATA_3 GPIO_PIN(PA, 11) /**< D3 / HOLD */
|
||||
#define SAM0_QSPI_MUX GPIO_MUX_H /**< QSPI mux */
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@ -400,6 +400,7 @@ static int mtd_spi_nor_power(mtd_dev_t *mtd, enum mtd_power_state power)
|
||||
#endif
|
||||
/* enable 32 bit address mode */
|
||||
if (dev->params->addr_width == 4) {
|
||||
mtd_spi_cmd(dev, dev->params->opcode->wren);
|
||||
mtd_spi_cmd(dev, SFLASH_CMD_4_BYTE_ADDR);
|
||||
}
|
||||
|
||||
|
||||
@ -245,6 +245,11 @@ config HAS_PERIPH_SPI
|
||||
help
|
||||
Indicates that an SPI peripheral is present.
|
||||
|
||||
config HAS_PERIPH_SPI_ON_QSPI
|
||||
bool
|
||||
help
|
||||
Indicates that the QSPI peripheral can be used in SPI mode.
|
||||
|
||||
config HAS_PERIPH_SPI_RECONFIGURE
|
||||
bool
|
||||
help
|
||||
|
||||
@ -3,10 +3,12 @@ include ../Makefile.tests_common
|
||||
|
||||
LOW_MEMORY_BOARDS := samd10-xmini
|
||||
|
||||
FEATURES_REQUIRED += periph_spi
|
||||
FEATURES_OPTIONAL += periph_spi_on_qspi
|
||||
|
||||
ifeq (,$(filter $(BOARD),$(LOW_MEMORY_BOARDS)))
|
||||
FEATURES_OPTIONAL += periph_spi_reconfigure
|
||||
endif
|
||||
FEATURES_REQUIRED += periph_spi
|
||||
|
||||
USEMODULE += xtimer
|
||||
USEMODULE += shell
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user