/* Copyright 2017 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ /* QMSPI master module for MCHP MEC family */ #include "common.h" #include "console.h" #include "dma.h" #include "gpio.h" #include "hooks.h" #include "qmspi_chip.h" #include "registers.h" #include "spi.h" #include "spi_chip.h" #include "task.h" #include "timer.h" #include "util.h" #if defined(CONFIG_MCHP_GPSPI) && !defined(LFW) #include "gpspi_chip.h" #endif #include "tfdp_chip.h" #define CPUTS(outstr) cputs(CC_SPI, outstr) #define CPRINTS(format, args...) cprints(CC_SPI, format, ##args) #define SPI_BYTE_TRANSFER_TIMEOUT_US (3 * MSEC) #define SPI_BYTE_TRANSFER_POLL_INTERVAL_US 100 #if defined(CONFIG_MCHP_GPSPI) && defined(CHIP_FAMILY_MEC152X) #error "FORCED BUILD ERROR: MEC152X does not implement GPSPI!" #endif static const struct dma_option spi_rx_option[] = { { MCHP_DMAC_QMSPI0_RX, (void *)(MCHP_QMSPI0_RX_FIFO_ADDR), MCHP_DMA_XFER_SIZE(1) + MCHP_DMA_INC_MEM }, #if defined(CONFIG_MCHP_GPSPI) && !defined(LFW) #if CONFIG_MCHP_GPSPI & 0x01 { MCHP_DMAC_SPI0_RX, (void *)&MCHP_SPI_RD(0), MCHP_DMA_XFER_SIZE(1) + MCHP_DMA_INC_MEM }, #endif #if CONFIG_MCHP_GPSPI & 0x02 { MCHP_DMAC_SPI1_RX, (void *)&MCHP_SPI_RD(1), MCHP_DMA_XFER_SIZE(1) + MCHP_DMA_INC_MEM }, #endif #endif }; static const struct dma_option spi_tx_option[] = { { MCHP_DMAC_QMSPI0_TX, (void *)(MCHP_QMSPI0_TX_FIFO_ADDR), MCHP_DMA_XFER_SIZE(1) + MCHP_DMA_INC_MEM }, #if defined(CONFIG_MCHP_GPSPI) && !defined(LFW) #if CONFIG_MCHP_GPSPI & 0x01 { MCHP_DMAC_SPI0_TX, (void *)&MCHP_SPI_TD(0), MCHP_DMA_XFER_SIZE(1) + MCHP_DMA_INC_MEM }, #endif #if CONFIG_MCHP_GPSPI & 0x02 { MCHP_DMAC_SPI1_TX, (void *)&MCHP_SPI_TD(1), MCHP_DMA_XFER_SIZE(1) + MCHP_DMA_INC_MEM }, #endif #endif }; /* only regular image needs mutex, LFW does not have scheduling */ #ifndef LFW static struct mutex spi_mutex[ARRAY_SIZE(spi_rx_option)]; /* * Acquire mutex for specified SPI controller/port. * Note if mutex is owned by another task this routine * will block until mutex is released. */ static void spi_mutex_lock(uint8_t hw_port) { uint32_t n; n = 0; #ifdef CONFIG_MCHP_GPSPI if (hw_port & 0xF0) { #if (CONFIG_MCHP_GPSPI & 0x03) == 0x03 n = (hw_port & 0x0F) + 1; #else n = 1; #endif } #endif mutex_lock(&spi_mutex[n]); } /* * Release mutex for specified SPI controller/port. */ static void spi_mutex_unlock(uint8_t hw_port) { uint32_t n; n = 0; #ifdef CONFIG_MCHP_GPSPI if (hw_port & 0xF0) { #if (CONFIG_MCHP_GPSPI & 0x03) == 0x03 n = (hw_port & 0x0F) + 1; #else n = 1; #endif } #endif mutex_unlock(&spi_mutex[n]); } #endif /* #ifndef LFW */ /* * Public SPI interface */ const void *spi_dma_option(const struct spi_device_t *spi_device, int is_tx) { uint32_t n; if (spi_device == NULL) return NULL; n = 0; #if defined(CONFIG_MCHP_GPSPI) && !defined(LFW) if (spi_device->port & 0xF0) { #if (CONFIG_MCHP_GPSPI & 0x03) == 0x03 n = (spi_device->port & 0x0F) + 1; #else n = 1; #endif } #endif if (is_tx) return &spi_tx_option[n]; else return &spi_rx_option[n]; } int spi_transaction_async(const struct spi_device_t *spi_device, const uint8_t *txdata, int txlen, uint8_t *rxdata, int rxlen) { int rc; if (spi_device == NULL) return EC_ERROR_INVAL; switch (spi_device->port) { #if defined(CONFIG_MCHP_GPSPI) && !defined(LFW) case GPSPI0_PORT: case GPSPI1_PORT: rc = gpspi_transaction_async(spi_device, txdata, txlen, rxdata, rxlen); break; #endif case QMSPI0_PORT: rc = qmspi_transaction_async(spi_device, txdata, txlen, rxdata, rxlen); break; default: rc = EC_ERROR_INVAL; } return rc; } int spi_transaction_flush(const struct spi_device_t *spi_device) { int rc; if (spi_device == NULL) return EC_ERROR_INVAL; switch (spi_device->port) { #if defined(CONFIG_MCHP_GPSPI) && !defined(LFW) case GPSPI0_PORT: case GPSPI1_PORT: rc = gpspi_transaction_flush(spi_device); break; #endif case QMSPI0_PORT: rc = qmspi_transaction_flush(spi_device); break; default: rc = EC_ERROR_INVAL; } return rc; } /* Wait for async response received but do not de-assert chip select */ int spi_transaction_wait(const struct spi_device_t *spi_device) { int rc; if (spi_device == NULL) return EC_ERROR_INVAL; switch (spi_device->port) { #if defined(CONFIG_MCHP_GPSPI) && !defined(LFW) #ifndef LFW case GPSPI0_PORT: case GPSPI1_PORT: rc = gpspi_transaction_wait(spi_device); break; #endif #endif case QMSPI0_PORT: rc = qmspi_transaction_wait(spi_device); break; default: rc = EC_ERROR_INVAL; } return rc; } /* * called from common/spi_flash.c * For tranfers reading less than the size of QMSPI RX FIFO call * a routine where reads use FIFO only no DMA. * GP-SPI only has a one byte RX FIFO but small data transfers will be OK * without the overhead of DMA setup. */ int spi_transaction(const struct spi_device_t *spi_device, const uint8_t *txdata, int txlen, uint8_t *rxdata, int rxlen) { int rc; if (spi_device == NULL) return EC_ERROR_PARAM1; #ifndef LFW spi_mutex_lock(spi_device->port); #endif rc = spi_transaction_async(spi_device, txdata, txlen, rxdata, rxlen); if (rc == EC_SUCCESS) rc = spi_transaction_flush(spi_device); #ifndef LFW spi_mutex_unlock(spi_device->port); #endif return rc; } /** * Enable SPI port and associated controller * * @param spi_device SPI device * @param enable * @return EC_SUCCESS or EC_ERROR_INVAL if port is unrecognized * @note called from common/spi_flash.c * * spi_device->port is defined as * bits[3:0] = controller instance * bits[7:4] = controller family 0 = QMSPI, 1 = GPSPI */ int spi_enable(const struct spi_device_t *spi_device, int enable) { int rc; uint8_t hw_port = spi_device->port; rc = EC_ERROR_INVAL; if ((hw_port & 0xF0) == QMSPI_CLASS) rc = qmspi_enable(hw_port, enable); #if defined(CONFIG_MCHP_GPSPI) && !defined(LFW) if ((hw_port & 0xF0) == GPSPI_CLASS) rc = gpspi_enable(hw_port, enable); #endif return rc; }