|  | /* | 
|  | * Copyright (c) 2019 Microchip Technology Inc. | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #define DT_DRV_COMPAT microchip_xec_qmspi | 
|  |  | 
|  | #include <logging/log.h> | 
|  | LOG_MODULE_REGISTER(spi_xec, CONFIG_SPI_LOG_LEVEL); | 
|  |  | 
|  | #include "spi_context.h" | 
|  | #include <errno.h> | 
|  | #include <device.h> | 
|  | #include <drivers/spi.h> | 
|  | #include <soc.h> | 
|  |  | 
|  | /* Device constant configuration parameters */ | 
|  | struct spi_qmspi_config { | 
|  | QMSPI_Type *regs; | 
|  | uint32_t cs_timing; | 
|  | uint8_t girq; | 
|  | uint8_t girq_pos; | 
|  | uint8_t girq_nvic_aggr; | 
|  | uint8_t girq_nvic_direct; | 
|  | uint8_t irq_pri; | 
|  | uint8_t chip_sel; | 
|  | uint8_t width;	/* 1(single), 2(dual), 4(quad) */ | 
|  | }; | 
|  |  | 
|  | /* Device run time data */ | 
|  | struct spi_qmspi_data { | 
|  | struct spi_context ctx; | 
|  | }; | 
|  |  | 
|  | static inline uint32_t descr_rd(QMSPI_Type *regs, uint32_t did) | 
|  | { | 
|  | uintptr_t raddr = (uintptr_t)regs + MCHP_QMSPI_DESC0_OFS + | 
|  | ((did & MCHP_QMSPI_C_NEXT_DESCR_MASK0) << 2); | 
|  |  | 
|  | return REG32(raddr); | 
|  | } | 
|  |  | 
|  | static inline void descr_wr(QMSPI_Type *regs, uint32_t did, uint32_t val) | 
|  | { | 
|  | uintptr_t raddr = (uintptr_t)regs + MCHP_QMSPI_DESC0_OFS + | 
|  | ((did & MCHP_QMSPI_C_NEXT_DESCR_MASK0) << 2); | 
|  |  | 
|  | REG32(raddr) = val; | 
|  | } | 
|  |  | 
|  | static inline void txb_wr8(QMSPI_Type *regs, uint8_t data8) | 
|  | { | 
|  | REG8(®s->TX_FIFO) = data8; | 
|  | } | 
|  |  | 
|  | static inline uint8_t rxb_rd8(QMSPI_Type *regs) | 
|  | { | 
|  | return REG8(®s->RX_FIFO); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Program QMSPI frequency. | 
|  | * MEC1501 base frequency is 48MHz. QMSPI frequency divider field in the | 
|  | * mode register is defined as: 0=maximum divider of 256. Values 1 through | 
|  | * 255 divide 48MHz by that value. | 
|  | */ | 
|  | static void qmspi_set_frequency(QMSPI_Type *regs, uint32_t freq_hz) | 
|  | { | 
|  | uint32_t div, qmode; | 
|  |  | 
|  | if (freq_hz == 0) { | 
|  | div = 0; /* max divider = 256 */ | 
|  | } else { | 
|  | div = MCHP_QMSPI_INPUT_CLOCK_FREQ_HZ / freq_hz; | 
|  | if (div == 0) { | 
|  | div = 1; /* max freq. divider = 1 */ | 
|  | } else if (div > 0xffu) { | 
|  | div = 0u; /* max divider = 256 */ | 
|  | } | 
|  | } | 
|  |  | 
|  | qmode = regs->MODE & ~(MCHP_QMSPI_M_FDIV_MASK); | 
|  | qmode |= (div << MCHP_QMSPI_M_FDIV_POS) & MCHP_QMSPI_M_FDIV_MASK; | 
|  | regs->MODE = qmode; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * SPI signalling mode: CPOL and CPHA | 
|  | * CPOL = 0 is clock idles low, 1 is clock idle 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 | 
|  | * MEC1501 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 max. SPI frequency (48MHz). | 
|  | * SPI frequency == 48MHz sample and change data on same edge. | 
|  | *  Mode 0: CPOL=0 CHPA=0 (CHPA_MISO=1 and CHPA_MOSI=0) | 
|  | *  Mode 3: CPOL=1 CHPA=1 (CHPA_MISO=0 and CHPA_MOSI=1) | 
|  | */ | 
|  |  | 
|  | const uint8_t smode_tbl[4] = { | 
|  | 0x00u, 0x06u, 0x01u, 0x07u | 
|  | }; | 
|  |  | 
|  | const uint8_t smode48_tbl[4] = { | 
|  | 0x04u, 0x02u, 0x05u, 0x03u | 
|  | }; | 
|  |  | 
|  | static void qmspi_set_signalling_mode(QMSPI_Type *regs, uint32_t smode) | 
|  | { | 
|  | const uint8_t *ptbl; | 
|  | uint32_t m; | 
|  |  | 
|  | ptbl = smode_tbl; | 
|  | if (((regs->MODE >> MCHP_QMSPI_M_FDIV_POS) & | 
|  | MCHP_QMSPI_M_FDIV_MASK0) == 1) { | 
|  | ptbl = smode48_tbl; | 
|  | } | 
|  |  | 
|  | m = (uint32_t)ptbl[smode & 0x03]; | 
|  | regs->MODE = (regs->MODE & ~(MCHP_QMSPI_M_SIG_MASK)) | 
|  | | (m << MCHP_QMSPI_M_SIG_POS); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * QMSPI HW support single, dual, and quad. | 
|  | * Return QMSPI Control/Descriptor register encoded value. | 
|  | */ | 
|  | static uint32_t qmspi_config_get_lines(const struct spi_config *config) | 
|  | { | 
|  | uint32_t qlines; | 
|  |  | 
|  | switch (config->operation & SPI_LINES_MASK) { | 
|  | case SPI_LINES_SINGLE: | 
|  | qlines = MCHP_QMSPI_C_IFM_1X; | 
|  | break; | 
|  | #if DT_INST_PROP(0, lines) > 1 | 
|  | case SPI_LINES_DUAL: | 
|  | qlines = MCHP_QMSPI_C_IFM_2X; | 
|  | break; | 
|  | #endif | 
|  | #if DT_INST_PROP(0, lines) > 2 | 
|  | case SPI_LINES_QUAD: | 
|  | qlines = MCHP_QMSPI_C_IFM_4X; | 
|  | break; | 
|  | #endif | 
|  | default: | 
|  | qlines = 0xffu; | 
|  | } | 
|  |  | 
|  | return qlines; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Configure QMSPI. | 
|  | * NOTE: QMSPI can control two chip selects. At this time we use CS0# only. | 
|  | */ | 
|  | static int qmspi_configure(const struct device *dev, | 
|  | const struct spi_config *config) | 
|  | { | 
|  | const struct spi_qmspi_config *cfg = dev->config; | 
|  | struct spi_qmspi_data *data = dev->data; | 
|  | QMSPI_Type *regs = cfg->regs; | 
|  | uint32_t smode; | 
|  |  | 
|  | if (spi_context_configured(&data->ctx, config)) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if (config->operation & (SPI_TRANSFER_LSB | SPI_OP_MODE_SLAVE | 
|  | | SPI_MODE_LOOP)) { | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | smode = qmspi_config_get_lines(config); | 
|  | if (smode == 0xff) { | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | regs->CTRL = smode; | 
|  |  | 
|  | /* Use the requested or next highest possible frequency */ | 
|  | qmspi_set_frequency(regs, config->frequency); | 
|  |  | 
|  | smode = 0; | 
|  | if ((config->operation & SPI_MODE_CPHA) != 0U) { | 
|  | smode |= (1ul << 0); | 
|  | } | 
|  |  | 
|  | if ((config->operation & SPI_MODE_CPOL) != 0U) { | 
|  | smode |= (1ul << 1); | 
|  | } | 
|  |  | 
|  | qmspi_set_signalling_mode(regs, smode); | 
|  |  | 
|  | if (SPI_WORD_SIZE_GET(config->operation) != 8) { | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | /* chip select */ | 
|  | smode = regs->MODE & ~(MCHP_QMSPI_M_CS_MASK); | 
|  | #if DT_INST_PROP(0, chip_select) == 0 | 
|  | smode |= MCHP_QMSPI_M_CS0; | 
|  | #else | 
|  | smode |= MCHP_QMSPI_M_CS1; | 
|  | #endif | 
|  | regs->MODE = smode; | 
|  |  | 
|  | /* chip select timing */ | 
|  | regs->CSTM = cfg->cs_timing; | 
|  |  | 
|  | data->ctx.config = config; | 
|  |  | 
|  | /* Add driver specific data to SPI context structure */ | 
|  | spi_context_cs_configure(&data->ctx); | 
|  |  | 
|  | regs->MODE |= MCHP_QMSPI_M_ACTIVATE; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Transmit dummy clocks - QMSPI will generate requested number of | 
|  | * SPI clocks with I/O pins tri-stated. | 
|  | * Single mode: 1 bit per clock -> IFM field = 00b. Max 0x7fff clocks | 
|  | * Dual mode: 2 bits per clock  -> IFM field = 01b. Max 0x3fff clocks | 
|  | * Quad mode: 4 bits per clock  -> IFM fiels = 1xb. Max 0x1fff clocks | 
|  | * QMSPI unit size set to bits. | 
|  | */ | 
|  | static int qmspi_tx_dummy_clocks(QMSPI_Type *regs, uint32_t nclocks) | 
|  | { | 
|  | uint32_t descr, ifm, qstatus; | 
|  |  | 
|  | ifm = regs->CTRL & MCHP_QMSPI_C_IFM_MASK; | 
|  | descr = ifm | MCHP_QMSPI_C_TX_DIS | MCHP_QMSPI_C_XFR_UNITS_BITS | 
|  | | MCHP_QMSPI_C_DESCR_LAST | MCHP_QMSPI_C_DESCR0; | 
|  |  | 
|  | if (ifm & 0x01) { | 
|  | nclocks <<= 1; | 
|  | } else if (ifm & 0x02) { | 
|  | nclocks <<= 2; | 
|  | } | 
|  | descr |= (nclocks << MCHP_QMSPI_C_XFR_NUNITS_POS); | 
|  |  | 
|  | descr_wr(regs, 0, descr); | 
|  |  | 
|  | regs->CTRL |= MCHP_QMSPI_C_DESCR_EN; | 
|  | regs->IEN = 0; | 
|  | regs->STS = 0xfffffffful; | 
|  |  | 
|  | regs->EXE = MCHP_QMSPI_EXE_START; | 
|  | do { | 
|  | qstatus = regs->STS; | 
|  | if (qstatus & MCHP_QMSPI_STS_PROG_ERR) { | 
|  | return -EIO; | 
|  | } | 
|  | } while ((qstatus & MCHP_QMSPI_STS_DONE) == 0); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Return unit size power of 2 given number of bytes to transfer. | 
|  | */ | 
|  | static uint32_t qlen_shift(uint32_t len) | 
|  | { | 
|  | uint32_t ushift; | 
|  |  | 
|  | /* is len a multiple of 4 or 16? */ | 
|  | if ((len & 0x0F) == 0) { | 
|  | ushift = 4; | 
|  | } else if ((len & 0x03) == 0) { | 
|  | ushift = 2; | 
|  | } else { | 
|  | ushift = 0; | 
|  | } | 
|  |  | 
|  | return ushift; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Return QMSPI unit size of the number of units field in QMSPI | 
|  | * control/descriptor register. | 
|  | * Input: power of 2 unit size 4, 2, or 0(default) corresponding | 
|  | * to 16, 4, or 1 byte units. | 
|  | */ | 
|  | static uint32_t get_qunits(uint32_t qshift) | 
|  | { | 
|  | if (qshift == 4) { | 
|  | return MCHP_QMSPI_C_XFR_UNITS_16; | 
|  | } else if (qshift == 2) { | 
|  | return MCHP_QMSPI_C_XFR_UNITS_4; | 
|  | } else { | 
|  | return MCHP_QMSPI_C_XFR_UNITS_1; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Allocate(build) one or more descriptors. | 
|  | * QMSPI contains 16 32-bit descriptor registers used as a linked | 
|  | * list of operations. Using only 32-bits there are limitations. | 
|  | * Each descriptor is limited to 0x7FFF units where unit size can | 
|  | * be 1, 4, or 16 bytes. A descriptor can perform transmit or receive | 
|  | * but not both simultaneously. Order of descriptor processing is specified | 
|  | * by the first descriptor field of the control register, the next descriptor | 
|  | * fields in each descriptor, and the descriptors last flag. | 
|  | */ | 
|  | static int qmspi_descr_alloc(QMSPI_Type *regs, const struct spi_buf *txb, | 
|  | int didx, bool is_tx) | 
|  | { | 
|  | uint32_t descr, qshift, n, nu; | 
|  | int dn; | 
|  |  | 
|  | if (didx >= MCHP_QMSPI_MAX_DESCR) { | 
|  | return -EAGAIN; | 
|  | } | 
|  |  | 
|  | if (txb->len == 0) { | 
|  | return didx; /* nothing to do */ | 
|  | } | 
|  |  | 
|  | /* b[1:0] IFM and b[3:2] transmit mode */ | 
|  | descr = (regs->CTRL & MCHP_QMSPI_C_IFM_MASK); | 
|  | if (is_tx) { | 
|  | descr |= MCHP_QMSPI_C_TX_DATA; | 
|  | } else { | 
|  | descr |= MCHP_QMSPI_C_RX_EN; | 
|  | } | 
|  |  | 
|  | /* b[11:10] unit size 1, 4, or 16 bytes */ | 
|  | qshift = qlen_shift(txb->len); | 
|  | nu = txb->len >> qshift; | 
|  | descr |= get_qunits(qshift); | 
|  |  | 
|  | do { | 
|  | descr &= 0x0FFFul; | 
|  |  | 
|  | dn = didx + 1; | 
|  | /* b[15:12] next descriptor pointer */ | 
|  | descr |= ((dn & MCHP_QMSPI_C_NEXT_DESCR_MASK0) << | 
|  | MCHP_QMSPI_C_NEXT_DESCR_POS); | 
|  |  | 
|  | n = nu; | 
|  | if (n > MCHP_QMSPI_C_MAX_UNITS) { | 
|  | n = MCHP_QMSPI_C_MAX_UNITS; | 
|  | } | 
|  |  | 
|  | descr |= (n << MCHP_QMSPI_C_XFR_NUNITS_POS); | 
|  | descr_wr(regs, didx, descr); | 
|  |  | 
|  | if (dn < MCHP_QMSPI_MAX_DESCR) { | 
|  | didx++; | 
|  | } else { | 
|  | return -EAGAIN; | 
|  | } | 
|  |  | 
|  | nu -= n; | 
|  | } while (nu); | 
|  |  | 
|  | return dn; | 
|  | } | 
|  |  | 
|  | static int qmspi_tx(QMSPI_Type *regs, const struct spi_buf *tx_buf, | 
|  | bool close) | 
|  | { | 
|  | const uint8_t *p = tx_buf->buf; | 
|  | size_t tlen = tx_buf->len; | 
|  | uint32_t descr; | 
|  | int didx; | 
|  |  | 
|  | if (tlen == 0) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Buffer pointer is NULL and number of bytes != 0 ? */ | 
|  | if (p == NULL) { | 
|  | return qmspi_tx_dummy_clocks(regs, tlen); | 
|  | } | 
|  |  | 
|  | didx = qmspi_descr_alloc(regs, tx_buf, 0, true); | 
|  | if (didx < 0) { | 
|  | return didx; | 
|  | } | 
|  |  | 
|  | /* didx points to last allocated descriptor + 1 */ | 
|  | __ASSERT(didx > 0, "QMSPI descriptor index=%d expected > 0\n", didx); | 
|  | didx--; | 
|  |  | 
|  | descr = descr_rd(regs, didx) | MCHP_QMSPI_C_DESCR_LAST; | 
|  | if (close) { | 
|  | descr |= MCHP_QMSPI_C_CLOSE; | 
|  | } | 
|  | descr_wr(regs, didx, descr); | 
|  |  | 
|  | regs->CTRL = (regs->CTRL & MCHP_QMSPI_C_IFM_MASK) | | 
|  | MCHP_QMSPI_C_DESCR_EN | MCHP_QMSPI_C_DESCR0; | 
|  | regs->IEN = 0; | 
|  | regs->STS = 0xfffffffful; | 
|  |  | 
|  | /* preload TX_FIFO */ | 
|  | while (tlen) { | 
|  | tlen--; | 
|  | txb_wr8(regs, *p); | 
|  | p++; | 
|  |  | 
|  | if (regs->STS & MCHP_QMSPI_STS_TXBF_RO) { | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | regs->EXE = MCHP_QMSPI_EXE_START; | 
|  |  | 
|  | if (regs->STS & MCHP_QMSPI_STS_PROG_ERR) { | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | while (tlen) { | 
|  |  | 
|  | while (regs->STS & MCHP_QMSPI_STS_TXBF_RO) { | 
|  | } | 
|  |  | 
|  | txb_wr8(regs, *p); | 
|  | p++; | 
|  | tlen--; | 
|  | } | 
|  |  | 
|  | /* Wait for TX FIFO to drain and last byte to be clocked out */ | 
|  | for (;;) { | 
|  | if (regs->STS & MCHP_QMSPI_STS_DONE) { | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int qmspi_rx(QMSPI_Type *regs, const struct spi_buf *rx_buf, | 
|  | bool close) | 
|  | { | 
|  | uint8_t *p = rx_buf->buf; | 
|  | size_t rlen = rx_buf->len; | 
|  | uint32_t descr; | 
|  | int didx; | 
|  | uint8_t data_byte; | 
|  |  | 
|  | if (rlen == 0) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | didx = qmspi_descr_alloc(regs, rx_buf, 0, false); | 
|  | if (didx < 0) { | 
|  | return didx; | 
|  | } | 
|  |  | 
|  | /* didx points to last allocated descriptor + 1 */ | 
|  | __ASSERT_NO_MSG(didx > 0); | 
|  | didx--; | 
|  |  | 
|  | descr = descr_rd(regs, didx) | MCHP_QMSPI_C_DESCR_LAST; | 
|  | if (close) { | 
|  | descr |= MCHP_QMSPI_C_CLOSE; | 
|  | } | 
|  | descr_wr(regs, didx, descr); | 
|  |  | 
|  | regs->CTRL = (regs->CTRL & MCHP_QMSPI_C_IFM_MASK) | 
|  | | MCHP_QMSPI_C_DESCR_EN | MCHP_QMSPI_C_DESCR0; | 
|  | regs->IEN = 0; | 
|  | regs->STS = 0xfffffffful; | 
|  |  | 
|  | /* | 
|  | * Trigger read based on the descriptor(s) programmed above. | 
|  | * QMSPI will generate clocks until the RX FIFO is filled. | 
|  | * More clocks will be generated as we pull bytes from the RX FIFO. | 
|  | * QMSPI Programming error will be triggered after start if | 
|  | * descriptors were programmed options that cannot be enabled | 
|  | * simultaneously. | 
|  | */ | 
|  | regs->EXE = MCHP_QMSPI_EXE_START; | 
|  | if (regs->STS & MCHP_QMSPI_STS_PROG_ERR) { | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | while (rlen) { | 
|  | if (!(regs->STS & MCHP_QMSPI_STS_RXBE_RO)) { | 
|  | data_byte = rxb_rd8(regs); | 
|  | if (p != NULL) { | 
|  | *p++ = data_byte; | 
|  | } | 
|  | rlen--; | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int qmspi_transceive(const struct device *dev, | 
|  | const struct spi_config *config, | 
|  | const struct spi_buf_set *tx_bufs, | 
|  | const struct spi_buf_set *rx_bufs) | 
|  | { | 
|  | const struct spi_qmspi_config *cfg = dev->config; | 
|  | struct spi_qmspi_data *data = dev->data; | 
|  | QMSPI_Type *regs = cfg->regs; | 
|  | const struct spi_buf *ptx; | 
|  | const struct spi_buf *prx; | 
|  | size_t nb; | 
|  | uint32_t descr, last_didx; | 
|  | int err; | 
|  |  | 
|  | spi_context_lock(&data->ctx, false, NULL); | 
|  |  | 
|  | err = qmspi_configure(dev, config); | 
|  | if (err != 0) { | 
|  | goto done; | 
|  | } | 
|  |  | 
|  | spi_context_cs_control(&data->ctx, true); | 
|  |  | 
|  | if (tx_bufs != NULL) { | 
|  | ptx = tx_bufs->buffers; | 
|  | nb = tx_bufs->count; | 
|  | while (nb--) { | 
|  | err = qmspi_tx(regs, ptx, false); | 
|  | if (err != 0) { | 
|  | goto done; | 
|  | } | 
|  | ptx++; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (rx_bufs != NULL) { | 
|  | prx = rx_bufs->buffers; | 
|  | nb = rx_bufs->count; | 
|  | while (nb--) { | 
|  | err = qmspi_rx(regs, prx, false); | 
|  | if (err != 0) { | 
|  | goto done; | 
|  | } | 
|  | prx++; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * If caller doesn't need CS# held asserted then find the last | 
|  | * descriptor, set its close flag, and set stop. | 
|  | */ | 
|  | if (!(config->operation & SPI_HOLD_ON_CS)) { | 
|  | /* Get last descriptor from status register */ | 
|  | last_didx = (regs->STS >> MCHP_QMSPI_C_NEXT_DESCR_POS) | 
|  | & MCHP_QMSPI_C_NEXT_DESCR_MASK0; | 
|  | descr = descr_rd(regs, last_didx) | MCHP_QMSPI_C_CLOSE; | 
|  | descr_wr(regs, last_didx, descr); | 
|  | regs->EXE = MCHP_QMSPI_EXE_STOP; | 
|  | } | 
|  |  | 
|  | spi_context_cs_control(&data->ctx, false); | 
|  |  | 
|  | done: | 
|  | spi_context_release(&data->ctx, err); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | static int qmspi_transceive_sync(const struct device *dev, | 
|  | const struct spi_config *config, | 
|  | const struct spi_buf_set *tx_bufs, | 
|  | const struct spi_buf_set *rx_bufs) | 
|  | { | 
|  | return qmspi_transceive(dev, config, tx_bufs, rx_bufs); | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_SPI_ASYNC | 
|  | static int qmspi_transceive_async(const struct device *dev, | 
|  | const struct spi_config *config, | 
|  | const struct spi_buf_set *tx_bufs, | 
|  | const struct spi_buf_set *rx_bufs, | 
|  | struct k_poll_signal *async) | 
|  | { | 
|  | return -ENOTSUP; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static int qmspi_release(const struct device *dev, | 
|  | const struct spi_config *config) | 
|  | { | 
|  | struct spi_qmspi_data *data = dev->data; | 
|  | const struct spi_qmspi_config *cfg = dev->config; | 
|  | QMSPI_Type *regs = cfg->regs; | 
|  |  | 
|  | /* Force CS# to de-assert on next unit boundary */ | 
|  | regs->EXE = MCHP_QMSPI_EXE_STOP; | 
|  |  | 
|  | while (regs->STS & MCHP_QMSPI_STS_ACTIVE_RO) { | 
|  | } | 
|  |  | 
|  | spi_context_unlock_unconditionally(&data->ctx); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Initialize QMSPI controller. | 
|  | * Disable sleep control. | 
|  | * Disable and clear interrupt status. | 
|  | * Initialize SPI context. | 
|  | * QMSPI will be configured and enabled when the transceive API is called. | 
|  | */ | 
|  | static int qmspi_init(const struct device *dev) | 
|  | { | 
|  | const struct spi_qmspi_config *cfg = dev->config; | 
|  | struct spi_qmspi_data *data = dev->data; | 
|  | QMSPI_Type *regs = cfg->regs; | 
|  |  | 
|  | mchp_pcr_periph_slp_ctrl(PCR_QMSPI, MCHP_PCR_SLEEP_DIS); | 
|  |  | 
|  | regs->MODE = MCHP_QMSPI_M_SRST; | 
|  |  | 
|  | MCHP_GIRQ_CLR_EN(cfg->girq, cfg->girq_pos); | 
|  | MCHP_GIRQ_SRC_CLR(cfg->girq, cfg->girq_pos); | 
|  |  | 
|  | MCHP_GIRQ_BLK_CLREN(cfg->girq); | 
|  | NVIC_ClearPendingIRQ(cfg->girq_nvic_direct); | 
|  |  | 
|  | spi_context_unlock_unconditionally(&data->ctx); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static const struct spi_driver_api spi_qmspi_driver_api = { | 
|  | .transceive = qmspi_transceive_sync, | 
|  | #ifdef CONFIG_SPI_ASYNC | 
|  | .transceive_async = qmspi_transceive_async, | 
|  | #endif | 
|  | .release = qmspi_release, | 
|  | }; | 
|  |  | 
|  |  | 
|  | #define XEC_QMSPI_CS_TIMING_VAL(a, b, c, d) (((a) & 0xFu) \ | 
|  | | (((b) & 0xFu) << 8) \ | 
|  | | (((c) & 0xFu) << 16) \ | 
|  | | (((d) & 0xFu) << 24)) | 
|  |  | 
|  |  | 
|  | #define XEC_QMSPI_0_CS_TIMING XEC_QMSPI_CS_TIMING_VAL(			\ | 
|  | DT_INST_PROP(0, dcsckon),		\ | 
|  | DT_INST_PROP(0, dckcsoff),		\ | 
|  | DT_INST_PROP(0, dldh),			\ | 
|  | DT_INST_PROP(0, dcsda)) | 
|  |  | 
|  | #if DT_NODE_HAS_STATUS(DT_INST(0, microchip_xec_qmspi), okay) | 
|  |  | 
|  | static const struct spi_qmspi_config spi_qmspi_0_config = { | 
|  | .regs = (QMSPI_Type *)DT_INST_REG_ADDR(0), | 
|  | .cs_timing = XEC_QMSPI_0_CS_TIMING, | 
|  | .girq = MCHP_QMSPI_GIRQ_NUM, | 
|  | .girq_pos = MCHP_QMSPI_GIRQ_POS, | 
|  | .girq_nvic_direct = MCHP_QMSPI_GIRQ_NVIC_DIRECT, | 
|  | .irq_pri = DT_INST_IRQ(0, priority), | 
|  | .chip_sel = DT_INST_PROP(0, chip_select), | 
|  | .width = DT_INST_PROP(0, lines) | 
|  | }; | 
|  |  | 
|  | static struct spi_qmspi_data spi_qmspi_0_dev_data = { | 
|  | SPI_CONTEXT_INIT_LOCK(spi_qmspi_0_dev_data, ctx), | 
|  | SPI_CONTEXT_INIT_SYNC(spi_qmspi_0_dev_data, ctx) | 
|  | }; | 
|  |  | 
|  | DEVICE_AND_API_INIT(spi_xec_qmspi_0, | 
|  | DT_INST_LABEL(0), | 
|  | &qmspi_init, &spi_qmspi_0_dev_data, | 
|  | &spi_qmspi_0_config, POST_KERNEL, | 
|  | CONFIG_SPI_INIT_PRIORITY, &spi_qmspi_driver_api); | 
|  |  | 
|  | #endif /* DT_NODE_HAS_STATUS(DT_INST(0, microchip_xec_qmspi), okay) */ |