diff --git a/bsp/apm32/libraries/Drivers/drv_sdio.h b/bsp/apm32/libraries/Drivers/drv_sdio.h index ee1d0438c96..4784dffd5a1 100644 --- a/bsp/apm32/libraries/Drivers/drv_sdio.h +++ b/bsp/apm32/libraries/Drivers/drv_sdio.h @@ -27,10 +27,6 @@ #define SDIO_BUFF_SIZE 4096 #define SDIO_ALIGN_LEN 32 -#ifndef SDIO_MAX_FREQ -#define SDIO_MAX_FREQ (1000000) -#endif - #ifndef SDIO_BASE_ADDRESS #define SDIO_BASE_ADDRESS (0x40012800U) #endif diff --git a/bsp/at32/libraries/rt_drivers/drv_sdio.h b/bsp/at32/libraries/rt_drivers/drv_sdio.h index 9a2b7d1c30a..bfcd76bd487 100644 --- a/bsp/at32/libraries/rt_drivers/drv_sdio.h +++ b/bsp/at32/libraries/rt_drivers/drv_sdio.h @@ -25,10 +25,6 @@ #define SDIO_BUFF_SIZE 4096 #define SDIO_ALIGN_LEN 32 -#ifndef SDIO_MAX_FREQ -#define SDIO_MAX_FREQ (1000000) -#endif - #ifndef SDIO_BASE_ADDRESS #define SDIO_BASE_ADDRESS SDIO1_BASE #endif diff --git a/bsp/bluetrum/libraries/hal_drivers/drv_sdio.h b/bsp/bluetrum/libraries/hal_drivers/drv_sdio.h index 76818293062..31a43a33aca 100644 --- a/bsp/bluetrum/libraries/hal_drivers/drv_sdio.h +++ b/bsp/bluetrum/libraries/hal_drivers/drv_sdio.h @@ -19,10 +19,6 @@ #define SDIO_BUFF_SIZE 1024 #define SDIO_ALIGN_LEN 32 -#ifndef SDIO_MAX_FREQ -#define SDIO_MAX_FREQ (1000000) -#endif - #ifndef SDIO_BASE_ADDRESS #define SDIO_BASE_ADDRESS (0x40012800U) #endif diff --git a/bsp/hc32/ev_hc32f460_lqfp100_v2/board/board_config.c b/bsp/hc32/ev_hc32f460_lqfp100_v2/board/board_config.c index 9762947184a..12940d80097 100644 --- a/bsp/hc32/ev_hc32f460_lqfp100_v2/board/board_config.c +++ b/bsp/hc32/ev_hc32f460_lqfp100_v2/board/board_config.c @@ -111,7 +111,7 @@ rt_err_t rt_hw_board_can_init(CM_CAN_TypeDef *CANx) switch ((rt_uint32_t)CANx) { #if defined(BSP_USING_CAN1) - case (rt_uint32_t)CM_CAN1: + case (rt_uint32_t)CM_CAN: GPIO_SetFunc(CAN1_TX_PORT, CAN1_TX_PIN, CAN1_TX_PIN_FUNC); GPIO_SetFunc(CAN1_RX_PORT, CAN1_RX_PIN, CAN1_RX_PIN_FUNC); break; diff --git a/bsp/hc32/libraries/hc32_drivers/drv_can.c b/bsp/hc32/libraries/hc32_drivers/drv_can.c index 2274262aa8d..61106abba7b 100644 --- a/bsp/hc32/libraries/hc32_drivers/drv_can.c +++ b/bsp/hc32/libraries/hc32_drivers/drv_can.c @@ -325,6 +325,7 @@ static uint8_t _get_can_data_bytes_len(uint32_t dlc) { data_bytes = dlc; } +#ifdef RT_CAN_USING_CANFD else { switch (dlc) @@ -355,6 +356,7 @@ static uint8_t _get_can_data_bytes_len(uint32_t dlc) break; } } +#endif return data_bytes; } diff --git a/bsp/nxp/imx/imxrt/libraries/drivers/drv_eth.c b/bsp/nxp/imx/imxrt/libraries/drivers/drv_eth.c index e240b5b969a..5ab9fcf6f61 100644 --- a/bsp/nxp/imx/imxrt/libraries/drivers/drv_eth.c +++ b/bsp/nxp/imx/imxrt/libraries/drivers/drv_eth.c @@ -151,7 +151,7 @@ void _enet_rx_callback(struct rt_imxrt_eth *eth) void _enet_tx_callback(struct rt_imxrt_eth *eth) { - dbg_log(DBG_LOG, "_enet_tx_callback\n"); + LOG_D("_enet_tx_callback\n"); if (eth->tx_is_waiting == RT_TRUE) { eth->tx_is_waiting = RT_FALSE; @@ -181,23 +181,23 @@ static void _enet_callback(ENET_Type *base, break; case kENET_ErrEvent: - dbg_log(DBG_LOG, "kENET_ErrEvent\n"); + LOG_D("kENET_ErrEvent\n"); break; case kENET_WakeUpEvent: - dbg_log(DBG_LOG, "kENET_WakeUpEvent\n"); + LOG_D("kENET_WakeUpEvent\n"); break; case kENET_TimeStampEvent: - dbg_log(DBG_LOG, "kENET_TimeStampEvent\n"); + LOG_D("kENET_TimeStampEvent\n"); break; case kENET_TimeStampAvailEvent: - dbg_log(DBG_LOG, "kENET_TimeStampAvailEvent \n"); + LOG_D("kENET_TimeStampAvailEvent \n"); break; default: - dbg_log(DBG_LOG, "unknow error\n"); + LOG_D("unknow error\n"); break; } } @@ -255,7 +255,7 @@ static void *_enet_rx_alloc(ENET_Type *base, void *userData, uint8_t ringId) void *buffer = NULL; int i; - // dbg_log(DBG_LOG, "get buff_wait sem in %d\r\n", __LINE__); + // LOG_D("get buff_wait sem in %d\r\n", __LINE__); rt_sem_take(&imxrt_eth_device.buff_wait, RT_WAITING_FOREVER); for (i = 0; i < ENET_RXBUFF_NUM; i++) @@ -269,7 +269,7 @@ static void *_enet_rx_alloc(ENET_Type *base, void *userData, uint8_t ringId) } rt_sem_release(&imxrt_eth_device.buff_wait); - // dbg_log(DBG_LOG, "release buff_wait sem in %d\r\n", __LINE__); + // LOG_D("release buff_wait sem in %d\r\n", __LINE__); return buffer; } @@ -282,7 +282,7 @@ static void _enet_rx_free(ENET_Type *base, void *buffer, void *userData, uint8_t LOG_E("Freed buffer out of range\r\n"); } - // dbg_log(DBG_LOG, "get buff_wait sem in %d\r\n", __LINE__); + // LOG_D("get buff_wait sem in %d\r\n", __LINE__); rt_sem_take(&imxrt_eth_device.buff_wait, RT_WAITING_FOREVER); if (!(imxrt_eth_device.RxPbufs[idx].buffer_used)) { @@ -290,7 +290,7 @@ static void _enet_rx_free(ENET_Type *base, void *buffer, void *userData, uint8_t } imxrt_eth_device.RxPbufs[idx].buffer_used = false; rt_sem_release(&imxrt_eth_device.buff_wait); - // dbg_log(DBG_LOG, "release buff_wait sem in %d\r\n", __LINE__); + // LOG_D("release buff_wait sem in %d\r\n", __LINE__); } /** @@ -356,13 +356,13 @@ static void _enet_config(void) // /* Set SMI to get PHY link status. */ // sysClock = CLOCK_GetFreq(kCLOCK_IpgClk); // -// dbg_log(DBG_LOG, "deinit\n"); +// LOG_D("deinit\n"); // ENET_Deinit(imxrt_eth_device.enet_base); -// dbg_log(DBG_LOG, "init\n"); +// LOG_D("init\n"); // ENET_Init(imxrt_eth_device.enet_base, &imxrt_eth_device.enet_handle, &config, &buffConfig[0], &imxrt_eth_device.dev_addr[0], sysClock); -//// dbg_log(DBG_LOG, "set call back\n"); +//// LOG_D("set call back\n"); //// ENET_SetCallback(&imxrt_eth_device.enet_handle, _enet_callback, &imxrt_eth_device); -// dbg_log(DBG_LOG, "active read\n"); +// LOG_D("active read\n"); // ENET_ActiveRead(imxrt_eth_device.enet_base); //#else int i; @@ -423,13 +423,13 @@ static void _enet_config(void) imxrt_eth_device.RxPbufs[i].buffer_used = false; } - // dbg_log(DBG_LOG, "deinit\n"); + // LOG_D("deinit\n"); // ENET_Deinit(imxrt_eth_device.enet_base); - dbg_log(DBG_LOG, "init\n"); + LOG_D("init\n"); ENET_Init(imxrt_eth_device.enet_base, &imxrt_eth_device.enet_handle, &config, &buffConfig[0], &imxrt_eth_device.dev_addr[0], sysClock); - // dbg_log(DBG_LOG, "set call back\n"); + // LOG_D("set call back\n"); // ENET_SetCallback(&imxrt_eth_device.enet_handle, _enet_callback, &imxrt_eth_device); - dbg_log(DBG_LOG, "active read\n"); + LOG_D("active read\n"); ENET_ActiveRead(imxrt_eth_device.enet_base); //#endif } @@ -474,7 +474,7 @@ static void packet_dump(const char *msg, const struct pbuf *p) /* initialize the interface */ static rt_err_t rt_imxrt_eth_init(rt_device_t dev) { - dbg_log(DBG_LOG, "rt_imxrt_eth_init...\n"); + LOG_D("rt_imxrt_eth_init...\n"); _enet_config(); return RT_EOK; @@ -482,33 +482,33 @@ static rt_err_t rt_imxrt_eth_init(rt_device_t dev) static rt_err_t rt_imxrt_eth_open(rt_device_t dev, rt_uint16_t oflag) { - dbg_log(DBG_LOG, "rt_imxrt_eth_open...\n"); + LOG_D("rt_imxrt_eth_open...\n"); return RT_EOK; } static rt_err_t rt_imxrt_eth_close(rt_device_t dev) { - dbg_log(DBG_LOG, "rt_imxrt_eth_close...\n"); + LOG_D("rt_imxrt_eth_close...\n"); return RT_EOK; } static rt_ssize_t rt_imxrt_eth_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) { - dbg_log(DBG_LOG, "rt_imxrt_eth_read...\n"); + LOG_D("rt_imxrt_eth_read...\n"); rt_set_errno(-RT_ENOSYS); return 0; } static rt_ssize_t rt_imxrt_eth_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) { - dbg_log(DBG_LOG, "rt_imxrt_eth_write...\n"); + LOG_D("rt_imxrt_eth_write...\n"); rt_set_errno(-RT_ENOSYS); return 0; } static rt_err_t rt_imxrt_eth_control(rt_device_t dev, int cmd, void *args) { - dbg_log(DBG_LOG, "rt_imxrt_eth_control...\n"); + LOG_D("rt_imxrt_eth_control...\n"); switch (cmd) { case NIOCTL_GADDR: @@ -807,7 +807,7 @@ rt_err_t rt_imxrt_eth_tx(rt_device_t dev, struct pbuf *p) RT_ASSERT(p != NULL); RT_ASSERT(enet_handle != RT_NULL); - dbg_log(DBG_LOG, "rt_imxrt_eth_tx: %d\n", p->len); + LOG_D("rt_imxrt_eth_tx: %d\n", p->len); #ifdef ETH_TX_DUMP packet_dump("send", p); @@ -861,18 +861,18 @@ struct pbuf *rt_imxrt_eth_rx(rt_device_t dev) } else { - dbg_log(DBG_LOG, " A frame read failed\n"); + LOG_D(" A frame read failed\n"); pbuf_free(p); } } else { - dbg_log(DBG_LOG, " pbuf_alloc faild\n"); + LOG_D(" pbuf_alloc faild\n"); } } else if (status == kStatus_ENET_RxFrameError) { - dbg_log(DBG_WARNING, "ENET_GetRxFrameSize: kStatus_ENET_RxFrameError\n"); + LOG_W("ENET_GetRxFrameSize: kStatus_ENET_RxFrameError\n"); /* Update the received buffer when error happened. */ /* Get the error information of the received g_frame. */ @@ -956,24 +956,24 @@ static void phy_monitor_thread_entry(void *parameter) if (PHY_SPEED_10M == speed) { - dbg_log(DBG_LOG, "10M\n"); + LOG_D("10M\n"); } else if (PHY_SPEED_100M == speed) { - dbg_log(DBG_LOG, "100M\n"); + LOG_D("100M\n"); } else { - dbg_log(DBG_LOG, "1000M\n"); + LOG_D("1000M\n"); } if (PHY_HALF_DUPLEX == duplex) { - dbg_log(DBG_LOG, "half dumplex\n"); + LOG_D("half dumplex\n"); } else { - dbg_log(DBG_LOG, "full dumplex\n"); + LOG_D("full dumplex\n"); } if ((imxrt_eth_device.speed != (enet_mii_speed_t)speed) || (imxrt_eth_device.duplex != (enet_mii_duplex_t)duplex)) @@ -981,19 +981,19 @@ static void phy_monitor_thread_entry(void *parameter) imxrt_eth_device.speed = (enet_mii_speed_t)speed; imxrt_eth_device.duplex = (enet_mii_duplex_t)duplex; - dbg_log(DBG_LOG, "link up, and update eth mode.\n"); + LOG_D("link up, and update eth mode.\n"); rt_imxrt_eth_init((rt_device_t)&imxrt_eth_device); } else { - dbg_log(DBG_LOG, "link up, eth not need re-config.\n"); + LOG_D("link up, eth not need re-config.\n"); } - dbg_log(DBG_LOG, "link up.\n"); + LOG_D("link up.\n"); eth_device_linkchange(&imxrt_eth_device.parent, RT_TRUE); } else { - dbg_log(DBG_LOG, "link down.\n"); + LOG_D("link down.\n"); eth_device_linkchange(&imxrt_eth_device.parent, RT_FALSE); } } @@ -1050,24 +1050,24 @@ static int rt_hw_imxrt_eth_init(void) imxrt_eth_device.parent.eth_rx = rt_imxrt_eth_rx; imxrt_eth_device.parent.eth_tx = rt_imxrt_eth_tx; - dbg_log(DBG_LOG, "sem init: tx_wait\r\n"); + LOG_D("sem init: tx_wait\r\n"); /* init tx semaphore */ rt_sem_init(&imxrt_eth_device.tx_wait, "tx_wait", 0, RT_IPC_FLAG_FIFO); - dbg_log(DBG_LOG, "sem init: buff_wait\r\n"); + LOG_D("sem init: buff_wait\r\n"); /* init tx semaphore */ rt_sem_init(&imxrt_eth_device.buff_wait, "buff_wait", 1, RT_IPC_FLAG_FIFO); /* register eth device */ - dbg_log(DBG_LOG, "eth_device_init start\r\n"); + LOG_D("eth_device_init start\r\n"); state = eth_device_init(&(imxrt_eth_device.parent), "e0"); if (RT_EOK == state) { - dbg_log(DBG_LOG, "eth_device_init success\r\n"); + LOG_D("eth_device_init success\r\n"); } else { - dbg_log(DBG_LOG, "eth_device_init faild: %d\r\n", state); + LOG_D("eth_device_init faild: %d\r\n", state); } eth_device_linkchange(&imxrt_eth_device.parent, RT_FALSE); diff --git a/bsp/stm32/libraries/HAL_Drivers/drivers/drv_sdio.h b/bsp/stm32/libraries/HAL_Drivers/drivers/drv_sdio.h index ebd0ff8ca20..2da9026b969 100644 --- a/bsp/stm32/libraries/HAL_Drivers/drivers/drv_sdio.h +++ b/bsp/stm32/libraries/HAL_Drivers/drivers/drv_sdio.h @@ -35,10 +35,6 @@ #define SDIO_BUFF_SIZE 4096 #define SDIO_ALIGN_LEN 32 -#ifndef SDIO_MAX_FREQ -#define SDIO_MAX_FREQ (1000000) -#endif - #ifndef SDIO_BASE_ADDRESS #define SDIO_BASE_ADDRESS (0x40012800U) #endif diff --git a/bsp/stm32/stm32l475-atk-pandora/board/linker_scripts/link.lds b/bsp/stm32/stm32l475-atk-pandora/board/linker_scripts/link.lds index 2cebc017797..197311fa17e 100644 --- a/bsp/stm32/stm32l475-atk-pandora/board/linker_scripts/link.lds +++ b/bsp/stm32/stm32l475-atk-pandora/board/linker_scripts/link.lds @@ -40,6 +40,12 @@ SECTIONS KEEP(*(VSymTab)) __vsymtab_end = .; + /* section information for utest */ + . = ALIGN(4); + __rt_utest_tc_tab_start = .; + KEEP(*(UtestTcTab)) + __rt_utest_tc_tab_end = .; + /* section information for initial. */ . = ALIGN(4); __rt_init_start = .; diff --git a/components/drivers/rtc/dev_soft_rtc.c b/components/drivers/rtc/dev_soft_rtc.c index abe35633b96..cd5a37ee027 100644 --- a/components/drivers/rtc/dev_soft_rtc.c +++ b/components/drivers/rtc/dev_soft_rtc.c @@ -35,7 +35,6 @@ #endif static struct rt_work rtc_sync_work; -static rt_device_t source_device = RT_NULL; static struct rt_device soft_rtc_dev; static rt_tick_t init_tick; @@ -82,18 +81,6 @@ static void set_rtc_time(time_t t) #endif } -static void _source_device_control(int cmd, void *args) -{ - if (source_device == RT_NULL) - return; - - if (rt_device_open(source_device, 0) == RT_EOK) - { - rt_device_control(source_device, cmd, args); - rt_device_close(source_device); - } -} - static rt_err_t soft_rtc_control(rt_device_t dev, int cmd, void *args) { time_t *t; @@ -114,7 +101,6 @@ static rt_err_t soft_rtc_control(rt_device_t dev, int cmd, void *args) { t = (time_t *) args; set_rtc_time(*t); - _source_device_control(RT_DEVICE_CTRL_RTC_SET_TIME, t); break; } #ifdef RT_USING_ALARM @@ -143,7 +129,6 @@ static rt_err_t soft_rtc_control(rt_device_t dev, int cmd, void *args) rt_ktime_boottime_get_us(&_tv); set_rtc_time(tv->tv_sec); init_tv.tv_usec = tv->tv_usec - _tv.tv_usec; - _source_device_control(RT_DEVICE_CTRL_RTC_SET_TIME, &(tv->tv_sec)); break; } case RT_DEVICE_CTRL_RTC_GET_TIMESPEC: @@ -162,7 +147,6 @@ static rt_err_t soft_rtc_control(rt_device_t dev, int cmd, void *args) rt_ktime_boottime_get_ns(&_ts); set_rtc_time(ts->tv_sec); init_ts.tv_nsec = ts->tv_nsec - _ts.tv_nsec; - _source_device_control(RT_DEVICE_CTRL_RTC_SET_TIME, &(ts->tv_sec)); break; } case RT_DEVICE_CTRL_RTC_GET_TIMERES: @@ -187,7 +171,6 @@ static rt_err_t soft_rtc_control(rt_device_t dev, int cmd, void *args) rt_tick_t tick = rt_tick_get() - init_tick; set_rtc_time(tv->tv_sec); init_tv.tv_usec = tv->tv_usec - ((tick % RT_TICK_PER_SECOND) * (1000000 / RT_TICK_PER_SECOND)); - _source_device_control(RT_DEVICE_CTRL_RTC_SET_TIME, &(tv->tv_sec)); break; } case RT_DEVICE_CTRL_RTC_GET_TIMERES: @@ -227,7 +210,7 @@ static int rt_soft_rtc_init(void) return 0; } /* make sure only one 'rtc' device */ -#if defined(RT_USING_SOFT_RTC) && defined(RT_USING_RTC) +#if defined(RT_USING_SOFT_RTC) && defined(BSP_USING_ONCHIP_RTC) #warning "Please note: Currently only one RTC device is allowed in the system, and the name is "rtc"." #endif RT_ASSERT(!rt_device_find("rtc")); @@ -263,8 +246,6 @@ static int rt_soft_rtc_init(void) rt_device_register(&soft_rtc_dev, "rtc", RT_DEVICE_FLAG_RDWR); - source_device = &soft_rtc_dev; - init_ok = RT_TRUE; return 0; @@ -277,13 +258,7 @@ rt_err_t rt_soft_rtc_sync(void) { time_t time = 0; - if (source_device == RT_NULL) - { - rt_kprintf("error: rtc source not found, please set it!!!\n"); - return RT_ENOSYS; - } - - _source_device_control(RT_DEVICE_CTRL_RTC_GET_TIME, &time); + rt_device_control(&soft_rtc_dev, RT_DEVICE_CTRL_RTC_GET_TIME, &time); set_rtc_time(time); return RT_EOK; } @@ -299,7 +274,6 @@ rt_err_t rt_soft_rtc_set_source(const char *name) RT_ASSERT(name != RT_NULL); RT_ASSERT(rt_device_find(name)); /* make sure source is exist*/ - source_device = rt_device_find(name); rt_work_init(&rtc_sync_work, rtc_sync_work_func, RT_NULL); rt_work_submit(&rtc_sync_work, rt_tick_from_millisecond(RTC_AUTO_SYNC_FIRST_DELAY * 1000)); diff --git a/components/drivers/sdio/Kconfig b/components/drivers/sdio/Kconfig index e318104932a..dd1fe0a593b 100644 --- a/components/drivers/sdio/Kconfig +++ b/components/drivers/sdio/Kconfig @@ -25,5 +25,8 @@ config RT_USING_SDIO default 16 config RT_SDIO_DEBUG bool "Enable SDIO debug log output" - default n - endif + default n + config RT_USING_SDHCI + bool "Using sdhci for sd/mmc drivers" + default n + endif diff --git a/components/drivers/sdio/SConscript b/components/drivers/sdio/SConscript index 04061a9e02a..98d71d1394f 100644 --- a/components/drivers/sdio/SConscript +++ b/components/drivers/sdio/SConscript @@ -11,7 +11,12 @@ dev_mmc.c """) # The set of source files associated with this SConscript file. -path = [cwd + '/../include'] +path = [cwd + '/../include' , cwd + '/sdhci/include'] + +if GetDepend('RT_USING_SDHCI'): + src += [os.path.join('sdhci', 'sdhci.c')] + src += [os.path.join('sdhci', 'fit-mmc.c')] + src += [os.path.join('sdhci', 'sdhci-platform.c')] group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_SDIO'], CPPPATH = path) diff --git a/components/drivers/sdio/sdhci/fit-mmc.c b/components/drivers/sdio/sdhci/fit-mmc.c new file mode 100644 index 00000000000..a3fe063194d --- /dev/null +++ b/components/drivers/sdio/sdhci/fit-mmc.c @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-08-16 zhujiale first version + */ +#include +#include "sdhci.h" +#include +#include +#include + + +static void rt_plat_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req) +{ + struct rt_mmc_host *mmc = (struct rt_mmc_host *)host; + rt_uint32_t flags = req->cmd->flags; + + switch (flags & RESP_MASK) + { + case RESP_NONE: + flags |= MMC_RSP_NONE; + break; + case RESP_R1: + flags |= MMC_RSP_R1; + break; + case RESP_R1B: + flags |= MMC_RSP_R1B; + break; + case RESP_R2: + flags |= MMC_RSP_R2; + break; + case RESP_R3: + flags |= MMC_RSP_R3; + break; + case RESP_R4: + flags |= MMC_RSP_R4; + break; + case RESP_R5: + flags |= MMC_RSP_R5; + break; + case RESP_R6: + flags |= MMC_RSP_R6; + break; + case RESP_R7: + flags |= MMC_RSP_R7; + break; + } + if (req->data) + { + if ((rt_uint64_t)rt_kmem_v2p(req->data->buf) > 0xffffffff) + { + void *dma_buffer = rt_malloc(req->data->blks * req->data->blksize); + void *req_buf = NULL; + + if (req->data->flags & DATA_DIR_WRITE) + { + rt_memcpy(dma_buffer, req->data->buf, req->data->blks * req->data->blksize); + req_buf = req->data->buf; + req->data->buf = dma_buffer; + } + else if (req->data->flags & DATA_DIR_READ) + { + req_buf = req->data->buf; + req->data->buf = dma_buffer; + } + req->cmd->flags |= flags; + mmc->ops->request(mmc, req); + + rt_sem_take(&host->sem_ack, RT_WAITING_FOREVER); + + if (req->data->flags & DATA_DIR_READ) + { + rt_memcpy(req_buf, dma_buffer, req->data->blksize * req->data->blks); + req->data->buf = req_buf; + }else{ + req->data->buf = req_buf; + } + + rt_free(dma_buffer); + rt_sem_release(&host->sem_ack); + } + else + { + req->cmd->flags |= flags; + mmc->ops->request(mmc, req); + } + } + else + { + req->cmd->flags |= flags; + mmc->ops->request(mmc, req); + } +} + +static void rt_plat_set_ioconfig(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *iocfg) +{ + struct rt_mmc_host *mmc = (struct rt_mmc_host *)host; + + LOG_D("clock:%d,width:%d,power:%d,vdd:%d,timing:%d\n", + iocfg->clock, iocfg->bus_width, + iocfg->power_mode, iocfg->vdd, iocfg->timing); + + mmc->ops->set_ios(mmc, iocfg); +} + +static rt_int32_t rt_plat_get_card_status(struct rt_mmcsd_host *host) +{ + struct rt_mmc_host *mmc = (struct rt_mmc_host *)host; + + return mmc->ops->get_cd(mmc); +} + +static rt_int32_t rt_plat_execute_tuning(struct rt_mmcsd_host *host, rt_int32_t opcode) +{ + struct rt_mmc_host *mmc = (struct rt_mmc_host *)host; + + return mmc->ops->execute_tuning(mmc, opcode); +} + +static void rt_plat_enable_sdio_irq(struct rt_mmcsd_host *host, rt_int32_t en) +{ + struct rt_mmc_host *mmc = (struct rt_mmc_host *)host; + + return mmc->ops->enable_sdio_irq(mmc, en); +} + + +static const struct rt_mmcsd_host_ops rt_mmcsd_ops = { + .request = rt_plat_request, + .set_iocfg = rt_plat_set_ioconfig, + .get_card_status = rt_plat_get_card_status, + .enable_sdio_irq = rt_plat_enable_sdio_irq, + .execute_tuning = rt_plat_execute_tuning, +}; + + +void rt_mmc_request_done(struct rt_mmc_host *host, struct rt_mmcsd_req *mrq) +{ + mmcsd_req_complete(&host->rthost); +} + +/*add host in rtt while sdhci complete*/ +int rt_mmc_add_host(struct rt_mmc_host *mmc) +{ + mmc->rthost.ops = &rt_mmcsd_ops; + mmc->rthost.flags = mmc->caps; + mmc->rthost.freq_max = mmc->f_max; + mmc->rthost.freq_min = 400000; + mmc->rthost.max_dma_segs = mmc->max_segs; + mmc->rthost.max_seg_size = mmc->max_seg_size; + mmc->rthost.max_blk_size = mmc->max_blk_size; + mmc->rthost.max_blk_count = mmc->max_blk_count; + mmc->rthost.valid_ocr = VDD_165_195|VDD_20_21|VDD_21_22|VDD_22_23|VDD_24_25|VDD_25_26|VDD_26_27|VDD_27_28|VDD_28_29|VDD_29_30|VDD_30_31|VDD_32_33|VDD_33_34|VDD_34_35|VDD_35_36; + + + mmcsd_change(&mmc->rthost); + return 0; +} + +struct rt_mmc_host *rt_mmc_alloc_host(int extra, struct rt_device *dev) +{ + struct rt_mmc_host *mmc; + + mmc = rt_malloc(sizeof(*mmc) + extra); + if (mmc) + { + rt_memset(mmc, 0, sizeof(*mmc) + extra); + mmc->parent = dev; + mmcsd_host_init(&mmc->rthost); + } + + return mmc; +} + +void rt_mmc_remove_host(struct rt_mmc_host *host) +{ + rt_free(host); +} + +int rt_mmc_abort_tuning(struct rt_mmc_host *host, rt_uint32_t opcode) +{ + return 0; +} + + +int rt_mmc_gpio_get_cd(struct rt_mmc_host *host) +{ + return -ENOSYS; +} + +void rt_mmc_detect_change(struct rt_mmc_host *host, unsigned long delay) +{ +} + + +int rt_mmc_regulator_set_vqmmc(struct rt_mmc_host *mmc, struct rt_mmcsd_io_cfg *ios) +{ + return 0; +} + +rt_bool_t rt_mmc_can_gpio_ro(struct rt_mmc_host *host) +{ + return RT_FALSE; +} + +int rt_mmc_gpio_get_ro(struct rt_mmc_host *host) +{ + return 0; +} + +int rt_mmc_send_abort_tuning(struct rt_mmc_host *host, rt_uint32_t opcode) +{ + return 0; +} +int rt_mmc_of_parse(struct rt_mmc_host *host) +{ + struct rt_device *dev = host->parent; + rt_uint32_t bus_width; + + if (!dev || !dev->ofw_node) + return 0; + + /* "bus-width" is translated to MMC_CAP_*_BIT_DATA flags */ + if (rt_dm_dev_prop_read_u32(dev, "bus-width", &bus_width) < 0) + { + bus_width = 1; + } + + switch (bus_width) + { + case 8: + host->caps |= MMC_CAP_8_BIT_DATA; + break; /* Hosts capable of 8-bit can also do 4 bits */ + case 4: + host->caps |= MMC_CAP_4_BIT_DATA; + break; + case 1: + break; + default: + return -EINVAL; + } + + /* f_max is obtained from the optional "max-frequency" property */ + rt_dm_dev_prop_read_u32(dev, "max-frequency", &host->f_max); + + if (rt_dm_dev_prop_read_bool(dev, "cap-mmc-highspeed")) + { + host->caps |= MMC_CAP_MMC_HIGHSPEED; + } + + if (rt_dm_dev_prop_read_bool(dev, "mmc-hs200-1_8v")) + { + host->caps |= MMC_CAP2_HS200_1_8V_SDR; + } + + if (rt_dm_dev_prop_read_bool(dev, "non-removable")) + { + host->caps |= MMC_CAP_NONREMOVABLE; + } + + if (rt_dm_dev_prop_read_bool(dev, "no-sdio")) + { + host->caps2 |= MMC_CAP2_NO_SDIO; + } + + if (rt_dm_dev_prop_read_bool(dev, "no-sd")) + { + host->caps2 |= MMC_CAP2_NO_SD; + } + + if (rt_dm_dev_prop_read_bool(dev, "mmc-ddr-3_3v")) + { + host->caps |= MMC_CAP_3_3V_DDR; + } + + if (rt_dm_dev_prop_read_bool(dev, "mmc-ddr-1_8v")) + { + host->caps |= MMC_CAP_1_8V_DDR; + } + + if (rt_dm_dev_prop_read_bool(dev, "mmc-ddr-1_2v")) + { + host->caps |= MMC_CAP_1_2V_DDR; + } + + return 0; +} + + +void rt_mmc_free_host(struct rt_mmc_host *host) +{ +} + +rt_bool_t rt_mmc_can_gpio_cd(struct rt_mmc_host *host) +{ + return RT_FALSE; +} + +int mmc_regulator_get_supply(struct rt_mmc_host *mmc) +{ + mmc->supply.vmmc = -RT_NULL; + mmc->supply.vqmmc = -RT_NULL; + + return 0; +} +int regulator_get_current_limit(struct regulator *regulator) +{ + return 0; +} + +int regulator_is_supported_voltage(struct regulator *regulator, + + int min_uV, int max_uV) +{ + return 0; +} diff --git a/components/drivers/sdio/sdhci/include/sdhci-platform.h b/components/drivers/sdio/sdhci/include/sdhci-platform.h new file mode 100644 index 00000000000..f8d96449051 --- /dev/null +++ b/components/drivers/sdio/sdhci/include/sdhci-platform.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-08-16 zhujiale first version + */ + +#ifndef _DRIVERS_MMC_RT_SDHCI_PLTFM_H +#define _DRIVERS_MMC_RT_SDHCI_PLTFM_H +#include +#include +#include +#include +#include +#include "sdhci.h" + +struct rt_sdhci_pltfm_data +{ + const struct rt_sdhci_ops *ops; + unsigned int quirks; + unsigned int quirks2; +}; + +struct rt_sdhci_pltfm_host +{ + struct rt_clk *clk; + unsigned int clock; + rt_uint64_t xfer_mode_shadow; + + unsigned long private[]; +}; +void rt_sdhci_get_property(struct rt_platform_device *pdev); + +static inline void sdhci_get_of_property(struct rt_platform_device *pdev) +{ + return rt_sdhci_get_property(pdev); +} +extern struct rt_sdhci_host *rt_sdhci_pltfm_init(struct rt_platform_device *pdev, + const struct rt_sdhci_pltfm_data *pdata, + size_t priv_size); +extern void rt_sdhci_pltfm_free(struct rt_platform_device *pdev); + +extern int rt_sdhci_pltfm_init_and_add_host(struct rt_platform_device *pdev, + const struct rt_sdhci_pltfm_data *pdata, + size_t priv_size); +extern void rt_sdhci_pltfm_remove(struct rt_platform_device *pdev); + +extern unsigned int rt_sdhci_pltfm_clk_get_max_clock(struct rt_sdhci_host *host); + +static inline void *sdhci_pltfm_priv(struct rt_sdhci_pltfm_host *host) +{ + return host->private; +} + +static inline int sdhci_pltfm_suspend(struct rt_device *dev) +{ + return 0; +} +static inline int sdhci_pltfm_resume(struct rt_device *dev) +{ + return 0; +} +#endif diff --git a/components/drivers/sdio/sdhci/include/sdhci.h b/components/drivers/sdio/sdhci/include/sdhci.h new file mode 100644 index 00000000000..1ad3db12882 --- /dev/null +++ b/components/drivers/sdio/sdhci/include/sdhci.h @@ -0,0 +1,677 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-08-16 zhujiale first version + */ +#ifndef __RT_SDHCI_HW_H +#define __RT_SDHCI_HW_H + +#include "sdhci_host.h" +#include "sdhci_misc.h" +#include "sdhci-platform.h" +#include +#include +#include +#include + +#define lower_32_bits(n) ((rt_uint32_t)((n) & 0xffffffff)) +#define upper_32_bits(n) ((rt_uint32_t)(((n) >> 16) >> 16)) + +#define MAX_TUNING_LOOP 40 +/* + * Controller registers + */ +#define RT_SDHCI_DMA_ADDRESS 0x00 +#define RT_SDHCI_ARGUMENT2 RT_SDHCI_DMA_ADDRESS +#define RT_SDHCI_32BIT_BLK_CNT RT_SDHCI_DMA_ADDRESS + +#define RT_SDHCI_BLOCK_SIZE 0x04 +#define RT_SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF)) + +#define RT_SDHCI_BLOCK_COUNT 0x06 + +#define RT_SDHCI_ARGUMENT 0x08 + +#define RT_SDHCI_TRANSFER_MODE 0x0C +#define RT_SDHCI_TRNS_DMA 0x01 +#define RT_SDHCI_TRNS_BLK_CNT_EN 0x02 +#define RT_SDHCI_TRNS_AUTO_CMD12 0x04 +#define RT_SDHCI_TRNS_AUTO_CMD23 0x08 +#define RT_SDHCI_TRNS_AUTO_SEL 0x0C +#define RT_SDHCI_TRNS_READ 0x10 +#define RT_SDHCI_TRNS_MULTI 0x20 + +#define RT_SDHCI_COMMAND 0x0E +#define RT_SDHCI_CMD_RESP_MASK 0x03 +#define RT_SDHCI_CMD_CRC 0x08 +#define RT_SDHCI_CMD_INDEX 0x10 +#define RT_SDHCI_CMD_DATA 0x20 +#define RT_SDHCI_CMD_ABORTCMD 0xC0 + +#define RT_SDHCI_CMD_RESP_NONE 0x00 +#define RT_SDHCI_CMD_RESP_LONG 0x01 +#define RT_SDHCI_CMD_RESP_SHORT 0x02 +#define RT_SDHCI_CMD_RESP_SHORT_BUSY 0x03 + +#define RT_SDHCI_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff)) +#define RT_SDHCI_GET_CMD(c) ((c >> 8) & 0x3f) + +#define RT_SDHCI_RESPONSE 0x10 + +#define RT_SDHCI_BUFFER 0x20 + +#define RT_SDHCI_PRESENT_STATE 0x24 +#define RT_SDHCI_CMD_INHIBIT 0x00000001 +#define RT_SDHCI_DATA_INHIBIT 0x00000002 +#define RT_SDHCI_DOING_WRITE 0x00000100 +#define RT_SDHCI_DOING_READ 0x00000200 +#define RT_SDHCI_SPACE_AVAILABLE 0x00000400 +#define RT_SDHCI_DATA_AVAILABLE 0x00000800 +#define RT_SDHCI_CARD_PRESENT 0x00010000 +#define RT_SDHCI_CARD_PRES_SHIFT 16 +#define RT_SDHCI_CD_STABLE 0x00020000 +#define RT_SDHCI_CD_LVL 0x00040000 +#define RT_SDHCI_CD_LVL_SHIFT 18 +#define RT_SDHCI_WRITE_PROTECT 0x00080000 +#define RT_SDHCI_DATA_LVL_MASK 0x00F00000 +#define RT_SDHCI_DATA_LVL_SHIFT 20 +#define RT_SDHCI_DATA_0_LVL_MASK 0x00100000 +#define RT_SDHCI_CMD_LVL 0x01000000 + +#define RT_SDHCI_HOST_CONTROL 0x28 +#define RT_SDHCI_CTRL_LED 0x01 +#define RT_SDHCI_CTRL_4BITBUS 0x02 +#define RT_SDHCI_CTRL_HISPD 0x04 +#define RT_SDHCI_CTRL_DMA_MASK 0x18 +#define RT_SDHCI_CTRL_SDMA 0x00 +#define RT_SDHCI_CTRL_ADMA1 0x08 +#define RT_SDHCI_CTRL_ADMA32 0x10 +#define RT_SDHCI_CTRL_ADMA64 0x18 +#define RT_SDHCI_CTRL_ADMA3 0x18 +#define RT_SDHCI_CTRL_8BITBUS 0x20 +#define RT_SDHCI_CTRL_CDTEST_INS 0x40 +#define RT_SDHCI_CTRL_CDTEST_EN 0x80 + +#define RT_SDHCI_POWER_CONTROL 0x29 +#define RT_SDHCI_POWER_ON 0x01 +#define RT_SDHCI_POWER_180 0x0A +#define RT_SDHCI_POWER_300 0x0C +#define RT_SDHCI_POWER_330 0x0E +/* + * VDD2 - UHS2 or PCIe/NVMe + * VDD2 power on/off and voltage select + */ +#define RT_SDHCI_VDD2_POWER_ON 0x10 +#define RT_SDHCI_VDD2_POWER_120 0x80 +#define RT_SDHCI_VDD2_POWER_180 0xA0 + +#define RT_SDHCI_BLOCK_GAP_CONTROL 0x2A + +#define RT_SDHCI_WAKE_UP_CONTROL 0x2B +#define RT_SDHCI_WAKE_ON_INT 0x01 +#define RT_SDHCI_WAKE_ON_INSERT 0x02 +#define RT_SDHCI_WAKE_ON_REMOVE 0x04 + +#define RT_SDHCI_CLOCK_CONTROL 0x2C +#define RT_SDHCI_DIVIDER_SHIFT 8 +#define RT_SDHCI_DIVIDER_HI_SHIFT 6 +#define RT_SDHCI_DIV_MASK 0xFF +#define RT_SDHCI_DIV_MASK_LEN 8 +#define RT_SDHCI_DIV_HI_MASK 0x300 +#define RT_SDHCI_PROG_CLOCK_MODE 0x0020 +#define RT_SDHCI_CLOCK_CARD_EN 0x0004 +#define RT_SDHCI_CLOCK_PLL_EN 0x0008 +#define RT_SDHCI_CLOCK_INT_STABLE 0x0002 +#define RT_SDHCI_CLOCK_INT_EN 0x0001 + +#define RT_SDHCI_TIMEOUT_CONTROL 0x2E + +#define RT_SDHCI_SOFTWARE_RESET 0x2F +#define RT_SDHCI_RESET_ALL 0x01 +#define RT_SDHCI_RESET_CMD 0x02 +#define RT_SDHCI_RESET_DATA 0x04 + +#define RT_SDHCI_INT_STATUS 0x30 +#define RT_SDHCI_INT_ENABLE 0x34 +#define RT_SDHCI_SIGNAL_ENABLE 0x38 +#define RT_SDHCI_INT_RESPONSE 0x00000001 +#define RT_SDHCI_INT_DATA_END 0x00000002 +#define RT_SDHCI_INT_BLK_GAP 0x00000004 +#define RT_SDHCI_INT_DMA_END 0x00000008 +#define RT_SDHCI_INT_SPACE_AVAIL 0x00000010 +#define RT_SDHCI_INT_DATA_AVAIL 0x00000020 +#define RT_SDHCI_INT_CARD_INSERT 0x00000040 +#define RT_SDHCI_INT_CARD_REMOVE 0x00000080 +#define RT_SDHCI_INT_CARD_INT 0x00000100 +#define RT_SDHCI_INT_RETUNE 0x00001000 +#define RT_SDHCI_INT_CQE 0x00004000 +#define RT_SDHCI_INT_ERROR 0x00008000 +#define RT_SDHCI_INT_TIMEOUT 0x00010000 +#define RT_SDHCI_INT_CRC 0x00020000 +#define RT_SDHCI_INT_END_BIT 0x00040000 +#define RT_SDHCI_INT_INDEX 0x00080000 +#define RT_SDHCI_INT_DATA_TIMEOUT 0x00100000 +#define RT_SDHCI_INT_DATA_CRC 0x00200000 +#define RT_SDHCI_INT_DATA_END_BIT 0x00400000 +#define RT_SDHCI_INT_BUS_POWER 0x00800000 +#define RT_SDHCI_INT_AUTO_CMD_ERR 0x01000000 +#define RT_SDHCI_INT_ADMA_ERROR 0x02000000 + +#define RT_SDHCI_INT_NORMAL_MASK 0x00007FFF +#define RT_SDHCI_INT_ERROR_MASK 0xFFFF8000 + +#define RT_SDHCI_INT_CMD_MASK (RT_SDHCI_INT_RESPONSE | RT_SDHCI_INT_TIMEOUT | RT_SDHCI_INT_CRC | RT_SDHCI_INT_END_BIT | RT_SDHCI_INT_INDEX | RT_SDHCI_INT_AUTO_CMD_ERR) +#define RT_SDHCI_INT_DATA_MASK (RT_SDHCI_INT_DATA_END | RT_SDHCI_INT_DMA_END | RT_SDHCI_INT_DATA_AVAIL | RT_SDHCI_INT_SPACE_AVAIL | RT_SDHCI_INT_DATA_TIMEOUT | RT_SDHCI_INT_DATA_CRC | RT_SDHCI_INT_DATA_END_BIT | RT_SDHCI_INT_ADMA_ERROR | RT_SDHCI_INT_BLK_GAP) +#define RT_SDHCI_INT_ALL_MASK ((unsigned int)-1) + +#define RT_SDHCI_CQE_INT_ERR_MASK ( \ + RT_SDHCI_INT_ADMA_ERROR | RT_SDHCI_INT_BUS_POWER | RT_SDHCI_INT_DATA_END_BIT | RT_SDHCI_INT_DATA_CRC | RT_SDHCI_INT_DATA_TIMEOUT | RT_SDHCI_INT_INDEX | RT_SDHCI_INT_END_BIT | RT_SDHCI_INT_CRC | RT_SDHCI_INT_TIMEOUT) + +#define RT_SDHCI_CQE_INT_MASK (RT_SDHCI_CQE_INT_ERR_MASK | RT_SDHCI_INT_CQE) + +#define RT_SDHCI_AUTO_CMD_STATUS 0x3C +#define RT_SDHCI_AUTO_CMD_TIMEOUT 0x00000002 +#define RT_SDHCI_AUTO_CMD_CRC 0x00000004 +#define RT_SDHCI_AUTO_CMD_END_BIT 0x00000008 +#define RT_SDHCI_AUTO_CMD_INDEX 0x00000010 + +#define RT_SDHCI_HOST_CONTROL2 0x3E +#define RT_SDHCI_CTRL_UHS_MASK 0x0007 +#define RT_SDHCI_CTRL_UHS_SDR12 0x0000 +#define RT_SDHCI_CTRL_UHS_SDR25 0x0001 +#define RT_SDHCI_CTRL_UHS_SDR50 0x0002 +#define RT_SDHCI_CTRL_UHS_SDR104 0x0003 +#define RT_SDHCI_CTRL_UHS_DDR50 0x0004 +#define RT_SDHCI_CTRL_HS400 0x0005 /* Non-standard */ +#define RT_SDHCI_CTRL_VDD_180 0x0008 +#define RT_SDHCI_CTRL_DRV_TYPE_MASK 0x0030 +#define RT_SDHCI_CTRL_DRV_TYPE_B 0x0000 +#define RT_SDHCI_CTRL_DRV_TYPE_A 0x0010 +#define RT_SDHCI_CTRL_DRV_TYPE_C 0x0020 +#define RT_SDHCI_CTRL_DRV_TYPE_D 0x0030 +#define RT_SDHCI_CTRL_EXEC_TUNING 0x0040 +#define RT_SDHCI_CTRL_TUNED_CLK 0x0080 +#define RT_SDHCI_CMD23_ENABLE 0x0800 +#define RT_SDHCI_CTRL_V4_MODE 0x1000 +#define RT_SDHCI_CTRL_64BIT_ADDR 0x2000 +#define RT_SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000 + +#define RT_SDHCI_CAPABILITIES 0x40 +#define RT_SDHCI_TIMEOUT_CLK_MASK RT_GENMASK(5, 0) +#define RT_SDHCI_TIMEOUT_CLK_SHIFT 0 +#define RT_SDHCI_TIMEOUT_CLK_UNIT 0x00000080 +#define RT_SDHCI_CLOCK_BASE_MASK RT_GENMASK(13, 8) +#define RT_SDHCI_CLOCK_BASE_SHIFT 8 +#define RT_SDHCI_CLOCK_V3_BASE_MASK RT_GENMASK(15, 8) +#define RT_SDHCI_MAX_BLOCK_MASK 0x00030000 +#define RT_SDHCI_MAX_BLOCK_SHIFT 16 +#define RT_SDHCI_CAN_DO_8BIT 0x00040000 +#define RT_SDHCI_CAN_DO_ADMA2 0x00080000 +#define RT_SDHCI_CAN_DO_ADMA1 0x00100000 +#define RT_SDHCI_CAN_DO_HISPD 0x00200000 +#define RT_SDHCI_CAN_DO_SDMA 0x00400000 +#define RT_SDHCI_CAN_DO_SUSPEND 0x00800000 +#define RT_SDHCI_CAN_VDD_330 0x01000000 +#define RT_SDHCI_CAN_VDD_300 0x02000000 +#define RT_SDHCI_CAN_VDD_180 0x04000000 +#define RT_SDHCI_CAN_64BIT_V4 0x08000000 +#define RT_SDHCI_CAN_64BIT 0x10000000 + +#define RT_SDHCI_CAPABILITIES_1 0x44 +#define RT_SDHCI_SUPPORT_SDR50 0x00000001 +#define RT_SDHCI_SUPPORT_SDR104 0x00000002 +#define RT_SDHCI_SUPPORT_DDR50 0x00000004 +#define RT_SDHCI_DRIVER_TYPE_A 0x00000010 +#define RT_SDHCI_DRIVER_TYPE_C 0x00000020 +#define RT_SDHCI_DRIVER_TYPE_D 0x00000040 +#define RT_SDHCI_RETUNING_TIMER_COUNT_MASK RT_GENMASK(11, 8) +#define RT_SDHCI_USE_SDR50_TUNING 0x00002000 +#define RT_SDHCI_RETUNING_MODE_MASK RT_GENMASK(15, 14) +#define RT_SDHCI_CLOCK_MUL_MASK RT_GENMASK(23, 16) +#define RT_SDHCI_CAN_DO_ADMA3 0x08000000 +#define RT_SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard */ + +#define RT_SDHCI_MAX_CURRENT 0x48 +#define RT_SDHCI_MAX_CURRENT_LIMIT RT_GENMASK(7, 0) +#define RT_SDHCI_MAX_CURRENT_330_MASK RT_GENMASK(7, 0) +#define RT_SDHCI_MAX_CURRENT_300_MASK RT_GENMASK(15, 8) +#define RT_SDHCI_MAX_CURRENT_180_MASK RT_GENMASK(23, 16) +#define RT_SDHCI_MAX_CURRENT_MULTIPLIER 4 + +/* 4C-4F reserved for more max current */ + +#define RT_SDHCI_SET_ACMD12_ERROR 0x50 +#define RT_SDHCI_SET_INT_ERROR 0x52 + +#define RT_SDHCI_ADMA_ERROR 0x54 + +/* 55-57 reserved */ + +#define RT_SDHCI_ADMA_ADDRESS 0x58 +#define RT_SDHCI_ADMA_ADDRESS_HI 0x5C + +/* 60-FB reserved */ + +#define RT_SDHCI_PRESET_FOR_HIGH_SPEED 0x64 +#define RT_SDHCI_PRESET_FOR_SDR12 0x66 +#define RT_SDHCI_PRESET_FOR_SDR25 0x68 +#define RT_SDHCI_PRESET_FOR_SDR50 0x6A +#define RT_SDHCI_PRESET_FOR_SDR104 0x6C +#define RT_SDHCI_PRESET_FOR_DDR50 0x6E +#define RT_SDHCI_PRESET_FOR_HS400 0x74 /* Non-standard */ +#define RT_SDHCI_PRESET_DRV_MASK RT_GENMASK(15, 14) +#define BIT(nr) ((1) << (nr)) + +#define RT_SDHCI_PRESET_CLKGEN_SEL BIT(10) +#define RT_SDHCI_PRESET_SDCLK_FREQ_MASK RT_GENMASK(9, 0) + +#define RT_SDHCI_SLOT_INT_STATUS 0xFC + +#define RT_SDHCI_HOST_VERSION 0xFE +#define RT_SDHCI_VENDOR_VER_MASK 0xFF00 +#define RT_SDHCI_VENDOR_VER_SHIFT 8 +#define RT_SDHCI_SPEC_VER_MASK 0x00FF +#define RT_SDHCI_SPEC_VER_SHIFT 0 +#define RT_SDHCI_SPEC_100 0 +#define RT_SDHCI_SPEC_200 1 +#define RT_SDHCI_SPEC_300 2 +#define RT_SDHCI_SPEC_400 3 +#define RT_SDHCI_SPEC_410 4 +#define RT_SDHCI_SPEC_420 5 + +/* + * End of controller registers. + */ + +#define RT_SDHCI_MAX_DIV_SPEC_200 256 +#define RT_SDHCI_MAX_DIV_SPEC_300 2046 + +/* + * Host SDMA buffer boundary. Valid values from 4K to 512K in powers of 2. + */ +#define RT_SDHCI_DEFAULT_BOUNDARY_SIZE (512 * 1024) +#define ilog2(v) __rt_ffs(v) +#define RT_SDHCI_DEFAULT_BOUNDARY_ARG (ilog2(RT_SDHCI_DEFAULT_BOUNDARY_SIZE) - 12) +#define RT_SDHCI_MAX_SEGS 128 + +/* Allow for a command request and a data request at the same time */ +#define RT_SDHCI_MAX_MRQS 2 +#define MMC_CMD_TRANSFER_TIME (10 * 1000000L) /* max 10 ms */ + + +enum rt_sdhci_cookie +{ + COOKIE_UNMAPPED, + COOKIE_PRE_MAPPED, /* mapped by sdhci_pre_req() */ + COOKIE_MAPPED, /* mapped by sdhci_prepare_data() */ +}; + +struct rt_sdhci_host +{ + const char *hw_name; /* Hardware bus name */ + + unsigned int quirks; /* Deviations from spec. */ + + void *data_buf; +/* Controller doesn't honor resets unless we touch the clock register */ +#define RT_SDHCI_QUIRK_CLOCK_BEFORE_RESET (1 << 0) +/* Controller has bad caps bits, but really supports DMA */ +#define RT_SDHCI_QUIRK_FORCE_DMA (1 << 1) +/* Controller doesn't like to be reset when there is no card inserted. */ +#define RT_SDHCI_QUIRK_NO_CARD_NO_RESET (1 << 2) +/* Controller doesn't like clearing the power reg before a change */ +#define RT_SDHCI_QUIRK_SINGLE_POWER_WRITE (1 << 3) +/* Controller has an unusable DMA engine */ +#define RT_SDHCI_QUIRK_BROKEN_DMA (1 << 5) +/* Controller has an unusable ADMA engine */ +#define RT_SDHCI_QUIRK_BROKEN_ADMA (1 << 6) +/* Controller can only DMA from 32-bit aligned addresses */ +#define RT_SDHCI_QUIRK_32BIT_DMA_ADDR (1 << 7) +/* Controller can only DMA chunk sizes that are a multiple of 32 bits */ +#define RT_SDHCI_QUIRK_32BIT_DMA_SIZE (1 << 8) +/* Controller can only ADMA chunks that are a multiple of 32 bits */ +#define RT_SDHCI_QUIRK_32BIT_ADMA_SIZE (1 << 9) +/* Controller needs to be reset after each request to stay stable */ +#define RT_SDHCI_QUIRK_RESET_AFTER_REQUEST (1 << 10) +/* Controller needs voltage and power writes to happen separately */ +#define RT_SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER (1 << 11) +/* Controller provides an incorrect timeout value for transfers */ +#define RT_SDHCI_QUIRK_BROKEN_TIMEOUT_VAL (1 << 12) +/* Controller has an issue with buffer bits for small transfers */ +#define RT_SDHCI_QUIRK_BROKEN_SMALL_PIO (1 << 13) +/* Controller does not provide transfer-complete interrupt when not busy */ +#define RT_SDHCI_QUIRK_NO_BUSY_IRQ (1 << 14) +/* Controller has unreliable card detection */ +#define RT_SDHCI_QUIRK_BROKEN_CARD_DETECTION (1 << 15) +/* Controller reports inverted write-protect state */ +#define RT_SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1 << 16) +/* Controller has unusable command queue engine */ +#define RT_SDHCI_QUIRK_BROKEN_CQE (1 << 17) +/* Controller does not like fast PIO transfers */ +#define RT_SDHCI_QUIRK_PIO_NEEDS_DELAY (1 << 18) +/* Controller does not have a LED */ +#define RT_SDHCI_QUIRK_NO_LED (1 << 19) +/* Controller has to be forced to use block size of 2048 bytes */ +#define RT_SDHCI_QUIRK_FORCE_BLK_SZ_2048 (1 << 20) +/* Controller cannot do multi-block transfers */ +#define RT_SDHCI_QUIRK_NO_MULTIBLOCK (1 << 21) +/* Controller can only handle 1-bit data transfers */ +#define RT_SDHCI_QUIRK_FORCE_1_BIT_DATA (1 << 22) +/* Controller needs 10ms delay between applying power and clock */ +#define RT_SDHCI_QUIRK_DELAY_AFTER_POWER (1 << 23) +/* Controller uses SDCLK instead of TMCLK for data timeouts */ +#define RT_SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK (1 << 24) +/* Controller reports wrong base clock capability */ +#define RT_SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN (1 << 25) +/* Controller cannot support End Attribute in NOP ADMA descriptor */ +#define RT_SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC (1 << 26) +/* Controller uses Auto CMD12 command to stop the transfer */ +#define RT_SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 (1 << 28) +/* Controller doesn't have HISPD bit field in HI-SPEED SD card */ +#define RT_SDHCI_QUIRK_NO_HISPD_BIT (1 << 29) +/* Controller treats ADMA descriptors with length 0000h incorrectly */ +#define RT_SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC (1 << 30) +/* The read-only detection via RT_SDHCI_PRESENT_STATE register is unstable */ +#define RT_SDHCI_QUIRK_UNSTABLE_RO_DETECT (1 << 31) + + unsigned int quirks2; /* More deviations from spec. */ + +#define RT_SDHCI_QUIRK2_HOST_OFF_CARD_ON (1 << 0) +#define RT_SDHCI_QUIRK2_HOST_NO_CMD23 (1 << 1) +/* The system physically doesn't support 1.8v, even if the host does */ +#define RT_SDHCI_QUIRK2_NO_1_8_V (1 << 2) +#define RT_SDHCI_QUIRK2_PRESET_VALUE_BROKEN (1 << 3) +#define RT_SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON (1 << 4) +/* Controller has a non-standard host control register */ +#define RT_SDHCI_QUIRK2_BROKEN_HOST_CONTROL (1 << 5) +/* Controller does not support HS200 */ +#define RT_SDHCI_QUIRK2_BROKEN_HS200 (1 << 6) +/* Controller does not support DDR50 */ +#define RT_SDHCI_QUIRK2_BROKEN_DDR50 (1 << 7) +/* Stop command (CMD12) can set Transfer Complete when not using MMC_RSP_BUSY */ +#define RT_SDHCI_QUIRK2_STOP_WITH_TC (1 << 8) +/* Controller does not support 64-bit DMA */ +#define RT_SDHCI_QUIRK2_BROKEN_64_BIT_DMA (1 << 9) +/* need clear transfer mode register before send cmd */ +#define RT_SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD (1 << 10) +/* Capability register bit-63 indicates HS400 support */ +#define RT_SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 (1 << 11) +/* forced tuned clock */ +#define RT_SDHCI_QUIRK2_TUNING_WORK_AROUND (1 << 12) +/* disable the block count for single block transactions */ +#define RT_SDHCI_QUIRK2_SUPPORT_SINGLE (1 << 13) +/* Controller broken with using ACMD23 */ +#define RT_SDHCI_QUIRK2_ACMD23_BROKEN (1 << 14) +/* Broken Clock divider zero in controller */ +#define RT_SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN (1 << 15) +/* Controller has CRC in 136 bit Command Response */ +#define RT_SDHCI_QUIRK2_RSP_136_HAS_CRC (1 << 16) + +#define RT_SDHCI_QUIRK2_DISABLE_HW_TIMEOUT (1 << 17) + +#define RT_SDHCI_QUIRK2_USE_32BIT_BLK_CNT (1 << 18) +/* Issue CMD and DATA reset together */ +#define RT_SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER (1 << 19) + + int irq; /* Device IRQ */ + void *ioaddr; /* Mapped address */ + char *bounce_buffer; /* For packing SDMA reads/writes */ + rt_uint64_t bounce_addr; + unsigned int bounce_buffer_size; + + const struct rt_sdhci_ops *ops; /* Low level hw interface */ + + /* Internal data */ + struct rt_mmc_host *mmc; /* MMC structure */ + struct mmc_host_ops mmc_host_ops; /* MMC host ops */ + rt_uint64_t dma_mask; /* custom DMA mask */ + + rt_spinlock_t lock; + int flags; /* Host attributes */ +#define RT_SDHCI_USE_SDMA (1 << 0) /* Host is SDMA capable */ +#define RT_SDHCI_USE_ADMA (1 << 1) /* Host is ADMA capable */ +#define RT_SDHCI_REQ_USE_DMA (1 << 2) /* Use DMA for this req. */ +#define RT_SDHCI_DEVICE_DEAD (1 << 3) /* Device unresponsive */ +#define RT_SDHCI_SDR50_NEEDS_TUNING (1 << 4) /* SDR50 needs tuning */ +#define RT_SDHCI_AUTO_CMD12 (1 << 6) /* Auto CMD12 support */ +#define RT_SDHCI_AUTO_CMD23 (1 << 7) /* Auto CMD23 support */ +#define RT_SDHCI_PV_ENABLED (1 << 8) /* Preset value enabled */ +#define RT_SDHCI_USE_64_BIT_DMA (1 << 12) /* Use 64-bit DMA */ +#define RT_SDHCI_HS400_TUNING (1 << 13) /* Tuning for HS400 */ +#define RT_SDHCI_SIGNALING_330 (1 << 14) /* Host is capable of 3.3V signaling */ +#define RT_SDHCI_SIGNALING_180 (1 << 15) /* Host is capable of 1.8V signaling */ +#define RT_SDHCI_SIGNALING_120 (1 << 16) /* Host is capable of 1.2V signaling */ + + unsigned int version; /* RT_SDHCI spec. version */ + + unsigned int max_clk; /* Max possible freq (MHz) */ + unsigned int timeout_clk; /* Timeout freq (KHz) */ + rt_uint8_t max_timeout_count; /* Vendor specific max timeout count */ + unsigned int clk_mul; /* Clock Muliplier value */ + + unsigned int clock; /* Current clock (MHz) */ + rt_uint8_t pwr; /* Current voltage */ + rt_uint8_t drv_type; /* Current UHS-I driver type */ + rt_bool_t reinit_uhs; /* Force UHS-related re-initialization */ + + rt_bool_t runtime_suspended; /* Host is runtime suspended */ + rt_bool_t bus_on; /* Bus power prevents runtime suspend */ + rt_bool_t preset_enabled; /* Preset is enabled */ + rt_bool_t pending_reset; /* Cmd/data reset is pending */ + rt_bool_t irq_wake_enabled; /* IRQ wakeup is enabled */ + rt_bool_t v4_mode; /* Host Version 4 Enable */ + rt_bool_t always_defer_done; /* Always defer to complete requests */ + + struct rt_mmcsd_req *mrqs_done[RT_SDHCI_MAX_MRQS]; /* Requests done */ + struct rt_mmcsd_cmd *cmd; /* Current command */ + struct rt_mmcsd_cmd *data_cmd; /* Current data command */ + struct rt_mmcsd_cmd *deferred_cmd; /* Deferred command */ + struct rt_mmcsd_data *data; /* Current data request */ + unsigned int data_early : 1; /* Data finished before cmd */ + + unsigned int blocks; /* remaining PIO blocks */ + size_t align_buffer_sz; /* Bounce buffer size */ + rt_uint64_t align_addr; /* Mapped bounce buffer */ + + struct rt_workqueue *complete_wq; /* Request completion wq */ + struct rt_work complete_work; /* Request completion work */ + + struct rt_workqueue *irq_wq; + struct rt_work irq_work; + + struct rt_timer timer; /* Timer for timeouts */ + struct rt_timer data_timer; /* Timer for data timeouts */ + + rt_uint32_t caps; /* CAPABILITY_0 */ + rt_uint32_t caps1; /* CAPABILITY_1 */ + rt_bool_t read_caps; /* Capability flags have been read */ + + rt_bool_t sdhci_core_to_disable_vqmmc; /* sdhci core can disable vqmmc */ + unsigned int ocr_avail_sdio; /* OCR bit masks */ + unsigned int ocr_avail_sd; + unsigned int ocr_avail_mmc; + rt_uint32_t ocr_mask; /* available voltages */ + + unsigned timing; /* Current timing */ + + rt_uint32_t thread_isr; + + /* cached registers */ + rt_uint32_t ier; + + rt_bool_t cqe_on; /* CQE is operating */ + rt_uint32_t cqe_ier; /* CQE interrupt mask */ + rt_uint32_t cqe_err_ier; /* CQE error interrupt mask */ + + rt_wqueue_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */ + unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */ + + unsigned int tuning_count; /* Timer count for re-tuning */ + unsigned int tuning_mode; /* Re-tuning mode supported by host */ + unsigned int tuning_err; /* Error code for re-tuning */ +#define RT_SDHCI_TUNING_MODE_1 0 +#define RT_SDHCI_TUNING_MODE_2 1 +#define RT_SDHCI_TUNING_MODE_3 2 + /* Delay (ms) between tuning commands */ + int tuning_delay; + int tuning_loop_count; + + /* Host SDMA buffer boundary. */ + rt_uint32_t sdma_boundary; + rt_uint64_t data_timeout; + + unsigned long private[]; +}; + +static inline rt_uint8_t u8_read(const volatile void *addr) +{ + return *(const volatile rt_uint8_t *)addr; +} + +static inline rt_uint16_t u16_read(const volatile void *addr) +{ + return *(const volatile rt_uint16_t *)addr; +} + +static inline rt_uint32_t u32_read(const volatile void *addr) +{ + return *(const volatile rt_uint32_t *)addr; +} + +static inline void u8_write(rt_uint8_t value, volatile void *addr) +{ + *(volatile rt_uint8_t *)addr = value; +} + +static inline void u16_write(rt_uint16_t value, volatile void *addr) +{ + *(volatile rt_uint16_t *)addr = value; +} + +static inline void u32_write(rt_uint32_t value, volatile void *addr) +{ + *(volatile rt_uint32_t *)addr = value; +} + +#define readb(c) u8_read(c) +#define readw(c) u16_read(c) +#define readl(c) u32_read(c) +#define readsb(p, d, l) ({ __raw_readsb(p,d,l); __iormb(); }) +#define readsw(p, d, l) ({ __raw_readsw(p,d,l); __iormb(); }) +#define readsl(p, d, l) ({ __raw_readsl(p,d,l); __iormb(); }) + +#define writeb(v, c) u8_write(v, c) +#define writew(v, c) u16_write(v, c) +#define writel(v, c) u32_write(v, c) +#define writesb(p, d, l) ({ __iowmb(); __raw_writesb(p,d,l); }) +#define writesw(p, d, l) ({ __iowmb(); __raw_writesw(p,d,l); }) +#define writesl(p, d, l) ({ __iowmb(); __raw_writesl(p,d,l); }) + +static inline void rt_sdhci_writel(struct rt_sdhci_host *host, rt_uint32_t val, int reg) +{ + writel(val, host->ioaddr + reg); +} + +static inline void rt_sdhci_writew(struct rt_sdhci_host *host, rt_uint16_t val, int reg) +{ + writew(val, host->ioaddr + reg); +} + +static inline void rt_sdhci_writeb(struct rt_sdhci_host *host, rt_uint8_t val, int reg) +{ + writeb(val, host->ioaddr + reg); +} + +static inline rt_uint32_t rt_sdhci_readl(struct rt_sdhci_host *host, int reg) +{ + return readl(host->ioaddr + reg); +} + +static inline rt_uint16_t rt_sdhci_readw(struct rt_sdhci_host *host, int reg) +{ + return readw(host->ioaddr + reg); +} + +static inline rt_uint8_t rt_sdhci_readb(struct rt_sdhci_host *host, int reg) +{ + return readb(host->ioaddr + reg); +} + + +struct rt_sdhci_ops +{ + void (*set_clock)(struct rt_sdhci_host *host, unsigned int clock); + void (*set_power)(struct rt_sdhci_host *host, unsigned char mode, + unsigned short vdd); + rt_uint32_t (*irq)(struct rt_sdhci_host *host, rt_uint32_t intmask); + int (*set_dma_mask)(struct rt_sdhci_host *host); + int (*enable_dma)(struct rt_sdhci_host *host); + unsigned int (*get_max_clock)(struct rt_sdhci_host *host); + unsigned int (*get_min_clock)(struct rt_sdhci_host *host); + unsigned int (*get_timeout_clock)(struct rt_sdhci_host *host); + unsigned int (*get_max_timeout_count)(struct rt_sdhci_host *host); + void (*set_timeout)(struct rt_sdhci_host *host, + struct rt_mmcsd_cmd *cmd); + void (*set_bus_width)(struct rt_sdhci_host *host, int width); + unsigned int (*get_ro)(struct rt_sdhci_host *host); + void (*reset)(struct rt_sdhci_host *host, rt_uint8_t mask); + int (*platform_execute_tuning)(struct rt_sdhci_host *host, rt_uint32_t opcode); + void (*set_uhs_signaling)(struct rt_sdhci_host *host, unsigned int uhs); + void (*hw_reset)(struct rt_sdhci_host *host); + void (*card_event)(struct rt_sdhci_host *host); + void (*voltage_switch)(struct rt_sdhci_host *host); + void (*request_done)(struct rt_sdhci_host *host, + struct rt_mmcsd_req *mrq); +}; + + +struct rt_sdhci_host *rt_sdhci_alloc_host(struct rt_device *dev, size_t priv_size); +void rt_sdhci_free_host(struct rt_sdhci_host *host); + +static inline void *sdhci_priv(struct rt_sdhci_host *host) +{ + return host->private; +} + +void rt_sdhci_read_caps(struct rt_sdhci_host *host, const rt_uint16_t *ver, + const rt_uint32_t *caps, const rt_uint32_t *caps1); +int rt_sdhci_setup_host(struct rt_sdhci_host *host); +void rt_sdhci_cleanup_host(struct rt_sdhci_host *host); +int rt_sdhci_set_and_add_host(struct rt_sdhci_host *host); +int rt_sdhci_init_host(struct rt_sdhci_host *host); +void rt_sdhci_uninit_host(struct rt_sdhci_host *host, int dead); + +rt_uint16_t rt_sdhci_clk_set(struct rt_sdhci_host *host, unsigned int clock, + unsigned int *actual_clock); +void rt_sdhci_set_clock(struct rt_sdhci_host *host, unsigned int clock); +void rt_sdhci_clk_enable(struct rt_sdhci_host *host, rt_uint16_t clk); +void rt_sdhci_set_power(struct rt_sdhci_host *host, unsigned char mode,unsigned short vdd); +void rt_read_reg(struct rt_sdhci_host* host); + +void rt_sdhci_set_power_with_noreg(struct rt_sdhci_host *host, unsigned char mode, + unsigned short vdd); +void rt_sdhci_start_request(struct rt_mmc_host *mmc, struct rt_mmcsd_req *mrq); +int rt_sdhci_start_request_atomic(struct rt_mmc_host *mmc, struct rt_mmcsd_req *mrq); +void rt_sdhci_set_bus_width(struct rt_sdhci_host *host, int width); +void rt_sdhci_reset(struct rt_sdhci_host *host, rt_uint8_t mask); +void rt_sdhci_set_uhs(struct rt_sdhci_host *host, unsigned timing); +int rt_sdhci_execute_tuning(struct rt_mmc_host *mmc, rt_uint32_t opcode); +int __sdhci_execute_tuning(struct rt_sdhci_host *host, rt_uint32_t opcode); +void rt_sdhci_ios_set(struct rt_mmc_host *mmc, struct rt_mmcsd_io_cfg *ios); +int rt_sdhci_start_signal_voltage_switch(struct rt_mmc_host *mmc, + struct rt_mmcsd_io_cfg *ios); +void rt_sdhci_enable_io_irq(struct rt_mmc_host *mmc, int enable); +void rt_sdhci_start_tuning(struct rt_sdhci_host *host); +void rt_sdhci_end_tuning(struct rt_sdhci_host *host); +void rt_sdhci_reset_tuning(struct rt_sdhci_host *host); +void rt_sdhci_send_tuning(struct rt_sdhci_host *host, rt_uint32_t opcode); +void rt_sdhci_abort_tuning(struct rt_sdhci_host *host, rt_uint32_t opcode); +void rt_sdhci_data_irq_timeout(struct rt_sdhci_host *host, rt_bool_t enable); +void rt_sdhci_timeout_set(struct rt_sdhci_host *host, struct rt_mmcsd_cmd *cmd); +void rt_read_reg_debug(struct rt_sdhci_host* host); + +#endif /* __RT_SDHCI_HW_H */ diff --git a/components/drivers/sdio/sdhci/include/sdhci_host.h b/components/drivers/sdio/sdhci/include/sdhci_host.h new file mode 100644 index 00000000000..8584ab4b9d5 --- /dev/null +++ b/components/drivers/sdio/sdhci/include/sdhci_host.h @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-08-16 zhujiale first version + */ +#ifndef __RT_SDHCI_MMC_H__ +#define __RT_SDHCI_MMC_H__ + +#include +#include +#include +#include +#include +#define mmc_dev(x) ((x)->parent) + +#define MMC_SEND_TUNING_BLOCK_HS200 SEND_TUNING_BLOCK_HS200 +#define MMC_SEND_TUNING_BLOCK SEND_TUNING_BLOCK +#define MMC_STOP_TRANSMISSION STOP_TRANSMISSION +#define MMC_BUS_TEST_R 14 /* adtc R1 */ +#define MMC_WRITE_MULTIPLE_BLOCK WRITE_MULTIPLE_BLOCK +#define MMC_READ_MULTIPLE_BLOCK READ_MULTIPLE_BLOCK + +#define MMC_TIMING_UHS_DDR50 MMCSD_TIMING_UHS_DDR50 +#define MMC_TIMING_UHS_SDR50 MMCSD_TIMING_UHS_SDR50 +#define MMC_TIMING_MMC_HS200 MMCSD_TIMING_MMC_HS200 +#define MMC_TIMING_MMC_HS400 MMCSD_TIMING_MMC_HS400 +#define MMC_TIMING_UHS_SDR104 MMCSD_TIMING_UHS_SDR104 +#define MMC_TIMING_UHS_SDR25 MMCSD_TIMING_UHS_SDR25 +#define MMC_TIMING_MMC_DDR52 MMCSD_TIMING_MMC_DDR52 +#define MMC_TIMING_UHS_SDR12 MMCSD_TIMING_UHS_SDR12 +#define MMC_TIMING_SD_HS MMCSD_TIMING_SD_HS +#define MMC_TIMING_MMC_HS MMCSD_TIMING_MMC_HS + +#define MMC_POWER_OFF MMCSD_POWER_OFF +#define MMC_POWER_UP MMCSD_POWER_UP +#define MMC_POWER_ON MMCSD_POWER_ON +#define MMC_POWER_UNDEFINED 3 + +#define MMC_SET_DRIVER_TYPE_B 0 +#define MMC_SET_DRIVER_TYPE_A 1 +#define MMC_SET_DRIVER_TYPE_C 2 +#define MMC_SET_DRIVER_TYPE_D 3 + +#define MMC_SIGNAL_VOLTAGE_330 0 +#define MMC_SIGNAL_VOLTAGE_180 1 +#define MMC_SIGNAL_VOLTAGE_120 2 + +#define MMC_RSP_PRESENT (1 << 16) +#define MMC_RSP_136 (1 << 17) /* 136 bit response */ +#define MMC_RSP_CRC (1 << 18) /* expect valid crc */ +#define MMC_RSP_BUSY (1 << 19) /* card may send busy */ +#define MMC_RSP_OPCODE (1 << 20) /* response contains opcode */ + +#define MMC_RSP_NONE (0) +#define MMC_RSP_R1 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE) +#define MMC_RSP_R1B (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE | MMC_RSP_BUSY) +#define MMC_RSP_R2 (MMC_RSP_PRESENT | MMC_RSP_136 | MMC_RSP_CRC) +#define MMC_RSP_R3 (MMC_RSP_PRESENT) +#define MMC_RSP_R4 (MMC_RSP_PRESENT) +#define MMC_RSP_R5 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE) +#define MMC_RSP_R6 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE) +#define MMC_RSP_R7 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE) + +#define MMC_CMD_ADTC CMD_ADTC + +#define MMC_BUS_WIDTH_8 MMCSD_BUS_WIDTH_8 +#define MMC_BUS_WIDTH_4 MMCSD_BUS_WIDTH_4 +#define MMC_BUS_WIDTH_1 MMCSD_BUS_WIDTH_1 + +#define MMC_PM_KEEP_POWER (1 << 0) /* preserve card power during suspend */ +#define MMC_PM_WAKE_SDIO_IRQ (1 << 1) /* wake up host system on SDIO IRQ assertion */ +enum mmc_blk_status +{ + MMC_BLK_SUCCESS = 0, + MMC_BLK_PARTIAL, + MMC_BLK_CMD_ERR, + MMC_BLK_RETRY, + MMC_BLK_ABORT, + MMC_BLK_DATA_ERR, + MMC_BLK_ECC_ERR, + MMC_BLK_NOMEDIUM, + MMC_BLK_NEW_REQUEST, +}; + +#define MMC_NUM_CLK_PHASES (MMC_TIMING_MMC_HS400 + 1) + +struct rt_mmc_host ; + +struct mmc_host_ops +{ + void (*request)(struct rt_mmc_host *host, struct rt_mmcsd_req *req); + void (*set_ios)(struct rt_mmc_host *host, struct rt_mmcsd_io_cfg *ios); + int (*get_ro)(struct rt_mmc_host *host); + int (*get_cd)(struct rt_mmc_host *host); + void (*enable_sdio_irq)(struct rt_mmc_host *host, int enable); + void (*ack_sdio_irq)(struct rt_mmc_host *host); + int (*start_signal_voltage_switch)(struct rt_mmc_host *host, struct rt_mmcsd_io_cfg *ios); + int (*card_busy)(struct rt_mmc_host *host); + int (*execute_tuning)(struct rt_mmc_host *host, unsigned opcode); + int (*prepare_hs400_tuning)(struct rt_mmc_host *host, struct rt_mmcsd_io_cfg *ios); + int (*hs400_prepare_ddr)(struct rt_mmc_host *host); + void (*hs400_downgrade)(struct rt_mmc_host *host); + void (*hs400_complete)(struct rt_mmc_host *host); + void (*hs400_enhanced_strobe)(struct rt_mmc_host *host, + struct rt_mmcsd_io_cfg* ios); + void (*hw_reset)(struct rt_mmc_host* host); + void (*card_event)(struct rt_mmc_host* host); +}; + +struct regulator; +struct mmc_pwrseq; + +struct mmc_supply +{ + struct regulator *vmmc; /* Card power supply */ + struct regulator *vqmmc; /* Optional Vccq supply */ +}; + +struct mmc_ctx +{ + struct task_struct *task; +}; + +/* VDD voltage 3.3 ~ 3.4 */ +#define MMC_VDD_34_35 0x00400000 /* VDD voltage 3.4 ~ 3.5 */ +#define MMC_VDD_35_36 0x00800000 /* VDD voltage 3.5 ~ 3.6 */ + +#define MMC_CAP2_HS200_1_8V_SDR MMCSD_SUP_HS200_1V8 +#define MMC_CAP_4_BIT_DATA MMCSD_BUSWIDTH_4 +#define MMC_CAP_8_BIT_DATA MMCSD_BUSWIDTH_8 +#define MMC_CAP2_HS200 MMCSD_SUP_HS200 +#define MMC_CAP_MMC_HIGHSPEED MMCSD_SUP_HIGHSPEED +#define MMC_CAP_SD_HIGHSPEED MMCSD_SUP_HIGHSPEED +#define MMC_CAP_1_8V_DDR MMCSD_SUP_DDR_1V8 +#define MMC_CAP_3_3V_DDR MMCSD_SUP_DDR_3V3 +#define MMC_CAP_1_2V_DDR MMCSD_SUP_DDR_1V2 +#define MMC_CAP_NONREMOVABLE MMCSD_SUP_NONREMOVABLE + + +#define MMC_CAP_UHS_DDR50 0 +#define MMC_CAP2_HS400 0 +#define MMC_CAP_UHS_SDR50 0 +#define MMC_CAP_UHS_SDR25 0 +#define MMC_CAP_UHS_SDR12 0 +#define MMC_CAP_UHS_SDR104 0 +#define MMC_CAP_UHS 0 +#define MMC_CAP2_HSX00_1_8V 0 +#define MMC_CAP2_HS400_ES 0 +#define MMC_CAP_NEEDS_POLL 0 +#define MMC_CAP2_HSX00_1_2V 0 +#define MMC_CAP2_HS400_1_8V 0 +#define MMC_CAP_DRIVER_TYPE_D 0 +#define MMC_CAP_DRIVER_TYPE_C 0 +#define MMC_SET_DRIVER_TYPE_B 0 +#define MMC_CAP_DRIVER_TYPE_A 0 +#define MMC_CAP2_SDIO_IRQ_NOTHREAD 0 +#define MMC_CAP_CMD23 0 +#define MMC_CAP_SDIO_IRQ 0 + +#define MMC_CAP2_NO_SDIO (1 << 19) +#define MMC_CAP2_NO_SD (1 << 21) +#define MMC_CAP2_NO_MMC (1 << 22) +#define MMC_CAP2_CQE (1 << 23) + +#define MMC_VDD_165_195 VDD_165_195 +#define MMC_VDD_20_21 VDD_20_21 +#define MMC_VDD_29_30 VDD_29_30 +#define MMC_VDD_30_31 VDD_30_31 +#define MMC_VDD_32_33 VDD_32_33 +#define MMC_VDD_33_34 VDD_33_34 + + +struct rt_mmc_host +{ + struct rt_mmcsd_host rthost; + struct rt_device *parent; + int index; + const struct mmc_host_ops *ops; + unsigned int f_min; + unsigned int f_max; + unsigned int f_init; + rt_uint32_t ocr_avail; + rt_uint32_t ocr_avail_sdio; /* SDIO-specific OCR */ + rt_uint32_t ocr_avail_sd; /* SD-specific OCR */ + rt_uint32_t ocr_avail_mmc; /* MMC-specific OCR */ + struct wakeup_source *ws; /* Enable consume of uevents */ + rt_uint32_t max_current_330; + rt_uint32_t max_current_300; + rt_uint32_t max_current_180; + rt_uint32_t caps; /* Host capabilities */ + + rt_uint32_t caps2; /* More host capabilities */ + + + /* host specific block data */ + unsigned int max_seg_size; /* see blk_queue_max_segment_size */ + unsigned short max_segs; /* see blk_queue_max_segments */ + unsigned short unused; + unsigned int max_req_size; /* maximum number of bytes in one req */ + unsigned int max_blk_size; /* maximum size of one mmc block */ + unsigned int max_blk_count; /* maximum number of blocks in one req */ + unsigned int max_busy_timeout; /* max busy timeout in ms */ + struct rt_mmcsd_io_cfg ios; /* current io bus settings */ + unsigned int retune_period; + /* group bitfields together to minimize padding */ + unsigned int use_spi_crc : 1; + unsigned int claimed : 1; /* host exclusively claimed */ + unsigned int doing_init_tune : 1; /* initial tuning in progress */ + unsigned int can_retune : 1; /* re-tuning can be used */ + unsigned int doing_retune : 1; /* re-tuning in progress */ + unsigned int retune_now : 1; /* do re-tuning at next req */ + unsigned int retune_paused : 1; /* re-tuning is temporarily disabled */ + unsigned int retune_crc_disable : 1; /* don't trigger retune upon crc */ + unsigned int can_dma_map_merge : 1; /* merging can be used */ + unsigned int vqmmc_enabled : 1; /* vqmmc regulator is enabled */ + + int need_retune; /* re-tuning is needed */ + int hold_retune; /* hold off re-tuning */ + rt_bool_t trigger_card_event; /* card_event necessary */ + unsigned int sdio_irqs; + rt_bool_t sdio_irq_pending; + + struct led_trigger *led; /* activity led */ + + struct mmc_supply supply; + + + /* Ongoing data transfer that allows commands during transfer */ + struct rt_mmcsd_req *ongoing_mrq; + + + unsigned int actual_clock; /* Actual HC clock rate */ + rt_uint32_t pm_caps; + unsigned long private[]; +}; + + +static inline int mmc_card_is_removable(struct rt_mmc_host *host) +{ + return !(host->caps & MMC_CAP_NONREMOVABLE); +} + +struct device_node; +struct rt_mmc_host *rt_mmc_alloc_host(int extra, struct rt_device *); +int rt_mmc_add_host(struct rt_mmc_host *); +void rt_mmc_remove_host(struct rt_mmc_host *); +void rt_mmc_free_host(struct rt_mmc_host *); +int rt_mmc_of_parse(struct rt_mmc_host *host); +int rt_mmc_of_parse_voltage(struct rt_mmc_host *host, rt_uint32_t *mask); + +static inline void *mmc_priv(struct rt_mmc_host *host) +{ + return (void *)host->private; +} + + +#define mmc_host_is_spi(host) ((host)->caps & MMC_CAP_SPI) + +#define mmc_dev(x) ((x)->parent) +#define mmc_classdev(x) (&(x)->class_dev) +#define mmc_hostname(x) (x->parent->parent.name) + +void rt_mmc_detect_change(struct rt_mmc_host *, unsigned long delay); +void rt_mmc_request_done(struct rt_mmc_host *, struct rt_mmcsd_req *); +void mmc_command_done(struct rt_mmc_host *host, struct rt_mmcsd_req *mrq); + +void mmc_cqe_request_done(struct rt_mmc_host *host, struct rt_mmcsd_req *mrq); + +static inline rt_bool_t sdio_irq_claimed(struct rt_mmc_host *host) +{ + return host->sdio_irqs > 0; +} + +static inline int mmc_regulator_set_ocr(struct rt_mmc_host *mmc, + struct regulator *supply, + unsigned short vdd_bit) +{ + return 0; +} + +int mmc_regulator_get_supply(struct rt_mmc_host *mmc); +int mmc_regulator_enable_vqmmc(struct rt_mmc_host *mmc); +void mmc_regulator_disable_vqmmc(struct rt_mmc_host *mmc); + +void mmc_retune_timer_stop(struct rt_mmc_host* host); + +enum dma_data_direction +{ + DMA_BIDIRECTIONAL = 0, + DMA_TO_DEVICE = 1, + DMA_FROM_DEVICE = 2, + DMA_NONE = 3, +}; +#define DMA_BIT_MASK(n) (((n) == 64) ? ~0ULL : ((1ULL << (n)) - 1)) +static inline void mmc_retune_needed(struct rt_mmc_host *host) +{ + if (host->can_retune) + host->need_retune = 1; +} + +static inline rt_bool_t mmc_can_retune(struct rt_mmc_host *host) +{ + return host->can_retune == 1; +} + +static inline rt_bool_t mmc_doing_retune(struct rt_mmc_host *host) +{ + return host->doing_retune == 1; +} + +static inline rt_bool_t mmc_doing_tune(struct rt_mmc_host *host) +{ + return host->doing_retune == 1 || host->doing_init_tune == 1; +} + +static inline int mmc_get_dma_dir(struct rt_mmcsd_data *data) +{ + return data->flags & DATA_DIR_WRITE ? DMA_TO_DEVICE : DMA_FROM_DEVICE; +} + +static inline rt_bool_t mmc_op_multi(rt_uint32_t opcode) +{ + return opcode == MMC_WRITE_MULTIPLE_BLOCK || opcode == MMC_READ_MULTIPLE_BLOCK; +} + +static inline rt_bool_t mmc_op_tuning(rt_uint32_t opcode) +{ + return opcode == MMC_SEND_TUNING_BLOCK || opcode == MMC_SEND_TUNING_BLOCK_HS200; +} + +int rt_mmc_gpio_get_cd(struct rt_mmc_host *host); +void rt_mmc_detect_change(struct rt_mmc_host *host, unsigned long delay); +int rt_mmc_regulator_set_vqmmc(struct rt_mmc_host *mmc, struct rt_mmcsd_io_cfg *ios); +rt_bool_t rt_mmc_can_gpio_ro(struct rt_mmc_host *host); +int rt_mmc_gpio_get_ro(struct rt_mmc_host *host); + +int rt_mmc_send_abort_tuning(struct rt_mmc_host *host, rt_uint32_t opcode); +int rt_mmc_of_parse(struct rt_mmc_host *host); + + +#endif diff --git a/components/drivers/sdio/sdhci/include/sdhci_misc.h b/components/drivers/sdio/sdhci/include/sdhci_misc.h new file mode 100644 index 00000000000..46144581c4f --- /dev/null +++ b/components/drivers/sdio/sdhci/include/sdhci_misc.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-08-16 zhujiale first version + */ +#ifndef __RT_SDHCI_MISC_H__ +#define __RT_SDHCI_MISC_H__ + + +#define __BF_FIELD_CHECK(...) +#define __bf_shf(x) (__builtin_ffsll(x) - 1) +#define FIELD_GET(_mask, _reg) \ + ({ \ + __BF_FIELD_CHECK(_mask, _reg, 0U, "FIELD_GET: "); \ + (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \ + }) + +#define FIELD_PREP(_mask, _val) \ + ({ \ + __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: "); \ + ((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask); \ + }) + +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) + +#define min_t(type, x, y) (((type)x < (type)y) ? x : y) +#define max_t(type, x, y) (((type)x > (type)y) ? x : y) +#define min(x, y) ((x) < (y) ? (x) : (y)) + +#define from_timer(var, callback_timer, timer_fieldname) \ + container_of(callback_timer, typeof(*var), timer_fieldname) + + +#define le32_to_cpu(x) (x) +#define le16_to_cpu(x) (x) +#define cpu_to_le16(x) (x) +#define cpu_to_le32(x) (x) +#define lower_32_bits(n) ((rt_uint32_t)((n) & 0xffffffff)) +#define upper_32_bits(n) ((rt_uint32_t)(((n) >> 16) >> 16)) + +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) + +#define do_div(n, base) ({ \ + uint32_t __base = (base); \ + uint32_t __rem; \ + __rem = ((uint64_t)(n)) % __base; \ + (n) = ((uint64_t)(n)) / __base; \ + __rem; \ +}) + +#define fallthrough \ + do { \ + } while (0) + +int regulator_is_supported_voltage(struct regulator *regulator, + int min_uV, int max_uV); +rt_bool_t rt_mmc_can_gpio_cd(struct rt_mmc_host *host); + +struct regulator +{ + const char *supply_name; +}; + +int regulator_get_current_limit(struct regulator *regulator); + +#endif diff --git a/components/drivers/sdio/sdhci/sdhci-platform.c b/components/drivers/sdio/sdhci/sdhci-platform.c new file mode 100644 index 00000000000..d6cf4ed2a35 --- /dev/null +++ b/components/drivers/sdio/sdhci/sdhci-platform.c @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-08-16 zhujiale first version + */ +#include "sdhci-platform.h" + +static const struct rt_sdhci_ops sdhci_pltfm_ops = { + .set_clock = rt_sdhci_set_clock, + .set_bus_width = rt_sdhci_set_bus_width, + .reset = rt_sdhci_reset, + .set_uhs_signaling = rt_sdhci_set_uhs, +}; + +void rt_sdhci_get_property(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct rt_sdhci_host *host = pdev->priv; + struct rt_sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + rt_uint32_t bus_width; + + if (rt_dm_dev_prop_read_bool(dev, "sdhci,auto-cmd12")) + host->quirks |= RT_SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12; + + if (rt_dm_dev_prop_read_bool(dev, "sdhci,1-bit-only") || (rt_dm_dev_prop_read_u32(dev, "bus-width", &bus_width) == 0 && bus_width == 1)) + host->quirks |= RT_SDHCI_QUIRK_FORCE_1_BIT_DATA; + + if (rt_dm_dev_prop_read_bool(dev, "broken-cd")) + host->quirks |= RT_SDHCI_QUIRK_BROKEN_CARD_DETECTION; + + if (rt_dm_dev_prop_read_bool(dev, "no-1-8-v")) + host->quirks2 |= RT_SDHCI_QUIRK2_NO_1_8_V; + + rt_dm_dev_prop_read_u32(dev, "clock-frequency", &pltfm_host->clock); + + if (rt_dm_dev_prop_read_bool(dev, "keep-power-in-suspend")) + host->mmc->pm_caps |= MMC_PM_KEEP_POWER; + + if (rt_dm_dev_prop_read_bool(dev, "wakeup-source") || rt_dm_dev_prop_read_bool(dev, "enable-sdio-wakeup")) /* legacy */ + host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; +} + +struct rt_sdhci_host *rt_sdhci_pltfm_init(struct rt_platform_device *pdev, + const struct rt_sdhci_pltfm_data *pdata, + size_t priv_size) +{ + struct rt_sdhci_host *host; + struct rt_device *dev = &pdev->parent; + void *ioaddr; + int irq; + + ioaddr = rt_dm_dev_iomap(dev, 0); + if (!ioaddr) + { + return RT_NULL; + } + + irq = rt_dm_dev_get_irq(dev, 0); + if (irq < 0) + { + return RT_NULL; + } + host = rt_sdhci_alloc_host(dev,sizeof(struct rt_sdhci_pltfm_host) + priv_size); + if (!host) + { + return RT_NULL; + } + host->irq = irq; + host->ioaddr = ioaddr; + host->hw_name = rt_dm_dev_get_name(dev); + + if (pdata && pdata->ops) + host->ops = pdata->ops; + else + host->ops = &sdhci_pltfm_ops; + if (pdata) + { + host->quirks = pdata->quirks; + host->quirks2 = pdata->quirks2; + } + + pdev->priv = host; + + return host; +} + +int rt_sdhci_pltfm_init_and_add_host(struct rt_platform_device *pdev, + const struct rt_sdhci_pltfm_data *pdata, + size_t priv_size) +{ + struct rt_sdhci_host *host; + int ret = 0; + + host = rt_sdhci_pltfm_init(pdev, pdata, priv_size); + if (!host) + return -RT_ERROR; + + rt_sdhci_get_property(pdev); + + ret = rt_sdhci_init_host(host); + if (ret) + rt_sdhci_pltfm_free(pdev); + + return ret; +} + +void rt_sdhci_pltfm_free(struct rt_platform_device *pdev) +{ + struct rt_sdhci_host *host = pdev->priv; + + rt_sdhci_free_host(host); +} + +void rt_sdhci_pltfm_remove(struct rt_platform_device *pdev) +{ + struct rt_sdhci_host *host = pdev->priv; + int dead = (readl(host->ioaddr + RT_SDHCI_INT_STATUS) == 0xffffffff); + + rt_sdhci_uninit_host(host, dead); + rt_sdhci_pltfm_free(pdev); +} diff --git a/components/drivers/sdio/sdhci/sdhci.c b/components/drivers/sdio/sdhci/sdhci.c new file mode 100644 index 00000000000..6f52b5e9914 --- /dev/null +++ b/components/drivers/sdio/sdhci/sdhci.c @@ -0,0 +1,3152 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-08-16 zhujiale first version + */ + +#include +#include +#include "sdhci.h" +#include +#define DBG_TAG "RT_SDHCI" +#ifdef DRV_DEBUG +#define DBG_LVL DBG_LOG +#else +#define DBG_LVL DBG_INFO +#endif /* DRV_DEBUG */ +#include +static unsigned int debug_quirks = 0; +static unsigned int debug_quirks2; +/********************************************************* */ +/* cmd */ +/********************************************************* */ + + +void rt_read_reg_debug(struct rt_sdhci_host *host) +{ + rt_kprintf("0x00 addddddddddddd = %x \n", rt_sdhci_readl(host, 0x00)); + rt_kprintf("0x04 EMMC_BLOCKSIZE = %x \n", rt_sdhci_readw(host, 0x04)); + rt_kprintf("0x06 EMMC_BLOCKCOUNT = %x \n", rt_sdhci_readw(host, 0x06)); + rt_kprintf("0x08 RT_SDHCI_ARGUMENT = %x \n", rt_sdhci_readl(host, 0x08)); + rt_kprintf("0x0c EMMC_XFER_MODE = %x \n", rt_sdhci_readw(host, 0x0c)); + rt_kprintf("0x0e RT_SDHCI_COMMAND = %x \n", rt_sdhci_readw(host, 0x0e)); + rt_kprintf("0x24 RT_SDHCI_PRESENT_STATE = %x \n", rt_sdhci_readl(host, 0x24)); + rt_kprintf("0x28 RT_SDHCI_HOST_CONTROL = %x \n", rt_sdhci_readb(host, 0x28)); + rt_kprintf("0x29 RT_SDHCI_POWER_CONTROL = %x \n", rt_sdhci_readb(host, 0x29)); + rt_kprintf("0x2a EMMC_BGAP_CTRL = %x \n", rt_sdhci_readb(host, 0x2a)); + rt_kprintf("0x2c EMMC_CLK_CTRL = %x \n", rt_sdhci_readw(host, 0x2c)); + rt_kprintf("0x2e EMMC_TOUT_CTRL = %x \n", rt_sdhci_readb(host, 0x2e)); + rt_kprintf("0x2f EMMC_SW_RST = %x \n", rt_sdhci_readb(host, 0x2f)); + rt_kprintf("0x30 RT_SDHCI_INT_STATUS = %x \n", rt_sdhci_readw(host, 0x30)); + rt_kprintf("0x32 RT_SDHCI_ERR_INT_STATUS = %x \n", rt_sdhci_readw(host, 0x32)); + rt_kprintf("0x34 RT_SDHCI_INT_ENABLE = %x \n", rt_sdhci_readw(host, 0x34)); + rt_kprintf("0x36 EMMC ERROR INT STATEN = %x \n", rt_sdhci_readw(host, 0x36)); + rt_kprintf("0x38 EMMC NORMAL INT SIGNAL EN = %x \n", rt_sdhci_readw(host, 0x38)); + rt_kprintf("0x3a EMMC ERROR INT SIGNAL EN = %x \n", rt_sdhci_readw(host, 0x3a)); + rt_kprintf("0x3c EMMC_AUTO_CMD_STAT = %x \n", rt_sdhci_readw(host, 0x3c)); + rt_kprintf("0x3e EMMC_HOST_CTRL2 = %x \n", rt_sdhci_readw(host, 0x3e)); + rt_kprintf("0x40 EMMC_CAPABILITIES1 = %x \n", rt_sdhci_readl(host, 0x40)); + rt_kprintf("0x44 EMMC_CAPABILITIES2 = %x \n", rt_sdhci_readl(host, 0x44)); + rt_kprintf("0x52 EMMC_FORC_ERR_INT_STAT = %x \n", rt_sdhci_readw(host, 0x52)); + rt_kprintf("0x54 EMMC_ADMA_ERR_STAT = %x \n", rt_sdhci_readb(host, 0x54)); + rt_kprintf("0x58 EMMC_ADMA_SA = %x \n", rt_sdhci_readl(host, 0x58)); + rt_kprintf("0x66 EMMC_PRESET_SDR12 = %x \n", rt_sdhci_readw(host, 0x66)); + rt_kprintf("0x68 EMMC_PRESET_SDR25 = %x \n", rt_sdhci_readw(host, 0x68)); + rt_kprintf("0x6a EMMC_PRESET_SDR50 = %x \n", rt_sdhci_readw(host, 0x6a)); + rt_kprintf("0x6c EMMC_PRESET_SDR104 = %x \n", rt_sdhci_readw(host, 0x6c)); + rt_kprintf("0x6e EMMC_PRESET_DDR50 = %x \n", rt_sdhci_readw(host, 0x6e)); + rt_kprintf("0x78 EMMC_ADMA_ID = %x \n", rt_sdhci_readl(host, 0x78)); + rt_kprintf("0xfe EMMC_HOST_CNTRL_VERS = %x \n", rt_sdhci_readw(host, 0xfe)); + +} +static inline rt_bool_t sdhci_has_requests(struct rt_sdhci_host *host) +{ + return host->cmd || host->data_cmd; +} + +static inline rt_bool_t sdhci_auto_cmd23(struct rt_sdhci_host *host, + struct rt_mmcsd_req *mrq) +{ + return mrq->sbc && (host->flags & RT_SDHCI_AUTO_CMD23); +} + +static inline rt_bool_t sdhci_auto_cmd12(struct rt_sdhci_host *host, + struct rt_mmcsd_req *mrq) +{ + return !mrq->sbc && (host->flags & RT_SDHCI_AUTO_CMD12) && !mrq->cap_cmd_during_tfr; +} + +static inline rt_bool_t sdhci_manual_cmd23(struct rt_sdhci_host *host, + struct rt_mmcsd_req *mrq) +{ + return mrq->sbc && !(host->flags & RT_SDHCI_AUTO_CMD23); +} + +static inline rt_bool_t sdhci_data_line_cmd(struct rt_mmcsd_cmd *cmd) +{ + return cmd->data || cmd->flags & MMC_RSP_BUSY; +} + +void rt_sdhci_data_irq_timeout(struct rt_sdhci_host *host, rt_bool_t enable) +{ + if (enable) + host->ier |= RT_SDHCI_INT_DATA_TIMEOUT; + else + host->ier &= ~RT_SDHCI_INT_DATA_TIMEOUT; + rt_sdhci_writel(host, host->ier, RT_SDHCI_INT_ENABLE); + rt_sdhci_writel(host, host->ier, RT_SDHCI_SIGNAL_ENABLE); +} + +void rt_sdhci_set_uhs(struct rt_sdhci_host *host, unsigned timing) +{ + rt_uint16_t ctrl_2; + + ctrl_2 = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2); + + ctrl_2 &= ~RT_SDHCI_CTRL_UHS_MASK; + if ((timing == MMC_TIMING_MMC_HS200) || (timing == MMC_TIMING_UHS_SDR104)) + ctrl_2 |= RT_SDHCI_CTRL_UHS_SDR104; + else if (timing == MMC_TIMING_UHS_SDR12) + ctrl_2 |= RT_SDHCI_CTRL_UHS_SDR12; + else if (timing == MMC_TIMING_UHS_SDR25) + ctrl_2 |= RT_SDHCI_CTRL_UHS_SDR25; + else if (timing == MMC_TIMING_UHS_SDR50) + ctrl_2 |= RT_SDHCI_CTRL_UHS_SDR50; + else if ((timing == MMC_TIMING_UHS_DDR50) || (timing == MMC_TIMING_MMC_DDR52)) + ctrl_2 |= RT_SDHCI_CTRL_UHS_DDR50; + else if (timing == MMC_TIMING_MMC_HS400) + ctrl_2 |= RT_SDHCI_CTRL_HS400; /* Non-standard */ + rt_sdhci_writew(host, ctrl_2, RT_SDHCI_HOST_CONTROL2); +} + +void rt_sdhci_set_bus_width(struct rt_sdhci_host *host, int width) +{ + rt_uint8_t ctrl; + + ctrl = rt_sdhci_readb(host, RT_SDHCI_HOST_CONTROL); + if (width == MMC_BUS_WIDTH_8) + { + ctrl &= ~RT_SDHCI_CTRL_4BITBUS; + ctrl |= RT_SDHCI_CTRL_8BITBUS; + } + else + { + if (host->mmc->caps & MMC_CAP_8_BIT_DATA) + ctrl &= ~RT_SDHCI_CTRL_8BITBUS; + if (width == MMC_BUS_WIDTH_4) + ctrl |= RT_SDHCI_CTRL_4BITBUS; + else + ctrl &= ~RT_SDHCI_CTRL_4BITBUS; + } + rt_sdhci_writeb(host, ctrl, RT_SDHCI_HOST_CONTROL); +} + +static inline rt_bool_t sdhci_can_64bit_dma(struct rt_sdhci_host *host) +{ + if (host->version >= RT_SDHCI_SPEC_410 && host->v4_mode) + return host->caps & RT_SDHCI_CAN_64BIT_V4; + + return host->caps & RT_SDHCI_CAN_64BIT; +} + +static void sdhci_do_enable_v4_mode(struct rt_sdhci_host *host) +{ + rt_uint16_t ctrl2; + + ctrl2 = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2); + if (ctrl2 & RT_SDHCI_CTRL_V4_MODE) + return; + + ctrl2 |= RT_SDHCI_CTRL_V4_MODE; + rt_sdhci_writew(host, ctrl2, RT_SDHCI_HOST_CONTROL2); +} + +void rt_sdhci_cleanup_host(struct rt_sdhci_host *host) +{ + return; +} + +static void sdhci_set_default_irqs(struct rt_sdhci_host *host) +{ + host->ier = RT_SDHCI_INT_BUS_POWER | RT_SDHCI_INT_DATA_END_BIT | RT_SDHCI_INT_DATA_CRC | RT_SDHCI_INT_DATA_TIMEOUT | RT_SDHCI_INT_INDEX | RT_SDHCI_INT_END_BIT | RT_SDHCI_INT_CRC | RT_SDHCI_INT_TIMEOUT | RT_SDHCI_INT_DATA_END | RT_SDHCI_INT_RESPONSE; + + if (host->tuning_mode == RT_SDHCI_TUNING_MODE_2 || host->tuning_mode == RT_SDHCI_TUNING_MODE_3) + host->ier |= RT_SDHCI_INT_RETUNE; + + rt_sdhci_writel(host, host->ier, RT_SDHCI_INT_ENABLE); + rt_sdhci_writel(host, host->ier, RT_SDHCI_SIGNAL_ENABLE); +} + + +static inline void sdhci_auto_cmd_select(struct rt_sdhci_host *host, + struct rt_mmcsd_cmd *cmd, + rt_uint16_t *mode) +{ + rt_bool_t use_cmd12 = sdhci_auto_cmd12(host, cmd->mrq) && (cmd->cmd_code != SD_IO_RW_EXTENDED); + rt_bool_t use_cmd23 = sdhci_auto_cmd23(host, cmd->mrq); + rt_uint16_t ctrl2; + + if (host->version >= RT_SDHCI_SPEC_410 && host->v4_mode && (use_cmd12 || use_cmd23)) + { + *mode |= RT_SDHCI_TRNS_AUTO_SEL; + + ctrl2 = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2); + if (use_cmd23) + ctrl2 |= RT_SDHCI_CMD23_ENABLE; + else + ctrl2 &= ~RT_SDHCI_CMD23_ENABLE; + rt_sdhci_writew(host, ctrl2, RT_SDHCI_HOST_CONTROL2); + + return; + } + + if (use_cmd12) + *mode |= RT_SDHCI_TRNS_AUTO_CMD12; + else if (use_cmd23) + *mode |= RT_SDHCI_TRNS_AUTO_CMD23; +} + + +static rt_bool_t sdhci_present_error(struct rt_sdhci_host *host, + struct rt_mmcsd_cmd *cmd, rt_bool_t present) +{ + if (!present || host->flags & RT_SDHCI_DEVICE_DEAD) + { + cmd->err = -ENOMEDIUM; + return RT_TRUE; + } + + return RT_FALSE; +} + +static rt_uint16_t sdhci_get_preset_value(struct rt_sdhci_host *host) +{ + rt_uint16_t preset = 0; + + switch (host->timing) + { + case MMC_TIMING_MMC_HS: + case MMC_TIMING_SD_HS: + preset = rt_sdhci_readw(host, RT_SDHCI_PRESET_FOR_HIGH_SPEED); + break; + case MMC_TIMING_UHS_SDR12: + preset = rt_sdhci_readw(host, RT_SDHCI_PRESET_FOR_SDR12); + break; + case MMC_TIMING_UHS_SDR25: + preset = rt_sdhci_readw(host, RT_SDHCI_PRESET_FOR_SDR25); + break; + case MMC_TIMING_UHS_SDR50: + preset = rt_sdhci_readw(host, RT_SDHCI_PRESET_FOR_SDR50); + break; + case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_MMC_HS200: + preset = rt_sdhci_readw(host, RT_SDHCI_PRESET_FOR_SDR104); + break; + case MMC_TIMING_UHS_DDR50: + case MMC_TIMING_MMC_DDR52: + preset = rt_sdhci_readw(host, RT_SDHCI_PRESET_FOR_DDR50); + break; + case MMC_TIMING_MMC_HS400: + preset = rt_sdhci_readw(host, RT_SDHCI_PRESET_FOR_HS400); + break; + default: + preset = rt_sdhci_readw(host, RT_SDHCI_PRESET_FOR_SDR12); + break; + } + return preset; +} + +static void sdhci_set_card_detection(struct rt_sdhci_host *host, rt_bool_t enable) +{ + rt_uint32_t present; + + if ((host->quirks & RT_SDHCI_QUIRK_BROKEN_CARD_DETECTION) || !mmc_card_is_removable(host->mmc)) + return; + + if (enable) + { + present = rt_sdhci_readl(host, RT_SDHCI_PRESENT_STATE) & RT_SDHCI_CARD_PRESENT; + + host->ier |= present ? RT_SDHCI_INT_CARD_REMOVE : RT_SDHCI_INT_CARD_INSERT; + } + else + { + host->ier &= ~(RT_SDHCI_INT_CARD_REMOVE | RT_SDHCI_INT_CARD_INSERT); + } + + rt_sdhci_writel(host, host->ier, RT_SDHCI_INT_ENABLE); + rt_sdhci_writel(host, host->ier, RT_SDHCI_SIGNAL_ENABLE); +} + +static void sdhci_enable_card_detection(struct rt_sdhci_host *host) +{ + sdhci_set_card_detection(host, RT_TRUE); +} + +/********************************************************* */ +/* reset */ +/********************************************************* */ +enum sdhci_reset_reason +{ + RT_SDHCI_RESET_FOR_INIT, + RT_SDHCI_RESET_FOR_REQUEST_ERROR, + RT_SDHCI_RESET_FOR_REQUEST_ERROR_DATA_ONLY, + RT_SDHCI_RESET_FOR_TUNING_ABORT, + RT_SDHCI_RESET_FOR_CARD_REMOVED, + RT_SDHCI_RESET_FOR_CQE_RECOVERY, +}; + +static rt_bool_t sdhci_needs_reset(struct rt_sdhci_host *host, struct rt_mmcsd_req *mrq) +{ + return (!(host->flags & RT_SDHCI_DEVICE_DEAD) && ((mrq->cmd && mrq->cmd->err) || (mrq->sbc && mrq->sbc->err) || (mrq->data && mrq->data->stop && mrq->data->stop->err) || (host->quirks & RT_SDHCI_QUIRK_RESET_AFTER_REQUEST))); +} + +static rt_bool_t sdhci_do_reset(struct rt_sdhci_host *host, rt_uint8_t mask) +{ + if (host->quirks & RT_SDHCI_QUIRK_NO_CARD_NO_RESET) + { + struct rt_mmc_host *mmc = host->mmc; + + if (!mmc->ops->get_cd(mmc)) + return RT_FALSE; + } + if (host->ops->reset) + { + host->ops->reset(host, mask); + } + return RT_TRUE; +} + +static void sdhci_reset_for_reason(struct rt_sdhci_host *host, enum sdhci_reset_reason reason) +{ + if (host->quirks2 & RT_SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER) + { + sdhci_do_reset(host, RT_SDHCI_RESET_CMD | RT_SDHCI_RESET_DATA); + return; + } + + switch (reason) + { + case RT_SDHCI_RESET_FOR_INIT: + sdhci_do_reset(host, RT_SDHCI_RESET_CMD | RT_SDHCI_RESET_DATA); + break; + case RT_SDHCI_RESET_FOR_REQUEST_ERROR: + case RT_SDHCI_RESET_FOR_TUNING_ABORT: + case RT_SDHCI_RESET_FOR_CARD_REMOVED: + case RT_SDHCI_RESET_FOR_CQE_RECOVERY: + sdhci_do_reset(host, RT_SDHCI_RESET_CMD); + sdhci_do_reset(host, RT_SDHCI_RESET_DATA); + break; + case RT_SDHCI_RESET_FOR_REQUEST_ERROR_DATA_ONLY: + sdhci_do_reset(host, RT_SDHCI_RESET_DATA); + break; + } +} + +#define sdhci_reset_for(h, r) sdhci_reset_for_reason((h), RT_SDHCI_RESET_FOR_##r) + +static void sdhci_reset_for_all(struct rt_sdhci_host *host) +{ + if (sdhci_do_reset(host, RT_SDHCI_RESET_ALL)) + { + if (host->flags & (RT_SDHCI_USE_SDMA)) + { + if (host->ops->enable_dma) + host->ops->enable_dma(host); + } + host->preset_enabled = RT_FALSE; + } +} + + +static void sdhci_runtime_pm_bus_on(struct rt_sdhci_host *host) +{ + if (host->bus_on) + return; + host->bus_on = RT_TRUE; +} + +static void sdhci_runtime_pm_bus_off(struct rt_sdhci_host *host) +{ + if (!host->bus_on) + return; + host->bus_on = RT_FALSE; +} + +void rt_sdhci_reset(struct rt_sdhci_host *host, rt_uint8_t mask) +{ + ssize_t timeout; + + rt_sdhci_writeb(host, mask, RT_SDHCI_SOFTWARE_RESET); + + if (mask & RT_SDHCI_RESET_ALL) + { + host->clock = 0; + if (host->quirks2 & RT_SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON) + sdhci_runtime_pm_bus_off(host); + } + + timeout = rt_tick_from_millisecond(150); + while (1) + { + timeout = timeout - rt_tick_get(); + + + if (!(rt_sdhci_readb(host, RT_SDHCI_SOFTWARE_RESET) & mask)) + break; + if (timeout < 0) + { + rt_kprintf("%s: Reset 0x%x never completed.\n", + mmc_hostname(host->mmc), (int)mask); + rt_read_reg_debug(host); + return; + } + rt_hw_us_delay(10); + } +} + +/********************************************************* */ +/* data */ +/********************************************************* */ +static rt_ubase_t sdhci_sdma_address(struct rt_sdhci_host *host) +{ + return (rt_ubase_t)rt_kmem_v2p(host->data->buf); +} + +static void sdhci_set_adma_addr(struct rt_sdhci_host *host, rt_uint32_t addr) +{ + rt_sdhci_writel(host, lower_32_bits(addr), RT_SDHCI_ADMA_ADDRESS); + if (host->flags & RT_SDHCI_USE_64_BIT_DMA) + rt_sdhci_writel(host, upper_32_bits(addr), RT_SDHCI_ADMA_ADDRESS_HI); +} + +static void sdhci_set_sdma_addr(struct rt_sdhci_host *host, rt_uint32_t addr) +{ + if (host->v4_mode) + sdhci_set_adma_addr(host, addr); + else + rt_sdhci_writel(host, addr, RT_SDHCI_DMA_ADDRESS); +} + +static void sdhci_config_dma(struct rt_sdhci_host *host) +{ + rt_uint8_t ctrl; + rt_uint16_t ctrl2; + + if (host->version < RT_SDHCI_SPEC_200) + return; + + ctrl = rt_sdhci_readb(host, RT_SDHCI_HOST_CONTROL); + + ctrl &= ~RT_SDHCI_CTRL_DMA_MASK; + if (!(host->flags & RT_SDHCI_REQ_USE_DMA)) + goto out; + + /* Note if DMA Select is zero then SDMA is selected */ + if (host->flags & RT_SDHCI_USE_64_BIT_DMA) + { + + if (host->v4_mode) + { + ctrl2 = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2); + ctrl2 |= RT_SDHCI_CTRL_64BIT_ADDR; + rt_sdhci_writew(host, ctrl2, RT_SDHCI_HOST_CONTROL2); + } + } + +out: + rt_sdhci_writeb(host, ctrl, RT_SDHCI_HOST_CONTROL); +} + +static inline void sdhci_set_block_info(struct rt_sdhci_host *host, + struct rt_mmcsd_data *data) +{ + int boundary; + size_t total_size = data->blks * data->blksize; + + if (total_size <= 512) + boundary = 0; /* 4k bytes*/ + else if (total_size <= 1024) + boundary = 1; /* 8 KB*/ + else if (total_size <= 2048) + boundary = 2; /* 16 KB*/ + else if (total_size <= 4096) + boundary = 3; /* 32 KB*/ + else if (total_size <= 8192) + boundary = 4; /* 64 KB*/ + else if (total_size <= 16384) + boundary = 5; /* 128 KB*/ + else if (total_size <= 32768) + boundary = 6; /* 256 KB*/ + else + boundary = 7; /* 512 KB*/ + rt_sdhci_writew(host, + RT_SDHCI_MAKE_BLKSZ(boundary, data->blksize), + RT_SDHCI_BLOCK_SIZE); + + if (host->version >= RT_SDHCI_SPEC_410 && host->v4_mode && (host->quirks2 & RT_SDHCI_QUIRK2_USE_32BIT_BLK_CNT)) + { + if (rt_sdhci_readw(host, RT_SDHCI_BLOCK_COUNT)) + rt_sdhci_writew(host, 0, RT_SDHCI_BLOCK_COUNT); + rt_sdhci_writew(host, data->blks, RT_SDHCI_32BIT_BLK_CNT); + } + else + { + rt_sdhci_writew(host, data->blks, RT_SDHCI_BLOCK_COUNT); + } +} + +static void sdhci_set_transfer_irqs(struct rt_sdhci_host *host) +{ + rt_uint32_t pio_irqs = RT_SDHCI_INT_DATA_AVAIL | RT_SDHCI_INT_SPACE_AVAIL; + rt_uint32_t dma_irqs = RT_SDHCI_INT_DMA_END; + + if (host->flags & RT_SDHCI_REQ_USE_DMA) + host->ier = (host->ier & ~pio_irqs) | dma_irqs; + else + host->ier = (host->ier & ~dma_irqs) | pio_irqs; + + if (host->flags & (RT_SDHCI_AUTO_CMD23 | RT_SDHCI_AUTO_CMD12)) + host->ier |= RT_SDHCI_INT_AUTO_CMD_ERR; + else + host->ier &= ~RT_SDHCI_INT_AUTO_CMD_ERR; + rt_sdhci_writel(host, host->ier, RT_SDHCI_INT_ENABLE); + rt_sdhci_writel(host, host->ier, RT_SDHCI_SIGNAL_ENABLE); +} + +static void sdhci_prepare_data(struct rt_sdhci_host *host, struct rt_mmcsd_cmd *cmd) +{ + struct rt_mmcsd_data *data = cmd->data; + + LOG_D(data->blksize * data->blks > 524288); + LOG_D(data->blksize > host->mmc->max_blk_size); + LOG_D(data->blks > 65535); + + host->data = data; + host->data_early = 0; + host->data->bytes_xfered = 0; + + if (host->flags & RT_SDHCI_USE_SDMA) + { + unsigned int length_mask, offset_mask; + + host->flags |= RT_SDHCI_REQ_USE_DMA; + + length_mask = 0; + offset_mask = 0; + if (host->quirks & RT_SDHCI_QUIRK_32BIT_DMA_SIZE) + length_mask = 3; + if (host->quirks & RT_SDHCI_QUIRK_32BIT_DMA_ADDR) + offset_mask = 3; + + if ((data->blks * data->blksize) & length_mask) + { + host->flags &= ~RT_SDHCI_REQ_USE_DMA; + } + else if ((rt_ubase_t)rt_kmem_v2p(data->buf) & offset_mask) + { + host->flags &= ~RT_SDHCI_REQ_USE_DMA; + } + } + + sdhci_config_dma(host); + + if (host->flags & RT_SDHCI_REQ_USE_DMA) + { + if (mmc_get_dma_dir(data) == DMA_FROM_DEVICE) + rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, data->buf, data->blks * data->blksize); + else + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, data->buf, data->blks * data->blksize); + + sdhci_set_sdma_addr(host, sdhci_sdma_address(host)); + } + + if (!(host->flags & RT_SDHCI_REQ_USE_DMA)) + { + host->blocks = data->blks; + } + + sdhci_set_transfer_irqs(host); + + sdhci_set_block_info(host, data); +} + +static void sdhci_set_mrq_done(struct rt_sdhci_host *host, struct rt_mmcsd_req *mrq) +{ + int i; + + for (i = 0; i < RT_SDHCI_MAX_MRQS; i++) + { + if (host->mrqs_done[i] == mrq) + { + LOG_D(1); + return; + } + } + + for (i = 0; i < RT_SDHCI_MAX_MRQS; i++) + { + if (!host->mrqs_done[i]) + { + host->mrqs_done[i] = mrq; + break; + } + } + + LOG_D(i >= RT_SDHCI_MAX_MRQS); +} + +static inline rt_bool_t sdhci_defer_done(struct rt_sdhci_host *host, + struct rt_mmcsd_req *mrq) +{ + struct rt_mmcsd_data *data = mrq->data; + + return host->pending_reset || host->always_defer_done || ((host->flags & RT_SDHCI_REQ_USE_DMA) && data && data->host_cookie == COOKIE_MAPPED); +} + + +/********************************************************* */ +/* pio */ +/********************************************************* */ + +static void rt_sdhci_read_block_pio(struct rt_sdhci_host *host,void **buf) +{ + rt_uint32_t scratch; + size_t len; + + rt_uint32_t blksize = host->data->blksize; + while (blksize) + { + len = min(4U, blksize); + + scratch = rt_sdhci_readl(host, RT_SDHCI_BUFFER); + rt_memcpy(*buf, &scratch, len); + + *buf += len; + blksize -= len; + } +} + +static void rt_sdhci_write_block_pio(struct rt_sdhci_host *host,void **buf) +{ + size_t blksize, len; + rt_uint32_t scratch; + LOG_D("PIO writing\n"); + + blksize = host->data->blksize; + scratch = 0; + + while (blksize) + { + len = min(4U, blksize); + rt_memcpy(&scratch, *buf, len); + *buf += len; + blksize -= len; + rt_sdhci_writel(host, scratch, RT_SDHCI_BUFFER); + } +} + +static void sdhci_transfer_pio(struct rt_sdhci_host *host) +{ + rt_uint32_t mask; + + if (host->blocks == 0) + return; + + if (host->data->flags & DATA_DIR_READ) + mask = RT_SDHCI_DATA_AVAILABLE; + else + mask = RT_SDHCI_SPACE_AVAILABLE; + + if ((host->quirks & RT_SDHCI_QUIRK_BROKEN_SMALL_PIO) && (host->data->blks == 1)) + { + mask = ~0; + } + void *buf = (void *)host->data->buf; + while (rt_sdhci_readl(host, RT_SDHCI_PRESENT_STATE) & mask) + { + if (host->quirks & RT_SDHCI_QUIRK_PIO_NEEDS_DELAY) + rt_hw_us_delay(100); + + if (host->data->flags & DATA_DIR_READ) + rt_sdhci_read_block_pio(host,&buf); + else + rt_sdhci_write_block_pio(host,&buf); + + host->data->blks--; + if (host->data->blks == 0) + break; + } +} + +/********************************************************* */ +/* config */ +/********************************************************* */ + + +static rt_bool_t sdhci_timing_has_preset(unsigned char timing) +{ + switch (timing) + { + case MMC_TIMING_UHS_SDR12: + case MMC_TIMING_UHS_SDR25: + case MMC_TIMING_UHS_SDR50: + case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_UHS_DDR50: + case MMC_TIMING_MMC_DDR52: + return RT_TRUE; + } + return RT_FALSE; +} + +static rt_bool_t sdhci_preset_needed(struct rt_sdhci_host *host, unsigned char timing) +{ + return !(host->quirks2 & RT_SDHCI_QUIRK2_PRESET_VALUE_BROKEN) && sdhci_timing_has_preset(timing); +} + +static rt_bool_t sdhci_presetable_values_change(struct rt_sdhci_host *host, struct rt_mmcsd_io_cfg *ios) +{ + return !host->preset_enabled && (sdhci_preset_needed(host, ios->timing) || host->drv_type != ios->drv_type); +} + + +static void sdhci_preset_value_enable(struct rt_sdhci_host *host, rt_bool_t enable) +{ + if (host->version < RT_SDHCI_SPEC_300) + return; + + if (host->preset_enabled != enable) + { + rt_uint16_t ctrl = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2); + + if (enable) + ctrl |= RT_SDHCI_CTRL_PRESET_VAL_ENABLE; + else + ctrl &= ~RT_SDHCI_CTRL_PRESET_VAL_ENABLE; + + rt_sdhci_writew(host, ctrl, RT_SDHCI_HOST_CONTROL2); + + if (enable) + host->flags |= RT_SDHCI_PV_ENABLED; + else + host->flags &= ~RT_SDHCI_PV_ENABLED; + + host->preset_enabled = enable; + } +} + +static void sdhci_set_power_reg(struct rt_sdhci_host *host, unsigned char mode, + unsigned short vdd) +{ + struct rt_mmc_host *mmc = host->mmc; + + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); + + if (mode != MMC_POWER_OFF) + rt_sdhci_writeb(host, RT_SDHCI_POWER_ON, RT_SDHCI_POWER_CONTROL); + else + rt_sdhci_writeb(host, 0, RT_SDHCI_POWER_CONTROL); +} + +void rt_sdhci_set_power_with_noreg(struct rt_sdhci_host *host, unsigned char mode, + unsigned short vdd) +{ + rt_uint8_t pwr = 0; + + if (mode != MMC_POWER_OFF) + { + switch (1 << vdd) + { + case MMC_VDD_165_195: + case MMC_VDD_20_21: + pwr = RT_SDHCI_POWER_180; + break; + case MMC_VDD_29_30: + case MMC_VDD_30_31: + pwr = RT_SDHCI_POWER_300; + break; + case MMC_VDD_32_33: + case MMC_VDD_33_34: + case MMC_VDD_34_35: + case MMC_VDD_35_36: + pwr = RT_SDHCI_POWER_330; + break; + default: + break; + } + } + + if (host->pwr == pwr) + return; + + host->pwr = pwr; + + if (pwr == 0) + { + rt_sdhci_writeb(host, 0, RT_SDHCI_POWER_CONTROL); + if (host->quirks2 & RT_SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON) + sdhci_runtime_pm_bus_off(host); + } + else + { + if (!(host->quirks & RT_SDHCI_QUIRK_SINGLE_POWER_WRITE)) + rt_sdhci_writeb(host, 0, RT_SDHCI_POWER_CONTROL); + + if (host->quirks & RT_SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER) + rt_sdhci_writeb(host, pwr, RT_SDHCI_POWER_CONTROL); + + pwr |= RT_SDHCI_POWER_ON; + + rt_sdhci_writeb(host, pwr, RT_SDHCI_POWER_CONTROL); + + if (host->quirks2 & RT_SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON) + sdhci_runtime_pm_bus_on(host); + + if (host->quirks & RT_SDHCI_QUIRK_DELAY_AFTER_POWER) + rt_thread_mdelay(10); + } +} + +void rt_sdhci_set_power(struct rt_sdhci_host *host, unsigned char mode, + unsigned short vdd) +{ + if (!host->mmc->supply.vmmc) + rt_sdhci_set_power_with_noreg(host, mode, vdd); + else + sdhci_set_power_reg(host, mode, vdd); +} + + +int rt_sdhci_start_signal_voltage_switch(struct rt_mmc_host *mmc, + struct rt_mmcsd_io_cfg *ios) +{ + struct rt_sdhci_host *host = mmc_priv(mmc); + rt_uint16_t ctrl; + int ret; + + if (host->version < RT_SDHCI_SPEC_300) + return 0; + + ctrl = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2); + + switch (ios->signal_voltage) + { + case MMC_SIGNAL_VOLTAGE_330: + if (!(host->flags & RT_SDHCI_SIGNALING_330)) + return -EINVAL; + ctrl &= ~RT_SDHCI_CTRL_VDD_180; + rt_sdhci_writew(host, ctrl, RT_SDHCI_HOST_CONTROL2); + + if (!mmc->supply.vqmmc) + { + ret = rt_mmc_regulator_set_vqmmc(mmc, ios); + if (ret < 0) + { + return -EIO; + } + } + rt_thread_mdelay(5); + + ctrl = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2); + if (!(ctrl & RT_SDHCI_CTRL_VDD_180)) + return 0; + return -EAGAIN; + case MMC_SIGNAL_VOLTAGE_180: + if (!(host->flags & RT_SDHCI_SIGNALING_180)) + return -EINVAL; + if (!mmc->supply.vqmmc) + { + ret = rt_mmc_regulator_set_vqmmc(mmc, ios); + if (ret < 0) + { + LOG_D("%s: Switching to 1.8V signalling voltage failed\n", + mmc_hostname(mmc)); + return -EIO; + } + } + + ctrl |= RT_SDHCI_CTRL_VDD_180; + rt_sdhci_writew(host, ctrl, RT_SDHCI_HOST_CONTROL2); + + if (host->ops->voltage_switch) + host->ops->voltage_switch(host); + + ctrl = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2); + if (ctrl & RT_SDHCI_CTRL_VDD_180) + return 0; + + LOG_D("%s: 1.8V regulator output did not become stable\n", + mmc_hostname(mmc)); + + return -EAGAIN; + case MMC_SIGNAL_VOLTAGE_120: + if (!(host->flags & RT_SDHCI_SIGNALING_120)) + return -EINVAL; + if (!mmc->supply.vqmmc) + { + ret = rt_mmc_regulator_set_vqmmc(mmc, ios); + if (ret < 0) + { + LOG_D("%s: Switching to 1.2V signalling voltage failed\n", + mmc_hostname(mmc)); + return -EIO; + } + } + return 0; + default: + return 0; + } +} + + +static int sdhci_get_cd(struct rt_mmc_host *mmc) +{ + struct rt_sdhci_host *host = mmc_priv(mmc); + int gpio_cd = rt_mmc_gpio_get_cd(mmc); + + if (host->flags & RT_SDHCI_DEVICE_DEAD) + return 0; + + if (!mmc_card_is_removable(mmc)) + return 1; + + if (gpio_cd >= 0) + return !!gpio_cd; + + if (host->quirks & RT_SDHCI_QUIRK_BROKEN_CARD_DETECTION) + return 1; + + return !!(rt_sdhci_readl(host, RT_SDHCI_PRESENT_STATE) & RT_SDHCI_CARD_PRESENT); +} + +static int sdhci_check_ro(struct rt_sdhci_host *host) +{ + int is_readonly; + rt_base_t flags; + flags = rt_spin_lock_irqsave(&host->lock); + + if (host->flags & RT_SDHCI_DEVICE_DEAD) + is_readonly = 0; + else if (host->ops->get_ro) + is_readonly = host->ops->get_ro(host); + else if (rt_mmc_can_gpio_ro(host->mmc)) + is_readonly = rt_mmc_gpio_get_ro(host->mmc); + else + is_readonly = !(rt_sdhci_readl(host, RT_SDHCI_PRESENT_STATE) + & RT_SDHCI_WRITE_PROTECT); + + rt_spin_unlock_irqrestore(&host->lock, flags); + + return host->quirks & RT_SDHCI_QUIRK_INVERTED_WRITE_PROTECT ? !is_readonly : is_readonly; +} + +#define SAMPLE_COUNT 5 +static int rt_sdhci_ro_get(struct rt_mmc_host *mmc) +{ + struct rt_sdhci_host *host = mmc_priv(mmc); + int i, ro_count; + + if (!(host->quirks & RT_SDHCI_QUIRK_UNSTABLE_RO_DETECT)) + return sdhci_check_ro(host); + + ro_count = 0; + for (i = 0; i < SAMPLE_COUNT; i++) + { + if (sdhci_check_ro(host)) + { + if (++ro_count > SAMPLE_COUNT / 2) + return 1; + } + rt_thread_mdelay(30); + } + return 0; +} + +static void rt_sdhci_enable_io_irq_nolock(struct rt_sdhci_host *host, int enable) +{ + if (!(host->flags & RT_SDHCI_DEVICE_DEAD)) + { + if (enable) + host->ier |= RT_SDHCI_INT_CARD_INT; + else + host->ier &= ~RT_SDHCI_INT_CARD_INT; + + rt_sdhci_writel(host, host->ier, RT_SDHCI_INT_ENABLE); + rt_sdhci_writel(host, host->ier, RT_SDHCI_SIGNAL_ENABLE); + } +} + +static void sdhci_ack_sdio_irq(struct rt_mmc_host *mmc) +{ + rt_base_t flags; + struct rt_sdhci_host *host = mmc_priv(mmc); + flags = rt_spin_lock_irqsave(&host->lock); + rt_sdhci_enable_io_irq_nolock(host, RT_TRUE); + rt_spin_unlock_irqrestore(&host->lock, flags); +} + +static void sdhci_del_timer(struct rt_sdhci_host *host, struct rt_mmcsd_req *mrq) +{ + if (sdhci_data_line_cmd(mrq->cmd)) + rt_timer_stop(&host->data_timer); + else + rt_timer_stop(&host->timer); +} + +static unsigned int sdhci_target_timeout(struct rt_sdhci_host *host, + struct rt_mmcsd_cmd *cmd, + struct rt_mmcsd_data *data) +{ + unsigned int target_timeout; + + if (!data) + { + target_timeout = cmd->busy_timeout * 1000; + } + else + { + target_timeout = DIV_ROUND_UP(data->timeout_ns, 1000); + if (host->clock && data->timeout_clks) + { + rt_uint32_t val; + + val = 1000000ULL * data->timeout_clks; + if (do_div(val, host->clock)) + target_timeout++; + target_timeout += val; + } + } + + return target_timeout; +} + +static rt_uint8_t sdhci_calc_timeout(struct rt_sdhci_host *host, struct rt_mmcsd_cmd *cmd, + rt_bool_t *too_big) +{ + rt_uint8_t count; + struct rt_mmcsd_data *data; + unsigned target_timeout, current_timeout; + + *too_big = RT_FALSE; + + if (host->quirks & RT_SDHCI_QUIRK_BROKEN_TIMEOUT_VAL) + return host->max_timeout_count; + + if (cmd == NULL) + return host->max_timeout_count; + + data = cmd->data; + if (!data && !cmd->busy_timeout) + return host->max_timeout_count; + + target_timeout = sdhci_target_timeout(host, cmd, data); + + count = 0; + current_timeout = (1 << 13) * 1000 / host->timeout_clk; + while (current_timeout < target_timeout) + { + count++; + current_timeout <<= 1; + if (count > host->max_timeout_count) + { + if (!(host->quirks2 & RT_SDHCI_QUIRK2_DISABLE_HW_TIMEOUT)) + LOG_D("Too large timeout 0x%x requested for CMD%d!\n", + count, cmd->cmd_code); + count = host->max_timeout_count; + *too_big = RT_TRUE; + break; + } + } + + return count; +} + +static void sdhci_calc_sw_timeout(struct rt_sdhci_host *host, + struct rt_mmcsd_cmd *cmd) +{ + struct rt_mmcsd_data *data = cmd->data; + struct rt_mmc_host *mmc = host->mmc; + struct rt_mmcsd_io_cfg *ios = &mmc->ios; + unsigned char bus_width = 1 << ios->bus_width; + unsigned int blksz; + unsigned int freq; + rt_uint64_t target_timeout; + rt_uint64_t transfer_time; + + target_timeout = sdhci_target_timeout(host, cmd, data); + target_timeout *= 1000L; + + if (data) + { + blksz = data->blksize; + freq = mmc->actual_clock ?: host->clock; + transfer_time = (rt_uint64_t)blksz * 1000000000L * (8 / bus_width); + do_div(transfer_time, freq); + transfer_time = transfer_time * 2; + host->data_timeout = data->blks * target_timeout + transfer_time; + } + else + { + host->data_timeout = target_timeout; + } + + if (host->data_timeout) + host->data_timeout += MMC_CMD_TRANSFER_TIME; +} + + +void rt_sdhci_timeout_set(struct rt_sdhci_host *host, struct rt_mmcsd_cmd *cmd) +{ + rt_bool_t too_big = RT_FALSE; + rt_uint8_t count = sdhci_calc_timeout(host, cmd, &too_big); + + if (too_big && host->quirks2 & RT_SDHCI_QUIRK2_DISABLE_HW_TIMEOUT) + { + sdhci_calc_sw_timeout(host, cmd); + rt_sdhci_data_irq_timeout(host, RT_FALSE); + } + else if (!(host->ier & RT_SDHCI_INT_DATA_TIMEOUT)) + { + rt_sdhci_data_irq_timeout(host, RT_FALSE); + } + + rt_sdhci_writeb(host, count, RT_SDHCI_TIMEOUT_CONTROL); +} + +static void sdhci_set_timeout(struct rt_sdhci_host *host, struct rt_mmcsd_cmd *cmd) +{ + if (host->ops->set_timeout) + host->ops->set_timeout(host, cmd); + else + rt_sdhci_timeout_set(host, cmd); +} + +static void sdhci_start_timer(struct rt_sdhci_host *host, struct rt_mmcsd_req *mrq, + unsigned long timeout) +{ + if (sdhci_data_line_cmd(mrq->cmd)) + { + rt_tick_t tick = rt_tick_get(); + + if (timeout < tick) + { + timeout = tick; + } + tick = timeout - tick; + + rt_timer_stop(&host->data_timer); + rt_timer_control(&host->data_timer, RT_TIMER_CTRL_SET_TIME, &tick); + rt_timer_start(&host->data_timer); + } + else + { + rt_tick_t tick = rt_tick_get(); + + if (timeout < tick) + { + timeout = tick; + } + tick = timeout - tick; + + rt_timer_stop(&host->timer); + rt_timer_control(&host->timer, RT_TIMER_CTRL_SET_TIME, &tick); + rt_timer_start(&host->timer); + } +} + +static void __sdhci_finish_mrq(struct rt_sdhci_host *host, struct rt_mmcsd_req *mrq) +{ + if (host->cmd && host->cmd->mrq == mrq) + host->cmd = NULL; + + if (host->data_cmd && host->data_cmd->mrq == mrq) + host->data_cmd = NULL; + + if (host->deferred_cmd && host->deferred_cmd->mrq == mrq) + host->deferred_cmd = NULL; + + if (host->data && host->data->mrq == mrq) + host->data = NULL; + + if (sdhci_needs_reset(host, mrq)) + host->pending_reset = RT_TRUE; + + sdhci_set_mrq_done(host, mrq); + + sdhci_del_timer(host, mrq); +} + +static void sdhci_finish_mrq(struct rt_sdhci_host *host, struct rt_mmcsd_req *mrq) +{ + __sdhci_finish_mrq(host, mrq); + + rt_workqueue_submit_work(host->complete_wq, &host->complete_work, 0); +} + +static void sdhci_error_out_mrqs(struct rt_sdhci_host *host, int err) +{ + if (host->data_cmd) + { + host->data_cmd->err = err; + sdhci_finish_mrq(host, host->data_cmd->mrq); + } + + if (host->cmd) + { + host->cmd->err = err; + sdhci_finish_mrq(host, host->cmd->mrq); + } +} + +static void sdhci_card_event(struct rt_mmc_host *mmc) +{ + struct rt_sdhci_host *host = mmc_priv(mmc); + rt_uint32_t flags; + int present; + + if (host->ops->card_event) + host->ops->card_event(host); + + present = mmc->ops->get_cd(mmc); + + flags = rt_spin_lock_irqsave(&host->lock); + + if (sdhci_has_requests(host) && !present) + { + rt_kprintf("%s: Card removed during transfer!\n", + mmc_hostname(mmc)); + rt_kprintf("%s: Resetting controller.\n", + mmc_hostname(mmc)); + + sdhci_do_reset(host, RT_SDHCI_RESET_CMD); + sdhci_do_reset(host, RT_SDHCI_RESET_DATA); + sdhci_error_out_mrqs(host, -ENOMEDIUM); + } + + rt_spin_unlock_irqrestore(&host->lock, flags); +} + +static int sdhci_card_busy(struct rt_mmc_host *mmc) +{ + struct rt_sdhci_host *host = mmc_priv(mmc); + rt_uint32_t present_state; + + present_state = rt_sdhci_readl(host, RT_SDHCI_PRESENT_STATE); + + return !(present_state & RT_SDHCI_DATA_0_LVL_MASK); +} + + +static int sdhci_prepare_hs400_tuning(struct rt_mmc_host *mmc, struct rt_mmcsd_io_cfg *ios) +{ + struct rt_sdhci_host *host = mmc_priv(mmc); + rt_uint32_t flags; + + flags = rt_spin_lock_irqsave(&host->lock); + host->flags |= RT_SDHCI_HS400_TUNING; + rt_spin_unlock_irqrestore(&host->lock, flags); + + return 0; +} + + +static void sdhci_set_transfer_mode(struct rt_sdhci_host *host, + struct rt_mmcsd_cmd *cmd) +{ + rt_uint16_t mode = 0; + struct rt_mmcsd_data *data = cmd->data; + + if (data == NULL) + { + if (host->quirks2 & RT_SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD) + { + if (!mmc_op_tuning(cmd->cmd_code)) + rt_sdhci_writew(host, 0x0, RT_SDHCI_TRANSFER_MODE); + } + else + { + mode = rt_sdhci_readw(host, RT_SDHCI_TRANSFER_MODE); + rt_sdhci_writew(host, mode & ~(RT_SDHCI_TRNS_AUTO_CMD12 | RT_SDHCI_TRNS_AUTO_CMD23), RT_SDHCI_TRANSFER_MODE); + } + return; + } + + if (!(host->quirks2 & RT_SDHCI_QUIRK2_SUPPORT_SINGLE)) + mode = RT_SDHCI_TRNS_BLK_CNT_EN; + + if (mmc_op_multi(cmd->cmd_code) || data->blks > 1) + { + mode = RT_SDHCI_TRNS_BLK_CNT_EN | RT_SDHCI_TRNS_MULTI; + sdhci_auto_cmd_select(host, cmd, &mode); + if (sdhci_auto_cmd23(host, cmd->mrq)) + rt_sdhci_writel(host, cmd->mrq->sbc->arg, RT_SDHCI_ARGUMENT2); + } + + if (data->flags & DATA_DIR_READ) + mode |= RT_SDHCI_TRNS_READ; + if (host->flags & RT_SDHCI_REQ_USE_DMA) + mode |= RT_SDHCI_TRNS_DMA; + + rt_sdhci_writew(host, mode, RT_SDHCI_TRANSFER_MODE); +} + +static rt_bool_t sdhci_send_command(struct rt_sdhci_host *host, struct rt_mmcsd_cmd *cmd) +{ + int flags; + rt_uint32_t mask; + unsigned long timeout; + cmd->err = 0; + + if ((host->quirks2 & RT_SDHCI_QUIRK2_STOP_WITH_TC) && cmd->cmd_code == MMC_STOP_TRANSMISSION) + cmd->flags |= MMC_RSP_BUSY; + + mask = RT_SDHCI_CMD_INHIBIT; + if (sdhci_data_line_cmd(cmd)) + mask |= RT_SDHCI_DATA_INHIBIT; + + if (cmd->mrq->data && (cmd == cmd->mrq->data->stop)) + mask &= ~RT_SDHCI_DATA_INHIBIT; + + if (rt_sdhci_readl(host, RT_SDHCI_PRESENT_STATE) & mask) + return RT_FALSE; + + host->cmd = cmd; + host->data_timeout = 0; + if (sdhci_data_line_cmd(cmd)) + { + host->data_cmd = cmd; + sdhci_set_timeout(host, cmd); + } + + if (cmd->data) + { + sdhci_prepare_data(host, cmd); + } + rt_sdhci_writel(host, cmd->arg, RT_SDHCI_ARGUMENT); + + sdhci_set_transfer_mode(host, cmd); + + if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) + { + cmd->flags &= ~MMC_RSP_BUSY; + } + + if (!(cmd->flags & MMC_RSP_PRESENT)) + flags = RT_SDHCI_CMD_RESP_NONE; + else if (cmd->flags & MMC_RSP_136) + flags = RT_SDHCI_CMD_RESP_LONG; + else if (cmd->flags & MMC_RSP_BUSY) + flags = RT_SDHCI_CMD_RESP_SHORT_BUSY; + else + flags = RT_SDHCI_CMD_RESP_SHORT; + + if (cmd->flags & MMC_RSP_CRC) + flags |= RT_SDHCI_CMD_CRC; + if (cmd->flags & MMC_RSP_OPCODE) + flags |= RT_SDHCI_CMD_INDEX; + + if (cmd->data || mmc_op_tuning(cmd->cmd_code)) + flags |= RT_SDHCI_CMD_DATA; + + timeout = rt_tick_get(); + if (host->data_timeout) + timeout += rt_tick_from_millisecond(host->data_timeout * 1000); + else if (!cmd->data && cmd->busy_timeout > 9000) + timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * RT_TICK_PER_SECOND + RT_TICK_PER_SECOND; + else + timeout += 10 * RT_TICK_PER_SECOND; + sdhci_start_timer(host, cmd->mrq, timeout); + + rt_sdhci_writew(host, RT_SDHCI_MAKE_CMD(cmd->cmd_code, flags), RT_SDHCI_COMMAND); + return RT_TRUE; +} + +/********************************************************* */ +/* dma */ +/********************************************************* */ +static void __sdhci_finish_data(struct rt_sdhci_host *host, rt_bool_t sw_data_timeout) +{ + struct rt_mmcsd_cmd *data_cmd = host->data_cmd; + struct rt_mmcsd_data *data = host->data; + + host->data = NULL; + host->data_cmd = NULL; + + if (data->err) + { + if (!host->cmd || host->cmd == data_cmd) + sdhci_reset_for(host, REQUEST_ERROR); + else + sdhci_reset_for(host, REQUEST_ERROR_DATA_ONLY); + } + + if (data->err) + { + data->bytes_xfered = 0; + } + else + { + data->bytes_xfered = data->blksize * data->blks; + } + + if (data->stop && ((!data->mrq->sbc && !sdhci_auto_cmd12(host, data->mrq)) || data->err)) + { + if (data->mrq->cap_cmd_during_tfr) + { + __sdhci_finish_mrq(host, data->mrq); + } + else + { + host->cmd = NULL; + if (!sdhci_send_command(host, data->stop)) + { + if (sw_data_timeout) + { + data->stop->err = -EIO; + __sdhci_finish_mrq(host, data->mrq); + } + else + { + host->deferred_cmd = data->stop; + } + } + } + } + else + { + __sdhci_finish_mrq(host, data->mrq); + } +} + +static void sdhci_finish_data(struct rt_sdhci_host *host) +{ + __sdhci_finish_data(host, RT_FALSE); +} + + +/********************************************************* */ +/* irq */ +/********************************************************* */ +static void sdhci_data_irq(struct rt_sdhci_host *host, rt_uint32_t intmask) +{ + rt_uint32_t command; + + if (intmask & RT_SDHCI_INT_DATA_AVAIL && !host->data) + { + command = RT_SDHCI_GET_CMD(rt_sdhci_readw(host, RT_SDHCI_COMMAND)); + if (command == MMC_SEND_TUNING_BLOCK || command == MMC_SEND_TUNING_BLOCK_HS200) + { + host->tuning_done = 1; + rt_wqueue_wakeup(&host->buf_ready_int, 0); + return; + } + } + + if (!host->data) + { + struct rt_mmcsd_cmd *data_cmd = host->data_cmd; + + if (data_cmd && (data_cmd->flags & MMC_RSP_BUSY)) + { + if (intmask & RT_SDHCI_INT_DATA_TIMEOUT) + { + host->data_cmd = NULL; + data_cmd->err = -ETIMEDOUT; + __sdhci_finish_mrq(host, data_cmd->mrq); + return; + } + if (intmask & RT_SDHCI_INT_DATA_END) + { + host->data_cmd = NULL; + + if (host->cmd == data_cmd) + return; + + __sdhci_finish_mrq(host, data_cmd->mrq); + return; + } + } + + + if (host->pending_reset) + return; + rt_kprintf("%s: Got data interrupt 0x%08x even though no data operation was in progress.\n", + mmc_hostname(host->mmc), (unsigned)intmask); + rt_read_reg_debug(host); + + return; + } + + if (intmask & RT_SDHCI_INT_DATA_TIMEOUT) + host->data->err = -ETIMEDOUT; + else if (intmask & RT_SDHCI_INT_DATA_END_BIT) + host->data->err = -EILSEQ; + else if ((intmask & RT_SDHCI_INT_DATA_CRC) && RT_SDHCI_GET_CMD(rt_sdhci_readw(host, RT_SDHCI_COMMAND)) != MMC_BUS_TEST_R) + { + host->data->err = -EILSEQ; + } + + + if (host->data->err) + { + sdhci_finish_data(host); + } + else + { + if (intmask & (RT_SDHCI_INT_DATA_AVAIL | RT_SDHCI_INT_SPACE_AVAIL)) + sdhci_transfer_pio(host); + + if (intmask & RT_SDHCI_INT_DMA_END) + { + rt_uint32_t dmastart, dmanow; + + dmastart = sdhci_sdma_address(host); + dmanow = dmastart + host->data->bytes_xfered; + dmanow = (dmanow & ~((rt_uint32_t)RT_SDHCI_DEFAULT_BOUNDARY_SIZE - 1)) + RT_SDHCI_DEFAULT_BOUNDARY_SIZE; + host->data->bytes_xfered = dmanow - dmastart; + LOG_D("DMA base %pad, transferred 0x%06x bytes, next %pad\n", + &dmastart, host->data->bytes_xfered, &dmanow); + sdhci_set_sdma_addr(host, dmanow); + } + + if (intmask & RT_SDHCI_INT_DATA_END) + { + struct rt_mmcsd_data *data = host->data; + if (data->buf) + { + if (mmc_get_dma_dir(data) == DMA_FROM_DEVICE) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, data->buf, data->blks * data->blksize); + } else { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, data->buf, data->blks * data->blksize); + } + } + if (host->cmd == host->data_cmd) + { + host->data_early = 1; + } + else + { + sdhci_finish_data(host); + } + } + } +} + +static void rt_sdhci_read_rsp_136(struct rt_sdhci_host *host, struct rt_mmcsd_cmd *cmd) +{ + int i, reg; + + for (i = 0; i < 4; i++) + { + reg = RT_SDHCI_RESPONSE + (3 - i) * 4; + cmd->resp[i] = rt_sdhci_readl(host, reg); + } + + if (host->quirks2 & RT_SDHCI_QUIRK2_RSP_136_HAS_CRC) + return; + + for (i = 0; i < 4; i++) + { + cmd->resp[i] <<= 8; + if (i != 3) + cmd->resp[i] |= cmd->resp[i + 1] >> 24; + } +} + +static void sdhci_command_end(struct rt_sdhci_host *host) +{ + struct rt_mmcsd_cmd *cmd = host->cmd; + + host->cmd = NULL; + + if (cmd->flags & MMC_RSP_PRESENT) + { + if (cmd->flags & MMC_RSP_136) + { + rt_sdhci_read_rsp_136(host, cmd); + } + else + { + cmd->resp[0] = rt_sdhci_readl(host, RT_SDHCI_RESPONSE); + } + } + + if (cmd->flags & MMC_RSP_BUSY) + { + if (cmd->data) + { + LOG_D("Cannot wait for busy signal when also doing a data transfer"); + } + else if (!(host->quirks & RT_SDHCI_QUIRK_NO_BUSY_IRQ) && cmd == host->data_cmd) + { + return; + } + } + + if (cmd == cmd->mrq->sbc) + { + if (!sdhci_send_command(host, cmd->mrq->cmd)) + { + host->deferred_cmd = cmd->mrq->cmd; + } + } + else + { + if (host->data && host->data_early) + sdhci_finish_data(host); + + if (!cmd->data) + __sdhci_finish_mrq(host, cmd->mrq); + } +} + + +static void sdhci_cmd_irq(struct rt_sdhci_host *host, rt_uint32_t intmask, rt_uint32_t *intmask_p) +{ + if (intmask & RT_SDHCI_INT_AUTO_CMD_ERR && host->data_cmd) + { + struct rt_mmcsd_req *mrq = host->data_cmd->mrq; + rt_uint16_t auto_cmd_status = rt_sdhci_readw(host, RT_SDHCI_AUTO_CMD_STATUS); + int data_err_bit = (auto_cmd_status & RT_SDHCI_AUTO_CMD_TIMEOUT) ? RT_SDHCI_INT_DATA_TIMEOUT : RT_SDHCI_INT_DATA_CRC; + + if (!mrq->sbc && (host->flags & RT_SDHCI_AUTO_CMD12)) + { + *intmask_p |= data_err_bit; + return; + } + } + + if (!host->cmd) + { + if (host->pending_reset) + return; + rt_kprintf("%s: Got command interrupt 0x%08x even though no command operation was in progress.\n", + mmc_hostname(host->mmc), (unsigned)intmask); + rt_read_reg_debug(host); + return; + } + if (intmask & (RT_SDHCI_INT_TIMEOUT | RT_SDHCI_INT_CRC | RT_SDHCI_INT_END_BIT | RT_SDHCI_INT_INDEX)) + { + if (intmask & RT_SDHCI_INT_TIMEOUT) + host->cmd->err = -ETIMEDOUT; + else + host->cmd->err = -EILSEQ; + + /* Treat data command CRC error the same as data CRC error */ + if (host->cmd->data && (intmask & (RT_SDHCI_INT_CRC | RT_SDHCI_INT_TIMEOUT)) == RT_SDHCI_INT_CRC) + { + host->cmd = NULL; + *intmask_p |= RT_SDHCI_INT_DATA_CRC; + return; + } + + __sdhci_finish_mrq(host, host->cmd->mrq); + return; + } + + if (intmask & RT_SDHCI_INT_AUTO_CMD_ERR) + { + struct rt_mmcsd_req *mrq = host->cmd->mrq; + rt_uint16_t auto_cmd_status = rt_sdhci_readw(host, RT_SDHCI_AUTO_CMD_STATUS); + int err = (auto_cmd_status & RT_SDHCI_AUTO_CMD_TIMEOUT) ? -ETIMEDOUT : -EILSEQ; + + if (mrq->sbc && (host->flags & RT_SDHCI_AUTO_CMD23)) + { + mrq->sbc->err = err; + __sdhci_finish_mrq(host, mrq); + return; + } + } + + if (intmask & RT_SDHCI_INT_RESPONSE) + sdhci_command_end(host); +} + +static void sdhci_irq(int irq, void *dev_id) +{ +#define IRQ_NONE 0 +#define IRQ_WAIT 1 +#define IRQ_DONE 2 + + struct rt_mmcsd_req* mrqs_done[RT_SDHCI_MAX_MRQS] = { 0 }; + struct rt_sdhci_host *host = dev_id; + rt_uint32_t intmask, mask, unexpected = 0; + int max_loops = 16; + int i, result= IRQ_NONE ; + rt_spin_lock(&host->lock); + + if (host->runtime_suspended) + { + rt_spin_unlock(&host->lock); + return; + } + + intmask = rt_sdhci_readl(host, RT_SDHCI_INT_STATUS); + if (!intmask || intmask == 0xffffffff) + { + result = IRQ_NONE; + goto out; + } + + do { + LOG_D("IRQ status 0x%08x\n", intmask); + + if (host->ops->irq) + { + intmask = host->ops->irq(host, intmask); + if (!intmask) + goto cont; + } + + /* Clear selected interrupts. */ + mask = intmask & (RT_SDHCI_INT_CMD_MASK | RT_SDHCI_INT_DATA_MASK | RT_SDHCI_INT_BUS_POWER); + rt_sdhci_writel(host, mask, RT_SDHCI_INT_STATUS); + + if (intmask & (RT_SDHCI_INT_CARD_INSERT | RT_SDHCI_INT_CARD_REMOVE)) + { + rt_uint32_t present = rt_sdhci_readl(host, RT_SDHCI_PRESENT_STATE) & RT_SDHCI_CARD_PRESENT; + + host->ier &= ~(RT_SDHCI_INT_CARD_INSERT | RT_SDHCI_INT_CARD_REMOVE); + host->ier |= present ? RT_SDHCI_INT_CARD_REMOVE : RT_SDHCI_INT_CARD_INSERT; + rt_sdhci_writel(host, host->ier, RT_SDHCI_INT_ENABLE); + rt_sdhci_writel(host, host->ier, RT_SDHCI_SIGNAL_ENABLE); + + rt_sdhci_writel(host, intmask & (RT_SDHCI_INT_CARD_INSERT | RT_SDHCI_INT_CARD_REMOVE), RT_SDHCI_INT_STATUS); + + host->thread_isr |= intmask & (RT_SDHCI_INT_CARD_INSERT | RT_SDHCI_INT_CARD_REMOVE); + result = IRQ_WAIT; + } + + if (intmask & RT_SDHCI_INT_CMD_MASK) + sdhci_cmd_irq(host, intmask & RT_SDHCI_INT_CMD_MASK, &intmask); + + if (intmask & RT_SDHCI_INT_DATA_MASK) + sdhci_data_irq(host, intmask & RT_SDHCI_INT_DATA_MASK); + + if (intmask & RT_SDHCI_INT_BUS_POWER) + rt_kprintf("%s: Card is consuming too much power!\n", + mmc_hostname(host->mmc)); + + intmask &= ~(RT_SDHCI_INT_CARD_INSERT | RT_SDHCI_INT_CARD_REMOVE | RT_SDHCI_INT_CMD_MASK | RT_SDHCI_INT_DATA_MASK | RT_SDHCI_INT_ERROR | RT_SDHCI_INT_BUS_POWER | RT_SDHCI_INT_RETUNE | RT_SDHCI_INT_CARD_INT); + + if (intmask) + { + unexpected |= intmask; + rt_sdhci_writel(host, intmask, RT_SDHCI_INT_STATUS); + } + cont: + if (result == IRQ_NONE) + result = IRQ_WAIT; + intmask = rt_sdhci_readl(host, RT_SDHCI_INT_STATUS); + } while (intmask && --max_loops); + + for (i = 0; i < RT_SDHCI_MAX_MRQS; i++) + { + struct rt_mmcsd_req *mrq = host->mrqs_done[i]; + + if (!mrq) + continue; + + if (sdhci_defer_done(host, mrq)) + { + result = IRQ_WAIT; + } + else + { + mrqs_done[i] = mrq; + host->mrqs_done[i] = NULL; + } + } +out: + if (host->deferred_cmd) + result = IRQ_WAIT; + + rt_spin_unlock(&host->lock); + + for (i = 0; i < RT_SDHCI_MAX_MRQS; i++) + { + if (!mrqs_done[i]) + continue; + + if (host->ops->request_done) + host->ops->request_done(host, mrqs_done[i]); + else + rt_mmc_request_done(host->mmc, mrqs_done[i]); + } + + if (unexpected) + { + rt_kprintf("%s: Unexpected interrupt 0x%08x.\n", + mmc_hostname(host->mmc), unexpected); + rt_read_reg_debug(host); + } + + if (result == IRQ_WAIT) + { + rt_workqueue_submit_work(host->irq_wq, &host->irq_work, 0); + } +} + +static rt_bool_t sdhci_send_command_retry(struct rt_sdhci_host *host, + struct rt_mmcsd_cmd *cmd, + unsigned long flags) +{ + struct rt_mmcsd_cmd *deferred_cmd = host->deferred_cmd; + int timeout = 10; /* Approx. 10 ms */ + rt_bool_t present; + while (!sdhci_send_command(host, cmd)) + { + if (!timeout--) + { + rt_kprintf("%s: Controller never released inhibit bit(s).\n", + mmc_hostname(host->mmc)); + rt_read_reg_debug(host); + cmd->err = -EIO; + return RT_FALSE; + } + + rt_spin_unlock_irqrestore(&host->lock, flags); + + rt_thread_mdelay(1); + + present = host->mmc->ops->get_cd(host->mmc); + + flags = rt_spin_lock_irqsave(&host->lock); + + if (cmd == deferred_cmd && cmd != host->deferred_cmd) + return RT_TRUE; + + if (sdhci_present_error(host, cmd, present)) + return RT_FALSE; + } + + if (cmd == host->deferred_cmd) + host->deferred_cmd = NULL; + + return RT_TRUE; +} + +static rt_bool_t rt_sdhci_start_request_done(struct rt_sdhci_host *host) +{ + rt_base_t flags; + struct rt_mmcsd_req *mrq; + int i; + + flags = rt_spin_lock_irqsave(&host->lock); + + for (i = 0; i < RT_SDHCI_MAX_MRQS; i++) + { + mrq = host->mrqs_done[i]; + if (mrq) + break; + } + + if (!mrq) + { + rt_spin_unlock_irqrestore(&host->lock, flags); + return RT_TRUE; + } + + if (sdhci_needs_reset(host, mrq)) + { + if (host->cmd || host->data_cmd) + { + rt_spin_unlock_irqrestore(&host->lock, flags); + return RT_TRUE; + } + + /* Some controllers need this kick or reset won't work here */ + if (host->quirks & RT_SDHCI_QUIRK_CLOCK_BEFORE_RESET) + /* This is to force an update */ + host->ops->set_clock(host, host->clock); + + sdhci_reset_for(host, REQUEST_ERROR); + + host->pending_reset = RT_FALSE; + } + + if (host->flags & RT_SDHCI_REQ_USE_DMA) + { + struct rt_mmcsd_data *data = mrq->data; + + if (data && data->host_cookie == COOKIE_MAPPED) + { + if (host->bounce_buffer) + { + /* + * On reads, copy the bounced data into the + * sglist + */ + if (mmc_get_dma_dir(data) == DMA_FROM_DEVICE) + { + unsigned int length = data->bytes_xfered; + + if (length > host->bounce_buffer_size) + { + LOG_E("%s: bounce buffer is %u bytes but DMA claims to have transferred %u bytes\n", + mmc_hostname(host->mmc), + host->bounce_buffer_size, + data->bytes_xfered); + /* Cap it down and continue */ + length = host->bounce_buffer_size; + } + rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, data->buf, data->blks * data->blksize); + } else { + /* No copying, just switch ownership */ + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, data->buf, data->blks * data->blksize); + } + } + data->host_cookie = COOKIE_UNMAPPED; + } + else { + if (mmc_get_dma_dir(data) == DMA_FROM_DEVICE) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, data->buf, data->blks * data->blksize); + } else { + /* No copying, just switch ownership */ + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, data->buf, data->blks * data->blksize); + } + } + } + + host->mrqs_done[i] = NULL; + + rt_spin_unlock_irqrestore(&host->lock, flags); + + if (host->ops->request_done) + host->ops->request_done(host, mrq); + else + rt_mmc_request_done(host->mmc, mrq); + + return RT_FALSE; +} + + +static void sdhci_thread_irq(struct rt_work *work, void *work_data) +{ + struct rt_sdhci_host* host = work_data; + struct rt_mmcsd_cmd *cmd; + rt_base_t flags; + rt_uint32_t isr; + + while (!rt_sdhci_start_request_done(host)); + + flags = rt_spin_lock_irqsave(&host->lock); + + isr = host->thread_isr; + host->thread_isr = 0; + + cmd = host->deferred_cmd; + if (cmd && !sdhci_send_command_retry(host, cmd, flags)) + sdhci_finish_mrq(host, cmd->mrq); + + rt_spin_unlock_irqrestore(&host->lock, flags); + + if (isr & (RT_SDHCI_INT_CARD_INSERT | RT_SDHCI_INT_CARD_REMOVE)) + { + struct rt_mmc_host *mmc = host->mmc; + + mmc->ops->card_event(mmc); + } +} + + +void rt_sdhci_enable_io_irq(struct rt_mmc_host *mmc, int enable) +{ + struct rt_sdhci_host *host = mmc_priv(mmc); + rt_uint32_t flags; + + flags = rt_spin_lock_irqsave(&host->lock); + rt_sdhci_enable_io_irq_nolock(host, enable); + rt_spin_unlock_irqrestore(&host->lock, flags); +} + + +/********************************************************* */ +/* request */ +/********************************************************* */ + +void rt_sdhci_start_request(struct rt_mmc_host *mmc, struct rt_mmcsd_req *mrq) +{ + struct rt_sdhci_host *host = mmc_priv(mmc); + struct rt_mmcsd_cmd *cmd; + rt_base_t flags; + rt_bool_t present; + + /* Firstly check card presence */ + present = mmc->ops->get_cd(mmc); + + flags = rt_spin_lock_irqsave(&host->lock); + + if (sdhci_present_error(host, mrq->cmd, present)) + goto out_finish; + + cmd = sdhci_manual_cmd23(host, mrq) ? mrq->sbc : mrq->cmd; + + if (!sdhci_send_command_retry(host, cmd, flags)) + goto out_finish; + + rt_spin_unlock_irqrestore(&host->lock, flags); + + return; + +out_finish: + sdhci_finish_mrq(host, mrq); + rt_spin_unlock_irqrestore(&host->lock, flags); +} + + +static void sdhci_complete_work(struct rt_work *work, void *work_data) +{ + struct rt_sdhci_host *host = work_data; + + while (!rt_sdhci_start_request_done(host)); +} + + +/********************************************************* */ +/* timer */ +/********************************************************* */ +static void sdhci_timeout_timer(void *parameter) +{ + struct rt_sdhci_host *host = parameter; + rt_base_t flags; + + flags = rt_spin_lock_irqsave(&host->lock); + + if (host->cmd && !sdhci_data_line_cmd(host->cmd)) + { + rt_kprintf("%s: Timeout waiting for hardware cmd interrupt.\n", + mmc_hostname(host->mmc)); + rt_read_reg_debug(host); + + host->cmd->err = -ETIMEDOUT; + sdhci_finish_mrq(host, host->cmd->mrq); + } + + rt_spin_unlock_irqrestore(&host->lock, flags); +} + +static void sdhci_timeout_data_timer(void *parameter) +{ + struct rt_sdhci_host *host = parameter; + rt_base_t flags; + + flags = rt_spin_lock_irqsave(&host->lock); + + if (host->data || host->data_cmd || (host->cmd && sdhci_data_line_cmd(host->cmd))) + { + rt_kprintf("%s: Timeout waiting for hardware interrupt.\n", + mmc_hostname(host->mmc)); + rt_read_reg_debug(host); + + if (host->data) + { + host->data->err = -ETIMEDOUT; + __sdhci_finish_data(host, RT_TRUE); + rt_workqueue_submit_work(host->complete_wq, &host->complete_work, 0); + } + else if (host->data_cmd) + { + host->data_cmd->err = -ETIMEDOUT; + sdhci_finish_mrq(host, host->data_cmd->mrq); + } + else + { + host->cmd->err = -ETIMEDOUT; + sdhci_finish_mrq(host, host->cmd->mrq); + } + } + + rt_spin_unlock_irqrestore(&host->lock, flags); +} + + +/********************************************************* */ +/* tuning */ +/********************************************************* */ +int rt_sdhci_execute_tuning(struct rt_mmc_host *mmc, rt_uint32_t opcode) +{ + struct rt_sdhci_host *host = mmc_priv(mmc); + int err = 0; + unsigned int tuning_count = 0; + rt_bool_t hs400_tuning; + + hs400_tuning = host->flags & RT_SDHCI_HS400_TUNING; + + if (host->tuning_mode == RT_SDHCI_TUNING_MODE_1) + tuning_count = host->tuning_count; + + switch (host->timing) + { + /* HS400 tuning is done in HS200 mode */ + case MMC_TIMING_MMC_HS400: + err = -EINVAL; + goto out; + + case MMC_TIMING_MMC_HS200: + if (hs400_tuning) + tuning_count = 0; + break; + + case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_UHS_DDR50: + break; + + case MMC_TIMING_UHS_SDR50: + if (host->flags & RT_SDHCI_SDR50_NEEDS_TUNING) + break; + fallthrough; + + default: + goto out; + } + + if (host->ops->platform_execute_tuning) + { + err = host->ops->platform_execute_tuning(host, opcode); + goto out; + } + + mmc->retune_period = tuning_count; + + if (host->tuning_delay < 0) + host->tuning_delay = opcode == MMC_SEND_TUNING_BLOCK; + + rt_sdhci_start_tuning(host); + + host->tuning_err = __sdhci_execute_tuning(host, opcode); + + rt_sdhci_end_tuning(host); +out: + host->flags &= ~RT_SDHCI_HS400_TUNING; + + return err; +} + +int __sdhci_execute_tuning(struct rt_sdhci_host *host, rt_uint32_t opcode) +{ + int i; + + for (i = 0; i < host->tuning_loop_count; i++) + { + rt_uint16_t ctrl; + + rt_sdhci_send_tuning(host, opcode); + + if (!host->tuning_done) + { + rt_sdhci_abort_tuning(host, opcode); + return -ETIMEDOUT; + } + + if (host->tuning_delay > 0) + rt_thread_mdelay(host->tuning_delay); + + ctrl = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2); + if (!(ctrl & RT_SDHCI_CTRL_EXEC_TUNING)) + { + if (ctrl & RT_SDHCI_CTRL_TUNED_CLK) + return 0; /* Success! */ + break; + } + } + + LOG_D("%s: Tuning failed, falling back to fixed sampling clock\n", + mmc_hostname(host->mmc)); + rt_sdhci_reset_tuning(host); + return -EAGAIN; +} + +void rt_sdhci_start_tuning(struct rt_sdhci_host *host) +{ + rt_uint16_t ctrl; + + ctrl = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2); + ctrl |= RT_SDHCI_CTRL_EXEC_TUNING; + if (host->quirks2 & RT_SDHCI_QUIRK2_TUNING_WORK_AROUND) + ctrl |= RT_SDHCI_CTRL_TUNED_CLK; + rt_sdhci_writew(host, ctrl, RT_SDHCI_HOST_CONTROL2); + + rt_sdhci_writel(host, RT_SDHCI_INT_DATA_AVAIL, RT_SDHCI_INT_ENABLE); + rt_sdhci_writel(host, RT_SDHCI_INT_DATA_AVAIL, RT_SDHCI_SIGNAL_ENABLE); +} + +void rt_sdhci_end_tuning(struct rt_sdhci_host *host) +{ + rt_sdhci_writel(host, host->ier, RT_SDHCI_INT_ENABLE); + rt_sdhci_writel(host, host->ier, RT_SDHCI_SIGNAL_ENABLE); +} + +void rt_sdhci_abort_tuning(struct rt_sdhci_host *host, rt_uint32_t opcode) +{ + rt_sdhci_reset_tuning(host); + + sdhci_reset_for(host, TUNING_ABORT); + + rt_sdhci_end_tuning(host); +} + +void rt_sdhci_send_tuning(struct rt_sdhci_host *host, rt_uint32_t opcode) +{ + struct rt_mmc_host *mmc = host->mmc; + struct rt_mmcsd_cmd cmd = {}; + struct rt_mmcsd_req mrq = {}; + unsigned long flags; + rt_uint32_t b = host->sdma_boundary; + + flags = rt_spin_lock_irqsave(&host->lock); + + cmd.cmd_code = opcode; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + cmd.mrq = &mrq; + + mrq.cmd = &cmd; + + if (cmd.cmd_code == MMC_SEND_TUNING_BLOCK_HS200 && mmc->ios.bus_width == MMC_BUS_WIDTH_8) + rt_sdhci_writew(host, RT_SDHCI_MAKE_BLKSZ(b, 128), RT_SDHCI_BLOCK_SIZE); + else + rt_sdhci_writew(host, RT_SDHCI_MAKE_BLKSZ(b, 64), RT_SDHCI_BLOCK_SIZE); + + rt_sdhci_writew(host, RT_SDHCI_TRNS_READ, RT_SDHCI_TRANSFER_MODE); + + if (!sdhci_send_command_retry(host, &cmd, flags)) + { + rt_spin_unlock_irqrestore(&host->lock, flags); + host->tuning_done = 0; + return; + } + + host->cmd = NULL; + + sdhci_del_timer(host, &mrq); + + host->tuning_done = 0; + + rt_spin_unlock_irqrestore(&host->lock, flags); +} + +void rt_sdhci_reset_tuning(struct rt_sdhci_host *host) +{ + rt_uint16_t ctrl; + + ctrl = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2); + ctrl &= ~RT_SDHCI_CTRL_TUNED_CLK; + ctrl &= ~RT_SDHCI_CTRL_EXEC_TUNING; + rt_sdhci_writew(host, ctrl, RT_SDHCI_HOST_CONTROL2); +} + + +/********************************************************* */ +/* error */ +/********************************************************* */ +static const struct mmc_host_ops rt_sdhci_ops = { + .request = rt_sdhci_start_request, + .set_ios = rt_sdhci_ios_set, + .get_cd = sdhci_get_cd, + .get_ro = rt_sdhci_ro_get, + .enable_sdio_irq = rt_sdhci_enable_io_irq, + .ack_sdio_irq = sdhci_ack_sdio_irq, + .start_signal_voltage_switch = rt_sdhci_start_signal_voltage_switch, + .prepare_hs400_tuning = sdhci_prepare_hs400_tuning, + .execute_tuning = rt_sdhci_execute_tuning, + .card_event = sdhci_card_event, + .card_busy = sdhci_card_busy, +}; + + +void rt_sdhci_uninit_host(struct rt_sdhci_host *host, int dead) +{ + struct rt_mmc_host *mmc = host->mmc; + unsigned long flags; + + if (dead) + { + flags = rt_spin_lock_irqsave(&host->lock); + + host->flags |= RT_SDHCI_DEVICE_DEAD; + + if (sdhci_has_requests(host)) + { + rt_kprintf("%s: Controller removed during " + " transfer!\n", + mmc_hostname(mmc)); + sdhci_error_out_mrqs(host, -ENOMEDIUM); + } + + rt_spin_unlock_irqrestore(&host->lock, flags); + } + + sdhci_set_card_detection(host, RT_FALSE); + + rt_mmc_remove_host(mmc); + + + if (!dead) + sdhci_reset_for_all(host); + + rt_sdhci_writel(host, 0, RT_SDHCI_INT_ENABLE); + rt_sdhci_writel(host, 0, RT_SDHCI_SIGNAL_ENABLE); + + rt_timer_delete(&host->timer); + rt_timer_delete(&host->data_timer); + + rt_workqueue_destroy(host->complete_wq); + +} + +rt_uint16_t rt_sdhci_clk_set(struct rt_sdhci_host *host, unsigned int clock, + unsigned int *actual_clock) +{ + int div = 0; /* Initialized for compiler warning */ + int real_div = div, clk_mul = 1; + rt_uint16_t clk = 0; + rt_bool_t switch_base_clk = RT_FALSE; + + if (host->version >= RT_SDHCI_SPEC_300) + { + if (host->preset_enabled) + { + rt_uint16_t pre_val; + + clk = rt_sdhci_readw(host, RT_SDHCI_CLOCK_CONTROL); + pre_val = sdhci_get_preset_value(host); + div = FIELD_GET(RT_SDHCI_PRESET_SDCLK_FREQ_MASK, pre_val); + if (host->clk_mul && (pre_val & RT_SDHCI_PRESET_CLKGEN_SEL)) + { + clk = RT_SDHCI_PROG_CLOCK_MODE; + real_div = div + 1; + clk_mul = host->clk_mul; + } + else + { + real_div = max_t(int, 1, div << 1); + } + goto clock_set; + } + + if (host->clk_mul) + { + for (div = 1; div <= 1024; div++) + { + if ((host->max_clk * host->clk_mul / div) + <= clock) + break; + } + if ((host->max_clk * host->clk_mul / div) <= clock) + { + + clk = RT_SDHCI_PROG_CLOCK_MODE; + real_div = div; + clk_mul = host->clk_mul; + div--; + } + else + { + + switch_base_clk = RT_TRUE; + } + } + + if (!host->clk_mul || switch_base_clk) + { + if (host->max_clk <= clock) + div = 1; + else + { + for (div = 2; div < RT_SDHCI_MAX_DIV_SPEC_300; + div += 2) + { + if ((host->max_clk / div) <= clock) + break; + } + } + real_div = div; + div >>= 1; + if ((host->quirks2 & RT_SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN) + && !div && host->max_clk <= 25000000) + div = 1; + } + } + else + { + for (div = 1; div < RT_SDHCI_MAX_DIV_SPEC_200; div *= 2) + { + if ((host->max_clk / div) <= clock) + break; + } + real_div = div; + div >>= 1; + } + +clock_set: + if (real_div) + *actual_clock = (host->max_clk * clk_mul) / real_div; + clk |= (div & RT_SDHCI_DIV_MASK) << RT_SDHCI_DIVIDER_SHIFT; + clk |= ((div & RT_SDHCI_DIV_HI_MASK) >> RT_SDHCI_DIV_MASK_LEN) + << RT_SDHCI_DIVIDER_HI_SHIFT; + + return clk; +} + +void rt_sdhci_clk_enable(struct rt_sdhci_host *host, rt_uint16_t clk) +{ + long timeout; + + clk |= RT_SDHCI_CLOCK_INT_EN; + rt_sdhci_writew(host, clk, RT_SDHCI_CLOCK_CONTROL); + + timeout = rt_tick_from_millisecond(150); + while (1) + { + timeout = timeout - rt_tick_get(); + + clk = rt_sdhci_readw(host, RT_SDHCI_CLOCK_CONTROL); + if (clk & RT_SDHCI_CLOCK_INT_STABLE) + break; + if (timeout < 0) + { + rt_kprintf("%s: Internal clock never stabilised.\n", + mmc_hostname(host->mmc)); + rt_read_reg_debug(host); + return; + } + rt_hw_us_delay(10); + } + + if (host->version >= RT_SDHCI_SPEC_410 && host->v4_mode) + { + clk |= RT_SDHCI_CLOCK_PLL_EN; + clk &= ~RT_SDHCI_CLOCK_INT_STABLE; + rt_sdhci_writew(host, clk, RT_SDHCI_CLOCK_CONTROL); + + timeout = rt_tick_from_millisecond(150); + while (1) + { + timeout = timeout - rt_tick_get(); + + clk = rt_sdhci_readw(host, RT_SDHCI_CLOCK_CONTROL); + if (clk & RT_SDHCI_CLOCK_INT_STABLE) + break; + if (timeout < 0) + { + rt_kprintf("%s: PLL clock never stabilised.\n", + mmc_hostname(host->mmc)); + rt_read_reg_debug(host); + return; + } + rt_hw_us_delay(10); + } + } + + clk |= RT_SDHCI_CLOCK_CARD_EN; + rt_sdhci_writew(host, clk, RT_SDHCI_CLOCK_CONTROL); +} + +void rt_sdhci_set_clock(struct rt_sdhci_host *host, unsigned int clock) +{ + rt_uint16_t clk; + + host->mmc->actual_clock = 0; + + rt_sdhci_writew(host, 0, RT_SDHCI_CLOCK_CONTROL); + + if (clock == 0) + return; + + clk = rt_sdhci_clk_set(host, clock, &host->mmc->actual_clock); + rt_sdhci_clk_enable(host, clk); +} + +void rt_sdhci_read_caps(struct rt_sdhci_host *host, const rt_uint16_t *ver, + const rt_uint32_t *caps, const rt_uint32_t *caps1) +{ + rt_uint16_t v; + rt_uint64_t dt_caps_mask = 0; + rt_uint64_t dt_caps = 0; + + if (host->read_caps) + return; + + host->read_caps = RT_TRUE; + + if (debug_quirks) + host->quirks = debug_quirks; + + if (debug_quirks2) + host->quirks2 = debug_quirks2; + + sdhci_reset_for_all(host); + + if (host->v4_mode) + sdhci_do_enable_v4_mode(host); +#ifdef RT_USING_OFW + rt_ofw_prop_read_u64(mmc_dev(host->mmc)->ofw_node, + "sdhci-caps-mask", &dt_caps_mask); + rt_ofw_prop_read_u64(mmc_dev(host->mmc)->ofw_node, + "sdhci-caps", &dt_caps); +#endif + v = ver ? *ver : rt_sdhci_readw(host, RT_SDHCI_HOST_VERSION); + host->version = (v & RT_SDHCI_SPEC_VER_MASK) >> RT_SDHCI_SPEC_VER_SHIFT; + + if (caps) + { + host->caps = *caps; + } + else + { + host->caps = rt_sdhci_readl(host, RT_SDHCI_CAPABILITIES); + host->caps &= ~lower_32_bits(dt_caps_mask); + host->caps |= lower_32_bits(dt_caps); + } + + if (host->version < RT_SDHCI_SPEC_300) + return; + + if (caps1) + { + host->caps1 = *caps1; + } + else + { + host->caps1 = rt_sdhci_readl(host, RT_SDHCI_CAPABILITIES_1); + host->caps1 &= ~upper_32_bits(dt_caps_mask); + host->caps1 |= upper_32_bits(dt_caps); + } +} + +struct rt_sdhci_host *rt_sdhci_alloc_host(struct rt_device *dev, + size_t priv_size) +{ + struct rt_mmc_host *mmc; + struct rt_sdhci_host *host; + + mmc = rt_mmc_alloc_host(sizeof(struct rt_sdhci_host) + priv_size, dev); + if (!mmc) + return NULL; + + host = mmc_priv(mmc); + host->mmc = mmc; + host->mmc_host_ops = rt_sdhci_ops; + mmc->ops = &host->mmc_host_ops; + + host->flags = RT_SDHCI_SIGNALING_330; + + host->cqe_ier = RT_SDHCI_CQE_INT_MASK; + host->cqe_err_ier = RT_SDHCI_CQE_INT_ERR_MASK; + + host->tuning_delay = -1; + host->tuning_loop_count = MAX_TUNING_LOOP; + + host->sdma_boundary = RT_SDHCI_DEFAULT_BOUNDARY_ARG; + + host->max_timeout_count = 0xE; + + return host; +} + +int rt_sdhci_setup_host(struct rt_sdhci_host *host) +{ + struct rt_mmc_host *mmc; + size_t max_current_caps; + unsigned int ocr_avail; + unsigned int override_timeout_clk; + size_t max_clk; + int ret = 0; + bool enable_vqmmc = RT_FALSE; + + RT_ASSERT(host != NULL); + + + mmc = host->mmc; + + if (!mmc->supply.vqmmc) + { + if (ret) + return ret; + enable_vqmmc = RT_TRUE; + } + + LOG_D("Version: 0x%08x | Present: 0x%08x\n", + rt_sdhci_readw(host, RT_SDHCI_HOST_VERSION), + rt_sdhci_readl(host, RT_SDHCI_PRESENT_STATE)); + LOG_D("Caps: 0x%08x | Caps_1: 0x%08x\n", + rt_sdhci_readl(host, RT_SDHCI_CAPABILITIES), + rt_sdhci_readl(host, RT_SDHCI_CAPABILITIES_1)); + + rt_sdhci_read_caps(host,RT_NULL,RT_NULL,RT_NULL); + + override_timeout_clk = host->timeout_clk; + + if (host->version > RT_SDHCI_SPEC_420) + { + rt_kprintf("%s: Unknown controller version (%d). You may experience problems.\n", + mmc_hostname(mmc), host->version); + } + + if (host->quirks & RT_SDHCI_QUIRK_FORCE_DMA) + host->flags |= RT_SDHCI_USE_SDMA; + else if (!(host->caps & RT_SDHCI_CAN_DO_SDMA)) + LOG_D("Controller doesn't have SDMA capability\n"); + else + host->flags |= RT_SDHCI_USE_SDMA; + + if ((host->quirks & RT_SDHCI_QUIRK_BROKEN_DMA) && (host->flags & RT_SDHCI_USE_SDMA)) + { + LOG_D("Disabling DMA as it is marked broken\n"); + host->flags &= ~RT_SDHCI_USE_SDMA; + } + + if (sdhci_can_64bit_dma(host)) + host->flags |= RT_SDHCI_USE_64_BIT_DMA; + + if (host->flags & RT_SDHCI_USE_SDMA) + { + if (host->ops->set_dma_mask) + ret = host->ops->set_dma_mask(host); + + if (!ret && host->ops->enable_dma) + ret = host->ops->enable_dma(host); + + if (ret) + { + rt_kprintf("%s: No suitable DMA available - falling back to PIO\n", + mmc_hostname(mmc)); + host->flags &= ~RT_SDHCI_USE_SDMA; + + ret = 0; + } + } + + if ((host->flags & RT_SDHCI_USE_64_BIT_DMA) && !host->v4_mode) + host->flags &= ~RT_SDHCI_USE_SDMA; + + if (!(host->flags & RT_SDHCI_USE_SDMA)) + { + host->dma_mask = DMA_BIT_MASK(64); + } + if (host->version >= RT_SDHCI_SPEC_300) + host->max_clk = FIELD_GET(RT_SDHCI_CLOCK_V3_BASE_MASK, host->caps); + else + host->max_clk = FIELD_GET(RT_SDHCI_CLOCK_BASE_MASK, host->caps); + + host->max_clk *= 1000000; + if (host->max_clk == 0 || host->quirks & RT_SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN) + { + if (!host->ops->get_max_clock) + { + rt_kprintf("%s: Hardware doesn't specify base clock frequency. %p \n", + mmc_hostname(mmc), host->ops->get_max_clock); + ret = -ENODEV; + goto undma; + } + host->max_clk = host->ops->get_max_clock(host); + } + + host->clk_mul = FIELD_GET(RT_SDHCI_CLOCK_MUL_MASK, host->caps1); + + if (host->clk_mul) + host->clk_mul += 1; + + max_clk = host->max_clk; + + if (host->ops->get_min_clock) + mmc->f_min = host->ops->get_min_clock(host); + else if (host->version >= RT_SDHCI_SPEC_300) + { + if (host->clk_mul) + max_clk = host->max_clk * host->clk_mul; + + mmc->f_min = host->max_clk / RT_SDHCI_MAX_DIV_SPEC_300; + } + else + mmc->f_min = host->max_clk / RT_SDHCI_MAX_DIV_SPEC_200; + + if (!mmc->f_max || mmc->f_max > max_clk) + mmc->f_max = max_clk; + + if (!(host->quirks & RT_SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) + { + host->timeout_clk = FIELD_GET(RT_SDHCI_TIMEOUT_CLK_MASK, host->caps); + + if (host->caps & RT_SDHCI_TIMEOUT_CLK_UNIT) + host->timeout_clk *= 1000; + + if (host->timeout_clk == 0) + { + if (!host->ops->get_timeout_clock) + { + rt_kprintf("%s: Hardware doesn't specify timeout clock frequency.\n", + mmc_hostname(mmc)); + ret = -ENODEV; + goto undma; + } + + host->timeout_clk = + DIV_ROUND_UP(host->ops->get_timeout_clock(host), + 1000); + } + + if (override_timeout_clk) + host->timeout_clk = override_timeout_clk; + + mmc->max_busy_timeout = host->ops->get_max_timeout_count ? host->ops->get_max_timeout_count(host) : 1 << 27; + mmc->max_busy_timeout /= host->timeout_clk; + } + + if (host->quirks2 & RT_SDHCI_QUIRK2_DISABLE_HW_TIMEOUT && !host->ops->get_max_timeout_count) + mmc->max_busy_timeout = 0; + + mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_CMD23; + mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; + + if (host->quirks & RT_SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) + host->flags |= RT_SDHCI_AUTO_CMD12; + + if ((host->version >= RT_SDHCI_SPEC_300) && (!(host->flags & RT_SDHCI_USE_SDMA) || host->v4_mode) && !(host->quirks2 & RT_SDHCI_QUIRK2_ACMD23_BROKEN)) + { + host->flags |= RT_SDHCI_AUTO_CMD23; + LOG_D("Auto-CMD23 available\n"); + } + else + { + LOG_D("Auto-CMD23 unavailable\n"); + } + + if (!(host->quirks & RT_SDHCI_QUIRK_FORCE_1_BIT_DATA)) + mmc->caps |= MMC_CAP_4_BIT_DATA; + + if (host->quirks2 & RT_SDHCI_QUIRK2_HOST_NO_CMD23) + mmc->caps &= ~MMC_CAP_CMD23; + + if (host->caps & RT_SDHCI_CAN_DO_HISPD) + mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; + if ((host->quirks & RT_SDHCI_QUIRK_BROKEN_CARD_DETECTION) && mmc_card_is_removable(mmc) && rt_mmc_gpio_get_cd(mmc) < 0) + mmc->caps |= MMC_CAP_NEEDS_POLL; + + if (mmc->supply.vqmmc) + { + if (enable_vqmmc) + { + host->sdhci_core_to_disable_vqmmc = !ret; + } + + if (!regulator_is_supported_voltage(mmc->supply.vqmmc, 1700000, + 1950000)) + host->caps1 &= ~(RT_SDHCI_SUPPORT_SDR104 | RT_SDHCI_SUPPORT_SDR50 | RT_SDHCI_SUPPORT_DDR50); + + if (!regulator_is_supported_voltage(mmc->supply.vqmmc, 2700000, + 3600000)) + host->flags &= ~RT_SDHCI_SIGNALING_330; + + if (ret) + { + rt_kprintf("%s: Failed to enable vqmmc regulator: %d\n", + mmc_hostname(mmc), ret); + mmc->supply.vqmmc = (void *)-EINVAL; + } + } + if (host->quirks2 & RT_SDHCI_QUIRK2_NO_1_8_V) + { + host->caps1 &= ~(RT_SDHCI_SUPPORT_SDR104 | RT_SDHCI_SUPPORT_SDR50 | RT_SDHCI_SUPPORT_DDR50); + mmc->caps2 &= ~(MMC_CAP2_HSX00_1_8V | MMC_CAP2_HS400_ES); + mmc->caps &= ~(MMC_CAP_1_8V_DDR | MMC_CAP_UHS); + } + + if (host->caps1 & (RT_SDHCI_SUPPORT_SDR104 | RT_SDHCI_SUPPORT_SDR50 | RT_SDHCI_SUPPORT_DDR50)) + mmc->caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25; + + if (host->caps1 & RT_SDHCI_SUPPORT_SDR104) + { + mmc->caps |= MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50; + + if (!(host->quirks2 & RT_SDHCI_QUIRK2_BROKEN_HS200)) + mmc->caps2 |= MMC_CAP2_HS200; + } + else if (host->caps1 & RT_SDHCI_SUPPORT_SDR50) + { + mmc->caps |= MMC_CAP_UHS_SDR50; + } + + if (host->quirks2 & RT_SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 && (host->caps1 & RT_SDHCI_SUPPORT_HS400)) + mmc->caps2 |= MMC_CAP2_HS400; + if ((mmc->caps2 & MMC_CAP2_HSX00_1_2V) && (!mmc->supply.vqmmc || !regulator_is_supported_voltage(mmc->supply.vqmmc, 1100000, 1300000))) + mmc->caps2 &= ~MMC_CAP2_HSX00_1_2V; + if ((host->caps1 & RT_SDHCI_SUPPORT_DDR50) && !(host->quirks2 & RT_SDHCI_QUIRK2_BROKEN_DDR50)) + mmc->caps |= MMC_CAP_UHS_DDR50; + + if (host->caps1 & RT_SDHCI_USE_SDR50_TUNING) + host->flags |= RT_SDHCI_SDR50_NEEDS_TUNING; + + if (host->caps1 & RT_SDHCI_DRIVER_TYPE_A) + mmc->caps |= MMC_CAP_DRIVER_TYPE_A; + if (host->caps1 & RT_SDHCI_DRIVER_TYPE_C) + mmc->caps |= MMC_CAP_DRIVER_TYPE_C; + if (host->caps1 & RT_SDHCI_DRIVER_TYPE_D) + mmc->caps |= MMC_CAP_DRIVER_TYPE_D; + + host->tuning_count = FIELD_GET(RT_SDHCI_RETUNING_TIMER_COUNT_MASK, + host->caps1); + + if (host->tuning_count) + host->tuning_count = 1 << (host->tuning_count - 1); + + /* Re-tuning mode supported by the Host Controller */ + host->tuning_mode = FIELD_GET(RT_SDHCI_RETUNING_MODE_MASK, host->caps1); + + ocr_avail = 0; + + max_current_caps = rt_sdhci_readl(host, RT_SDHCI_MAX_CURRENT); + + if (!max_current_caps && mmc->supply.vmmc) + { + int curr = regulator_get_current_limit(mmc->supply.vmmc); + if (curr > 0) + { + curr = curr / 1000; /* convert to mA */ + curr = curr / RT_SDHCI_MAX_CURRENT_MULTIPLIER; + + curr = min_t(rt_uint32_t, curr, RT_SDHCI_MAX_CURRENT_LIMIT); + max_current_caps = + FIELD_PREP(RT_SDHCI_MAX_CURRENT_330_MASK, curr) | FIELD_PREP(RT_SDHCI_MAX_CURRENT_300_MASK, curr) | FIELD_PREP(RT_SDHCI_MAX_CURRENT_180_MASK, curr); + } + } + + if (host->caps & RT_SDHCI_CAN_VDD_330) + { + ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34; + + mmc->max_current_330 = FIELD_GET(RT_SDHCI_MAX_CURRENT_330_MASK, + max_current_caps) + * RT_SDHCI_MAX_CURRENT_MULTIPLIER; + } + if (host->caps & RT_SDHCI_CAN_VDD_300) + { + ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31; + + mmc->max_current_300 = FIELD_GET(RT_SDHCI_MAX_CURRENT_300_MASK, + max_current_caps) + * RT_SDHCI_MAX_CURRENT_MULTIPLIER; + } + if (host->caps & RT_SDHCI_CAN_VDD_180) + { + ocr_avail |= MMC_VDD_165_195; + + mmc->max_current_180 = FIELD_GET(RT_SDHCI_MAX_CURRENT_180_MASK, + max_current_caps) + * RT_SDHCI_MAX_CURRENT_MULTIPLIER; + } + + if (host->ocr_mask) + ocr_avail = host->ocr_mask; + + if (mmc->ocr_avail) + ocr_avail = mmc->ocr_avail; + + mmc->ocr_avail = ocr_avail; + mmc->ocr_avail_sdio = ocr_avail; + if (host->ocr_avail_sdio) + mmc->ocr_avail_sdio &= host->ocr_avail_sdio; + mmc->ocr_avail_sd = ocr_avail; + if (host->ocr_avail_sd) + mmc->ocr_avail_sd &= host->ocr_avail_sd; + else + mmc->ocr_avail_sd &= ~MMC_VDD_165_195; + mmc->ocr_avail_mmc = ocr_avail; + if (host->ocr_avail_mmc) + mmc->ocr_avail_mmc &= host->ocr_avail_mmc; + + if (mmc->ocr_avail == 0) + { + rt_kprintf("%s: Hardware doesn't report any support voltages.\n", + mmc_hostname(mmc)); + ret = -ENODEV; + goto unreg; + } + + if ((mmc->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR)) || (mmc->caps2 & (MMC_CAP2_HS200_1_8V_SDR | MMC_CAP2_HS400_1_8V))) + host->flags |= RT_SDHCI_SIGNALING_180; + + if (mmc->caps2 & MMC_CAP2_HSX00_1_2V) + host->flags |= RT_SDHCI_SIGNALING_120; + + rt_spin_lock_init(&host->lock); + + mmc->max_req_size = 524288; + if (host->flags & RT_SDHCI_USE_SDMA) + { + mmc->max_segs = 1; + } + else + { /* PIO */ + mmc->max_segs = RT_SDHCI_MAX_SEGS; + } + + mmc->max_seg_size = mmc->max_req_size; + + if (host->quirks & RT_SDHCI_QUIRK_FORCE_BLK_SZ_2048) + { + mmc->max_blk_size = 2; + } + else + { + mmc->max_blk_size = (host->caps & RT_SDHCI_MAX_BLOCK_MASK) >> RT_SDHCI_MAX_BLOCK_SHIFT; + if (mmc->max_blk_size >= 3) + { + rt_kprintf("%s: Invalid maximum block size, assuming 512 bytes\n", + mmc_hostname(mmc)); + mmc->max_blk_size = 0; + } + } + + mmc->max_blk_size = 512 << mmc->max_blk_size; + + /* + * Maximum block count. + */ + mmc->max_blk_count = (host->quirks & RT_SDHCI_QUIRK_NO_MULTIBLOCK) ? 1 : 65535; + return 0; + +unreg: +undma: + return ret; +} + +static void sdhci_init(struct rt_sdhci_host *host, int soft) +{ + struct rt_mmc_host *mmc = host->mmc; + rt_base_t flags; + + if (soft) + { + sdhci_do_reset(host, RT_SDHCI_RESET_CMD | RT_SDHCI_RESET_DATA); + } + else + { + sdhci_do_reset(host, RT_SDHCI_RESET_ALL); + } + if (host->v4_mode) + { + sdhci_do_enable_v4_mode(host); + } + flags = rt_spin_lock_irqsave(&host->lock); + sdhci_set_default_irqs(host); + rt_spin_unlock_irqrestore(&host->lock, flags); + + host->cqe_on = RT_FALSE; + + if (soft) + { + /* force clock reconfiguration */ + host->clock = 0; + host->reinit_uhs = RT_TRUE; + mmc->ops->set_ios(mmc, &mmc->ios); + } +} + +static void sdhci_reinit(struct rt_sdhci_host *host) +{ + rt_uint32_t cd = host->ier & (RT_SDHCI_INT_CARD_REMOVE | RT_SDHCI_INT_CARD_INSERT); + + sdhci_init(host, 0); + sdhci_enable_card_detection(host); + + if (cd != (host->ier & (RT_SDHCI_INT_CARD_REMOVE | RT_SDHCI_INT_CARD_INSERT))) + rt_mmc_detect_change(host->mmc, rt_tick_from_millisecond(200)); +} + +int rt_sdhci_init_host(struct rt_sdhci_host *host) +{ + struct rt_mmc_host *mmc = host->mmc; + int ret; + + if ((mmc->caps2 & MMC_CAP2_CQE) && (host->quirks & RT_SDHCI_QUIRK_BROKEN_CQE)) + { + mmc->caps2 &= ~MMC_CAP2_CQE; + } + + host->complete_wq = rt_workqueue_create("sdhci", 4096, 20); + if (!host->complete_wq) + return -ENOMEM; + + rt_work_init(&host->complete_work, sdhci_complete_work, host); + + rt_timer_init(&host->timer, "sdhci_timer", sdhci_timeout_timer, host, 0, RT_TIMER_FLAG_SOFT_TIMER); + rt_timer_init(&host->data_timer, "sdhci_data_timer", sdhci_timeout_data_timer, host, 0, RT_TIMER_FLAG_SOFT_TIMER); + + rt_wqueue_init(&host->buf_ready_int); + + sdhci_init(host, 0); + + host->irq_wq = rt_workqueue_create("sdhci_irq", 8192, 1); + rt_work_init(&host->irq_work, sdhci_thread_irq, host); + rt_hw_interrupt_install(host->irq, sdhci_irq, host, mmc_hostname(mmc)); + rt_pic_irq_unmask(host->irq); + ret = rt_mmc_add_host(mmc); + if (ret) + goto unirq; + + LOG_D("%s: RT_SDHCI controller on %s [%s] using %s\n", + mmc_hostname(mmc), host->hw_name, mmc_dev(mmc)->parent.name, + (host->flags & RT_SDHCI_USE_SDMA) ? "DMA" : "PIO"); + + sdhci_enable_card_detection(host); + + return 0; + +unirq: + sdhci_reset_for_all(host); + rt_sdhci_writel(host, 0, RT_SDHCI_INT_ENABLE); + rt_sdhci_writel(host, 0, RT_SDHCI_SIGNAL_ENABLE); + + return ret; +} + +int rt_sdhci_set_and_add_host(struct rt_sdhci_host *host) +{ + int ret; + ret = rt_sdhci_setup_host(host); + if (ret) + return ret; + + ret = rt_sdhci_init_host(host); + if (ret) + goto cleanup; + + return 0; + +cleanup: + rt_sdhci_cleanup_host(host); + + return ret; +} + +void rt_sdhci_ios_set(struct rt_mmc_host *mmc, struct rt_mmcsd_io_cfg *ios) +{ + struct rt_sdhci_host *host = mmc_priv(mmc); + rt_bool_t reinit_uhs = host->reinit_uhs; + rt_bool_t turning_on_clk = RT_FALSE; + rt_uint8_t ctrl; + + host->reinit_uhs = RT_FALSE; + + if (ios->power_mode == MMC_POWER_UNDEFINED) + return; + + if (host->flags & RT_SDHCI_DEVICE_DEAD) + { + if (mmc->supply.vmmc && ios->power_mode == MMC_POWER_OFF) + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); + return; + } + + if (ios->power_mode == MMC_POWER_OFF) + { + rt_sdhci_writel(host, 0, RT_SDHCI_SIGNAL_ENABLE); + sdhci_reinit(host); + } + + if (host->version >= RT_SDHCI_SPEC_300 && (ios->power_mode == MMC_POWER_UP) && !(host->quirks2 & RT_SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) + sdhci_preset_value_enable(host, RT_FALSE); + + if (!ios->clock || ios->clock != host->clock) + { + turning_on_clk = ios->clock && !host->clock; + + host->ops->set_clock(host, ios->clock); + host->clock = ios->clock; + + if (host->quirks & RT_SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK && host->clock) + { + host->timeout_clk = mmc->actual_clock ? mmc->actual_clock / 1000 : host->clock / 1000; + mmc->max_busy_timeout = + host->ops->get_max_timeout_count ? host->ops->get_max_timeout_count(host) : 1 << 27; + mmc->max_busy_timeout /= host->timeout_clk; + } + } + + if (host->ops->set_power) + host->ops->set_power(host, ios->power_mode, ios->vdd); + else + rt_sdhci_set_power(host, ios->power_mode, ios->vdd); + + host->ops->set_bus_width(host, ios->bus_width); + + if (!reinit_uhs && turning_on_clk && host->timing == ios->timing && host->version >= RT_SDHCI_SPEC_300 && !sdhci_presetable_values_change(host, ios)) + return; + + ctrl = rt_sdhci_readb(host, RT_SDHCI_HOST_CONTROL); + + if (!(host->quirks & RT_SDHCI_QUIRK_NO_HISPD_BIT)) + { + if (ios->timing == MMC_TIMING_SD_HS || ios->timing == MMC_TIMING_MMC_HS || ios->timing == MMC_TIMING_MMC_HS400 || ios->timing == MMC_TIMING_MMC_HS200 || ios->timing == MMC_TIMING_MMC_DDR52 || ios->timing == MMC_TIMING_UHS_SDR50 || ios->timing == MMC_TIMING_UHS_SDR104 || ios->timing == MMC_TIMING_UHS_DDR50 || ios->timing == MMC_TIMING_UHS_SDR25) + ctrl |= RT_SDHCI_CTRL_HISPD; + else + ctrl &= ~RT_SDHCI_CTRL_HISPD; + } + + if (host->version >= RT_SDHCI_SPEC_300) + { + rt_uint16_t clk, ctrl_2; + + clk = rt_sdhci_readw(host, RT_SDHCI_CLOCK_CONTROL); + if (clk & RT_SDHCI_CLOCK_CARD_EN) + { + clk &= ~RT_SDHCI_CLOCK_CARD_EN; + rt_sdhci_writew(host, clk, RT_SDHCI_CLOCK_CONTROL); + } + + rt_sdhci_writeb(host, ctrl, RT_SDHCI_HOST_CONTROL); + + if (!host->preset_enabled) + { + ctrl_2 = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2); + ctrl_2 &= ~RT_SDHCI_CTRL_DRV_TYPE_MASK; + if (ios->drv_type == MMC_SET_DRIVER_TYPE_A) + ctrl_2 |= RT_SDHCI_CTRL_DRV_TYPE_A; + else if (ios->drv_type == MMC_SET_DRIVER_TYPE_B) + ctrl_2 |= RT_SDHCI_CTRL_DRV_TYPE_B; + else if (ios->drv_type == MMC_SET_DRIVER_TYPE_C) + ctrl_2 |= RT_SDHCI_CTRL_DRV_TYPE_C; + else if (ios->drv_type == MMC_SET_DRIVER_TYPE_D) + ctrl_2 |= RT_SDHCI_CTRL_DRV_TYPE_D; + else + { + LOG_D("%s: invalid driver type, default to driver type B\n", + mmc_hostname(mmc)); + ctrl_2 |= RT_SDHCI_CTRL_DRV_TYPE_B; + } + + rt_sdhci_writew(host, ctrl_2, RT_SDHCI_HOST_CONTROL2); + host->drv_type = ios->drv_type; + } + + host->ops->set_uhs_signaling(host, ios->timing); + host->timing = ios->timing; + + if (sdhci_preset_needed(host, ios->timing)) + { + rt_uint16_t preset; + + sdhci_preset_value_enable(host, RT_TRUE); + preset = sdhci_get_preset_value(host); + ios->drv_type = FIELD_GET(RT_SDHCI_PRESET_DRV_MASK, + preset); + host->drv_type = ios->drv_type; + } + + host->ops->set_clock(host, host->clock); + } + else + rt_sdhci_writeb(host, ctrl, RT_SDHCI_HOST_CONTROL); +} +void rt_sdhci_free_host(struct rt_sdhci_host *host) +{ + rt_sdhci_cleanup_host(host); + rt_free(host); +} diff --git a/components/libc/compilers/common/cstring.c b/components/libc/compilers/common/cstring.c index 59c516d273e..41f6c658ceb 100644 --- a/components/libc/compilers/common/cstring.c +++ b/components/libc/compilers/common/cstring.c @@ -13,13 +13,13 @@ #include #include +#ifndef RT_USING_PICOLIBC /** * @brief erases the data in the n bytes of the memory starting at the * location pointed to by s, by writing zeros (bytes containing '\0') to that area. * * @note The bzero() function is deprecated (marked as LEGACY in POSIX. 1-2001). */ -#ifndef RT_USING_PICOLIBC void bzero(void* s, size_t n) { rt_memset(s, 0, n); @@ -46,12 +46,12 @@ void explicit_bzero(void* s, size_t n) } } -char* index(const char* s, int c) +char *index(const char* s, int c) { return strchr(s, c); } -char* rindex(const char* s, int c) +char *rindex(const char* s, int c) { return strrchr(s, c); } @@ -99,7 +99,7 @@ int ffsll(long long i) * * @note This function is GNU extension, available since glibc 2.1.91. */ -void* memrchr(const void* ptr, int ch, size_t pos) +void *memrchr(const void* ptr, int ch, size_t pos) { char* end = (char*)ptr + pos - 1; while (end != ptr) @@ -118,7 +118,7 @@ size_t strnlen(const char *s, size_t maxlen) return sc - s; } -char* strchrnul(const char* s, int c) +char *strchrnul(const char* s, int c) { while (*s != '\0' && *s != c) s++; diff --git a/components/libc/compilers/common/cwchar.c b/components/libc/compilers/common/cwchar.c index c808083331a..efa0a7ceb16 100644 --- a/components/libc/compilers/common/cwchar.c +++ b/components/libc/compilers/common/cwchar.c @@ -112,12 +112,12 @@ int wcwidth(wchar_t ucs) (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ (ucs >= 0xff00 && ucs <= 0xff5f) || /* Fullwidth Forms */ - (ucs >= 0xffe0 && ucs <= 0xffe6) // || - //#ifndef _WIN32 - // (ucs >= 0x20000 && ucs <= 0x2ffff) - //#else - // 0 - //#endif + (ucs >= 0xffe0 && ucs <= 0xffe6) || + #ifndef _WIN32 + (ucs >= 0x20000 && ucs <= 0x2ffff) + #else + 0 + #endif )); } diff --git a/components/lwp/arch/x86/i386/lwp_arch.c b/components/lwp/arch/x86/i386/lwp_arch.c index 4b054f969b1..c49e1989ae6 100644 --- a/components/lwp/arch/x86/i386/lwp_arch.c +++ b/components/lwp/arch/x86/i386/lwp_arch.c @@ -48,7 +48,7 @@ int arch_expand_user_stack(void *addr) else /* map failed, send signal SIGSEGV */ { #ifdef RT_USING_SIGNALS - dbg_log(DBG_ERROR, "[fault] thread %s mapped addr %p failed!\n", rt_thread_self()->parent.name, addr); + LOG_E("[fault] thread %s mapped addr %p failed!\n", rt_thread_self()->parent.name, addr); lwp_thread_kill(rt_thread_self(), SIGSEGV); ret = 1; /* return 1, will return back to intr, then check exit */ #endif @@ -57,7 +57,7 @@ int arch_expand_user_stack(void *addr) else /* not stack, send signal SIGSEGV */ { #ifdef RT_USING_SIGNALS - dbg_log(DBG_ERROR, "[fault] thread %s access unmapped addr %p!\n", rt_thread_self()->parent.name, addr); + LOG_E("[fault] thread %s access unmapped addr %p!\n", rt_thread_self()->parent.name, addr); lwp_thread_kill(rt_thread_self(), SIGSEGV); ret = 1; /* return 1, will return back to intr, then check exit */ #endif diff --git a/components/lwp/lwp.c b/components/lwp/lwp.c index e8092226cae..f991d0ecdbd 100644 --- a/components/lwp/lwp.c +++ b/components/lwp/lwp.c @@ -447,7 +447,7 @@ pid_t lwp_execve(char *filename, int debug, int argc, char **argv, char **envp) if (lwp == RT_NULL) { - dbg_log(DBG_ERROR, "lwp struct out of memory!\n"); + LOG_E("lwp struct out of memory!\n"); return -ENOMEM; } LOG_D("lwp malloc : %p, size: %d!", lwp, sizeof(struct rt_lwp)); diff --git a/components/lwp/lwp_syscall.c b/components/lwp/lwp_syscall.c index 3239c6cb046..598e19ef8ad 100644 --- a/components/lwp/lwp_syscall.c +++ b/components/lwp/lwp_syscall.c @@ -821,7 +821,7 @@ sysret_t sys_unlink(const char *pathname) sysret_t sys_nanosleep(const struct timespec *rqtp, struct timespec *rmtp) { int ret = 0; - dbg_log(DBG_LOG, "sys_nanosleep\n"); + LOG_D("sys_nanosleep\n"); if (!lwp_user_accessable((void *)rqtp, sizeof *rqtp)) return -EFAULT; @@ -4759,7 +4759,7 @@ sysret_t sys_clock_gettime(clockid_t clk, struct timespec *ts) sysret_t sys_clock_nanosleep(clockid_t clk, int flags, const struct timespec *rqtp, struct timespec *rmtp) { int ret = 0; - dbg_log(DBG_LOG, "sys_nanosleep\n"); + LOG_D("sys_nanosleep\n"); if (!lwp_user_accessable((void *)rqtp, sizeof *rqtp)) return -EFAULT; diff --git a/components/utilities/ulog/ulog_def.h b/components/utilities/ulog/ulog_def.h index 42869b15631..e84b19846cf 100644 --- a/components/utilities/ulog/ulog_def.h +++ b/components/utilities/ulog/ulog_def.h @@ -43,16 +43,10 @@ extern "C" { #undef DBG_WARNING #undef DBG_INFO #undef DBG_LOG -#undef dbg_log #define DBG_ERROR LOG_LVL_ERROR #define DBG_WARNING LOG_LVL_WARNING #define DBG_INFO LOG_LVL_INFO #define DBG_LOG LOG_LVL_DBG -#define dbg_log(level, ...) \ - if ((level) <= LOG_LVL) \ - { \ - ulog_output(level, LOG_TAG, RT_FALSE, __VA_ARGS__);\ - } #if !defined(LOG_TAG) /* compatible for rtdbg */ diff --git a/components/utilities/utest/utest.c b/components/utilities/utest/utest.c index 45068f3f7de..af51508ce51 100644 --- a/components/utilities/utest/utest.c +++ b/components/utilities/utest/utest.c @@ -11,12 +11,8 @@ #include #include #include - #include "utest.h" -#include - -#undef DBG_TAG -#undef DBG_LVL +#include "utest_log.h" #define DBG_TAG "utest" #ifdef UTEST_DEBUG @@ -191,7 +187,7 @@ static int utest_help(void) return 0; } -static void utest_run(const char *utest_name) +static void utest_do_run(const char *utest_name) { rt_size_t i; rt_uint32_t index; @@ -217,7 +213,7 @@ static void utest_run(const char *utest_name) { if (utest_name) { - int len = strlen(utest_name); + int len = rt_strlen(utest_name); if (utest_name[len - 1] == '*') { len -= 1; @@ -300,17 +296,38 @@ static void utest_run(const char *utest_name) } } -static void utest_thr_entry(const char *utest_name) +static void utest_thr_entry(void *para) { - /* see commit:0dc7b9a for details */ - rt_thread_mdelay(1000); + char *utest_name = (char *)para; + rt_thread_mdelay(1000); /* see commit:0dc7b9a for details */ + rt_kprintf("\n"); + utest_do_run(utest_name); +} - utest_run(utest_name); +static void utest_thread_create(const char *utest_name) +{ + rt_thread_t tid = RT_NULL; + tid = rt_thread_create("utest", + utest_thr_entry, (void *)utest_name, + UTEST_THREAD_STACK_SIZE, UTEST_THREAD_PRIORITY, 10); + if (tid != RT_NULL) + { + rt_thread_startup(tid); + } } -long utest_testcase_run(int argc, char** argv) +#ifdef RT_USING_CI_ACTION +static int utest_ci_action(void) { + tc_loop = 1; + utest_thread_create(RT_NULL); + return RT_EOK; +} +INIT_APP_EXPORT(utest_ci_action); +#endif /* RT_USING_CI_ACTION */ +int utest_testcase_run(int argc, char** argv) +{ static char utest_name[UTEST_NAME_MAX_LEN]; rt_memset(utest_name, 0x0, sizeof(utest_name)); @@ -318,27 +335,21 @@ long utest_testcase_run(int argc, char** argv) if (argc == 1) { - utest_run(RT_NULL); - return 0; + utest_thread_create(RT_NULL); } else if (argc == 2 || argc == 3 || argc == 4) { if (rt_strcmp(argv[1], "-thread") == 0) { - rt_thread_t tid = RT_NULL; if (argc == 3 || argc == 4) { rt_strncpy(utest_name, argv[2], sizeof(utest_name) -1); - - if (argc == 4) tc_loop = atoi(argv[3]); - } - tid = rt_thread_create("utest", - (void (*)(void *))utest_thr_entry, utest_name, - UTEST_THREAD_STACK_SIZE, UTEST_THREAD_PRIORITY, 10); - if (tid != NULL) - { - rt_thread_startup(tid); + if (argc == 4) + { + tc_loop = atoi(argv[3]); + } } + utest_thread_create(utest_name); } else if (rt_strcmp(argv[1], "-help") == 0) { @@ -347,8 +358,11 @@ long utest_testcase_run(int argc, char** argv) else { rt_strncpy(utest_name, argv[1], sizeof(utest_name) -1); - if (argc == 3) tc_loop = atoi(argv[2]); - utest_run(utest_name); + if (argc == 3) + { + tc_loop = atoi(argv[2]); + } + utest_do_run(utest_name); } } else @@ -356,7 +370,8 @@ long utest_testcase_run(int argc, char** argv) LOG_E("[ error ] at (%s:%d), in param error.", __func__, __LINE__); utest_help(); } - return 0; + + return RT_EOK; } MSH_CMD_EXPORT_ALIAS(utest_testcase_run, utest_run, utest_run [-thread or -help] [testcase name] [loop num]); @@ -378,13 +393,27 @@ void utest_unit_run(test_unit_func func, const char *unit_func_name) } } -void utest_assert(int value, const char *file, int line, const char *func, const char *msg) +/* +* utest_assert - assert function +* +* @param value - assert value +* @param file - file name +* @param line - line number +* @param func - function name +* @param msg - assert message +* +* @return - RT_TRUE: assert success; RT_FALSE: assert failed +*/ +rt_bool_t utest_assert(int value, const char *file, int line, const char *func, const char *msg) { + rt_bool_t rst = RT_FALSE; + if (!(value)) { local_utest.error = UTEST_FAILED; local_utest.failed_num ++; LOG_E("[ ASSERT ] [ unit ] at (%s); func: (%s:%d); msg: (%s)", file_basename(file), func, line, msg); + rst = RT_FALSE; } else { @@ -394,37 +423,49 @@ void utest_assert(int value, const char *file, int line, const char *func, const } local_utest.error = UTEST_PASSED; local_utest.passed_num ++; + rst = RT_TRUE; } + + return rst; } void utest_assert_string(const char *a, const char *b, rt_bool_t equal, const char *file, int line, const char *func, const char *msg) { + rt_bool_t rst = RT_FALSE; + if (a == RT_NULL || b == RT_NULL) { - utest_assert(0, file, line, func, msg); + rst = utest_assert(0, file, line, func, msg); } - - if (equal) + else { - if (rt_strcmp(a, b) == 0) + if (equal) { - utest_assert(1, file, line, func, msg); + if (rt_strcmp(a, b) == 0) + { + rst = utest_assert(1, file, line, func, msg); + } + else + { + rst = utest_assert(0, file, line, func, msg); + } } else { - utest_assert(0, file, line, func, msg); + if (rt_strcmp(a, b) == 0) + { + rst = utest_assert(0, file, line, func, msg); + } + else + { + rst = utest_assert(1, file, line, func, msg); + } } } - else + + if (!rst) { - if (rt_strcmp(a, b) == 0) - { - utest_assert(0, file, line, func, msg); - } - else - { - utest_assert(1, file, line, func, msg); - } + LOG_E("[ ASSERT ] [ unit ] str-a: (%s); str-b: (%s)", a, b); } } diff --git a/components/utilities/utest/utest_assert.h b/components/utilities/utest/utest_assert.h index ba76fc39ee7..8edfd1b9adb 100644 --- a/components/utilities/utest/utest_assert.h +++ b/components/utilities/utest/utest_assert.h @@ -19,7 +19,7 @@ extern "C" { #endif /* No need for the user to use this function directly */ -void utest_assert(int value, const char *file, int line, const char *func, const char *msg); +rt_bool_t utest_assert(int value, const char *file, int line, const char *func, const char *msg); /* No need for the user to use this function directly */ void utest_assert_string(const char *a, const char *b, rt_bool_t equal, const char *file, int line, const char *func, const char *msg); @@ -56,6 +56,9 @@ void utest_assert_buf(const char *a, const char *b, rt_size_t sz, rt_bool_t equa #define uassert_int_equal(a, b) __utest_assert((a) == (b), "(" #a ") not equal to (" #b ")") #define uassert_int_not_equal(a, b) __utest_assert((a) != (b), "(" #a ") equal to (" #b ")") +#define uassert_ptr_equal(a, b) __utest_assert((const void*)(a) == (const void*)(b), "(" #a ") not equal to (" #b ")") +#define uassert_ptr_not_equal(a, b) __utest_assert((const void*)(a) != (const void*)(b), "(" #a ") equal to (" #b ")") + #define uassert_str_equal(a, b) utest_assert_string((const char*)(a), (const char*)(b), RT_TRUE, __FILE__, __LINE__, __func__, "string not equal") #define uassert_str_not_equal(a, b) utest_assert_string((const char*)(a), (const char*)(b), RT_FALSE, __FILE__, __LINE__, __func__, "string equal") diff --git a/components/utilities/utest/utest_log.h b/components/utilities/utest/utest_log.h index 6aa54386a8b..e39d0d9344a 100644 --- a/components/utilities/utest/utest_log.h +++ b/components/utilities/utest/utest_log.h @@ -13,12 +13,12 @@ #include -#define UTEST_DEBUG +// #define UTEST_DEBUG #undef DBG_TAG #undef DBG_LVL -#define DBG_TAG "testcase" +#define DBG_TAG "utest" #ifdef UTEST_DEBUG #define DBG_LVL DBG_LOG #else diff --git a/documentation/doxygen/readme.md b/documentation/doxygen/readme.md index 3958b02ddb8..5228645937c 100644 --- a/documentation/doxygen/readme.md +++ b/documentation/doxygen/readme.md @@ -6,3 +6,43 @@ 4. Open the file ./Doxyfile 5. To tab `Run` , Click `Run doxygen` +# How to build & run doxygen html on Ubuntu + +The following steps are verified on Ubuntu 22.04: + +```shell +$ lsb_release -a +No LSB modules are available. +Distributor ID: Ubuntu +Description: Ubuntu 22.04.5 LTS +Release: 22.04 +Codename: jammy +``` + +The following packages (and dependents) need to be installed: + +```shell +$ sudo apt update +$ sudo apt install doxygen +$ sudo apt install graphviz +``` + +Assume that the path of RT-Thead code tree is $RTT, execute the following command to build html. + +```shell +$ cd $RTT/documentation/doxygen +$ rm -rf html +$ doxygen +``` + +A new html directory will be created and all the html files will be placed in this directory. + +If you want to quickly browse HTML locally (in Ubuntu environment), you can enter the html directory and start a local HTML server through Python. + +```shell +$ cd html +$ python3 -m http.server +Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ... +``` + +Open the browser and enter `http://:8000/index.html` to access the created html web pages. If it is a local access, then `` should be replaced by `localhost`. If it is a remote access, then `` should be replaced by the actual accessible IP address of the machine where HTML is located. diff --git a/include/rtdbg.h b/include/rtdbg.h index 10331edc0e0..080c722fc43 100644 --- a/include/rtdbg.h +++ b/include/rtdbg.h @@ -113,32 +113,6 @@ extern "C" { rt_kprintf("\n") #endif /* DBG_COLOR */ -/* - * static debug routine - * NOTE: This is a NOT RECOMMENDED API. Please using LOG_X API. - * It will be DISCARDED later. Because it will take up more resources. - */ -#define dbg_log(level, fmt, ...) \ - if ((level) <= DBG_LEVEL) \ - { \ - switch(level) \ - { \ - case DBG_ERROR: _DBG_LOG_HDR("E", 31); break; \ - case DBG_WARNING: _DBG_LOG_HDR("W", 33); break; \ - case DBG_INFO: _DBG_LOG_HDR("I", 32); break; \ - case DBG_LOG: _DBG_LOG_HDR("D", 0); break; \ - default: break; \ - } \ - rt_kprintf(fmt, ##__VA_ARGS__); \ - _DBG_COLOR(0); \ - } - -#define dbg_here \ - if ((DBG_LEVEL) <= DBG_LOG){ \ - rt_kprintf(DBG_SECTION_NAME " Here %s:%d\n", \ - __FUNCTION__, __LINE__); \ - } - #define dbg_log_line(lvl, color_n, fmt, ...) \ do \ { \ @@ -151,10 +125,6 @@ extern "C" { #define dbg_raw(...) rt_kprintf(__VA_ARGS__); #else -#define dbg_log(level, fmt, ...) -#define dbg_here -#define dbg_enter -#define dbg_exit #define dbg_log_line(lvl, color_n, fmt, ...) #define dbg_raw(...) #endif /* DBG_ENABLE */ diff --git a/src/Kconfig b/src/Kconfig index e7f2a5108b6..d4cad60d953 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -242,6 +242,8 @@ menuconfig RT_USING_DEBUG endif config RT_USING_CI_ACTION + bool "Enable CI Action build mode" + select RT_USING_UTEST default n help Identify that the environment is CI Action. diff --git a/src/klibc/Kconfig b/src/klibc/Kconfig index 0b00d10b8e8..bc90e90e8fa 100644 --- a/src/klibc/Kconfig +++ b/src/klibc/Kconfig @@ -1,7 +1,7 @@ menu "klibc options" menu "rt_vsnprintf options" config RT_KLIBC_USING_LIBC_VSNPRINTF - bool "Enable rt_vsnprintf to use libc vsscanf" + bool "Enable rt_vsnprintf to use libc vsnprintf" default n config RT_KLIBC_USING_VSNPRINTF_LONGLONG @@ -249,4 +249,10 @@ menu "klibc options" bool "Enable rt_strnlen to use user-defined version" default n endmenu # rt_strnlen options + + config RT_UTEST_TC_USING_KLIBC + bool "Enable klibc utest cases" + select RT_USING_UTEST + default n + endmenu diff --git a/src/klibc/rt_vsnprintf_tiny.c b/src/klibc/rt_vsnprintf_tiny.c index 75c89512d91..7ef610c51da 100644 --- a/src/klibc/rt_vsnprintf_tiny.c +++ b/src/klibc/rt_vsnprintf_tiny.c @@ -448,7 +448,7 @@ int rt_vsnprintf(char *buf, rt_size_t size, const char *fmt, va_list args) s = va_arg(args, char *); if (!s) { - s = "(NULL)"; + s = "(null)"; } for (len = 0; (len != field_width) && (s[len] != '\0'); len++); diff --git a/src/klibc/utest/SConscript b/src/klibc/utest/SConscript new file mode 100644 index 00000000000..43ec5e97a5f --- /dev/null +++ b/src/klibc/utest/SConscript @@ -0,0 +1,10 @@ +from building import * + +src = [] + +if GetDepend('RT_USING_CI_ACTION') or GetDepend('RT_UTEST_TC_USING_KLIBC'): + src += Glob('TC_*.c') + +group = DefineGroup('utestcases', src, depend = ['']) + +Return('group') diff --git a/src/klibc/utest/TC_rt_memcmp.c b/src/klibc/utest/TC_rt_memcmp.c new file mode 100644 index 00000000000..80839007cbc --- /dev/null +++ b/src/klibc/utest/TC_rt_memcmp.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2020-05-06 Phillip Johnston the first version + * 2024-12-24 Meco Man port to utest + */ + +#include +#include + +static rt_err_t utest_tc_init(void) +{ + return RT_EOK; +} + +static rt_err_t utest_tc_cleanup(void) +{ + return RT_EOK; +} + +static void TC_rt_memcmp_str(void) +{ + const char* s = "abc 123"; + + uassert_int_equal(rt_memcmp("abc", "abc", 4), 0); + uassert_int_equal(rt_memcmp(s, "abc", 3), 0); + uassert_int_equal(rt_memcmp("abc", s, 3), 0); + + /* The following tests intentionally use a length > 3 */ + /* To test what rt_memcmp does in such a situation */ + uassert_int_equal(!!(rt_memcmp(s, "abc", 6) > 0), 1); + uassert_int_equal(!!(rt_memcmp("abc", s, 6) < 0), 1); + + /* Check RT_NULL input handling */ + uassert_int_not_equal(rt_memcmp("abc", RT_NULL, 3), 0); + uassert_int_not_equal(rt_memcmp(RT_NULL, "abc", 3), 0); + + /* Check that two RT_NULL strings will match */ + uassert_int_equal(rt_memcmp(RT_NULL, RT_NULL, 0), 0); +} + +static void utest_do_tc(void) +{ + UTEST_UNIT_RUN(TC_rt_memcmp_str); +} + +UTEST_TC_EXPORT(utest_do_tc, "klibc.rt_memcmp", utest_tc_init, utest_tc_cleanup, 1000); diff --git a/src/klibc/utest/TC_rt_memcpy.c b/src/klibc/utest/TC_rt_memcpy.c new file mode 100644 index 00000000000..f5cbdaa8471 --- /dev/null +++ b/src/klibc/utest/TC_rt_memcpy.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2020-05-06 Phillip Johnston the first version + * 2024-12-24 Meco Man port to utest + */ + +#include +#include + +static rt_err_t utest_tc_init(void) +{ + return RT_EOK; +} + +static rt_err_t utest_tc_cleanup(void) +{ + return RT_EOK; +} + +#define N 80 /**< Define the constant N for buffer size as 80 */ +static char buf[512] = {0}; /**< Define a static buffer of 512 bytes, initialized to 0 */ + +/** + * Align a given pointer to a 64-byte boundary. + * @param p The pointer to align. + * @return The aligned pointer. + */ +static void* aligned(void* p) +{ + return (void*)(((intptr_t)p + 63) & -64); +} + +/** + * Test memory copy with alignment. + * @param dalign The alignment offset for the destination buffer. + * @param salign The alignment offset for the source buffer. + * @param len The length of data to copy. + */ +static void test_align(unsigned dalign, unsigned salign, size_t len) +{ + char* src = aligned(buf); /**< Source buffer starting address, 64-byte aligned */ + char* dst = aligned(buf + 128); /**< Destination buffer starting address, 64-byte aligned from buf+128 */ + char* want = aligned(buf + 256); /**< Expected result buffer starting address, 64-byte aligned from buf+256 */ + char* p; /**< Pointer to receive the return value of rt_memcpy */ + unsigned i; + + /** Assert that the source alignment offset plus length does not exceed N */ + uassert_false(salign + len > N); + /** Assert that the destination alignment offset plus length does not exceed N */ + uassert_false(dalign + len > N); + + /** Initialize all buffers with '#' or ' ' */ + for(i = 0; i < N; i++) + { + src[i] = '#'; + dst[i] = want[i] = ' '; + } + + /** Set data in the specified alignment offsets of the source and expected result buffers */ + for(i = 0; i < len; i++) + { + src[salign + i] = want[dalign + i] = (char)('0' + i); + } + + /** Call rt_memcpy to copy data */ + p = rt_memcpy(dst + dalign, src + salign, len); + + /** Assert that the return value of rt_memcpy is the pointer to the start of the copied data in the destination buffer */ + uassert_ptr_equal(p, dst + dalign); + + /** Assert that the content of the destination buffer matches the expected result buffer */ + for(i = 0; i < N; i++) + { + uassert_int_equal(dst[i], want[i]); + } +} + +/** + * Test case to iterate over all possible alignment offsets and length combinations. + */ +static void TC_rt_memcpy_align(void) +{ + for(unsigned i = 0; i < 16; i++) /**< Iterate over source alignment offsets from 0 to 15 */ + { + for(unsigned j = 0; j < 16; j++) /**< Iterate over destination alignment offsets from 0 to 15 */ + { + for(size_t k = 0; k < 64; k++) /**< Iterate over data lengths from 0 to 63 */ + { + test_align(i, j, k); /**< Call the test_align function */ + } + } + } +} + +static void TC_rt_memcpy_str(void) +{ + const char src[] = "Hello, memcpy!"; + char dest[20] = {0}; + rt_memcpy(dest, src, sizeof(src)); + uassert_true(rt_strcmp(src, dest) == 0); +} + +static void utest_do_tc(void) +{ + UTEST_UNIT_RUN(TC_rt_memcpy_str); + UTEST_UNIT_RUN(TC_rt_memcpy_align); +} + +UTEST_TC_EXPORT(utest_do_tc, "klibc.rt_memcpy", utest_tc_init, utest_tc_cleanup, 1000); diff --git a/src/klibc/utest/TC_rt_sprintf.c b/src/klibc/utest/TC_rt_sprintf.c new file mode 100644 index 00000000000..86faf3ad1f4 --- /dev/null +++ b/src/klibc/utest/TC_rt_sprintf.c @@ -0,0 +1,1075 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-12-22 Meco Man the first version + */ + +/** + * @author (c) Eyal Rozenberg + * 2021-2022, Haifa, Palestine/Israel + * @author (c) Marco Paland (info@paland.com) + * 2014-2019, PALANDesign Hannover, Germany + * + * @note Others have made smaller contributions to this file: see the + * contributors page at https://github.com/eyalroz/printf/graphs/contributors + * or ask one of the authors. The original code for exponential specifiers was + * contributed by Martijn Jasperse . + * + * @brief Small stand-alone implementation of the printf family of functions + * (`(v)printf`, `(v)s(n)printf` etc., geared towards use on embedded systems with + * a very limited resources. + * + * @note the implementations are thread-safe; re-entrant; use no functions from + * the standard library; and do not dynamically allocate any memory. + * + * @license The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* +Unported test cases: +buffer_length +extremal_signed_integer_values +extremal_unsigned_integer_values +*/ + +#include +#include +#include + +#define base_buffer_size 100 + +#define SPRINTF_CHECK(expected, buffer, format, ...) \ +do { \ + rt_memset(buffer, 0xCC, base_buffer_size); \ + rt_sprintf(buffer, format, ##__VA_ARGS__); \ + uassert_str_equal(expected, buffer); \ +} while (0) + +#define SPRINTF_TEST_CASE_NAME(testname) TC_##testname +#define SPRINTF_TEST_CASE(testname) static void SPRINTF_TEST_CASE_NAME(testname)(void) + +static rt_err_t utest_tc_init(void) +{ + return RT_EOK; +} + +static rt_err_t utest_tc_cleanup(void) +{ + return RT_EOK; +} + +SPRINTF_TEST_CASE(space_flag) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK(" 42", buffer, "% d", 42); + SPRINTF_CHECK("-42", buffer, "% d", -42); + SPRINTF_CHECK(" 42", buffer, "% 5d", 42); + SPRINTF_CHECK(" -42", buffer, "% 5d", -42); + SPRINTF_CHECK(" 42", buffer, "% 15d", 42); + SPRINTF_CHECK(" -42", buffer, "% 15d", -42); + SPRINTF_CHECK(" -42", buffer, "% 15d", -42); +#ifdef RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS + SPRINTF_CHECK(" -42.987", buffer, "% 15.3f", -42.987); + SPRINTF_CHECK(" 42.987", buffer, "% 15.3f", 42.987); +#endif + SPRINTF_CHECK(" 1024", buffer, "% d", 1024); + SPRINTF_CHECK("-1024", buffer, "% d", -1024); + SPRINTF_CHECK(" 1024", buffer, "% i", 1024); + SPRINTF_CHECK("-1024", buffer, "% i", -1024); +} + +SPRINTF_TEST_CASE(space_flag__non_standard_format) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK("Hello testing", buffer, "% s", "Hello testing"); + SPRINTF_CHECK("1024", buffer, "% u", 1024); +#ifdef RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS + SPRINTF_CHECK("1024", buffer, "% I16u", (uint16_t) 1024); + SPRINTF_CHECK("1024", buffer, "% I32u", (uint32_t) 1024); + SPRINTF_CHECK("1024", buffer, "% I64u", (uint64_t) 1024); +#endif + SPRINTF_CHECK("4294966272", buffer, "% u", 4294966272U); +#ifdef RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS + SPRINTF_CHECK("4294966272", buffer, "% I32u", (uint32_t) 4294966272U); + SPRINTF_CHECK("4294966272", buffer, "% I64u", (uint64_t) 4294966272U); +#endif + SPRINTF_CHECK("777", buffer, "% o", 511); + SPRINTF_CHECK("37777777001", buffer, "% o", 4294966785U); + SPRINTF_CHECK("1234abcd", buffer, "% x", 305441741); + SPRINTF_CHECK("edcb5433", buffer, "% x", 3989525555U); + SPRINTF_CHECK("1234ABCD", buffer, "% X", 305441741); + SPRINTF_CHECK("EDCB5433", buffer, "% X", 3989525555U); + SPRINTF_CHECK("x", buffer, "% c", 'x'); +} + +SPRINTF_TEST_CASE(plus_flag) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK("+42", buffer, "%+d", 42); + SPRINTF_CHECK("-42", buffer, "%+d", -42); + SPRINTF_CHECK(" +42", buffer, "%+5d", 42); + SPRINTF_CHECK(" -42", buffer, "%+5d", -42); + SPRINTF_CHECK(" +42", buffer, "%+15d", 42); + SPRINTF_CHECK(" -42", buffer, "%+15d", -42); + SPRINTF_CHECK("+1024", buffer, "%+d", 1024); + SPRINTF_CHECK("-1024", buffer, "%+d", -1024); + SPRINTF_CHECK("+1024", buffer, "%+i", 1024); + SPRINTF_CHECK("-1024", buffer, "%+i", -1024); +#ifdef RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS + SPRINTF_CHECK("+1024", buffer, "%+I16d", (int16_t) 1024); + SPRINTF_CHECK("-1024", buffer, "%+I16d", (int16_t) -1024); + SPRINTF_CHECK("+1024", buffer, "%+I32d", (int32_t) 1024); + SPRINTF_CHECK("-1024", buffer, "%+I32d", (int32_t) -1024); + SPRINTF_CHECK("+1024", buffer, "%+I64d", (int64_t) 1024); + SPRINTF_CHECK("-1024", buffer, "%+I64d", (int64_t) -1024); +#endif + SPRINTF_CHECK("+", buffer, "%+.0d", 0); +} + +SPRINTF_TEST_CASE(plus_flag__non_standard_format) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK("Hello testing", buffer, "%+s", "Hello testing"); + SPRINTF_CHECK("1024", buffer, "%+u", 1024); +#ifdef RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS + SPRINTF_CHECK("1024", buffer, "%+I32u", (uint32_t) 1024); +#endif + SPRINTF_CHECK("4294966272", buffer, "%+u", 4294966272U); +#ifdef RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS + SPRINTF_CHECK("4294966272", buffer, "%+I32u", (uint32_t) 4294966272U); +#endif + SPRINTF_CHECK("777", buffer, "%+o", 511); + SPRINTF_CHECK("37777777001", buffer, "%+o", 4294966785U); + SPRINTF_CHECK("1234abcd", buffer, "%+x", 305441741); + SPRINTF_CHECK("edcb5433", buffer, "%+x", 3989525555U); + SPRINTF_CHECK("1234ABCD", buffer, "%+X", 305441741); + SPRINTF_CHECK("EDCB5433", buffer, "%+X", 3989525555U); + SPRINTF_CHECK("x", buffer, "%+c", 'x'); +} + +SPRINTF_TEST_CASE(zero_flag) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK("42", buffer, "%0d", 42); + SPRINTF_CHECK("42", buffer, "%0ld", 42L); + SPRINTF_CHECK("-42", buffer, "%0d", -42); + SPRINTF_CHECK("00042", buffer, "%05d", 42); + SPRINTF_CHECK("-0042", buffer, "%05d", -42); + SPRINTF_CHECK("000000000000042", buffer, "%015d", 42); + SPRINTF_CHECK("-00000000000042", buffer, "%015d", -42); +#ifdef RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS + SPRINTF_CHECK("000000000042.12", buffer, "%015.2f", 42.1234); + SPRINTF_CHECK("00000000042.988", buffer, "%015.3f", 42.9876); + SPRINTF_CHECK("-00000042.98760", buffer, "%015.5f", -42.9876); +#endif +} + +SPRINTF_TEST_CASE(minus_flag) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK("42", buffer, "%-d", 42); + SPRINTF_CHECK("-42", buffer, "%-d", -42); + SPRINTF_CHECK("42 ", buffer, "%-5d", 42); + SPRINTF_CHECK("-42 ", buffer, "%-5d", -42); + SPRINTF_CHECK("42 ", buffer, "%-15d", 42); + SPRINTF_CHECK("-42 ", buffer, "%-15d", -42); +} + +SPRINTF_TEST_CASE(minus_flag_and_non_standard_zero_modifier_for_integers) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK("42", buffer, "%-0d", 42); + SPRINTF_CHECK("-42", buffer, "%-0d", -42); + SPRINTF_CHECK("42 ", buffer, "%-05d", 42); + SPRINTF_CHECK("-42 ", buffer, "%-05d", -42); + SPRINTF_CHECK("42 ", buffer, "%-015d", 42); + SPRINTF_CHECK("-42 ", buffer, "%-015d", -42); + SPRINTF_CHECK("42", buffer, "%0-d", 42); + SPRINTF_CHECK("-42", buffer, "%0-d", -42); + SPRINTF_CHECK("42 ", buffer, "%0-5d", 42); + SPRINTF_CHECK("-42 ", buffer, "%0-5d", -42); + SPRINTF_CHECK("42 ", buffer, "%0-15d", 42); + SPRINTF_CHECK("-42 ", buffer, "%0-15d", -42); + +#ifdef RT_KLIBC_USING_VSNPRINTF_STANDARD +#ifdef RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS + SPRINTF_CHECK("-4.200e+01 ", buffer, "%0-15.3e", -42.); + SPRINTF_CHECK("-42 ", buffer, "%0-15.3g", -42.); +#else + SPRINTF_CHECK("e", buffer, "%0-15.3e", -42.); + SPRINTF_CHECK("g", buffer, "%0-15.3g", -42.); +#endif /* RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS */ +#endif /* RT_KLIBC_USING_VSNPRINTF_STANDARD */ +} + +SPRINTF_TEST_CASE(sharp_flag) +{ + char buffer[base_buffer_size]; +#ifdef RT_KLIBC_USING_VSNPRINTF_STANDARD + SPRINTF_CHECK("0", buffer, "%#o", 0); + SPRINTF_CHECK("0", buffer, "%#0o", 0); +#endif + SPRINTF_CHECK("0", buffer, "%#.0o", 0); +#ifdef RT_KLIBC_USING_VSNPRINTF_STANDARD + SPRINTF_CHECK("0", buffer, "%#.1o", 0); + SPRINTF_CHECK(" 0", buffer, "%#4o", 0); + SPRINTF_CHECK("0000", buffer, "%#.4o", 0); +#endif + SPRINTF_CHECK("01", buffer, "%#o", 1); + SPRINTF_CHECK("01", buffer, "%#0o", 1); +#ifdef RT_KLIBC_USING_VSNPRINTF_STANDARD + SPRINTF_CHECK("01", buffer, "%#.0o", 1); +#endif + SPRINTF_CHECK("01", buffer, "%#.1o", 1); + SPRINTF_CHECK(" 01", buffer, "%#4o", 1); +#ifdef RT_KLIBC_USING_VSNPRINTF_STANDARD + SPRINTF_CHECK("0001", buffer, "%#.4o", 1); +#endif + SPRINTF_CHECK("0x1001", buffer, "%#04x", 0x1001); + SPRINTF_CHECK("01001", buffer, "%#04o", 01001); +#ifdef RT_KLIBC_USING_VSNPRINTF_STANDARD + SPRINTF_CHECK("", buffer, "%#.0x", 0); +#endif + SPRINTF_CHECK("0x0000614e", buffer, "%#.8x", 0x614e); +} + +SPRINTF_TEST_CASE(sharp_flag__non_standard_format) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK("0b110", buffer, "%#b", 6); + SPRINTF_CHECK("0b11111111", buffer, "%#010b", 0xff); + SPRINTF_CHECK("0b011111111", buffer, "%#011b", 0xff); + SPRINTF_CHECK("077", buffer, "%#03o", 077); + SPRINTF_CHECK("0077", buffer, "%#04o", 077); +} + +#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG +SPRINTF_TEST_CASE(sharp_flag_with_long_long) +{ + char buffer[base_buffer_size]; +#ifdef RT_KLIBC_USING_VSNPRINTF_STANDARD + SPRINTF_CHECK("0", buffer, "%#llo", (long long) 0); + SPRINTF_CHECK("0", buffer, "%#0llo", (long long) 0); +#endif /* RT_KLIBC_USING_VSNPRINTF_STANDARD */ + SPRINTF_CHECK("0", buffer, "%#.0llo", (long long) 0); +#ifdef RT_KLIBC_USING_VSNPRINTF_STANDARD + SPRINTF_CHECK("0", buffer, "%#.1llo", (long long) 0); + SPRINTF_CHECK(" 0", buffer, "%#4llo", (long long) 0); + SPRINTF_CHECK("0000", buffer, "%#.4llo", (long long) 0); +#endif /* RT_KLIBC_USING_VSNPRINTF_STANDARD */ + SPRINTF_CHECK("01", buffer, "%#llo", (long long) 1); + SPRINTF_CHECK("01", buffer, "%#0llo", (long long) 1); +#ifdef RT_KLIBC_USING_VSNPRINTF_STANDARD + SPRINTF_CHECK("01", buffer, "%#.0llo", (long long) 1); +#endif /* RT_KLIBC_USING_VSNPRINTF_STANDARD */ + SPRINTF_CHECK("01", buffer, "%#.1llo", (long long) 1); + SPRINTF_CHECK(" 01", buffer, "%#4llo", (long long) 1); +#ifdef RT_KLIBC_USING_VSNPRINTF_STANDARD + SPRINTF_CHECK("0001", buffer, "%#.4llo", (long long) 1); +#endif /* RT_KLIBC_USING_VSNPRINTF_STANDARD */ + SPRINTF_CHECK("0x1001", buffer, "%#04llx", (long long) 0x1001); + SPRINTF_CHECK("01001", buffer, "%#04llo", (long long) 01001); +#ifdef RT_KLIBC_USING_VSNPRINTF_STANDARD + SPRINTF_CHECK("", buffer, "%#.0llx", (long long) 0); +#endif /* RT_KLIBC_USING_VSNPRINTF_STANDARD */ + SPRINTF_CHECK("0x0000614e", buffer, "%#.8llx", (long long) 0x614e); +} + +SPRINTF_TEST_CASE(sharp_flag_with_long_long__non_standard_format) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK("0b110", buffer, "%#llb", (long long) 6); +} +#endif /* RT_KLIBC_USING_VSNPRINTF_LONGLONG */ + +SPRINTF_TEST_CASE(specifier) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK("Hello testing", buffer, "Hello testing"); + SPRINTF_CHECK("Hello testing", buffer, "%s", "Hello testing"); + SPRINTF_CHECK("(null)", buffer, "%s", (const char *) RT_NULL); + SPRINTF_CHECK("1024", buffer, "%d", 1024); +#if INT_MAX >= 2147483647LL + SPRINTF_CHECK("2147483647", buffer, "%d", 2147483647); + SPRINTF_CHECK("4294966272", buffer, "%u", 4294966272U); + SPRINTF_CHECK("37777777001", buffer, "%o", 4294966785U); + SPRINTF_CHECK("1234abcd", buffer, "%x", 305441741); + SPRINTF_CHECK("edcb5433", buffer, "%x", 3989525555U); + SPRINTF_CHECK("1234ABCD", buffer, "%X", 305441741); + SPRINTF_CHECK("EDCB5433", buffer, "%X", 3989525555U); +#endif + SPRINTF_CHECK("-1024", buffer, "%d", -1024); + SPRINTF_CHECK("1024", buffer, "%i", 1024); + SPRINTF_CHECK("-1024", buffer, "%i", -1024); + SPRINTF_CHECK("1024", buffer, "%u", 1024); + SPRINTF_CHECK("777", buffer, "%o", 511); + SPRINTF_CHECK("%", buffer, "%%"); + +#ifdef RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS + SPRINTF_CHECK("127", buffer, "%I8d", (int8_t) 127LL); +#if (SHRT_MAX >= 32767) + SPRINTF_CHECK("32767", buffer, "%I16d", (int16_t) 32767LL); +#endif +#if (LLONG_MAX >= 2147483647) + SPRINTF_CHECK("2147483647", buffer, "%I32d", (int32_t) 2147483647LL); +#if (LLONG_MAX >= 9223372036854775807LL) + SPRINTF_CHECK("9223372036854775807", buffer, "%I64d", (int64_t) 9223372036854775807LL); +#endif +#endif +#endif /* RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS */ +} + +SPRINTF_TEST_CASE(width) +{ + char buffer[base_buffer_size]; +#ifdef RT_KLIBC_USING_VSNPRINTF_STANDARD + SPRINTF_CHECK("Hello testing", buffer, "%1s", "Hello testing"); +#endif /* RT_KLIBC_USING_VSNPRINTF_STANDARD */ + SPRINTF_CHECK("1024", buffer, "%1d", 1024); + SPRINTF_CHECK("-1024", buffer, "%1d", -1024); + SPRINTF_CHECK("1024", buffer, "%1i", 1024); + SPRINTF_CHECK("-1024", buffer, "%1i", -1024); + SPRINTF_CHECK("1024", buffer, "%1u", 1024); +#ifdef RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS + SPRINTF_CHECK("1024", buffer, "%1I16u", (uint16_t) 1024); + SPRINTF_CHECK("1024", buffer, "%1I32u", (uint32_t) 1024); + SPRINTF_CHECK("1024", buffer, "%1I64u", (uint64_t) 1024); +#endif /* RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS */ + SPRINTF_CHECK("4294966272", buffer, "%1u", 4294966272U); +#ifdef RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS + SPRINTF_CHECK("4294966272", buffer, "%1I32u", (uint32_t) 4294966272U); + SPRINTF_CHECK("4294966272", buffer, "%1I64u", (uint64_t) 4294966272U); +#endif /* RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS */ + SPRINTF_CHECK("777", buffer, "%1o", 511); + SPRINTF_CHECK("37777777001", buffer, "%1o", 4294966785U); + SPRINTF_CHECK("1234abcd", buffer, "%1x", 305441741); + SPRINTF_CHECK("edcb5433", buffer, "%1x", 3989525555U); + SPRINTF_CHECK("1234ABCD", buffer, "%1X", 305441741); + SPRINTF_CHECK("EDCB5433", buffer, "%1X", 3989525555U); + SPRINTF_CHECK("x", buffer, "%1c", 'x'); +} + +SPRINTF_TEST_CASE(width_20) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK(" Hello", buffer, "%20s", "Hello"); + SPRINTF_CHECK(" 1024", buffer, "%20d", 1024); + SPRINTF_CHECK(" -1024", buffer, "%20d", -1024); + SPRINTF_CHECK(" 1024", buffer, "%20i", 1024); + SPRINTF_CHECK(" -1024", buffer, "%20i", -1024); + SPRINTF_CHECK(" 0", buffer, "%20i", 0); + SPRINTF_CHECK(" 1024", buffer, "%20u", 1024); +#ifdef RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS + SPRINTF_CHECK(" 1024", buffer, "%20I16u", (uint16_t) 1024); + SPRINTF_CHECK(" 1024", buffer, "%20I32u", (uint32_t) 1024); + SPRINTF_CHECK(" 1024", buffer, "%20I64u", (uint64_t) 1024); +#endif /* RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS */ + SPRINTF_CHECK(" 4294966272", buffer, "%20u", 4294966272U); +#ifdef RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS + SPRINTF_CHECK(" 4294966272", buffer, "%20I32u", (uint32_t) 4294966272U); + SPRINTF_CHECK(" 4294966272", buffer, "%20I64u", (uint64_t) 4294966272U); +#endif /* RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS */ + SPRINTF_CHECK(" 777", buffer, "%20o", 511); + SPRINTF_CHECK(" 37777777001", buffer, "%20o", 4294966785U); + SPRINTF_CHECK(" 1234abcd", buffer, "%20x", 305441741); + SPRINTF_CHECK(" edcb5433", buffer, "%20x", 3989525555U); + SPRINTF_CHECK(" 1234ABCD", buffer, "%20X", 305441741); + SPRINTF_CHECK(" EDCB5433", buffer, "%20X", 3989525555U); + SPRINTF_CHECK(" 0", buffer, "%20X", 0); + SPRINTF_CHECK(" 0", buffer, "%20X", 0U); +#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG + SPRINTF_CHECK(" 0", buffer, "%20llX", 0ULL); +#endif /* RT_KLIBC_USING_VSNPRINTF_LONGLONG */ + SPRINTF_CHECK(" x", buffer, "%20c", 'x'); +} + +SPRINTF_TEST_CASE(width_asterisk_20) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK(" Hello", buffer, "%*s", 20, "Hello"); + SPRINTF_CHECK(" 1024", buffer, "%*d", 20, 1024); + SPRINTF_CHECK(" -1024", buffer, "%*d", 20, -1024); + SPRINTF_CHECK(" 1024", buffer, "%*i", 20, 1024); + SPRINTF_CHECK(" -1024", buffer, "%*i", 20, -1024); + SPRINTF_CHECK(" 1024", buffer, "%*u", 20, 1024); +#ifdef RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS + SPRINTF_CHECK(" 1024", buffer, "%*I16u", 20, (uint16_t) 1024); + SPRINTF_CHECK(" 1024", buffer, "%*I32u", 20, (uint32_t) 1024); + SPRINTF_CHECK(" 1024", buffer, "%*I64u", 20, (uint64_t) 1024); +#endif /* RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS */ + SPRINTF_CHECK(" 4294966272", buffer, "%*u", 20, 4294966272U); +#ifdef RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS + SPRINTF_CHECK(" 4294966272", buffer, "%*I32u", 20, (uint32_t) 4294966272U); + SPRINTF_CHECK(" 4294966272", buffer, "%*I64u", 20, (uint64_t) 4294966272U); +#endif /* RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS */ + SPRINTF_CHECK(" 777", buffer, "%*o", 20, 511); + SPRINTF_CHECK(" 37777777001", buffer, "%*o", 20, 4294966785U); + SPRINTF_CHECK(" 1234abcd", buffer, "%*x", 20, 305441741); + SPRINTF_CHECK(" edcb5433", buffer, "%*x", 20, 3989525555U); + SPRINTF_CHECK(" 1234ABCD", buffer, "%*X", 20, 305441741); + SPRINTF_CHECK(" EDCB5433", buffer, "%*X", 20, 3989525555U); + SPRINTF_CHECK(" x", buffer, "%*c", 20, 'x'); +} + +SPRINTF_TEST_CASE(width_minus_20) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK("Hello ", buffer, "%-20s", "Hello"); + SPRINTF_CHECK("1024 ", buffer, "%-20d", 1024); + SPRINTF_CHECK("-1024 ", buffer, "%-20d", -1024); + SPRINTF_CHECK("1024 ", buffer, "%-20i", 1024); + SPRINTF_CHECK("-1024 ", buffer, "%-20i", -1024); + SPRINTF_CHECK("1024 ", buffer, "%-20u", 1024); +#ifdef RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS + SPRINTF_CHECK("1024.1234 ", buffer, "%-20.4f", 1024.1234); +#endif /* RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS */ + SPRINTF_CHECK("4294966272 ", buffer, "%-20u", 4294966272U); + SPRINTF_CHECK("777 ", buffer, "%-20o", 511); + SPRINTF_CHECK("37777777001 ", buffer, "%-20o", 4294966785U); + SPRINTF_CHECK("1234abcd ", buffer, "%-20x", 305441741); + SPRINTF_CHECK("edcb5433 ", buffer, "%-20x", 3989525555U); + SPRINTF_CHECK("1234ABCD ", buffer, "%-20X", 305441741); + SPRINTF_CHECK("EDCB5433 ", buffer, "%-20X", 3989525555U); + SPRINTF_CHECK("x ", buffer, "%-20c", 'x'); + SPRINTF_CHECK("| 9| |9 | | 9|", buffer, "|%5d| |%-2d| |%5d|", 9, 9, 9); + SPRINTF_CHECK("| 10| |10| | 10|", buffer, "|%5d| |%-2d| |%5d|", 10, 10, 10); + SPRINTF_CHECK("| 9| |9 | | 9|", buffer, "|%5d| |%-12d| |%5d|", 9, 9, 9); + SPRINTF_CHECK("| 10| |10 | | 10|", buffer, "|%5d| |%-12d| |%5d|", 10, 10, 10); +} + +SPRINTF_TEST_CASE(width_0_minus_20) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK("Hello ", buffer, "%0-20s", "Hello"); + SPRINTF_CHECK("1024 ", buffer, "%0-20d", 1024); + SPRINTF_CHECK("-1024 ", buffer, "%0-20d", -1024); + SPRINTF_CHECK("1024 ", buffer, "%0-20i", 1024); + SPRINTF_CHECK("-1024 ", buffer, "%0-20i", -1024); + SPRINTF_CHECK("1024 ", buffer, "%0-20u", 1024); + SPRINTF_CHECK("4294966272 ", buffer, "%0-20u", 4294966272U); + SPRINTF_CHECK("777 ", buffer, "%0-20o", 511); + SPRINTF_CHECK("37777777001 ", buffer, "%0-20o", 4294966785U); + SPRINTF_CHECK("1234abcd ", buffer, "%0-20x", 305441741); + SPRINTF_CHECK("edcb5433 ", buffer, "%0-20x", 3989525555U); + SPRINTF_CHECK("1234ABCD ", buffer, "%0-20X", 305441741); + SPRINTF_CHECK("EDCB5433 ", buffer, "%0-20X", 3989525555U); + SPRINTF_CHECK("x ", buffer, "%0-20c", 'x'); +} + +SPRINTF_TEST_CASE(padding_20) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK("00000000000000001024", buffer, "%020d", 1024); + SPRINTF_CHECK("-0000000000000001024", buffer, "%020d", -1024); + SPRINTF_CHECK("00000000000000001024", buffer, "%020i", 1024); + SPRINTF_CHECK("-0000000000000001024", buffer, "%020i", -1024); + SPRINTF_CHECK("00000000000000001024", buffer, "%020u", 1024); + SPRINTF_CHECK("00000000004294966272", buffer, "%020u", 4294966272U); + SPRINTF_CHECK("00000000000000000777", buffer, "%020o", 511); + SPRINTF_CHECK("00000000037777777001", buffer, "%020o", 4294966785U); + SPRINTF_CHECK("0000000000001234abcd", buffer, "%020x", 305441741); + SPRINTF_CHECK("000000000000edcb5433", buffer, "%020x", 3989525555U); + SPRINTF_CHECK("0000000000001234ABCD", buffer, "%020X", 305441741); + SPRINTF_CHECK("000000000000EDCB5433", buffer, "%020X", 3989525555U); +} + +SPRINTF_TEST_CASE(padding_dot_20) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK("00000000000000001024", buffer, "%.20d", 1024); + SPRINTF_CHECK("-00000000000000001024", buffer, "%.20d", -1024); + SPRINTF_CHECK("00000000000000001024", buffer, "%.20i", 1024); + SPRINTF_CHECK("-00000000000000001024", buffer, "%.20i", -1024); + SPRINTF_CHECK("00000000000000001024", buffer, "%.20u", 1024); + SPRINTF_CHECK("00000000004294966272", buffer, "%.20u", 4294966272U); + SPRINTF_CHECK("00000000000000000777", buffer, "%.20o", 511); + SPRINTF_CHECK("00000000037777777001", buffer, "%.20o", 4294966785U); + SPRINTF_CHECK("0000000000001234abcd", buffer, "%.20x", 305441741); + SPRINTF_CHECK("000000000000edcb5433", buffer, "%.20x", 3989525555U); + SPRINTF_CHECK("0000000000001234ABCD", buffer, "%.20X", 305441741); + SPRINTF_CHECK("000000000000EDCB5433", buffer, "%.20X", 3989525555U); +} + +SPRINTF_TEST_CASE(padding_sharp_020__non_standard_format) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK("00000000000000001024", buffer, "%#020d", 1024); + SPRINTF_CHECK("-0000000000000001024", buffer, "%#020d", -1024); + SPRINTF_CHECK("00000000000000001024", buffer, "%#020i", 1024); + SPRINTF_CHECK("-0000000000000001024", buffer, "%#020i", -1024); + SPRINTF_CHECK("00000000000000001024", buffer, "%#020u", 1024); + SPRINTF_CHECK("00000000004294966272", buffer, "%#020u", 4294966272U); +} + +SPRINTF_TEST_CASE(padding_sharp_020) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK("00000000000000000777", buffer, "%#020o", 511); + SPRINTF_CHECK("00000000037777777001", buffer, "%#020o", 4294966785U); + SPRINTF_CHECK("0x00000000001234abcd", buffer, "%#020x", 305441741); + SPRINTF_CHECK("0x0000000000edcb5433", buffer, "%#020x", 3989525555U); + SPRINTF_CHECK("0X00000000001234ABCD", buffer, "%#020X", 305441741); + SPRINTF_CHECK("0X0000000000EDCB5433", buffer, "%#020X", 3989525555U); +} + +SPRINTF_TEST_CASE(padding_sharp_20__non_standard_format) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK(" 1024", buffer, "%#20d", 1024); + SPRINTF_CHECK(" -1024", buffer, "%#20d", -1024); + SPRINTF_CHECK(" 1024", buffer, "%#20i", 1024); + SPRINTF_CHECK(" -1024", buffer, "%#20i", -1024); + SPRINTF_CHECK(" 1024", buffer, "%#20u", 1024); + SPRINTF_CHECK(" 4294966272", buffer, "%#20u", 4294966272U); +} + +SPRINTF_TEST_CASE(padding_sharp_20) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK(" 0777", buffer, "%#20o", 511); + SPRINTF_CHECK(" 037777777001", buffer, "%#20o", 4294966785U); + SPRINTF_CHECK(" 0x1234abcd", buffer, "%#20x", 305441741); + SPRINTF_CHECK(" 0xedcb5433", buffer, "%#20x", 3989525555U); + SPRINTF_CHECK(" 0X1234ABCD", buffer, "%#20X", 305441741); + SPRINTF_CHECK(" 0XEDCB5433", buffer, "%#20X", 3989525555U); +} + +SPRINTF_TEST_CASE(padding_20_point_5) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK(" 01024", buffer, "%20.5d", 1024); + SPRINTF_CHECK(" -01024", buffer, "%20.5d", -1024); + SPRINTF_CHECK(" 01024", buffer, "%20.5i", 1024); + SPRINTF_CHECK(" -01024", buffer, "%20.5i", -1024); + SPRINTF_CHECK(" 01024", buffer, "%20.5u", 1024); + SPRINTF_CHECK(" 4294966272", buffer, "%20.5u", 4294966272U); + SPRINTF_CHECK(" 00777", buffer, "%20.5o", 511); + SPRINTF_CHECK(" 37777777001", buffer, "%20.5o", 4294966785U); + SPRINTF_CHECK(" 1234abcd", buffer, "%20.5x", 305441741); + SPRINTF_CHECK(" 00edcb5433", buffer, "%20.10x", 3989525555U); + SPRINTF_CHECK(" 1234ABCD", buffer, "%20.5X", 305441741); + SPRINTF_CHECK(" 00EDCB5433", buffer, "%20.10X", 3989525555U); +} + +SPRINTF_TEST_CASE(padding_negative_numbers) +{ + char buffer[base_buffer_size]; + + /* space padding */ + SPRINTF_CHECK("-5", buffer, "% 1d", -5); + SPRINTF_CHECK("-5", buffer, "% 2d", -5); + SPRINTF_CHECK(" -5", buffer, "% 3d", -5); + SPRINTF_CHECK(" -5", buffer, "% 4d", -5); + + /* zero padding */ + SPRINTF_CHECK("-5", buffer, "%01d", -5); + SPRINTF_CHECK("-5", buffer, "%02d", -5); + SPRINTF_CHECK("-05", buffer, "%03d", -5); + SPRINTF_CHECK("-005", buffer, "%04d", -5); +} + +#if defined(RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS) || \ + defined(RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS) +SPRINTF_TEST_CASE(float_padding_negative_numbers) +{ + char buffer[base_buffer_size]; + + /* space padding */ +#ifdef RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS + SPRINTF_CHECK("-5.0", buffer, "% 3.1f", -5.); + SPRINTF_CHECK("-5.0", buffer, "% 4.1f", -5.); + SPRINTF_CHECK(" -5.0", buffer, "% 5.1f", -5.); +#endif + +#ifdef RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS + SPRINTF_CHECK(" -5", buffer, "% 6.1g", -5.); + SPRINTF_CHECK("-5.0e+00", buffer, "% 6.1e", -5.); + SPRINTF_CHECK(" -5.0e+00", buffer, "% 10.1e", -5.); +#endif + + /* zero padding */ +#ifdef RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS + SPRINTF_CHECK("-5.0", buffer, "%03.1f", -5.); + SPRINTF_CHECK("-5.0", buffer, "%04.1f", -5.); + SPRINTF_CHECK("-05.0", buffer, "%05.1f", -5.); + + /* zero padding no decimal point */ + SPRINTF_CHECK("-5", buffer, "%01.0f", -5.); + SPRINTF_CHECK("-5", buffer, "%02.0f", -5.); + SPRINTF_CHECK("-05", buffer, "%03.0f", -5.); +#endif + +#ifdef RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS + SPRINTF_CHECK("-005.0e+00", buffer, "%010.1e", -5.); + SPRINTF_CHECK("-05E+00", buffer, "%07.0E", -5.); + SPRINTF_CHECK("-05", buffer, "%03.0g", -5.); +#endif +} + +SPRINTF_TEST_CASE(infinity_and_not_a_number_values) +{ + char buffer[base_buffer_size]; + + /* test special-case floats using math.h macros */ +#ifdef RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS + SPRINTF_CHECK(" nan", buffer, "%8f", (double) NAN); + SPRINTF_CHECK(" inf", buffer, "%8f", (double) INFINITY); + SPRINTF_CHECK("-inf ", buffer, "%-8f", (double) -INFINITY); +#endif /* RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS */ +#ifdef RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS + SPRINTF_CHECK(" nan", buffer, "%8e", (double) NAN); + SPRINTF_CHECK(" inf", buffer, "%8e", (double) INFINITY); + SPRINTF_CHECK("-inf ", buffer, "%-8e", (double) -INFINITY); +#endif /* RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS */ +} + +SPRINTF_TEST_CASE(floating_point_specifiers_precision_and_flags) +{ + char buffer[base_buffer_size]; +#ifdef RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS + SPRINTF_CHECK("3.1415", buffer, "%.4f", 3.1415354); + SPRINTF_CHECK("30343.142", buffer, "%.3f", 30343.1415354); + SPRINTF_CHECK("34", buffer, "%.0f", 34.1415354); + SPRINTF_CHECK("1", buffer, "%.0f", 1.3); + SPRINTF_CHECK("2", buffer, "%.0f", 1.55); + SPRINTF_CHECK("1.6", buffer, "%.1f", 1.64); + SPRINTF_CHECK("42.90", buffer, "%.2f", 42.8952); + SPRINTF_CHECK("42.895200000", buffer, "%.9f", 42.8952); + SPRINTF_CHECK("42.8952230000", buffer, "%.10f", 42.895223); + SPRINTF_CHECK("42.895223123457", buffer, "%.12f", 42.89522312345678); + SPRINTF_CHECK("42477.371093750000000", buffer, "%020.15f", 42477.37109375); + SPRINTF_CHECK("42.895223876543", buffer, "%.12f", 42.89522387654321); + SPRINTF_CHECK(" 42.90", buffer, "%6.2f", 42.8952); + SPRINTF_CHECK("+42.90", buffer, "%+6.2f", 42.8952); + SPRINTF_CHECK("+42.9", buffer, "%+5.1f", 42.9252); + SPRINTF_CHECK("42.500000", buffer, "%f", 42.5); + SPRINTF_CHECK("42.5", buffer, "%.1f", 42.5); + SPRINTF_CHECK("42167.000000", buffer, "%f", 42167.0); + SPRINTF_CHECK("-12345.987654321", buffer, "%.9f", -12345.987654321); + SPRINTF_CHECK("4.0", buffer, "%.1f", 3.999); + SPRINTF_CHECK("4", buffer, "%.0f", 3.5); + SPRINTF_CHECK("4", buffer, "%.0f", 4.5); + SPRINTF_CHECK("3", buffer, "%.0f", 3.49); + SPRINTF_CHECK("3.5", buffer, "%.1f", 3.49); + SPRINTF_CHECK("a0.5 ", buffer, "a%-5.1f", 0.5); + SPRINTF_CHECK("a0.5 end", buffer, "a%-5.1fend", 0.5); + + /* %f for double */ + SPRINTF_CHECK("42.895223123457", buffer, "%.12f", 42.89522312345678); + /* %F for double */ + SPRINTF_CHECK("42.895223123457", buffer, "%.12F", 42.89522312345678); + /* %lf for double */ + SPRINTF_CHECK("42.895223123457", buffer, "%.12lf", 42.89522312345678); + /* %Lf for long double */ + // TODO: fix me + // SPRINTF_CHECK("42.895223123457", buffer, "%.12Lf", 42.89522312345678l); + // SPRINTF_CHECK("42.895223123457", buffer, "%.12Lf", 42.89522312345678L); +#endif /* RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS */ + +#ifdef RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS + SPRINTF_CHECK("0.5", buffer, "%.4g", 0.5); + SPRINTF_CHECK("1", buffer, "%.4g", 1.0); + SPRINTF_CHECK("12345.7", buffer, "%G", 12345.678); + SPRINTF_CHECK("12345.68", buffer, "%.7G", 12345.678); + SPRINTF_CHECK("1.2346E+08", buffer, "%.5G", 123456789.); + SPRINTF_CHECK("12345", buffer, "%.6G", 12345.); + SPRINTF_CHECK(" +1.235e+08", buffer, "%+12.4g", 123456789.); + SPRINTF_CHECK("0.0012", buffer, "%.2G", 0.001234); + SPRINTF_CHECK(" +0.001234", buffer, "%+10.4G", 0.001234); + SPRINTF_CHECK("+001.234e-05", buffer, "%+012.4g", 0.00001234); + /* Note: The following two values are _barely_ normal; + make their mantissa 1.1 and they lose their normality. */ + SPRINTF_CHECK("-1.23e-308", buffer, "%.3g", -1.2345e-308); + SPRINTF_CHECK("+1.230E+308", buffer, "%+.3E", 1.23e+308); + SPRINTF_CHECK("1.000e+01", buffer, "%.3e", 9.9996); + SPRINTF_CHECK("0", buffer, "%g", 0.); + SPRINTF_CHECK("-0", buffer, "%g", -0.); + SPRINTF_CHECK("+0", buffer, "%+g", 0.); + SPRINTF_CHECK("-0", buffer, "%+g", -0.); + SPRINTF_CHECK("-4e+04", buffer, "%.1g", -40661.5); + SPRINTF_CHECK("-4.e+04", buffer, "%#.1g", -40661.5); + SPRINTF_CHECK("100.", buffer, "%#.3g", 99.998580932617187500); + // TODO: fix me + // SPRINTF_CHECK("1.e01", buffer, "%# 01.1g", 9.8); + /* Note: The following value is _barely_ normal; + make the mantissa 1.1 and it loses its normality. */ + SPRINTF_CHECK("1.2345678901e-308", buffer, "%.10e", 1.2345678901e-308); + /* Rounding-focused checks */ + SPRINTF_CHECK("4.895512e+04", buffer, "%e", 48955.125); + SPRINTF_CHECK("9.2524e+04", buffer, "%.4e", 92523.5); + SPRINTF_CHECK("-8.380923438e+04", buffer, "%.9e", -83809.234375); + + /* %g for double */ + SPRINTF_CHECK("100.", buffer, "%#.3g", 99.998580932617187500); + /* %G for double */ + SPRINTF_CHECK("100.", buffer, "%#.3G", 99.998580932617187500); + /* %lg for double */ + SPRINTF_CHECK("100.", buffer, "%#.3lg", 99.998580932617187500); + /* %Lg for long double */ + // TODO: fix me + // SPRINTF_CHECK("100.", buffer, "%#.3Lg", 99.998580932617187500l); + // SPRINTF_CHECK("100.", buffer, "%#.3Lg", 99.998580932617187500L); + + /* %e for double */ + SPRINTF_CHECK("-8.380923438e+04", buffer, "%.9e", -83809.234375); + /* %E for double */ + SPRINTF_CHECK("-8.380923438E+04", buffer, "%.9E", -83809.234375); + /* %le for double */ + SPRINTF_CHECK("-8.380923438e+04", buffer, "%.9le", -83809.234375); + /* %Le for long double */ + // TODO: fix me + // SPRINTF_CHECK("-8.380923438e+04", buffer, "%.9Le", -83809.234375l); + // SPRINTF_CHECK("-8.380923438e+04", buffer, "%.9Le", -83809.234375L); +#endif /* RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS */ +} + +#ifdef RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS +SPRINTF_TEST_CASE(floating_point_specifiers_with_31_to_32_bit_integer_values) +{ + char buffer[base_buffer_size]; +#if RT_KLIBC_USING_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL >= 10 + SPRINTF_CHECK("2147483647", buffer, "%.10f", 2147483647.0); /* 2^31 - 1 */ + SPRINTF_CHECK("2147483648", buffer, "%.10f", 2147483648.0); /* 2^31 */ + SPRINTF_CHECK("4294967295", buffer, "%.10f", 4294967295.0); /* 2^32 - 1 */ + SPRINTF_CHECK("4294967296", buffer, "%.10f", 4294967296.0); /* 2^32 */ +#else + SPRINTF_CHECK("2.1474836470e+09", buffer, "%.10f", 2147483647.0); /* 2^31 - 1 */ + SPRINTF_CHECK("2.1474836480e+09", buffer, "%.10f", 2147483648.0); /* 2^31 */ + SPRINTF_CHECK("4.2949672950e+09", buffer, "%.10f", 4294967295.0); /* 2^32 - 1 */ + SPRINTF_CHECK("4.2949672960e+09", buffer, "%.10f", 4294967296.0); /* 2^32 */ +#endif /* RT_KLIBC_USING_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL */ + SPRINTF_CHECK("2147483647", buffer, "%.10g", 2147483647.0); /* 2^31 - 1 */ + SPRINTF_CHECK("2147483648", buffer, "%.10g", 2147483648.0); /* 2^31 */ + SPRINTF_CHECK("4294967295", buffer, "%.10g", 4294967295.0); /* 2^32 - 1 */ + SPRINTF_CHECK("4294967296", buffer, "%.10g", 4294967296.0); /* 2^32 */ +} + +SPRINTF_TEST_CASE(fallback_from_decimal_to_exponential) +{ + char buffer[base_buffer_size]; + + /* Check for 1 * 1000 */ + if (RT_KLIBC_USING_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL < 3) { + SPRINTF_CHECK("1e+3", buffer, "%.0f", (double) ((int64_t) 1 * 1000)); + } else { + SPRINTF_CHECK("1000", buffer, "%.0f", (double) ((int64_t) 1 * 1000)); + } + + /* Check for 1 * 1000 * 1000 */ + if (RT_KLIBC_USING_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL < 6) { + SPRINTF_CHECK("1e+6", buffer, "%.0f", (double) ((int64_t) 1 * 1000 * 1000)); + } else { + SPRINTF_CHECK("1000000", buffer, "%.0f", (double) ((int64_t) 1 * 1000 * 1000)); + } + + /* Check for 1 * 1000 * 1000 * 1000 */ + if (RT_KLIBC_USING_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL < 9) { + SPRINTF_CHECK("1e+9", buffer, "%.0f", (double) ((int64_t) 1 * 1000 * 1000 * 1000)); + } else { + SPRINTF_CHECK("1000000000", buffer, "%.0f", (double) ((int64_t) 1 * 1000 * 1000 * 1000)); + } + + /* Check for 1 * 1000 * 1000 * 1000 * 1000 */ + if (RT_KLIBC_USING_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL < 12) { +#ifdef RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS + SPRINTF_CHECK("1e+12", buffer, "%.0f", (double) ((int64_t) 1 * 1000 * 1000 * 1000 * 1000)); +#else + SPRINTF_CHECK("", buffer, "%.0f", (double) ((int64_t) 1 * 1000 * 1000 * 1000 * 1000)); +#endif + } else { + SPRINTF_CHECK("1000000000000", buffer, "%.0f", (double) ((int64_t) 1 * 1000 * 1000 * 1000 * 1000)); + } + + /* Check for 1 * 1000 * 1000 * 1000 * 1000 * 1000 */ + if (RT_KLIBC_USING_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL < 15) { +#ifdef RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS + SPRINTF_CHECK("1e+15", buffer, "%.0f", (double) ((int64_t) 1 * 1000 * 1000 * 1000 * 1000 * 1000)); +#else + SPRINTF_CHECK("", buffer, "%.0f", (double) ((int64_t) 1 * 1000 * 1000 * 1000 * 1000 * 1000)); +#endif + } else { + SPRINTF_CHECK("1000000000000000", buffer, "%.0f", (double) ((int64_t) 1 * 1000 * 1000 * 1000 * 1000 * 1000)); + } + + /* Check for a value which should definitely be out of range for float */ +#ifdef RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS + SPRINTF_CHECK("1.0e+20", buffer, "%.1f", 1E20); +#else + SPRINTF_CHECK("", buffer, "%.1f", 1E20); +#endif +} +#endif /* RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS */ + +#ifdef RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS +SPRINTF_TEST_CASE(tiny_floating_point_values) +{ + char buffer[base_buffer_size]; + + SPRINTF_CHECK("1e-23", buffer, "%.0e", 1.380651569e-23); + SPRINTF_CHECK("1.4e-23", buffer, "%.1e", 1.380651569e-23); + SPRINTF_CHECK("1.38e-23", buffer, "%.2e", 1.380651569e-23); + SPRINTF_CHECK("1.381e-23", buffer, "%.3e", 1.380651569e-23); + SPRINTF_CHECK("1.3807e-23", buffer, "%.4e", 1.380651569e-23); + SPRINTF_CHECK("1.38065e-23", buffer, "%.5e", 1.380651569e-23); + SPRINTF_CHECK("1.380652e-23", buffer, "%.6e", 1.380651569e-23); + SPRINTF_CHECK("1.3806516e-23", buffer, "%.7e", 1.380651569e-23); + SPRINTF_CHECK("1.38065157e-23", buffer, "%.8e", 1.380651569e-23); + SPRINTF_CHECK("1.380651569e-23", buffer, "%.9e", 1.380651569e-23); + SPRINTF_CHECK("1.3806515690e-23", buffer, "%.10e", 1.380651569e-23); + SPRINTF_CHECK("1.38065156900e-23", buffer, "%.11e", 1.380651569e-23); +} +#endif /* RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS */ +#endif /* RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS || RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS */ + +#ifdef RT_KLIBC_USING_VSNPRINTF_STANDARD +SPRINTF_TEST_CASE(length) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK("", buffer, "%.0s", "Hello testing"); + SPRINTF_CHECK(" ", buffer, "%20.0s", "Hello testing"); + SPRINTF_CHECK("", buffer, "%.s", "Hello testing"); + SPRINTF_CHECK(" ", buffer, "%20.s", "Hello testing"); + SPRINTF_CHECK(" 1024", buffer, "%20.0d", 1024); + SPRINTF_CHECK(" -1024", buffer, "%20.0d", -1024); + SPRINTF_CHECK(" ", buffer, "%20.d", 0); + SPRINTF_CHECK(" 1024", buffer, "%20.0i", 1024); + SPRINTF_CHECK(" -1024", buffer, "%20.i", -1024); + SPRINTF_CHECK(" ", buffer, "%20.i", 0); + SPRINTF_CHECK(" 1024", buffer, "%20.u", 1024); + SPRINTF_CHECK(" 4294966272", buffer, "%20.0u", 4294966272U); + SPRINTF_CHECK(" ", buffer, "%20.u", 0U); + SPRINTF_CHECK(" 777", buffer, "%20.o", 511); + SPRINTF_CHECK(" 37777777001", buffer, "%20.0o", 4294966785U); + SPRINTF_CHECK(" ", buffer, "%20.o", 0U); + SPRINTF_CHECK(" 1234abcd", buffer, "%20.x", 305441741); + SPRINTF_CHECK(" 1234abcd", + buffer, "%50.x", 305441741); + SPRINTF_CHECK(" 1234abcd 12345", + buffer, "%50.x%10.u", 305441741, 12345); + SPRINTF_CHECK(" edcb5433", buffer, "%20.0x", 3989525555U); + SPRINTF_CHECK(" ", buffer, "%20.x", 0U); + SPRINTF_CHECK(" 1234ABCD", buffer, "%20.X", 305441741); + SPRINTF_CHECK(" EDCB5433", buffer, "%20.0X", 3989525555U); + SPRINTF_CHECK(" ", buffer, "%20.X", 0U); +} + +SPRINTF_TEST_CASE(length__non_standard_format) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK(" ", buffer, "%02.0u", 0U); + SPRINTF_CHECK(" ", buffer, "%02.0d", 0); +} + +SPRINTF_TEST_CASE(unknown_flag__non_standard_format) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK("kmarco", buffer, "%kmarco", 42, 37); +} + +SPRINTF_TEST_CASE(string_length__non_standard_format) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK(".2s", buffer, "%.4.2s", "123456"); +} +#endif /* RT_KLIBC_USING_VSNPRINTF_STANDARD */ + +SPRINTF_TEST_CASE(integer_types) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK("0", buffer, "%i", 0); + SPRINTF_CHECK("1234", buffer, "%i", 1234); + SPRINTF_CHECK("32767", buffer, "%i", 32767); + SPRINTF_CHECK("-32767", buffer, "%i", -32767); + SPRINTF_CHECK("30", buffer, "%li", 30L); + SPRINTF_CHECK("-2147483647", buffer, "%li", -2147483647L); + SPRINTF_CHECK("2147483647", buffer, "%li", 2147483647L); +#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG + SPRINTF_CHECK("30", buffer, "%lli", 30LL); +#ifdef RT_KLIBC_USING_VSNPRINTF_STANDARD + SPRINTF_CHECK("-9223372036854775807", buffer, "%lli", -9223372036854775807LL); + SPRINTF_CHECK("9223372036854775807", buffer, "%lli", 9223372036854775807LL); +#endif /* RT_KLIBC_USING_VSNPRINTF_STANDARD */ +#endif /* RT_KLIBC_USING_VSNPRINTF_LONGLONG */ + SPRINTF_CHECK("100000", buffer, "%lu", 100000L); + SPRINTF_CHECK("4294967295", buffer, "%lu", 0xFFFFFFFFL); +#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG + SPRINTF_CHECK("281474976710656", buffer, "%llu", 281474976710656LLU); + SPRINTF_CHECK("18446744073709551615", buffer, "%llu", 18446744073709551615LLU); +#endif /* RT_KLIBC_USING_VSNPRINTF_LONGLONG */ + SPRINTF_CHECK("2147483647", buffer, "%zu", (size_t) 2147483647UL); + SPRINTF_CHECK("2147483647", buffer, "%zd", (size_t) 2147483647UL); + SPRINTF_CHECK("-2147483647", buffer, "%zi", (ssize_t) -2147483647L); + SPRINTF_CHECK("165140", buffer, "%o", 60000); + SPRINTF_CHECK("57060516", buffer, "%lo", 12345678L); + SPRINTF_CHECK("12345678", buffer, "%lx", 0x12345678L); +#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG + SPRINTF_CHECK("1234567891234567", buffer, "%llx", 0x1234567891234567LLU); +#endif /* RT_KLIBC_USING_VSNPRINTF_LONGLONG */ + SPRINTF_CHECK("abcdefab", buffer, "%lx", 0xabcdefabL); + SPRINTF_CHECK("ABCDEFAB", buffer, "%lX", 0xabcdefabL); + SPRINTF_CHECK("v", buffer, "%c", 'v'); + SPRINTF_CHECK("wv", buffer, "%cv", 'w'); + SPRINTF_CHECK("A Test", buffer, "%s", "A Test"); +#ifdef RT_KLIBC_USING_VSNPRINTF_STANDARD + SPRINTF_CHECK("255", buffer, "%hhu", (unsigned char) 0xFFU); +#endif /* RT_KLIBC_USING_VSNPRINTF_STANDARD */ + SPRINTF_CHECK("4660", buffer, "%hu", (unsigned short) 0x1234u); + SPRINTF_CHECK("Test100 65535", buffer, "%s%hhi %hu", "Test", (char) 100, (unsigned short) 0xFFFF); +#ifdef RT_KLIBC_USING_VSNPRINTF_STANDARD + SPRINTF_CHECK("a", buffer, "%tx", &buffer[10] - &buffer[0]); + SPRINTF_CHECK("-2147483647", buffer, "%ji", (intmax_t) -2147483647L); +#endif /* RT_KLIBC_USING_VSNPRINTF_STANDARD */ +} + +SPRINTF_TEST_CASE(types__non_standard_format) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK("1110101001100000", buffer, "%b", 60000); + SPRINTF_CHECK("101111000110000101001110", buffer, "%lb", 12345678L); +} + +SPRINTF_TEST_CASE(pointer) +{ + char buffer[base_buffer_size]; + + /* Test for pointer value 0x1234U */ + SPRINTF_CHECK((sizeof(void *) == 4U) ? "0x00001234" : "0x0000000000001234", + buffer, "%p", (void *) 0x1234U); + + /* Test for pointer value 0x12345678U */ + SPRINTF_CHECK((sizeof(void *) == 4U) ? "0x12345678" : "0x0000000012345678", + buffer, "%p", (void *) 0x12345678U); + + /* Test for pointer range 0x12345678U to 0x7EDCBA98U */ + SPRINTF_CHECK((sizeof(void *) == 4U) ? "0x12345678-0x7edcba98" : "0x0000000012345678-0x000000007edcba98", + buffer, "%p-%p", (void *) 0x12345678U, (void *) 0x7EDCBA98U); + + /* Test for maximum uintptr_t value 0xFFFFFFFFU */ + if (sizeof(uintptr_t) == sizeof(uint64_t)) { + SPRINTF_CHECK("0x00000000ffffffff", buffer, "%p", (void *) (uintptr_t) 0xFFFFFFFFU); + } else { + SPRINTF_CHECK("0xffffffff", buffer, "%p", (void *) (uintptr_t) 0xFFFFFFFFU); + } + +#ifdef RT_KLIBC_USING_VSNPRINTF_STANDARD + /* Test for NULL pointer */ + SPRINTF_CHECK("(nil)", buffer, "%p", (const void *)RT_NULL); +#endif /* RT_KLIBC_USING_VSNPRINTF_STANDARD */ +} + +SPRINTF_TEST_CASE(string_length) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK("This", buffer, "%.4s", "This is a test"); + SPRINTF_CHECK("test", buffer, "%.4s", "test"); + SPRINTF_CHECK("123", buffer, "%.7s", "123"); + SPRINTF_CHECK("", buffer, "%.7s", ""); + SPRINTF_CHECK("1234ab", buffer, "%.4s%.2s", "123456", "abcdef"); + SPRINTF_CHECK("123", buffer, "%.*s", 3, "123456"); +#ifdef RT_KLIBC_USING_VSNPRINTF_STANDARD + SPRINTF_CHECK("(null)", buffer, "%.*s", 3, (const char *) RT_NULL); +#else + SPRINTF_CHECK("(nu", buffer, "%.*s", 3, (const char *) RT_NULL); +#endif /* RT_KLIBC_USING_VSNPRINTF_STANDARD */ +} + +SPRINTF_TEST_CASE(misc) +{ + char buffer[base_buffer_size]; + SPRINTF_CHECK("53000atest-20 bit", buffer, "%u%u%ctest%d %s", 5, 3000, 'a', -20, "bit"); +#ifdef RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS + SPRINTF_CHECK("0.33", buffer, "%.*f", 2, 0.33333333); + SPRINTF_CHECK("1", buffer, "%.*d", -1, 1); + SPRINTF_CHECK("foo", buffer, "%.3s", "foobar"); + SPRINTF_CHECK(" ", buffer, "% .0d", 0); + SPRINTF_CHECK(" 00004", buffer, "%10.5d", 4); + SPRINTF_CHECK("hi x", buffer, "%*sx", -3, "hi"); + SPRINTF_CHECK("00123 ", buffer, "%-20.5i", 123); + SPRINTF_CHECK("-67224.546875000000000000", buffer, "%.18f", -67224.546875); +#endif /* RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS */ +#ifdef RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS + SPRINTF_CHECK("0.33", buffer, "%.*g", 2, 0.33333333); + SPRINTF_CHECK("3.33e-01", buffer, "%.*e", 2, 0.33333333); + SPRINTF_CHECK("0.000000e+00", buffer, "%e", 0.0); + SPRINTF_CHECK("-0.000000e+00", buffer, "%e", -0.0); +#endif /* RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS */ +} + +static void utest_do_tc(void) +{ + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(space_flag)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(space_flag__non_standard_format)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(plus_flag)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(plus_flag__non_standard_format)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(zero_flag)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(minus_flag)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(minus_flag_and_non_standard_zero_modifier_for_integers)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(sharp_flag)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(sharp_flag__non_standard_format)); +#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(sharp_flag_with_long_long)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(sharp_flag_with_long_long__non_standard_format)); +#endif /* RT_KLIBC_USING_VSNPRINTF_LONGLONG */ + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(specifier)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(width)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(width_20)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(width_asterisk_20)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(width_minus_20)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(width_0_minus_20)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(padding_20)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(padding_dot_20)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(padding_sharp_020__non_standard_format)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(padding_sharp_020)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(padding_sharp_20__non_standard_format)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(padding_sharp_20)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(padding_20_point_5)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(padding_negative_numbers)); +#if defined(RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS) || \ + defined(RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS) + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(float_padding_negative_numbers)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(infinity_and_not_a_number_values)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(floating_point_specifiers_precision_and_flags)); +#ifdef RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(floating_point_specifiers_with_31_to_32_bit_integer_values)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(fallback_from_decimal_to_exponential)); +#endif /* RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS */ +#ifdef RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(tiny_floating_point_values)); +#endif /* RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS */ +#endif /* RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS || RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS */ +#ifdef RT_KLIBC_USING_VSNPRINTF_STANDARD + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(length)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(length__non_standard_format)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(unknown_flag__non_standard_format)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(string_length__non_standard_format)); +#endif /* RT_KLIBC_USING_VSNPRINTF_STANDARD */ + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(integer_types)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(types__non_standard_format)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(pointer)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(string_length)); + UTEST_UNIT_RUN(SPRINTF_TEST_CASE_NAME(misc)); +} + +UTEST_TC_EXPORT(utest_do_tc, "klibc.rt_sprintf", utest_tc_init, utest_tc_cleanup, 1000);