| /* |
| * 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 <hal/hal_spi.h> |
| #include <hal/hal_spi.h> |
| #include <mcu/cmsis_nvic.h> |
| #include "mcu/nrf51_hal.h" |
| #include "nrf51_bitfields.h" |
| |
| /* XXX: |
| * 1) what about stats? |
| * 2) Dealing with errors (OVERFLOW, OVERREAD) |
| * 3) Dont think I need dummy_rx as I can set master RX maxcnt to zero. |
| */ |
| /* IRQ handler type */ |
| typedef void (*nrf51_spi_irq_handler_t)(void); |
| |
| /* The maximum number of SPI interfaces we will allow */ |
| #define NRF51_HAL_SPI_MAX (2) |
| |
| /* 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) |
| |
| struct nrf51_hal_spi { |
| uint8_t spi_type; |
| uint8_t spi_xfr_flag; /* Master only */ |
| uint8_t slave_state; /* Slave only */ |
| uint16_t nhs_buflen; |
| uint16_t nhs_rxd_bytes; |
| uint16_t nhs_txd_bytes; |
| struct hal_spi_settings spi_cfg; /* Slave and master */ |
| union { |
| NRF_SPI_Type *spim; |
| NRF_SPIS_Type *spis; |
| } nhs_spi; |
| |
| /* IRQ number */ |
| IRQn_Type irq_num; |
| |
| /* 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; |
| }; |
| |
| #if MYNEWT_VAL(SPI_0_MASTER) |
| struct nrf51_hal_spi nrf51_hal_spi0; |
| #endif |
| #if MYNEWT_VAL(SPI_1_MASTER) || MYNEWT_VAL(SPI_1_SLAVE) |
| struct nrf51_hal_spi nrf51_hal_spi1; |
| #endif |
| |
| static const struct nrf51_hal_spi *nrf51_hal_spis[NRF51_HAL_SPI_MAX] = { |
| #if MYNEWT_VAL(SPI_0_MASTER) |
| &nrf51_hal_spi0, |
| #else |
| NULL, |
| #endif |
| #if MYNEWT_VAL(SPI_1_MASTER) || MYNEWT_VAL(SPI_1_SLAVE) |
| &nrf51_hal_spi1 |
| #else |
| NULL |
| #endif |
| }; |
| |
| #define NRF51_HAL_SPI_RESOLVE(__n, __v) \ |
| if ((__n) >= NRF51_HAL_SPI_MAX) { \ |
| rc = EINVAL; \ |
| goto err; \ |
| } \ |
| (__v) = (struct nrf51_hal_spi *) nrf51_hal_spis[(__n)]; \ |
| if ((__v) == NULL) { \ |
| rc = EINVAL; \ |
| goto err; \ |
| } |
| |
| #if (MYNEWT_VAL(SPI_0_MASTER) || MYNEWT_VAL(SPI_1_MASTER) ) |
| static void |
| nrf51_irqm_handler(struct nrf51_hal_spi *spi) |
| { |
| NRF_SPI_Type *spim; |
| |
| spim = (NRF_SPI_Type *)spi->nhs_spi.spim; |
| if (spim->EVENTS_READY) { |
| spim->EVENTS_READY = 0; |
| |
| if (spi->spi_xfr_flag == 0) { |
| return; |
| } |
| |
| if (spi->nhs_rxbuf) { |
| spi->nhs_rxbuf[spi->nhs_rxd_bytes] = spim->RXD; |
| } |
| ++spi->nhs_rxd_bytes; |
| if (spi->nhs_rxd_bytes == spi->nhs_buflen) { |
| if (spi->txrx_cb_func) { |
| spi->txrx_cb_func(spi->txrx_cb_arg, spi->nhs_buflen); |
| } |
| spi->spi_xfr_flag = 0; |
| spim->INTENCLR = SPI_INTENCLR_READY_Msk; |
| } |
| if (spi->nhs_txd_bytes != spi->nhs_buflen) { |
| spim->TXD = spi->nhs_txbuf[spi->nhs_txd_bytes]; |
| ++spi->nhs_txd_bytes; |
| } |
| } |
| } |
| #endif |
| |
| #if (MYNEWT_VAL(SPI_1_SLAVE) ) |
| static void |
| nrf51_irqs_handler(struct nrf51_hal_spi *spi) |
| { |
| uint8_t xfr_len; |
| NRF_SPIS_Type *spis; |
| |
| spis = spi->nhs_spi.spis; |
| |
| /* Semaphore acquired event */ |
| if (spis->EVENTS_ACQUIRED) { |
| spis->EVENTS_ACQUIRED = 0; |
| |
| if (spi->slave_state == HAL_SPI_SLAVE_STATE_ACQ_SEM) { |
| if (spi->nhs_txbuf == NULL) { |
| spis->TXDPTR = 0; |
| spis->MAXTX = 0; |
| } else { |
| spis->TXDPTR = (uint32_t)spi->nhs_txbuf; |
| spis->MAXTX = spi->nhs_buflen; |
| } |
| |
| if (spi->nhs_rxbuf == NULL) { |
| spis->RXDPTR = 0; |
| spis->MAXRX = 0; |
| |
| } else { |
| spis->RXDPTR = (uint32_t)spi->nhs_rxbuf; |
| spis->MAXRX = spi->nhs_buflen; |
| } |
| spis->TASKS_RELEASE = 1; |
| spi->slave_state = HAL_SPI_SLAVE_STATE_READY; |
| } |
| } |
| |
| /* SPI transaction complete */ |
| if (spis->EVENTS_END) { |
| spis->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_len = (uint8_t)spis->AMOUNTRX; |
| |
| } else { |
| xfr_len = (uint8_t)spis->AMOUNTTX; |
| } |
| spi->txrx_cb_func(spi->txrx_cb_arg, xfr_len); |
| } |
| spi->slave_state = HAL_SPI_SLAVE_STATE_IDLE; |
| } |
| } |
| |
| } |
| #endif |
| |
| /* Interrupt handlers for SPI ports */ |
| #if MYNEWT_VAL(SPI_0_MASTER) |
| void |
| nrf51_spi0_irq_handler(void) |
| { |
| if (nrf51_hal_spi0.spi_type == HAL_SPI_TYPE_MASTER) { |
| nrf51_irqm_handler(&nrf51_hal_spi0); |
| } else { |
| assert(0); |
| } |
| } |
| #endif |
| |
| #if MYNEWT_VAL(SPI_1_MASTER) || MYNEWT_VAL(SPI_1_SLAVE) |
| void |
| nrf51_spi1_irq_handler(void) |
| { |
| if (nrf51_hal_spi1.spi_type == HAL_SPI_TYPE_MASTER) { |
| #if MYNEWT_VAL(SPI_1_MASTER) |
| nrf51_irqm_handler(&nrf51_hal_spi1); |
| #else |
| assert(0); |
| #endif |
| } else { |
| #if MYNEWT_VAL(SPI_1_SLAVE) |
| nrf51_irqs_handler(&nrf51_hal_spi1); |
| #else |
| assert(0); |
| #endif |
| } |
| } |
| #endif |
| |
| static int |
| hal_spi_config_master(struct nrf51_hal_spi *spi, |
| struct hal_spi_settings *settings) |
| { |
| int rc; |
| uint32_t nrf_config; |
| uint32_t frequency; |
| NRF_SPI_Type *spim; |
| |
| spim = (NRF_SPI_Type *)spi->nhs_spi.spim; |
| memcpy(&spi->spi_cfg, settings, sizeof(*settings)); |
| |
| /* Only 8-bit word sizes supported. */ |
| rc = 0; |
| switch (settings->data_mode) { |
| case HAL_SPI_MODE0: |
| nrf_config = (SPI_CONFIG_CPOL_ActiveHigh << SPI_CONFIG_CPOL_Pos) | |
| (SPI_CONFIG_CPHA_Leading << SPI_CONFIG_CPHA_Pos); |
| break; |
| case HAL_SPI_MODE1: |
| nrf_config = (SPI_CONFIG_CPOL_ActiveHigh << SPI_CONFIG_CPOL_Pos) | |
| (SPI_CONFIG_CPHA_Trailing << SPI_CONFIG_CPHA_Pos); |
| break; |
| case HAL_SPI_MODE2: |
| nrf_config = (SPI_CONFIG_CPOL_ActiveLow << SPI_CONFIG_CPOL_Pos) | |
| (SPI_CONFIG_CPHA_Leading << SPI_CONFIG_CPHA_Pos); |
| break; |
| case HAL_SPI_MODE3: |
| nrf_config = (SPI_CONFIG_CPOL_ActiveLow << SPI_CONFIG_CPOL_Pos) | |
| (SPI_CONFIG_CPHA_Trailing << SPI_CONFIG_CPHA_Pos); |
| break; |
| default: |
| nrf_config = 0; |
| rc = EINVAL; |
| break; |
| } |
| |
| if (settings->data_order == HAL_SPI_LSB_FIRST) { |
| nrf_config |= SPI_CONFIG_ORDER_LsbFirst; |
| } |
| spim->CONFIG = nrf_config; |
| |
| switch (settings->baudrate) { |
| case 125: |
| frequency = SPI_FREQUENCY_FREQUENCY_K125; |
| break; |
| case 250: |
| frequency = SPI_FREQUENCY_FREQUENCY_K250; |
| break; |
| case 500: |
| frequency = SPI_FREQUENCY_FREQUENCY_K500; |
| break; |
| case 1000: |
| frequency = SPI_FREQUENCY_FREQUENCY_M1; |
| break; |
| case 2000: |
| frequency = SPI_FREQUENCY_FREQUENCY_M2; |
| break; |
| case 4000: |
| frequency = SPI_FREQUENCY_FREQUENCY_M4; |
| break; |
| case 8000: |
| frequency = SPI_FREQUENCY_FREQUENCY_M8; |
| break; |
| default: |
| frequency = 0; |
| rc = EINVAL; |
| break; |
| } |
| spim->FREQUENCY = frequency; |
| |
| /* Only supports 8-bit word size */ |
| if (settings->word_size != HAL_SPI_WORD_SIZE_8BIT) { |
| rc = EINVAL; |
| } |
| |
| return rc; |
| } |
| |
| static int |
| hal_spi_config_slave(struct nrf51_hal_spi *spi, |
| struct hal_spi_settings *settings) |
| { |
| int rc; |
| uint32_t nrf_config; |
| NRF_SPIS_Type *spis; |
| |
| spis = spi->nhs_spi.spis; |
| memcpy(&spi->spi_cfg, settings, sizeof(*settings)); |
| |
| 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; |
| } |
| spis->CONFIG = nrf_config; |
| |
| /* Only 8-bit word sizes supported. */ |
| if (settings->word_size != HAL_SPI_WORD_SIZE_8BIT) { |
| rc = EINVAL; |
| } |
| return rc; |
| } |
| |
| static int |
| hal_spi_init_master(struct nrf51_hal_spi *spi, |
| struct nrf51_hal_spi_cfg *cfg, |
| nrf51_spi_irq_handler_t handler) |
| { |
| NRF_SPI_Type *spim; |
| |
| /* Configure SCK pin */ |
| if (spi->spi_cfg.data_mode <= HAL_SPI_MODE1) { |
| NRF_GPIO->OUTCLR = (1UL << cfg->sck_pin); |
| } else { |
| NRF_GPIO->OUTSET = (1UL << cfg->sck_pin); |
| } |
| NRF_GPIO->PIN_CNF[cfg->sck_pin] = |
| (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos) |
| | (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
| | (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) |
| | (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |
| | (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos); |
| |
| /* Configure MOSI */ |
| NRF_GPIO->OUTCLR = (1UL << cfg->mosi_pin); |
| NRF_GPIO->PIN_CNF[cfg->mosi_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 */ |
| NRF_GPIO->PIN_CNF[cfg->miso_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); |
| |
| spim = (NRF_SPI_Type *)spi->nhs_spi.spim; |
| spim->PSELSCK = cfg->sck_pin; |
| spim->PSELMOSI = cfg->mosi_pin; |
| spim->PSELMISO = cfg->miso_pin; |
| |
| spim->INTENCLR = SPI_INTENCLR_READY_Msk; |
| |
| NVIC_SetVector(spi->irq_num, (uint32_t)handler); |
| NVIC_SetPriority(spi->irq_num, (1 << __NVIC_PRIO_BITS) - 1); |
| NVIC_ClearPendingIRQ(spi->irq_num); |
| NVIC_EnableIRQ(spi->irq_num); |
| |
| return 0; |
| } |
| |
| static int |
| hal_spi_init_slave(struct nrf51_hal_spi *spi, |
| struct nrf51_hal_spi_cfg *cfg, |
| nrf51_spi_irq_handler_t handler) |
| { |
| NRF_SPIS_Type *spis; |
| |
| NRF_GPIO->PIN_CNF[cfg->miso_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_GPIO->PIN_CNF[cfg->mosi_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_GPIO->PIN_CNF[cfg->ss_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); |
| |
| NRF_GPIO->PIN_CNF[cfg->sck_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); |
| |
| spis = (NRF_SPIS_Type *)spi->nhs_spi.spis; |
| spis->PSELSCK = cfg->sck_pin; |
| spis->PSELMOSI = cfg->mosi_pin; |
| spis->PSELMISO = cfg->miso_pin; |
| spis->PSELCSN = cfg->ss_pin; |
| |
| /* Disable interrupt and clear any interrupt events */ |
| spis->INTENCLR = SPIS_INTENSET_ACQUIRED_Msk | SPIS_INTENSET_END_Msk; |
| spis->EVENTS_END = 0; |
| spis->EVENTS_ACQUIRED = 0; |
| |
| /* Enable END_ACQUIRE shortcut. */ |
| spis->SHORTS = SPIS_SHORTS_END_ACQUIRE_Msk; |
| |
| /* Set interrupt vector and enable IRQ */ |
| NVIC_SetVector(spi->irq_num, (uint32_t)handler); |
| NVIC_SetPriority(spi->irq_num, (1 << __NVIC_PRIO_BITS) - 1); |
| NVIC_ClearPendingIRQ(spi->irq_num); |
| NVIC_EnableIRQ(spi->irq_num); |
| |
| return 0; |
| } |
| |
| static void |
| hal_spi_master_send_first(NRF_SPI_Type *spim, uint8_t txval) |
| { |
| while (spim->EVENTS_READY) { |
| (void)spim->RXD; |
| spim->EVENTS_READY = 0; |
| } |
| spim->TXD = txval; |
| } |
| |
| /** |
| * 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) |
| { |
| int rc; |
| struct nrf51_hal_spi *spi; |
| nrf51_spi_irq_handler_t irq_handler; |
| |
| NRF51_HAL_SPI_RESOLVE(spi_num, spi); |
| |
| /* Check for valid arguments */ |
| rc = EINVAL; |
| if (cfg == NULL) { |
| goto err; |
| } |
| |
| if ((spi_type != HAL_SPI_TYPE_MASTER) && (spi_type != HAL_SPI_TYPE_SLAVE)) { |
| goto err; |
| } |
| |
| irq_handler = NULL; |
| spi->spi_type = spi_type; |
| if (spi_num == 0) { |
| #if MYNEWT_VAL(SPI_0_MASTER) |
| spi->irq_num = SPI0_TWI0_IRQn; |
| irq_handler = nrf51_spi0_irq_handler; |
| if (spi_type == HAL_SPI_TYPE_MASTER) { |
| spi->nhs_spi.spim = NRF_SPI0; |
| } else { |
| assert(0); |
| } |
| #else |
| goto err; |
| #endif |
| } else if (spi_num == 1) { |
| #if MYNEWT_VAL(SPI_1_MASTER) || MYNEWT_VAL(SPI_1_SLAVE) |
| spi->irq_num = SPI1_TWI1_IRQn; |
| irq_handler = nrf51_spi1_irq_handler; |
| if (spi_type == HAL_SPI_TYPE_MASTER) { |
| #if MYNEWT_VAL(SPI_1_MASTER) |
| spi->nhs_spi.spim = NRF_SPI1; |
| #else |
| assert(0); |
| #endif |
| } else { |
| #if MYNEWT_VAL(SPI_1_SLAVE) |
| spi->nhs_spi.spis = NRF_SPIS1; |
| #else |
| assert(0); |
| #endif |
| } |
| #else |
| goto err; |
| #endif |
| } else { |
| goto err; |
| } |
| |
| if (spi_type == HAL_SPI_TYPE_MASTER) { |
| rc = hal_spi_init_master(spi, (struct nrf51_hal_spi_cfg *)cfg, |
| irq_handler); |
| |
| |
| } else { |
| rc = hal_spi_init_slave(spi, (struct nrf51_hal_spi_cfg *)cfg, |
| irq_handler); |
| } |
| |
| err: |
| return (rc); |
| } |
| |
| /** |
| * 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) |
| { |
| int rc; |
| struct nrf51_hal_spi *spi; |
| |
| NRF51_HAL_SPI_RESOLVE(spi_num, spi); |
| |
| if (spi->spi_type == HAL_SPI_TYPE_MASTER) { |
| rc = hal_spi_config_master(spi, settings); |
| } else { |
| rc = hal_spi_config_slave(spi, settings); |
| } |
| |
| err: |
| return (rc); |
| } |
| |
| /** |
| * 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) |
| { |
| int rc; |
| NRF_SPIS_Type *spis; |
| NRF_SPI_Type *spim; |
| struct nrf51_hal_spi *spi; |
| |
| NRF51_HAL_SPI_RESOLVE(spi_num, spi); |
| |
| if (spi->spi_type == HAL_SPI_TYPE_MASTER) { |
| spim = (NRF_SPI_Type *)spi->nhs_spi.spim; |
| spim->ENABLE = (SPI_ENABLE_ENABLE_Enabled << SPI_ENABLE_ENABLE_Pos); |
| } else { |
| if (spi->txrx_cb_func == NULL) { |
| rc = EINVAL; |
| goto err; |
| } |
| |
| spis = spi->nhs_spi.spis; |
| spis->EVENTS_END = 0; |
| spis->EVENTS_ACQUIRED = 0; |
| spis->INTENSET = SPIS_INTENSET_ACQUIRED_Msk | SPIS_INTENSET_END_Msk; |
| spis->ENABLE = (SPIS_ENABLE_ENABLE_Enabled << SPIS_ENABLE_ENABLE_Pos); |
| } |
| rc = 0; |
| |
| err: |
| return rc; |
| } |
| |
| /** |
| * 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) |
| { |
| int rc; |
| NRF_SPIS_Type *spis; |
| NRF_SPI_Type *spim; |
| struct nrf51_hal_spi *spi; |
| |
| NRF51_HAL_SPI_RESOLVE(spi_num, spi); |
| |
| if (spi->spi_type == HAL_SPI_TYPE_MASTER) { |
| spim = (NRF_SPI_Type *)spi->nhs_spi.spim; |
| spim->INTENCLR = SPI_INTENCLR_READY_Msk; |
| spi->spi_xfr_flag = 0; |
| spim->ENABLE = 0; |
| } else { |
| spis = spi->nhs_spi.spis; |
| spis->INTENCLR = NRF_SPI_IRQ_DISABLE_ALL; |
| spis->EVENTS_END = 0; |
| spis->EVENTS_ACQUIRED = 0; |
| spis->ENABLE = 0; |
| spi->slave_state = HAL_SPI_SLAVE_STATE_IDLE; |
| spi->nhs_txbuf = NULL; |
| spi->nhs_rxbuf = NULL; |
| } |
| rc = 0; |
| |
| err: |
| return rc; |
| } |
| |
| /** |
| * Blocking call to send a value on the SPI. Returns the value received from the |
| * SPI slave. |
| * |
| * MASTER: Sends the value and returns the received value from the slave. |
| * SLAVE: Invalid API. Returns 0xFFFF |
| * |
| * @param spi_num Spi interface to use |
| * @param val Value to send |
| * |
| * @return uint16_t Value received on SPI interface from slave. Returns 0xFFFF |
| * if called when the SPI is configured to be a slave |
| */ |
| uint16_t |
| hal_spi_tx_val(int spi_num, uint16_t val) |
| { |
| int rc; |
| uint16_t retval; |
| NRF_SPI_Type *spim; |
| struct nrf51_hal_spi *spi; |
| |
| NRF51_HAL_SPI_RESOLVE(spi_num, spi); |
| |
| if (spi->spi_type == HAL_SPI_TYPE_MASTER) { |
| spim = (NRF_SPI_Type *) spi->nhs_spi.spim; |
| spim->EVENTS_READY = 0; |
| spim->TXD = (uint8_t)val; |
| while (!spim->EVENTS_READY) {} |
| spim->EVENTS_READY = 0; |
| retval = (uint16_t)spim->RXD; |
| } else { |
| retval = 0xFFFF; |
| } |
| |
| return retval; |
| |
| err: |
| return rc; |
| } |
| |
| /** |
| * 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) |
| { |
| int rc; |
| NRF_SPI_Type *spim; |
| struct nrf51_hal_spi *spi; |
| |
| NRF51_HAL_SPI_RESOLVE(spi_num, spi); |
| |
| /* |
| * This looks odd, but the ENABLE register is in the same location for |
| * SPIM, SPI and SPIS |
| */ |
| spim = (NRF_SPI_Type *)spi->nhs_spi.spim; |
| if (spim->ENABLE != 0) { |
| rc = -1; |
| } else { |
| spi->txrx_cb_func = txrx_cb; |
| spi->txrx_cb_arg = arg; |
| rc = 0; |
| } |
| |
| err: |
| return rc; |
| } |
| |
| /** |
| * Blocking interface to send a buffer and store the received values from the |
| * slave. 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: cannot be called for a slave; returns -1 |
| * |
| * @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(int spi_num, void *txbuf, void *rxbuf, int len) |
| { |
| int i; |
| int rc; |
| int txcnt; |
| uint8_t *txd, *rxd; |
| uint8_t rxval; |
| NRF_SPI_Type *spim; |
| struct nrf51_hal_spi *spi; |
| |
| rc = EINVAL; |
| if (!len) { |
| goto err; |
| } |
| |
| NRF51_HAL_SPI_RESOLVE(spi_num, spi); |
| |
| if (spi->spi_type == HAL_SPI_TYPE_MASTER) { |
| /* Must have a txbuf for master! */ |
| if (txbuf == NULL) { |
| goto err; |
| } |
| |
| spim = (NRF_SPI_Type *) spi->nhs_spi.spim; |
| spim->INTENCLR = SPI_INTENCLR_READY_Msk; |
| |
| /* Blocking spi transfer */ |
| txd = (uint8_t *)txbuf; |
| hal_spi_master_send_first(spim, txd[0]); |
| txcnt = len - 1; |
| rxd = (uint8_t *)rxbuf; |
| for (i = 0; i < len; ++i) { |
| if (txcnt) { |
| ++txd; |
| spim->TXD = *txd; |
| --txcnt; |
| } |
| while (!spim->EVENTS_READY) {} |
| spim->EVENTS_READY = 0; |
| rxval = spim->RXD; |
| if (rxbuf) { |
| *rxd = rxval; |
| ++rxd; |
| } |
| } |
| return 0; |
| } |
| |
| err: |
| return rc; |
| } |
| |
| /** |
| * 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) |
| { |
| int rc; |
| uint8_t *txd; |
| NRF_SPI_Type *spim; |
| struct nrf51_hal_spi *spi; |
| |
| rc = EINVAL; |
| NRF51_HAL_SPI_RESOLVE(spi_num, spi); |
| |
| if ((spi->txrx_cb_func == NULL) || (len == 0)) { |
| goto err; |
| } |
| |
| if (spi->spi_type == HAL_SPI_TYPE_MASTER) { |
| /* Must have a txbuf for master! */ |
| if (txbuf == NULL) { |
| goto err; |
| } |
| |
| /* Not allowed if transfer in progress */ |
| if (spi->spi_xfr_flag) { |
| rc = -1; |
| goto err; |
| } |
| |
| spim = (NRF_SPI_Type *) spi->nhs_spi.spim; |
| spim->INTENCLR = SPI_INTENCLR_READY_Msk; |
| |
| spi->spi_xfr_flag = 1; |
| spi->nhs_buflen = (uint16_t)len; |
| spi->nhs_txbuf = txbuf; |
| spi->nhs_rxbuf = rxbuf; |
| spi->nhs_rxd_bytes = 0; |
| txd = (uint8_t *)txbuf; |
| hal_spi_master_send_first(spim, txd[0]); |
| spi->nhs_txd_bytes = 1; |
| if (len > 1) { |
| spim->TXD = txd[1]; |
| ++spi->nhs_txd_bytes; |
| } |
| spim->INTENSET = SPI_INTENSET_READY_Msk; |
| } else { |
| /* Must have txbuf or rxbuf */ |
| if ((txbuf == NULL) && (rxbuf == NULL)) { |
| goto err; |
| } |
| |
| /* XXX: not sure what to for length > 255 */ |
| if (len > 255) { |
| goto err; |
| } |
| |
| /* |
| * 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) { |
| rc = -1; |
| goto err; |
| } |
| |
| spi->nhs_rxbuf = rxbuf; |
| spi->nhs_txbuf = txbuf; |
| spi->nhs_buflen = len; |
| spi->slave_state = HAL_SPI_SLAVE_STATE_ACQ_SEM; |
| spi->nhs_spi.spis->TASKS_ACQUIRE = 1; |
| } |
| return 0; |
| |
| err: |
| return rc; |
| } |
| |
| /** |
| * 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) |
| { |
| int rc; |
| NRF_SPIS_Type *spis; |
| struct nrf51_hal_spi *spi; |
| |
| NRF51_HAL_SPI_RESOLVE(spi_num, spi); |
| if (spi->spi_type == HAL_SPI_TYPE_SLAVE) { |
| spis = spi->nhs_spi.spis; |
| spis->DEF = (uint8_t)val; |
| spis->ORC = (uint8_t)val; |
| rc = 0; |
| } else { |
| rc = EINVAL; |
| } |
| |
| err: |
| return rc; |
| } |
| |
| /** |
| * 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) |
| { |
| int rc; |
| NRF_SPI_Type *spim; |
| struct nrf51_hal_spi *spi; |
| |
| NRF51_HAL_SPI_RESOLVE(spi_num, spi); |
| |
| rc = 0; |
| if (spi->spi_type == HAL_SPI_TYPE_MASTER) { |
| spim = (NRF_SPI_Type *)spi->nhs_spi.spim; |
| if (spi->spi_xfr_flag) { |
| spim->INTENCLR = SPI_INTENCLR_READY_Msk; |
| spim->ENABLE = 0; |
| spim->EVENTS_READY = 0; |
| spi->spi_xfr_flag = 0; |
| spim->INTENSET = SPI_INTENSET_READY_Msk; |
| } |
| } else { |
| /* Only way I can see doing this is to disable, then re-enable */ |
| rc = hal_spi_disable(spi_num); |
| if (rc) { |
| goto err; |
| } |
| rc = hal_spi_enable(spi_num); |
| if (rc) { |
| goto err; |
| } |
| } |
| |
| err: |
| return rc; |
| } |