| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #include <string.h> |
| #include <errno.h> |
| #include <assert.h> |
| #include <os/mynewt.h> |
| #include <mcu/cmsis_nvic.h> |
| #include <hal/hal_spi.h> |
| #include <mcu/nrf5340_net_hal.h> |
| #include <nrf.h> |
| |
| #if MYNEWT_VAL(SPI_0_MASTER) || MYNEWT_VAL(SPI_0_SLAVE) |
| |
| #define SPIM_TXD_MAXCNT_MAX 0xffff |
| |
| /* Used to disable all interrupts */ |
| #define NRF_SPI_IRQ_DISABLE_ALL 0xffffffff |
| |
| /* |
| * Slave states |
| * |
| * IDLE: Slave not ready to be used. If master attempts to access |
| * slave it will receive the default character |
| * ACQ_SEM: Slave is attempting to acquire semaphore. |
| * READY: Slave is ready for master to send it data |
| * |
| */ |
| #define HAL_SPI_SLAVE_STATE_IDLE (0) |
| #define HAL_SPI_SLAVE_STATE_ACQ_SEM (1) |
| #define HAL_SPI_SLAVE_STATE_READY (2) |
| |
| #if MYNEWT_VAL(SPI_0_MASTER) |
| struct nrf5340_net_hal_spi |
| { |
| uint8_t spi_xfr_flag; |
| uint8_t dummy_rx; |
| uint16_t nhs_buflen; |
| uint16_t nhs_bytes_txd; |
| struct hal_spi_settings spi_cfg; |
| |
| /* Pointers to tx/rx buffers */ |
| uint8_t *nhs_txbuf; |
| uint8_t *nhs_rxbuf; |
| |
| /* Callback and arguments */ |
| hal_spi_txrx_cb txrx_cb_func; |
| void *txrx_cb_arg; |
| }; |
| #endif |
| |
| #if MYNEWT_VAL(SPI_0_SLAVE) |
| struct nrf5340_net_hal_spi |
| { |
| uint8_t slave_state; |
| uint16_t nhs_buflen; |
| uint16_t nhs_bytes_txd; |
| struct hal_spi_settings spi_cfg; |
| |
| /* Pointers to tx/rx buffers */ |
| uint8_t *nhs_txbuf; |
| uint8_t *nhs_rxbuf; |
| |
| /* Callback and arguments */ |
| hal_spi_txrx_cb txrx_cb_func; |
| void *txrx_cb_arg; |
| }; |
| #endif |
| |
| static struct nrf5340_net_hal_spi nrf5340_net_hal_spi0; |
| |
| static void |
| nrf5340_net_spi0_irq_handler(void) |
| { |
| struct nrf5340_net_hal_spi *spi = &nrf5340_net_hal_spi0; |
| uint16_t xfr_bytes; |
| #if MYNEWT_VAL(SPI_0_MASTER) |
| uint16_t len; |
| #endif |
| |
| os_trace_isr_enter(); |
| |
| #if MYNEWT_VAL(SPI_0_MASTER) |
| if (NRF_SPIM0_NS->EVENTS_END) { |
| NRF_SPIM0_NS->EVENTS_END = 0; |
| |
| /* Should not occur but if no transfer just leave */ |
| if (spi->spi_xfr_flag != 0) { |
| /* Are there more bytes to send? */ |
| xfr_bytes = NRF_SPIM0_NS->TXD.AMOUNT; |
| spi->nhs_bytes_txd += xfr_bytes; |
| if (spi->nhs_bytes_txd < spi->nhs_buflen) { |
| spi->nhs_txbuf += xfr_bytes; |
| len = spi->nhs_buflen - spi->nhs_bytes_txd; |
| len = min(SPIM_TXD_MAXCNT_MAX, len); |
| NRF_SPIM0_NS->TXD.PTR = (uint32_t)spi->nhs_txbuf; |
| NRF_SPIM0_NS->TXD.MAXCNT = len; |
| |
| /* If no rxbuf, we need to set rxbuf and maxcnt to 1 */ |
| if (spi->nhs_rxbuf) { |
| spi->nhs_rxbuf += xfr_bytes; |
| NRF_SPIM0_NS->RXD.PTR = (uint32_t)spi->nhs_rxbuf; |
| NRF_SPIM0_NS->RXD.MAXCNT = len; |
| } |
| NRF_SPIM0_NS->TASKS_START = 1; |
| } else { |
| if (spi->txrx_cb_func) { |
| spi->txrx_cb_func(spi->txrx_cb_arg, spi->nhs_buflen); |
| |
| } |
| spi->spi_xfr_flag = 0; |
| NRF_SPIM0_NS->INTENCLR = SPIM_INTENSET_END_Msk; |
| } |
| } |
| } |
| #endif |
| |
| #if MYNEWT_VAL(SPI_0_SLAVE) |
| /* Semaphore acquired event */ |
| if (NRF_SPIS0_NS->EVENTS_ACQUIRED) { |
| NRF_SPIS0_NS->EVENTS_ACQUIRED = 0; |
| |
| if (spi->slave_state == HAL_SPI_SLAVE_STATE_ACQ_SEM) { |
| if (spi->nhs_txbuf == NULL) { |
| NRF_SPIS0_NS->TXD.PTR = 0; |
| NRF_SPIS0_NS->TXD.MAXCNT = 0; |
| } else { |
| NRF_SPIS0_NS->TXD.PTR = (uint32_t)spi->nhs_txbuf; |
| NRF_SPIS0_NS->TXD.MAXCNT = spi->nhs_buflen; |
| } |
| |
| if (spi->nhs_rxbuf == NULL) { |
| NRF_SPIS0_NS->RXD.PTR = 0; |
| NRF_SPIS0_NS->RXD.MAXCNT = 0; |
| } else { |
| NRF_SPIS0_NS->RXD.PTR = (uint32_t)spi->nhs_rxbuf; |
| NRF_SPIS0_NS->RXD.MAXCNT = spi->nhs_buflen; |
| } |
| NRF_SPIS0_NS->TASKS_RELEASE = 1; |
| spi->slave_state = HAL_SPI_SLAVE_STATE_READY; |
| } |
| } |
| |
| /* SPI transaction complete */ |
| if (NRF_SPIS0_NS->EVENTS_END) { |
| NRF_SPIS0_NS->EVENTS_END = 0; |
| if (spi->slave_state == HAL_SPI_SLAVE_STATE_READY) { |
| if (spi->txrx_cb_func) { |
| /* Get transfer length */ |
| if (spi->nhs_txbuf == NULL) { |
| xfr_bytes = NRF_SPIS0_NS->RXD.AMOUNT; |
| } else { |
| xfr_bytes = NRF_SPIS0_NS->TXD.AMOUNT; |
| } |
| spi->txrx_cb_func(spi->txrx_cb_arg, xfr_bytes); |
| } |
| spi->slave_state = HAL_SPI_SLAVE_STATE_IDLE; |
| } |
| } |
| #endif |
| |
| os_trace_isr_exit(); |
| } |
| |
| #if MYNEWT_VAL(SPI_0_MASTER) |
| static void |
| hal_spi_master_stop_transfer(void) |
| { |
| NRF_SPIM0_NS->TASKS_STOP = 1; |
| while (!NRF_SPIM0_NS->EVENTS_STOPPED) { |
| }; |
| NRF_SPIM0_NS->EVENTS_STOPPED = 0; |
| } |
| |
| static int |
| hal_spi_config_master(struct nrf5340_net_hal_spi *spi, |
| struct hal_spi_settings *settings) |
| { |
| int rc; |
| uint32_t nrf_config; |
| uint32_t frequency; |
| NRF_GPIO_Type *port; |
| uint32_t pin; |
| |
| memcpy(&spi->spi_cfg, settings, sizeof(*settings)); |
| |
| /* |
| * Configure SCK. NOTE: this is done here in the config API as the data |
| * mode is not set at init time so we do it here when we configure the SPI. |
| */ |
| pin = NRF_SPIM0_NS->PSEL.SCK & SPIM_PSEL_SCK_PIN_Msk; |
| if (NRF_SPIM0_NS->PSEL.SCK & SPIM_PSEL_SCK_PORT_Msk) { |
| port = NRF_P1_NS; |
| } else { |
| port = NRF_P0_NS; |
| } |
| |
| if (settings->data_mode <= HAL_SPI_MODE1) { |
| port->OUTCLR = (1UL << pin); |
| } else { |
| port->OUTSET = (1UL << pin); |
| } |
| port->PIN_CNF[pin] = |
| (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos) | |
| (GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos); |
| |
| /* Only 8-bit word sizes supported. */ |
| rc = 0; |
| switch (settings->word_size) { |
| case HAL_SPI_WORD_SIZE_8BIT: |
| break; |
| default: |
| rc = EINVAL; |
| break; |
| } |
| |
| switch (settings->data_mode) { |
| case HAL_SPI_MODE0: |
| nrf_config = (SPIM_CONFIG_CPOL_ActiveHigh << SPIM_CONFIG_CPOL_Pos) | |
| (SPIM_CONFIG_CPHA_Leading << SPIM_CONFIG_CPHA_Pos); |
| break; |
| case HAL_SPI_MODE1: |
| nrf_config = (SPIM_CONFIG_CPOL_ActiveHigh << SPIM_CONFIG_CPOL_Pos) | |
| (SPIM_CONFIG_CPHA_Trailing << SPIM_CONFIG_CPHA_Pos); |
| break; |
| case HAL_SPI_MODE2: |
| nrf_config = (SPIM_CONFIG_CPOL_ActiveLow << SPIM_CONFIG_CPOL_Pos) | |
| (SPIM_CONFIG_CPHA_Leading << SPIM_CONFIG_CPHA_Pos); |
| break; |
| case HAL_SPI_MODE3: |
| nrf_config = (SPIM_CONFIG_CPOL_ActiveLow << SPIM_CONFIG_CPOL_Pos) | |
| (SPIM_CONFIG_CPHA_Trailing << SPIM_CONFIG_CPHA_Pos); |
| break; |
| default: |
| nrf_config = 0; |
| rc = EINVAL; |
| break; |
| } |
| |
| /* NOTE: msb first is 0 so no check done */ |
| if (settings->data_order == HAL_SPI_LSB_FIRST) { |
| nrf_config |= SPIM_CONFIG_ORDER_LsbFirst; |
| } |
| NRF_SPIM0_NS->CONFIG = nrf_config; |
| |
| switch (settings->baudrate) { |
| case 125: |
| frequency = SPIM_FREQUENCY_FREQUENCY_K125; |
| break; |
| case 250: |
| frequency = SPIM_FREQUENCY_FREQUENCY_K250; |
| break; |
| case 500: |
| frequency = SPIM_FREQUENCY_FREQUENCY_K500; |
| break; |
| case 1000: |
| frequency = SPIM_FREQUENCY_FREQUENCY_M1; |
| break; |
| case 2000: |
| frequency = SPIM_FREQUENCY_FREQUENCY_M2; |
| break; |
| case 4000: |
| frequency = SPIM_FREQUENCY_FREQUENCY_M4; |
| break; |
| case 8000: |
| frequency = SPIM_FREQUENCY_FREQUENCY_M8; |
| break; |
| default: |
| frequency = 0; |
| rc = EINVAL; |
| break; |
| } |
| NRF_SPIM0_NS->FREQUENCY = frequency; |
| |
| return rc; |
| } |
| #endif |
| |
| #if MYNEWT_VAL(SPI_0_SLAVE) |
| static int |
| hal_spi_config_slave(struct nrf5340_net_hal_spi *spi, |
| struct hal_spi_settings *settings) |
| { |
| int rc; |
| uint32_t nrf_config; |
| |
| rc = 0; |
| switch (settings->data_mode) { |
| case HAL_SPI_MODE0: |
| nrf_config = (SPIS_CONFIG_CPOL_ActiveHigh << SPIS_CONFIG_CPOL_Pos) | |
| (SPIS_CONFIG_CPHA_Leading << SPIS_CONFIG_CPHA_Pos); |
| break; |
| case HAL_SPI_MODE1: |
| nrf_config = (SPIS_CONFIG_CPOL_ActiveHigh << SPIS_CONFIG_CPOL_Pos) | |
| (SPIS_CONFIG_CPHA_Trailing << SPIS_CONFIG_CPHA_Pos); |
| break; |
| case HAL_SPI_MODE2: |
| nrf_config = (SPIS_CONFIG_CPOL_ActiveLow << SPIS_CONFIG_CPOL_Pos) | |
| (SPIS_CONFIG_CPHA_Leading << SPIS_CONFIG_CPHA_Pos); |
| break; |
| case HAL_SPI_MODE3: |
| nrf_config = (SPIS_CONFIG_CPOL_ActiveLow << SPIS_CONFIG_CPOL_Pos) | |
| (SPIS_CONFIG_CPHA_Trailing << SPIS_CONFIG_CPHA_Pos); |
| break; |
| default: |
| nrf_config = 0; |
| rc = EINVAL; |
| break; |
| } |
| |
| if (settings->data_order == HAL_SPI_LSB_FIRST) { |
| nrf_config |= SPIS_CONFIG_ORDER_LsbFirst; |
| } |
| NRF_SPIS0_NS->CONFIG = nrf_config; |
| |
| /* Only 8-bit word sizes supported. */ |
| switch (settings->word_size) { |
| case HAL_SPI_WORD_SIZE_8BIT: |
| break; |
| default: |
| rc = EINVAL; |
| break; |
| } |
| |
| return rc; |
| } |
| #endif |
| |
| #if MYNEWT_VAL(SPI_0_MASTER) |
| static int |
| hal_spi_init_master(struct nrf5340_net_hal_spi *spi, |
| struct nrf5340_net_hal_spi_cfg *cfg) |
| { |
| NRF_GPIO_Type *port; |
| uint32_t pin; |
| |
| /* Configure MOSI */ |
| port = HAL_GPIO_PORT(cfg->mosi_pin); |
| pin = HAL_GPIO_INDEX(cfg->mosi_pin); |
| port->OUTCLR = (1UL << pin); |
| port->PIN_CNF[pin] = |
| ((uint32_t)GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos) | |
| ((uint32_t)GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos); |
| |
| /* Configure MISO */ |
| port = HAL_GPIO_PORT(cfg->miso_pin); |
| pin = HAL_GPIO_INDEX(cfg->miso_pin); |
| port->PIN_CNF[pin] = |
| ((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) | |
| ((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos); |
| |
| NRF_SPIM0_NS->PSEL.SCK = cfg->sck_pin; |
| NRF_SPIM0_NS->PSEL.MOSI = cfg->mosi_pin; |
| NRF_SPIM0_NS->PSEL.MISO = cfg->miso_pin; |
| |
| NRF_SPIM0_NS->INTENCLR = NRF_SPI_IRQ_DISABLE_ALL; |
| NVIC_SetVector(SPIM0_SPIS0_TWIM0_TWIS0_UARTE0_IRQn, |
| (uint32_t)nrf5340_net_spi0_irq_handler); |
| NVIC_SetPriority(SPIM0_SPIS0_TWIM0_TWIS0_UARTE0_IRQn, |
| (1 << __NVIC_PRIO_BITS) - 1); |
| NVIC_ClearPendingIRQ(SPIM0_SPIS0_TWIM0_TWIS0_UARTE0_IRQn); |
| NVIC_EnableIRQ(SPIM0_SPIS0_TWIM0_TWIS0_UARTE0_IRQn); |
| |
| return 0; |
| } |
| #endif |
| |
| #if MYNEWT_VAL(SPI_0_SLAVE) |
| static int |
| hal_spi_init_slave(struct nrf5340_net_hal_spi *spi, |
| struct nrf5340_net_hal_spi_cfg *cfg) |
| { |
| NRF_GPIO_Type *port; |
| uint32_t pin; |
| |
| /* NOTE: making this pin an input is correct! See datasheet */ |
| port = HAL_GPIO_PORT(cfg->miso_pin); |
| pin = HAL_GPIO_INDEX(cfg->miso_pin); |
| port->PIN_CNF[pin] = |
| ((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) | |
| ((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos); |
| |
| port = HAL_GPIO_PORT(cfg->mosi_pin); |
| pin = HAL_GPIO_INDEX(cfg->mosi_pin); |
| port->PIN_CNF[pin] = |
| ((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) | |
| ((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos); |
| |
| port = HAL_GPIO_PORT(cfg->ss_pin); |
| pin = HAL_GPIO_INDEX(cfg->ss_pin); |
| port->PIN_CNF[pin] = |
| ((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) | |
| ((uint32_t)GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) | |
| ((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos); |
| |
| port = HAL_GPIO_PORT(cfg->sck_pin); |
| pin = HAL_GPIO_INDEX(cfg->sck_pin); |
| port->PIN_CNF[pin] = |
| ((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) | |
| ((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos); |
| |
| NRF_SPIS0_NS->PSEL.SCK = cfg->sck_pin; |
| NRF_SPIS0_NS->PSEL.MOSI = cfg->mosi_pin; |
| NRF_SPIS0_NS->PSEL.MISO = cfg->miso_pin; |
| NRF_SPIS0_NS->PSEL.CSN = cfg->ss_pin; |
| |
| /* Disable interrupt and clear any interrupt events */ |
| NRF_SPIS0_NS->INTENCLR = SPIS_INTENSET_ACQUIRED_Msk | SPIS_INTENSET_END_Msk; |
| NRF_SPIS0_NS->EVENTS_END = 0; |
| NRF_SPIS0_NS->EVENTS_ACQUIRED = 0; |
| |
| /* Enable END_ACQUIRE shortcut. */ |
| NRF_SPIS0_NS->SHORTS = SPIS_SHORTS_END_ACQUIRE_Msk; |
| |
| /* Set interrupt vector and enable IRQ */ |
| NVIC_SetVector(SPIM0_SPIS0_TWIM0_TWIS0_UARTE0_IRQn, |
| (uint32_t)nrf5340_net_spi0_irq_handler); |
| NVIC_SetPriority(SPIM0_SPIS0_TWIM0_TWIS0_UARTE0_IRQn, |
| (1 << __NVIC_PRIO_BITS) - 1); |
| NVIC_ClearPendingIRQ(SPIM0_SPIS0_TWIM0_TWIS0_UARTE0_IRQn); |
| NVIC_EnableIRQ(SPIM0_SPIS0_TWIM0_TWIS0_UARTE0_IRQn); |
| |
| return 0; |
| } |
| #endif |
| |
| /** |
| * Initialize the SPI, given by spi_num. |
| * |
| * @param spi_num The number of the SPI to initialize |
| * @param cfg HW/MCU specific configuration, |
| * passed to the underlying implementation, providing extra |
| * configuration. |
| * @param spi_type SPI type (master or slave) |
| * |
| * @return int 0 on success, non-zero error code on failure. |
| */ |
| int |
| hal_spi_init(int spi_num, void *cfg, uint8_t spi_type) |
| { |
| struct nrf5340_net_hal_spi *spi = &nrf5340_net_hal_spi0; |
| |
| /* Check for valid arguments */ |
| if (spi_num != 0 || cfg == NULL) { |
| return EINVAL; |
| } |
| |
| #if MYNEWT_VAL(SPI_0_MASTER) |
| if (spi_type != HAL_SPI_TYPE_MASTER) { |
| return EINVAL; |
| } |
| |
| return hal_spi_init_master(spi, (struct nrf5340_net_hal_spi_cfg *)cfg); |
| #endif |
| |
| #if MYNEWT_VAL(SPI_0_SLAVE) |
| if (spi_type != HAL_SPI_TYPE_SLAVE) { |
| return EINVAL; |
| } |
| |
| return hal_spi_init_slave(spi, (struct nrf5340_net_hal_spi_cfg *)cfg); |
| #endif |
| } |
| |
| int |
| hal_spi_init_hw(uint8_t spi_num, uint8_t spi_type, |
| const struct hal_spi_hw_settings *cfg) |
| { |
| struct nrf5340_net_hal_spi_cfg hal_cfg; |
| |
| hal_cfg.sck_pin = cfg->pin_sck; |
| hal_cfg.mosi_pin = cfg->pin_mosi; |
| hal_cfg.miso_pin = cfg->pin_miso; |
| hal_cfg.ss_pin = cfg->pin_ss; |
| |
| return hal_spi_init(spi_num, &hal_cfg, spi_type); |
| } |
| |
| /** |
| * Configure the spi. Must be called after the spi is initialized (after |
| * hal_spi_init is called) and when the spi is disabled (user must call |
| * hal_spi_disable if the spi has been enabled through hal_spi_enable prior |
| * to calling this function). Can also be used to reconfigure an initialized |
| * SPI (assuming it is disabled as described previously). |
| * |
| * @param spi_num The number of the SPI to configure. |
| * @param psettings The settings to configure this SPI with |
| * |
| * @return int 0 on success, non-zero error code on failure. |
| */ |
| int |
| hal_spi_config(int spi_num, struct hal_spi_settings *settings) |
| { |
| struct nrf5340_net_hal_spi *spi = &nrf5340_net_hal_spi0; |
| |
| if (spi_num != 0) { |
| return EINVAL; |
| } |
| |
| #if MYNEWT_VAL(SPI_0_MASTER) |
| if (NRF_SPIM0_NS->ENABLE != 0) { |
| return EINVAL; |
| } |
| |
| return hal_spi_config_master(spi, settings); |
| #endif |
| |
| #if MYNEWT_VAL(SPI_0_SLAVE) |
| if (NRF_SPIS0_NS->ENABLE != 0) { |
| return EINVAL; |
| } |
| |
| return hal_spi_config_slave(spi, settings); |
| #endif |
| } |
| |
| /** |
| * Enables the SPI. This does not start a transmit or receive operation; |
| * it is used for power mgmt. Cannot be called when a SPI transfer is in |
| * progress. |
| * |
| * @param spi_num |
| * |
| * @return int 0 on success, non-zero error code on failure. |
| */ |
| int |
| hal_spi_enable(int spi_num) |
| { |
| struct nrf5340_net_hal_spi *spi = &nrf5340_net_hal_spi0; |
| |
| if (spi_num != 0 || spi->txrx_cb_func == NULL) { |
| return EINVAL; |
| } |
| |
| #if MYNEWT_VAL(SPI_0_MASTER) |
| NRF_SPIM0_NS->EVENTS_END = 0; |
| NRF_SPIM0_NS->ENABLE = (SPIM_ENABLE_ENABLE_Enabled << SPIM_ENABLE_ENABLE_Pos); |
| #endif |
| |
| #if MYNEWT_VAL(SPI_0_SLAVE) |
| NRF_SPIS0_NS->EVENTS_END = 0; |
| NRF_SPIS0_NS->EVENTS_ACQUIRED = 0; |
| NRF_SPIS0_NS->INTENSET = SPIS_INTENSET_END_Msk | SPIS_INTENSET_ACQUIRED_Msk; |
| NRF_SPIS0_NS->ENABLE = (SPIS_ENABLE_ENABLE_Enabled << SPIS_ENABLE_ENABLE_Pos); |
| #endif |
| |
| return 0; |
| } |
| |
| /** |
| * Disables the SPI. Used for power mgmt. It will halt any current SPI transfers |
| * in progress. |
| * |
| * @param spi_num |
| * |
| * @return int 0 on success, non-zero error code on failure. |
| */ |
| int |
| hal_spi_disable(int spi_num) |
| { |
| struct nrf5340_net_hal_spi *spi = &nrf5340_net_hal_spi0; |
| |
| if (spi_num != 0) { |
| return -EINVAL; |
| } |
| |
| #if MYNEWT_VAL(SPI_0_MASTER) |
| NRF_SPIM0_NS->INTENCLR = NRF_SPI_IRQ_DISABLE_ALL; |
| |
| if (spi->spi_xfr_flag) { |
| hal_spi_master_stop_transfer(); |
| spi->spi_xfr_flag = 0; |
| } |
| NRF_SPIM0_NS->ENABLE = 0; |
| #endif |
| |
| #if MYNEWT_VAL(SPI_0_SLAVE) |
| NRF_SPIS0_NS->INTENCLR = NRF_SPI_IRQ_DISABLE_ALL; |
| NRF_SPIS0_NS->EVENTS_END = 0; |
| NRF_SPIS0_NS->EVENTS_ACQUIRED = 0; |
| NRF_SPIS0_NS->ENABLE = 0; |
| spi->slave_state = HAL_SPI_SLAVE_STATE_IDLE; |
| #endif |
| |
| spi->nhs_txbuf = NULL; |
| spi->nhs_rxbuf = NULL; |
| spi->nhs_buflen = 0; |
| spi->nhs_bytes_txd = 0; |
| |
| return 0; |
| } |
| |
| /** |
| * Sets the txrx callback (executed at interrupt context) when the |
| * buffer is transferred by the master or the slave using the non-blocking API. |
| * Cannot be called when the spi is enabled. This callback will also be called |
| * when chip select is de-asserted on the slave. |
| * |
| * NOTE: This callback is only used for the non-blocking interface and must |
| * be called prior to using the non-blocking API. |
| * |
| * @param spi_num SPI interface on which to set callback |
| * @param txrx Callback function |
| * @param arg Argument to be passed to callback function |
| * |
| * @return int 0 on success, non-zero error code on failure. |
| */ |
| int |
| hal_spi_set_txrx_cb(int spi_num, hal_spi_txrx_cb txrx_cb, void *arg) |
| { |
| struct nrf5340_net_hal_spi *spi = &nrf5340_net_hal_spi0; |
| |
| if (spi_num != 0) { |
| return EINVAL; |
| } |
| |
| #if MYNEWT_VAL(SPI_0_MASTER) |
| if (NRF_SPIM0_NS->ENABLE != 0) { |
| return EINVAL; |
| } |
| #endif |
| |
| #if MYNEWT_VAL(SPI_0_SLAVE) |
| if (NRF_SPIS0_NS->ENABLE != 0) { |
| return EINVAL; |
| } |
| #endif |
| |
| spi->txrx_cb_func = txrx_cb; |
| spi->txrx_cb_arg = arg; |
| |
| return 0; |
| } |
| |
| /** |
| * Non-blocking interface to send a buffer and store received values. Can be |
| * used for both master and slave SPI types. The user must configure the |
| * callback (using hal_spi_set_txrx_cb); the txrx callback is executed at |
| * interrupt context when the buffer is sent. |
| * |
| * The transmit and receive buffers are either arrays of 8-bit (uint8_t) |
| * values or 16-bit values depending on whether the spi is configured for 8 bit |
| * data or more than 8 bits per value. The 'cnt' parameter is the number of |
| * 8-bit or 16-bit values. Thus, if 'cnt' is 10, txbuf/rxbuf would point to an |
| * array of size 10 (in bytes) if the SPI is using 8-bit data; otherwise |
| * txbuf/rxbuf would point to an array of size 20 bytes (ten, uint16_t values). |
| * |
| * NOTE: these buffers are in the native endian-ness of the platform. |
| * |
| * MASTER: master sends all the values in the buffer and stores the |
| * stores the values in the receive buffer if rxbuf is not NULL. |
| * The txbuf parameter cannot be NULL |
| * SLAVE: Slave "preloads" the data to be sent to the master (values |
| * stored in txbuf) and places received data from master in rxbuf |
| * (if not NULL). The txrx callback occurs when len values are |
| * transferred or master de-asserts chip select. If txbuf is NULL, |
| * the slave transfers its default byte. Both rxbuf and txbuf cannot |
| * be NULL. |
| * |
| * @param spi_num SPI interface to use |
| * @param txbuf Pointer to buffer where values to transmit are stored. |
| * @param rxbuf Pointer to buffer to store values received from peer. |
| * @param cnt Number of 8-bit or 16-bit values to be transferred. |
| * |
| * @return int 0 on success, non-zero error code on failure. |
| */ |
| int |
| hal_spi_txrx_noblock(int spi_num, void *txbuf, void *rxbuf, int len) |
| { |
| struct nrf5340_net_hal_spi *spi = &nrf5340_net_hal_spi0; |
| |
| if (spi_num != 0 || (spi->txrx_cb_func == NULL) || (len == 0)) { |
| return EINVAL; |
| } |
| |
| #if MYNEWT_VAL(SPI_0_MASTER) |
| /* Must have a txbuf for master! */ |
| if (txbuf == NULL) { |
| return EINVAL; |
| } |
| |
| /* Not allowed if transfer in progress */ |
| if (spi->spi_xfr_flag) { |
| return EBUSY; |
| } |
| |
| NRF_SPIM0_NS->INTENCLR = SPIM_INTENCLR_END_Msk; |
| spi->spi_xfr_flag = 1; |
| |
| /* Set internal data structure information */ |
| spi->nhs_bytes_txd = 0; |
| spi->nhs_buflen = len; |
| spi->nhs_txbuf = txbuf; |
| |
| len = min(SPIM_TXD_MAXCNT_MAX, len); |
| |
| /* Set chip registers */ |
| NRF_SPIM0_NS->TXD.PTR = (uint32_t)txbuf; |
| NRF_SPIM0_NS->TXD.MAXCNT = len; |
| |
| /* If no rxbuf, we need to set rxbuf and maxcnt to 1 */ |
| spi->nhs_rxbuf = rxbuf; |
| if (rxbuf == NULL) { |
| NRF_SPIM0_NS->RXD.PTR = (uint32_t)&spi->dummy_rx; |
| NRF_SPIM0_NS->RXD.MAXCNT = 1; |
| } else { |
| NRF_SPIM0_NS->RXD.PTR = (uint32_t)rxbuf; |
| NRF_SPIM0_NS->RXD.MAXCNT = len; |
| } |
| |
| NRF_SPIM0_NS->EVENTS_END = 0; |
| NRF_SPIM0_NS->EVENTS_STOPPED = 0; |
| NRF_SPIM0_NS->TASKS_START = 1; |
| NRF_SPIM0_NS->INTENSET = SPIM_INTENSET_END_Msk; |
| #endif |
| |
| #if MYNEWT_VAL(SPI_0_SLAVE) |
| /* Must have txbuf or rxbuf */ |
| if ((txbuf == NULL) && (rxbuf == NULL)) { |
| return EINVAL; |
| } |
| |
| /* XXX: what to do here? */ |
| if (len > 255) { |
| return EINVAL; |
| } |
| |
| /* |
| * Ready the slave for a transfer. Do not allow this to be called |
| * if the slave has already been readied or is requesting the |
| * semaphore |
| */ |
| if (spi->slave_state != HAL_SPI_SLAVE_STATE_IDLE) { |
| return EBUSY; |
| } |
| |
| spi->nhs_rxbuf = rxbuf; |
| spi->nhs_txbuf = txbuf; |
| spi->nhs_buflen = len; |
| spi->slave_state = HAL_SPI_SLAVE_STATE_ACQ_SEM; |
| NRF_SPIS0_NS->TASKS_ACQUIRE = 1; |
| #endif |
| |
| return 0; |
| } |
| |
| /** |
| * Sets the default value transferred by the slave. Not valid for master |
| * |
| * @param spi_num SPI interface to use |
| * |
| * @return int 0 on success, non-zero error code on failure. |
| */ |
| int |
| hal_spi_slave_set_def_tx_val(int spi_num, uint16_t val) |
| { |
| #if MYNEWT_VAL(SPI_0_SLAVE) |
| if (spi_num != 0){ |
| return EINVAL; |
| } |
| |
| NRF_SPIS0_NS->DEF = (uint8_t)val; |
| NRF_SPIS0_NS->ORC = (uint8_t)val; |
| |
| return 0; |
| #else |
| return EINVAL; |
| #endif |
| } |
| |
| /** |
| * This aborts the current transfer but keeps the spi enabled. |
| * |
| * @param spi_num SPI interface on which transfer should be aborted. |
| * |
| * @return int 0 on success, non-zero error code on failure. |
| * |
| * NOTE: does not return an error if no transfer was in progress. |
| */ |
| int |
| hal_spi_abort(int spi_num) |
| { |
| if (spi_num != 0) { |
| return EINVAL; |
| } |
| |
| #if MYNEWT_VAL(SPI_0_MASTER) |
| if (nrf5340_net_hal_spi0.spi_xfr_flag) { |
| NRF_SPIM0_NS->INTENCLR = NRF_SPI_IRQ_DISABLE_ALL; |
| hal_spi_master_stop_transfer(); |
| nrf5340_net_hal_spi0.spi_xfr_flag = 0; |
| NRF_SPIM0_NS->INTENSET = SPIM_INTENSET_END_Msk; |
| } |
| #endif |
| |
| #if MYNEWT_VAL(SPI_0_SLAVE) |
| /* Only way I can see doing this is to disable, then re-enable */ |
| rc = hal_spi_disable(spi_num); |
| if (rc) { |
| return rc; |
| } |
| rc = hal_spi_enable(spi_num); |
| if (rc) { |
| return rc; |
| } |
| #endif |
| |
| return 0; |
| } |
| |
| #endif |