1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237 |
- /*
- * Copyright (C) 2017 C-SKY Microsystems Co., Ltd. All rights reserved.
- *
- * Licensed 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.
- */
- /******************************************************************************
- * @file dw_spi.c
- * @brief CSI Source File for SPI Driver
- * @version V1.0
- * @date 02. June 2017
- ******************************************************************************/
- #include "csi_core.h"
- #include "drv_spi.h"
- #include "dw_spi.h"
- #include "drv_gpio.h"
- #ifdef CONFIG_SPI_DMA
- #include "ck_dmac.h"
- #include <string.h>
- #endif
- #include "soc.h" /*CONFIG_SPI_NUM*/
- #define ERR_SPI(errno) (CSI_DRV_ERRNO_SPI_BASE | errno)
- #define SPI_NULL_PARAM_CHK(para) \
- do { \
- if (para == NULL) { \
- return ERR_SPI(EDRV_PARAMETER); \
- } \
- } while (0)
- typedef struct {
- uint32_t base;
- uint32_t irq;
- pin_t ssel;
- spi_event_cb_t cb_event;
- void *cb_arg;
- uint32_t send_num;
- uint32_t recv_num;
- uint8_t *send_buf;
- uint8_t *recv_buf;
- uint8_t enable_slave;
- uint32_t transfer_num;
- uint32_t clk_num; //clock number with a process of communication
- uint8_t state; //Current SPI state
- uint32_t mode; //Current SPI mode
- uint8_t ss_mode;
- spi_status_t status;
- #ifdef CONFIG_SPI_DMA
- dmac_handle_t dma_handle;
- int32_t dma_tx_id;
- int32_t dma_rx_id;
- #endif
- } dw_spi_priv_t;
- static dw_spi_priv_t spi_instance[CONFIG_SPI_NUM];
- static gpio_pin_handle_t pgpio_pin_handle;
- static const spi_capabilities_t spi_capabilities = {
- .simplex = 1, /* Simplex Mode (Master and Slave) */
- .ti_ssi = 1, /* TI Synchronous Serial Interface */
- .microwire = 1, /* Microwire Interface */
- .event_mode_fault = 0 /* Signal Mode Fault event: \ref CSKY_SPI_EVENT_MODE_FAULT */
- };
- static int32_t dw_spi_set_datawidth(dw_spi_reg_t *addr, DWENUM_SPI_DATAWIDTH datawidth);
- static int32_t dw_spi_set_mode(spi_handle_t handle, DWENUM_SPI_MODE mode);
- /**
- \brief use phobos gpio pin to simulate ss line for hardware controlled Output mode.
- */
- static int32_t dw_spi_ss_init(dw_spi_priv_t *spi_priv)
- {
- csi_gpio_port_initialize(0, NULL);
- pgpio_pin_handle = csi_gpio_pin_initialize(spi_priv->ssel);
- csi_gpio_pin_config(pgpio_pin_handle, GPIO_MODE_PULLNONE, GPIO_DIRECTION_OUTPUT);
- csi_gpio_pin_write(pgpio_pin_handle, true);
- spi_priv->ss_mode = SPI_SS_MASTER_HW_OUTPUT;
- return 0;
- }
- /**
- \brief control ss line depend on controlled Output mode.
- */
- static int32_t dw_spi_ss_control(dw_spi_priv_t *spi_priv, spi_ss_stat_e stat)
- {
- if (spi_priv->ss_mode == SPI_SS_MASTER_HW_OUTPUT) {
- if (stat == SPI_SS_INACTIVE) {
- csi_gpio_pin_write(pgpio_pin_handle, true);
- } else if (stat == SPI_SS_ACTIVE) {
- csi_gpio_pin_write(pgpio_pin_handle, false);
- } else {
- return -1;
- }
- }
- return 0;
- }
- #ifdef CONFIG_SPI_DMA
- void dw_spi_dma_event_cb(dma_event_e event, int32_t ch)
- {
- dw_spi_priv_t *spi_priv = NULL;
- uint8_t i = 0u;
- for (i = 0; i < CONFIG_SPI_NUM; i++) { /* find the SPI id */
- spi_priv = &spi_instance[i];
- if ((spi_priv->dma_tx_id == ch) || (spi_priv->dma_rx_id == ch)) {
- break;
- }
- }
- if (spi_priv->dma_tx_id == ch) {
- spi_priv->dma_tx_id = -1;
- } else {
- spi_priv->dma_rx_id = -1;
- }
- if (event == DMA_EVENT_TRANSFER_ERROR) { /* DMA transfer ERROR */
- if (spi_priv->cb_event) {
- spi_priv->cb_event(SPI_EVENT_DATA_LOST, NULL);
- }
- } else if (event == DMA_EVENT_TRANSFER_DONE) { /* DMA transfer complete */
- if (spi_priv->mode == DWENUM_SPI_TXRX) {
- if (spi_priv->cb_event) {
- spi_priv->cb_event(SPI_EVENT_TRANSFER_COMPLETE, spi_priv->cb_arg);
- }
- } else if (spi_priv->mode == DWENUM_SPI_TX) {
- dw_spi_reg_t *addr = (dw_spi_reg_t *)(spi_priv->base);
- while (addr->SR & DW_SPI_DISABLE);
- if (spi_priv->cb_event) {
- spi_priv->cb_event(SPI_EVENT_TX_COMPLETE, spi_priv->cb_arg);
- }
- } else {
- if (spi_priv->cb_event) {
- spi_priv->cb_event(SPI_EVENT_RX_COMPLETE, spi_priv->cb_arg);
- }
- }
- }
- spi_priv->status.busy = 0U;
- }
- /**
- \brief sending data to SPI transmitter with DMA,(received data is ignored).
- */
- static int32_t dw_spi_dma_send(dw_spi_priv_t *spi_priv, const void *data, uint32_t num)
- {
- int32_t ch = csi_dma_alloc_channel(spi_priv->dma_handle, -1);
- spi_priv->dma_tx_id = ch;
- dma_config_t config;
- config.src_inc = DMA_ADDR_INC;
- config.dst_inc = DMA_ADDR_CONSTANT;
- config.src_tw = DMA_DATAWIDTH_SIZE8;
- config.dst_tw = DMA_DATAWIDTH_SIZE8;
- config.hs_if = CKENUM_DMA_SPI1_TX;
- config.type = DMA_MEM2PERH;
- dw_spi_ss_control(spi_priv, SPI_SS_ACTIVE);
- dw_spi_reg_t *addr = (dw_spi_reg_t *)(spi_priv->base);
- uint8_t *ptxbuffer = (uint8_t *)data;
- int32_t ret = csi_dma_config(spi_priv->dma_handle, ch, ptxbuffer, (uint8_t *) & (addr->DR), num, &config, dw_spi_dma_event_cb);
- if (ret < 0) {
- return ret;
- }
- addr->SPIENR = DW_SPI_DISABLE; /* enable SPI */
- dw_spi_set_mode(spi_priv, DWENUM_SPI_TX);
- addr->DMATDLR = 0;
- addr->DMACR = DW_SPI_TDMAE;
- addr->SER = spi_priv->enable_slave;
- addr->SPIENR = DW_SPI_ENABLE;
- csi_dma_start(spi_priv->dma_handle, ch);
- while (csi_dma_get_status(spi_priv->dma_handle, ch) != DMA_STATE_DONE);
- while (addr->SR & DW_SPI_BUSY);
- csi_dma_stop(spi_priv->dma_handle, ch);
- csi_dma_release_channel(spi_priv->dma_handle, ch);
- addr->SPIENR = DW_SPI_DISABLE;
- addr->SER = 0;
- dw_spi_ss_control(spi_priv, SPI_SS_INACTIVE);
- spi_priv->status.busy = 0U;
- return 0;
- }
- /**
- \brief receiving data from SPI receiver with DMA.
- */
- static int32_t dw_spi_dma_receive(dw_spi_priv_t *spi_priv, void *data, uint32_t num)
- {
- int32_t ch;
- ch = csi_dma_alloc_channel(spi_priv->dma_handle, -1);
- spi_priv->dma_tx_id = ch;
- dma_config_t config;
- config.src_inc = DMA_ADDR_CONSTANT;
- config.dst_inc = DMA_ADDR_INC;
- config.src_tw = DMA_DATAWIDTH_SIZE8;
- config.dst_tw = DMA_DATAWIDTH_SIZE8;
- config.hs_if = CKENUM_DMA_SPI1_RX;
- config.type = DMA_PERH2MEM;
- dw_spi_ss_control(spi_priv, SPI_SS_ACTIVE);
- uint8_t *prx_buffer = (uint8_t *)data;
- spi_priv->recv_buf = prx_buffer;
- spi_priv->clk_num = num;
- spi_priv->recv_num = num;
- dw_spi_reg_t *addr = (dw_spi_reg_t *)(spi_priv->base);
- int32_t ret;
- while (spi_priv->clk_num) {
- if (spi_priv->clk_num >= DW_SPI_FIFO_MAX_LV / 2) {
- spi_priv->recv_num = DW_SPI_FIFO_MAX_LV / 2;
- }
- ret = csi_dma_config(spi_priv->dma_handle, ch, (uint8_t *) & (addr->DR), spi_priv->recv_buf, spi_priv->recv_num, &config, dw_spi_dma_event_cb);
- if (ret < 0) {
- return ret;
- }
- addr->SPIENR = DW_SPI_DISABLE; /* enable SPI */
- dw_spi_set_mode(spi_priv, DWENUM_SPI_RX);
- addr->DMARDLR = spi_priv->recv_num - 1;
- addr->CTRLR1 = spi_priv->recv_num - 1;
- addr->DMACR = DW_SPI_RDMAE;
- addr->SER = spi_priv->enable_slave;
- csi_dma_start(spi_priv->dma_handle, ch);
- addr->SPIENR = DW_SPI_ENABLE;
- addr->DR = DW_SPI_START_RX;
- while (csi_dma_get_status(spi_priv->dma_handle, ch) != DMA_STATE_DONE);
- csi_dma_stop(spi_priv->dma_handle, ch);
- spi_priv->clk_num -= spi_priv->recv_num;
- spi_priv->recv_buf += spi_priv->recv_num;
- }
- addr->SPIENR = DW_SPI_DISABLE;
- addr->SER = 0;
- csi_dma_release_channel(spi_priv->dma_handle, ch);
- dw_spi_ss_control(spi_priv, SPI_SS_INACTIVE);
- spi_priv->status.busy = 0U;
- return 0;
- }
- /**
- \brief sending/receiving data to/from SPI transmitter/receiver with DMA.
- */
- static int32_t dw_spi_dma_transfer(dw_spi_priv_t *spi_priv, const void *data_out, void *data_in, uint32_t num_out, uint32_t num_in)
- {
- int32_t tx_ch = csi_dma_alloc_channel(spi_priv->dma_handle, -1);
- if (tx_ch < 0) {
- return tx_ch;
- }
- spi_priv->dma_tx_id = tx_ch;
- int32_t rx_ch = csi_dma_alloc_channel(spi_priv->dma_handle, -1);
- if (rx_ch < 0) {
- return rx_ch;
- }
- spi_priv->dma_rx_id = rx_ch;
- dma_config_t tx_config, rx_config;
- tx_config.src_inc = DMA_ADDR_INC;
- tx_config.dst_inc = DMA_ADDR_CONSTANT;
- tx_config.src_tw = DMA_DATAWIDTH_SIZE8;
- tx_config.dst_tw = DMA_DATAWIDTH_SIZE8;
- tx_config.hs_if = CKENUM_DMA_SPI1_TX;
- tx_config.type = DMA_MEM2PERH;
- rx_config.src_inc = DMA_ADDR_CONSTANT;
- rx_config.dst_inc = DMA_ADDR_INC;
- rx_config.src_tw = DMA_DATAWIDTH_SIZE8;
- rx_config.dst_tw = DMA_DATAWIDTH_SIZE8;
- rx_config.hs_if = CKENUM_DMA_SPI1_RX;
- rx_config.type = DMA_PERH2MEM;
- dw_spi_ss_control(spi_priv, SPI_SS_ACTIVE);
- uint8_t *ptx_buffer = (uint8_t *)data_out;
- uint8_t *prx_buffer = (uint8_t *)data_in;
- spi_priv->send_buf = ptx_buffer;
- spi_priv->recv_buf = prx_buffer;
- spi_priv->send_num = num_out;
- spi_priv->recv_num = num_in;
- spi_priv->clk_num = num_in;
- dw_spi_reg_t *addr = (dw_spi_reg_t *)(spi_priv->base);
- int32_t ret;
- uint8_t dma_send_buf[DW_SPI_FIFO_MAX_LV];
- while (spi_priv->clk_num) {
- if (spi_priv->clk_num >= DW_SPI_FIFO_MAX_LV) {
- spi_priv->transfer_num = DW_SPI_FIFO_MAX_LV;
- } else {
- spi_priv->transfer_num = spi_priv->clk_num;
- }
- if (spi_priv->send_num >= spi_priv->transfer_num) {
- memcpy(dma_send_buf, spi_priv->send_buf, spi_priv->transfer_num);
- spi_priv->send_num -= spi_priv->transfer_num;
- spi_priv->send_buf += spi_priv->transfer_num;
- } else {
- if (spi_priv->send_num > 0) {
- memcpy(dma_send_buf, spi_priv->send_buf, spi_priv->send_num);
- spi_priv->send_buf = NULL;
- }
- memset(&dma_send_buf[spi_priv->send_num], 0, spi_priv->transfer_num - spi_priv->send_num);
- spi_priv->send_num = 0;
- }
- ret = csi_dma_config(spi_priv->dma_handle, tx_ch, dma_send_buf, (uint8_t *) & (addr->DR), spi_priv->transfer_num, &tx_config, dw_spi_dma_event_cb);
- if (ret < 0) {
- return ret;
- }
- ret = csi_dma_config(spi_priv->dma_handle, rx_ch, (uint8_t *) & (addr->DR), spi_priv->recv_buf, spi_priv->transfer_num, &rx_config, dw_spi_dma_event_cb);
- if (ret < 0) {
- return ret;
- }
- addr->SPIENR = DW_SPI_DISABLE; /* disable SPI */
- addr->DMARDLR = (spi_priv->transfer_num - 1) % 16; /* set dma receive data level */
- addr->DMATDLR = (spi_priv->transfer_num - 1) % 16 + 1; /* set dma transmit data level */
- addr->DMACR = DW_SPI_RDMAE | DW_SPI_TDMAE;
- dw_spi_set_mode(spi_priv, DWENUM_SPI_TXRX);
- addr->SER = spi_priv->enable_slave;
- addr->SPIENR = DW_SPI_ENABLE; /* enable SPI */
- ret = csi_dma_start(spi_priv->dma_handle, tx_ch);
- if (ret < 0) {
- return ret;
- }
- while (csi_dma_get_status(spi_priv->dma_handle, tx_ch) != DMA_STATE_DONE);
- ret = csi_dma_start(spi_priv->dma_handle, rx_ch);
- if (ret < 0) {
- return ret;
- }
- while (csi_dma_get_status(spi_priv->dma_handle, rx_ch) != DMA_STATE_DONE);
- spi_priv->recv_buf += spi_priv->transfer_num;
- spi_priv->recv_num -= spi_priv->transfer_num;
- spi_priv->clk_num -= spi_priv->transfer_num;
- addr->SPIENR = DW_SPI_DISABLE;
- csi_dma_stop(spi_priv->dma_handle, tx_ch);
- csi_dma_stop(spi_priv->dma_handle, rx_ch);
- }
- dw_spi_ss_control(spi_priv, SPI_SS_INACTIVE);
- addr->SER = 0;
- csi_dma_release_channel(spi_priv->dma_handle, tx_ch);
- csi_dma_release_channel(spi_priv->dma_handle, rx_ch);
- spi_priv->status.busy = 0U;
- return 0;
- }
- #endif
- /**
- \brief Set the SPI datawidth.
- \param[in] addr pointer to register address
- \param[in] datawidth date frame size
- \return error code
- */
- static int32_t dw_spi_set_datawidth(dw_spi_reg_t *addr, DWENUM_SPI_DATAWIDTH datawidth)
- {
- if ((datawidth >= DWENUM_SPI_DATASIZE_4) && (datawidth <= DWENUM_SPI_DATASIZE_16)) {
- uint16_t temp = addr->CTRLR0;
- temp &= 0xfff0; /* temp has the value of CTRLR0 with DFS being cleared.*/
- temp |= (datawidth); /* get the final CTRLR0 after datawidth config. */
- addr->CTRLR0 = temp; /* write CTRLR0 */
- return 0;
- }
- return -1;
- }
- /**
- \brief Set the SPI clock divider.
- \param[in] addr pointer to register address
- \param[in] baud spi baud rate
- \param[in] apbfreq sysclk for spi module.
- \return error code
- */
- static int32_t dw_spi_set_baudrate(dw_spi_reg_t *addr, int32_t baud, int32_t apbfreq)
- {
- int32_t sckdv = apbfreq / baud;
- if (sckdv < 0x10000) {
- addr->BAUDR = sckdv;
- } else {
- return -1;
- }
- return 0;
- }
- /**
- \brief Set the SPI polarity.
- \param[in] addr pointer to register address
- \param[in] polarity spi polarity
- \return error code
- */
- static int32_t dw_spi_set_polarity(dw_spi_reg_t *addr, DWENUM_SPI_POLARITY polarity)
- {
- /* To config the polarity, we can set the SCPOL bit(CTRLR0[7]) as below:
- * 0 - inactive state of serial clock is low
- * 1 - inactive state of serial clock is high
- */
- switch (polarity) {
- case DWENUM_SPI_CLOCK_POLARITY_LOW:
- addr->CTRLR0 &= (~DW_SPI_POLARITY);
- break;
- case DWENUM_SPI_CLOCK_POLARITY_HIGH:
- addr->CTRLR0 |= DW_SPI_POLARITY;
- break;
- default:
- return -1;
- }
- return 0;
- }
- /**
- \brief Set the SPI Phase.
- \param[in] addr pointer to register address
- \param[in] phase Serial clock phase
- \return error code
- */
- static int32_t dw_spi_set_phase(dw_spi_reg_t *addr, DWENUM_SPI_PHASE phase)
- {
- switch (phase) {
- case DWENUM_SPI_CLOCK_PHASE_MIDDLE:
- addr->CTRLR0 &= (~DW_SPI_PHASE);
- break;
- case DWENUM_SPI_CLOCK_PHASE_START:
- addr->CTRLR0 |= DW_SPI_PHASE;
- break;
- default:
- return -1;
- }
- return 0;
- }
- /**
- \brief Set the SPI mode.
- \param[in] addr pointer to register address
- \param[in] mode SPI_Mode
- \return error code
- */
- static int32_t dw_spi_set_mode(spi_handle_t handle, DWENUM_SPI_MODE mode)
- {
- dw_spi_priv_t *spi_priv = handle;
- dw_spi_reg_t *addr = (dw_spi_reg_t *)(spi_priv->base);
- /* It is impossible to write to this register when the SSI is enabled.*/
- /* we can set the TMOD to config transfer mode as below:
- * TMOD_BIT9 TMOD_BIT8 transfer mode
- * 0 0 transmit & receive
- * 0 1 transmit only
- * 1 0 receive only
- * 1 1 reserved
- */
- switch (mode) {
- case DWENUM_SPI_TXRX:
- addr->CTRLR0 &= (~DW_SPI_TMOD_BIT8);
- addr->CTRLR0 &= (~DW_SPI_TMOD_BIT9);
- break;
- case DWENUM_SPI_TX:
- addr->CTRLR0 |= DW_SPI_TMOD_BIT8;
- addr->CTRLR0 &= (~DW_SPI_TMOD_BIT9);
- break;
- case DWENUM_SPI_RX:
- addr->CTRLR0 &= (~DW_SPI_TMOD_BIT8);
- addr->CTRLR0 |= DW_SPI_TMOD_BIT9;
- break;
- default:
- addr->CTRLR0 |= DW_SPI_TMOD_BIT8;
- addr->CTRLR0 |= DW_SPI_TMOD_BIT9;
- break;
- }
- spi_priv->mode = mode;
- return 0;
- }
- /**
- \brief interrupt service function for receive FIFO full interrupt .
- \param[in] spi_priv pointer to spi private.
- */
- static void dw_spi_intr_rx_full(dw_spi_priv_t *spi_priv)
- {
- dw_spi_reg_t *addr = (dw_spi_reg_t *)(spi_priv->base);
- uint8_t temp = addr->ICR;
- uint8_t *pbuffer = spi_priv->recv_buf;
- uint32_t length = spi_priv->recv_num;
- uint8_t rxnum;
- rxnum = addr->RXFLR;
- uint32_t i = 0u;
- for (i = 0; i < rxnum; i++) {
- *pbuffer = addr->DR;
- pbuffer++;
- }
- length -= rxnum;
- if (length < DW_SPI_FIFO_MAX_LV) {
- addr->RXFTLR = length - 1;
- }
- if (length <= 0) {
- temp = addr->IMR;
- temp &= 0x2f;
- addr->IMR = temp;
- addr->SER = 0;
- addr->SPIENR = DW_SPI_DISABLE;
- spi_priv->status.busy = 0U;
- dw_spi_ss_control(spi_priv, SPI_SS_INACTIVE);
- if (spi_priv->cb_event) {
- spi_priv->cb_event(SPI_EVENT_RX_COMPLETE, spi_priv->cb_arg);
- return;
- }
- } else {
- addr->SPIENR = DW_SPI_DISABLE; /* disable SPI */
- dw_spi_set_mode(spi_priv, DWENUM_SPI_RX);
- addr->SER = spi_priv->enable_slave; /* enable all cs */
- spi_priv->recv_buf = pbuffer;
- spi_priv->recv_num = length;
- if (spi_priv->recv_num > DW_SPI_FIFO_MAX_LV) {
- addr->RXFTLR = DW_SPI_FIFO_MAX_LV - 1;
- addr->CTRLR1 = DW_SPI_FIFO_MAX_LV - 1;
- } else {
- addr->RXFTLR = spi_priv->recv_num - 1;
- addr->CTRLR1 = spi_priv->recv_num - 1;
- }
- addr->SPIENR = DW_SPI_ENABLE; /* enable SPI */
- addr->DR = DW_SPI_START_RX ;
- }
- }
- /**
- \brief interrupt service function for transmit FIFO empty interrupt.
- \param[in] spi_priv pointer to spi private.
- */
- static void dw_spi_intr_tx_empty(dw_spi_priv_t *spi_priv)
- {
- dw_spi_reg_t *addr = (dw_spi_reg_t *)(spi_priv->base);
- uint8_t temp = addr->ICR;
- /* transfer mode:transmit & receive */
- uint32_t i = 0u;
- if (spi_priv->
- mode == DWENUM_SPI_TXRX) {
- /* read data out from rx FIFO */
- while (spi_priv->transfer_num) {
- *spi_priv->recv_buf = addr->DR;
- spi_priv->recv_buf++;
- spi_priv->transfer_num--;
- }
- if (spi_priv->clk_num >= DW_SPI_FIFO_MAX_LV) {
- spi_priv->transfer_num = DW_SPI_FIFO_MAX_LV;
- } else {
- spi_priv->transfer_num = spi_priv->clk_num;
- }
- for (i = 0; i < spi_priv->transfer_num; i++) {
- if (spi_priv->send_num == 0) {
- addr->DR = 0x0;
- } else {
- addr->DR = *spi_priv->send_buf;
- spi_priv->send_buf++;
- }
- spi_priv->send_num--;
- }
- } else { //transfer mode :transmit only
- if (spi_priv->clk_num >= DW_SPI_FIFO_MAX_LV) {
- spi_priv->transfer_num = DW_SPI_FIFO_MAX_LV;
- } else {
- spi_priv->transfer_num = spi_priv->clk_num;
- }
- for (i = 0; i < spi_priv->transfer_num; i++) {
- addr->DR = *spi_priv->send_buf;
- spi_priv->send_buf++;
- spi_priv->send_num--;
- }
- }
- if (spi_priv->clk_num == 0) {
- temp = addr->IMR;
- temp &= ~DW_SPI_IMR_TXEIM;
- addr->IMR = temp;
- addr->SER = 0;
- addr->SPIENR = DW_SPI_DISABLE;
- spi_priv->status.busy = 0U;
- dw_spi_ss_control(spi_priv, SPI_SS_INACTIVE);
- if (spi_priv->mode == DWENUM_SPI_TXRX) {
- if (spi_priv->cb_event) {
- spi_priv->cb_event(SPI_EVENT_TRANSFER_COMPLETE, spi_priv->cb_arg);
- return;
- }
- } else {
- if (spi_priv->cb_event) {
- spi_priv->cb_event(SPI_EVENT_TX_COMPLETE, spi_priv->cb_arg);
- return;
- }
- }
- }
- spi_priv->clk_num -= spi_priv->transfer_num;
- }
- /**
- \brief handler the interrupt.
- \param[in] spi Pointer to \ref SPI_RESOURCES
- */
- void dw_spi_irqhandler(int32_t idx)
- {
- dw_spi_priv_t *spi_priv = &spi_instance[idx];
- dw_spi_reg_t *addr = (dw_spi_reg_t *)(spi_priv->base);
- uint32_t intr = addr->ISR;
- /* deal with receive FIFO full interrupt */
- if (intr & DW_SPI_RXFIFO_FULL) {
- dw_spi_intr_rx_full(spi_priv);
- }
- /* deal with transmit FIFO empty interrupt */
- else if (intr & DW_SPI_TXFIFO_EMPTY) {
- dw_spi_intr_tx_empty(spi_priv);
- }
- }
- int32_t __attribute__((weak)) target_spi_init(pin_t mosi, pin_t miso, pin_t sclk, pin_t ssel, uint32_t *base, uint32_t *irq)
- {
- return -1;
- }
- /**
- \brief Initialize SPI Interface. 1. Initializes the resources needed for the SPI interface 2.registers event callback function
- \param[in] spi pin of mosi
- \param[in] spi pin of miso
- \param[in] spi pin of sclk
- \param[in] spi pin of ssel
- \param[in] cb_event event call back function \ref spi_event_cb_t
- \return return spi handle if success
- */
- spi_handle_t csi_spi_initialize(pin_t mosi, pin_t miso, pin_t sclk, pin_t ssel, spi_event_cb_t cb_event, void *cb_arg)
- {
- uint32_t base = 0u;
- uint32_t irq = 0u;
- int32_t idx = target_spi_init(mosi, miso, sclk, ssel, &base, &irq);
- if (idx < 0 || idx >= CONFIG_SPI_NUM) {
- return NULL;
- }
- dw_spi_priv_t *spi_priv = &spi_instance[idx];
- spi_priv->base = base;
- spi_priv->irq = irq;
- spi_priv->ssel = ssel;
- spi_priv->cb_event = cb_event;
- spi_priv->cb_arg = cb_arg;
- spi_priv->status.busy = 0U;
- spi_priv->status.data_lost = 0U;
- spi_priv->status.mode_fault = 0U;
- spi_priv->enable_slave = 1U;
- spi_priv->state = SPI_INITIALIZED;
- drv_nvic_enable_irq(spi_priv->irq);
- #ifdef CONFIG_SPI_DMA
- spi_priv->dma_handle = csi_dma_initialize(0);
- #endif
- return (spi_handle_t)spi_priv;
- }
- /**
- \brief De-initialize SPI Interface. stops operation and releases the software resources used by the interface
- \param[in] handle spi handle to operate.
- \return error code
- */
- int32_t csi_spi_uninitialize(spi_handle_t handle)
- {
- SPI_NULL_PARAM_CHK(handle);
- dw_spi_priv_t *spi_priv = handle;
- drv_nvic_disable_irq(spi_priv->irq);
- spi_priv->cb_event = NULL;
- spi_priv->state = 0U;
- spi_priv->status.busy = 0U;
- spi_priv->status.data_lost = 0U;
- spi_priv->status.mode_fault = 0U;
- #ifdef CONFIG_SPI_DMA
- csi_dma_uninitialize(spi_priv->dma_handle);
- #endif
- return 0;
- }
- /**
- \brief Get driver capabilities.
- \param[in] spi instance to operate.
- \return \ref spi_capabilities_t
- */
- spi_capabilities_t csi_spi_get_capabilities(spi_handle_t handle)
- {
- return spi_capabilities;
- }
- /**
- \brief config spi mode.
- \param[in] handle spi handle to operate.
- \param[in] sysclk sysclk for spi module.
- \param[in] baud spi baud rate. if negative, then this attribute not changed
- \param[in] mode \ref spi_mode_e . if negative, then this attribute not changed
- \param[in] format \ref spi_format_e . if negative, then this attribute not changed
- \param[in] order \ref spi_bit_order_e . if negative, then this attribute not changed
- \param[in] ss_mode \ref spi_ss_mode_e . if negative, then this attribute not changed
- \param[in] bit_width spi data bitwidth: (1 ~ SPI_DATAWIDTH_MAX) . if negative, then this attribute not changed
- \return error code
- */
- int32_t csi_spi_config(spi_handle_t handle,
- int32_t sysclk,
- int32_t baud,
- spi_mode_e mode,
- spi_format_e format,
- spi_bit_order_e order,
- spi_ss_mode_e ss_mode,
- int32_t bit_width)
- {
- SPI_NULL_PARAM_CHK(handle);
- dw_spi_priv_t *spi_priv = handle;
- dw_spi_reg_t *addr = (dw_spi_reg_t *)(spi_priv->base);
- if ((spi_priv->state & SPI_INITIALIZED) == 0U) {
- return ERR_SPI(EDRV_UNSUPPORTED);
- }
- if (spi_priv->status.busy) {
- return ERR_SPI(EDRV_BUSY);
- }
- spi_priv->status.busy = 0U;
- spi_priv->status.data_lost = 0U;
- spi_priv->status.mode_fault = 0U;
- addr->SPIENR = DW_SPI_DISABLE;
- addr->IMR = DW_SPI_INT_DISABLE;
- int32_t ret = 0;
- if (baud >= 0) {
- ret = dw_spi_set_baudrate(addr, baud, sysclk);
- if (ret < 0) {
- return ERR_SPI(EDRV_PARAMETER);
- }
- }
- if (mode >= 0) {
- switch (mode) {
- case SPI_MODE_MASTER:
- break;
- default:
- return ERR_SPI(EDRV_UNSUPPORTED);
- }
- }
- if (format >= 0) {
- switch (format) {
- case SPI_FORMAT_CPOL0_CPHA0:
- dw_spi_set_polarity(addr, DWENUM_SPI_CLOCK_POLARITY_LOW);
- dw_spi_set_phase(addr, DWENUM_SPI_CLOCK_PHASE_MIDDLE);
- break;
- case SPI_FORMAT_CPOL0_CPHA1:
- dw_spi_set_polarity(addr, DWENUM_SPI_CLOCK_POLARITY_LOW);
- dw_spi_set_phase(addr, DWENUM_SPI_CLOCK_PHASE_START);
- break;
- case SPI_FORMAT_CPOL1_CPHA0:
- dw_spi_set_polarity(addr, DWENUM_SPI_CLOCK_POLARITY_HIGH);
- dw_spi_set_phase(addr, DWENUM_SPI_CLOCK_PHASE_MIDDLE);
- break;
- case SPI_FORMAT_CPOL1_CPHA1:
- dw_spi_set_polarity(addr, DWENUM_SPI_CLOCK_POLARITY_HIGH);
- dw_spi_set_phase(addr, DWENUM_SPI_CLOCK_PHASE_START);
- break;
- default:
- return ERR_SPI(EDRV_PARAMETER);
- }
- }
- if (order >= 0) {
- //TD:
- }
- if (ss_mode >= 0) {
- switch (ss_mode) {
- case SPI_SS_MASTER_SW:
- spi_priv->ss_mode = SPI_SS_MASTER_SW;
- break;
- case SPI_SS_MASTER_HW_OUTPUT:
- dw_spi_ss_init(spi_priv);
- break;
- default:
- return ERR_SPI(EDRV_UNSUPPORTED);
- }
- }
- if (bit_width >= 0) {
- ret = dw_spi_set_datawidth(addr, bit_width);
- if (ret < 0) {
- return ERR_SPI(EDRV_PARAMETER);
- }
- }
- spi_priv->state |= SPI_CONFIGURED;
- return 0;
- }
- /**
- \brief config spi default tx value.
- \param[in] handle spi handle to operate.
- \param[in] value default tx value
- \return error code
- */
- int32_t csi_spi_set_default_tx_value(spi_handle_t handle, uint32_t value)
- {
- SPI_NULL_PARAM_CHK(handle);
- return ERR_SPI(EDRV_UNSUPPORTED);
- }
- /**
- \brief sending data to SPI transmitter,(received data is ignored).
- if non-blocking mode, this function only start the sending,
- \ref spi_event_e is signaled when operation completes or error happens.
- \ref csi_spi_get_status can indicates operation status.
- if blocking mode, this function return after operation completes or error happens.
- \param[in] handle spi handle to operate.
- \param[in] data Pointer to buffer with data to send to SPI transmitter. data_type is : uint8_t for 1..8 data bits, uint16_t for 9..16 data bits,uint32_t for 17..32 data bits,
- \param[in] num Number of data items to send.
- \param[in] block_mode blocking and non_blocking to selcect
- \return error code
- */
- int32_t csi_spi_send(spi_handle_t handle, const void *data, uint32_t num, uint8_t block_mode)
- {
- if (handle == NULL || data == NULL || num == 0) {
- return ERR_SPI(EDRV_PARAMETER);
- }
- dw_spi_priv_t *spi_priv = handle;
- if ((spi_priv->state & SPI_CONFIGURED) == 0U) {
- return ERR_SPI(EDRV_UNSUPPORTED);
- }
- if (spi_priv->status.busy) {
- return ERR_SPI(EDRV_BUSY);
- }
- spi_priv->status.busy = 1U;
- spi_priv->status.data_lost = 0U;
- spi_priv->status.mode_fault = 0U;
- #ifdef CONFIG_SPI_DMA
- return dw_spi_dma_send(spi_priv, data, num);
- #endif
- dw_spi_ss_control(spi_priv, SPI_SS_ACTIVE);
- dw_spi_reg_t *addr = (dw_spi_reg_t *)(spi_priv->base);
- addr->SPIENR = DW_SPI_DISABLE; /* disable SPI */
- dw_spi_set_mode(spi_priv, DWENUM_SPI_TX);
- addr->SPIENR = DW_SPI_ENABLE; /* enable SPI */
- uint8_t *ptxbuffer = (uint8_t *)data;
- if (block_mode) {
- addr->SER = spi_priv->enable_slave;
- addr->TXFTLR = DW_SPI_TXFIFO_LV;
- dw_spi_ss_control(spi_priv, SPI_SS_ACTIVE);
- spi_priv->send_num = num;
- uint32_t once_len , i;
- while (spi_priv->send_num) {
- once_len = (spi_priv->send_num >= DW_SPI_FIFO_MAX_LV) ?
- DW_SPI_FIFO_MAX_LV : spi_priv->send_num;
- for (i = 0; i < once_len; i++) {
- addr->DR = *ptxbuffer++;
- }
- while (!(addr->SR & DW_SPI_TFE));
- while (addr->SR & DW_SPI_BUSY);
- spi_priv->send_num -= once_len;
- }
- dw_spi_ss_control(spi_priv, SPI_SS_INACTIVE);
- spi_priv->status.busy = 0U;
- } else {
- spi_priv->send_num = num;
- spi_priv->clk_num = num;
- spi_priv->send_buf = ptxbuffer;
- spi_priv->transfer_num = 0;
- addr->SPIENR = DW_SPI_ENABLE; /* enable SPI */
- addr->TXFTLR = DW_SPI_TXFIFO_LV;
- addr->SER = spi_priv->enable_slave;
- addr->IMR = DW_SPI_IMR_TXEIM;
- }
- return 0;
- }
- /**
- \brief receiving data from SPI receiver.transmits the default value as specified by csi_spi_set_default_tx_value
- if non-blocking mode, this function only start the receiving,
- \ref spi_event_e is signaled when operation completes or error happens.
- \ref csi_spi_get_status can indicates operation status.
- if blocking mode, this function return after operation completes or error happens.
- \param[in] handle spi handle to operate.
- \param[out] data Pointer to buffer for data to receive from SPI receiver
- \param[in] num Number of data items to receive
- \param[in] block_mode blocking and non_blocking to selcect
- \return error code
- */
- int32_t csi_spi_receive(spi_handle_t handle, void *data, uint32_t num, uint8_t block_mode)
- {
- if (handle == NULL || data == NULL || num == 0) {
- return ERR_SPI(EDRV_PARAMETER);
- }
- dw_spi_priv_t *spi_priv = handle;
- if ((spi_priv->state & SPI_CONFIGURED) == 0U) {
- return ERR_SPI(EDRV_UNSUPPORTED);
- }
- if (spi_priv->status.busy) {
- return ERR_SPI(EDRV_BUSY);
- }
- spi_priv->status.busy = 1U;
- spi_priv->status.data_lost = 0U;
- spi_priv->status.mode_fault = 0U;
- #ifdef CONFIG_SPI_DMA
- return dw_spi_dma_receive(spi_priv, data, num);
- #endif
- dw_spi_reg_t *addr = (dw_spi_reg_t *)(spi_priv->base);
- uint8_t *prx_buffer = data;
- if (block_mode) {
- dw_spi_ss_control(spi_priv, SPI_SS_ACTIVE);
- spi_priv->recv_buf = prx_buffer;
- spi_priv->recv_num = num;
- while (spi_priv->recv_num) {
- addr->SPIENR = DW_SPI_DISABLE; /* disable SPI */
- dw_spi_set_mode(spi_priv, DWENUM_SPI_RX);
- addr->SER = spi_priv->enable_slave; /* enable all cs */
- uint32_t once_len = (spi_priv->recv_num >= DW_SPI_FIFO_MAX_LV) ?
- DW_SPI_FIFO_MAX_LV : spi_priv->recv_num;
- addr->CTRLR1 = once_len - 1;
- addr->RXFTLR = once_len - 1;
- addr->SPIENR = DW_SPI_ENABLE;
- addr->DR = 0;
- while (addr->RXFLR < once_len);
- int i = 0;
- for (i = 0; i < once_len; i++) {
- *spi_priv->recv_buf++ = addr->DR;
- }
- spi_priv->recv_num -= once_len;
- }
- dw_spi_ss_control(spi_priv, SPI_SS_INACTIVE);
- spi_priv->status.busy = 0U;
- } else {
- dw_spi_ss_control(spi_priv, SPI_SS_ACTIVE);
- addr->SPIENR = DW_SPI_DISABLE; /* disable SPI */
- spi_priv->recv_buf = prx_buffer;
- spi_priv->recv_num = num;
- dw_spi_set_mode(spi_priv, DWENUM_SPI_RX);
- addr->SER = spi_priv->enable_slave; /* enable all cs */
- if (num > DW_SPI_FIFO_MAX_LV) {
- addr->RXFTLR = DW_SPI_FIFO_MAX_LV - 1;
- addr->CTRLR1 = DW_SPI_FIFO_MAX_LV - 1;
- } else {
- addr->RXFTLR = num - 1;
- addr->CTRLR1 = num - 1;
- }
- addr->IMR = DW_SPI_IMR_RXFIM;
- addr->SPIENR = DW_SPI_ENABLE; /* enable SPI */
- addr->DR = DW_SPI_START_RX ;
- }
- return 0;
- }
- /**
- \brief sending/receiving data to/from SPI transmitter/receiver.
- if non-blocking mode, this function only start the transfer,
- \ref spi_event_e is signaled when operation completes or error happens.
- \ref csi_spi_get_status can indicates operation status.
- if blocking mode, this function return after operation completes or error happens.
- \param[in] handle spi handle to operate.
- \param[in] data_out Pointer to buffer with data to send to SPI transmitter
- \param[out] data_in Pointer to buffer for data to receive from SPI receiver
- \param[in] num_out Number of data items to send
- \param[in] num_in Number of data items to receive
- \param[in] block_mode blocking and non_blocking to selcect
- \return error code
- */
- int32_t csi_spi_transfer(spi_handle_t handle, const void *data_out, void *data_in, uint32_t num_out, uint32_t num_in, uint8_t block_mode)
- {
- if (handle == NULL || data_in == NULL || num_out == 0 || num_in == 0 || data_out == NULL) {
- return ERR_SPI(EDRV_PARAMETER);
- }
- dw_spi_priv_t *spi_priv = handle;
- if ((spi_priv->state & SPI_CONFIGURED) == 0U) {
- return ERR_SPI(EDRV_UNSUPPORTED);
- }
- if (spi_priv->status.busy) {
- return ERR_SPI(EDRV_BUSY);
- }
- spi_priv->status.busy = 1U;
- spi_priv->status.data_lost = 0U;
- spi_priv->status.mode_fault = 0U;
- #ifdef CONFIG_SPI_DMA
- return dw_spi_dma_transfer(spi_priv, data_out, data_in, num_out, num_in);
- #endif
- dw_spi_ss_control(spi_priv, SPI_SS_ACTIVE);
- dw_spi_reg_t *addr = (dw_spi_reg_t *)(spi_priv->base);
- addr->SPIENR = DW_SPI_DISABLE; /* disable SPI */
- dw_spi_set_mode(spi_priv, DWENUM_SPI_TXRX);
- addr->SER = spi_priv->enable_slave; /* enable all cs */
- uint8_t *ptx_buffer = (uint8_t *)data_out;
- uint8_t *prx_buffer = (uint8_t *)data_in;
- uint32_t i = 0u;
- if (block_mode) {
- for (i = 0; i < num_out; i++) { /* transmit datas in transmit-buffer */
- addr->DR = *ptx_buffer;
- ptx_buffer++;
- }
- while ((addr->SR & DW_SPI_BUSY));
- while ((addr->SR & DW_SPI_TFE) == 0);
- while ((addr->SR & DW_SPI_RFNE) == 0);
- addr->SPIENR = DW_SPI_DISABLE; /* disable SPI */
- dw_spi_set_mode(addr, DWENUM_SPI_RX);
- addr->SPIENR = DW_SPI_ENABLE;
- addr->DR = DW_SPI_START_RX;
- for (i = 0; i < num_in; i++) {
- *prx_buffer = addr->DR;
- prx_buffer++;
- }
- addr->SER = 0;
- dw_spi_ss_control(spi_priv, SPI_SS_INACTIVE);
- addr->SPIENR = DW_SPI_DISABLE;
- spi_priv->status.busy = 0U;
- } else {
- spi_priv->send_buf = ptx_buffer;
- spi_priv->recv_buf = prx_buffer;
- spi_priv->send_num = num_out;
- spi_priv->recv_num = num_in;
- spi_priv->clk_num = num_in;
- spi_priv->transfer_num = 0;
- addr->TXFTLR = DW_SPI_TXFIFO_LV;
- addr->SPIENR = DW_SPI_ENABLE;
- /* enable transmit FIFO empty interrupt */
- addr->IMR |= DW_SPI_IMR_TXEIM;
- }
- return 0;
- }
- /**
- \brief abort spi transfer.
- \param[in] handle spi handle to operate.
- \return error code
- */
- int32_t csi_spi_abort_transfer(spi_handle_t handle)
- {
- SPI_NULL_PARAM_CHK(handle);
- dw_spi_priv_t *spi_priv = handle;
- dw_spi_reg_t *addr = (dw_spi_reg_t *)(spi_priv->base);
- addr->SPIENR = DW_SPI_DISABLE;
- spi_priv->status.busy = 0U;
- spi_priv->recv_buf = NULL;
- spi_priv->recv_num = 0;
- return 0;
- }
- /**
- \brief Get SPI status.
- \param[in] handle spi handle to operate.
- \return SPI status \ref ARM_SPI_STATUS
- */
- spi_status_t csi_spi_get_status(spi_handle_t handle)
- {
- spi_status_t spi_status = {0};
- if (handle == NULL) {
- return spi_status;
- }
- dw_spi_priv_t *spi_priv = handle;
- return spi_priv->status;
- }
|