123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517 |
- /*
- * 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 ck_dmac.c
- * @brief CSI Source File for DMAC Driver
- * @version V1.0
- * @date 02. June 2017
- ******************************************************************************/
- #include <stdbool.h>
- #include "ck_dmac.h"
- #include "csi_core.h"
- #include "drv_dmac.h"
- #include "soc.h"
- #define ERR_DMA(errno) (CSI_DRV_ERRNO_DMA_BASE | errno)
- typedef struct {
- uint32_t base;
- uint32_t irq;
- dma_event_cb_t cb_event;
- uint8_t ch_num;
- } ck_dma_priv_t;
- static ck_dma_priv_t dma_instance[CONFIG_DMAC_NUM];
- static const dma_capabilities_t dma_capabilities = {
- .unalign_addr = 1, ///< support for unalign address transfer when memory is source
- };
- static volatile dma_status_e status[CK_DMA_MAXCHANNEL] = {DMA_STATE_FREE, DMA_STATE_FREE};
- static volatile uint8_t ch_opened[CK_DMA_MAXCHANNEL] = {0, 0};
- static int32_t ck_dma_set_channel(ck_dma_reg_t *addr, uint32_t source, uint32_t dest, uint32_t size)
- {
- uint32_t temp = addr->CHCTRLA;
- temp &= 0xff000fff;
- temp |= (size << 12);
- addr->SAR = source;
- addr->DAR = dest ;
- addr->CHCTRLA = temp;
- return 0;
- }
- static int32_t ck_dma_set_transfertype(ck_dma_reg_t *addr, dma_trans_type_e transtype)
- {
- uint32_t temp = addr->CHCTRLB;
- temp &= 0xffffff7f;
- if (transtype >= DMA_PERH2PERH) {
- return ERR_DMA(EDRV_PARAMETER);
- }
- if (transtype == DMA_MEM2MEM) {
- temp |= (transtype << 7);
- } else {
- temp |= (1 << 7);
- }
- addr->CHCTRLB = temp;
- return 0;
- }
- static int32_t ck_dma_set_addrinc(ck_dma_reg_t *addr, enum_addr_state_e src_addrinc, enum_addr_state_e dst_addrinc)
- {
- if ((src_addrinc != DMA_ADDR_INCREMENT && src_addrinc != DMA_ADDR_DECREMENT && src_addrinc != DMA_ADDR_NOCHANGE) ||
- (dst_addrinc != DMA_ADDR_INCREMENT && dst_addrinc != DMA_ADDR_DECREMENT && dst_addrinc != DMA_ADDR_NOCHANGE)) {
- return ERR_DMA(EDRV_PARAMETER);
- }
- uint32_t temp = addr->CHCTRLA;
- temp &= 0xffffff0f;
- temp |= (src_addrinc << 6);
- temp |= (dst_addrinc << 4);
- addr->CHCTRLA = temp;
- return 0;
- }
- static int32_t ck_dma_set_transferwidth(ck_dma_reg_t *addr, dma_datawidth_e src_width, dma_datawidth_e dst_width)
- {
- if ((src_width != DMA_DATAWIDTH_SIZE8 && src_width != DMA_DATAWIDTH_SIZE16 && src_width != DMA_DATAWIDTH_SIZE32) ||
- (dst_width != DMA_DATAWIDTH_SIZE8 && dst_width != DMA_DATAWIDTH_SIZE16 && dst_width != DMA_DATAWIDTH_SIZE32)) {
- return ERR_DMA(EDRV_PARAMETER);
- }
- uint32_t temp = addr->CHCTRLA;
- temp &= 0xfffffff0;
- temp |= (src_width - 1) << 2;
- temp |= dst_width - 1;
- addr->CHCTRLA = temp;
- return 0;
- }
- static int32_t ck_dma_set_burstlength(ck_dma_reg_t *addr, uint8_t burstlength)
- {
- uint32_t temp = addr->CHCTRLA;
- temp &= 0xfffff0ff;
- temp |= (burstlength << 8);
- addr->CHCTRLA = temp;
- return 0;
- }
- /**
- \brief Set software or hardware handshaking.
- \param[in] addr pointer to dma register.
- \return error code
- */
- static int32_t ck_dma_set_handshaking(ck_dma_reg_t *addr, dma_handshaking_select_e handshaking)
- {
- uint32_t temp = addr->CHCTRLB;
- temp &= 0xfffffeff;
- temp |= (handshaking << 8);
- addr->CHCTRLB = temp;
- return 0;
- }
- static int ck_dma_assign_hdhs_interface(ck_dma_reg_t *addr, ckenum_dma_device_e device)
- {
- if (device < 0 || device >= CKENUM_DMA_MEMORY) {
- return ERR_DMA(EDRV_PARAMETER);
- }
- addr->CHCTRLB &= 0xffffe1ff;
- addr->CHCTRLB |= (device << 9);
- return 0;
- }
- void ck_dma_irqhandler(int32_t idx)
- {
- ck_dma_priv_t *dma_priv = &dma_instance[idx];
- ck_dma_reg_t *addr = (ck_dma_reg_t *)(dma_priv->base);
- /*
- * StatusInt_temp contain the information that which types of interrupr are
- * requested.
- */
- int32_t count = 0;
- uint32_t temp = 0;
- for (count = 0; count < dma_priv->ch_num; count++) {
- addr = (ck_dma_reg_t *)(dma_priv->base + count * 0x30);
- temp = addr->CHINTS;
- if (temp != 0) {
- break;
- }
- }
- /* If Tfr interrupt is requested */
- if (temp == CK_DMA_TFR) {
- status[count] = DMA_STATE_DONE;
- addr->CHINTC = temp;
- if (dma_priv->cb_event) {
- dma_priv->cb_event(DMA_EVENT_TRANSFER_DONE, count);
- }
- }
- /* If Err interrput is requested */
- if (temp == CK_DMA_ERR) {
- status[count] = DMA_STATE_ERROR;
- addr->CHINTC = temp;
- if (dma_priv->cb_event) {
- dma_priv->cb_event(DMA_EVENT_TRANSFER_ERROR, count);
- }
- }
- }
- int32_t __attribute__((weak)) target_get_dmac_count(void)
- {
- return 0;
- }
- int32_t __attribute__((weak)) target_get_dmac(uint32_t idx, uint32_t *base, uint32_t *irq)
- {
- return NULL;
- }
- /**
- \brief get dma instance count.
- \return dma instance count
- */
- int32_t csi_dma_get_instance_count(void)
- {
- return target_get_dmac_count();
- }
- /**
- \brief Initialize DMA Interface. 1. Initializes the resources needed for the DMA interface 2.registers event callback function
- \param[in] idx must not exceed return value of csi_dma_get_instance_count()
- \return pointer to dma instances
- */
- dmac_handle_t csi_dma_initialize(int32_t idx)
- {
- if (idx < 0 || idx >= CONFIG_DMAC_NUM) {
- return NULL;
- }
- uint32_t base = 0u;
- uint32_t irq = 0u;
- int32_t real_idx = target_get_dmac(idx, &base, &irq);
- if (real_idx != idx) {
- return NULL;
- }
- ck_dma_priv_t *dma_priv = &dma_instance[idx];
- dma_priv->base = base;
- dma_priv->irq = irq;
- dma_priv->ch_num = CK_DMA_MAXCHANNEL;
- drv_nvic_enable_irq(dma_priv->irq);
- uint8_t count = 0u;
- for (count = 0; count < dma_priv->ch_num; count++) {
- ck_dma_reg_t *addr = (ck_dma_reg_t *)(dma_priv->base + count * 0x30);
- addr->CHINTM = CK_DMA_MASK;
- addr->CHINTC = CK_DMA_INTC;
- }
- return (dmac_handle_t)dma_priv;
- }
- /**
- \brief De-initialize DMA Interface. stops operation and releases the software resources used by the interface
- \param[in] handle damc handle to operate.
- \return error code
- */
- int32_t csi_dma_uninitialize(dmac_handle_t handle)
- {
- if (handle == NULL) {
- return ERR_DMA(EDRV_PARAMETER);
- }
- ck_dma_priv_t *dma_priv = handle;
- ck_dma_reg_t *addr = (ck_dma_reg_t *)(dma_priv->base);
- uint8_t count;
- for (count = 0; count < dma_priv->ch_num; count++) {
- addr = (ck_dma_reg_t *)(dma_priv->base + count * 0x30);
- addr->CHINTM = CK_DMA_MASK;
- addr->CHINTC = CK_DMA_INTC;
- }
- drv_nvic_disable_irq(dma_priv->irq);
- return 0;
- }
- /**
- \brief Get driver capabilities.
- \param[in] handle damc handle to operate.
- \return \ref dma_capabilities_t
- */
- dma_capabilities_t csi_dma_get_capabilities(dmac_handle_t handle)
- {
- return dma_capabilities;
- }
- /**
- \brief get one free dma channel
- \param[in] handle damc handle to operate.
- \param[in] ch channel num. if -1 then allocate a free channal in this dma
- \return -1 - no channel can be used, other - channel index
- */
- int32_t csi_dma_alloc_channel(dmac_handle_t handle, int32_t ch)
- {
- ck_dma_priv_t *dma_priv = handle;
- if (handle == NULL || ch > dma_priv->ch_num) {
- return ERR_DMA(EDRV_PARAMETER);
- }
- uint8_t ch_num = 0;
- ck_dma_reg_t *addr = NULL;
- if (ch == -1) { // alloc a free channal
- for (ch_num = 0; ch_num < dma_priv->ch_num; ch_num++) {
- addr = (ck_dma_reg_t *)(dma_priv->base + ch_num * 0x30);
- if (ch_opened[ch_num] != 0x1) {
- ch_opened[ch_num] = 1;
- break;
- }
- }
- if (ch_num >= dma_priv->ch_num) {
- return -1;
- }
- } else { //alloc a fixed channel
- addr = (ck_dma_reg_t *)(dma_priv->base + ch * 0x30);
- if (ch_opened[ch] == 0x1) {
- return ERR_DMA(EDRV_BUSY);
- }
- ch_opened[ch] = 1;
- ch_num = ch;
- }
- addr->CHINTC = CK_DMA_INTC;
- addr->CHINTM &= ~CK_DMA_MASK;
- status[ch_num] = DMA_STATE_READY;
- return ch_num;
- }
- /**
- \brief release dma channel and related resources
- \param[in] handle damc handle to operate.
- \param[in] ch channel num.
- \return error code
- */
- int32_t csi_dma_release_channel(dmac_handle_t handle, int32_t ch)
- {
- ck_dma_priv_t *dma_priv = handle;
- if (handle == NULL || ch >= dma_priv->ch_num || ch < 0) {
- return ERR_DMA(EDRV_PARAMETER);
- }
- ck_dma_reg_t *addr = (ck_dma_reg_t *)(dma_priv->base + ch * 0x30);
- status[ch] = DMA_STATE_FREE;
- ch_opened[ch] = 0;
- addr->CHINTC = CK_DMA_INTC;
- addr->CHINTM = CK_DMA_MASK;
- return 0;
- }
- /**
- \brief
- \param[in] handle damc handle to operate.
- \param[in] ch channel num. if -1 then allocate a free channal in this dma
- \param[in] psrcaddr dma transfer source address
- \param[in] pstdaddr dma transfer source address
- \param[in] length dma transfer length
- \param[in] config dma transfer configure
- \param[in] cb_event Pointer to \ref dma_event_cb_t
- \return error code
- */
- int32_t csi_dma_config(dmac_handle_t handle, int32_t ch,
- void *psrcaddr, void *pstdaddr,
- uint32_t length, dma_config_t *config, dma_event_cb_t cb_event)
- {
- ck_dma_priv_t *dma_priv = handle;
- if (handle == NULL || ch >= dma_priv->ch_num || config == NULL) {
- return ERR_DMA(EDRV_PARAMETER);
- }
- if (ch == -1) { //alloc a free channel
- ch = csi_dma_alloc_channel(handle, -1);
- if (ch < 0) {
- return ERR_DMA(EDRV_BUSY);
- }
- }
- dma_priv->cb_event = cb_event;
- ck_dma_reg_t *addr = (ck_dma_reg_t *)(dma_priv->base + ch * 0x30);
- /* Initializes corresponding channel registers */
- if ((length * config->src_tw) % config->dst_tw != 0) {
- return ERR_DMA(EDRV_PARAMETER);
- }
- int32_t ret = ck_dma_set_transferwidth(addr, config->src_tw, config->dst_tw);
- if (ret) {
- return ret;
- }
- int32_t grouplen = ((length * config->src_tw / config->dst_tw) - 1) % 16;
- ck_dma_set_burstlength(addr, grouplen);
- ret = ck_dma_set_transfertype(addr, config->type);
- if (ret < 0) {
- return ret;
- }
- if (config->type == DMA_MEM2MEM) {
- ck_dma_set_handshaking(addr, DMA_HANDSHAKING_SOFTWARE);
- ret = ck_dma_set_addrinc(addr , config->src_inc, config->dst_inc);
- } else if (config->type == DMA_MEM2PERH) {
- ck_dma_set_handshaking(addr, DMA_HANDSHAKING_HARDWARE);
- ret = ck_dma_set_addrinc(addr , config->src_inc, config->dst_inc);
- if (ret) {
- return ret;
- }
- ret = ck_dma_assign_hdhs_interface(addr, config->hs_if);
- if (ret) {
- return ret;
- }
- } else if (config->type == DMA_PERH2MEM) {
- ck_dma_set_handshaking(addr, DMA_HANDSHAKING_HARDWARE);
- ret = ck_dma_set_addrinc(addr , config->src_inc, config->dst_inc);
- if (ret) {
- return ret;
- }
- ret = ck_dma_assign_hdhs_interface(addr, config->hs_if);
- if (ret) {
- return ret;
- }
- }
- ck_dma_set_channel(addr, (uint32_t)psrcaddr, (uint32_t)pstdaddr, length);
- status[ch] = DMA_STATE_READY;
- return 0;
- }
- /**
- \brief start generate dma signal.
- \param[in] handle damc handle to operate.
- \param[in] ch channel num.
- \return error code
- */
- int32_t csi_dma_start(dmac_handle_t handle, int32_t ch)
- {
- ck_dma_priv_t *dma_priv = handle;
- if (handle == NULL || ch >= dma_priv->ch_num || ch < 0) {
- return ERR_DMA(EDRV_PARAMETER);
- }
- status[ch] = DMA_STATE_BUSY;
- ck_dma_reg_t *addr = (ck_dma_reg_t *)(dma_priv->base + ch * 0x30);
- addr->CHCTRLB |= CK_DMA_INT_EN; // interrupt enable
- addr->CHEN |= CK_DMA_CH_EN;
- return 0;
- }
- /**
- \brief Stop generate dma signal.
- \param[in] handle damc handle to operate.
- \param[in] ch channel num.
- \return error code
- */
- int32_t csi_dma_stop(dmac_handle_t handle, int32_t ch)
- {
- if (handle == NULL) {
- return ERR_DMA(EDRV_PARAMETER);
- }
- ck_dma_priv_t *dma_priv = handle;
- if (ch >= dma_priv->ch_num || ch < 0) {
- return ERR_DMA(EDRV_PARAMETER);
- }
- status[ch] = DMA_STATE_DONE;
- ck_dma_reg_t *addr = (ck_dma_reg_t *)(dma_priv->base + ch * 0x30);
- addr->CHCTRLB &= ~CK_DMA_INT_EN; // interrupt disable
- addr->CHEN &= ~CK_DMA_CH_EN;
- return 0;
- }
- /**
- \brief Get DMA status.
- \param[in] handle damc handle to operate.
- \param[in] ch channel num.
- \return DMA status \ref dma_status_t
- */
- dma_status_e csi_dma_get_status(dmac_handle_t handle, int32_t ch)
- {
- if (handle == NULL) {
- return ERR_DMA(EDRV_PARAMETER);
- }
- ck_dma_priv_t *dma_priv = handle;
- if (ch >= dma_priv->ch_num || ch < 0) {
- return ERR_DMA(EDRV_PARAMETER);
- }
- return status[ch];
- }
|