drivers: spi: Added MEC172x full duplex qmspi driver

Add MEC172x full duplex qmspi driver version to support full
duplex transfers as expected by the Zephyr spi driver model.
On every spi clock we transmit one bit and receive one bit.
This driver will work with Zephyr SPI NOR driver.

Signed-off-by: Jay Vasanth <jay.vasanth@microchip.com>
This commit is contained in:
Jay Vasanth 2022-06-02 16:46:42 -04:00 committed by Maureen Helm
commit ae9de20222
9 changed files with 924 additions and 16 deletions

View file

@ -205,6 +205,7 @@
&spi0 {
status = "okay";
compatible = "microchip,xec-qmspi-ldma";
clock-frequency = <4000000>;
lines = <4>;
chip-select = <0>;

View file

@ -28,6 +28,7 @@ zephyr_library_sources_ifdef(CONFIG_SPI_PSOC6 spi_psoc6.c)
zephyr_library_sources_ifdef(CONFIG_SPI_NPCX_FIU spi_npcx_fiu.c)
zephyr_library_sources_ifdef(CONFIG_SPI_BITBANG spi_bitbang.c)
zephyr_library_sources_ifdef(CONFIG_SPI_XEC_QMSPI_LDMA spi_xec_qmspi_ldma.c)
zephyr_library_sources_ifdef(CONFIG_SPI_XEC_QMSPI_FULL_DUPLEX spi_xec_qmspi_full_duplex.c)
zephyr_library_sources_ifdef(CONFIG_SPI_GD32 spi_gd32.c)
zephyr_library_sources_ifdef(CONFIG_SPI_MCHP_QSPI spi_mchp_mss_qspi.c)
zephyr_library_sources_ifdef(CONFIG_SPI_PL022 spi_pl022.c)

View file

@ -97,8 +97,6 @@ source "drivers/spi/Kconfig.npcx_fiu"
source "drivers/spi/Kconfig.bitbang"
source "drivers/spi/Kconfig.xec_qmspi_ldma"
source "drivers/spi/Kconfig.gd32"
source "drivers/spi/Kconfig.mchp_mss_qspi"

View file

@ -4,9 +4,23 @@
# SPDX-License-Identifier: Apache-2.0
config SPI_XEC_QMSPI
bool "Microchip XEC QMSPI driver"
bool "Microchip MEC15xx XEC QMSPI driver"
default y
depends on DT_HAS_MICROCHIP_XEC_QMSPI_ENABLED
select DMA if SPI_ASYNC
help
Enable support for the Microchip XEC QMSPI driver.
Enable support for Microchip MEC15xx XEC QMSPI driver.
config SPI_XEC_QMSPI_LDMA
bool "Microchip XEC MEC17xx QMSPI LDMA driver"
default y
depends on DT_HAS_MICROCHIP_XEC_QMSPI_LDMA_ENABLED
help
Enable support for Microchip MEC17xx QMSPI with local DMA driver.
config SPI_XEC_QMSPI_FULL_DUPLEX
bool "Microchip XEC MEC17xx QMSPI Full Duplex driver"
depends on DT_HAS_MICROCHIP_XEC_QMSPI_FULL_DUPLEX_ENABLED
help
Enable support for Microchip MEC17xx QMSPI full duplex driver
to work with Zephyr NOR driver

View file

@ -1,11 +0,0 @@
# Microchip XEC QMSPI with LDMA
# Copyright (c) 2021 Microchip Technology Inc.
# SPDX-License-Identifier: Apache-2.0
config SPI_XEC_QMSPI_LDMA
bool "Microchip XEC QMSPI LDMA driver"
default y
depends on DT_HAS_MICROCHIP_XEC_QMSPI_LDMA_ENABLED
help
Enable support for the Microchip XEC QMSPI with local DMA driver.

View file

@ -0,0 +1,715 @@
/*
* Copyright (c) 2022 Microchip Technology Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT microchip_xec_qmspi_full_duplex
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(spi_xec, CONFIG_SPI_LOG_LEVEL);
#include <errno.h>
#include <zephyr/device.h>
#include <zephyr/drivers/clock_control/mchp_xec_clock_control.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/interrupt_controller/intc_mchp_xec_ecia.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/pinmux.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/dt-bindings/interrupt-controller/mchp-xec-ecia.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/sys_io.h>
#include <zephyr/sys/util.h>
#include <soc.h>
#include "spi_context.h"
#include "spi_xec_qmspi_full_duplex.h"
#if XEC_QSPI_TX_FIFO_SIZE < XEC_QSPI_RX_FIFO_SIZE
#define XEC_QSPI_CHUNK_SIZE XEC_QSPI_TX_FIFO_SIZE
#else
#define XEC_QSPI_CHUNK_SIZE XEC_QSPI_RX_FIFO_SIZE
#endif
/* spin loops waiting for HW to clear soft reset bit */
#define XEC_QSPI_SRST_LOOPS 16
/* microseconds for busy wait and total wait interval */
#define XEC_QSPI_WAIT_INTERVAL 8
#define XEC_QSPI_WAIT_COUNT 64
#define XEC_QSPI_WAIT_FULL_FIFO 1024
/* 3 Tap Regs - Tap, Tap Ctrl, Tap Adjust */
#define TAP_REGS_MAX 3
#define CLOCK_DIV_0_VALUE 0x10000
/*
* Maximum number of units to generate clocks with data lines
* tri-stated depends upon bus width. Maximum bus width is 4.
*/
#define XEC_QSPI_MAX_TSCLK_UNITS (MCHP_QMSPI_C_MAX_UNITS / 4)
#define XEC_QSPI_HALF_DUPLEX 0
#define XEC_QSPI_FULL_DUPLEX 1
#define XEC_QSPI_DUAL 2
#define XEC_QSPI_QUAD 4
#define XEC_QSPI_STS_ERRORS (BIT(XEC_QSPI_STS_TXB_ERR_POS) | \
BIT(XEC_QSPI_STS_RXB_ERR_POS) | \
BIT(XEC_QSPI_STS_PROG_ERR_POS) | \
BIT(XEC_QSPI_STS_LDMA_RX_ERR_POS) | \
BIT(XEC_QSPI_STS_LDMA_TX_ERR_POS))
#define XEC_QSPI_IEN_DONE_ERR (BIT(XEC_QSPI_IEN_XFR_DONE_POS) | \
BIT(XEC_QSPI_IEN_TXB_ERR_POS) | \
BIT(XEC_QSPI_IEN_RXB_ERR_POS) | \
BIT(XEC_QSPI_IEN_PROG_ERR_POS) | \
BIT(XEC_QSPI_IEN_LDMA_RX_ERR_POS) | \
BIT(XEC_QSPI_IEN_LDMA_TX_ERR_POS));
/* Device constant configuration parameters */
struct spi_xec_qspi_config {
struct qmspi_regs *regs;
uint32_t cs1_freq;
uint32_t cs_timing;
uint16_t taps_adj;
uint8_t girq;
uint8_t girq_pos;
uint8_t girq_nvic_aggr;
uint8_t girq_nvic_direct;
uint8_t irq_pri;
uint8_t pcr_idx;
uint8_t pcr_pos;
uint8_t chip_sel;
uint8_t width; /* 0(half) 1(single), 2(dual), 4(quad) */
uint8_t unused[2];
const struct pinctrl_dev_config *pcfg;
};
#define XEC_QMSPI_XFR_FLAG_TX BIT(0)
#define XEC_QMSPI_XFR_FLAG_STARTED BIT(1)
/* Device run time data */
struct spi_xec_qspi_data {
struct spi_context ctx;
uint32_t qstatus;
uint8_t np; /* number of data pins: 1, 2, or 4 */
};
static int xec_qspi_spin_yield(int *counter, int max_count)
{
*counter = *counter + 1;
if (*counter > max_count) {
return -ETIMEDOUT;
}
k_busy_wait(XEC_QSPI_WAIT_INTERVAL);
return 0;
}
/*
* reset QMSPI controller with save/restore of timing registers.
* Some QMSPI timing register may be modified by the Boot-ROM OTP
* values.
*/
static void xec_qspi_reset(struct qmspi_regs *regs)
{
uint32_t taps[TAP_REGS_MAX];
uint32_t malt1;
uint32_t cstm;
uint32_t mode;
uint32_t cnt = XEC_QSPI_SRST_LOOPS;
taps[0] = regs->TM_TAPS;
taps[1] = regs->TM_TAPS_ADJ;
taps[2] = regs->TM_TAPS_CTRL;
malt1 = regs->MODE_ALT1;
cstm = regs->CSTM;
mode = regs->MODE;
regs->MODE = MCHP_QMSPI_M_SRST;
while (regs->MODE & MCHP_QMSPI_M_SRST) {
if (cnt == 0) {
break;
}
cnt--;
}
regs->MODE = 0;
regs->MODE = mode & ~MCHP_QMSPI_M_ACTIVATE;
regs->CSTM = cstm;
regs->MODE_ALT1 = malt1;
regs->TM_TAPS = taps[0];
regs->TM_TAPS_ADJ = taps[1];
regs->TM_TAPS_CTRL = taps[2];
}
static uint32_t qspi_source_clock_freq(void)
{
struct pcr_regs const *pcr =
(struct pcr_regs *)(DT_REG_ADDR_BY_IDX(DT_NODELABEL(pcr), 0));
if (pcr->TURBO_CLK & MCHP_PCR_TURBO_CLK_96M) {
return MEC172X_QSPI_TURBO_SRC_CLOCK_HZ;
}
return MEC172X_QSPI_SRC_CLOCK_HZ;
}
/*
* Calculate QMSPI frequency divider register field value based upon
* the configured QMSPI input frequency: 48 or 96 MHz.
* The hardware divider is encoded as:
* 0 is divide by full divider range: 256 or 65536.
* Non-zero is divide by that value: 1 to 256 or 655356.
*/
static uint32_t qspi_encoded_fdiv(uint32_t freq_hz)
{
uint32_t fdiv = 1;
uint32_t src_clk = qspi_source_clock_freq();
if (freq_hz < (src_clk / 256u)) {
fdiv = 0; /* HW fdiv = 0 is divide by 256 */
} else if (freq_hz < src_clk) {
/* truncated integer division may result in lower freq. */
fdiv = src_clk / freq_hz;
}
return fdiv;
}
/* Program QMSPI frequency divider field in mode register */
static void qspi_set_frequency(struct qmspi_regs *regs, uint32_t freq_hz)
{
uint32_t fdiv, mode;
fdiv = qspi_encoded_fdiv(freq_hz);
mode = regs->MODE & ~(XEC_QSPI_M_CLK_DIV_MASK);
mode |= ((fdiv << XEC_QSPI_M_CLK_DIV_POS) & XEC_QSPI_M_CLK_DIV_MASK);
regs->MODE = mode;
}
static uint32_t qspi_get_frequency(struct qmspi_regs *regs)
{
uint32_t src_clk = qspi_source_clock_freq();
uint32_t fdiv = (regs->MODE & XEC_QSPI_M_CLK_DIV_MASK)
>> XEC_QSPI_M_CLK_DIV_POS;
if (fdiv == 0) {
fdiv = CLOCK_DIV_0_VALUE;
}
return (src_clk / fdiv);
}
/*
* SPI signalling mode: CPOL and CPHA
* CPOL = 0 is clock idle state is low, 1 is clock idle state is high
* CPHA = 0 Transmitter changes data on trailing of preceding clock cycle.
* Receiver samples data on leading edge of clock cyle.
* 1 Transmitter changes data on leading edge of current clock cycle.
* Receiver samples data on the trailing edge of clock cycle.
* SPI Mode nomenclature:
* Mode CPOL CPHA
* 0 0 0
* 1 0 1
* 2 1 0
* 3 1 1
* QMSPI has three controls, CPOL, CPHA for output and CPHA for input.
* SPI frequency < 48MHz
* Mode 0: CPOL=0 CHPA=0 (CHPA_MISO=0 and CHPA_MOSI=0)
* Mode 3: CPOL=1 CHPA=1 (CHPA_MISO=1 and CHPA_MOSI=1)
* Data sheet recommends when QMSPI set at >= 48MHz, sample and change data
* on the same edge.
* Mode 0: CPOL=0 CPHA=0 (CHPA_MISO=1 and CHPA_MOSI=0)
* Mode 3: CPOL=1 CPHA=1 (CHPA_MISO=0 and CHPA_MOSI=1)
*
* smode_tbl and smode48_tbl has the byte values for Mode 0, 1, 2, 3
*
* Byte values correspond to bits 8. 9. 10 in QMSPI Mode Register
* Bit 8 - CPOL
* Bit 9 - CHPA MOSI
* Bit 10 - CHPA MISO
*/
const uint8_t smode_tbl[4] = {
0x00u, 0x06u, 0x01u, 0x07u
};
const uint8_t smode48_tbl[4] = {
0x04u, 0x02u, 0x05u, 0x03u
};
static void qspi_set_signalling_mode(struct qmspi_regs *regs, uint32_t smode)
{
const uint8_t *ptbl;
uint32_t m;
ptbl = smode_tbl;
if (qspi_get_frequency(regs) >= MHZ(48)) {
ptbl = smode48_tbl;
}
m = (uint32_t)ptbl[smode & GENMASK(1, 0)];
regs->MODE = (regs->MODE & ~(XEC_QSPI_M_CP_MSK)) |
(m << XEC_QSPI_M_CPOL_POS);
}
static uint8_t npins_from_spi_config(const struct spi_config *config)
{
uint8_t lines = 1u;
if (IS_ENABLED(CONFIG_SPI_EXTENDED_MODES)) {
switch (config->operation & SPI_LINES_MASK) {
case SPI_LINES_DUAL:
lines = 2u;
break;
case SPI_LINES_QUAD:
lines = 4u;
break;
default:
lines = 1u;
break;
}
}
return lines;
}
/*
* Configure QSPI.
* NOTE: QSPI Port 0 has two chip selects available. Ports 1 & 2
* support only CS0#.
*/
static int qspi_configure(const struct device *dev,
const struct spi_config *spi_conf)
{
const struct spi_xec_qspi_config * const cfg = dev->config;
struct spi_xec_qspi_data * const qdata = dev->data;
struct qmspi_regs * const regs = cfg->regs;
struct spi_context *ctx = &qdata->ctx;
uint32_t smode;
if (spi_context_configured(ctx, spi_conf)) {
return 0;
}
if (spi_conf->operation & (SPI_TRANSFER_LSB | SPI_OP_MODE_SLAVE
| SPI_MODE_LOOP)) {
return -ENOTSUP;
}
if ((IS_ENABLED(CONFIG_SPI_EXTENDED_MODES) &&
((spi_conf->operation & SPI_LINES_MASK) != SPI_LINES_SINGLE))) {
LOG_ERR("Single(full-duplex) only");
return -EINVAL;
}
if (spi_conf->operation & SPI_CS_ACTIVE_HIGH) {
LOG_ERR("CS active high not supported");
return -ENOTSUP;
}
if (SPI_WORD_SIZE_GET(spi_conf->operation) != 8) {
LOG_ERR("Word size != 8 not supported");
return -ENOTSUP;
}
smode = SPI_LINES_SINGLE;
qdata->np = npins_from_spi_config(spi_conf);
regs->CTRL = smode;
/* Use the requested or next highest possible frequency */
qspi_set_frequency(regs, spi_conf->frequency);
smode = 0;
if ((spi_conf->operation & SPI_MODE_CPHA) != 0U) {
smode |= (BIT(0));
}
if ((spi_conf->operation & SPI_MODE_CPOL) != 0U) {
smode |= (BIT(1));
}
qspi_set_signalling_mode(regs, smode);
/* chip select */
smode = regs->MODE & ~(XEC_QSPI_M_CS_SEL_MSK);
if (cfg->chip_sel == 0) {
smode |= XEC_QSPI_M_CS0_SEL;
} else {
smode |= XEC_QSPI_M_CS1_SEL;
}
regs->MODE = smode;
/* chip select timing */
regs->CSTM = cfg->cs_timing;
regs->TM_TAPS_ADJ = cfg->taps_adj;
/* CS1 alternate mode (frequency) */
regs->MODE_ALT1 = 0;
if (cfg->cs1_freq) {
uint32_t fdiv = qspi_encoded_fdiv(cfg->cs1_freq);
regs->MODE_ALT1 = (fdiv << XEC_QSPI_MALT1_CLK_DIV_POS) &
XEC_QSPI_MALT1_CLK_DIV_MSK;
regs->MODE_ALT1 |= BIT(XEC_QSPI_MALT1_EN_POS);
}
ctx->config = spi_conf;
regs->MODE |= BIT(XEC_QSPI_M_ACTV_POS);
return 0;
}
static uint32_t encode_npins(uint8_t npins)
{
uint32_t encoding = XEC_QSPI_C_IFC_1X;
if (npins == 4) {
encoding = XEC_QSPI_C_IFC_4X;
} else if (npins == 2) {
encoding = XEC_QSPI_C_IFC_2X;
} else {
encoding = XEC_QSPI_C_IFC_1X;
}
return encoding;
}
/* Allocate QMSPI HW descriptor registers to process the given number of bytes
* or until all descriptors are allocated. Returns the number of remaining
* bytes not in allocation. Updates the word pointed to by ndescr with the
* number of descriptors allocated. Descriptor allocation always begins with
* descriptor 0. Descriptor QMSPI unit size, number of units, and next
* descriptor fields are programmed other fields in descr_base are preserved.
*/
static size_t descr_alloc(struct qmspi_regs * const regs, size_t nbytes,
uint32_t descr_base, uint32_t *ndescr)
{
size_t nb = nbytes;
uint32_t idx = 0u;
uint32_t descr = 0u;
descr_base &= ~(XEC_QSPI_C_Q_XFR_UNITS_MSK | XEC_QSPI_C_Q_NUNITS_MSK |
XEC_QSPI_C_FN_DESCR_MSK);
while (nb && (idx < 16)) {
if (nb <= XEC_QSPI_C_Q_NUNITS_MAX) {
descr = nb;
descr <<= XEC_QSPI_C_Q_NUNITS_POS;
descr |= XEC_QSPI_C_Q_XFR_UNITS_1B;
nb = 0u;
} else {
descr = (nb >> 4);
nb -= (descr << 4);
descr <<= XEC_QSPI_C_Q_NUNITS_POS;
descr |= XEC_QSPI_C_Q_XFR_UNITS_16B;
}
descr |= (XEC_QSPI_C_IFC_1X | XEC_QSPI_C_TX_EN_DATA |
BIT(XEC_QSPI_C_RX_EN_POS));
descr |= XEC_QSPI_C_FN_DESCR((idx + 1u));
regs->DESCR[idx++] = descr_base | descr;
}
if (idx) {
regs->DESCR[idx - 1u] |= BIT(XEC_QSPI_D_DESCR_LAST_POS);
}
if (ndescr) {
*ndescr = idx;
}
return nb;
}
/* Polling full-duplex transfer using QMSPI descriptors and FIFO's.
* Allocate hardware descriptors for maximum total transfer size.
* Descriptors configured for both transmit and receive.
* If TX context has no data, transmit 0 bytes.
* If RX context has no buffer, throw away received bytes.
* If user set SPI_HOLD_ON_CS flag configure to not de-assert chip select
* when the last descriptor is completed. When transfer is completed without
* error mark context complete.
*/
static int xec_qspi_fd_descr(const struct device *dev,
const struct spi_config *spi_conf,
const struct spi_buf_set *tx_bufs,
const struct spi_buf_set *rx_bufs)
{
const struct spi_xec_qspi_config *cfg = dev->config;
struct spi_xec_qspi_data * const qdata = dev->data;
struct qmspi_regs * const regs = cfg->regs;
struct spi_context *ctx = &qdata->ctx;
uint32_t descr_base, nd;
size_t len, ntx, nrx, rem, xfr_len;
uint8_t txb, rxb;
bool close = true;
xfr_len = MAX(spi_context_total_tx_len(ctx), spi_context_total_rx_len(ctx));
if (!xfr_len) {
return 0;
}
regs->CTRL = 0;
regs->EXE = BIT(XEC_QSPI_EXE_CLR_FIFOS_POS);
regs->STS |= regs->STS;
regs->CTRL = BIT(XEC_QSPI_C_DESCR_MODE_EN_POS);
descr_base = encode_npins(qdata->np);
descr_base |= (XEC_QSPI_C_TX_EN_DATA | BIT(XEC_QSPI_C_RX_EN_POS) |
BIT(XEC_QSPI_C_CLOSE_POS));
if (spi_conf->operation & SPI_HOLD_ON_CS) {
close = false;
}
len = xfr_len;
while (len) {
nd = 0u;
rem = descr_alloc(regs, len, descr_base, &nd);
__ASSERT_NO_MSG(nd != 0);
__ASSERT_NO_MSG(rem < len);
if ((rem == 0) && close) {
regs->DESCR[nd - 1u] |= BIT(XEC_QSPI_C_CLOSE_POS);
}
/* NOTE: start with TX FIFO empty causes read-only TX stall
* status to be set.
*/
regs->EXE = BIT(XEC_QSPI_EXE_START_POS);
ntx = len - rem;
nrx = ntx;
while (ntx || nrx) {
if (regs->STS & XEC_QSPI_STS_ERRORS) {
LOG_ERR("QMSPI errors(sts): 0x%08x\n", regs->STS);
return -EIO;
}
if (ntx && !(regs->STS & BIT(XEC_QSPI_STS_TXB_FULL_POS))) {
txb = 0u;
if (ctx->tx_buf) {
txb = *(uint8_t *)(ctx->tx_buf);
}
sys_write8(txb, (mem_addr_t)&regs->TX_FIFO);
spi_context_update_tx(ctx, 1, 1);
ntx--;
}
if (nrx && !(regs->STS & BIT(XEC_QSPI_STS_RXB_EMPTY_POS))) {
rxb = sys_read8((mem_addr_t)&regs->RX_FIFO);
if (ctx->rx_buf) {
*(uint8_t *)(ctx->rx_buf) = rxb;
}
spi_context_update_rx(ctx, 1, 1);
nrx--;
}
}
len = rem;
}
spi_context_complete(ctx, dev, 0);
return 0;
}
static int xec_qspi_xfr(const struct device *dev,
const struct spi_config *spi_conf,
const struct spi_buf_set *tx_bufs,
const struct spi_buf_set *rx_bufs,
bool asynchronous)
{
const struct spi_xec_qspi_config *cfg = dev->config;
struct spi_xec_qspi_data * const qdata = dev->data;
struct qmspi_regs * const regs = cfg->regs;
struct spi_context *ctx = &qdata->ctx;
int ret = 0;
spi_context_lock(ctx, asynchronous, NULL, NULL, spi_conf);
ret = qspi_configure(dev, spi_conf);
if (ret != 0) {
spi_context_release(ctx, ret);
return ret;
}
spi_context_cs_control(&qdata->ctx, true);
spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, 1);
ret = xec_qspi_fd_descr(dev, spi_conf, tx_bufs, rx_bufs);
if (ret) {
regs->EXE = BIT(XEC_QSPI_EXE_STOP_POS);
spi_context_unlock_unconditionally(&qdata->ctx);
return ret;
}
if (!(spi_conf->operation & SPI_HOLD_ON_CS)) {
spi_context_cs_control(ctx, false);
}
/* Attempts to take semaphore with timeout. Descriptor transfer
* routine completes the context giving the semaphore.
*/
ret = spi_context_wait_for_completion(ctx);
/* gives semaphore */
spi_context_release(ctx, ret);
return ret;
}
static int xec_qspi_transceive(const struct device *dev,
const struct spi_config *spi_conf,
const struct spi_buf_set *tx_bufs,
const struct spi_buf_set *rx_bufs)
{
return xec_qspi_xfr(dev, spi_conf, tx_bufs, rx_bufs, false);
}
#ifdef CONFIG_SPI_ASYNC
static int xec_qspi_transceive_async(const struct device *dev,
const struct spi_config *spi_conf,
const struct spi_buf_set *tx_bufs,
const struct spi_buf_set *rx_bufs,
struct k_poll_signal *async)
{
return -ENOTSUP;
}
#endif
static int xec_qspi_release(const struct device *dev,
const struct spi_config *spi_conf)
{
struct spi_xec_qspi_data * const qdata = dev->data;
const struct spi_xec_qspi_config *cfg = dev->config;
struct qmspi_regs * const regs = cfg->regs;
struct spi_context *ctx = &qdata->ctx;
int ret = 0;
int counter = 0;
if (regs->STS & BIT(XEC_QSPI_STS_XFR_ACTIVE_POS)) {
/* Force CS# to de-assert on next unit boundary */
regs->EXE = BIT(XEC_QSPI_EXE_STOP_POS);
while (regs->STS & BIT(XEC_QSPI_STS_XFR_ACTIVE_POS)) {
ret = xec_qspi_spin_yield(&counter,
XEC_QSPI_WAIT_COUNT);
if (ret != 0) {
break;
}
}
}
spi_context_unlock_unconditionally(ctx);
return ret;
}
/*
* Called for each QMSPI controller instance
* Initialize QMSPI controller.
* Disable sleep control.
* Disable and clear interrupt status.
* Initialize SPI context.
* QMSPI will be fully configured and enabled when the transceive API
* is called.
*/
static int xec_qspi_init(const struct device *dev)
{
const struct spi_xec_qspi_config *cfg = dev->config;
struct spi_xec_qspi_data * const qdata = dev->data;
struct qmspi_regs * const regs = cfg->regs;
int ret = 0;
qdata->qstatus = 0;
qdata->np = cfg->width;
z_mchp_xec_pcr_periph_sleep(cfg->pcr_idx, cfg->pcr_pos, 0);
ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
if (ret != 0) {
LOG_ERR("QSPI pinctrl setup failed (%d)", ret);
return ret;
}
xec_qspi_reset(regs);
spi_context_unlock_unconditionally(&qdata->ctx);
return 0;
}
static const struct spi_driver_api spi_xec_qspi_driver_api = {
.transceive = xec_qspi_transceive,
.release = xec_qspi_release,
#ifdef CONFIG_SPI_ASYNC
.transceive_async = xec_qspi_transceive_async,
#endif
};
#define XEC_QSPI_CS_TIMING_VAL(a, b, c, d) (((a) & 0xFu) \
| (((b) & 0xFu) << 8) \
| (((c) & 0xFu) << 16) \
| (((d) & 0xFu) << 24))
#define XEC_QSPI_TAPS_ADJ_VAL(a, b) (((a) & 0xffu) | (((b) & 0xffu) << 8))
#define XEC_QSPI_CS_TIMING(i) XEC_QSPI_CS_TIMING_VAL( \
DT_INST_PROP_OR(i, dcsckon, 6), \
DT_INST_PROP_OR(i, dckcsoff, 4), \
DT_INST_PROP_OR(i, dldh, 6), \
DT_INST_PROP_OR(i, dcsda, 6))
#define XEC_QSPI_TAPS_ADJ(i) XEC_QSPI_TAPS_ADJ_VAL( \
DT_INST_PROP_OR(i, tctradj, 0), \
DT_INST_PROP_OR(i, tsckadj, 0))
#define XEC_QSPI_GIRQ(i) \
MCHP_XEC_ECIA_GIRQ(DT_INST_PROP_BY_IDX(i, girqs, 0))
#define XEC_QSPI_GIRQ_POS(i) \
MCHP_XEC_ECIA_GIRQ_POS(DT_INST_PROP_BY_IDX(i, girqs, 0))
#define XEC_QSPI_NVIC_AGGR(i) \
MCHP_XEC_ECIA_NVIC_AGGR(DT_INST_PROP_BY_IDX(i, girqs, 0))
#define XEC_QSPI_NVIC_DIRECT(i) \
MCHP_XEC_ECIA_NVIC_DIRECT(DT_INST_PROP_BY_IDX(i, girqs, 0))
/*
* The instance number, i is not related to block ID's rather the
* order the DT tools process all DT files in a build.
*/
#define XEC_QSPI_DEVICE(i) \
\
PINCTRL_DT_INST_DEFINE(i); \
\
static struct spi_xec_qspi_data xec_qspi_data_##i = { \
SPI_CONTEXT_INIT_LOCK(xec_qspi_data_##i, ctx), \
SPI_CONTEXT_INIT_SYNC(xec_qspi_data_##i, ctx), \
}; \
static const struct spi_xec_qspi_config xec_qspi_config_##i = { \
.regs = (struct qmspi_regs *) DT_INST_REG_ADDR(i), \
.cs1_freq = DT_INST_PROP_OR(i, cs1_freq, 0), \
.cs_timing = XEC_QSPI_CS_TIMING(i), \
.taps_adj = XEC_QSPI_TAPS_ADJ(i), \
.girq = XEC_QSPI_GIRQ(i), \
.girq_pos = XEC_QSPI_GIRQ_POS(i), \
.girq_nvic_aggr = XEC_QSPI_NVIC_AGGR(i), \
.girq_nvic_direct = XEC_QSPI_NVIC_DIRECT(i), \
.irq_pri = DT_INST_IRQ(i, priority), \
.pcr_idx = DT_INST_PROP_BY_IDX(i, pcrs, 0), \
.pcr_pos = DT_INST_PROP_BY_IDX(i, pcrs, 1), \
.chip_sel = DT_INST_PROP_OR(i, chip_select, 0), \
.width = DT_INST_PROP_OR(i, lines, 1), \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(i), \
}; \
DEVICE_DT_INST_DEFINE(i, &xec_qspi_init, NULL, \
&xec_qspi_data_##i, &xec_qspi_config_##i, \
POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, \
&spi_xec_qspi_driver_api);
DT_INST_FOREACH_STATUS_OKAY(XEC_QSPI_DEVICE)

View file

@ -0,0 +1,182 @@
/*
* Copyright (c) 2022 Microchip Technology Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _SPI_XEC_QMSPI_V2_H
#define _SPI_XEC_QMSPI_V2_H
#define MEC152X_QSPI_SRC_CLOCK_HZ 48000000u
#define MEC172X_QSPI_SRC_CLOCK_HZ 48000000u
#define MEC172X_QSPI_TURBO_SRC_CLOCK_HZ 96000000u
#define XEC_QSPI_TX_FIFO_SIZE 8
#define XEC_QSPI_RX_FIFO_SIZE 8
#define XEC_QSPI_DESCR_MAX 16
/* mode register */
#define XEC_QSPI_M_ACTV_POS 0
#define XEC_QSPI_M_SRST_POS 1
#define XEC_QSPI_M_RX_LDMA_EN_POS 3
#define XEC_QSPI_M_TX_LDMA_EN_POS 4
#define XEC_QSPI_M_CPOL_POS 8
#define XEC_QSPI_M_CPHA_MOSI_POS 9
#define XEC_QSPI_M_CPHA_MISO_POS 10
#define XEC_QSPI_M_CP_MSK (0x7u << XEC_QSPI_M_CPOL_POS)
#define XEC_QSPI_M_CS_SEL_POS 12
#define XEC_QSPI_M_CS_SEL_MSK (0x3u << XEC_QSPI_M_CS_SEL_POS)
#define XEC_QSPI_M_CS0_SEL 0
#define XEC_QSPI_M_CS1_SEL (1u << XEC_QSPI_M_CS_SEL_POS)
#define XEC_QSPI_M_CLK_DIV_POS 16
#ifdef CONFIG_SOC_SERIES_MEC172X
#define XEC_QSPI_M_CLK_DIV_MASK 0xffff0000u
#else
#define XEC_QSPI_M_CLK_DIV_MASK 0xff000000u
#endif
/* control register */
#define XEC_QSPI_C_IFC_POS 0
#define XEC_QSPI_C_IFC_MSK 0x3u
#define XEC_QSPI_C_IFC_1X 0
#define XEC_QSPI_C_IFC_2X 0x1u
#define XEC_QSPI_C_IFC_4X 0x2u
#define XEC_QSPI_C_TX_EN_POS 2
#define XEC_QSPI_C_TX_EN_MSK (0x3u << XEC_QSPI_C_TX_EN_POS)
#define XEC_QSPI_C_TX_EN_DATA (0x1u << XEC_QSPI_C_TX_EN_POS)
#define XEC_QSPI_C_TX_EN_ZEROS (0x2u << XEC_QSPI_C_TX_EN_POS)
#define XEC_QSPI_C_TX_EN_ONES (0x3u << XEC_QSPI_C_TX_EN_POS)
#define XEC_QSPI_C_TX_DMA_EN_POS 4
#define XEC_QSPI_C_TX_DMA_EN_MSK (0x3u << XEC_QSPI_C_TX_DMA_EN_POS)
#define XEC_QSPI_C_TX_DMA_EN_1B (0x1u << XEC_QSPI_C_TX_DMA_EN_POS)
#define XEC_QSPI_C_TX_DMA_EN_2B (0x2u << XEC_QSPI_C_TX_DMA_EN_POS)
#define XEC_QSPI_C_TX_DMA_EN_4B (0x3u << XEC_QSPI_C_TX_DMA_EN_POS)
#ifdef CONFIG_SOC_SERIES_MEC172X
#define XEC_QSPI_C_TX_DMA_EN_LDCH0 (0x1u << XEC_QSPI_C_TX_DMA_EN_POS)
#define XEC_QSPI_C_TX_DMA_EN_LDCH1 (0x2u << XEC_QSPI_C_TX_DMA_EN_POS)
#define XEC_QSPI_C_TX_DMA_EN_LDCH2 (0x3u << XEC_QSPI_C_TX_DMA_EN_POS)
#endif
#define XEC_QSPI_C_RX_EN_POS 6
#define XEC_QSPI_C_RX_DMA_EN_POS 7
#define XEC_QSPI_C_RX_DMA_EN_MSK (0x3u << XEC_QSPI_C_RX_DMA_EN_POS)
#define XEC_QSPI_C_RX_DMA_EN_1B (0x1u << XEC_QSPI_C_RX_DMA_EN_POS)
#define XEC_QSPI_C_RX_DMA_EN_2B (0x2u << XEC_QSPI_C_RX_DMA_EN_POS)
#define XEC_QSPI_C_RX_DMA_EN_4B (0x3u << XEC_QSPI_C_RX_DMA_EN_POS)
#ifdef CONFIG_SOC_SERIES_MEC172X
#define XEC_QSPI_C_RX_DMA_EN_LDCH0 (0x1u << XEC_QSPI_C_RX_DMA_EN_POS)
#define XEC_QSPI_C_RX_DMA_EN_LDCH1 (0x2u << XEC_QSPI_C_RX_DMA_EN_POS)
#define XEC_QSPI_C_RX_DMA_EN_LDCH2 (0x3u << XEC_QSPI_C_RX_DMA_EN_POS)
#endif
#define XEC_QSPI_C_CLOSE_POS 9
#define XEC_QSPI_C_Q_XFR_UNITS_POS 10
#define XEC_QSPI_C_Q_XFR_UNITS_MSK (0x3u << XEC_QSPI_C_Q_XFR_UNITS_POS)
#define XEC_QSPI_C_Q_XFR_UNITS_BITS 0
#define XEC_QSPI_C_Q_XFR_UNITS_1B (0x1u << XEC_QSPI_C_Q_XFR_UNITS_POS)
#define XEC_QSPI_C_Q_XFR_UNITS_4B (0x2u << XEC_QSPI_C_Q_XFR_UNITS_POS)
#define XEC_QSPI_C_Q_XFR_UNITS_16B (0x3u << XEC_QSPI_C_Q_XFR_UNITS_POS)
#define XEC_QSPI_C_FN_DESCR_POS 12
#define XEC_QSPI_C_FN_DESCR_MSK (0xfu << XEC_QSPI_C_FN_DESCR_POS)
#define XEC_QSPI_C_FN_DESCR(n) \
(((uint32_t)(n) & 0xfu) << XEC_QSPI_C_FN_DESCR_POS)
/* control register enable descriptor mode */
#define XEC_QSPI_C_DESCR_MODE_EN_POS 16
/* descriptor specifies last descriptor to be processed */
#define XEC_QSPI_D_DESCR_LAST_POS 16
#define XEC_QSPI_C_Q_NUNITS_POS 17
#define XEC_QSPI_C_Q_NUNITS_MAX 0x7fffu
#define XEC_QSPI_C_Q_NUNITS_MSK (0x7fffu << XEC_QSPI_C_Q_NUNITS_POS)
#define XEC_QSPI_C_NUNITS(n) \
(((uint32_t)(n) & 0x7fffu) << XEC_QSPI_C_Q_NUNITS_POS)
/* execute register (WO). Set one bit at a time! */
#define XEC_QSPI_EXE_START_POS 0
#define XEC_QSPI_EXE_STOP_POS 1
#define XEC_QSPI_EXE_CLR_FIFOS_POS 2
/* status register */
#define XEC_QSPI_STS_MSK 0x0f01ff7fu
#define XEC_QSPI_STS_MSK_RW1C 0x0000cc1fu
#define XEC_QSPI_STS_XFR_DONE_POS 0
#define XEC_QSPI_STS_DMA_DONE_POS 1
#define XEC_QSPI_STS_TXB_ERR_POS 2
#define XEC_QSPI_STS_RXB_ERR_POS 3
#define XEC_QSPI_STS_PROG_ERR_POS 4
#ifdef CONFIG_SOC_SERIES_MEC172X
#define XEC_QSPI_STS_LDMA_RX_ERR_POS 5
#define XEC_QSPI_STS_LDMA_TX_ERR_POS 6
#endif
#define XEC_QSPI_STS_TXB_FULL_POS 8
#define XEC_QSPI_STS_TXB_EMPTY_POS 9
#define XEC_QSPI_STS_TXB_REQ_POS 10
#define XEC_QSPI_STS_TXB_STALL_POS 11
#define XEC_QSPI_STS_RXB_FULL_POS 12
#define XEC_QSPI_STS_RXB_EMPTY_POS 13
#define XEC_QSPI_STS_RXB_REQ_POS 14
#define XEC_QSPI_STS_RXB_STALL_POS 15
#define XEC_QSPI_STS_XFR_ACTIVE_POS 16
#define XEC_QSPI_STS_CURR_DESCR_POS 24
#define XEC_QSPI_STS_CURR_DESCR_MSK (0xfu << XEC_QSPI_STS_CURR_DESCR_POS)
#define XEC_QSPI_STS_ALL_ERR (BIT(XEC_QSPI_STS_TXB_ERR_POS) | \
BIT(XEC_QSPI_STS_RXB_ERR_POS) | \
BIT(XEC_QSPI_STS_PROG_ERR_POS))
/* buffer count status (RO) */
#define XEC_QSPI_BCNT_STS_TX_POS 0
#define XEC_QSPI_BCNT_STS_TX_MSK 0xffffu
#define XEC_QSPI_BCNT_STS_RX_POS 16
#define XEC_QSPI_BCNT_STS_RX_MSK (0xffffu << XEC_QSPI_BCNT_STS_RX_POS)
/* interrupt enable */
#define XEC_QSPI_IEN_XFR_DONE_POS 0
#define XEC_QSPI_IEN_DMA_DONE_POS 1
#define XEC_QSPI_IEN_TXB_ERR_POS 2
#define XEC_QSPI_IEN_RXB_ERR_POS 3
#define XEC_QSPI_IEN_PROG_ERR_POS 4
#ifdef CONFIG_SOC_SERIES_MEC172X
#define XEC_QSPI_IEN_LDMA_RX_ERR_POS 5
#define XEC_QSPI_IEN_LDMA_TX_ERR_POS 6
#endif
#define XEC_QSPI_IEN_TXB_FULL_POS 8
#define XEC_QSPI_IEN_TXB_EMPTY_POS 9
#define XEC_QSPI_IEN_TXB_REQ_POS 10
#define XEC_QSPI_IEN_RXB_FULL_POS 12
#define XEC_QSPI_IEN_RXB_EMPTY_POS 13
#define XEC_QSPI_IEN_RXB_REQ_POS 14
/* chip select timing */
#define XEC_QSPI_CSTM_DLY_CS_TO_START_POS 0
#define XEC_QSPI_CSTM_DLY_CS_TO_START_MSK 0xfu
#define XEC_QSPI_CSTM_DLY_CLK_OFF_TO_CS_OFF_POS 8
#define XEC_QSPI_CSTM_DLY_CLK_OFF_TO_CS_OFF_MSK 0xf00u
#define XEC_QSPI_CSTM_DLY_LAST_DATA_HOLD_POS 16
#define XEC_QSPI_CSTM_DLY_LAST_DATA_HOLD_MSK 0xf0000u
#define XEC_QSPI_CSTM_DLY_CS_OFF_TO_CS_ON_POS 24
#define XEC_QSPI_CSTM_DLY_CS_OFF_TO_CS_ON_MSK 0xff000000u
#ifdef CONFIG_SOC_SERIES_MEC172X
#define XEC_QSPI_MALT1_EN_POS 0
#define XEC_QSPI_MALT1_CLK_DIV_POS 16
#define XEC_QSPI_MALT1_CLK_DIV_MSK 0xffff0000u
#define XEC_QSPI_LDCH_CTRL_EN_POS 0
#define XEC_QSPI_LDCH_CTRL_RESTART_EN_POS 1
#define XEC_QSPI_LDCH_CTRL_RESTART_ADDR_EN_POS 2
#define XEC_QSPI_LDCH_CTRL_OVRLEN_POS 3
#define XEC_QSPI_LDCH_CTRL_ACCSZ_POS 4
#define XEC_QSPI_LDCH_CTRL_ACCSZ_MSK 0x30u
#define XEC_QSPI_LDCH_CTRL_ACCSZ_1B 0u
#define XEC_QSPI_LDCH_CTRL_ACCSZ_2B 1u
#define XEC_QSPI_LDCH_CTRL_ACCSZ_4B 2u
#define XEC_QSPI_LDCH_CTRL_INCR_ADDR_POS 6
struct qspi_ldma_chan {
volatile uint32_t ldctrl;
volatile uint32_t mstart;
volatile uint32_t nbytes;
uint32_t rsvd[1];
};
#endif /* CONFIG_SOC_SERIES_MEC172X */
#endif /* _SPI_XEC_QMSPI_V2_H */

View file

@ -690,7 +690,6 @@
#size-cells = <0>;
};
spi0: spi@40070000 {
compatible = "microchip,xec-qmspi-ldma";
reg = <0x40070000 0x400>;
interrupts = <91 2>;
girqs = < MCHP_XEC_ECIA(18, 1, 10, 91) >;

View file

@ -0,0 +1,9 @@
# Copyright (c) 2018, Google LLC.
# Copyright (c) 2022, Microchip Technology Inc.
# SPDX-License-Identifier: Apache-2.0
description: Microchip XEC QMSPI controller with local DMA
compatible: "microchip,xec-qmspi-full-duplex"
include: [microchip-xec-qmspi-v2.yaml]