/** ****************************************************************************** * @file bflb_sdio2.c * @version V1.0 * @date 2022-10-12 * @brief This file is the low hardware abstraction layer file ****************************************************************************** * @attention * *

© COPYRIGHT(c) 2022 Bouffalo Lab

* * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of Bouffalo Lab nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************** */ #include "../include/bflb_sdio2.h" #include "../include/hardware/sdio2_reg.h" //#define SDIO2_DRV_DBG(a, ...) printf(a, ##__VA_ARGS__) #define SDIO2_DRV_DBG(a, ...) #ifndef BOOTROM #define SDIO2_DRV_ERR(a, ...) printf("[Error]:" a, ##__VA_ARGS__) #else extern void bflb_bootrom_printf(char *fmt, ...); #define SDIO2_DRV_ERR(a, ...) bflb_bootrom_printf("[Error]:" a, ##__VA_ARGS__) #endif #define SDU_INT_HOST 0 static uintptr_t local_tx_buf[SDIO2_MAX_PORT_NUM][SDIO2_BYTE_PER_BUF / sizeof(uintptr_t)] ATTR_NOCACHE_RAM_SECTION; static uintptr_t local_rx_buf[SDIO2_MAX_PORT_NUM][SDIO2_BYTE_PER_BUF / sizeof(uintptr_t)] ATTR_NOCACHE_RAM_SECTION; /****************************************************************************/ /** * @brief Get sdio2 block size * * @param dev SDIO device pointer * * @return Block size * *******************************************************************************/ uint32_t bflb_sdio2_get_block_size(struct bflb_device_s *dev) { uint16_t blk_size = 0; uint32_t reg_base = dev->reg_base; blk_size = getreg8(reg_base + SDIO2_FN1_BLK_SIZE_0_OFFSET); blk_size |= ((getreg8(reg_base + SDIO2_FN1_BLK_SIZE_1_OFFSET) & SDIO2_FN1_BLK_SIZE_1_MASK) << 8); return blk_size; } /****************************************************************************/ /** * @brief sdio2 init * * @param dev: SDIO device pointer * * @return BFLB_RET:0 means success and other value means error * *******************************************************************************/ int bflb_sdio2_init(struct bflb_device_s *dev) { uint32_t reg_base = dev->reg_base; uint32_t regval = 0; putreg16(0, reg_base + SDIO2_RD_BIT_MAP_OFFSET); putreg16(0, reg_base + SDIO2_WR_BIT_MAP_OFFSET); /* toggle SDIO_CCR_CIC_DnLdOvr on WL_SDIO_CCR_CARD_INT_CAUSE */ putreg8(SDIO2_CCR_CIC_DnLdOvr, reg_base + SDIO2_CARD_INT_STATUS_OFFSET); putreg8(0, reg_base + SDIO2_CARD_INT_STATUS_OFFSET); if (1) { /* multiport */ regval = getreg32(reg_base + SDIO2_CONFIG2_OFFSET); putreg32(regval | SDIO2_CONFIG2_MSK, reg_base + SDIO2_CONFIG2_OFFSET); regval = getreg8(reg_base + SDIO2_CONFIG_OFFSET); putreg8(regval | 0x00000010, reg_base + SDIO2_CONFIG_OFFSET); } /* unmask the interrupts */ putreg8(SDIO2_CCR_CIM_MASK, reg_base + SDIO2_CARD_INT_MASK_OFFSET); /* select interrupt reset mode */ putreg8(0, reg_base + SDIO2_CARD_INT_MODE_OFFSET); bflb_sdio2_tx_rx_queue_init(dev); bflb_irq_attach(dev->irq_num, bflb_sdio2_isr, dev); bflb_irq_enable(dev->irq_num); return 0; } /****************************************************************************/ /** * @brief SDIO2 tx and rx queue init * * @param dev: SDIO device pointer * * @return BFLB_RET:0 means success and other value means error * *******************************************************************************/ int bflb_sdio2_tx_rx_queue_init(struct bflb_device_s *dev) { uint32_t reg_base = dev->reg_base; uint16_t wr_bit_map = getreg16(reg_base + SDIO2_WR_BIT_MAP_OFFSET); for (uint8_t i = 0; i < SDIO2_MAX_PORT_NUM; i++) { if (!(wr_bit_map & (1 << i))) { /* attach new buffer */ putreg8(i, reg_base + SDIO2_WRITE_INDEX_OFFSET); putreg32((uint32_t)&local_rx_buf[i][0], reg_base + SDIO2_SQ_WRITE_BASE_OFFSET); putreg16(1 << i, reg_base + SDIO2_WR_BIT_MAP_OFFSET); #if SDU_INT_HOST putreg8((SDIO2_CCR_CS_ReadCISRdy | SDIO2_CCR_CS_DnLdRdy | SDIO2_CCR_CS_IORdy), reg_base + SDIO2_CARD_TO_HOST_EVENT_OFFSET); #endif } } return 0; } /****************************************************************************/ /** * @brief SDIO2 check host ready * * @param dev: SDIO device pointer * * @return 1 for host ready, 0 for not * *******************************************************************************/ int bflb_sdio2_check_host_ready(struct bflb_device_s *dev) { uint32_t reg_base = dev->reg_base; uint32_t regval = 0; regval = getreg8(reg_base + SDIO2_SCRATCH_OFFSET); if (regval != 0) { return 1; } else { return 0; } } /****************************************************************************/ /** * @brief SDIO2 send data,this function can be in user app context * * @param dev: SDIO device pointer * @param qos: qos number for this data buffer to send * @param buff: data buffer pointer * @param len: data length * * @return BFLB_RET:0 means success and other value means error * *******************************************************************************/ int bflb_sdio2_send_data(struct bflb_device_s *dev, int qos, uintptr_t *buff, int len) { uint32_t reg_base = dev->reg_base; uint16_t rd_bit_map = getreg16(reg_base + SDIO2_RD_BIT_MAP_OFFSET); static uint8_t curr_upld_port = 0; if (rd_bit_map & (1 << curr_upld_port)) { return -1; } arch_memcpy_fast(local_tx_buf[curr_upld_port], buff, len); SDIO2_DRV_DBG("Copy port=%d,index=%d\r\n",curr_upld_port,local_tx_buf[curr_upld_port][0]); putreg8(curr_upld_port, reg_base + SDIO2_READ_INDEX_OFFSET); putreg16(SDIO2_BYTE_PER_BUF, reg_base + SDIO2_RD_LEN_OFFSET + curr_upld_port * 2); putreg32((uint32_t)&local_tx_buf[curr_upld_port][0], reg_base + SDIO2_SQ_READ_BASE_OFFSET); putreg16(1 << curr_upld_port, reg_base + SDIO2_RD_BIT_MAP_OFFSET); #if SDU_INT_HOST putreg8(SDIO2_CCR_CS_UpLdRdy, reg_base + SDIO2_CARD_TO_HOST_EVENT_OFFSET); #endif curr_upld_port++; if(curr_upld_port == SDIO2_MAX_PORT_NUM){ curr_upld_port = 0; } return 0; } /****************************************************************************/ /** * @brief SDIO2 receive data,this function can be in user app context * * @param dev: SDIO device pointer * @param qos: qos number for this data buffer to receive * @param buff: data buffer pointer * @param len: data length * * @return BFLB_RET:0 means success and other value means error * *******************************************************************************/ int bflb_sdio2_recv_data(struct bflb_device_s *dev, int qos, uintptr_t *buff, int *len) { static uint16_t curr_dnld_port = 0; uint32_t reg_base = dev->reg_base; uint16_t wr_bit_map = getreg16(reg_base + SDIO2_WR_BIT_MAP_OFFSET); uint8_t crcerror = 0; #if 0 uint8_t card_status = 0; /* get card status */ card_status = getreg8(reg_base + SDIO2_CARD_INT_STATUS_OFFSET); #endif /* get erro */ crcerror = getreg8(reg_base + SDIO2_HOST_TRANS_STATUS_OFFSET); #if 0 if (!(card_status & SDIO2_CCR_CIC_DnLdOvr)){ SDIO2_DRV_DBG("No data come from host\r\n"); return -1; } #endif if (crcerror & SDIO2_CCR_HOST_INT_DnLdCRC_err) { SDIO2_DRV_ERR("RX CRC error\r\n"); } if (!(wr_bit_map & (1 << curr_dnld_port))) { arch_memcpy_fast(buff, local_rx_buf[curr_dnld_port], SDIO2_BYTE_PER_BUF); SDIO2_DRV_DBG("Copy port=%d,index=%d\r\n",curr_dnld_port,buff[0]); *len = SDIO2_BYTE_PER_BUF; /* attach new buffer */ putreg8(curr_dnld_port, reg_base + SDIO2_WRITE_INDEX_OFFSET); putreg32((uint32_t)&local_rx_buf[curr_dnld_port][0], reg_base + SDIO2_SQ_WRITE_BASE_OFFSET); putreg16(1 << curr_dnld_port, reg_base + SDIO2_WR_BIT_MAP_OFFSET); #if SDU_INT_HOST putreg8((SDIO2_CCR_CS_ReadCISRdy | SDIO2_CCR_CS_DnLdRdy | SDIO2_CCR_CS_IORdy), reg_base + SDIO2_CARD_TO_HOST_EVENT_OFFSET); #endif /* go to next */ curr_dnld_port++; if (curr_dnld_port == SDIO2_MAX_PORT_NUM) { curr_dnld_port = 0; } return 0; }else{ return -1; } } /****************************************************************************/ /** * @brief sdio2 irq handler * * @param irq: sdio3 interrupt type * @param arg: arg for callback * * @return None * *******************************************************************************/ void bflb_sdio2_isr(int irq, void *arg) { struct bflb_device_s *dev = (struct bflb_device_s *)arg; uint32_t reg_base = 0; uint32_t regval = 0; uint8_t crcerror = 0; reg_base = dev->reg_base; regval = getreg8(reg_base + SDIO2_CARD_INT_STATUS_OFFSET); /* clear interrupt */ putreg8(~regval | SDIO2_CCR_CIC_PwrUp, reg_base + SDIO2_CARD_INT_STATUS_OFFSET); /* get erro */ crcerror = getreg8(reg_base + SDIO2_HOST_TRANS_STATUS_OFFSET); if ((regval & SDIO2_CCR_CIC_UpLdOvr)) { SDIO2_DRV_DBG("TX interrupt come\r\n"); } if ((regval & SDIO2_CCR_CIC_DnLdOvr) && !(crcerror & SDIO2_CCR_HOST_INT_DnLdCRC_err)) { SDIO2_DRV_DBG("RX interrupt come\r\n"); } if (crcerror & SDIO2_CCR_HOST_INT_DnLdCRC_err) { SDIO2_DRV_ERR("RX CRC error\r\n"); } }