| /* | 
 |  * Copyright (c) 2019 NXP | 
 |  * | 
 |  * SPDX-License-Identifier: Apache-2.0 | 
 |  */ | 
 |  | 
 | #define DT_DRV_COMPAT nxp_imx_usdhc | 
 |  | 
 | #include <sys/__assert.h> | 
 | #include <drivers/disk.h> | 
 | #include <drivers/gpio.h> | 
 | #include <sys/byteorder.h> | 
 | #include <soc.h> | 
 | #include <drivers/clock_control.h> | 
 |  | 
 | #include "sdmmc_sdhc.h" | 
 |  | 
 | #include <logging/log.h> | 
 | LOG_MODULE_REGISTER(usdhc, CONFIG_SDMMC_LOG_LEVEL); | 
 |  | 
 | enum usdhc_cmd_type { | 
 | 	USDHC_CMD_TYPE_NORMAL = 0U, | 
 | 	/*!< Normal command */ | 
 | 	USDHC_CMD_TYPE_SUSPEND = 1U, | 
 | 	/*!< Suspend command */ | 
 | 	USDHC_CMD_TYPE_RESUME = 2U, | 
 | 	/*!< Resume command */ | 
 | 	USDHC_CMD_TYPE_ABORT = 3U, | 
 | 	/*!< Abort command */ | 
 | 	USDHC_CMD_TYPE_EMPTY = 4U, | 
 | 	/*!< Empty command */ | 
 | }; | 
 |  | 
 | enum usdhc_status_flag { | 
 | 	USDHC_CMD_INHIBIT_FLAG = | 
 | 		USDHC_PRES_STATE_CIHB_MASK, | 
 | 	/*!< Command inhibit */ | 
 | 	USDHC_DATA_INHIBIT_FLAG = | 
 | 		USDHC_PRES_STATE_CDIHB_MASK, | 
 | 	/*!< Data inhibit */ | 
 | 	USDHC_DATA_LINE_ACTIVE_FLAG = | 
 | 		USDHC_PRES_STATE_DLA_MASK, | 
 | 	/*!< Data line active */ | 
 | 	USDHC_SD_CLK_STATUS_FLAG = | 
 | 		USDHC_PRES_STATE_SDSTB_MASK, | 
 | 	/*!< SD bus clock stable */ | 
 | 	USDHC_WRITE_ACTIVE_FLAG = | 
 | 		USDHC_PRES_STATE_WTA_MASK, | 
 | 	/*!< Write transfer active */ | 
 | 	USDHC_READ_ACTIVE_FLAG = | 
 | 		USDHC_PRES_STATE_RTA_MASK, | 
 | 	/*!< Read transfer active */ | 
 | 	USDHC_BUF_WRITE_ENABLE_FLAG = | 
 | 		USDHC_PRES_STATE_BWEN_MASK, | 
 | 	/*!< Buffer write enable */ | 
 | 	USDHC_BUF_READ_ENABLE_FLAG = | 
 | 		USDHC_PRES_STATE_BREN_MASK, | 
 | 	/*!< Buffer read enable */ | 
 | 	USDHC_RETUNING_REQ_FLAG = | 
 | 		USDHC_PRES_STATE_RTR_MASK, | 
 | 	/*!< re-tuning request flag ,only used for SDR104 mode */ | 
 | 	USDHC_DELAY_SETTING_DONE_FLAG = | 
 | 		USDHC_PRES_STATE_TSCD_MASK, | 
 | 	/*!< delay setting finished flag */ | 
 | 	USDHC_CARD_INSERTED_FLAG = | 
 | 		USDHC_PRES_STATE_CINST_MASK, | 
 | 	/*!< Card inserted */ | 
 | 	USDHC_CMD_LINE_LEVEL_FLAG = | 
 | 		USDHC_PRES_STATE_CLSL_MASK, | 
 | 	/*!< Command line signal level */ | 
 | 	USDHC_DATA0_LINE_LEVEL_FLAG = | 
 | 		1U << USDHC_PRES_STATE_DLSL_SHIFT, | 
 | 	/*!< Data0 line signal level */ | 
 | 	USDHC_DATA1_LINE_LEVEL_FLAG = | 
 | 		1U << (USDHC_PRES_STATE_DLSL_SHIFT + 1U), | 
 | 	/*!< Data1 line signal level */ | 
 | 	USDHC_DATA2_LINE_LEVEL_FLAG = | 
 | 		1U << (USDHC_PRES_STATE_DLSL_SHIFT + 2U), | 
 | 	/*!< Data2 line signal level */ | 
 | 	USDHC_DATA3_LINE_LEVEL_FLAG = | 
 | 		1U << (USDHC_PRES_STATE_DLSL_SHIFT + 3U), | 
 | 	/*!< Data3 line signal level */ | 
 | 	USDHC_DATA4_LINE_LEVEL_FLAG = | 
 | 		1U << (USDHC_PRES_STATE_DLSL_SHIFT + 4U), | 
 | 	/*!< Data4 line signal level */ | 
 | 	USDHC_DATA5_LINE_LEVEL_FLAG = | 
 | 		1U << (USDHC_PRES_STATE_DLSL_SHIFT + 5U), | 
 | 	/*!< Data5 line signal level */ | 
 | 	USDHC_DATA6_LINE_LEVEL_FLAG = | 
 | 		1U << (USDHC_PRES_STATE_DLSL_SHIFT + 6U), | 
 | 	/*!< Data6 line signal level */ | 
 | 	USDHC_DATA7_LINE_LEVEL_FLAG = | 
 | 		(int)(1U << (USDHC_PRES_STATE_DLSL_SHIFT + 7U)), | 
 | 	/*!< Data7 line signal level */ | 
 | }; | 
 |  | 
 | enum usdhc_transfer_flag { | 
 | 	USDHC_ENABLE_DMA_FLAG = | 
 | 		USDHC_MIX_CTRL_DMAEN_MASK, | 
 | 	/*!< Enable DMA */ | 
 |  | 
 | 	USDHC_CMD_TYPE_SUSPEND_FLAG = | 
 | 		(USDHC_CMD_XFR_TYP_CMDTYP(1U)), | 
 | 	/*!< Suspend command */ | 
 | 	USDHC_CMD_TYPE_RESUME_FLAG = | 
 | 		(USDHC_CMD_XFR_TYP_CMDTYP(2U)), | 
 | 	/*!< Resume command */ | 
 | 	USDHC_CMD_TYPE_ABORT_FLAG = | 
 | 		(USDHC_CMD_XFR_TYP_CMDTYP(3U)), | 
 | 	/*!< Abort command */ | 
 |  | 
 | 	USDHC_BLOCK_COUNT_FLAG = | 
 | 		USDHC_MIX_CTRL_BCEN_MASK, | 
 | 	/*!< Enable block count */ | 
 | 	USDHC_AUTO_CMD12_FLAG = | 
 | 		USDHC_MIX_CTRL_AC12EN_MASK, | 
 | 	/*!< Enable auto CMD12 */ | 
 | 	USDHC_DATA_READ_FLAG = | 
 | 		USDHC_MIX_CTRL_DTDSEL_MASK, | 
 | 	/*!< Enable data read */ | 
 | 	USDHC_MULTIPLE_BLOCK_FLAG = | 
 | 		USDHC_MIX_CTRL_MSBSEL_MASK, | 
 | 	/*!< Multiple block data read/write */ | 
 | 	USDHC_AUTO_CMD23FLAG = | 
 | 		USDHC_MIX_CTRL_AC23EN_MASK, | 
 | 	/*!< Enable auto CMD23 */ | 
 | 	USDHC_RSP_LEN_136_FLAG = | 
 | 		USDHC_CMD_XFR_TYP_RSPTYP(1U), | 
 | 	/*!< 136 bit response length */ | 
 | 	USDHC_RSP_LEN_48_FLAG = | 
 | 		USDHC_CMD_XFR_TYP_RSPTYP(2U), | 
 | 	/*!< 48 bit response length */ | 
 | 	USDHC_RSP_LEN_48_BUSY_FLAG = | 
 | 		USDHC_CMD_XFR_TYP_RSPTYP(3U), | 
 | 	/*!< 48 bit response length with busy status */ | 
 |  | 
 | 	USDHC_CRC_CHECK_FLAG = | 
 | 		USDHC_CMD_XFR_TYP_CCCEN_MASK, | 
 | 	/*!< Enable CRC check */ | 
 | 	USDHC_IDX_CHECK_FLAG = | 
 | 		USDHC_CMD_XFR_TYP_CICEN_MASK, | 
 | 	/*!< Enable index check */ | 
 | 	USDHC_DATA_PRESENT_FLAG = | 
 | 		USDHC_CMD_XFR_TYP_DPSEL_MASK, | 
 | 	/*!< Data present flag */ | 
 | }; | 
 |  | 
 | enum usdhc_int_status_flag { | 
 | 	USDHC_INT_CMD_DONE_FLAG = | 
 | 		USDHC_INT_STATUS_CC_MASK, | 
 | 	/*!< Command complete */ | 
 | 	USDHC_INT_DATA_DONE_FLAG = | 
 | 		USDHC_INT_STATUS_TC_MASK, | 
 | 	/*!< Data complete */ | 
 | 	USDHC_INT_BLK_GAP_EVENT_FLAG = | 
 | 		USDHC_INT_STATUS_BGE_MASK, | 
 | 	/*!< Block gap event */ | 
 | 	USDHC_INT_DMA_DONE_FLAG = | 
 | 		USDHC_INT_STATUS_DINT_MASK, | 
 | 	/*!< DMA interrupt */ | 
 | 	USDHC_INT_BUF_WRITE_READY_FLAG = | 
 | 		USDHC_INT_STATUS_BWR_MASK, | 
 | 	/*!< Buffer write ready */ | 
 | 	USDHC_INT_BUF_READ_READY_FLAG = | 
 | 		USDHC_INT_STATUS_BRR_MASK, | 
 | 	/*!< Buffer read ready */ | 
 | 	USDHC_INT_CARD_INSERTED_FLAG = | 
 | 		USDHC_INT_STATUS_CINS_MASK, | 
 | 	/*!< Card inserted */ | 
 | 	USDHC_INT_CARD_REMOVED_FLAG = | 
 | 		USDHC_INT_STATUS_CRM_MASK, | 
 | 	/*!< Card removed */ | 
 | 	USDHC_INT_CARD_INTERRUPT_FLAG = | 
 | 		USDHC_INT_STATUS_CINT_MASK, | 
 | 	/*!< Card interrupt */ | 
 |  | 
 | 	USDHC_INT_RE_TUNING_EVENT_FLAG = | 
 | 		USDHC_INT_STATUS_RTE_MASK, | 
 | 	/*!< Re-Tuning event,only for SD3.0 SDR104 mode */ | 
 | 	USDHC_INT_TUNING_PASS_FLAG = | 
 | 		USDHC_INT_STATUS_TP_MASK, | 
 | 	/*!< SDR104 mode tuning pass flag */ | 
 | 	USDHC_INT_TUNING_ERR_FLAG = | 
 | 		USDHC_INT_STATUS_TNE_MASK, | 
 | 	/*!< SDR104 tuning error flag */ | 
 |  | 
 | 	USDHC_INT_CMD_TIMEOUT_FLAG = | 
 | 		USDHC_INT_STATUS_CTOE_MASK, | 
 | 	/*!< Command timeout error */ | 
 | 	USDHC_INT_CMD_CRC_ERR_FLAG = | 
 | 		USDHC_INT_STATUS_CCE_MASK, | 
 | 	/*!< Command CRC error */ | 
 | 	USDHC_INT_CMD_ENDBIT_ERR_FLAG = | 
 | 		USDHC_INT_STATUS_CEBE_MASK, | 
 | 	/*!< Command end bit error */ | 
 | 	USDHC_INT_CMD_IDX_ERR_FLAG = | 
 | 		USDHC_INT_STATUS_CIE_MASK, | 
 | 	/*!< Command index error */ | 
 | 	USDHC_INT_DATA_TIMEOUT_FLAG = | 
 | 		USDHC_INT_STATUS_DTOE_MASK, | 
 | 	/*!< Data timeout error */ | 
 | 	USDHC_INT_DATA_CRC_ERR_FLAG = | 
 | 		USDHC_INT_STATUS_DCE_MASK, | 
 | 	/*!< Data CRC error */ | 
 | 	USDHC_INT_DATA_ENDBIT_ERR_FLAG = | 
 | 		USDHC_INT_STATUS_DEBE_MASK, | 
 | 	/*!< Data end bit error */ | 
 | 	USDHC_INT_AUTO_CMD12_ERR_FLAG = | 
 | 		USDHC_INT_STATUS_AC12E_MASK, | 
 | 	/*!< Auto CMD12 error */ | 
 | 	USDHC_INT_DMA_ERR_FLAG = | 
 | 		USDHC_INT_STATUS_DMAE_MASK, | 
 | 	/*!< DMA error */ | 
 |  | 
 | 	USDHC_INT_CMD_ERR_FLAG = | 
 | 		(USDHC_INT_CMD_TIMEOUT_FLAG | | 
 | 		USDHC_INT_CMD_CRC_ERR_FLAG | | 
 | 		USDHC_INT_CMD_ENDBIT_ERR_FLAG | | 
 | 		USDHC_INT_CMD_IDX_ERR_FLAG), | 
 | 	/*!< Command error */ | 
 | 	USDHC_INT_DATA_ERR_FLAG = | 
 | 		(USDHC_INT_DATA_TIMEOUT_FLAG | | 
 | 		USDHC_INT_DATA_CRC_ERR_FLAG | | 
 | 		USDHC_INT_DATA_ENDBIT_ERR_FLAG | | 
 | 		USDHC_INT_AUTO_CMD12_ERR_FLAG), | 
 | 	/*!< Data error */ | 
 | 	USDHC_INT_ERR_FLAG = | 
 | 		(USDHC_INT_CMD_ERR_FLAG | | 
 | 		USDHC_INT_DATA_ERR_FLAG | | 
 | 		USDHC_INT_DMA_ERR_FLAG), | 
 | 	/*!< All error */ | 
 | 	USDHC_INT_DATA_FLAG = | 
 | 		(USDHC_INT_DATA_DONE_FLAG | | 
 | 		USDHC_INT_DMA_DONE_FLAG | | 
 | 		USDHC_INT_BUF_WRITE_READY_FLAG | | 
 | 		USDHC_INT_BUF_READ_READY_FLAG | | 
 | 		USDHC_INT_DATA_ERR_FLAG | | 
 | 		USDHC_INT_DMA_ERR_FLAG), | 
 | 	/*!< Data interrupts */ | 
 | 	USDHC_INT_CMD_FLAG = | 
 | 		(USDHC_INT_CMD_DONE_FLAG | | 
 | 		USDHC_INT_CMD_ERR_FLAG), | 
 | 	/*!< Command interrupts */ | 
 | 	USDHC_INT_CARD_DETECT_FLAG = | 
 | 		(USDHC_INT_CARD_INSERTED_FLAG | | 
 | 		USDHC_INT_CARD_REMOVED_FLAG), | 
 | 	/*!< Card detection interrupts */ | 
 | 	USDHC_INT_SDR104_TUNING_FLAG = | 
 | 		(USDHC_INT_RE_TUNING_EVENT_FLAG | | 
 | 		USDHC_INT_TUNING_PASS_FLAG | | 
 | 		USDHC_INT_TUNING_ERR_FLAG), | 
 |  | 
 | 	USDHC_INT_ALL_FLAGS = | 
 | 		(USDHC_INT_BLK_GAP_EVENT_FLAG | | 
 | 		USDHC_INT_CARD_INTERRUPT_FLAG | | 
 | 		USDHC_INT_CMD_FLAG | | 
 | 		USDHC_INT_DATA_FLAG | | 
 | 		USDHC_INT_ERR_FLAG | | 
 | 		USDHC_INT_SDR104_TUNING_FLAG), | 
 | 	/*!< All flags mask */ | 
 | }; | 
 |  | 
 | enum usdhc_data_bus_width { | 
 | 	USDHC_DATA_BUS_WIDTH_1BIT = 0U, | 
 | 	/*!< 1-bit mode */ | 
 | 	USDHC_DATA_BUS_WIDTH_4BIT = 1U, | 
 | 	/*!< 4-bit mode */ | 
 | 	USDHC_DATA_BUS_WIDTH_8BIT = 2U, | 
 | 	/*!< 8-bit mode */ | 
 | }; | 
 |  | 
 | #define USDHC_MAX_BLOCK_COUNT	\ | 
 | 	(USDHC_BLK_ATT_BLKCNT_MASK >>	\ | 
 | 	USDHC_BLK_ATT_BLKCNT_SHIFT) | 
 |  | 
 | struct usdhc_cmd { | 
 | 	uint32_t index;	/*cmd idx*/ | 
 | 	uint32_t argument;	/*cmd arg*/ | 
 | 	enum usdhc_cmd_type cmd_type; | 
 | 	enum sdhc_rsp_type rsp_type; | 
 | 	uint32_t response[4U]; | 
 | 	uint32_t rsp_err_flags; | 
 | 	uint32_t flags; | 
 | }; | 
 |  | 
 | struct usdhc_data { | 
 | 	bool cmd12; | 
 | 	/* Enable auto CMD12 */ | 
 | 	bool cmd23; | 
 | 	/* Enable auto CMD23 */ | 
 | 	bool ignore_err; | 
 | 	/* Enable to ignore error event | 
 | 	 * to read/write all the data | 
 | 	 */ | 
 | 	bool data_enable; | 
 | 	uint8_t data_type; | 
 | 	/* this is used to distinguish | 
 | 	 * the normal/tuning/boot data | 
 | 	 */ | 
 | 	uint32_t block_size; | 
 | 	/* Block size | 
 | 	 */ | 
 | 	uint32_t block_count; | 
 | 	/* Block count | 
 | 	 */ | 
 | 	uint32_t *rx_data; | 
 | 	/* Buffer to save data read | 
 | 	 */ | 
 | 	const uint32_t *tx_data; | 
 | 	/* Data buffer to write | 
 | 	 */ | 
 | }; | 
 |  | 
 | enum usdhc_dma_mode { | 
 | 	USDHC_DMA_SIMPLE = 0U, | 
 | 	/* external DMA | 
 | 	 */ | 
 | 	USDHC_DMA_ADMA1 = 1U, | 
 | 	/* ADMA1 is selected | 
 | 	 */ | 
 | 	USDHC_DMA_ADMA2 = 2U, | 
 | 	/* ADMA2 is selected | 
 | 	 */ | 
 | 	USDHC_EXT_DMA = 3U, | 
 | 	/* external dma mode select | 
 | 	 */ | 
 | }; | 
 |  | 
 | enum usdhc_burst_len { | 
 | 	USDHC_INCR_BURST_LEN = 0x01U, | 
 | 	/* enable burst len for INCR | 
 | 	 */ | 
 | 	USDHC_INCR4816_BURST_LEN = 0x02U, | 
 | 	/* enable burst len for INCR4/INCR8/INCR16 | 
 | 	 */ | 
 | 	USDHC_INCR4816_BURST_LEN_WRAP = 0x04U, | 
 | 	/* enable burst len for INCR4/8/16 WRAP | 
 | 	 */ | 
 | }; | 
 |  | 
 | struct usdhc_adma_config { | 
 | 	enum usdhc_dma_mode dma_mode; | 
 | 	/* DMA mode | 
 | 	 */ | 
 | 	enum usdhc_burst_len burst_len; | 
 | 	/* burst len config | 
 | 	 */ | 
 | 	uint32_t *adma_table; | 
 | 	/* ADMA table address, | 
 | 	 * can't be null if transfer way is ADMA1/ADMA2 | 
 | 	 */ | 
 | 	uint32_t adma_table_words; | 
 | 	/* ADMA table length united as words, | 
 | 	 * can't be 0 if transfer way is ADMA1/ADMA2 | 
 | 	 */ | 
 | }; | 
 |  | 
 | struct usdhc_context { | 
 | 	bool cmd_only; | 
 | 	struct usdhc_cmd cmd; | 
 | 	struct usdhc_data data; | 
 | 	struct usdhc_adma_config dma_cfg; | 
 | }; | 
 |  | 
 | enum usdhc_endian_mode { | 
 | 	USDHC_BIG_ENDIAN = 0U, | 
 | 	/* Big endian mode | 
 | 	 */ | 
 | 	USDHC_HALF_WORD_BIG_ENDIAN = 1U, | 
 | 	/* Half word big endian mode | 
 | 	 */ | 
 | 	USDHC_LITTLE_ENDIAN = 2U, | 
 | 	/* Little endian mode | 
 | 	 */ | 
 | }; | 
 |  | 
 | struct usdhc_config { | 
 | 	USDHC_Type *base; | 
 | 	const struct device *clock_dev; | 
 | 	clock_control_subsys_t clock_subsys; | 
 | 	uint8_t nusdhc; | 
 |  | 
 | 	char *pwr_name; | 
 | 	uint8_t pwr_pin; | 
 | 	gpio_dt_flags_t pwr_flags; | 
 |  | 
 | 	char *detect_name; | 
 | 	uint8_t detect_pin; | 
 | 	gpio_dt_flags_t detect_flags; | 
 |  | 
 | 	bool no_1_8_v; | 
 |  | 
 | 	uint32_t data_timeout; | 
 | 	/* Data timeout value | 
 | 	 */ | 
 | 	enum usdhc_endian_mode endian; | 
 | 	/* Endian mode | 
 | 	 */ | 
 | 	uint8_t read_watermark; | 
 | 	/* Watermark level for DMA read operation. | 
 | 	 * Available range is 1 ~ 128. | 
 | 	 */ | 
 | 	uint8_t write_watermark; | 
 | 	/* Watermark level for DMA write operation. | 
 | 	 * Available range is 1 ~ 128. | 
 | 	 */ | 
 | 	uint8_t read_burst_len; | 
 | 	/* Read burst len | 
 | 	 */ | 
 | 	uint8_t write_burst_len; | 
 | 	/* Write burst len | 
 | 	 */ | 
 | }; | 
 |  | 
 | struct usdhc_capability { | 
 | 	uint32_t max_blk_len; | 
 | 	uint32_t max_blk_cnt; | 
 | 	uint32_t host_flags; | 
 | }; | 
 |  | 
 | enum host_detect_type { | 
 | 	SD_DETECT_GPIO_CD, | 
 | 	/* sd card detect by CD pin through GPIO | 
 | 	 */ | 
 | 	SD_DETECT_HOST_CD, | 
 | 	/* sd card detect by CD pin through host | 
 | 	 */ | 
 | 	SD_DETECT_HOST_DATA3, | 
 | 	/* sd card detect by DAT3 pin through host | 
 | 	 */ | 
 | }; | 
 |  | 
 | struct usdhc_client_info { | 
 | 	uint32_t busclk_hz; | 
 | 	uint32_t relative_addr; | 
 | 	uint32_t version; | 
 | 	uint32_t card_flags; | 
 | 	uint32_t raw_cid[4U]; | 
 | 	uint32_t raw_csd[4U]; | 
 | 	uint32_t raw_scr[2U]; | 
 | 	uint32_t raw_ocr; | 
 | 	struct sd_cid cid; | 
 | 	struct sd_csd csd; | 
 | 	struct sd_scr scr; | 
 | 	uint32_t sd_block_count; | 
 | 	uint32_t sd_block_size; | 
 | 	enum sd_timing_mode sd_timing; | 
 | 	enum sd_driver_strength driver_strength; | 
 | 	enum sd_max_current max_current; | 
 | 	enum sd_voltage voltage; | 
 | }; | 
 |  | 
 | struct usdhc_priv { | 
 | 	bool host_ready; | 
 | 	uint8_t status; | 
 |  | 
 | 	const struct device *pwr_gpio; | 
 | 	const struct device *detect_gpio; | 
 | 	struct gpio_callback detect_cb; | 
 |  | 
 | 	enum host_detect_type detect_type; | 
 | 	bool inserted; | 
 |  | 
 | 	uint32_t src_clk_hz; | 
 |  | 
 | 	const struct usdhc_config *config; | 
 | 	struct usdhc_capability host_capability; | 
 |  | 
 | 	struct usdhc_client_info card_info; | 
 |  | 
 | 	struct usdhc_context op_context; | 
 | }; | 
 |  | 
 | enum usdhc_xfer_data_type { | 
 | 	USDHC_XFER_NORMAL = 0U, | 
 | 	/* transfer normal read/write data | 
 | 	 */ | 
 | 	USDHC_XFER_TUNING = 1U, | 
 | 	/* transfer tuning data | 
 | 	 */ | 
 | 	USDHC_XFER_BOOT = 2U, | 
 | 	/* transfer boot data | 
 | 	 */ | 
 | 	USDHC_XFER_BOOT_CONTINUOUS = 3U, | 
 | 	/* transfer boot data continuous | 
 | 	 */ | 
 | }; | 
 |  | 
 | #define USDHC_ADMA1_ADDRESS_ALIGN (4096U) | 
 | #define USDHC_ADMA1_LENGTH_ALIGN (4096U) | 
 | #define USDHC_ADMA2_ADDRESS_ALIGN (4U) | 
 | #define USDHC_ADMA2_LENGTH_ALIGN (4U) | 
 |  | 
 | #define USDHC_ADMA2_DESCRIPTOR_LENGTH_SHIFT (16U) | 
 | #define USDHC_ADMA2_DESCRIPTOR_LENGTH_MASK (0xFFFFU) | 
 | #define USDHC_ADMA2_DESCRIPTOR_MAX_LENGTH_PER_ENTRY	\ | 
 | 	(USDHC_ADMA2_DESCRIPTOR_LENGTH_MASK - 3U) | 
 |  | 
 | #define SWAP_WORD_BYTE_SEQUENCE(x) (__REV(x)) | 
 | #define SWAP_HALF_WROD_BYTE_SEQUENCE(x) (__REV16(x)) | 
 |  | 
 | #define SDMMCHOST_NOT_SUPPORT 0U | 
 |  | 
 | #define CARD_BUS_FREQ_50MHZ (0U) | 
 | #define CARD_BUS_FREQ_100MHZ0 (1U) | 
 | #define CARD_BUS_FREQ_100MHZ1 (2U) | 
 | #define CARD_BUS_FREQ_200MHZ (3U) | 
 |  | 
 | #define CARD_BUS_STRENGTH_0 (0U) | 
 | #define CARD_BUS_STRENGTH_1 (1U) | 
 | #define CARD_BUS_STRENGTH_2 (2U) | 
 | #define CARD_BUS_STRENGTH_3 (3U) | 
 | #define CARD_BUS_STRENGTH_4 (4U) | 
 | #define CARD_BUS_STRENGTH_5 (5U) | 
 | #define CARD_BUS_STRENGTH_6 (6U) | 
 | #define CARD_BUS_STRENGTH_7 (7U) | 
 |  | 
 | enum usdhc_adma_flag { | 
 | 	USDHC_ADMA_SINGLE_FLAG = 0U, | 
 | 	USDHC_ADMA_MUTI_FLAG = 1U, | 
 | }; | 
 |  | 
 | enum usdhc_adma2_descriptor_flag { | 
 | 	USDHC_ADMA2_VALID_FLAG = (1U << 0U), | 
 | 	/* Valid flag | 
 | 	 */ | 
 | 	USDHC_ADMA2_END_FLAG = (1U << 1U), | 
 | 	/* End flag | 
 | 	 */ | 
 | 	USDHC_ADMA2_INT_FLAG = (1U << 2U), | 
 | 	/* Interrupt flag | 
 | 	 */ | 
 | 	USDHC_ADMA2_ACTIVITY1_FLAG = (1U << 4U), | 
 | 	/* Activity 1 mask | 
 | 	 */ | 
 | 	USDHC_ADMA2_ACTIVITY2_FLAG = (1U << 5U), | 
 | 	/* Activity 2 mask | 
 | 	 */ | 
 |  | 
 | 	USDHC_ADMA2_NOP_FLAG = | 
 | 		(USDHC_ADMA2_VALID_FLAG), | 
 | 	/* No operation | 
 | 	 */ | 
 | 	USDHC_ADMA2_RESERVED_FLAG = | 
 | 		(USDHC_ADMA2_ACTIVITY1_FLAG | | 
 | 		USDHC_ADMA2_VALID_FLAG), | 
 | 	/* Reserved | 
 | 	 */ | 
 | 	USDHC_ADMA2_XFER_FLAG = | 
 | 		(USDHC_ADMA2_ACTIVITY2_FLAG | | 
 | 		USDHC_ADMA2_VALID_FLAG), | 
 | 	/* Transfer type | 
 | 	 */ | 
 | 	USDHC_ADMA2_LINK_FLAG = | 
 | 		(USDHC_ADMA2_ACTIVITY1_FLAG | | 
 | 		USDHC_ADMA2_ACTIVITY2_FLAG | | 
 | 		USDHC_ADMA2_VALID_FLAG), | 
 | 	/* Link type | 
 | 	 */ | 
 | }; | 
 |  | 
 | struct usdhc_adma2_descriptor { | 
 | 	uint32_t attribute; | 
 | 	/*!< The control and status field */ | 
 | 	const uint32_t *address; | 
 | 	/*!< The address field */ | 
 | }; | 
 |  | 
 | enum usdhc_card_flag { | 
 | 	USDHC_HIGH_CAPACITY_FLAG = | 
 | 		(1U << 1U), | 
 | 	/* Support high capacity | 
 | 	 */ | 
 | 	USDHC_4BIT_WIDTH_FLAG = | 
 | 		(1U << 2U), | 
 | 	/* Support 4-bit data width | 
 | 	 */ | 
 | 	USDHC_SDHC_FLAG = | 
 | 		(1U << 3U), | 
 | 	/* Card is SDHC | 
 | 	 */ | 
 | 	USDHC_SDXC_FLAG = | 
 | 		(1U << 4U), | 
 | 	/* Card is SDXC | 
 | 	 */ | 
 | 	USDHC_VOL_1_8V_FLAG = | 
 | 		(1U << 5U), | 
 | 	/* card support 1.8v voltage | 
 | 	 */ | 
 | 	USDHC_SET_BLK_CNT_CMD23_FLAG = | 
 | 		(1U << 6U), | 
 | 	/* card support cmd23 flag | 
 | 	 */ | 
 | 	USDHC_SPEED_CLASS_CONTROL_CMD_FLAG = | 
 | 		(1U << 7U), | 
 | 	/* card support speed class control flag | 
 | 	 */ | 
 | }; | 
 |  | 
 | enum usdhc_capability_flag { | 
 | 	USDHC_SUPPORT_ADMA_FLAG = | 
 | 		USDHC_HOST_CTRL_CAP_ADMAS_MASK, | 
 | 	/*!< Support ADMA */ | 
 | 	USDHC_SUPPORT_HIGHSPEED_FLAG = | 
 | 		USDHC_HOST_CTRL_CAP_HSS_MASK, | 
 | 	/*!< Support high-speed */ | 
 | 	USDHC_SUPPORT_DMA_FLAG = | 
 | 		USDHC_HOST_CTRL_CAP_DMAS_MASK, | 
 | 	/*!< Support DMA */ | 
 | 	USDHC_SUPPORT_SUSPEND_RESUME_FLAG = | 
 | 		USDHC_HOST_CTRL_CAP_SRS_MASK, | 
 | 	/*!< Support suspend/resume */ | 
 | 	USDHC_SUPPORT_V330_FLAG = | 
 | 		USDHC_HOST_CTRL_CAP_VS33_MASK, | 
 | 	/*!< Support voltage 3.3V */ | 
 | 	USDHC_SUPPORT_V300_FLAG = | 
 | 		USDHC_HOST_CTRL_CAP_VS30_MASK, | 
 | 	/*!< Support voltage 3.0V */ | 
 | 	USDHC_SUPPORT_V180_FLAG = | 
 | 		USDHC_HOST_CTRL_CAP_VS18_MASK, | 
 | 	/*!< Support voltage 1.8V */ | 
 | 	/* Put additional two flags in | 
 | 	 * HTCAPBLT_MBL's position. | 
 | 	 */ | 
 | 	USDHC_SUPPORT_4BIT_FLAG = | 
 | 		(USDHC_HOST_CTRL_CAP_MBL_SHIFT << 0U), | 
 | 	/*!< Support 4 bit mode */ | 
 | 	USDHC_SUPPORT_8BIT_FLAG = | 
 | 		(USDHC_HOST_CTRL_CAP_MBL_SHIFT << 1U), | 
 | 	/*!< Support 8 bit mode */ | 
 | 	/* sd version 3.0 new feature */ | 
 | 	USDHC_SUPPORT_DDR50_FLAG = | 
 | 		USDHC_HOST_CTRL_CAP_DDR50_SUPPORT_MASK, | 
 | 	/*!< support DDR50 mode */ | 
 |  | 
 | #if defined(FSL_FEATURE_USDHC_HAS_SDR104_MODE) &&\ | 
 | 	(!FSL_FEATURE_USDHC_HAS_SDR104_MODE) | 
 | 	USDHC_SUPPORT_SDR104_FLAG = 0, | 
 | 	/*!< not support SDR104 mode */ | 
 | #else | 
 | 	USDHC_SUPPORT_SDR104_FLAG = | 
 | 		USDHC_HOST_CTRL_CAP_SDR104_SUPPORT_MASK, | 
 | 	/*!< support SDR104 mode */ | 
 | #endif | 
 | #if defined(FSL_FEATURE_USDHC_HAS_SDR50_MODE) &&\ | 
 | 	(!FSL_FEATURE_USDHC_HAS_SDR50_MODE) | 
 | 	USDHC_SUPPORT_SDR50_FLAG = 0U, | 
 | 	/*!< not support SDR50 mode */ | 
 | #else | 
 | 	USDHC_SUPPORT_SDR50_FLAG = | 
 | 		USDHC_HOST_CTRL_CAP_SDR50_SUPPORT_MASK, | 
 | 	/*!< support SDR50 mode */ | 
 | #endif | 
 | }; | 
 |  | 
 | #define NXP_SDMMC_MAX_VOLTAGE_RETRIES (1000U) | 
 |  | 
 | #define CARD_DATA0_STATUS_MASK USDHC_DATA0_LINE_LEVEL_FLAG | 
 | #define CARD_DATA1_STATUS_MASK USDHC_DATA1_LINE_LEVEL_FLAG | 
 | #define CARD_DATA2_STATUS_MASK USDHC_DATA2_LINE_LEVEL_FLAG | 
 | #define CARD_DATA3_STATUS_MASK USDHC_DATA3_LINE_LEVEL_FLAG | 
 | #define CARD_DATA0_NOT_BUSY USDHC_DATA0_LINE_LEVEL_FLAG | 
 |  | 
 | #define SDHC_STANDARD_TUNING_START (10U) | 
 | /*!< standard tuning start point */ | 
 | #define SDHC_TUINIG_STEP (2U) | 
 | /*!< standard tuning step */ | 
 | #define SDHC_RETUNING_TIMER_COUNT (0U) | 
 | /*!< Re-tuning timer */ | 
 |  | 
 | #define USDHC_MAX_DVS	\ | 
 | 	((USDHC_SYS_CTRL_DVS_MASK >>	\ | 
 | 	USDHC_SYS_CTRL_DVS_SHIFT) + 1U) | 
 | #define USDHC_MAX_CLKFS	\ | 
 | 	((USDHC_SYS_CTRL_SDCLKFS_MASK >>	\ | 
 | 	USDHC_SYS_CTRL_SDCLKFS_SHIFT) + 1U) | 
 | #define USDHC_PREV_DVS(x) ((x) -= 1U) | 
 | #define USDHC_PREV_CLKFS(x, y) ((x) >>= (y)) | 
 |  | 
 | #define SDMMCHOST_SUPPORT_SDR104_FREQ SD_CLOCK_208MHZ | 
 |  | 
 | #define USDHC_ADMA_TABLE_WORDS (8U) | 
 | #define USDHC_ADMA2_ADDR_ALIGN (4U) | 
 | #define USDHC_READ_BURST_LEN (8U) | 
 | #define USDHC_WRITE_BURST_LEN (8U) | 
 | #define USDHC_DATA_TIMEOUT (0xFU) | 
 |  | 
 | #define USDHC_READ_WATERMARK_LEVEL (0x80U) | 
 | #define USDHC_WRITE_WATERMARK_LEVEL (0x80U) | 
 |  | 
 | enum usdhc_reset { | 
 | 	USDHC_RESET_ALL = | 
 | 		USDHC_SYS_CTRL_RSTA_MASK, | 
 | 	/*!< Reset all except card detection */ | 
 | 	USDHC_RESET_CMD = | 
 | 		USDHC_SYS_CTRL_RSTC_MASK, | 
 | 	/*!< Reset command line */ | 
 | 	USDHC_RESET_DATA = | 
 | 		USDHC_SYS_CTRL_RSTD_MASK, | 
 | 	/*!< Reset data line */ | 
 |  | 
 | #if defined(FSL_FEATURE_USDHC_HAS_SDR50_MODE) &&\ | 
 | 	(!FSL_FEATURE_USDHC_HAS_SDR50_MODE) | 
 | 	USDHC_RESET_TUNING = 0U, | 
 | 	/*!< no reset tuning circuit bit */ | 
 | #else | 
 | 	USDHC_RESET_TUNING = USDHC_SYS_CTRL_RSTT_MASK, | 
 | 	/*!< reset tuning circuit */ | 
 | #endif | 
 |  | 
 | 	USDHC_RESETS_All = | 
 | 		(USDHC_RESET_ALL | | 
 | 		USDHC_RESET_CMD | | 
 | 		USDHC_RESET_DATA | | 
 | 		USDHC_RESET_TUNING), | 
 | 	/*!< All reset types */ | 
 | }; | 
 |  | 
 | static void usdhc_millsec_delay(unsigned int cycles_to_wait) | 
 | { | 
 | 	unsigned int start = sys_clock_cycle_get_32(); | 
 |  | 
 | 	while (sys_clock_cycle_get_32() - start < (cycles_to_wait * 1000)) | 
 | 		; | 
 | } | 
 |  | 
 | uint32_t g_usdhc_boot_dummy __aligned(64); | 
 | uint32_t g_usdhc_rx_dummy[2048] __aligned(64); | 
 |  | 
 | static int usdhc_adma2_descriptor_cfg( | 
 | 	uint32_t *adma_table, uint32_t adma_table_words, | 
 | 	const uint32_t *data_addr, uint32_t data_size, uint32_t flags) | 
 | { | 
 | 	uint32_t min_entries, start_entry = 0U; | 
 | 	uint32_t max_entries = (adma_table_words * sizeof(uint32_t)) / | 
 | 		sizeof(struct usdhc_adma2_descriptor); | 
 | 	struct usdhc_adma2_descriptor *adma2_addr = | 
 | 		(struct usdhc_adma2_descriptor *)(adma_table); | 
 | 	uint32_t i, dma_buf_len = 0U; | 
 |  | 
 | 	if ((uint32_t)data_addr % USDHC_ADMA2_ADDRESS_ALIGN) { | 
 | 		return -EIO; | 
 | 	} | 
 | 	/* Add non aligned access support. | 
 | 	 */ | 
 | 	if (data_size % sizeof(uint32_t)) { | 
 | 		/* make the data length as word-aligned */ | 
 | 		data_size += sizeof(uint32_t) - (data_size % sizeof(uint32_t)); | 
 | 	} | 
 |  | 
 | 	/* Check if ADMA descriptor's number is enough. */ | 
 | 	if (!(data_size % USDHC_ADMA2_DESCRIPTOR_MAX_LENGTH_PER_ENTRY)) { | 
 | 		min_entries = data_size / | 
 | 			USDHC_ADMA2_DESCRIPTOR_MAX_LENGTH_PER_ENTRY; | 
 | 	} else { | 
 | 		min_entries = ((data_size / | 
 | 			USDHC_ADMA2_DESCRIPTOR_MAX_LENGTH_PER_ENTRY) + 1U); | 
 | 	} | 
 | 	/* calcucate the start entry for multiple descriptor mode, | 
 | 	 * ADMA engine is not stop, so update the descriptor | 
 | 	 * data address and data size is enough | 
 | 	 */ | 
 | 	if (flags == USDHC_ADMA_MUTI_FLAG) { | 
 | 		for (i = 0U; i < max_entries; i++) { | 
 | 			if (!(adma2_addr[i].attribute & USDHC_ADMA2_VALID_FLAG)) | 
 | 				break; | 
 | 		} | 
 | 		start_entry = i; | 
 | 		/* add one entry for dummy entry */ | 
 | 		min_entries += 1U; | 
 | 	} | 
 |  | 
 | 	if ((min_entries + start_entry) > max_entries) { | 
 | 		return -EIO; | 
 | 	} | 
 |  | 
 | 	for (i = start_entry; i < (min_entries + start_entry); i++) { | 
 | 		if (data_size > USDHC_ADMA2_DESCRIPTOR_MAX_LENGTH_PER_ENTRY) { | 
 | 			dma_buf_len = | 
 | 				USDHC_ADMA2_DESCRIPTOR_MAX_LENGTH_PER_ENTRY; | 
 | 		} else { | 
 | 			dma_buf_len = (data_size == 0U ? sizeof(uint32_t) : | 
 | 				data_size); | 
 | 			/* adma don't support 0 data length transfer | 
 | 			 * descriptor | 
 | 			 */ | 
 | 		} | 
 |  | 
 | 		/* Each descriptor for ADMA2 is 64-bit in length */ | 
 | 		adma2_addr[i].address = (data_size == 0U) ? | 
 | 			&g_usdhc_boot_dummy : data_addr; | 
 | 		adma2_addr[i].attribute = (dma_buf_len << | 
 | 			USDHC_ADMA2_DESCRIPTOR_LENGTH_SHIFT); | 
 | 		adma2_addr[i].attribute |= | 
 | 			(data_size == 0U) ? 0U : | 
 | 				(USDHC_ADMA2_XFER_FLAG | USDHC_ADMA2_INT_FLAG); | 
 | 		data_addr += (dma_buf_len / sizeof(uint32_t)); | 
 |  | 
 | 		if (data_size != 0U) | 
 | 			data_size -= dma_buf_len; | 
 | 	} | 
 |  | 
 | 	/* add a dummy valid ADMA descriptor for multiple descriptor mode, | 
 | 	 * this is useful when transfer boot data, the ADMA | 
 | 	 * engine will not stop at block gap | 
 | 	 */ | 
 | 	if (flags == USDHC_ADMA_MUTI_FLAG) { | 
 | 		adma2_addr[start_entry + 1U].attribute |= USDHC_ADMA2_XFER_FLAG; | 
 | 	} else { | 
 | 		adma2_addr[i - 1U].attribute |= USDHC_ADMA2_END_FLAG; | 
 | 		/* set the end bit */ | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int usdhc_Internal_dma_cfg(struct usdhc_priv *priv, | 
 | 	struct usdhc_adma_config *dma_cfg, | 
 | 	const uint32_t *data_addr) | 
 | { | 
 | 	USDHC_Type *base = priv->config->base; | 
 | 	bool cmd23 = priv->op_context.data.cmd23; | 
 |  | 
 | 	if (dma_cfg->dma_mode == USDHC_DMA_SIMPLE) { | 
 | 		/* check DMA data buffer address align or not */ | 
 | 		if (((uint32_t)data_addr % USDHC_ADMA2_ADDRESS_ALIGN) != 0U) { | 
 | 			return -EIO; | 
 | 		} | 
 | 		/* in simple DMA mode if use auto CMD23, | 
 | 		 * address should load to ADMA addr, | 
 | 		 * and block count should load to DS_ADDR | 
 | 		 */ | 
 | 		if (cmd23) | 
 | 			base->ADMA_SYS_ADDR = (uint32_t)data_addr; | 
 | 		else | 
 | 			base->DS_ADDR = (uint32_t)data_addr; | 
 | 	} else { | 
 | 		/* When use ADMA, disable simple DMA */ | 
 | 		base->DS_ADDR = 0U; | 
 | 		base->ADMA_SYS_ADDR = (uint32_t)(dma_cfg->adma_table); | 
 | 	} | 
 |  | 
 | 	/* select DMA mode and config the burst length */ | 
 | 	base->PROT_CTRL &= ~(USDHC_PROT_CTRL_DMASEL_MASK | | 
 | 		USDHC_PROT_CTRL_BURST_LEN_EN_MASK); | 
 | 	base->PROT_CTRL |= USDHC_PROT_CTRL_DMASEL(dma_cfg->dma_mode) | | 
 | 		USDHC_PROT_CTRL_BURST_LEN_EN(dma_cfg->burst_len); | 
 | 	/* enable DMA */ | 
 | 	base->MIX_CTRL |= USDHC_MIX_CTRL_DMAEN_MASK; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 |  | 
 | static int usdhc_adma_table_cfg(struct usdhc_priv *priv, uint32_t flags) | 
 | { | 
 | 	int error = -EIO; | 
 | 	struct usdhc_data *data = &priv->op_context.data; | 
 | 	struct usdhc_adma_config *dma_cfg = &priv->op_context.dma_cfg; | 
 | 	uint32_t boot_dummy_off = data->data_type == USDHC_XFER_BOOT_CONTINUOUS ? | 
 | 		sizeof(uint32_t) : 0U; | 
 | 	const uint32_t *data_addr = (const uint32_t *)((uint32_t)((!data->rx_data) ? | 
 | 		data->tx_data : data->rx_data) + boot_dummy_off); | 
 | 	uint32_t data_size = data->block_size * data->block_count - boot_dummy_off; | 
 |  | 
 | 	switch (dma_cfg->dma_mode) { | 
 | 	case USDHC_DMA_SIMPLE: | 
 | 		error = 0; | 
 | 	break; | 
 |  | 
 | 	case USDHC_DMA_ADMA1: | 
 | 		error = -EINVAL; | 
 | 	break; | 
 |  | 
 | 	case USDHC_DMA_ADMA2: | 
 | 		error = usdhc_adma2_descriptor_cfg(dma_cfg->adma_table, | 
 | 			dma_cfg->adma_table_words, data_addr, data_size, flags); | 
 | 	break; | 
 | 	default: | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	/* for internal dma, internal DMA configurations should not update | 
 | 	 * the configurations when continuous transfer the | 
 | 	 * boot data, only the DMA descriptor need update | 
 | 	 */ | 
 | 	if ((!error) && (data->data_type != USDHC_XFER_BOOT_CONTINUOUS)) { | 
 | 		error = usdhc_Internal_dma_cfg(priv, dma_cfg, data_addr); | 
 | 	} | 
 |  | 
 | 	return error; | 
 | } | 
 |  | 
 | static int usdhc_data_xfer_cfg(struct usdhc_priv *priv, | 
 | 	bool en_dma) | 
 | { | 
 | 	USDHC_Type *base = priv->config->base; | 
 | 	uint32_t mix_ctrl = base->MIX_CTRL; | 
 | 	struct usdhc_data *data = NULL; | 
 | 	uint32_t *flag = &priv->op_context.cmd.flags; | 
 |  | 
 | 	if (!priv->op_context.cmd_only) | 
 | 		data = &priv->op_context.data; | 
 |  | 
 | 	if (data != NULL) { | 
 | 		if (data->data_type == USDHC_XFER_BOOT_CONTINUOUS) { | 
 | 			/* clear stop at block gap request */ | 
 | 			base->PROT_CTRL &= ~USDHC_PROT_CTRL_SABGREQ_MASK; | 
 | 			/* continuous transfer data */ | 
 | 			base->PROT_CTRL |= USDHC_PROT_CTRL_CREQ_MASK; | 
 | 			return 0; | 
 | 		} | 
 |  | 
 | 		/* check data inhibit flag */ | 
 | 		if (base->PRES_STATE & USDHC_DATA_INHIBIT_FLAG) | 
 | 			return -EBUSY; | 
 | 		/* check transfer block count */ | 
 | 		if ((data->block_count > USDHC_MAX_BLOCK_COUNT) || | 
 | 			(!data->tx_data && !data->rx_data)) | 
 | 			return -EINVAL; | 
 |  | 
 | 		/* config mix parameter */ | 
 | 		mix_ctrl &= ~(USDHC_MIX_CTRL_MSBSEL_MASK | | 
 | 			USDHC_MIX_CTRL_BCEN_MASK | | 
 | 			USDHC_MIX_CTRL_DTDSEL_MASK | | 
 | 			USDHC_MIX_CTRL_AC12EN_MASK); | 
 |  | 
 | 		if (data->rx_data) { | 
 | 			mix_ctrl |= USDHC_MIX_CTRL_DTDSEL_MASK; | 
 | 		} | 
 |  | 
 | 		if (data->block_count > 1U) { | 
 | 			mix_ctrl |= USDHC_MIX_CTRL_MSBSEL_MASK | | 
 | 				USDHC_MIX_CTRL_BCEN_MASK; | 
 | 			/* auto command 12 */ | 
 | 			if (data->cmd12) { | 
 | 				mix_ctrl |= USDHC_MIX_CTRL_AC12EN_MASK; | 
 | 			} | 
 | 		} | 
 |  | 
 | 		/* auto command 23, auto send set block count cmd before | 
 | 		 * multiple read/write | 
 | 		 */ | 
 | 		if ((data->cmd23)) { | 
 | 			mix_ctrl |= USDHC_MIX_CTRL_AC23EN_MASK; | 
 | 			base->VEND_SPEC2 |= | 
 | 				USDHC_VEND_SPEC2_ACMD23_ARGU2_EN_MASK; | 
 | 			/* config the block count to DS_ADDR */ | 
 | 			base->DS_ADDR = data->block_count; | 
 | 		} else { | 
 | 			mix_ctrl &= ~USDHC_MIX_CTRL_AC23EN_MASK; | 
 | 			base->VEND_SPEC2 &= | 
 | 				(~USDHC_VEND_SPEC2_ACMD23_ARGU2_EN_MASK); | 
 | 		} | 
 |  | 
 | 		if (data->data_type != USDHC_XFER_BOOT) { | 
 | 			/* config data block size/block count */ | 
 | 			base->BLK_ATT = | 
 | 				((base->BLK_ATT & ~(USDHC_BLK_ATT_BLKSIZE_MASK | | 
 | 				USDHC_BLK_ATT_BLKCNT_MASK)) | | 
 | 				(USDHC_BLK_ATT_BLKSIZE(data->block_size) | | 
 | 				USDHC_BLK_ATT_BLKCNT(data->block_count))); | 
 | 		} else { | 
 | 			mix_ctrl |= USDHC_MIX_CTRL_MSBSEL_MASK | | 
 | 				USDHC_MIX_CTRL_BCEN_MASK; | 
 | 			base->PROT_CTRL |= | 
 | 				USDHC_PROT_CTRL_RD_DONE_NO_8CLK_MASK; | 
 | 		} | 
 |  | 
 | 		/* data present flag */ | 
 | 		*flag |= USDHC_DATA_PRESENT_FLAG; | 
 | 		/* Disable useless interrupt */ | 
 | 		if (en_dma) { | 
 | 			base->INT_SIGNAL_EN &= | 
 | 				~(USDHC_INT_BUF_WRITE_READY_FLAG | | 
 | 				USDHC_INT_BUF_READ_READY_FLAG | | 
 | 				USDHC_INT_DMA_DONE_FLAG); | 
 | 			base->INT_STATUS_EN &= | 
 | 				~(USDHC_INT_BUF_WRITE_READY_FLAG | | 
 | 				USDHC_INT_BUF_READ_READY_FLAG | | 
 | 				USDHC_INT_DMA_DONE_FLAG); | 
 | 		} else { | 
 | 			base->INT_SIGNAL_EN |= | 
 | 				USDHC_INT_BUF_WRITE_READY_FLAG | | 
 | 				USDHC_INT_BUF_READ_READY_FLAG; | 
 | 			base->INT_STATUS_EN |= | 
 | 				USDHC_INT_BUF_WRITE_READY_FLAG | | 
 | 				USDHC_INT_BUF_READ_READY_FLAG; | 
 | 		} | 
 | 	} else { | 
 | 		/* clear data flags */ | 
 | 		mix_ctrl &= ~(USDHC_MIX_CTRL_MSBSEL_MASK | | 
 | 					USDHC_MIX_CTRL_BCEN_MASK | | 
 | 					USDHC_MIX_CTRL_DTDSEL_MASK | | 
 | 					USDHC_MIX_CTRL_AC12EN_MASK | | 
 | 					USDHC_MIX_CTRL_AC23EN_MASK); | 
 |  | 
 | 		if (base->PRES_STATE & USDHC_CMD_INHIBIT_FLAG) | 
 | 			return -EBUSY; | 
 | 	} | 
 |  | 
 | 	/* config the mix parameter */ | 
 | 	base->MIX_CTRL = mix_ctrl; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void usdhc_send_cmd(USDHC_Type *base, struct usdhc_cmd *command) | 
 | { | 
 | 	uint32_t xfer_type = base->CMD_XFR_TYP; | 
 | 	uint32_t flags = command->flags; | 
 |  | 
 | 	if (!(base->PRES_STATE & USDHC_CMD_INHIBIT_FLAG) | 
 | 		&& (command->cmd_type != USDHC_CMD_TYPE_EMPTY)) { | 
 | 		/* Define the flag corresponding to each response type. */ | 
 | 		switch (command->rsp_type) { | 
 | 		case SDHC_RSP_TYPE_NONE: | 
 | 			break; | 
 | 		case SDHC_RSP_TYPE_R1: /* Response 1 */ | 
 | 		case SDHC_RSP_TYPE_R5: /* Response 5 */ | 
 | 		case SDHC_RSP_TYPE_R6: /* Response 6 */ | 
 | 		case SDHC_RSP_TYPE_R7: /* Response 7 */ | 
 | 			flags |= (USDHC_RSP_LEN_48_FLAG | | 
 | 					USDHC_CRC_CHECK_FLAG | | 
 | 					USDHC_IDX_CHECK_FLAG); | 
 | 		break; | 
 |  | 
 | 		case SDHC_RSP_TYPE_R1b: /* Response 1 with busy */ | 
 | 		case SDHC_RSP_TYPE_R5b: /* Response 5 with busy */ | 
 | 			flags |= (USDHC_RSP_LEN_48_BUSY_FLAG | | 
 | 					USDHC_CRC_CHECK_FLAG | | 
 | 					USDHC_IDX_CHECK_FLAG); | 
 | 		break; | 
 |  | 
 | 		case SDHC_RSP_TYPE_R2: /* Response 2 */ | 
 | 			flags |= (USDHC_RSP_LEN_136_FLAG | | 
 | 				USDHC_CRC_CHECK_FLAG); | 
 | 		break; | 
 |  | 
 | 		case SDHC_RSP_TYPE_R3: /* Response 3 */ | 
 | 		case SDHC_RSP_TYPE_R4: /* Response 4 */ | 
 | 			flags |= (USDHC_RSP_LEN_48_FLAG); | 
 | 		break; | 
 |  | 
 | 		default: | 
 | 			break; | 
 | 		} | 
 |  | 
 | 		if (command->cmd_type == USDHC_CMD_TYPE_ABORT) | 
 | 			flags |= USDHC_CMD_TYPE_ABORT_FLAG; | 
 |  | 
 | 		/* config cmd index */ | 
 | 		xfer_type &= ~(USDHC_CMD_XFR_TYP_CMDINX_MASK | | 
 | 					USDHC_CMD_XFR_TYP_CMDTYP_MASK | | 
 | 					USDHC_CMD_XFR_TYP_CICEN_MASK | | 
 | 					USDHC_CMD_XFR_TYP_CCCEN_MASK | | 
 | 					USDHC_CMD_XFR_TYP_RSPTYP_MASK | | 
 | 					USDHC_CMD_XFR_TYP_DPSEL_MASK); | 
 |  | 
 | 		xfer_type |= | 
 | 			(((command->index << USDHC_CMD_XFR_TYP_CMDINX_SHIFT) & | 
 | 				USDHC_CMD_XFR_TYP_CMDINX_MASK) | | 
 | 				((flags) & (USDHC_CMD_XFR_TYP_CMDTYP_MASK | | 
 | 				USDHC_CMD_XFR_TYP_CICEN_MASK | | 
 | 				USDHC_CMD_XFR_TYP_CCCEN_MASK | | 
 | 				USDHC_CMD_XFR_TYP_RSPTYP_MASK | | 
 | 				USDHC_CMD_XFR_TYP_DPSEL_MASK))); | 
 |  | 
 | 		/* config the command xfertype and argument */ | 
 | 		base->CMD_ARG = command->argument; | 
 | 		base->CMD_XFR_TYP = xfer_type; | 
 | 	} | 
 |  | 
 | 	if (command->cmd_type == USDHC_CMD_TYPE_EMPTY) { | 
 | 		/* disable CMD done interrupt for empty command */ | 
 | 		base->INT_SIGNAL_EN &= ~USDHC_INT_SIGNAL_EN_CCIEN_MASK; | 
 | 	} | 
 | } | 
 |  | 
 | static int usdhc_cmd_rsp(struct usdhc_priv *priv) | 
 | { | 
 | 	uint32_t i; | 
 | 	USDHC_Type *base = priv->config->base; | 
 | 	struct usdhc_cmd *cmd = &priv->op_context.cmd; | 
 |  | 
 | 	if (cmd->rsp_type != SDHC_RSP_TYPE_NONE) { | 
 | 		cmd->response[0U] = base->CMD_RSP0; | 
 | 		if (cmd->rsp_type == SDHC_RSP_TYPE_R2) { | 
 | 			cmd->response[1U] = base->CMD_RSP1; | 
 | 			cmd->response[2U] = base->CMD_RSP2; | 
 | 			cmd->response[3U] = base->CMD_RSP3; | 
 |  | 
 | 			i = 4U; | 
 | 			/* R3-R2-R1-R0(lowest 8 bit is invalid bit) | 
 | 			 * has the same format | 
 | 			 * as R2 format in SD specification document | 
 | 			 * after removed internal CRC7 and end bit. | 
 | 			 */ | 
 | 			do { | 
 | 				cmd->response[i - 1U] <<= 8U; | 
 | 				if (i > 1U) { | 
 | 					cmd->response[i - 1U] |= | 
 | 						((cmd->response[i - 2U] & | 
 | 						0xFF000000U) >> 24U); | 
 | 				} | 
 | 				i--; | 
 | 			} while (i); | 
 | 		} | 
 | 	} | 
 | 	/* check response error flag */ | 
 | 	if ((cmd->rsp_err_flags) && | 
 | 		((cmd->rsp_type == SDHC_RSP_TYPE_R1) || | 
 | 		(cmd->rsp_type == SDHC_RSP_TYPE_R1b) || | 
 | 		(cmd->rsp_type == SDHC_RSP_TYPE_R6) || | 
 | 		(cmd->rsp_type == SDHC_RSP_TYPE_R5))) { | 
 | 		if (((cmd->rsp_err_flags) & (cmd->response[0U]))) | 
 | 			return -EIO; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int usdhc_wait_cmd_done(struct usdhc_priv *priv, | 
 | 	bool poll_cmd) | 
 | { | 
 | 	int error = 0; | 
 | 	uint32_t int_status = 0U; | 
 | 	USDHC_Type *base = priv->config->base; | 
 |  | 
 | 	/* check if need polling command done or not */ | 
 | 	if (poll_cmd) { | 
 | 		/* Wait command complete or USDHC encounters error. */ | 
 | 		while (!(int_status & (USDHC_INT_CMD_DONE_FLAG | | 
 | 			USDHC_INT_CMD_ERR_FLAG))) { | 
 | 			int_status = base->INT_STATUS; | 
 | 		} | 
 |  | 
 | 		if ((int_status & USDHC_INT_TUNING_ERR_FLAG) || | 
 | 			(int_status & USDHC_INT_CMD_ERR_FLAG)) { | 
 | 			error = -EIO; | 
 | 		} | 
 | 		/* Receive response when command completes successfully. */ | 
 | 		if (!error) { | 
 | 			error = usdhc_cmd_rsp(priv); | 
 | 		} else { | 
 | 			LOG_ERR("CMD%d Polling ERROR", | 
 | 				priv->op_context.cmd.index); | 
 | 		} | 
 |  | 
 | 		base->INT_STATUS = (USDHC_INT_CMD_DONE_FLAG | | 
 | 				USDHC_INT_CMD_ERR_FLAG | | 
 | 				USDHC_INT_TUNING_ERR_FLAG); | 
 | 	} | 
 |  | 
 | 	return error; | 
 | } | 
 |  | 
 | static inline void usdhc_write_data(USDHC_Type *base, uint32_t data) | 
 | { | 
 | 	base->DATA_BUFF_ACC_PORT = data; | 
 | } | 
 |  | 
 | static inline uint32_t usdhc_read_data(USDHC_Type *base) | 
 | { | 
 | 	return base->DATA_BUFF_ACC_PORT; | 
 | } | 
 |  | 
 | static uint32_t usdhc_read_data_port(struct usdhc_priv *priv, | 
 | 	uint32_t xfered_words) | 
 | { | 
 | 	USDHC_Type *base = priv->config->base; | 
 | 	struct usdhc_data *data = &priv->op_context.data; | 
 | 	uint32_t i, total_words, remaing_words; | 
 | 	/* The words can be read at this time. */ | 
 | 	uint32_t watermark = ((base->WTMK_LVL & USDHC_WTMK_LVL_RD_WML_MASK) >> | 
 | 		USDHC_WTMK_LVL_RD_WML_SHIFT); | 
 |  | 
 | 	/* If DMA is enable, do not need to polling data port */ | 
 | 	if (!(base->MIX_CTRL & USDHC_MIX_CTRL_DMAEN_MASK)) { | 
 | 		/*Add non aligned access support.*/ | 
 | 		if (data->block_size % sizeof(uint32_t)) { | 
 | 			data->block_size += | 
 | 				sizeof(uint32_t) - | 
 | 				(data->block_size % sizeof(uint32_t)); | 
 | 			/* make the block size as word-aligned */ | 
 | 		} | 
 |  | 
 | 		total_words = ((data->block_count * data->block_size) / | 
 | 			sizeof(uint32_t)); | 
 |  | 
 | 		if (watermark >= total_words) { | 
 | 			remaing_words = total_words; | 
 | 		} else if ((watermark < total_words) && | 
 | 			((total_words - xfered_words) >= watermark)) { | 
 | 			remaing_words = watermark; | 
 | 		} else { | 
 | 			remaing_words = (total_words - xfered_words); | 
 | 		} | 
 |  | 
 | 		i = 0U; | 
 | 		while (i < remaing_words) { | 
 | 			data->rx_data[xfered_words++] = usdhc_read_data(base); | 
 | 			i++; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return xfered_words; | 
 | } | 
 |  | 
 | static int usdhc_read_data_port_sync(struct usdhc_priv *priv) | 
 | { | 
 | 	USDHC_Type *base = priv->config->base; | 
 | 	struct usdhc_data *data = &priv->op_context.data; | 
 | 	uint32_t total_words; | 
 | 	uint32_t xfered_words = 0U, int_status = 0U; | 
 | 	int error = 0; | 
 |  | 
 | 	if (data->block_size % sizeof(uint32_t)) { | 
 | 		data->block_size += | 
 | 			sizeof(uint32_t) - | 
 | 			(data->block_size % sizeof(uint32_t)); | 
 | 	} | 
 |  | 
 | 	total_words = | 
 | 		((data->block_count * data->block_size) / | 
 | 		sizeof(uint32_t)); | 
 |  | 
 | 	while ((!error) && (xfered_words < total_words)) { | 
 | 		while (!(int_status & (USDHC_INT_BUF_READ_READY_FLAG | | 
 | 			USDHC_INT_DATA_ERR_FLAG | | 
 | 			USDHC_INT_TUNING_ERR_FLAG))) | 
 | 			int_status = base->INT_STATUS; | 
 |  | 
 | 		/* during std tuning process, software do not need to read data, | 
 | 		 * but wait BRR is enough | 
 | 		 */ | 
 | 		if ((data->data_type == USDHC_XFER_TUNING) && | 
 | 			(int_status & USDHC_INT_BUF_READ_READY_FLAG)) { | 
 | 			base->INT_STATUS = USDHC_INT_BUF_READ_READY_FLAG | | 
 | 				USDHC_INT_TUNING_PASS_FLAG; | 
 |  | 
 | 			return 0; | 
 | 		} else if ((int_status & USDHC_INT_TUNING_ERR_FLAG)) { | 
 | 			base->INT_STATUS = USDHC_INT_TUNING_ERR_FLAG; | 
 | 			/* if tuning error occur ,return directly */ | 
 | 			error = -EIO; | 
 | 		} else if ((int_status & USDHC_INT_DATA_ERR_FLAG)) { | 
 | 			if (!(data->ignore_err)) | 
 | 				error = -EIO; | 
 | 			/* clear data error flag */ | 
 | 			base->INT_STATUS = USDHC_INT_DATA_ERR_FLAG; | 
 | 		} | 
 |  | 
 | 		if (!error) { | 
 | 			xfered_words = usdhc_read_data_port(priv, xfered_words); | 
 | 			/* clear buffer read ready */ | 
 | 			base->INT_STATUS = USDHC_INT_BUF_READ_READY_FLAG; | 
 | 			int_status = 0U; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* Clear data complete flag after the last read operation. */ | 
 | 	base->INT_STATUS = USDHC_INT_DATA_DONE_FLAG; | 
 |  | 
 | 	return error; | 
 | } | 
 |  | 
 | static uint32_t usdhc_write_data_port(struct usdhc_priv *priv, | 
 | 	uint32_t xfered_words) | 
 | { | 
 | 	USDHC_Type *base = priv->config->base; | 
 | 	struct usdhc_data *data = &priv->op_context.data; | 
 | 	uint32_t i, total_words, remaing_words; | 
 | 	/* Words can be wrote at this time. */ | 
 | 	uint32_t watermark = ((base->WTMK_LVL & USDHC_WTMK_LVL_WR_WML_MASK) >> | 
 | 		USDHC_WTMK_LVL_WR_WML_SHIFT); | 
 |  | 
 | 	/* If DMA is enable, do not need to polling data port */ | 
 | 	if (!(base->MIX_CTRL & USDHC_MIX_CTRL_DMAEN_MASK)) { | 
 | 		if (data->block_size % sizeof(uint32_t)) { | 
 | 			data->block_size += | 
 | 				sizeof(uint32_t) - | 
 | 				(data->block_size % sizeof(uint32_t)); | 
 | 		} | 
 |  | 
 | 		total_words = | 
 | 			((data->block_count * data->block_size) / | 
 | 			sizeof(uint32_t)); | 
 |  | 
 | 		if (watermark >= total_words) { | 
 | 			remaing_words = total_words; | 
 | 		} else if ((watermark < total_words) && | 
 | 			((total_words - xfered_words) >= watermark)) { | 
 | 			remaing_words = watermark; | 
 | 		} else { | 
 | 			remaing_words = (total_words - xfered_words); | 
 | 		} | 
 |  | 
 | 		i = 0U; | 
 | 		while (i < remaing_words) { | 
 | 			usdhc_write_data(base, data->tx_data[xfered_words++]); | 
 | 			i++; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return xfered_words; | 
 | } | 
 |  | 
 | static status_t usdhc_write_data_port_sync(struct usdhc_priv *priv) | 
 | { | 
 | 	USDHC_Type *base = priv->config->base; | 
 | 	struct usdhc_data *data = &priv->op_context.data; | 
 | 	uint32_t total_words; | 
 | 	uint32_t xfered_words = 0U, int_status = 0U; | 
 | 	int error = 0; | 
 |  | 
 | 	if (data->block_size % sizeof(uint32_t)) { | 
 | 		data->block_size += | 
 | 			sizeof(uint32_t) - (data->block_size % sizeof(uint32_t)); | 
 | 	} | 
 |  | 
 | 	total_words = (data->block_count * data->block_size) / sizeof(uint32_t); | 
 |  | 
 | 	while ((!error) && (xfered_words < total_words)) { | 
 | 		while (!(int_status & (USDHC_INT_BUF_WRITE_READY_FLAG | | 
 | 				USDHC_INT_DATA_ERR_FLAG | | 
 | 				USDHC_INT_TUNING_ERR_FLAG))) { | 
 | 			int_status = base->INT_STATUS; | 
 | 		} | 
 |  | 
 | 		if (int_status & USDHC_INT_TUNING_ERR_FLAG) { | 
 | 			base->INT_STATUS = USDHC_INT_TUNING_ERR_FLAG; | 
 | 			/* if tuning error occur ,return directly */ | 
 | 			return -EIO; | 
 | 		} else if (int_status & USDHC_INT_DATA_ERR_FLAG) { | 
 | 			if (!(data->ignore_err)) | 
 | 				error = -EIO; | 
 | 			/* clear data error flag */ | 
 | 			base->INT_STATUS = USDHC_INT_DATA_ERR_FLAG; | 
 | 		} | 
 |  | 
 | 		if (!error) { | 
 | 			xfered_words = usdhc_write_data_port(priv, | 
 | 				xfered_words); | 
 | 			/* clear buffer write ready */ | 
 | 			base->INT_STATUS = USDHC_INT_BUF_WRITE_READY_FLAG; | 
 | 			int_status = 0U; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* Wait write data complete or data transfer error | 
 | 	 * after the last writing operation. | 
 | 	 */ | 
 | 	while (!(int_status & (USDHC_INT_DATA_DONE_FLAG | | 
 | 		USDHC_INT_DATA_ERR_FLAG))) { | 
 | 		int_status = base->INT_STATUS; | 
 | 	} | 
 |  | 
 | 	if (int_status & USDHC_INT_DATA_ERR_FLAG) { | 
 | 		if (!(data->ignore_err)) | 
 | 			error = -EIO; | 
 | 	} | 
 | 	base->INT_STATUS = USDHC_INT_DATA_DONE_FLAG | | 
 | 		USDHC_INT_DATA_ERR_FLAG; | 
 |  | 
 | 	return error; | 
 | } | 
 |  | 
 | static int usdhc_data_sync_xfer(struct usdhc_priv *priv, bool en_dma) | 
 | { | 
 | 	int error = 0; | 
 | 	uint32_t int_status = 0U; | 
 | 	USDHC_Type *base = priv->config->base; | 
 | 	struct usdhc_data *data = &priv->op_context.data; | 
 |  | 
 | 	if (en_dma) { | 
 | 		/* Wait data complete or USDHC encounters error. */ | 
 | 		while (!((int_status & | 
 | 			(USDHC_INT_DATA_DONE_FLAG | USDHC_INT_DATA_ERR_FLAG | | 
 | 			USDHC_INT_CMD_ERR_FLAG | USDHC_INT_TUNING_ERR_FLAG)))) { | 
 | 			int_status = base->INT_STATUS; | 
 | 		} | 
 |  | 
 | 		if (int_status & USDHC_INT_TUNING_ERR_FLAG) { | 
 | 			error = -EIO; | 
 | 		} else if ((int_status & (USDHC_INT_DATA_ERR_FLAG | | 
 | 			USDHC_INT_DMA_ERR_FLAG))) { | 
 | 			if ((!(data->ignore_err)) || | 
 | 				(int_status & | 
 | 				USDHC_INT_DATA_TIMEOUT_FLAG)) { | 
 | 				error = -EIO; | 
 | 			} | 
 | 		} | 
 | 		/* load dummy data */ | 
 | 		if ((data->data_type == USDHC_XFER_BOOT_CONTINUOUS) && (!error)) | 
 | 			*(data->rx_data) = g_usdhc_boot_dummy; | 
 |  | 
 | 		base->INT_STATUS = (USDHC_INT_DATA_DONE_FLAG | | 
 | 			USDHC_INT_DATA_ERR_FLAG | | 
 | 			USDHC_INT_DMA_ERR_FLAG | | 
 | 			USDHC_INT_TUNING_PASS_FLAG | | 
 | 			USDHC_INT_TUNING_ERR_FLAG); | 
 | 	} else { | 
 | 		if (data->rx_data) { | 
 | 			error = usdhc_read_data_port_sync(priv); | 
 | 		} else { | 
 | 			error = usdhc_write_data_port_sync(priv); | 
 | 		} | 
 | 	} | 
 | 	return error; | 
 | } | 
 |  | 
 | static int usdhc_xfer(struct usdhc_priv *priv) | 
 | { | 
 | 	int error = -EIO; | 
 | 	struct usdhc_data *data = NULL; | 
 | 	bool en_dma = true, execute_tuning; | 
 | 	USDHC_Type *base = priv->config->base; | 
 |  | 
 | 	if (!priv->op_context.cmd_only) { | 
 | 		data = &priv->op_context.data; | 
 | 		if (data->data_type == USDHC_XFER_TUNING) | 
 | 			execute_tuning = true; | 
 | 		else | 
 | 			execute_tuning = false; | 
 | 	} else { | 
 | 		execute_tuning = false; | 
 | 	} | 
 |  | 
 | 	/*check re-tuning request*/ | 
 | 	if ((base->INT_STATUS & USDHC_INT_RE_TUNING_EVENT_FLAG)) { | 
 | 		base->INT_STATUS = USDHC_INT_RE_TUNING_EVENT_FLAG; | 
 | 		return -EAGAIN; | 
 | 	} | 
 |  | 
 | 	/* Update ADMA descriptor table according to different DMA mode | 
 | 	 * (no DMA, ADMA1, ADMA2). | 
 | 	 */ | 
 |  | 
 | 	if (data && (!execute_tuning) && priv->op_context.dma_cfg.adma_table) | 
 | 		error = usdhc_adma_table_cfg(priv, | 
 | 			(data->data_type & USDHC_XFER_BOOT) ? | 
 | 			USDHC_ADMA_MUTI_FLAG : USDHC_ADMA_SINGLE_FLAG); | 
 |  | 
 | 	/* if the DMA descriptor configure fail or not needed , disable it */ | 
 | 	if (error) { | 
 | 		en_dma = false; | 
 | 		/* disable DMA, using polling mode in this situation */ | 
 | 		base->MIX_CTRL &= ~USDHC_MIX_CTRL_DMAEN_MASK; | 
 | 		base->PROT_CTRL &= ~USDHC_PROT_CTRL_DMASEL_MASK; | 
 | 	} | 
 |  | 
 | 	/* config the data transfer parameter */ | 
 | 	error = usdhc_data_xfer_cfg(priv, en_dma); | 
 | 	if (error) | 
 | 		return error; | 
 | 	/* send command first */ | 
 | 	usdhc_send_cmd(base, &priv->op_context.cmd); | 
 | 	/* wait command done */ | 
 | 	error = usdhc_wait_cmd_done(priv, (data == NULL) || | 
 | 		(data->data_type == USDHC_XFER_NORMAL)); | 
 | 	/* wait transfer data finish */ | 
 | 	if (data && (!error)) { | 
 | 		return usdhc_data_sync_xfer(priv, en_dma); | 
 | 	} | 
 |  | 
 | 	return error; | 
 | } | 
 |  | 
 | static inline void usdhc_select_1_8_vol(USDHC_Type *base, bool en_1_8_v) | 
 | { | 
 | 	if (en_1_8_v) | 
 | 		base->VEND_SPEC |= USDHC_VEND_SPEC_VSELECT_MASK; | 
 | 	else | 
 | 		base->VEND_SPEC &= ~USDHC_VEND_SPEC_VSELECT_MASK; | 
 | } | 
 |  | 
 | static inline void usdhc_force_clk_on(USDHC_Type *base, bool on) | 
 | { | 
 | 	if (on) | 
 | 		base->VEND_SPEC |= USDHC_VEND_SPEC_FRC_SDCLK_ON_MASK; | 
 | 	else | 
 | 		base->VEND_SPEC &= ~USDHC_VEND_SPEC_FRC_SDCLK_ON_MASK; | 
 | } | 
 |  | 
 | static void usdhc_tuning(USDHC_Type *base, uint32_t start, uint32_t step, bool enable) | 
 | { | 
 | 	uint32_t tuning_ctrl = 0U; | 
 |  | 
 | 	if (enable) { | 
 | 		/* feedback clock */ | 
 | 		base->MIX_CTRL |= USDHC_MIX_CTRL_FBCLK_SEL_MASK; | 
 | 		/* config tuning start and step */ | 
 | 		tuning_ctrl = base->TUNING_CTRL; | 
 | 		tuning_ctrl &= ~(USDHC_TUNING_CTRL_TUNING_START_TAP_MASK | | 
 | 			USDHC_TUNING_CTRL_TUNING_STEP_MASK); | 
 | 		tuning_ctrl |= (USDHC_TUNING_CTRL_TUNING_START_TAP(start) | | 
 | 			USDHC_TUNING_CTRL_TUNING_STEP(step) | | 
 | 			USDHC_TUNING_CTRL_STD_TUNING_EN_MASK); | 
 | 		base->TUNING_CTRL = tuning_ctrl; | 
 |  | 
 | 		/* excute tuning */ | 
 | 		base->AUTOCMD12_ERR_STATUS |= | 
 | 			(USDHC_AUTOCMD12_ERR_STATUS_EXECUTE_TUNING_MASK | | 
 | 			USDHC_AUTOCMD12_ERR_STATUS_SMP_CLK_SEL_MASK); | 
 | 	} else { | 
 | 		/* disable the standard tuning */ | 
 | 		base->TUNING_CTRL &= ~USDHC_TUNING_CTRL_STD_TUNING_EN_MASK; | 
 | 		/* clear excute tuning */ | 
 | 		base->AUTOCMD12_ERR_STATUS &= | 
 | 			~(USDHC_AUTOCMD12_ERR_STATUS_EXECUTE_TUNING_MASK | | 
 | 			USDHC_AUTOCMD12_ERR_STATUS_SMP_CLK_SEL_MASK); | 
 | 	} | 
 | } | 
 |  | 
 | int usdhc_adjust_tuning_timing(USDHC_Type *base, uint32_t delay) | 
 | { | 
 | 	uint32_t clk_tune_ctrl = 0U; | 
 |  | 
 | 	clk_tune_ctrl = base->CLK_TUNE_CTRL_STATUS; | 
 |  | 
 | 	clk_tune_ctrl &= ~USDHC_CLK_TUNE_CTRL_STATUS_DLY_CELL_SET_PRE_MASK; | 
 |  | 
 | 	clk_tune_ctrl |= USDHC_CLK_TUNE_CTRL_STATUS_DLY_CELL_SET_PRE(delay); | 
 |  | 
 | 	/* load the delay setting */ | 
 | 	base->CLK_TUNE_CTRL_STATUS = clk_tune_ctrl; | 
 | 	/* check delat setting error */ | 
 | 	if (base->CLK_TUNE_CTRL_STATUS & | 
 | 		(USDHC_CLK_TUNE_CTRL_STATUS_PRE_ERR_MASK | | 
 | 		USDHC_CLK_TUNE_CTRL_STATUS_NXT_ERR_MASK)) | 
 | 		return -EIO; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static inline void usdhc_set_retuning_timer(USDHC_Type *base, uint32_t counter) | 
 | { | 
 | 	base->HOST_CTRL_CAP &= ~USDHC_HOST_CTRL_CAP_TIME_COUNT_RETUNING_MASK; | 
 | 	base->HOST_CTRL_CAP |= USDHC_HOST_CTRL_CAP_TIME_COUNT_RETUNING(counter); | 
 | } | 
 |  | 
 | static inline void usdhc_set_bus_width(USDHC_Type *base, | 
 | 	enum usdhc_data_bus_width width) | 
 | { | 
 | 	base->PROT_CTRL = ((base->PROT_CTRL & ~USDHC_PROT_CTRL_DTW_MASK) | | 
 | 		USDHC_PROT_CTRL_DTW(width)); | 
 | } | 
 |  | 
 | static int usdhc_execute_tuning(struct usdhc_priv *priv) | 
 | { | 
 | 	bool tuning_err = true; | 
 | 	int ret; | 
 | 	USDHC_Type *base = priv->config->base; | 
 |  | 
 | 	/* enable the standard tuning */ | 
 | 	usdhc_tuning(base, SDHC_STANDARD_TUNING_START, SDHC_TUINIG_STEP, true); | 
 |  | 
 | 	while (true) { | 
 | 		/* send tuning block */ | 
 | 		ret = usdhc_xfer(priv); | 
 | 		if (ret) { | 
 | 			return ret; | 
 | 		} | 
 | 		usdhc_millsec_delay(10); | 
 |  | 
 | 		/*wait excute tuning bit clear*/ | 
 | 		if ((base->AUTOCMD12_ERR_STATUS & | 
 | 			USDHC_AUTOCMD12_ERR_STATUS_EXECUTE_TUNING_MASK)) { | 
 | 			continue; | 
 | 		} | 
 |  | 
 | 		/* if tuning error , re-tuning again */ | 
 | 		if ((base->CLK_TUNE_CTRL_STATUS & | 
 | 			(USDHC_CLK_TUNE_CTRL_STATUS_NXT_ERR_MASK | | 
 | 			USDHC_CLK_TUNE_CTRL_STATUS_PRE_ERR_MASK)) && | 
 | 			tuning_err) { | 
 | 			tuning_err = false; | 
 | 			/* enable the standard tuning */ | 
 | 			usdhc_tuning(base, SDHC_STANDARD_TUNING_START, | 
 | 				SDHC_TUINIG_STEP, true); | 
 | 			usdhc_adjust_tuning_timing(base, | 
 | 				SDHC_STANDARD_TUNING_START); | 
 | 		} else { | 
 | 			break; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* delay to wait the host controller stable */ | 
 | 	usdhc_millsec_delay(1000); | 
 |  | 
 | 	/* check tuning result*/ | 
 | 	if (!(base->AUTOCMD12_ERR_STATUS & | 
 | 		USDHC_AUTOCMD12_ERR_STATUS_SMP_CLK_SEL_MASK)) { | 
 | 		return -EIO; | 
 | 	} | 
 |  | 
 | 	usdhc_set_retuning_timer(base, SDHC_RETUNING_TIMER_COUNT); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int usdhc_vol_switch(struct usdhc_priv *priv) | 
 | { | 
 | 	USDHC_Type *base = priv->config->base; | 
 | 	int retry = 0xffff; | 
 |  | 
 | 	while (base->PRES_STATE & | 
 | 		(CARD_DATA1_STATUS_MASK | CARD_DATA2_STATUS_MASK | | 
 | 		CARD_DATA3_STATUS_MASK | CARD_DATA0_NOT_BUSY)) { | 
 | 		retry--; | 
 | 		if (retry <= 0) { | 
 | 			return -EACCES; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* host switch to 1.8V */ | 
 | 	usdhc_select_1_8_vol(base, true); | 
 |  | 
 | 	usdhc_millsec_delay(20000U); | 
 |  | 
 | 	/*enable force clock on*/ | 
 | 	usdhc_force_clk_on(base, true); | 
 | 	/* dealy 1ms,not exactly correct when use while */ | 
 | 	usdhc_millsec_delay(20000U); | 
 | 	/*disable force clock on*/ | 
 | 	usdhc_force_clk_on(base, false); | 
 |  | 
 | 	/* check data line and cmd line status */ | 
 | 	retry = 0xffff; | 
 | 	while (!(base->PRES_STATE & | 
 | 		(CARD_DATA1_STATUS_MASK | CARD_DATA2_STATUS_MASK | | 
 | 		CARD_DATA3_STATUS_MASK | CARD_DATA0_NOT_BUSY))) { | 
 | 		retry--; | 
 | 		if (retry <= 0) { | 
 | 			return -EBUSY; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static inline void usdhc_op_ctx_init(struct usdhc_priv *priv, | 
 | 	bool cmd_only, uint8_t cmd_idx, uint32_t arg, enum sdhc_rsp_type rsp_type) | 
 | { | 
 | 	struct usdhc_cmd *cmd = &priv->op_context.cmd; | 
 | 	struct usdhc_data *data = &priv->op_context.data; | 
 |  | 
 | 	priv->op_context.cmd_only = cmd_only; | 
 |  | 
 | 	memset((char *)cmd, 0, sizeof(struct usdhc_cmd)); | 
 | 	memset((char *)data, 0, sizeof(struct usdhc_data)); | 
 |  | 
 | 	cmd->index = cmd_idx; | 
 | 	cmd->argument = arg; | 
 | 	cmd->rsp_type = rsp_type; | 
 | } | 
 |  | 
 | static int usdhc_select_fun(struct usdhc_priv *priv, | 
 | 	uint32_t group, uint32_t function) | 
 | { | 
 | 	const struct usdhc_config *config = priv->config; | 
 | 	uint32_t *fun_status; | 
 | 	uint16_t fun_grp_info[6U] = {0}; | 
 | 	uint32_t current_fun_status = 0U, arg; | 
 | 	struct usdhc_cmd *cmd = &priv->op_context.cmd; | 
 | 	struct usdhc_data *data = &priv->op_context.data; | 
 | 	int ret; | 
 |  | 
 | 	/* check if card support CMD6 */ | 
 | 	if ((priv->card_info.version <= SD_SPEC_VER1_0) || | 
 | 		(!(priv->card_info.csd.cmd_class & SD_CMD_CLASS_SWITCH))) { | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	/* Check if card support high speed mode. */ | 
 | 	arg = (SD_SWITCH_CHECK << 31U | 0x00FFFFFFU); | 
 | 	arg &= ~((uint32_t)(0xFU) << (group * 4U)); | 
 | 	arg |= (function << (group * 4U)); | 
 | 	usdhc_op_ctx_init(priv, 0, SDHC_SWITCH, arg, SDHC_RSP_TYPE_R1); | 
 |  | 
 | 	data->block_size = 64U; | 
 | 	data->block_count = 1U; | 
 | 	data->rx_data = &g_usdhc_rx_dummy[0]; | 
 | 	ret = usdhc_xfer(priv); | 
 | 	if (ret || (cmd->response[0U] & SDHC_R1ERR_All_FLAG)) | 
 | 		return -EIO; | 
 |  | 
 | 	fun_status = data->rx_data; | 
 |  | 
 | 	/* Switch function status byte sequence | 
 | 	 * from card is big endian(MSB first). | 
 | 	 */ | 
 | 	switch (config->endian) { | 
 | 	case USDHC_LITTLE_ENDIAN: | 
 | 		fun_status[0U] = SWAP_WORD_BYTE_SEQUENCE(fun_status[0U]); | 
 | 		fun_status[1U] = SWAP_WORD_BYTE_SEQUENCE(fun_status[1U]); | 
 | 		fun_status[2U] = SWAP_WORD_BYTE_SEQUENCE(fun_status[2U]); | 
 | 		fun_status[3U] = SWAP_WORD_BYTE_SEQUENCE(fun_status[3U]); | 
 | 		fun_status[4U] = SWAP_WORD_BYTE_SEQUENCE(fun_status[4U]); | 
 | 		break; | 
 | 	case USDHC_BIG_ENDIAN: | 
 | 		break; | 
 | 	case USDHC_HALF_WORD_BIG_ENDIAN: | 
 | 		fun_status[0U] = SWAP_HALF_WROD_BYTE_SEQUENCE(fun_status[0U]); | 
 | 		fun_status[1U] = SWAP_HALF_WROD_BYTE_SEQUENCE(fun_status[1U]); | 
 | 		fun_status[2U] = SWAP_HALF_WROD_BYTE_SEQUENCE(fun_status[2U]); | 
 | 		fun_status[3U] = SWAP_HALF_WROD_BYTE_SEQUENCE(fun_status[3U]); | 
 | 		fun_status[4U] = SWAP_HALF_WROD_BYTE_SEQUENCE(fun_status[4U]); | 
 | 		break; | 
 | 	default: | 
 | 		return -ENOTSUP; | 
 | 	} | 
 |  | 
 | 	fun_grp_info[5U] = (uint16_t)fun_status[0U]; | 
 | 	fun_grp_info[4U] = (uint16_t)(fun_status[1U] >> 16U); | 
 | 	fun_grp_info[3U] = (uint16_t)(fun_status[1U]); | 
 | 	fun_grp_info[2U] = (uint16_t)(fun_status[2U] >> 16U); | 
 | 	fun_grp_info[1U] = (uint16_t)(fun_status[2U]); | 
 | 	fun_grp_info[0U] = (uint16_t)(fun_status[3U] >> 16U); | 
 | 	current_fun_status = ((fun_status[3U] & 0xFFU) << 8U) | | 
 | 		(fun_status[4U] >> 24U); | 
 |  | 
 | 	/* check if function is support */ | 
 | 	if (((fun_grp_info[group] & (1 << function)) == 0U) || | 
 | 		((current_fun_status >> | 
 | 		(group * 4U)) & 0xFU) != function) { | 
 | 		return -ENOTSUP; | 
 | 	} | 
 |  | 
 | 	/* Switch to high speed mode. */ | 
 | 	usdhc_op_ctx_init(priv, 0, SDHC_SWITCH, arg, SDHC_RSP_TYPE_R1); | 
 |  | 
 | 	data->block_size = 64U; | 
 | 	data->block_count = 1U; | 
 | 	data->rx_data = &g_usdhc_rx_dummy[0]; | 
 |  | 
 | 	cmd->argument = (SD_SWITCH_SET << 31U | 0x00FFFFFFU); | 
 | 	cmd->argument &= ~((uint32_t)(0xFU) << (group * 4U)); | 
 | 	cmd->argument |= (function << (group * 4U)); | 
 |  | 
 | 	ret = usdhc_xfer(priv); | 
 | 	if (ret || (cmd->response[0U] & SDHC_R1ERR_All_FLAG)) | 
 | 		return -EIO; | 
 | 	/* Switch function status byte sequence | 
 | 	 * from card is big endian(MSB first). | 
 | 	 */ | 
 | 	switch (config->endian) { | 
 | 	case USDHC_LITTLE_ENDIAN: | 
 | 		fun_status[3U] = SWAP_WORD_BYTE_SEQUENCE(fun_status[3U]); | 
 | 		fun_status[4U] = SWAP_WORD_BYTE_SEQUENCE(fun_status[4U]); | 
 | 		break; | 
 | 	case USDHC_BIG_ENDIAN: | 
 | 		break; | 
 | 	case USDHC_HALF_WORD_BIG_ENDIAN: | 
 | 		fun_status[3U] = SWAP_HALF_WROD_BYTE_SEQUENCE(fun_status[3U]); | 
 | 		fun_status[4U] = SWAP_HALF_WROD_BYTE_SEQUENCE(fun_status[4U]); | 
 | 		break; | 
 | 	default: | 
 | 		return -ENOTSUP; | 
 | 	} | 
 | 	/* According to the "switch function status[bits 511~0]" return | 
 | 	 * by switch command in mode "set function": | 
 | 	 * -check if group 1 is successfully changed to function 1 by checking | 
 | 	 * if bits 379~376 equal value 1; | 
 | 	 */ | 
 | 	current_fun_status = ((fun_status[3U] & 0xFFU) << 8U) | | 
 | 		(fun_status[4U] >> 24U); | 
 |  | 
 | 	if (((current_fun_status >> | 
 | 		(group * 4U)) & 0xFU) != function) { | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | uint32_t usdhc_set_sd_clk(USDHC_Type *base, uint32_t src_clk_hz, uint32_t sd_clk_hz) | 
 | { | 
 | 	uint32_t total_div = 0U; | 
 | 	uint32_t divisor = 0U; | 
 | 	uint32_t prescaler = 0U; | 
 | 	uint32_t sysctl = 0U; | 
 | 	uint32_t nearest_freq = 0U; | 
 |  | 
 | 	__ASSERT_NO_MSG(src_clk_hz != 0U); | 
 | 	__ASSERT_NO_MSG((sd_clk_hz != 0U) && (sd_clk_hz <= src_clk_hz)); | 
 |  | 
 | 	/* calculate total divisor first */ | 
 | 	total_div = src_clk_hz / sd_clk_hz; | 
 | 	if (total_div > (USDHC_MAX_CLKFS * USDHC_MAX_DVS)) { | 
 | 		return 0U; | 
 | 	} | 
 |  | 
 | 	if (total_div) { | 
 | 		/* calculate the divisor (src_clk_hz / divisor) <= sd_clk_hz */ | 
 | 		if ((src_clk_hz / total_div) > sd_clk_hz) | 
 | 			total_div++; | 
 |  | 
 | 		/* divide the total divisor to div and prescaler */ | 
 | 		if (total_div > USDHC_MAX_DVS) { | 
 | 			prescaler = total_div / USDHC_MAX_DVS; | 
 | 			/* prescaler must be a value which equal 2^n and | 
 | 			 * smaller than SDHC_MAX_CLKFS | 
 | 			 */ | 
 | 			while (((USDHC_MAX_CLKFS % prescaler) != 0U) || | 
 | 				(prescaler == 1U)) | 
 | 				prescaler++; | 
 | 			/* calculate the divisor */ | 
 | 			divisor = total_div / prescaler; | 
 | 			/* fine tuning the divisor until | 
 | 			 * divisor * prescaler >= total_div | 
 | 			 */ | 
 | 			while ((divisor * prescaler) < total_div) { | 
 | 				divisor++; | 
 | 				if (divisor > USDHC_MAX_DVS) { | 
 | 					if ((prescaler <<= 1U) > | 
 | 						USDHC_MAX_CLKFS) { | 
 | 						return 0; | 
 | 					} | 
 | 				divisor = total_div / prescaler; | 
 | 				} | 
 | 			} | 
 | 		} else { | 
 | 			/* in this situation , divsior and SDCLKFS | 
 | 			 * can generate same clock | 
 | 			 * use SDCLKFS | 
 | 			 */ | 
 | 			if (((total_div % 2U) != 0U) & (total_div != 1U)) { | 
 | 				divisor = total_div; | 
 | 				prescaler = 1U; | 
 | 			} else { | 
 | 				divisor = 1U; | 
 | 				prescaler = total_div; | 
 | 			} | 
 | 		} | 
 | 		nearest_freq = src_clk_hz / (divisor == 0U ? 1U : divisor) / | 
 | 			prescaler; | 
 | 	} else { | 
 | 		/* in this condition , src_clk_hz = busClock_Hz, */ | 
 | 		/* in DDR mode , set SDCLKFS to 0, divisor = 0, actually the | 
 | 		 * totoal divider = 2U | 
 | 		 */ | 
 | 		divisor = 0U; | 
 | 		prescaler = 0U; | 
 | 		nearest_freq = src_clk_hz; | 
 | 	} | 
 |  | 
 | 	/* calculate the value write to register */ | 
 | 	if (divisor != 0U) { | 
 | 		USDHC_PREV_DVS(divisor); | 
 | 	} | 
 | 	/* calculate the value write to register */ | 
 | 	if (prescaler != 0U) { | 
 | 		USDHC_PREV_CLKFS(prescaler, 1U); | 
 | 	} | 
 |  | 
 | 	/* Set the SD clock frequency divisor, SD clock frequency select, | 
 | 	 * data timeout counter value. | 
 | 	 */ | 
 | 	sysctl = base->SYS_CTRL; | 
 | 	sysctl &= ~(USDHC_SYS_CTRL_DVS_MASK | | 
 | 		USDHC_SYS_CTRL_SDCLKFS_MASK); | 
 | 	sysctl |= (USDHC_SYS_CTRL_DVS(divisor) | | 
 | 		USDHC_SYS_CTRL_SDCLKFS(prescaler)); | 
 | 	base->SYS_CTRL = sysctl; | 
 |  | 
 | 	/* Wait until the SD clock is stable. */ | 
 | 	while (!(base->PRES_STATE & USDHC_PRES_STATE_SDSTB_MASK)) { | 
 | 		; | 
 | 	} | 
 |  | 
 | 	return nearest_freq; | 
 | } | 
 |  | 
 | static void usdhc_enable_ddr_mode(USDHC_Type *base, | 
 | 	bool enable, uint32_t nibble_pos) | 
 | { | 
 | 	uint32_t prescaler = (base->SYS_CTRL & USDHC_SYS_CTRL_SDCLKFS_MASK) >> | 
 | 		USDHC_SYS_CTRL_SDCLKFS_SHIFT; | 
 |  | 
 | 	if (enable) { | 
 | 		base->MIX_CTRL &= ~USDHC_MIX_CTRL_NIBBLE_POS_MASK; | 
 | 		base->MIX_CTRL |= (USDHC_MIX_CTRL_DDR_EN_MASK | | 
 | 			USDHC_MIX_CTRL_NIBBLE_POS(nibble_pos)); | 
 | 		prescaler >>= 1U; | 
 | 	} else { | 
 | 		base->MIX_CTRL &= ~USDHC_MIX_CTRL_DDR_EN_MASK; | 
 |  | 
 | 		if (prescaler == 0U) { | 
 | 			prescaler += 1U; | 
 | 		} else { | 
 | 			prescaler <<= 1U; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	base->SYS_CTRL = (base->SYS_CTRL & (~USDHC_SYS_CTRL_SDCLKFS_MASK)) | | 
 | 		USDHC_SYS_CTRL_SDCLKFS(prescaler); | 
 | } | 
 |  | 
 | static int usdhc_select_bus_timing(struct usdhc_priv *priv) | 
 | { | 
 | 	const struct usdhc_config *config = priv->config; | 
 | 	int error = -EIO; | 
 |  | 
 | 	if (priv->card_info.voltage != SD_VOL_1_8_V) { | 
 | 		/* Switch the card to high speed mode */ | 
 | 		if (priv->host_capability.host_flags & | 
 | 			USDHC_SUPPORT_HIGHSPEED_FLAG) { | 
 | 			/* group 1, function 1 ->high speed mode*/ | 
 | 			error = usdhc_select_fun(priv, SD_GRP_TIMING_MODE, | 
 | 				SD_TIMING_SDR25_HIGH_SPEED_MODE); | 
 | 			/* If the result isn't "switching to | 
 | 			 * high speed mode(50MHZ) | 
 | 			 * successfully or card doesn't support | 
 | 			 * high speed | 
 | 			 * mode". Return failed status. | 
 | 			 */ | 
 | 			if (!error) { | 
 | 				priv->card_info.sd_timing = | 
 | 					SD_TIMING_SDR25_HIGH_SPEED_MODE; | 
 | 				priv->card_info.busclk_hz = | 
 | 					usdhc_set_sd_clk(config->base, | 
 | 						priv->src_clk_hz, | 
 | 						SD_CLOCK_50MHZ); | 
 | 			} else if (error == -ENOTSUP) { | 
 | 				/* if not support high speed, | 
 | 				 * keep the card work at default mode | 
 | 				 */ | 
 | 				return 0; | 
 | 			} | 
 | 		} else { | 
 | 			/* if not support high speed, | 
 | 			 * keep the card work at default mode | 
 | 			 */ | 
 | 			return 0; | 
 | 		} | 
 | 	} else if ((USDHC_SUPPORT_SDR104_FLAG != | 
 | 		SDMMCHOST_NOT_SUPPORT) || | 
 | 		(USDHC_SUPPORT_SDR50_FLAG != SDMMCHOST_NOT_SUPPORT) || | 
 | 		(USDHC_SUPPORT_DDR50_FLAG != SDMMCHOST_NOT_SUPPORT)) { | 
 | 		/* card is in UHS_I mode */ | 
 | 		switch (priv->card_info.sd_timing) { | 
 | 			/* if not select timing mode, | 
 | 			 * sdmmc will handle it automatically | 
 | 			 */ | 
 | 		case SD_TIMING_SDR12_DFT_MODE: | 
 | 		case SD_TIMING_SDR104_MODE: | 
 | 			error = usdhc_select_fun(priv, SD_GRP_TIMING_MODE, | 
 | 				SD_TIMING_SDR104_MODE); | 
 | 			if (!error) { | 
 | 				priv->card_info.sd_timing = | 
 | 					SD_TIMING_SDR104_MODE; | 
 | 				priv->card_info.busclk_hz = | 
 | 					usdhc_set_sd_clk(config->base, | 
 | 						priv->src_clk_hz, | 
 | 						SDMMCHOST_SUPPORT_SDR104_FREQ); | 
 | 				break; | 
 | 			} | 
 | 		case SD_TIMING_DDR50_MODE: | 
 | 			error = usdhc_select_fun(priv, SD_GRP_TIMING_MODE, | 
 | 				SD_TIMING_DDR50_MODE); | 
 | 			if (!error) { | 
 | 				priv->card_info.sd_timing = | 
 | 					SD_TIMING_DDR50_MODE; | 
 | 				priv->card_info.busclk_hz = | 
 | 					usdhc_set_sd_clk( | 
 | 						config->base, | 
 | 						priv->src_clk_hz, | 
 | 						SD_CLOCK_50MHZ); | 
 | 				usdhc_enable_ddr_mode(config->base, true, 0U); | 
 | 			} | 
 | 			break; | 
 | 		case SD_TIMING_SDR50_MODE: | 
 | 			error = usdhc_select_fun(priv, | 
 | 				SD_GRP_TIMING_MODE, | 
 | 				SD_TIMING_SDR50_MODE); | 
 | 			if (!error) { | 
 | 				priv->card_info.sd_timing = | 
 | 					SD_TIMING_SDR50_MODE; | 
 | 				priv->card_info.busclk_hz = | 
 | 					usdhc_set_sd_clk( | 
 | 						config->base, | 
 | 						priv->src_clk_hz, | 
 | 						SD_CLOCK_100MHZ); | 
 | 			} | 
 | 			break; | 
 | 		case SD_TIMING_SDR25_HIGH_SPEED_MODE: | 
 | 			error = usdhc_select_fun(priv, SD_GRP_TIMING_MODE, | 
 | 				SD_TIMING_SDR25_HIGH_SPEED_MODE); | 
 | 			if (!error) { | 
 | 				priv->card_info.sd_timing = | 
 | 					SD_TIMING_SDR25_HIGH_SPEED_MODE; | 
 | 				priv->card_info.busclk_hz = | 
 | 					usdhc_set_sd_clk( | 
 | 						config->base, | 
 | 						priv->src_clk_hz, | 
 | 						SD_CLOCK_50MHZ); | 
 | 			} | 
 | 			break; | 
 |  | 
 | 		default: | 
 | 			break; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* SDR50 and SDR104 mode need tuning */ | 
 | 	if ((priv->card_info.sd_timing == SD_TIMING_SDR50_MODE) || | 
 | 		(priv->card_info.sd_timing == SD_TIMING_SDR104_MODE)) { | 
 | 		struct usdhc_cmd *cmd = &priv->op_context.cmd; | 
 | 		struct usdhc_data *data = &priv->op_context.data; | 
 |  | 
 | 		/* config IO strength in IOMUX*/ | 
 | 		if (priv->card_info.sd_timing == SD_TIMING_SDR50_MODE) { | 
 | 			imxrt_usdhc_pinmux(config->nusdhc, false, | 
 | 				CARD_BUS_FREQ_100MHZ1, | 
 | 				CARD_BUS_STRENGTH_7); | 
 | 		} else { | 
 | 			imxrt_usdhc_pinmux(config->nusdhc, false, | 
 | 				CARD_BUS_FREQ_200MHZ, | 
 | 				CARD_BUS_STRENGTH_7); | 
 | 		} | 
 | 		/* execute tuning */ | 
 | 		priv->op_context.cmd_only = 0; | 
 |  | 
 | 		memset((char *)cmd, 0, sizeof(struct usdhc_cmd)); | 
 | 		memset((char *)data, 0, sizeof(struct usdhc_data)); | 
 |  | 
 | 		cmd->index = SDHC_SEND_TUNING_BLOCK; | 
 | 		cmd->rsp_type = SDHC_RSP_TYPE_R1; | 
 |  | 
 | 		data->block_size = 64; | 
 | 		data->block_count = 1U; | 
 | 		data->rx_data = &g_usdhc_rx_dummy[0]; | 
 | 		data->data_type = USDHC_XFER_TUNING; | 
 | 		error = usdhc_execute_tuning(priv); | 
 | 		if (error) | 
 | 			return error; | 
 | 	} else { | 
 | 		/* set default IO strength to 4 to cover card adapter driver | 
 | 		 * strength difference | 
 | 		 */ | 
 | 		imxrt_usdhc_pinmux(config->nusdhc, false, | 
 | 			CARD_BUS_FREQ_100MHZ1, | 
 | 			CARD_BUS_STRENGTH_4); | 
 | 	} | 
 |  | 
 | 	return error; | 
 | } | 
 |  | 
 | static int usdhc_write_sector(void *bus_data, const uint8_t *buf, uint32_t sector, | 
 | 		     uint32_t count) | 
 | { | 
 | 	struct usdhc_priv *priv = bus_data; | 
 | 	struct usdhc_cmd *cmd = &priv->op_context.cmd; | 
 | 	struct usdhc_data *data = &priv->op_context.data; | 
 |  | 
 | 	memset((char *)cmd, 0, sizeof(struct usdhc_cmd)); | 
 | 	memset((char *)data, 0, sizeof(struct usdhc_data)); | 
 |  | 
 | 	priv->op_context.cmd_only = 0; | 
 | 	cmd->index = SDHC_WRITE_MULTIPLE_BLOCK; | 
 | 	data->block_size = priv->card_info.sd_block_size; | 
 | 	data->block_count = count; | 
 | 	data->tx_data = (const uint32_t *)buf; | 
 | 	data->cmd12 = true; | 
 | 	if (data->block_count == 1U) { | 
 | 		cmd->index = SDHC_WRITE_BLOCK; | 
 | 	} | 
 | 	cmd->argument = sector; | 
 | 	if (!(priv->card_info.card_flags & SDHC_HIGH_CAPACITY_FLAG)) { | 
 | 		cmd->argument *= priv->card_info.sd_block_size; | 
 | 	} | 
 | 	cmd->rsp_type = SDHC_RSP_TYPE_R1; | 
 | 	cmd->rsp_err_flags = SDHC_R1ERR_All_FLAG; | 
 |  | 
 | 	return usdhc_xfer(priv); | 
 | } | 
 |  | 
 | static int usdhc_read_sector(void *bus_data, uint8_t *buf, uint32_t sector, | 
 | 		     uint32_t count) | 
 | { | 
 | 	struct usdhc_priv *priv = bus_data; | 
 | 	struct usdhc_cmd *cmd = &priv->op_context.cmd; | 
 | 	struct usdhc_data *data = &priv->op_context.data; | 
 |  | 
 | 	memset((char *)cmd, 0, sizeof(struct usdhc_cmd)); | 
 | 	memset((char *)data, 0, sizeof(struct usdhc_data)); | 
 |  | 
 | 	priv->op_context.cmd_only = 0; | 
 | 	cmd->index = SDHC_READ_MULTIPLE_BLOCK; | 
 | 	data->block_size = priv->card_info.sd_block_size; | 
 | 	data->block_count = count; | 
 | 	data->rx_data = (uint32_t *)buf; | 
 | 	data->cmd12 = true; | 
 |  | 
 | 	if (data->block_count == 1U) { | 
 | 		cmd->index = SDHC_READ_SINGLE_BLOCK; | 
 | 	} | 
 |  | 
 | 	cmd->argument = sector; | 
 | 	if (!(priv->card_info.card_flags & SDHC_HIGH_CAPACITY_FLAG)) { | 
 | 		cmd->argument *= priv->card_info.sd_block_size; | 
 | 	} | 
 |  | 
 | 	cmd->rsp_type = SDHC_RSP_TYPE_R1; | 
 | 	cmd->rsp_err_flags = SDHC_R1ERR_All_FLAG; | 
 |  | 
 | 	return usdhc_xfer(priv); | 
 | } | 
 |  | 
 | static bool usdhc_set_sd_active(USDHC_Type *base) | 
 | { | 
 | 	uint32_t timeout = 0xffff; | 
 |  | 
 | 	base->SYS_CTRL |= USDHC_SYS_CTRL_INITA_MASK; | 
 | 	/* Delay some time to wait card become active state. */ | 
 | 	while ((base->SYS_CTRL & USDHC_SYS_CTRL_INITA_MASK) == | 
 | 		USDHC_SYS_CTRL_INITA_MASK) { | 
 | 		if (!timeout) { | 
 | 			break; | 
 | 		} | 
 | 		timeout--; | 
 | 	} | 
 |  | 
 | 	return ((!timeout) ? false : true); | 
 | } | 
 |  | 
 | static void usdhc_get_host_capability(USDHC_Type *base, | 
 | 	struct usdhc_capability *capability) | 
 | { | 
 | 	uint32_t host_cap; | 
 | 	uint32_t max_blk_len; | 
 |  | 
 | 	host_cap = base->HOST_CTRL_CAP; | 
 |  | 
 | 	/* Get the capability of USDHC. */ | 
 | 	max_blk_len = ((host_cap & USDHC_HOST_CTRL_CAP_MBL_MASK) >> | 
 | 		USDHC_HOST_CTRL_CAP_MBL_SHIFT); | 
 | 	capability->max_blk_len = (512U << max_blk_len); | 
 | 	/* Other attributes not in HTCAPBLT register. */ | 
 | 	capability->max_blk_cnt = USDHC_MAX_BLOCK_COUNT; | 
 | 	capability->host_flags = (host_cap & (USDHC_SUPPORT_ADMA_FLAG | | 
 | 		USDHC_SUPPORT_HIGHSPEED_FLAG | USDHC_SUPPORT_DMA_FLAG | | 
 | 		USDHC_SUPPORT_SUSPEND_RESUME_FLAG | USDHC_SUPPORT_V330_FLAG)); | 
 | 	capability->host_flags |= (host_cap & USDHC_SUPPORT_V300_FLAG); | 
 | 	capability->host_flags |= (host_cap & USDHC_SUPPORT_V180_FLAG); | 
 | 	capability->host_flags |= | 
 | 		(host_cap & (USDHC_SUPPORT_DDR50_FLAG | | 
 | 			USDHC_SUPPORT_SDR104_FLAG | | 
 | 			USDHC_SUPPORT_SDR50_FLAG)); | 
 | 	/* USDHC support 4/8 bit data bus width. */ | 
 | 	capability->host_flags |= (USDHC_SUPPORT_4BIT_FLAG | | 
 | 		USDHC_SUPPORT_8BIT_FLAG); | 
 | } | 
 |  | 
 | static bool usdhc_hw_reset(USDHC_Type *base, uint32_t mask, uint32_t timeout) | 
 | { | 
 | 	base->SYS_CTRL |= (mask & (USDHC_SYS_CTRL_RSTA_MASK | | 
 | 		USDHC_SYS_CTRL_RSTC_MASK | USDHC_SYS_CTRL_RSTD_MASK)); | 
 | 	/* Delay some time to wait reset success. */ | 
 | 	while ((base->SYS_CTRL & mask)) { | 
 | 		if (!timeout) { | 
 | 			break; | 
 | 		} | 
 | 		timeout--; | 
 | 	} | 
 |  | 
 | 	return ((!timeout) ? false : true); | 
 | } | 
 |  | 
 | static void usdhc_host_hw_init(USDHC_Type *base, | 
 | 	const struct usdhc_config *config) | 
 | { | 
 | 	uint32_t proctl, sysctl, wml; | 
 | 	uint32_t int_mask; | 
 |  | 
 | 	__ASSERT_NO_MSG(config); | 
 | 	__ASSERT_NO_MSG((config->write_watermark >= 1U) && | 
 | 			(config->write_watermark <= 128U)); | 
 | 	__ASSERT_NO_MSG((config->read_watermark >= 1U) && | 
 | 			(config->read_watermark <= 128U)); | 
 | 	__ASSERT_NO_MSG(config->write_burst_len <= 16U); | 
 |  | 
 | 	/* Reset USDHC. */ | 
 | 	usdhc_hw_reset(base, USDHC_RESET_ALL, 100U); | 
 |  | 
 | 	proctl = base->PROT_CTRL; | 
 | 	wml = base->WTMK_LVL; | 
 | 	sysctl = base->SYS_CTRL; | 
 |  | 
 | 	proctl &= ~(USDHC_PROT_CTRL_EMODE_MASK | USDHC_PROT_CTRL_DMASEL_MASK); | 
 | 	/* Endian mode*/ | 
 | 	proctl |= USDHC_PROT_CTRL_EMODE(config->endian); | 
 |  | 
 | 	/* Watermark level */ | 
 | 	wml &= ~(USDHC_WTMK_LVL_RD_WML_MASK | | 
 | 			USDHC_WTMK_LVL_WR_WML_MASK | | 
 | 			USDHC_WTMK_LVL_RD_BRST_LEN_MASK | | 
 | 			USDHC_WTMK_LVL_WR_BRST_LEN_MASK); | 
 | 	wml |= (USDHC_WTMK_LVL_RD_WML(config->read_watermark) | | 
 | 			USDHC_WTMK_LVL_WR_WML(config->write_watermark) | | 
 | 			USDHC_WTMK_LVL_RD_BRST_LEN(config->read_burst_len) | | 
 | 			USDHC_WTMK_LVL_WR_BRST_LEN(config->write_burst_len)); | 
 |  | 
 | 	/* config the data timeout value */ | 
 | 	sysctl &= ~USDHC_SYS_CTRL_DTOCV_MASK; | 
 | 	sysctl |= USDHC_SYS_CTRL_DTOCV(config->data_timeout); | 
 |  | 
 | 	base->SYS_CTRL = sysctl; | 
 | 	base->WTMK_LVL = wml; | 
 | 	base->PROT_CTRL = proctl; | 
 |  | 
 | 	/* disable internal DMA and DDR mode */ | 
 | 	base->MIX_CTRL &= ~(USDHC_MIX_CTRL_DMAEN_MASK | | 
 | 		USDHC_MIX_CTRL_DDR_EN_MASK); | 
 |  | 
 | 	int_mask = (USDHC_INT_CMD_FLAG | USDHC_INT_CARD_DETECT_FLAG | | 
 | 		USDHC_INT_DATA_FLAG | USDHC_INT_SDR104_TUNING_FLAG | | 
 | 		USDHC_INT_BLK_GAP_EVENT_FLAG); | 
 |  | 
 | 	base->INT_STATUS_EN |= int_mask; | 
 |  | 
 | } | 
 |  | 
 | static void usdhc_cd_gpio_cb(const struct device *dev, | 
 | 				  struct gpio_callback *cb, uint32_t pins) | 
 | { | 
 | 	struct usdhc_priv *priv = | 
 | 		CONTAINER_OF(cb, struct usdhc_priv, detect_cb); | 
 | 	const struct usdhc_config *config = priv->config; | 
 |  | 
 | 	gpio_pin_interrupt_configure(dev, config->detect_pin, GPIO_INT_DISABLE); | 
 | } | 
 |  | 
 | static int usdhc_cd_gpio_init(const struct device *detect_gpio, | 
 | 			      uint32_t pin, gpio_dt_flags_t flags, | 
 | 			      struct gpio_callback *callback) | 
 | { | 
 | 	int ret; | 
 |  | 
 | 	ret = gpio_pin_configure(detect_gpio, pin, GPIO_INPUT | flags); | 
 | 	if (ret) | 
 | 		return ret; | 
 |  | 
 | 	gpio_init_callback(callback, usdhc_cd_gpio_cb, BIT(pin)); | 
 |  | 
 | 	return gpio_add_callback(detect_gpio, callback); | 
 | } | 
 |  | 
 | static void usdhc_host_reset(struct usdhc_priv *priv) | 
 | { | 
 | 	USDHC_Type *base = priv->config->base; | 
 |  | 
 | 	usdhc_select_1_8_vol(base, false); | 
 | 	usdhc_enable_ddr_mode(base, false, 0); | 
 | 	usdhc_tuning(base, SDHC_STANDARD_TUNING_START, SDHC_TUINIG_STEP, false); | 
 | #if FSL_FEATURE_USDHC_HAS_HS400_MODE | 
 | #error Not implemented! | 
 | 	/* Disable HS400 mode */ | 
 | 	/* Disable DLL */ | 
 | #endif | 
 | } | 
 |  | 
 | static int usdhc_app_host_cmd(struct usdhc_priv *priv, int retry, | 
 | 	uint32_t arg, uint8_t app_cmd, uint32_t app_arg, enum sdhc_rsp_type rsp_type, | 
 | 	enum sdhc_rsp_type app_rsp_type, bool app_cmd_only) | 
 | { | 
 | 	struct usdhc_cmd *cmd = &priv->op_context.cmd; | 
 | 	int ret; | 
 |  | 
 | APP_CMD_XFER_AGAIN: | 
 | 	priv->op_context.cmd_only = 1; | 
 | 	cmd->index = SDHC_APP_CMD; | 
 | 	cmd->argument = arg; | 
 | 	cmd->rsp_type = rsp_type; | 
 | 	ret = usdhc_xfer(priv); | 
 | 	retry--; | 
 | 	if (ret && retry > 0) { | 
 | 		goto APP_CMD_XFER_AGAIN; | 
 | 	} | 
 |  | 
 | 	priv->op_context.cmd_only = app_cmd_only; | 
 | 	cmd->index = app_cmd; | 
 | 	cmd->argument = app_arg; | 
 | 	cmd->rsp_type = app_rsp_type; | 
 | 	ret = usdhc_xfer(priv); | 
 | 	if (ret && retry > 0) { | 
 | 		goto APP_CMD_XFER_AGAIN; | 
 | 	} | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | static int usdhc_sd_init(struct usdhc_priv *priv) | 
 | { | 
 | 	const struct usdhc_config *config = priv->config; | 
 | 	USDHC_Type *base = config->base; | 
 | 	uint32_t app_cmd_41_arg = 0U; | 
 | 	int ret, retry; | 
 | 	struct usdhc_cmd *cmd = &priv->op_context.cmd; | 
 | 	struct usdhc_data *data = &priv->op_context.data; | 
 |  | 
 | 	if (!priv->host_ready) { | 
 | 		return -ENODEV; | 
 | 	} | 
 |  | 
 | 	/* reset variables */ | 
 | 	priv->card_info.card_flags = 0U; | 
 | 	/* set DATA bus width 1bit at beginning*/ | 
 | 	usdhc_set_bus_width(base, USDHC_DATA_BUS_WIDTH_1BIT); | 
 | 	/*set card freq to 400KHZ at begging*/ | 
 | 	priv->card_info.busclk_hz = | 
 | 		usdhc_set_sd_clk(base, priv->src_clk_hz, | 
 | 			SDMMC_CLOCK_400KHZ); | 
 | 	/* send card active */ | 
 | 	ret = usdhc_set_sd_active(base); | 
 | 	if (ret == false) { | 
 | 		return -EIO; | 
 | 	} | 
 |  | 
 | 	/* Get host capability. */ | 
 | 	usdhc_get_host_capability(base, &priv->host_capability); | 
 |  | 
 | 	/* card go idle */ | 
 | 	usdhc_op_ctx_init(priv, 1, SDHC_GO_IDLE_STATE, 0, SDHC_RSP_TYPE_NONE); | 
 |  | 
 | 	ret = usdhc_xfer(priv); | 
 | 	if (ret) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	if (USDHC_SUPPORT_V330_FLAG != SDMMCHOST_NOT_SUPPORT) { | 
 | 		app_cmd_41_arg |= (SD_OCR_VDD32_33FLAG | SD_OCR_VDD33_34FLAG); | 
 | 		priv->card_info.voltage = SD_VOL_3_3_V; | 
 | 	} else if (USDHC_SUPPORT_V300_FLAG != SDMMCHOST_NOT_SUPPORT) { | 
 | 		app_cmd_41_arg |= SD_OCR_VDD29_30FLAG; | 
 | 		priv->card_info.voltage = SD_VOL_3_3_V; | 
 | 	} | 
 |  | 
 | 	/* allow user select the work voltage, if not select, | 
 | 	 * sdmmc will handle it automatically | 
 | 	 */ | 
 | 	if (priv->config->no_1_8_v == false) { | 
 | 		if (USDHC_SUPPORT_V180_FLAG != SDMMCHOST_NOT_SUPPORT) { | 
 | 			app_cmd_41_arg |= SD_OCR_SWITCH_18_REQ_FLAG; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* Check card's supported interface condition. */ | 
 | 	usdhc_op_ctx_init(priv, 1, SDHC_SEND_IF_COND, | 
 | 		SDHC_VHS_3V3 | SDHC_CHECK, SDHC_RSP_TYPE_R7); | 
 |  | 
 | 	retry = 10; | 
 | 	while (retry) { | 
 | 		ret = usdhc_xfer(priv); | 
 | 		if (!ret) { | 
 | 			if ((cmd->response[0U] & 0xFFU) != SDHC_CHECK) { | 
 | 				ret = -ENOTSUP; | 
 | 			} else { | 
 | 				break; | 
 | 			} | 
 | 		} | 
 | 		retry--; | 
 | 	} | 
 |  | 
 | 	if (!ret) { | 
 | 		/* SDHC or SDXC card */ | 
 | 		app_cmd_41_arg |= SD_OCR_HOST_CAP_FLAG; | 
 | 		priv->card_info.card_flags |= USDHC_SDHC_FLAG; | 
 | 	} else { | 
 | 		/* SDSC card */ | 
 | 		LOG_ERR("USDHC SDSC not implemented yet!"); | 
 | 		return -ENOTSUP; | 
 | 	} | 
 |  | 
 | 	/* Set card interface condition according to SDHC capability and | 
 | 	 * card's supported interface condition. | 
 | 	 */ | 
 | APP_SEND_OP_COND_AGAIN: | 
 | 	usdhc_op_ctx_init(priv, 1, 0, 0, SDHC_RSP_TYPE_NONE); | 
 | 	ret = usdhc_app_host_cmd(priv, NXP_SDMMC_MAX_VOLTAGE_RETRIES, 0, | 
 | 		SDHC_APP_SEND_OP_COND, app_cmd_41_arg, | 
 | 		SDHC_RSP_TYPE_R1, SDHC_RSP_TYPE_R3, 1); | 
 | 	if (ret) { | 
 | 		LOG_ERR("APP Condition CMD failed:%d", ret); | 
 | 		return ret; | 
 | 	} | 
 | 	if (cmd->response[0U] & SD_OCR_PWR_BUSY_FLAG) { | 
 | 		/* high capacity check */ | 
 | 		if (cmd->response[0U] & SD_OCR_CARD_CAP_FLAG) { | 
 | 			priv->card_info.card_flags |= SDHC_HIGH_CAPACITY_FLAG; | 
 | 		} | 
 |  | 
 | 		if (priv->config->no_1_8_v == false) { | 
 | 			/* 1.8V support */ | 
 | 			if (cmd->response[0U] & SD_OCR_SWITCH_18_ACCEPT_FLAG) { | 
 | 				priv->card_info.card_flags |= SDHC_1800MV_FLAG; | 
 | 			} | 
 | 		} | 
 | 		priv->card_info.raw_ocr = cmd->response[0U]; | 
 | 	} else { | 
 | 		goto APP_SEND_OP_COND_AGAIN; | 
 | 	} | 
 |  | 
 | 	/* check if card support 1.8V */ | 
 | 	if ((priv->card_info.card_flags & USDHC_VOL_1_8V_FLAG)) { | 
 | 		usdhc_op_ctx_init(priv, 1, SDHC_VOL_SWITCH, | 
 | 			0, SDHC_RSP_TYPE_R1); | 
 |  | 
 | 		ret = usdhc_xfer(priv); | 
 | 		if (!ret) { | 
 | 			ret = usdhc_vol_switch(priv); | 
 | 		} | 
 | 		if (ret) { | 
 | 			LOG_ERR("Voltage switch failed: %d", ret); | 
 | 			return ret; | 
 | 		} | 
 | 		priv->card_info.voltage = SD_VOL_1_8_V; | 
 | 	} | 
 |  | 
 | 	/* Initialize card if the card is SD card. */ | 
 | 	usdhc_op_ctx_init(priv, 1, SDHC_ALL_SEND_CID, 0, SDHC_RSP_TYPE_R2); | 
 |  | 
 | 	ret = usdhc_xfer(priv); | 
 | 	if (!ret) { | 
 | 		memcpy(priv->card_info.raw_cid, cmd->response, | 
 | 			sizeof(priv->card_info.raw_cid)); | 
 | 		sdhc_decode_cid(&priv->card_info.cid, | 
 | 			priv->card_info.raw_cid); | 
 | 	} else { | 
 | 		LOG_ERR("All send CID CMD failed: %d", ret); | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	usdhc_op_ctx_init(priv, 1, SDHC_SEND_RELATIVE_ADDR, | 
 | 		0, SDHC_RSP_TYPE_R6); | 
 |  | 
 | 	ret = usdhc_xfer(priv); | 
 | 	if (!ret) { | 
 | 		priv->card_info.relative_addr = (cmd->response[0U] >> 16U); | 
 | 	} else { | 
 | 		LOG_ERR("Send relative address CMD failed: %d", ret); | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	usdhc_op_ctx_init(priv, 1, SDHC_SEND_CSD, | 
 | 		(priv->card_info.relative_addr << 16U), SDHC_RSP_TYPE_R2); | 
 |  | 
 | 	ret = usdhc_xfer(priv); | 
 | 	if (!ret) { | 
 | 		memcpy(priv->card_info.raw_csd, cmd->response, | 
 | 			sizeof(priv->card_info.raw_csd)); | 
 | 		sdhc_decode_csd(&priv->card_info.csd, priv->card_info.raw_csd, | 
 | 			&priv->card_info.sd_block_count, | 
 | 			&priv->card_info.sd_block_size); | 
 | 	} else { | 
 | 		LOG_ERR("Send CSD CMD failed: %d", ret); | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	usdhc_op_ctx_init(priv, 1, SDHC_SELECT_CARD, | 
 | 		priv->card_info.relative_addr << 16U, | 
 | 		SDHC_RSP_TYPE_R1); | 
 |  | 
 | 	ret = usdhc_xfer(priv); | 
 | 	if (ret || (cmd->response[0U] & SDHC_R1ERR_All_FLAG)) { | 
 | 		LOG_ERR("Select card CMD failed: %d", ret); | 
 | 		return -EIO; | 
 | 	} | 
 |  | 
 | 	usdhc_op_ctx_init(priv, 0, 0, 0, SDHC_RSP_TYPE_NONE); | 
 | 	data->block_size = 8; | 
 | 	data->block_count = 1; | 
 | 	data->rx_data = &priv->card_info.raw_scr[0]; | 
 | 	ret = usdhc_app_host_cmd(priv, 1, (priv->card_info.relative_addr << 16), | 
 | 		SDHC_APP_SEND_SCR, 0, | 
 | 		SDHC_RSP_TYPE_R1, SDHC_RSP_TYPE_R1, 0); | 
 |  | 
 | 	if (ret) { | 
 | 		LOG_ERR("Send SCR following APP CMD failed: %d", ret); | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	switch (config->endian) { | 
 | 	case USDHC_LITTLE_ENDIAN: | 
 | 		priv->card_info.raw_scr[0] = | 
 | 			SWAP_WORD_BYTE_SEQUENCE(priv->card_info.raw_scr[0]); | 
 | 		priv->card_info.raw_scr[1] = | 
 | 			SWAP_WORD_BYTE_SEQUENCE(priv->card_info.raw_scr[1]); | 
 | 	break; | 
 | 	case USDHC_BIG_ENDIAN: | 
 | 	break; | 
 | 	case USDHC_HALF_WORD_BIG_ENDIAN: | 
 | 		priv->card_info.raw_scr[0U] = | 
 | 			SWAP_HALF_WROD_BYTE_SEQUENCE( | 
 | 				priv->card_info.raw_scr[0U]); | 
 | 		priv->card_info.raw_scr[1U] = | 
 | 			SWAP_HALF_WROD_BYTE_SEQUENCE( | 
 | 				priv->card_info.raw_scr[1U]); | 
 | 	break; | 
 | 	default: | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	sdhc_decode_scr(&priv->card_info.scr, priv->card_info.raw_scr, | 
 | 		&priv->card_info.version); | 
 | 	if (priv->card_info.scr.sd_width & 0x4U) { | 
 | 		priv->card_info.card_flags |= | 
 | 			USDHC_4BIT_WIDTH_FLAG; | 
 | 	} | 
 | 	/* speed class control cmd */ | 
 | 	if (priv->card_info.scr.cmd_support & 0x01U) { | 
 | 		priv->card_info.card_flags |= | 
 | 			USDHC_SPEED_CLASS_CONTROL_CMD_FLAG; | 
 | 	} | 
 | 	/* set block count cmd */ | 
 | 	if (priv->card_info.scr.cmd_support & 0x02U) { | 
 | 		priv->card_info.card_flags |= | 
 | 			USDHC_SET_BLK_CNT_CMD23_FLAG; | 
 | 	} | 
 |  | 
 | 	/* Set to max frequency in non-high speed mode. */ | 
 | 	priv->card_info.busclk_hz = usdhc_set_sd_clk(base, | 
 | 		priv->src_clk_hz, SD_CLOCK_25MHZ); | 
 |  | 
 | 	/* Set to 4-bit data bus mode. */ | 
 | 	if ((priv->host_capability.host_flags & USDHC_SUPPORT_4BIT_FLAG) && | 
 | 		(priv->card_info.card_flags & USDHC_4BIT_WIDTH_FLAG)) { | 
 | 		usdhc_op_ctx_init(priv, 1, 0, 0, SDHC_RSP_TYPE_NONE); | 
 |  | 
 | 		ret = usdhc_app_host_cmd(priv, 1, | 
 | 				(priv->card_info.relative_addr << 16), | 
 | 				SDHC_APP_SET_BUS_WIDTH, 2, | 
 | 				SDHC_RSP_TYPE_R1, SDHC_RSP_TYPE_R1, 1); | 
 |  | 
 | 		if (ret) { | 
 | 			LOG_ERR("Set bus width failed: %d", ret); | 
 | 			return ret; | 
 | 		} | 
 | 		usdhc_set_bus_width(base, USDHC_DATA_BUS_WIDTH_4BIT); | 
 | 	} | 
 |  | 
 | 	if (priv->card_info.version >= SD_SPEC_VER3_0) { | 
 | 		/* set sd card driver strength */ | 
 | 		ret = usdhc_select_fun(priv, SD_GRP_DRIVER_STRENGTH_MODE, | 
 | 			priv->card_info.driver_strength); | 
 | 		if (ret) { | 
 | 			LOG_ERR("Set SD driver strength failed: %d", ret); | 
 | 			return ret; | 
 | 		} | 
 |  | 
 | 		/* set sd card current limit */ | 
 | 		ret = usdhc_select_fun(priv, SD_GRP_CURRENT_LIMIT_MODE, | 
 | 			priv->card_info.max_current); | 
 | 		if (ret) { | 
 | 			LOG_ERR("Set SD current limit failed: %d", ret); | 
 | 			return ret; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* set block size */ | 
 | 	usdhc_op_ctx_init(priv, 1, SDHC_SET_BLOCK_SIZE, | 
 | 		priv->card_info.sd_block_size, SDHC_RSP_TYPE_R1); | 
 |  | 
 | 	ret = usdhc_xfer(priv); | 
 | 	if (ret || cmd->response[0U] & SDHC_R1ERR_All_FLAG) { | 
 | 		LOG_ERR("Set block size failed: %d", ret); | 
 | 		return -EIO; | 
 | 	} | 
 |  | 
 | 	if (priv->card_info.version > SD_SPEC_VER1_0) { | 
 | 		/* select bus timing */ | 
 | 		ret = usdhc_select_bus_timing(priv); | 
 | 		if (ret) { | 
 | 			LOG_ERR("Select bus timing failed: %d", ret); | 
 | 			return ret; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	retry = 10; | 
 | 	ret = -EIO; | 
 | 	while (ret && retry >= 0) { | 
 | 		ret = usdhc_read_sector(priv, (uint8_t *)g_usdhc_rx_dummy, 0, 1); | 
 | 		if (!ret) { | 
 | 			break; | 
 | 		} | 
 | 		retry--; | 
 | 	} | 
 |  | 
 | 	if (ret) { | 
 | 		LOG_ERR("USDHC bus device initalization failed!"); | 
 | 	} | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | static K_MUTEX_DEFINE(z_usdhc_init_lock); | 
 |  | 
 | static int usdhc_board_access_init(struct usdhc_priv *priv) | 
 | { | 
 | 	const struct usdhc_config *config = priv->config; | 
 | 	int ret; | 
 | 	uint32_t gpio_level; | 
 |  | 
 | 	if (config->pwr_name) { | 
 | 		priv->pwr_gpio = device_get_binding(config->pwr_name); | 
 | 		if (!priv->pwr_gpio) { | 
 | 			return -ENODEV; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if (config->detect_name) { | 
 | 		priv->detect_type = SD_DETECT_GPIO_CD; | 
 | 		priv->detect_gpio = device_get_binding(config->detect_name); | 
 | 		if (!priv->detect_gpio) { | 
 | 			return -ENODEV; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if (priv->pwr_gpio) { | 
 | 		ret = gpio_pin_configure(priv->pwr_gpio, | 
 | 				config->pwr_pin, | 
 | 				GPIO_OUTPUT_ACTIVE | | 
 | 				config->pwr_flags); | 
 | 		if (ret) { | 
 | 			return ret; | 
 | 		} | 
 |  | 
 | 		/* 100ms delay to make sure SD card is stable, | 
 | 		 * maybe could be shorter | 
 | 		 */ | 
 | 		k_busy_wait(100000); | 
 | 	} | 
 |  | 
 | 	if (!priv->detect_gpio) { | 
 | 		LOG_INF("USDHC detection other than GPIO not implemented!"); | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	ret = usdhc_cd_gpio_init(priv->detect_gpio, | 
 | 			config->detect_pin, | 
 | 			config->detect_flags, | 
 | 			&priv->detect_cb); | 
 | 	if (ret) { | 
 | 		return ret; | 
 | 	} | 
 | 	ret = gpio_pin_get(priv->detect_gpio, config->detect_pin); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	gpio_level = ret; | 
 |  | 
 | 	if (gpio_level == 0) { | 
 | 		priv->inserted = false; | 
 | 		LOG_ERR("NO SD inserted!"); | 
 |  | 
 | 		return -ENODEV; | 
 | 	} | 
 |  | 
 | 	priv->inserted = true; | 
 | 	LOG_INF("SD inserted!"); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int usdhc_access_init(const struct device *dev) | 
 | { | 
 | 	const struct usdhc_config *config = dev->config; | 
 | 	struct usdhc_priv *priv = dev->data; | 
 | 	int ret; | 
 |  | 
 | 	(void)k_mutex_lock(&z_usdhc_init_lock, K_FOREVER); | 
 |  | 
 | 	memset((char *)priv, 0, sizeof(struct usdhc_priv)); | 
 | 	priv->config = config; | 
 |  | 
 | 	if (!config->base) { | 
 | 		k_mutex_unlock(&z_usdhc_init_lock); | 
 |  | 
 | 		return -ENODEV; | 
 | 	} | 
 |  | 
 | 	if (clock_control_get_rate(config->clock_dev, | 
 | 				   config->clock_subsys, | 
 | 				   &priv->src_clk_hz)) { | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	ret = usdhc_board_access_init(priv); | 
 | 	if (ret) { | 
 | 		k_mutex_unlock(&z_usdhc_init_lock); | 
 |  | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	priv->op_context.dma_cfg.dma_mode = USDHC_DMA_ADMA2; | 
 | 	priv->op_context.dma_cfg.burst_len = USDHC_INCR_BURST_LEN; | 
 | 	/*No DMA used for this Version*/ | 
 | 	priv->op_context.dma_cfg.adma_table = 0; | 
 | 	priv->op_context.dma_cfg.adma_table_words = USDHC_ADMA_TABLE_WORDS; | 
 | 	usdhc_host_hw_init(config->base, config); | 
 | 	priv->host_ready = 1; | 
 |  | 
 | 	usdhc_host_reset(priv); | 
 | 	ret = usdhc_sd_init(priv); | 
 | 	k_mutex_unlock(&z_usdhc_init_lock); | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | static int disk_usdhc_access_status(struct disk_info *disk) | 
 | { | 
 | 	const struct device *dev = disk->dev; | 
 | 	struct usdhc_priv *priv = dev->data; | 
 |  | 
 | 	return priv->status; | 
 | } | 
 |  | 
 | static int disk_usdhc_access_read(struct disk_info *disk, uint8_t *buf, | 
 | 				 uint32_t sector, uint32_t count) | 
 | { | 
 | 	const struct device *dev = disk->dev; | 
 | 	struct usdhc_priv *priv = dev->data; | 
 |  | 
 | 	LOG_DBG("sector=%u count=%u", sector, count); | 
 |  | 
 | 	return usdhc_read_sector(priv, buf, sector, count); | 
 | } | 
 |  | 
 | static int disk_usdhc_access_write(struct disk_info *disk, const uint8_t *buf, | 
 | 				  uint32_t sector, uint32_t count) | 
 | { | 
 | 	const struct device *dev = disk->dev; | 
 | 	struct usdhc_priv *priv = dev->data; | 
 |  | 
 | 	LOG_DBG("sector=%u count=%u", sector, count); | 
 |  | 
 | 	return usdhc_write_sector(priv, buf, sector, count); | 
 | } | 
 |  | 
 | static int disk_usdhc_access_ioctl(struct disk_info *disk, uint8_t cmd, void *buf) | 
 | { | 
 | 	const struct device *dev = disk->dev; | 
 | 	struct usdhc_priv *priv = dev->data; | 
 | 	int err; | 
 |  | 
 | 	err = sdhc_map_disk_status(priv->status); | 
 | 	if (err != 0) { | 
 | 		return err; | 
 | 	} | 
 |  | 
 | 	switch (cmd) { | 
 | 	case DISK_IOCTL_CTRL_SYNC: | 
 | 		break; | 
 | 	case DISK_IOCTL_GET_SECTOR_COUNT: | 
 | 		*(uint32_t *)buf = priv->card_info.sd_block_count; | 
 | 		break; | 
 | 	case DISK_IOCTL_GET_SECTOR_SIZE: | 
 | 		*(uint32_t *)buf = priv->card_info.sd_block_size; | 
 | 		break; | 
 | 	case DISK_IOCTL_GET_ERASE_BLOCK_SZ: | 
 | 		*(uint32_t *)buf = priv->card_info.sd_block_size; | 
 | 		break; | 
 | 	default: | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int disk_usdhc_access_init(struct disk_info *disk) | 
 | { | 
 | 	const struct device *dev = disk->dev; | 
 | 	struct usdhc_priv *priv = dev->data; | 
 |  | 
 | 	if (priv->status == DISK_STATUS_OK) { | 
 | 		/* Called twice, don't re-init. */ | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	return usdhc_access_init(dev); | 
 | } | 
 |  | 
 | static const struct disk_operations usdhc_disk_ops = { | 
 | 	.init = disk_usdhc_access_init, | 
 | 	.status = disk_usdhc_access_status, | 
 | 	.read = disk_usdhc_access_read, | 
 | 	.write = disk_usdhc_access_write, | 
 | 	.ioctl = disk_usdhc_access_ioctl, | 
 | }; | 
 |  | 
 | static struct disk_info usdhc_disk = { | 
 | 	.name = CONFIG_SDMMC_VOLUME_NAME, | 
 | 	.ops = &usdhc_disk_ops, | 
 | }; | 
 |  | 
 | static int disk_usdhc_init(const struct device *dev) | 
 | { | 
 | 	struct usdhc_priv *priv = dev->data; | 
 |  | 
 | 	priv->status = DISK_STATUS_UNINIT; | 
 |  | 
 | 	usdhc_disk.dev = dev; | 
 |  | 
 | 	return disk_access_register(&usdhc_disk); | 
 | } | 
 |  | 
 | #define DISK_ACCESS_USDHC_INIT_NONE(n) | 
 |  | 
 | #define DISK_ACCESS_USDHC_INIT_PWR_PROPS(n)				\ | 
 | 	.pwr_name = DT_INST_GPIO_LABEL(n, pwr_gpios),			\ | 
 | 	.pwr_pin = DT_INST_GPIO_PIN(n, pwr_gpios),			\ | 
 | 	.pwr_flags = DT_INST_GPIO_FLAGS(n, pwr_gpios), | 
 |  | 
 | #define DISK_ACCESS_USDHC_INIT_CD_PROPS(n)				\ | 
 | 	.detect_name = DT_INST_GPIO_LABEL(n, cd_gpios),			\ | 
 | 	.detect_pin = DT_INST_GPIO_PIN(n, cd_gpios),			\ | 
 | 	.detect_flags = DT_INST_GPIO_FLAGS(n, cd_gpios), | 
 |  | 
 | #define DISK_ACCESS_USDHC_INIT_PWR(n)					\ | 
 | 	COND_CODE_1(DT_INST_NODE_HAS_PROP(n, pwr_gpios),		\ | 
 | 		    (DISK_ACCESS_USDHC_INIT_PWR_PROPS(n)),		\ | 
 | 		    (DISK_ACCESS_USDHC_INIT_NONE(n))) | 
 |  | 
 | #define DISK_ACCESS_USDHC_INIT_CD(n)					\ | 
 | 	COND_CODE_1(DT_INST_NODE_HAS_PROP(n, cd_gpios),			\ | 
 | 		    (DISK_ACCESS_USDHC_INIT_CD_PROPS(n)),		\ | 
 | 		    (DISK_ACCESS_USDHC_INIT_NONE(n))) | 
 |  | 
 | #define DISK_ACCESS_USDHC_INIT(n)					\ | 
 | 	static const struct usdhc_config usdhc_config_##n = {		\ | 
 | 		.base = (USDHC_Type  *) DT_INST_REG_ADDR(n),		\ | 
 | 		.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)),	\ | 
 | 		.clock_subsys =						\ | 
 | 		(clock_control_subsys_t)DT_INST_CLOCKS_CELL(n, name),	\ | 
 | 		.nusdhc = n,						\ | 
 | 		DISK_ACCESS_USDHC_INIT_PWR(n)				\ | 
 | 		DISK_ACCESS_USDHC_INIT_CD(n)				\ | 
 | 		.no_1_8_v = DT_INST_PROP(n, no_1_8_v),			\ | 
 | 		.data_timeout = USDHC_DATA_TIMEOUT,			\ | 
 | 		.endian = USDHC_LITTLE_ENDIAN,				\ | 
 | 		.read_watermark = USDHC_READ_WATERMARK_LEVEL,		\ | 
 | 		.write_watermark =  USDHC_WRITE_WATERMARK_LEVEL,	\ | 
 | 		.read_burst_len = USDHC_READ_BURST_LEN,			\ | 
 | 		.write_burst_len = USDHC_WRITE_BURST_LEN,		\ | 
 | 	};								\ | 
 | 									\ | 
 | 	static struct usdhc_priv usdhc_priv_##n;			\ | 
 | 									\ | 
 | 	DEVICE_DT_INST_DEFINE(n,					\ | 
 | 			    &disk_usdhc_init,				\ | 
 | 			    device_pm_control_nop,			\ | 
 | 			    &usdhc_priv_##n,				\ | 
 | 			    &usdhc_config_##n,				\ | 
 | 			    POST_KERNEL,				\ | 
 | 			    CONFIG_SDMMC_INIT_PRIORITY,			\ | 
 | 			    NULL); | 
 |  | 
 | DT_INST_FOREACH_STATUS_OKAY(DISK_ACCESS_USDHC_INIT) |