1757bff49SJaehoon Chung /* 2757bff49SJaehoon Chung * (C) Copyright 2012 SAMSUNG Electronics 3757bff49SJaehoon Chung * Jaehoon Chung <jh80.chung@samsung.com> 4757bff49SJaehoon Chung * Rajeshawari Shinde <rajeshwari.s@samsung.com> 5757bff49SJaehoon Chung * 61a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 7757bff49SJaehoon Chung */ 8757bff49SJaehoon Chung 9757bff49SJaehoon Chung #include <common.h> 1047f7fd3aSJason Zhu #include <bouncebuf.h> 11bbd0d600SZiyuan Xu #include <div64.h> 121c87ffe8SSimon Glass #include <errno.h> 13757bff49SJaehoon Chung #include <malloc.h> 14cf92e05cSSimon Glass #include <memalign.h> 15757bff49SJaehoon Chung #include <mmc.h> 16757bff49SJaehoon Chung #include <dwmmc.h> 17a32c3f92SYifeng Zhao #include <dm/pinctrl.h> 18a32c3f92SYifeng Zhao #include <dm.h> 195743ef64SJason Zhu #ifdef CONFIG_DM_GPIO 205743ef64SJason Zhu #include <asm/gpio.h> 215743ef64SJason Zhu #include <asm-generic/gpio.h> 225743ef64SJason Zhu #endif 23757bff49SJaehoon Chung 24757bff49SJaehoon Chung #define PAGE_SIZE 4096 25bbd0d600SZiyuan Xu #define MSEC_PER_SEC 1000ULL 26757bff49SJaehoon Chung 27bda599f7SShawn Lin /* 28bda599f7SShawn Lin * Currently it supports read/write up to 8*8*4 Bytes per 29bda599f7SShawn Lin * stride as a burst mode. Please note that if you change 30bda599f7SShawn Lin * MAX_STRIDE, you should also update dwmci_memcpy_fromio 31bda599f7SShawn Lin * to augment the groups of {ldm, stm}. 32bda599f7SShawn Lin */ 33bda599f7SShawn Lin #define MAX_STRIDE 64 34699945cbSJason Zhu #if (CONFIG_ARM && CONFIG_CPU_V7 && !defined(CONFIG_MMC_SIMPLE)) 35bda599f7SShawn Lin void noinline dwmci_memcpy_fromio(void *buffer, void *fifo_addr) 36bda599f7SShawn Lin { 37bda599f7SShawn Lin __asm__ __volatile__ ( 38bda599f7SShawn Lin "push {r2, r3, r4, r5, r6, r7, r8, r9}\n" 39bda599f7SShawn Lin "ldm r1, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 40bda599f7SShawn Lin "stm r0!, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 41bda599f7SShawn Lin "ldm r1, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 42bda599f7SShawn Lin "stm r0!, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 43bda599f7SShawn Lin "ldm r1, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 44bda599f7SShawn Lin "stm r0!, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 45bda599f7SShawn Lin "ldm r1, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 46bda599f7SShawn Lin "stm r0!, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 47bda599f7SShawn Lin "ldm r1, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 48bda599f7SShawn Lin "stm r0!, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 49bda599f7SShawn Lin "ldm r1, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 50bda599f7SShawn Lin "stm r0!, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 51bda599f7SShawn Lin "ldm r1, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 52bda599f7SShawn Lin "stm r0!, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 53bda599f7SShawn Lin "ldm r1, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 54bda599f7SShawn Lin "stm r0!, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 55bda599f7SShawn Lin "pop {r2, r3, r4, r5, r6,r7,r8,r9}\n" 56bda599f7SShawn Lin :::"memory" 57bda599f7SShawn Lin ); 58bda599f7SShawn Lin } 59bda599f7SShawn Lin 60bda599f7SShawn Lin void noinline dwmci_memcpy_toio(void *buffer, void *fifo_addr) 61bda599f7SShawn Lin { 6212ee84b1SShawn Lin __asm__ __volatile__ ( 6312ee84b1SShawn Lin "push {r2, r3, r4, r5, r6, r7, r8, r9}\n" 6412ee84b1SShawn Lin "ldm r0!, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 6512ee84b1SShawn Lin "stm r1, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 6612ee84b1SShawn Lin "ldm r0!, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 6712ee84b1SShawn Lin "stm r1, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 6812ee84b1SShawn Lin "ldm r0!, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 6912ee84b1SShawn Lin "stm r1, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 7012ee84b1SShawn Lin "ldm r0!, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 7112ee84b1SShawn Lin "stm r1, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 7212ee84b1SShawn Lin "ldm r0!, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 7312ee84b1SShawn Lin "stm r1, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 7412ee84b1SShawn Lin "ldm r0!, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 7512ee84b1SShawn Lin "stm r1, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 7612ee84b1SShawn Lin "ldm r0!, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 7712ee84b1SShawn Lin "stm r1, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 7812ee84b1SShawn Lin "ldm r0!, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 7912ee84b1SShawn Lin "stm r1, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 8012ee84b1SShawn Lin "pop {r2, r3, r4, r5, r6,r7,r8,r9}\n" 8112ee84b1SShawn Lin :::"memory" 8212ee84b1SShawn Lin ); 83bda599f7SShawn Lin } 84bda599f7SShawn Lin #else 85bda599f7SShawn Lin void dwmci_memcpy_fromio(void *buffer, void *fifo_addr) {}; 86bda599f7SShawn Lin void dwmci_memcpy_toio(void *buffer, void *fifo_addr) {}; 87bda599f7SShawn Lin #endif 88699945cbSJason Zhu 89757bff49SJaehoon Chung static int dwmci_wait_reset(struct dwmci_host *host, u32 value) 90757bff49SJaehoon Chung { 91757bff49SJaehoon Chung unsigned long timeout = 1000; 92757bff49SJaehoon Chung u32 ctrl; 93757bff49SJaehoon Chung 94757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CTRL, value); 95757bff49SJaehoon Chung 96757bff49SJaehoon Chung while (timeout--) { 97757bff49SJaehoon Chung ctrl = dwmci_readl(host, DWMCI_CTRL); 98757bff49SJaehoon Chung if (!(ctrl & DWMCI_RESET_ALL)) 99757bff49SJaehoon Chung return 1; 100757bff49SJaehoon Chung } 101757bff49SJaehoon Chung return 0; 102757bff49SJaehoon Chung } 103757bff49SJaehoon Chung 104757bff49SJaehoon Chung static void dwmci_set_idma_desc(struct dwmci_idmac *idmac, 105757bff49SJaehoon Chung u32 desc0, u32 desc1, u32 desc2) 106757bff49SJaehoon Chung { 107757bff49SJaehoon Chung struct dwmci_idmac *desc = idmac; 108757bff49SJaehoon Chung 109757bff49SJaehoon Chung desc->flags = desc0; 110757bff49SJaehoon Chung desc->cnt = desc1; 111757bff49SJaehoon Chung desc->addr = desc2; 11241f7be3cSPrabhakar Kushwaha desc->next_addr = (ulong)desc + sizeof(struct dwmci_idmac); 113757bff49SJaehoon Chung } 114757bff49SJaehoon Chung 115757bff49SJaehoon Chung static void dwmci_prepare_data(struct dwmci_host *host, 1162a7a210eSAlexey Brodkin struct mmc_data *data, 1172a7a210eSAlexey Brodkin struct dwmci_idmac *cur_idmac, 1182a7a210eSAlexey Brodkin void *bounce_buffer) 119757bff49SJaehoon Chung { 120757bff49SJaehoon Chung unsigned long ctrl; 121757bff49SJaehoon Chung unsigned int i = 0, flags, cnt, blk_cnt; 1222a7a210eSAlexey Brodkin ulong data_start, data_end; 123757bff49SJaehoon Chung 124757bff49SJaehoon Chung 125757bff49SJaehoon Chung blk_cnt = data->blocks; 126757bff49SJaehoon Chung 127757bff49SJaehoon Chung dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET); 128757bff49SJaehoon Chung 129757bff49SJaehoon Chung data_start = (ulong)cur_idmac; 13041f7be3cSPrabhakar Kushwaha dwmci_writel(host, DWMCI_DBADDR, (ulong)cur_idmac); 131757bff49SJaehoon Chung 132757bff49SJaehoon Chung do { 133757bff49SJaehoon Chung flags = DWMCI_IDMAC_OWN | DWMCI_IDMAC_CH ; 134757bff49SJaehoon Chung flags |= (i == 0) ? DWMCI_IDMAC_FS : 0; 135757bff49SJaehoon Chung if (blk_cnt <= 8) { 136757bff49SJaehoon Chung flags |= DWMCI_IDMAC_LD; 137757bff49SJaehoon Chung cnt = data->blocksize * blk_cnt; 138757bff49SJaehoon Chung } else 139757bff49SJaehoon Chung cnt = data->blocksize * 8; 140757bff49SJaehoon Chung 141757bff49SJaehoon Chung dwmci_set_idma_desc(cur_idmac, flags, cnt, 14241f7be3cSPrabhakar Kushwaha (ulong)bounce_buffer + (i * PAGE_SIZE)); 143757bff49SJaehoon Chung 14421bd5761SMischa Jonker if (blk_cnt <= 8) 145757bff49SJaehoon Chung break; 146757bff49SJaehoon Chung blk_cnt -= 8; 147757bff49SJaehoon Chung cur_idmac++; 148757bff49SJaehoon Chung i++; 149757bff49SJaehoon Chung } while(1); 150757bff49SJaehoon Chung 151757bff49SJaehoon Chung data_end = (ulong)cur_idmac; 152757bff49SJaehoon Chung flush_dcache_range(data_start, data_end + ARCH_DMA_MINALIGN); 153757bff49SJaehoon Chung 154757bff49SJaehoon Chung ctrl = dwmci_readl(host, DWMCI_CTRL); 155757bff49SJaehoon Chung ctrl |= DWMCI_IDMAC_EN | DWMCI_DMA_EN; 156757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CTRL, ctrl); 157757bff49SJaehoon Chung 158757bff49SJaehoon Chung ctrl = dwmci_readl(host, DWMCI_BMOD); 159757bff49SJaehoon Chung ctrl |= DWMCI_BMOD_IDMAC_FB | DWMCI_BMOD_IDMAC_EN; 160757bff49SJaehoon Chung dwmci_writel(host, DWMCI_BMOD, ctrl); 161757bff49SJaehoon Chung 162757bff49SJaehoon Chung dwmci_writel(host, DWMCI_BLKSIZ, data->blocksize); 163757bff49SJaehoon Chung dwmci_writel(host, DWMCI_BYTCNT, data->blocksize * data->blocks); 164757bff49SJaehoon Chung } 165757bff49SJaehoon Chung 166bbd0d600SZiyuan Xu #ifdef CONFIG_SPL_BUILD 167bbd0d600SZiyuan Xu static unsigned int dwmci_get_drto(struct dwmci_host *host, 168bbd0d600SZiyuan Xu const unsigned int size) 169bbd0d600SZiyuan Xu { 170bbd0d600SZiyuan Xu unsigned int drto_clks; 171bbd0d600SZiyuan Xu unsigned int drto_div; 172bbd0d600SZiyuan Xu unsigned int drto_ms; 173bbd0d600SZiyuan Xu 174bbd0d600SZiyuan Xu drto_clks = dwmci_readl(host, DWMCI_TMOUT) >> 8; 175bbd0d600SZiyuan Xu drto_div = (dwmci_readl(host, DWMCI_CLKDIV) & 0xff) * 2; 176bbd0d600SZiyuan Xu if (drto_div == 0) 177bbd0d600SZiyuan Xu drto_div = 1; 178bbd0d600SZiyuan Xu 179bbd0d600SZiyuan Xu drto_ms = DIV_ROUND_UP_ULL((u64)MSEC_PER_SEC * drto_clks * drto_div, 180bbd0d600SZiyuan Xu host->mmc->clock); 181bbd0d600SZiyuan Xu 182bbd0d600SZiyuan Xu /* add a bit spare time */ 1830953e389SYifeng Zhao drto_ms += 50; 184bbd0d600SZiyuan Xu 185bbd0d600SZiyuan Xu return drto_ms; 186bbd0d600SZiyuan Xu } 187bbd0d600SZiyuan Xu #else 188bbd0d600SZiyuan Xu static unsigned int dwmci_get_drto(struct dwmci_host *host, 189bbd0d600SZiyuan Xu const unsigned int size) 19034d21c9aSJason Zhu { 19134d21c9aSJason Zhu unsigned int timeout; 19234d21c9aSJason Zhu 19334d21c9aSJason Zhu timeout = size * 8; /* counting in bits */ 19434d21c9aSJason Zhu timeout *= 10; /* wait 10 times as long */ 195bbd0d600SZiyuan Xu timeout /= host->mmc->clock; 196bbd0d600SZiyuan Xu timeout /= host->mmc->bus_width; 19734d21c9aSJason Zhu timeout *= 1000; /* counting in msec */ 19834d21c9aSJason Zhu timeout = (timeout < 10000) ? 10000 : timeout; 19934d21c9aSJason Zhu 20034d21c9aSJason Zhu return timeout; 20134d21c9aSJason Zhu } 202bbd0d600SZiyuan Xu #endif 203bbd0d600SZiyuan Xu 204bbd0d600SZiyuan Xu static unsigned int dwmci_get_cto(struct dwmci_host *host) 205bbd0d600SZiyuan Xu { 206bbd0d600SZiyuan Xu unsigned int cto_clks; 207bbd0d600SZiyuan Xu unsigned int cto_div; 208bbd0d600SZiyuan Xu unsigned int cto_ms; 209bbd0d600SZiyuan Xu 210bbd0d600SZiyuan Xu cto_clks = dwmci_readl(host, DWMCI_TMOUT) & 0xff; 211bbd0d600SZiyuan Xu cto_div = (dwmci_readl(host, DWMCI_CLKDIV) & 0xff) * 2; 212bbd0d600SZiyuan Xu if (cto_div == 0) 213bbd0d600SZiyuan Xu cto_div = 1; 214bbd0d600SZiyuan Xu 215bbd0d600SZiyuan Xu cto_ms = DIV_ROUND_UP_ULL((u64)MSEC_PER_SEC * cto_clks * cto_div, 216bbd0d600SZiyuan Xu host->mmc->clock); 217bbd0d600SZiyuan Xu 218bbd0d600SZiyuan Xu /* add a bit spare time */ 219bbd0d600SZiyuan Xu cto_ms += 10; 220bbd0d600SZiyuan Xu 221bbd0d600SZiyuan Xu return cto_ms; 222bbd0d600SZiyuan Xu } 22334d21c9aSJason Zhu 224a65f51b9Shuang lin static int dwmci_data_transfer(struct dwmci_host *host, struct mmc_data *data) 225f382eb83Shuang lin { 226f382eb83Shuang lin int ret = 0; 227f7c0370cSJason Zhu int reset_timeout = 100; 22834d21c9aSJason Zhu u32 timeout, status, ctrl, mask, size, i, len = 0; 229a65f51b9Shuang lin u32 *buf = NULL; 230f382eb83Shuang lin ulong start = get_timer(0); 231a65f51b9Shuang lin u32 fifo_depth = (((host->fifoth_val & RX_WMARK_MASK) >> 232a65f51b9Shuang lin RX_WMARK_SHIFT) + 1) * 2; 233bda599f7SShawn Lin bool stride; 234a65f51b9Shuang lin 23534d21c9aSJason Zhu size = data->blocksize * data->blocks; 236bda599f7SShawn Lin /* Still use legacy PIO mode if size < 512(128 * 4) Bytes */ 237bda599f7SShawn Lin stride = host->stride_pio && size > 128; 238a65f51b9Shuang lin if (data->flags == MMC_DATA_READ) 239a65f51b9Shuang lin buf = (unsigned int *)data->dest; 240a65f51b9Shuang lin else 241a65f51b9Shuang lin buf = (unsigned int *)data->src; 242f382eb83Shuang lin 243bbd0d600SZiyuan Xu timeout = dwmci_get_drto(host, size); 244315c0aefSYifeng Zhao /* The tuning data is 128bytes, a timeout of 1ms is sufficient.*/ 245315c0aefSYifeng Zhao if ((dwmci_readl(host, DWMCI_CMD) & 0x1F) == MMC_SEND_TUNING_BLOCK_HS200) 246315c0aefSYifeng Zhao timeout = 1; 247315c0aefSYifeng Zhao 24834d21c9aSJason Zhu size /= 4; 24934d21c9aSJason Zhu 250f382eb83Shuang lin for (;;) { 251f382eb83Shuang lin mask = dwmci_readl(host, DWMCI_RINTSTS); 252f382eb83Shuang lin /* Error during data transfer. */ 253f382eb83Shuang lin if (mask & (DWMCI_DATA_ERR | DWMCI_DATA_TOUT)) { 254f382eb83Shuang lin debug("%s: DATA ERROR!\n", __func__); 255a0f322f5SYifeng Zhao /* 256a0f322f5SYifeng Zhao * It is necessary to wait for several cycles before 257a0f322f5SYifeng Zhao * resetting the controller while data timeout or error. 258a0f322f5SYifeng Zhao */ 259a0f322f5SYifeng Zhao udelay(1); 2600d797f18SZiyuan Xu dwmci_wait_reset(host, DWMCI_RESET_ALL); 2610d797f18SZiyuan Xu dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT | 2620d797f18SZiyuan Xu DWMCI_CMD_UPD_CLK | DWMCI_CMD_START); 2630d797f18SZiyuan Xu 2640d797f18SZiyuan Xu do { 2650d797f18SZiyuan Xu status = dwmci_readl(host, DWMCI_CMD); 266f7c0370cSJason Zhu if (reset_timeout-- < 0) 267f7c0370cSJason Zhu break; 268f7c0370cSJason Zhu udelay(100); 2690d797f18SZiyuan Xu } while (status & DWMCI_CMD_START); 2700d797f18SZiyuan Xu 2710d797f18SZiyuan Xu if (!host->fifo_mode) { 2720d797f18SZiyuan Xu ctrl = dwmci_readl(host, DWMCI_BMOD); 2730d797f18SZiyuan Xu ctrl |= DWMCI_BMOD_IDMAC_RESET; 2740d797f18SZiyuan Xu dwmci_writel(host, DWMCI_BMOD, ctrl); 2750d797f18SZiyuan Xu } 2760d797f18SZiyuan Xu 277f382eb83Shuang lin ret = -EINVAL; 278f382eb83Shuang lin break; 279f382eb83Shuang lin } 280f382eb83Shuang lin 281a65f51b9Shuang lin if (host->fifo_mode && size) { 282720724d0SXu Ziyuan len = 0; 2832b429033SJacob Chen if (data->flags == MMC_DATA_READ && 2841aaee36eSShawn Lin (mask & (DWMCI_INTMSK_RXDR | DWMCI_INTMSK_DTO))) { 2852b429033SJacob Chen while (size) { 286a65f51b9Shuang lin len = dwmci_readl(host, DWMCI_STATUS); 287a65f51b9Shuang lin len = (len >> DWMCI_FIFO_SHIFT) & 288a65f51b9Shuang lin DWMCI_FIFO_MASK; 2892990e07aSXu Ziyuan len = min(size, len); 290bda599f7SShawn Lin if (!stride) { 291bda599f7SShawn Lin /* Legacy pio mode */ 292a65f51b9Shuang lin for (i = 0; i < len; i++) 293bda599f7SShawn Lin *buf++ = dwmci_readl(host, DWMCI_DATA); 294bda599f7SShawn Lin goto read_again; 295bda599f7SShawn Lin } 296bda599f7SShawn Lin 297bda599f7SShawn Lin /* dwmci_memcpy_fromio now bursts 256 Bytes once */ 298bda599f7SShawn Lin if (len < MAX_STRIDE) 299bda599f7SShawn Lin continue; 300bda599f7SShawn Lin 301bda599f7SShawn Lin for (i = 0; i < len / MAX_STRIDE; i++) { 302bda599f7SShawn Lin dwmci_memcpy_fromio(buf, host->ioaddr + DWMCI_DATA); 303bda599f7SShawn Lin buf += MAX_STRIDE; 304bda599f7SShawn Lin } 305bda599f7SShawn Lin 306bda599f7SShawn Lin len = i * MAX_STRIDE; 307bda599f7SShawn Lin read_again: 3082b429033SJacob Chen size = size > len ? (size - len) : 0; 3092b429033SJacob Chen } 310*d8db857fSShawn Lin 311*d8db857fSShawn Lin dwmci_writel(host, DWMCI_RINTSTS, 312*d8db857fSShawn Lin mask & (DWMCI_INTMSK_RXDR | DWMCI_INTMSK_DTO)); 313bbd0d600SZiyuan Xu start = get_timer(0); 3142b429033SJacob Chen } else if (data->flags == MMC_DATA_WRITE && 3152b429033SJacob Chen (mask & DWMCI_INTMSK_TXDR)) { 3162b429033SJacob Chen while (size) { 317a65f51b9Shuang lin len = dwmci_readl(host, DWMCI_STATUS); 318a65f51b9Shuang lin len = fifo_depth - ((len >> 319a65f51b9Shuang lin DWMCI_FIFO_SHIFT) & 320a65f51b9Shuang lin DWMCI_FIFO_MASK); 3212990e07aSXu Ziyuan len = min(size, len); 322bda599f7SShawn Lin if (!stride) { 323a65f51b9Shuang lin for (i = 0; i < len; i++) 324a65f51b9Shuang lin dwmci_writel(host, DWMCI_DATA, 325a65f51b9Shuang lin *buf++); 326bda599f7SShawn Lin goto write_again; 327bda599f7SShawn Lin } 328bda599f7SShawn Lin /* dwmci_memcpy_toio now bursts 256 Bytes once */ 329bda599f7SShawn Lin if (len < MAX_STRIDE) 330bda599f7SShawn Lin continue; 331bda599f7SShawn Lin 332bda599f7SShawn Lin for (i = 0; i < len / MAX_STRIDE; i++) { 333bda599f7SShawn Lin dwmci_memcpy_toio(buf, host->ioaddr + DWMCI_DATA); 334bda599f7SShawn Lin buf += MAX_STRIDE; 335bda599f7SShawn Lin } 336bda599f7SShawn Lin 337bda599f7SShawn Lin len = i * MAX_STRIDE; 338bda599f7SShawn Lin write_again: 3392b429033SJacob Chen size = size > len ? (size - len) : 0; 3402b429033SJacob Chen } 341a65f51b9Shuang lin dwmci_writel(host, DWMCI_RINTSTS, 342a65f51b9Shuang lin DWMCI_INTMSK_TXDR); 343bbd0d600SZiyuan Xu start = get_timer(0); 344a65f51b9Shuang lin } 345a65f51b9Shuang lin } 346a65f51b9Shuang lin 347f382eb83Shuang lin /* Data arrived correctly. */ 348f382eb83Shuang lin if (mask & DWMCI_INTMSK_DTO) { 349f382eb83Shuang lin ret = 0; 350f382eb83Shuang lin break; 351f382eb83Shuang lin } 352f382eb83Shuang lin 353f382eb83Shuang lin /* Check for timeout. */ 354f382eb83Shuang lin if (get_timer(start) > timeout) { 355f382eb83Shuang lin debug("%s: Timeout waiting for data!\n", 356f382eb83Shuang lin __func__); 357915ffa52SJaehoon Chung ret = -ETIMEDOUT; 358f382eb83Shuang lin break; 359f382eb83Shuang lin } 360f382eb83Shuang lin } 361f382eb83Shuang lin 362f382eb83Shuang lin dwmci_writel(host, DWMCI_RINTSTS, mask); 363f382eb83Shuang lin 364f382eb83Shuang lin return ret; 365f382eb83Shuang lin } 366f382eb83Shuang lin 367757bff49SJaehoon Chung static int dwmci_set_transfer_mode(struct dwmci_host *host, 368757bff49SJaehoon Chung struct mmc_data *data) 369757bff49SJaehoon Chung { 370757bff49SJaehoon Chung unsigned long mode; 371757bff49SJaehoon Chung 372757bff49SJaehoon Chung mode = DWMCI_CMD_DATA_EXP; 373757bff49SJaehoon Chung if (data->flags & MMC_DATA_WRITE) 374757bff49SJaehoon Chung mode |= DWMCI_CMD_RW; 375757bff49SJaehoon Chung 376757bff49SJaehoon Chung return mode; 377757bff49SJaehoon Chung } 378757bff49SJaehoon Chung 379e7881d85SSimon Glass #ifdef CONFIG_DM_MMC 3805628347fSJaehoon Chung static int dwmci_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, 381691272feSSimon Glass struct mmc_data *data) 382691272feSSimon Glass { 383691272feSSimon Glass struct mmc *mmc = mmc_get_mmc_dev(dev); 384691272feSSimon Glass #else 385757bff49SJaehoon Chung static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, 386757bff49SJaehoon Chung struct mmc_data *data) 387757bff49SJaehoon Chung { 388691272feSSimon Glass #endif 38993bfd616SPantelis Antoniou struct dwmci_host *host = mmc->priv; 3902136d226SMischa Jonker ALLOC_CACHE_ALIGN_BUFFER(struct dwmci_idmac, cur_idmac, 39121bd5761SMischa Jonker data ? DIV_ROUND_UP(data->blocks, 8) : 0); 392bbd0d600SZiyuan Xu int ret = 0, flags = 0; 39302ebd42cSXu Ziyuan unsigned int timeout = 500; 394757bff49SJaehoon Chung u32 mask, ctrl; 3959c50e35fSAmar ulong start = get_timer(0); 3962a7a210eSAlexey Brodkin struct bounce_buffer bbstate; 397757bff49SJaehoon Chung 398757bff49SJaehoon Chung while (dwmci_readl(host, DWMCI_STATUS) & DWMCI_BUSY) { 3999c50e35fSAmar if (get_timer(start) > timeout) { 4001c87ffe8SSimon Glass debug("%s: Timeout on data busy\n", __func__); 401915ffa52SJaehoon Chung return -ETIMEDOUT; 402757bff49SJaehoon Chung } 403757bff49SJaehoon Chung } 404757bff49SJaehoon Chung 405757bff49SJaehoon Chung dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_ALL); 406757bff49SJaehoon Chung 4072a7a210eSAlexey Brodkin if (data) { 408a65f51b9Shuang lin if (host->fifo_mode) { 409a65f51b9Shuang lin dwmci_writel(host, DWMCI_BLKSIZ, data->blocksize); 410a65f51b9Shuang lin dwmci_writel(host, DWMCI_BYTCNT, 411a65f51b9Shuang lin data->blocksize * data->blocks); 412a65f51b9Shuang lin dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET); 413a65f51b9Shuang lin } else { 4142a7a210eSAlexey Brodkin if (data->flags == MMC_DATA_READ) { 4158cdfda89SMarek Vasut ret = bounce_buffer_start(&bbstate, 4168cdfda89SMarek Vasut (void*)data->dest, 4172a7a210eSAlexey Brodkin data->blocksize * 4182a7a210eSAlexey Brodkin data->blocks, GEN_BB_WRITE); 4192a7a210eSAlexey Brodkin } else { 4208cdfda89SMarek Vasut ret = bounce_buffer_start(&bbstate, 4218cdfda89SMarek Vasut (void*)data->src, 4222a7a210eSAlexey Brodkin data->blocksize * 4232a7a210eSAlexey Brodkin data->blocks, GEN_BB_READ); 4242a7a210eSAlexey Brodkin } 4258cdfda89SMarek Vasut 4268cdfda89SMarek Vasut if (ret) 4278cdfda89SMarek Vasut return ret; 4288cdfda89SMarek Vasut 4292a7a210eSAlexey Brodkin dwmci_prepare_data(host, data, cur_idmac, 4302a7a210eSAlexey Brodkin bbstate.bounce_buffer); 4312a7a210eSAlexey Brodkin } 432a65f51b9Shuang lin } 433757bff49SJaehoon Chung 434757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CMDARG, cmd->cmdarg); 435757bff49SJaehoon Chung 436757bff49SJaehoon Chung if (data) 437757bff49SJaehoon Chung flags = dwmci_set_transfer_mode(host, data); 438757bff49SJaehoon Chung 439757bff49SJaehoon Chung if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) 440757bff49SJaehoon Chung return -1; 441757bff49SJaehoon Chung 442757bff49SJaehoon Chung if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) 443757bff49SJaehoon Chung flags |= DWMCI_CMD_ABORT_STOP; 4445135be73SYifeng Zhao else if (cmd->cmdidx == MMC_CMD_GO_IDLE_STATE) 4455135be73SYifeng Zhao flags |= SDMMC_CMD_INIT | DWMCI_CMD_ABORT_STOP; 446757bff49SJaehoon Chung else 447757bff49SJaehoon Chung flags |= DWMCI_CMD_PRV_DAT_WAIT; 448757bff49SJaehoon Chung 449757bff49SJaehoon Chung if (cmd->resp_type & MMC_RSP_PRESENT) { 450757bff49SJaehoon Chung flags |= DWMCI_CMD_RESP_EXP; 451757bff49SJaehoon Chung if (cmd->resp_type & MMC_RSP_136) 452757bff49SJaehoon Chung flags |= DWMCI_CMD_RESP_LENGTH; 453757bff49SJaehoon Chung } 454757bff49SJaehoon Chung 455757bff49SJaehoon Chung if (cmd->resp_type & MMC_RSP_CRC) 456757bff49SJaehoon Chung flags |= DWMCI_CMD_CHECK_CRC; 457757bff49SJaehoon Chung 458757bff49SJaehoon Chung flags |= (cmd->cmdidx | DWMCI_CMD_START | DWMCI_CMD_USE_HOLD_REG); 459757bff49SJaehoon Chung 460757bff49SJaehoon Chung debug("Sending CMD%d\n",cmd->cmdidx); 461757bff49SJaehoon Chung 462757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CMD, flags); 463757bff49SJaehoon Chung 464bbd0d600SZiyuan Xu timeout = dwmci_get_cto(host); 465bbd0d600SZiyuan Xu start = get_timer(0); 466bbd0d600SZiyuan Xu do { 467757bff49SJaehoon Chung mask = dwmci_readl(host, DWMCI_RINTSTS); 468757bff49SJaehoon Chung if (mask & DWMCI_INTMSK_CDONE) { 469757bff49SJaehoon Chung if (!data) 470757bff49SJaehoon Chung dwmci_writel(host, DWMCI_RINTSTS, mask); 471757bff49SJaehoon Chung break; 472757bff49SJaehoon Chung } 473bbd0d600SZiyuan Xu } while (!(get_timer(start) > timeout)); 474757bff49SJaehoon Chung 475bbd0d600SZiyuan Xu if (get_timer(start) > timeout) { 4761c87ffe8SSimon Glass debug("%s: Timeout.\n", __func__); 477915ffa52SJaehoon Chung return -ETIMEDOUT; 478f33c9305SPavel Machek } 479757bff49SJaehoon Chung 480757bff49SJaehoon Chung if (mask & DWMCI_INTMSK_RTO) { 481f33c9305SPavel Machek /* 482f33c9305SPavel Machek * Timeout here is not necessarily fatal. (e)MMC cards 483f33c9305SPavel Machek * will splat here when they receive CMD55 as they do 484f33c9305SPavel Machek * not support this command and that is exactly the way 485f33c9305SPavel Machek * to tell them apart from SD cards. Thus, this output 486f33c9305SPavel Machek * below shall be debug(). eMMC cards also do not favor 487f33c9305SPavel Machek * CMD8, please keep that in mind. 488f33c9305SPavel Machek */ 489f33c9305SPavel Machek debug("%s: Response Timeout.\n", __func__); 490915ffa52SJaehoon Chung return -ETIMEDOUT; 491757bff49SJaehoon Chung } else if (mask & DWMCI_INTMSK_RE) { 4921c87ffe8SSimon Glass debug("%s: Response Error.\n", __func__); 4931c87ffe8SSimon Glass return -EIO; 494757bff49SJaehoon Chung } 495757bff49SJaehoon Chung 496757bff49SJaehoon Chung 497757bff49SJaehoon Chung if (cmd->resp_type & MMC_RSP_PRESENT) { 498757bff49SJaehoon Chung if (cmd->resp_type & MMC_RSP_136) { 499757bff49SJaehoon Chung cmd->response[0] = dwmci_readl(host, DWMCI_RESP3); 500757bff49SJaehoon Chung cmd->response[1] = dwmci_readl(host, DWMCI_RESP2); 501757bff49SJaehoon Chung cmd->response[2] = dwmci_readl(host, DWMCI_RESP1); 502757bff49SJaehoon Chung cmd->response[3] = dwmci_readl(host, DWMCI_RESP0); 503757bff49SJaehoon Chung } else { 504757bff49SJaehoon Chung cmd->response[0] = dwmci_readl(host, DWMCI_RESP0); 505757bff49SJaehoon Chung } 506757bff49SJaehoon Chung } 507757bff49SJaehoon Chung 508757bff49SJaehoon Chung if (data) { 509a65f51b9Shuang lin ret = dwmci_data_transfer(host, data); 510757bff49SJaehoon Chung 511a65f51b9Shuang lin /* only dma mode need it */ 512a65f51b9Shuang lin if (!host->fifo_mode) { 513757bff49SJaehoon Chung ctrl = dwmci_readl(host, DWMCI_CTRL); 514757bff49SJaehoon Chung ctrl &= ~(DWMCI_DMA_EN); 515757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CTRL, ctrl); 5162a7a210eSAlexey Brodkin bounce_buffer_stop(&bbstate); 517757bff49SJaehoon Chung } 518a65f51b9Shuang lin } 519757bff49SJaehoon Chung 5209042d974SMarek Vasut return ret; 521757bff49SJaehoon Chung } 522757bff49SJaehoon Chung 52347f7fd3aSJason Zhu #ifdef CONFIG_SPL_BLK_READ_PREPARE 52447f7fd3aSJason Zhu #ifdef CONFIG_DM_MMC 52547f7fd3aSJason Zhu static int dwmci_send_cmd_prepare(struct udevice *dev, struct mmc_cmd *cmd, 52647f7fd3aSJason Zhu struct mmc_data *data) 52747f7fd3aSJason Zhu { 52847f7fd3aSJason Zhu struct mmc *mmc = mmc_get_mmc_dev(dev); 52947f7fd3aSJason Zhu #else 53047f7fd3aSJason Zhu static int dwmci_send_cmd_prepare(struct mmc *mmc, struct mmc_cmd *cmd, 53147f7fd3aSJason Zhu struct mmc_data *data) 53247f7fd3aSJason Zhu { 53347f7fd3aSJason Zhu #endif 53447f7fd3aSJason Zhu struct dwmci_host *host = mmc->priv; 53547f7fd3aSJason Zhu struct dwmci_idmac *cur_idmac; 536bbd0d600SZiyuan Xu int ret = 0, flags = 0; 53747f7fd3aSJason Zhu unsigned int timeout = 500; 53847f7fd3aSJason Zhu u32 mask; 53947f7fd3aSJason Zhu ulong start = get_timer(0); 54047f7fd3aSJason Zhu struct bounce_buffer bbstate; 54147f7fd3aSJason Zhu 54247f7fd3aSJason Zhu cur_idmac = malloc(ROUND(DIV_ROUND_UP(data->blocks, 8) * 54347f7fd3aSJason Zhu sizeof(struct dwmci_idmac), 54447f7fd3aSJason Zhu ARCH_DMA_MINALIGN) + ARCH_DMA_MINALIGN - 1); 54547f7fd3aSJason Zhu if (!cur_idmac) 54647f7fd3aSJason Zhu return -ENODATA; 54747f7fd3aSJason Zhu 54847f7fd3aSJason Zhu while (dwmci_readl(host, DWMCI_STATUS) & DWMCI_BUSY) { 54947f7fd3aSJason Zhu if (get_timer(start) > timeout) { 55047f7fd3aSJason Zhu debug("%s: Timeout on data busy\n", __func__); 55147f7fd3aSJason Zhu return -ETIMEDOUT; 55247f7fd3aSJason Zhu } 55347f7fd3aSJason Zhu } 55447f7fd3aSJason Zhu 55547f7fd3aSJason Zhu dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_ALL); 55647f7fd3aSJason Zhu 55747f7fd3aSJason Zhu if (data) { 55847f7fd3aSJason Zhu if (host->fifo_mode) { 55947f7fd3aSJason Zhu dwmci_writel(host, DWMCI_BLKSIZ, data->blocksize); 56047f7fd3aSJason Zhu dwmci_writel(host, DWMCI_BYTCNT, 56147f7fd3aSJason Zhu data->blocksize * data->blocks); 56247f7fd3aSJason Zhu dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET); 56347f7fd3aSJason Zhu } else { 56447f7fd3aSJason Zhu if (data->flags == MMC_DATA_READ) { 56547f7fd3aSJason Zhu bounce_buffer_start(&bbstate, (void *)data->dest, 56647f7fd3aSJason Zhu data->blocksize * 56747f7fd3aSJason Zhu data->blocks, GEN_BB_WRITE); 56847f7fd3aSJason Zhu } else { 56947f7fd3aSJason Zhu bounce_buffer_start(&bbstate, (void *)data->src, 57047f7fd3aSJason Zhu data->blocksize * 57147f7fd3aSJason Zhu data->blocks, GEN_BB_READ); 57247f7fd3aSJason Zhu } 57347f7fd3aSJason Zhu dwmci_prepare_data(host, data, cur_idmac, 57447f7fd3aSJason Zhu bbstate.bounce_buffer); 57547f7fd3aSJason Zhu } 57647f7fd3aSJason Zhu } 57747f7fd3aSJason Zhu 57847f7fd3aSJason Zhu dwmci_writel(host, DWMCI_CMDARG, cmd->cmdarg); 57947f7fd3aSJason Zhu 58047f7fd3aSJason Zhu if (data) 58147f7fd3aSJason Zhu flags = dwmci_set_transfer_mode(host, data); 58247f7fd3aSJason Zhu 58347f7fd3aSJason Zhu if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) 58447f7fd3aSJason Zhu return -1; 58547f7fd3aSJason Zhu 58647f7fd3aSJason Zhu if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) 58747f7fd3aSJason Zhu flags |= DWMCI_CMD_ABORT_STOP; 58847f7fd3aSJason Zhu else 58947f7fd3aSJason Zhu flags |= DWMCI_CMD_PRV_DAT_WAIT; 59047f7fd3aSJason Zhu 59147f7fd3aSJason Zhu if (cmd->resp_type & MMC_RSP_PRESENT) { 59247f7fd3aSJason Zhu flags |= DWMCI_CMD_RESP_EXP; 59347f7fd3aSJason Zhu if (cmd->resp_type & MMC_RSP_136) 59447f7fd3aSJason Zhu flags |= DWMCI_CMD_RESP_LENGTH; 59547f7fd3aSJason Zhu } 59647f7fd3aSJason Zhu 59747f7fd3aSJason Zhu if (cmd->resp_type & MMC_RSP_CRC) 59847f7fd3aSJason Zhu flags |= DWMCI_CMD_CHECK_CRC; 59947f7fd3aSJason Zhu 60047f7fd3aSJason Zhu flags |= (cmd->cmdidx | DWMCI_CMD_START | DWMCI_CMD_USE_HOLD_REG); 60147f7fd3aSJason Zhu 60247f7fd3aSJason Zhu debug("Sending CMD%d\n", cmd->cmdidx); 60347f7fd3aSJason Zhu 60447f7fd3aSJason Zhu dwmci_writel(host, DWMCI_CMD, flags); 60547f7fd3aSJason Zhu 606bbd0d600SZiyuan Xu timeout = dwmci_get_cto(host); 607bbd0d600SZiyuan Xu start = get_timer(0); 608bbd0d600SZiyuan Xu do { 60947f7fd3aSJason Zhu mask = dwmci_readl(host, DWMCI_RINTSTS); 61047f7fd3aSJason Zhu if (mask & DWMCI_INTMSK_CDONE) { 61147f7fd3aSJason Zhu if (!data) 61247f7fd3aSJason Zhu dwmci_writel(host, DWMCI_RINTSTS, mask); 61347f7fd3aSJason Zhu break; 61447f7fd3aSJason Zhu } 615bbd0d600SZiyuan Xu } while (!(get_timer(start) > timeout)); 61647f7fd3aSJason Zhu 617bbd0d600SZiyuan Xu if (get_timer(start) > timeout) { 61847f7fd3aSJason Zhu debug("%s: Timeout.\n", __func__); 61947f7fd3aSJason Zhu return -ETIMEDOUT; 62047f7fd3aSJason Zhu } 62147f7fd3aSJason Zhu 62247f7fd3aSJason Zhu if (mask & DWMCI_INTMSK_RTO) { 62347f7fd3aSJason Zhu /* 62447f7fd3aSJason Zhu * Timeout here is not necessarily fatal. (e)MMC cards 62547f7fd3aSJason Zhu * will splat here when they receive CMD55 as they do 62647f7fd3aSJason Zhu * not support this command and that is exactly the way 62747f7fd3aSJason Zhu * to tell them apart from SD cards. Thus, this output 62847f7fd3aSJason Zhu * below shall be debug(). eMMC cards also do not favor 62947f7fd3aSJason Zhu * CMD8, please keep that in mind. 63047f7fd3aSJason Zhu */ 63147f7fd3aSJason Zhu debug("%s: Response Timeout.\n", __func__); 63247f7fd3aSJason Zhu return -ETIMEDOUT; 63347f7fd3aSJason Zhu } else if (mask & DWMCI_INTMSK_RE) { 63447f7fd3aSJason Zhu debug("%s: Response Error.\n", __func__); 63547f7fd3aSJason Zhu return -EIO; 63647f7fd3aSJason Zhu } 63747f7fd3aSJason Zhu 63847f7fd3aSJason Zhu if (cmd->resp_type & MMC_RSP_PRESENT) { 63947f7fd3aSJason Zhu if (cmd->resp_type & MMC_RSP_136) { 64047f7fd3aSJason Zhu cmd->response[0] = dwmci_readl(host, DWMCI_RESP3); 64147f7fd3aSJason Zhu cmd->response[1] = dwmci_readl(host, DWMCI_RESP2); 64247f7fd3aSJason Zhu cmd->response[2] = dwmci_readl(host, DWMCI_RESP1); 64347f7fd3aSJason Zhu cmd->response[3] = dwmci_readl(host, DWMCI_RESP0); 64447f7fd3aSJason Zhu } else { 64547f7fd3aSJason Zhu cmd->response[0] = dwmci_readl(host, DWMCI_RESP0); 64647f7fd3aSJason Zhu } 64747f7fd3aSJason Zhu } 64847f7fd3aSJason Zhu 64947f7fd3aSJason Zhu return ret; 65047f7fd3aSJason Zhu } 65147f7fd3aSJason Zhu #endif 65247f7fd3aSJason Zhu 653757bff49SJaehoon Chung static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) 654757bff49SJaehoon Chung { 655757bff49SJaehoon Chung u32 div, status; 656757bff49SJaehoon Chung int timeout = 10000; 657757bff49SJaehoon Chung unsigned long sclk; 658757bff49SJaehoon Chung 65924527ef9SZiyuan Xu if (freq == 0) 660757bff49SJaehoon Chung return 0; 661757bff49SJaehoon Chung /* 662f33c9305SPavel Machek * If host->get_mmc_clk isn't defined, 663757bff49SJaehoon Chung * then assume that host->bus_hz is source clock value. 664f33c9305SPavel Machek * host->bus_hz should be set by user. 665757bff49SJaehoon Chung */ 666b44fe83aSJaehoon Chung if (host->get_mmc_clk) 667e3563f2eSSimon Glass sclk = host->get_mmc_clk(host, freq); 668757bff49SJaehoon Chung else if (host->bus_hz) 669757bff49SJaehoon Chung sclk = host->bus_hz; 670757bff49SJaehoon Chung else { 6711c87ffe8SSimon Glass debug("%s: Didn't get source clock value.\n", __func__); 672757bff49SJaehoon Chung return -EINVAL; 673757bff49SJaehoon Chung } 674757bff49SJaehoon Chung 675ddeaf211SJason Zhu if (sclk == 0) 676ddeaf211SJason Zhu return -EINVAL; 677ddeaf211SJason Zhu 6786ace153dSChin Liang See if (sclk == freq) 6796ace153dSChin Liang See div = 0; /* bypass mode */ 6806ace153dSChin Liang See else 681757bff49SJaehoon Chung div = DIV_ROUND_UP(sclk, 2 * freq); 682757bff49SJaehoon Chung 683757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CLKENA, 0); 684757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CLKSRC, 0); 685757bff49SJaehoon Chung 686757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CLKDIV, div); 687757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT | 688757bff49SJaehoon Chung DWMCI_CMD_UPD_CLK | DWMCI_CMD_START); 689757bff49SJaehoon Chung 690757bff49SJaehoon Chung do { 691757bff49SJaehoon Chung status = dwmci_readl(host, DWMCI_CMD); 692757bff49SJaehoon Chung if (timeout-- < 0) { 6931c87ffe8SSimon Glass debug("%s: Timeout!\n", __func__); 694757bff49SJaehoon Chung return -ETIMEDOUT; 695757bff49SJaehoon Chung } 696757bff49SJaehoon Chung } while (status & DWMCI_CMD_START); 697757bff49SJaehoon Chung 698757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CLKENA, DWMCI_CLKEN_ENABLE | 699757bff49SJaehoon Chung DWMCI_CLKEN_LOW_PWR); 700757bff49SJaehoon Chung 701757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT | 702757bff49SJaehoon Chung DWMCI_CMD_UPD_CLK | DWMCI_CMD_START); 703757bff49SJaehoon Chung 704757bff49SJaehoon Chung timeout = 10000; 705757bff49SJaehoon Chung do { 706757bff49SJaehoon Chung status = dwmci_readl(host, DWMCI_CMD); 707757bff49SJaehoon Chung if (timeout-- < 0) { 7081c87ffe8SSimon Glass debug("%s: Timeout!\n", __func__); 709757bff49SJaehoon Chung return -ETIMEDOUT; 710757bff49SJaehoon Chung } 711757bff49SJaehoon Chung } while (status & DWMCI_CMD_START); 712757bff49SJaehoon Chung 713757bff49SJaehoon Chung host->clock = freq; 714757bff49SJaehoon Chung 715757bff49SJaehoon Chung return 0; 716757bff49SJaehoon Chung } 717757bff49SJaehoon Chung 718e7881d85SSimon Glass #ifdef CONFIG_DM_MMC 719ba0e56e1SZiyuan Xu static bool dwmci_card_busy(struct udevice *dev) 720ba0e56e1SZiyuan Xu { 721ba0e56e1SZiyuan Xu struct mmc *mmc = mmc_get_mmc_dev(dev); 722ba0e56e1SZiyuan Xu #else 723ba0e56e1SZiyuan Xu static bool dwmci_card_busy(struct mmc *mmc) 724ba0e56e1SZiyuan Xu { 725ba0e56e1SZiyuan Xu #endif 726ba0e56e1SZiyuan Xu u32 status; 727ba0e56e1SZiyuan Xu struct dwmci_host *host = (struct dwmci_host *)mmc->priv; 728ba0e56e1SZiyuan Xu 729ba0e56e1SZiyuan Xu /* 730ba0e56e1SZiyuan Xu * Check the busy bit which is low when DAT[3:0] 731ba0e56e1SZiyuan Xu * (the data lines) are 0000 732ba0e56e1SZiyuan Xu */ 733ba0e56e1SZiyuan Xu status = dwmci_readl(host, DWMCI_STATUS); 734ba0e56e1SZiyuan Xu 735ba0e56e1SZiyuan Xu return !!(status & DWMCI_BUSY); 736ba0e56e1SZiyuan Xu } 737ba0e56e1SZiyuan Xu 738ba0e56e1SZiyuan Xu #ifdef CONFIG_DM_MMC 7398c921dceSZiyuan Xu static int dwmci_execute_tuning(struct udevice *dev, u32 opcode) 7408c921dceSZiyuan Xu { 7418c921dceSZiyuan Xu struct mmc *mmc = mmc_get_mmc_dev(dev); 7428c921dceSZiyuan Xu #else 7438c921dceSZiyuan Xu static int dwmci_execute_tuning(struct mmc *mmc, u32 opcode) 7448c921dceSZiyuan Xu { 7458c921dceSZiyuan Xu #endif 7468c921dceSZiyuan Xu struct dwmci_host *host = (struct dwmci_host *)mmc->priv; 7478c921dceSZiyuan Xu 7488c921dceSZiyuan Xu if (!host->execute_tuning) 7498c921dceSZiyuan Xu return -EIO; 7508c921dceSZiyuan Xu 7518c921dceSZiyuan Xu return host->execute_tuning(host, opcode); 7528c921dceSZiyuan Xu } 7538c921dceSZiyuan Xu 7548c921dceSZiyuan Xu #ifdef CONFIG_DM_MMC 7555628347fSJaehoon Chung static int dwmci_set_ios(struct udevice *dev) 756691272feSSimon Glass { 757691272feSSimon Glass struct mmc *mmc = mmc_get_mmc_dev(dev); 758691272feSSimon Glass #else 75907b0b9c0SJaehoon Chung static int dwmci_set_ios(struct mmc *mmc) 760757bff49SJaehoon Chung { 761691272feSSimon Glass #endif 762045bdcd0SJaehoon Chung struct dwmci_host *host = (struct dwmci_host *)mmc->priv; 763045bdcd0SJaehoon Chung u32 ctype, regs; 764757bff49SJaehoon Chung 765757bff49SJaehoon Chung debug("Buswidth = %d, clock: %d\n", mmc->bus_width, mmc->clock); 766757bff49SJaehoon Chung 767757bff49SJaehoon Chung dwmci_setup_bus(host, mmc->clock); 768757bff49SJaehoon Chung switch (mmc->bus_width) { 769757bff49SJaehoon Chung case 8: 770757bff49SJaehoon Chung ctype = DWMCI_CTYPE_8BIT; 771757bff49SJaehoon Chung break; 772757bff49SJaehoon Chung case 4: 773757bff49SJaehoon Chung ctype = DWMCI_CTYPE_4BIT; 774757bff49SJaehoon Chung break; 775757bff49SJaehoon Chung default: 776757bff49SJaehoon Chung ctype = DWMCI_CTYPE_1BIT; 777757bff49SJaehoon Chung break; 778757bff49SJaehoon Chung } 779757bff49SJaehoon Chung 780757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CTYPE, ctype); 781757bff49SJaehoon Chung 782045bdcd0SJaehoon Chung regs = dwmci_readl(host, DWMCI_UHS_REG); 783caa21a21SZiyuan Xu if (mmc_card_ddr(mmc)) 784045bdcd0SJaehoon Chung regs |= DWMCI_DDR_MODE; 785045bdcd0SJaehoon Chung else 786afc9e2b5SJaehoon Chung regs &= ~DWMCI_DDR_MODE; 787045bdcd0SJaehoon Chung 788045bdcd0SJaehoon Chung dwmci_writel(host, DWMCI_UHS_REG, regs); 789045bdcd0SJaehoon Chung 790757bff49SJaehoon Chung if (host->clksel) 791757bff49SJaehoon Chung host->clksel(host); 79207b0b9c0SJaehoon Chung 793691272feSSimon Glass return 0; 794757bff49SJaehoon Chung } 795757bff49SJaehoon Chung 796757bff49SJaehoon Chung static int dwmci_init(struct mmc *mmc) 797757bff49SJaehoon Chung { 79893bfd616SPantelis Antoniou struct dwmci_host *host = mmc->priv; 79939abf9c1SPaweł Jarosz uint32_t use_dma; 80033e40bacSJason Zhu uint32_t verid; 801757bff49SJaehoon Chung 802a32c3f92SYifeng Zhao #if defined(CONFIG_DM_GPIO) && (defined(CONFIG_SPL_GPIO_SUPPORT) || !defined(CONFIG_SPL_BUILD)) 803a32c3f92SYifeng Zhao struct gpio_desc pwr_en_gpio; 804a32c3f92SYifeng Zhao u32 delay_ms; 805a32c3f92SYifeng Zhao 806a32c3f92SYifeng Zhao if (mmc_getcd(mmc) == 1 && 807a32c3f92SYifeng Zhao !gpio_request_by_name(mmc->dev, "pwr-en-gpios", 0, &pwr_en_gpio, GPIOD_IS_OUT)) { 808a32c3f92SYifeng Zhao dm_gpio_set_value(&pwr_en_gpio, 0); 809a32c3f92SYifeng Zhao pinctrl_select_state(mmc->dev, "idle"); 810a32c3f92SYifeng Zhao delay_ms = dev_read_u32_default(mmc->dev, "power-off-delay-ms", 200); 811a32c3f92SYifeng Zhao mdelay(delay_ms); 812a32c3f92SYifeng Zhao dm_gpio_set_value(&pwr_en_gpio, 1); 813a32c3f92SYifeng Zhao pinctrl_select_state(mmc->dev, "default"); 814a32c3f92SYifeng Zhao dm_gpio_free(mmc->dev, &pwr_en_gpio); 815a32c3f92SYifeng Zhao } 816a32c3f92SYifeng Zhao #endif 817a32c3f92SYifeng Zhao 81818ab6755SJaehoon Chung if (host->board_init) 81918ab6755SJaehoon Chung host->board_init(host); 820204f7c39SJason Zhu #ifdef CONFIG_ARCH_ROCKCHIP 821204f7c39SJason Zhu if (host->dev_index == 0) 822757bff49SJaehoon Chung dwmci_writel(host, DWMCI_PWREN, 1); 823204f7c39SJason Zhu else if (host->dev_index == 1) 824f88a10fcSYifeng Zhao dwmci_writel(host, DWMCI_PWREN, CONFIG_MMC_DW_PWREN_VALUE); 825204f7c39SJason Zhu else 826204f7c39SJason Zhu dwmci_writel(host, DWMCI_PWREN, 1); 827204f7c39SJason Zhu #else 828204f7c39SJason Zhu dwmci_writel(host, DWMCI_PWREN, 1); 829204f7c39SJason Zhu #endif 830757bff49SJaehoon Chung 83133e40bacSJason Zhu verid = dwmci_readl(host, DWMCI_VERID) & 0x0000ffff; 83233e40bacSJason Zhu if (verid >= DW_MMC_240A) 83333e40bacSJason Zhu dwmci_writel(host, DWMCI_CARDTHRCTL, DWMCI_CDTHRCTRL_CONFIG); 83433e40bacSJason Zhu 835757bff49SJaehoon Chung if (!dwmci_wait_reset(host, DWMCI_RESET_ALL)) { 8361c87ffe8SSimon Glass debug("%s[%d] Fail-reset!!\n", __func__, __LINE__); 8371c87ffe8SSimon Glass return -EIO; 838757bff49SJaehoon Chung } 839757bff49SJaehoon Chung 84039abf9c1SPaweł Jarosz use_dma = SDMMC_GET_TRANS_MODE(dwmci_readl(host, DWMCI_HCON)); 84139abf9c1SPaweł Jarosz if (use_dma == DMA_INTERFACE_IDMA) { 84239abf9c1SPaweł Jarosz host->fifo_mode = 0; 84339abf9c1SPaweł Jarosz } else { 84439abf9c1SPaweł Jarosz host->fifo_mode = 1; 84539abf9c1SPaweł Jarosz } 84639abf9c1SPaweł Jarosz 8479c50e35fSAmar /* Enumerate at 400KHz */ 84893bfd616SPantelis Antoniou dwmci_setup_bus(host, mmc->cfg->f_min); 8499c50e35fSAmar 850757bff49SJaehoon Chung dwmci_writel(host, DWMCI_RINTSTS, 0xFFFFFFFF); 851757bff49SJaehoon Chung dwmci_writel(host, DWMCI_INTMASK, 0); 852757bff49SJaehoon Chung 853757bff49SJaehoon Chung dwmci_writel(host, DWMCI_TMOUT, 0xFFFFFFFF); 854757bff49SJaehoon Chung 855757bff49SJaehoon Chung dwmci_writel(host, DWMCI_IDINTEN, 0); 856757bff49SJaehoon Chung dwmci_writel(host, DWMCI_BMOD, 1); 857757bff49SJaehoon Chung 858760177dfSSimon Glass if (!host->fifoth_val) { 859760177dfSSimon Glass uint32_t fifo_size; 860760177dfSSimon Glass 861760177dfSSimon Glass fifo_size = dwmci_readl(host, DWMCI_FIFOTH); 862760177dfSSimon Glass fifo_size = ((fifo_size & RX_WMARK_MASK) >> RX_WMARK_SHIFT) + 1; 8635ef89808SJason Zhu host->fifoth_val = MSIZE(DWMCI_MSIZE) | 8645ef89808SJason Zhu RX_WMARK(fifo_size / 2 - 1) | 865760177dfSSimon Glass TX_WMARK(fifo_size / 2); 8669108b315SAlexey Brodkin } 867760177dfSSimon Glass dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val); 868757bff49SJaehoon Chung 869757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CLKENA, 0); 870757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CLKSRC, 0); 871757bff49SJaehoon Chung 872757bff49SJaehoon Chung return 0; 873757bff49SJaehoon Chung } 874757bff49SJaehoon Chung 8755743ef64SJason Zhu static int dwmci_get_cd(struct udevice *dev) 8765743ef64SJason Zhu { 8775743ef64SJason Zhu int ret = -1; 8783e4b5e88SYifeng Zhao struct mmc *mmc = mmc_get_mmc_dev(dev); 8793e4b5e88SYifeng Zhao struct dwmci_host *host = mmc->priv; 880dc58c997SJason Zhu 881dc58c997SJason Zhu #if defined(CONFIG_DM_GPIO) && (defined(CONFIG_SPL_GPIO_SUPPORT) || !defined(CONFIG_SPL_BUILD)) 8825743ef64SJason Zhu struct gpio_desc detect; 8835743ef64SJason Zhu 8845743ef64SJason Zhu ret = gpio_request_by_name(dev, "cd-gpios", 0, &detect, GPIOD_IS_IN); 8855743ef64SJason Zhu if (ret) { 8863e4b5e88SYifeng Zhao goto dw_mmc_cdetect; 8875743ef64SJason Zhu } 8885743ef64SJason Zhu 8895743ef64SJason Zhu ret = !dm_gpio_get_value(&detect); 890850d604aSJason Zhu dm_gpio_free(dev, &detect); 8913e4b5e88SYifeng Zhao return ret; 8923e4b5e88SYifeng Zhao dw_mmc_cdetect: 8935743ef64SJason Zhu #endif 8943e4b5e88SYifeng Zhao ret = (dwmci_readl(host, DWMCI_CDETECT) & (1 << 0)) == 0 ? 1 : 0; 8953e4b5e88SYifeng Zhao 8965743ef64SJason Zhu return ret; 8975743ef64SJason Zhu } 8985743ef64SJason Zhu 899e7881d85SSimon Glass #ifdef CONFIG_DM_MMC 900691272feSSimon Glass int dwmci_probe(struct udevice *dev) 901691272feSSimon Glass { 902691272feSSimon Glass struct mmc *mmc = mmc_get_mmc_dev(dev); 903691272feSSimon Glass 904691272feSSimon Glass return dwmci_init(mmc); 905691272feSSimon Glass } 906691272feSSimon Glass 907691272feSSimon Glass const struct dm_mmc_ops dm_dwmci_ops = { 908ba0e56e1SZiyuan Xu .card_busy = dwmci_card_busy, 909691272feSSimon Glass .send_cmd = dwmci_send_cmd, 91047f7fd3aSJason Zhu #ifdef CONFIG_SPL_BLK_READ_PREPARE 91147f7fd3aSJason Zhu .send_cmd_prepare = dwmci_send_cmd_prepare, 91247f7fd3aSJason Zhu #endif 913691272feSSimon Glass .set_ios = dwmci_set_ios, 9145743ef64SJason Zhu .get_cd = dwmci_get_cd, 9158c921dceSZiyuan Xu .execute_tuning = dwmci_execute_tuning, 916691272feSSimon Glass }; 917691272feSSimon Glass 918691272feSSimon Glass #else 919ab769f22SPantelis Antoniou static const struct mmc_ops dwmci_ops = { 920ba0e56e1SZiyuan Xu .card_busy = dwmci_card_busy, 921ab769f22SPantelis Antoniou .send_cmd = dwmci_send_cmd, 922ab769f22SPantelis Antoniou .set_ios = dwmci_set_ios, 9235743ef64SJason Zhu .get_cd = dwmci_get_cd, 924ab769f22SPantelis Antoniou .init = dwmci_init, 9258c921dceSZiyuan Xu .execute_tuning = dwmci_execute_tuning, 926ab769f22SPantelis Antoniou }; 927691272feSSimon Glass #endif 928ab769f22SPantelis Antoniou 929e5113c33SJaehoon Chung void dwmci_setup_cfg(struct mmc_config *cfg, struct dwmci_host *host, 930e5113c33SJaehoon Chung u32 max_clk, u32 min_clk) 9315e6ff810SSimon Glass { 932e5113c33SJaehoon Chung cfg->name = host->name; 933e7881d85SSimon Glass #ifndef CONFIG_DM_MMC 9345e6ff810SSimon Glass cfg->ops = &dwmci_ops; 935691272feSSimon Glass #endif 9365e6ff810SSimon Glass cfg->f_min = min_clk; 9375e6ff810SSimon Glass cfg->f_max = max_clk; 9385e6ff810SSimon Glass 9395e6ff810SSimon Glass cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; 9405e6ff810SSimon Glass 941e5113c33SJaehoon Chung cfg->host_caps = host->caps; 9425e6ff810SSimon Glass 94380ac95fcSZiyuan Xu switch (host->buswidth) { 94480ac95fcSZiyuan Xu case 8: 945c1cfa99bSZiyuan Xu cfg->host_caps |= MMC_MODE_8BIT | MMC_MODE_4BIT; 94680ac95fcSZiyuan Xu break; 94780ac95fcSZiyuan Xu case 4: 9485e6ff810SSimon Glass cfg->host_caps |= MMC_MODE_4BIT; 9495e6ff810SSimon Glass cfg->host_caps &= ~MMC_MODE_8BIT; 95080ac95fcSZiyuan Xu break; 95180ac95fcSZiyuan Xu case 1: 95280ac95fcSZiyuan Xu cfg->host_caps &= ~MMC_MODE_4BIT; 95380ac95fcSZiyuan Xu cfg->host_caps &= ~MMC_MODE_8BIT; 95480ac95fcSZiyuan Xu break; 95580ac95fcSZiyuan Xu default: 95680ac95fcSZiyuan Xu printf("Unsupported bus width: %d\n", host->buswidth); 95780ac95fcSZiyuan Xu break; 9585e6ff810SSimon Glass } 9595e6ff810SSimon Glass cfg->host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz; 9605e6ff810SSimon Glass 9615e6ff810SSimon Glass cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; 9625e6ff810SSimon Glass } 9635e6ff810SSimon Glass 9645e6ff810SSimon Glass #ifdef CONFIG_BLK 9655e6ff810SSimon Glass int dwmci_bind(struct udevice *dev, struct mmc *mmc, struct mmc_config *cfg) 9665e6ff810SSimon Glass { 9675e6ff810SSimon Glass return mmc_bind(dev, mmc, cfg); 9685e6ff810SSimon Glass } 9695e6ff810SSimon Glass #else 970757bff49SJaehoon Chung int add_dwmci(struct dwmci_host *host, u32 max_clk, u32 min_clk) 971757bff49SJaehoon Chung { 972e5113c33SJaehoon Chung dwmci_setup_cfg(&host->cfg, host, max_clk, min_clk); 973757bff49SJaehoon Chung 97493bfd616SPantelis Antoniou host->mmc = mmc_create(&host->cfg, host); 97593bfd616SPantelis Antoniou if (host->mmc == NULL) 97693bfd616SPantelis Antoniou return -1; 97793bfd616SPantelis Antoniou 97893bfd616SPantelis Antoniou return 0; 979757bff49SJaehoon Chung } 9805e6ff810SSimon Glass #endif 981