| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482 |
- /*
- * Copyright : (C) 2022 Phytium Information Technology, Inc.
- * All Rights Reserved.
- *
- * This program is OPEN SOURCE software: you can redistribute it and/or modify it
- * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd,
- * either version 1.0 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the Phytium Public License for more details.
- *
- *
- * FilePath: fsdio.c
- * Date: 2022-05-26 16:27:54
- * LastEditTime: 2022-05-26 16:27:54
- * Description: This files is for SDIO user function implementation
- *
- * Modify History:
- * Ver Who Date Changes
- * ----- ------ -------- --------------------------------------
- * 1.0 zhugengyu 2021/12/2 init
- * 1.1 zhugengyu 2022/6/6 modify according to tech manual.
- */
- /***************************** Include Files *********************************/
- #include "fio.h"
- #include "fdebug.h"
- #include "fassert.h"
- #include "ftypes.h"
- #include "fsleep.h"
- #include "fcache.h"
- #include "fsdio_hw.h"
- #include "fsdio.h"
- /************************** Constant Definitions *****************************/
- /**************************** Type Definitions *******************************/
- /***************** Macros (Inline Functions) Definitions *********************/
- #define FSDIO_DEBUG_TAG "FSDIO"
- #define FSDIO_ERROR(format, ...) FT_DEBUG_PRINT_E(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__)
- #define FSDIO_WARN(format, ...) FT_DEBUG_PRINT_W(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__)
- #define FSDIO_INFO(format, ...) FT_DEBUG_PRINT_I(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__)
- #define FSDIO_DEBUG(format, ...) FT_DEBUG_PRINT_D(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__)
- /************************** Function Prototypes ******************************/
- static FError FSdioReset(FSdio *const instance_p);
- static FError FSdioUpdateExternalClk(uintptr base_addr, u32 uhs_reg_val);
- /*****************************************************************************/
- /**
- * @name: FSdioCfgInitialize
- * @msg: initialization SDIO controller instance
- * @return {FError} FSDIO_SUCCESS if initialization success, otherwise failed
- * @param {FSdio} *instance_p, SDIO controller instance
- * @param {FSdioConfig} *input_config_p, SDIO controller configure
- * @note get into card-detect mode after initialization, bus width = 1, card freq = 400kHz
- */
- FError FSdioCfgInitialize(FSdio *const instance_p, const FSdioConfig *input_config_p)
- {
- FASSERT(instance_p && input_config_p);
- FError ret = FSDIO_SUCCESS;
- if (FT_COMPONENT_IS_READY == instance_p->is_ready)
- {
- FSDIO_WARN("device is already initialized!!!");
- }
- if (&instance_p->config != input_config_p)
- instance_p->config = *input_config_p;
- ret = FSdioReset(instance_p); /* reset the device */
- if (FSDIO_SUCCESS == ret)
- {
- instance_p->is_ready = FT_COMPONENT_IS_READY;
- FSDIO_INFO("device initialize success !!!");
- }
- return ret;
- }
- /**
- * @name: FSdioDeInitialize
- * @msg: deinitialization SDIO controller instance
- * @return {NONE}
- * @param {FSdio} *instance_p, SDIO controller instance
- */
- void FSdioDeInitialize(FSdio *const instance_p)
- {
- FASSERT(instance_p);
- uintptr base_addr = instance_p->config.base_addr;
- FSdioSetInterruptMask(instance_p, FSDIO_GENERAL_INTR, FSDIO_INT_ALL_BITS, FALSE); /* 关闭控制器中断位 */
- FSdioSetInterruptMask(instance_p, FSDIO_IDMA_INTR, FSDIO_DMAC_INT_ENA_ALL, FALSE); /* 关闭DMA中断位 */
- FSdioClearRawStatus(base_addr); /* 清除中断状态 */
- FSdioClearDMAStatus(base_addr);
- FSdioSetPower(base_addr, FALSE); /* 关闭电源 */
- FSdioSetClock(base_addr, FALSE); /* 关闭卡时钟 */
- FSDIO_CLR_BIT(base_addr, FSDIO_UHS_REG_EXT_OFFSET, FSDIO_UHS_EXT_CLK_ENA); /* 关闭外部时钟 */
- FSDIO_CLR_BIT(base_addr, FSDIO_UHS_REG_OFFSET, FSDIO_UHS_REG_VOLT_180); /* 恢复为3.3v默认电压 */
- instance_p->is_ready = 0;
- }
- /**
- * @name: FSdioSetClkFreq
- * @msg: Set the Card clock freqency
- * @return {None}
- * @param {FSdio} *instance_p, SDIO controller instance
- * @param {u32} input_clk_hz, Card clock freqency in Hz
- */
- FError FSdioSetClkFreq(FSdio *const instance_p, u32 input_clk_hz)
- {
- FASSERT(instance_p);
- uintptr base_addr = instance_p->config.base_addr;
- u32 reg_val;
- u32 div = 0xff, drv = 0, sample = 0;
- u32 first_uhs_div, tmp_ext_reg, div_reg;
- FError ret = FSDIO_SUCCESS;
- FSDIO_INFO("set clk as %ld", input_clk_hz);
- /* must set 2nd stage clcok first then set 1st stage clock */
- /* experimental uhs setting --> 2nd stage clock, below setting parameters get from
- experiment, for better sample timing */
- if (input_clk_hz >= FSDIO_SD_25_MHZ) /* e.g. 25MHz or 50MHz */
- {
- tmp_ext_reg = FSDIO_UHS_REG(0U, 0U, 0x2U) | FSDIO_UHS_EXT_CLK_ENA;
- FASSERT(tmp_ext_reg == 0x202);
- }
- else if (input_clk_hz == FSDIO_SD_400KHZ) /* 400kHz */
- {
- tmp_ext_reg = FSDIO_UHS_REG(0U, 0U, 0x5U) | FSDIO_UHS_EXT_CLK_ENA;
- FASSERT(tmp_ext_reg == 0x502);
- }
- else /* e.g. 20MHz */
- {
- tmp_ext_reg = FSDIO_UHS_REG(0U, 0U, 0x3U) | FSDIO_UHS_EXT_CLK_ENA;
- FASSERT(tmp_ext_reg == 0x302);
- }
- /* update uhs setting */
- ret = FSdioUpdateExternalClk(base_addr, tmp_ext_reg);
- if (FSDIO_SUCCESS != ret)
- return ret;
- FSdioSetClock(base_addr, FALSE); /* disable clock */
- /* send private cmd to update clock */
- ret = FSdioSendPrivateCmd(base_addr, FSDIO_CMD_UPD_CLK, 0U);
- if (FSDIO_SUCCESS != ret)
- return ret;
- /* experimental clk divide setting -- 1st stage clock */
- first_uhs_div = 1 + FSDIO_UHS_CLK_DIV_GET(tmp_ext_reg);
- div = FSDIO_CLK_RATE_HZ / (2 * first_uhs_div * input_clk_hz);
- if (div > 2)
- {
- sample = div / 2 + 1;
- drv = sample - 1;
- }
- else if (div == 2)
- {
- drv = 0;
- sample = 1;
- }
- div_reg = FSDIO_CLK_DIV(sample, drv, div);
- FSDIO_WRITE_REG(base_addr, FSDIO_CLKDIV_OFFSET, div_reg);
- FSDIO_INFO("UHS_REG_EXT: %x, CLKDIV: %x",
- FSDIO_READ_REG(base_addr, FSDIO_UHS_REG_EXT_OFFSET),
- FSDIO_READ_REG(base_addr, FSDIO_CLKDIV_OFFSET));
- FSDIO_INFO("UHS_REG_EXT ext: 0x%x, CLKDIV: 0x%x",
- FSDIO_READ_REG(base_addr, FSDIO_UHS_REG_EXT_OFFSET),
- FSDIO_READ_REG(base_addr, FSDIO_CLKDIV_OFFSET));
- FSdioSetClock(base_addr, TRUE); /* enable clock */
- /* update clock for 1 stage clock */
- ret = FSdioSendPrivateCmd(base_addr, FSDIO_CMD_UPD_CLK, 0U);
- if (FSDIO_SUCCESS != ret)
- return ret;
- return ret;
- }
- /**
- * @name: FSdioWaitClkReady
- * @msg: Wait clock ready after modify clock setting
- * @return {FError} FSDIO_SUCCESS if wait success, FSDIO_ERR_TIMEOUT if wait timeout
- * @param {uintptr} base_addr, base address of SDIO controller
- * @param {int} retries, retry times in waiting
- */
- static FError FSdioWaitClkReady(uintptr base_addr, int retries)
- {
- FASSERT(retries > 1);
- u32 reg_val = 0;
- do
- {
- reg_val = FSDIO_READ_REG(base_addr, FSDIO_GPIO_OFFSET);
- }
- while (!(reg_val & FSDIO_CLK_READY) && (retries-- > 0));
- if (!(reg_val & FSDIO_CLK_READY) && (retries <= 0))
- {
- FSDIO_ERROR("wait clk ready timeout !!! status: 0x%x",
- reg_val);
- return FSDIO_ERR_TIMEOUT;
- }
- return FSDIO_SUCCESS;
- }
- /**
- * @name: FSdioUpdateExternalClk
- * @msg: update uhs clock value and wait clock ready
- * @return {FError}
- * @param {uintptr} base_addr
- * @param {u32} uhs_reg_val
- */
- static FError FSdioUpdateExternalClk(uintptr base_addr, u32 uhs_reg_val)
- {
- u32 reg_val;
- int retries = FSDIO_TIMEOUT;
- FSDIO_WRITE_REG(base_addr, FSDIO_UHS_REG_EXT_OFFSET, 0U);
- FSDIO_WRITE_REG(base_addr, FSDIO_UHS_REG_EXT_OFFSET, uhs_reg_val);
- do
- {
- reg_val = FSDIO_READ_REG(base_addr, FSDIO_GPIO_OFFSET);
- if (--retries <= 0)
- break;
- }
- while (!(reg_val & FSDIO_CLK_READY));
- return (retries <= 0) ? FSDIO_ERR_TIMEOUT : FSDIO_SUCCESS;
- }
- /**
- * @name: FSdioResetCtrl
- * @msg: Reset fifo/DMA in cntrl register
- * @return {FError} FSDIO_SUCCESS if reset success
- * @param {uintptr} base_addr, base address of SDIO controller
- * @param {u32} reset_bits, bits to be reset
- */
- FError FSdioResetCtrl(uintptr base_addr, u32 reset_bits)
- {
- u32 reg_val;
- int retries = FSDIO_TIMEOUT;
- FSDIO_SET_BIT(base_addr, FSDIO_CNTRL_OFFSET, reset_bits);
- do
- {
- reg_val = FSDIO_READ_REG(base_addr, FSDIO_CNTRL_OFFSET);
- if (--retries <= 0)
- break;
- }
- while (reset_bits & reg_val);
- if (retries <= 0)
- return FSDIO_ERR_TIMEOUT;
- return FSDIO_SUCCESS;
- }
- /**
- * @name: FSdioResetBusyCard
- * @msg: reset controller from card busy state
- * @return {FError} FSDIO_SUCCESS if reset success
- * @param {uintptr} base_addr, base address of controller
- */
- FError FSdioResetBusyCard(uintptr base_addr)
- {
- u32 reg_val;
- int retries = FSDIO_TIMEOUT;
- FSDIO_SET_BIT(base_addr, FSDIO_CNTRL_OFFSET, FSDIO_CNTRL_CONTROLLER_RESET);
- do
- {
- FSDIO_SET_BIT(base_addr, FSDIO_CNTRL_OFFSET, FSDIO_CNTRL_CONTROLLER_RESET);
- reg_val = FSDIO_READ_REG(base_addr, FSDIO_STATUS_OFFSET);
- if (--retries <= 0)
- break;
- }
- while (reg_val & FSDIO_STATUS_DATA_BUSY);
- return (retries <= 0) ? FSDIO_ERR_BUSY : FSDIO_SUCCESS;
- }
- /**
- * @name: FSdioRestartClk
- * @msg: restart controller clock from error status
- * @return {FError} FSDIO_SUCCESS if reset success
- * @param {uintptr} base_addr, base address of controller
- */
- FError FSdioRestartClk(uintptr base_addr)
- {
- u32 clk_div, uhs;
- int retries = FSDIO_TIMEOUT;
- u32 reg_val;
- FError ret = FSDIO_SUCCESS;
- /* wait command finish if previous command is in error state */
- do
- {
- reg_val = FSDIO_READ_REG(base_addr, FSDIO_CMD_OFFSET);
- if (--retries <= 0)
- break;
- }
- while (reg_val & FSDIO_CMD_START);
- if (retries <= 0)
- return FSDIO_ERR_TIMEOUT;
- /* update clock */
- FSdioSetClock(base_addr, FALSE);
- clk_div = FSDIO_READ_REG(base_addr, FSDIO_CLKDIV_OFFSET);
- uhs = FSDIO_READ_REG(base_addr, FSDIO_UHS_REG_EXT_OFFSET);
- ret = FSdioUpdateExternalClk(base_addr, uhs);
- if (FSDIO_SUCCESS != ret)
- return ret;
- FSDIO_WRITE_REG(base_addr, FSDIO_CLKDIV_OFFSET, clk_div);
- FSdioSetClock(base_addr, TRUE);
- ret = FSdioSendPrivateCmd(base_addr, FSDIO_CMD_UPD_CLK, 0U);
- return ret;
- }
- /**
- * @name: FSdioReset
- * @msg: Reset SDIO controller instance
- * @return {FError} FSDIO_SUCCESS if reset success
- * @param {FSdio} *instance_p, SDIO controller instance
- */
- static FError FSdioReset(FSdio *const instance_p)
- {
- FASSERT(instance_p);
- uintptr base_addr = instance_p->config.base_addr;
- u32 reg_val;
- FError ret = FSDIO_SUCCESS;
- /* set creg_nand_mmcsd DMA path */
- FSDIO_INFO("Prev LSD CFG: 0x%x", FtIn32(FLSD_CONFIG_BASE + FLSD_NAND_MMCSD_HADDR));
- FtOut32(FLSD_CONFIG_BASE + FLSD_NAND_MMCSD_HADDR, 0x0U);
- FSDIO_INFO("Curr LSD CFG: 0x%x", FtIn32(FLSD_CONFIG_BASE + FLSD_NAND_MMCSD_HADDR));
- /* set fifo */
- reg_val = FSDIO_FIFOTH(FSDIO_FIFOTH_DMA_TRANS_8, FSDIO_RX_WMARK, FSDIO_TX_WMARK);
- FSDIO_WRITE_REG(base_addr, FSDIO_FIFOTH_OFFSET, reg_val);
- /* set card threshold */
- reg_val = FSDIO_CARD_THRCTL_THRESHOLD(FSDIO_FIFO_DEPTH_8) | FSDIO_CARD_THRCTL_CARDRD;
- FSDIO_WRITE_REG(base_addr, FSDIO_CARD_THRCTL_OFFSET, reg_val);
- /* disable clock and update ext clk */
- FSdioSetClock(base_addr, FALSE);
- /* set 1st clock */
- reg_val = FSDIO_UHS_REG(0U, 0U, 0x5U) | FSDIO_UHS_EXT_CLK_ENA;
- FASSERT_MSG(0x502 == reg_val, "invalid uhs config");
- ret = FSdioUpdateExternalClk(base_addr, reg_val);
- if (FSDIO_SUCCESS != ret)
- {
- FSDIO_ERROR("update extern clock failed !!!");
- return ret;
- }
- /* power on */
- FSdioSetPower(base_addr, TRUE);
- FSdioSetClock(base_addr, TRUE);
- FSdioSetExtClock(base_addr, TRUE);
- /* set voltage as 3.3v */
- if (FSDIO_SD_1_8V_VOLTAGE == instance_p->config.voltage)
- FSdioSetVoltage1_8V(base_addr, TRUE);
- else
- FSdioSetVoltage1_8V(base_addr, FALSE);
- /* reset controller and card */
- ret = FSdioResetCtrl(base_addr, FSDIO_CNTRL_FIFO_RESET | FSDIO_CNTRL_DMA_RESET);
- if (FSDIO_SUCCESS != ret)
- {
- FSDIO_ERROR("reset controller failed !!!");
- return ret;
- }
- /* send private command to update clock */
- ret = FSdioSendPrivateCmd(base_addr, FSDIO_CMD_UPD_CLK, 0U);
- if (FSDIO_SUCCESS != ret)
- {
- FSDIO_ERROR("update clock failed !!!");
- return ret;
- }
- /* reset card for no-removeable media, e.g. eMMC */
- if (TRUE == instance_p->config.non_removable)
- FSDIO_SET_BIT(base_addr, FSDIO_CARD_RESET_OFFSET, FSDIO_CARD_RESET_ENABLE);
- else
- FSDIO_CLR_BIT(base_addr, FSDIO_CARD_RESET_OFFSET, FSDIO_CARD_RESET_ENABLE);
- /* clear interrupt status */
- FSDIO_WRITE_REG(base_addr, FSDIO_INT_MASK_OFFSET, 0U);
- reg_val = FSDIO_READ_REG(base_addr, FSDIO_RAW_INTS_OFFSET);
- FSDIO_WRITE_REG(base_addr, FSDIO_RAW_INTS_OFFSET, reg_val);
- FSDIO_WRITE_REG(base_addr, FSDIO_DMAC_INT_EN_OFFSET, 0U);
- reg_val = FSDIO_READ_REG(base_addr, FSDIO_DMAC_STATUS_OFFSET);
- FSDIO_WRITE_REG(base_addr, FSDIO_DMAC_STATUS_OFFSET, reg_val);
- /* enable card detect interrupt */
- if (FALSE == instance_p->config.non_removable)
- FSDIO_SET_BIT(base_addr, FSDIO_INT_MASK_OFFSET, FSDIO_INT_CD_BIT);
- /* enable controller and internal DMA */
- FSDIO_SET_BIT(base_addr, FSDIO_CNTRL_OFFSET, FSDIO_CNTRL_INT_ENABLE | FSDIO_CNTRL_USE_INTERNAL_DMAC);
- /* set data and resp timeout */
- FSDIO_WRITE_REG(base_addr, FSDIO_TMOUT_OFFSET,
- FSDIO_TIMEOUT_DATA(FSDIO_MAX_DATA_TIMEOUT, FSDIO_MAX_RESP_TIMEOUT));
- /* reset descriptors and dma */
- FSdioSetDescriptor(base_addr, (uintptr)NULL); /* set decriptor list as NULL */
- FSdioResetIDMA(base_addr);
- FSDIO_INFO("init hardware done !!!");
- return ret;
- }
- /**
- * @name: FSdioRestart
- * @msg: reset controller from error state
- * @return {FError} FSDIO_SUCCESS if restart success
- * @param {FSdio} *instance_p, instance of controller
- */
- FError FSdioRestart(FSdio *const instance_p)
- {
- FASSERT(instance_p);
- uintptr base_addr = instance_p->config.base_addr;
- u32 reg_val;
- FError ret = FSDIO_SUCCESS;
- if (FT_COMPONENT_IS_READY != instance_p->is_ready)
- {
- FSDIO_ERROR("device is not yet initialized!!!");
- return FSDIO_ERR_NOT_INIT;
- }
- /* reset controller */
- ret = FSdioResetCtrl(base_addr, FSDIO_CNTRL_FIFO_RESET);
- if (FSDIO_SUCCESS != ret)
- return ret;
- /* reset controller if in busy state */
- ret = FSdioResetBusyCard(base_addr);
- if (FSDIO_SUCCESS != ret)
- return ret;
- /* reset clock */
- ret = FSdioRestartClk(base_addr);
- if (FSDIO_SUCCESS != ret)
- return ret;
- /* reset internal DMA */
- FSdioResetIDMA(base_addr);
- return ret;
- }
|