diff --git a/boards/arm/secureiot1702/doc/secureiot1702.rst b/boards/arm/secureiot1702/doc/secureiot1702.rst index 67b2dcfb5698..4819947f5116 100644 --- a/boards/arm/secureiot1702/doc/secureiot1702.rst +++ b/boards/arm/secureiot1702/doc/secureiot1702.rst @@ -32,6 +32,7 @@ The following devices are supported: - Nested Vectored Interrupt Controller (NVIC) - System Tick System Clock (SYSTICK) - Serial Ports (NS16550) +- Quad Master-only SPI controller (QMSPI) Connections and IOs diff --git a/boards/arm/secureiot1702/pinmux.c b/boards/arm/secureiot1702/pinmux.c index 09d595b8d191..3c1ef775aa34 100644 --- a/boards/arm/secureiot1702/pinmux.c +++ b/boards/arm/secureiot1702/pinmux.c @@ -29,6 +29,23 @@ static int board_init(struct device *dev) UART1_INST->CONFIG = 0; UART1_INST->ACTIVATE = 1; #endif +#ifdef CONFIG_SPI_0 + /* Set clock request, muxing and drive strength */ + PCR_INST->CLK_REQ_4_b.QSPI_CLK_REQ = 1; + GPIO_040_076_INST->GPIO_055_PIN_CONTROL_b.MUX_CONTROL = 2; + GPIO_040_076_INST->GPIO_056_PIN_CONTROL_b.MUX_CONTROL = 2; + GPIO_200_236_INST->GPIO_223_PIN_CONTROL_b.MUX_CONTROL = 2; + GPIO_200_236_INST->GPIO_224_PIN_CONTROL_b.MUX_CONTROL = 2; + GPIO_200_236_INST->GPIO_227_PIN_CONTROL_b.MUX_CONTROL = 2; + GPIO_000_036_INST->GPIO_016_PIN_CONTROL_b.MUX_CONTROL = 2; + GPIO_PIN_CONTROL_2_INST->GPIO_055_PIN_CONTROL_2_b.DRIVE_STRENGTH = 2; + GPIO_PIN_CONTROL_2_INST->GPIO_056_PIN_CONTROL_2_b.DRIVE_STRENGTH = 2; + GPIO_PIN_CONTROL_2_INST->GPIO_223_PIN_CONTROL_2_b.DRIVE_STRENGTH = 2; + GPIO_PIN_CONTROL_2_INST->GPIO_224_PIN_CONTROL_2_b.DRIVE_STRENGTH = 2; + GPIO_PIN_CONTROL_2_INST->GPIO_227_PIN_CONTROL_2_b.DRIVE_STRENGTH = 2; + GPIO_PIN_CONTROL_2_INST->GPIO_016_PIN_CONTROL_2_b.DRIVE_STRENGTH = 2; +#endif + return 0; } diff --git a/drivers/spi/CMakeLists.txt b/drivers/spi/CMakeLists.txt index eb6eb0357c25..f66c65813d2f 100644 --- a/drivers/spi/CMakeLists.txt +++ b/drivers/spi/CMakeLists.txt @@ -3,6 +3,7 @@ zephyr_library() zephyr_library_sources_ifdef(CONFIG_SPI_CC13XX_CC26XX spi_cc13xx_cc26xx.c) +zephyr_library_sources_ifdef(CONFIG_SPI_CEC_QMSPI spi_cec_qmspi.c) zephyr_library_sources_ifdef(CONFIG_SPI_DW spi_dw.c) zephyr_library_sources_ifdef(CONFIG_SPI_INTEL spi_intel.c) zephyr_library_sources_ifdef(CONFIG_SPI_STM32 spi_ll_stm32.c) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index bcb0b959a57b..b98d84b9b6c8 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -180,6 +180,8 @@ config SPI_6_OP_MODES endif # SPI_6 +source "drivers/spi/Kconfig.cec_qmspi" + config SPI_INTEL bool "Intel SPI controller driver" depends on CPU_MINUTEIA diff --git a/drivers/spi/Kconfig.cec_qmspi b/drivers/spi/Kconfig.cec_qmspi new file mode 100644 index 000000000000..71a24959fbc6 --- /dev/null +++ b/drivers/spi/Kconfig.cec_qmspi @@ -0,0 +1,23 @@ +# +# Copyright (c) 2019 Crypta Labs Ltd. +# +# SPDX-License-Identifier: Apache-2.0 +# + +config SPI_CEC_QMSPI + bool + prompt "CEC MCU QMSPI controller driver" + depends on SPI && SOC_SERIES_CEC + default n + help + Enable Quad Master SPI support on the CEC series of processors. + +if SPI_CEC_QMSPI + +config SPI_CEC_QMSPI_INTERRUPT + bool "CEC QMSPI Interrupt Support" + help + Enable Interrupt support for the SPI Driver of CEC QMSPI. + NOTE: Work in progress. Not ready yet. + +endif # SPI_CEC_QMSPI diff --git a/drivers/spi/spi_cec_qmspi.c b/drivers/spi/spi_cec_qmspi.c new file mode 100644 index 000000000000..ed9d07b23995 --- /dev/null +++ b/drivers/spi/spi_cec_qmspi.c @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2017 Crypta Labs Ltd + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define LOG_DOMAIN "QMSPI CEC" +#define LOG_LEVEL CONFIG_SPI_LOG_LEVEL +#include +LOG_MODULE_REGISTER(spi_cec); + +#include +#include +#include + +#include "spi_context.h" + +typedef void (*irq_config_func_t)(struct device *port); + +struct spi_qmspi_data { + struct spi_context ctx; +}; + +struct spi_qmspi_config { + QMSPI_INST_Type *spi; +}; + +static inline struct spi_qmspi_data *get_dev_data(struct device *dev) +{ + return dev->driver_data; +} + +static inline const struct spi_qmspi_config *get_dev_config(struct device *dev) +{ + return dev->config->config_info; +} + +static int spi_qmspi_configure(struct device *dev, + const struct spi_config *spi_cfg) +{ + const struct spi_qmspi_config *cfg = get_dev_config(dev); + struct spi_qmspi_data *data = get_dev_data(dev); + QMSPI_INST_Type *spi = cfg->spi; + + if (SPI_OP_MODE_GET(spi_cfg->operation) == SPI_OP_MODE_SLAVE || + (spi_cfg->operation & SPI_TRANSFER_LSB) || + (spi_cfg->frequency == 0)) { + return -ENOTSUP; + } + + if (SPI_WORD_SIZE_GET(spi_cfg->operation) != 8) { + return -ENOTSUP; + } + + spi->QMSPI_MODE_b.CLOCK_DIVIDE = + SYSCLK_DEFAULT_IOSC_HZ / spi_cfg->frequency; + spi->QMSPI_MODE_b.ACTIVATE = 1; + spi->QMSPI_MODE_b.CPOL = + SPI_MODE_GET(spi_cfg->operation) == SPI_MODE_CPOL; + spi->QMSPI_MODE_b.CHPA_MISO = + SPI_MODE_GET(spi_cfg->operation) == SPI_MODE_CPHA; + spi->QMSPI_MODE_b.CHPA_MOSI = + SPI_MODE_GET(spi_cfg->operation) == SPI_MODE_CPHA; + + spi->QMSPI_CONTROL_b.CLOSE_TRANSFER_ENABLE = + !(spi_cfg->operation & SPI_HOLD_ON_CS); + spi->QMSPI_CONTROL_b.TRANSFER_UNITS = 1; /* Unit of byte */ + switch (spi_cfg->operation & SPI_LINES_MASK) { + case SPI_LINES_SINGLE: + spi->QMSPI_CONTROL_b.INTERFACE_MODE = 0; + break; +#if 0 +#error These modes require transfer direction which is not yet supported. + case SPI_LINES_DUAL: + spi->QMSPI_CONTROL_b.INTERFACE_MODE = 1; + break; + case SPI_LINES_QUAD: + spi->QMSPI_CONTROL_b.INTERFACE_MODE = 2; + break; +#endif + default: + return -ENOTSUP; + } + + /* At this point, it's mandatory to set this on the context! */ + data->ctx.config = spi_cfg; + + spi_context_cs_configure(&data->ctx); + + LOG_DBG("Installed config %p: freq %uHz," + " mode %u/%u/%u, slave %u, if_mode=%d", + spi_cfg, spi_cfg->frequency, + (SPI_MODE_GET(spi_cfg->operation) & SPI_MODE_CPOL) ? 1 : 0, + (SPI_MODE_GET(spi_cfg->operation) & SPI_MODE_CPHA) ? 1 : 0, + (SPI_MODE_GET(spi_cfg->operation) & SPI_MODE_LOOP) ? 1 : 0, + spi_cfg->slave, + spi->QMSPI_CONTROL_b.INTERFACE_MODE); + + return 0; +} + +static void spi_qmspi_complete(struct spi_qmspi_data *data, + QMSPI_INST_Type *spi, int status) +{ +#ifdef CONFIG_SPI_CEC_QMSPI_INTERRUPT + spi->QMSPI_INTERRUPT_ENABLE_b.TRANSFER_COMPLETE_ENABLE = 0; +#endif + + spi_context_cs_control(&data->ctx, false); + + spi->QMSPI_EXECUTE_b.STOP = 1; + spi->QMSPI_EXECUTE_b.CLEAR_DATA_BUFFER = 1; + + if (status) { + spi->QMSPI_MODE_b.SOFT_RESET = 1; + while (spi->QMSPI_MODE_b.SOFT_RESET) + ; + } + +#ifdef CONFIG_SPI_CEC_QMSPI_INTERRUPT + spi_context_complete(&data->ctx, status); +#endif +} + +static int spi_qmspi_shift(QMSPI_INST_Type *spi, struct spi_qmspi_data *data) +{ + u8_t byte; + + if (spi_context_tx_on(&data->ctx)) { + byte = 0; + if (data->ctx.tx_buf) { + byte = UNALIGNED_GET((u8_t *)(data->ctx.tx_buf)); + } + spi_context_update_tx(&data->ctx, 1, 1); + while (!spi->QMSPI_STATUS_b.TRANSMIT_BUFFER_EMPTY) + ; + sys_write8(byte, (mem_addr_t)&spi->QMSPI_TRAMSMIT_BUFFER); + } + + if (spi_context_rx_on(&data->ctx)) { + while (spi->QMSPI_STATUS_b.RECEIVE_BUFFER_EMPTY) + ; + byte = sys_read8((mem_addr_t)&spi->QMSPI_RECEIVE_BUFFER); + if (data->ctx.rx_buf) { + UNALIGNED_PUT(byte, (u8_t *)data->ctx.rx_buf); + } + spi_context_update_rx(&data->ctx, 1, 1); + } + + /* Check for error bits 0b11100 */ + return spi->QMSPI_STATUS & 0x1c; +} + +static int transceive(struct device *dev, + const struct spi_config *spi_cfg, + const struct spi_buf_set *tx_bufs, + const struct spi_buf_set *rx_bufs, + struct k_poll_signal *async) +{ + const struct spi_qmspi_config *cfg = get_dev_config(dev); + struct spi_qmspi_data *data = get_dev_data(dev); + QMSPI_INST_Type *spi = cfg->spi; + int ret; + +#ifndef CONFIG_SPI_CEC_QMSPI_INTERRUPT + if (async != NULL) { + return -ENOTSUP; + } +#endif + + spi_context_lock(&data->ctx, async != NULL, async); + + ret = spi_qmspi_configure(dev, spi_cfg); + if (ret) { + return ret; + } + + spi_context_buffers_setup(&data->ctx, tx_bufs, rx_bufs, 1); + + spi->QMSPI_CONTROL_b.TRANSFER_LENGTH = + spi_context_transfer_length(&data->ctx); + + spi->QMSPI_CONTROL_b.RX_TRANSFER_ENABLE = rx_bufs ? 1 : 0; + spi->QMSPI_CONTROL_b.TX_TRANSFER_ENABLE = tx_bufs ? 1 : 0; + + spi_context_cs_control(&data->ctx, true); + spi->QMSPI_EXECUTE_b.START = 1; + +#ifdef CONFIG_SPI_CEC_QMSPI_INTERRUPT +#error Not supported yet. + spi->QMSPI_INTERRUPT_ENABLE_b.TRANSFER_COMPLETE_ENABLE = 1; + ret = spi_context_wait_for_completion(&data->ctx); +#else + do { + ret = spi_qmspi_shift(spi, data); + } while (!ret && + (spi_context_tx_on(&data->ctx) || + spi_context_rx_on(&data->ctx))); + + while (!spi->QMSPI_STATUS_b.TRANSMIT_BUFFER_EMPTY) + ; + + spi_qmspi_complete(data, spi, ret); +#endif + + spi_context_release(&data->ctx, ret); + + if (ret) { + LOG_ERR("error mask 0x%x", ret); + } + + return ret ? -EIO : 0; +} + +static int spi_qmspi_transceive(struct device *dev, + const struct spi_config *spi_cfg, + const struct spi_buf_set *tx_bufs, + const struct spi_buf_set *rx_bufs) +{ + return transceive(dev, spi_cfg, tx_bufs, rx_bufs, NULL); +} + +#ifdef CONFIG_POLL +static int spi_qmspi_transceive_async(struct device *dev, + const struct spi_config *spi_cfg, + const struct spi_buf_set *tx_bufs, + const struct spi_buf_set *rx_bufs, + struct k_poll_signal *async) +{ + return transceive(dev, spi_cfg, tx_bufs, rx_bufs, async); +} +#endif /* CONFIG_POLL */ + +static int spi_qmspi_release(struct device *dev, + const struct spi_config *config) +{ + const struct spi_qmspi_config *cfg = get_dev_config(dev); + struct spi_qmspi_data *data = get_dev_data(dev); + QMSPI_INST_Type *spi = cfg->spi; + + spi_context_unlock_unconditionally(&data->ctx); + spi->QMSPI_MODE_b.ACTIVATE = 0; + + return 0; +} + +static const struct spi_driver_api api_funcs = { + .transceive = spi_qmspi_transceive, +#ifdef CONFIG_POLL + .transceive_async = spi_qmspi_transceive_async, +#endif + .release = spi_qmspi_release, +}; + +static int spi_qmspi_init(struct device *dev) +{ + const struct spi_qmspi_config *cfg = dev->config->config_info; + struct spi_qmspi_data *data = dev->driver_data; + QMSPI_INST_Type *spi = cfg->spi; + + spi_context_unlock_unconditionally(&data->ctx); + + /* Reset block */ + spi->QMSPI_MODE_b.ACTIVATE = 1; + spi->QMSPI_MODE_b.SOFT_RESET = 1; + while (spi->QMSPI_MODE_b.SOFT_RESET) + ; + spi->QMSPI_MODE_b.ACTIVATE = 0; + + return 0; +} + +#ifdef DT_MICROCHIP_CEC_QMSPI_0_LABEL + +static const struct spi_qmspi_config spi_qmspi_cfg_0 = { + .spi = (QMSPI_INST_Type *) DT_MICROCHIP_CEC_QMSPI_0_BASE_ADDRESS, +}; + +static struct spi_qmspi_data spi_qmspi_dev_data_0 = { + SPI_CONTEXT_INIT_LOCK(spi_qmspi_dev_data_0, ctx), + SPI_CONTEXT_INIT_SYNC(spi_qmspi_dev_data_0, ctx), +}; + +DEVICE_AND_API_INIT(spi_qmspi_0, DT_MICROCHIP_CEC_QMSPI_0_LABEL, + &spi_qmspi_init, &spi_qmspi_dev_data_0, + &spi_qmspi_cfg_0, + POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, + &api_funcs); + +#endif diff --git a/drivers/spi/spi_context.h b/drivers/spi/spi_context.h index 3797cd62430a..900ef5a1ece3 100644 --- a/drivers/spi/spi_context.h +++ b/drivers/spi/spi_context.h @@ -363,6 +363,23 @@ static inline size_t spi_context_longest_current_buf(struct spi_context *ctx) return ctx->rx_len; } +static inline size_t spi_context_transfer_length(struct spi_context *ctx) +{ + size_t i, rx = 0, tx = 0; + + for (i = 0; i < ctx->rx_count; i++) { + rx += ctx->current_rx[i].len; + } + for (i = 0; i < ctx->tx_count; i++) { + tx += ctx->current_tx[i].len; + } + if (tx > rx) { + return tx; + } + return rx; +} + + #ifdef __cplusplus } #endif diff --git a/dts/arm/microchip/cec1702.dtsi b/dts/arm/microchip/cec1702.dtsi index c2a06db9c632..fd71a6bbab97 100644 --- a/dts/arm/microchip/cec1702.dtsi +++ b/dts/arm/microchip/cec1702.dtsi @@ -45,6 +45,15 @@ label = "UART_1"; reg-shift = <0>; }; + spi0: spi@40005400 { + compatible = "microchip,cec-qmspi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x40005400 0x44>; + interrupts = <91 0>; + label = "spi_0"; + status = "disabled"; + }; }; }; diff --git a/dts/bindings/spi/microchip,cec-qmspi.yaml b/dts/bindings/spi/microchip,cec-qmspi.yaml new file mode 100644 index 000000000000..70422258eb61 --- /dev/null +++ b/dts/bindings/spi/microchip,cec-qmspi.yaml @@ -0,0 +1,26 @@ +# +# Copyright (c) 2019 Crypta Labs Ltd. +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: Microchip CEC QMSPI +version: 0.1 + +description: > + This binding gives a base representation of the Microchip CEC QMSPI controller + +inherits: + !include spi.yaml + +properties: + compatible: + constraint: "microchip,cec-qmspi" + + reg: + type: array + description: mmio register space + generation: define + category: required + +... diff --git a/soc/arm/microchip_cec/Kconfig.defconfig b/soc/arm/microchip_cec/Kconfig.defconfig index e45d739b54e5..4b30a1a2433f 100644 --- a/soc/arm/microchip_cec/Kconfig.defconfig +++ b/soc/arm/microchip_cec/Kconfig.defconfig @@ -27,4 +27,11 @@ config UART_NS16550 endif # SERIAL +if SPI + +config SPI_CEC_QMSPI + default y + +endif # SPI + endif