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 static unsigned int dwmci_get_drto(struct dwmci_host *host, 167bbd0d600SZiyuan Xu const unsigned int size) 16834d21c9aSJason Zhu { 16934d21c9aSJason Zhu unsigned int timeout; 17034d21c9aSJason Zhu 17134d21c9aSJason Zhu timeout = size * 8; /* counting in bits */ 172bbd0d600SZiyuan Xu timeout /= host->mmc->bus_width; 173*3aa32998SYifeng Zhao timeout *= 10; /* wait 10 times as long */ 174*3aa32998SYifeng Zhao timeout /= (host->mmc->clock / 1000); /* counting in msec */ 175*3aa32998SYifeng Zhao timeout = (timeout < 1000) ? 1000 : timeout; 17634d21c9aSJason Zhu 17734d21c9aSJason Zhu return timeout; 17834d21c9aSJason Zhu } 179bbd0d600SZiyuan Xu 180bbd0d600SZiyuan Xu static unsigned int dwmci_get_cto(struct dwmci_host *host) 181bbd0d600SZiyuan Xu { 182bbd0d600SZiyuan Xu unsigned int cto_clks; 183bbd0d600SZiyuan Xu unsigned int cto_div; 184bbd0d600SZiyuan Xu unsigned int cto_ms; 185bbd0d600SZiyuan Xu 186bbd0d600SZiyuan Xu cto_clks = dwmci_readl(host, DWMCI_TMOUT) & 0xff; 187bbd0d600SZiyuan Xu cto_div = (dwmci_readl(host, DWMCI_CLKDIV) & 0xff) * 2; 188bbd0d600SZiyuan Xu if (cto_div == 0) 189bbd0d600SZiyuan Xu cto_div = 1; 190bbd0d600SZiyuan Xu 191bbd0d600SZiyuan Xu cto_ms = DIV_ROUND_UP_ULL((u64)MSEC_PER_SEC * cto_clks * cto_div, 192bbd0d600SZiyuan Xu host->mmc->clock); 193bbd0d600SZiyuan Xu 194bbd0d600SZiyuan Xu /* add a bit spare time */ 195bbd0d600SZiyuan Xu cto_ms += 10; 196bbd0d600SZiyuan Xu 197bbd0d600SZiyuan Xu return cto_ms; 198bbd0d600SZiyuan Xu } 19934d21c9aSJason Zhu 200a65f51b9Shuang lin static int dwmci_data_transfer(struct dwmci_host *host, struct mmc_data *data) 201f382eb83Shuang lin { 202f382eb83Shuang lin int ret = 0; 203f7c0370cSJason Zhu int reset_timeout = 100; 20434d21c9aSJason Zhu u32 timeout, status, ctrl, mask, size, i, len = 0; 205a65f51b9Shuang lin u32 *buf = NULL; 206f382eb83Shuang lin ulong start = get_timer(0); 207a65f51b9Shuang lin u32 fifo_depth = (((host->fifoth_val & RX_WMARK_MASK) >> 208a65f51b9Shuang lin RX_WMARK_SHIFT) + 1) * 2; 209bda599f7SShawn Lin bool stride; 210a65f51b9Shuang lin 21134d21c9aSJason Zhu size = data->blocksize * data->blocks; 212bda599f7SShawn Lin /* Still use legacy PIO mode if size < 512(128 * 4) Bytes */ 213bda599f7SShawn Lin stride = host->stride_pio && size > 128; 214a65f51b9Shuang lin if (data->flags == MMC_DATA_READ) 215a65f51b9Shuang lin buf = (unsigned int *)data->dest; 216a65f51b9Shuang lin else 217a65f51b9Shuang lin buf = (unsigned int *)data->src; 218f382eb83Shuang lin 219bbd0d600SZiyuan Xu timeout = dwmci_get_drto(host, size); 220315c0aefSYifeng Zhao /* The tuning data is 128bytes, a timeout of 1ms is sufficient.*/ 221315c0aefSYifeng Zhao if ((dwmci_readl(host, DWMCI_CMD) & 0x1F) == MMC_SEND_TUNING_BLOCK_HS200) 222315c0aefSYifeng Zhao timeout = 1; 223315c0aefSYifeng Zhao 22434d21c9aSJason Zhu size /= 4; 22534d21c9aSJason Zhu 226f382eb83Shuang lin for (;;) { 227f382eb83Shuang lin mask = dwmci_readl(host, DWMCI_RINTSTS); 228f382eb83Shuang lin /* Error during data transfer. */ 229f382eb83Shuang lin if (mask & (DWMCI_DATA_ERR | DWMCI_DATA_TOUT)) { 230f382eb83Shuang lin debug("%s: DATA ERROR!\n", __func__); 231a0f322f5SYifeng Zhao /* 232a0f322f5SYifeng Zhao * It is necessary to wait for several cycles before 233a0f322f5SYifeng Zhao * resetting the controller while data timeout or error. 234a0f322f5SYifeng Zhao */ 235a0f322f5SYifeng Zhao udelay(1); 2360d797f18SZiyuan Xu dwmci_wait_reset(host, DWMCI_RESET_ALL); 2370d797f18SZiyuan Xu dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT | 2380d797f18SZiyuan Xu DWMCI_CMD_UPD_CLK | DWMCI_CMD_START); 2390d797f18SZiyuan Xu 2400d797f18SZiyuan Xu do { 2410d797f18SZiyuan Xu status = dwmci_readl(host, DWMCI_CMD); 242f7c0370cSJason Zhu if (reset_timeout-- < 0) 243f7c0370cSJason Zhu break; 244f7c0370cSJason Zhu udelay(100); 2450d797f18SZiyuan Xu } while (status & DWMCI_CMD_START); 2460d797f18SZiyuan Xu 2470d797f18SZiyuan Xu if (!host->fifo_mode) { 2480d797f18SZiyuan Xu ctrl = dwmci_readl(host, DWMCI_BMOD); 2490d797f18SZiyuan Xu ctrl |= DWMCI_BMOD_IDMAC_RESET; 2500d797f18SZiyuan Xu dwmci_writel(host, DWMCI_BMOD, ctrl); 2510d797f18SZiyuan Xu } 2520d797f18SZiyuan Xu 253f382eb83Shuang lin ret = -EINVAL; 254f382eb83Shuang lin break; 255f382eb83Shuang lin } 256f382eb83Shuang lin 257a65f51b9Shuang lin if (host->fifo_mode && size) { 258720724d0SXu Ziyuan len = 0; 2592b429033SJacob Chen if (data->flags == MMC_DATA_READ && 2601aaee36eSShawn Lin (mask & (DWMCI_INTMSK_RXDR | DWMCI_INTMSK_DTO))) { 2612b429033SJacob Chen while (size) { 262a65f51b9Shuang lin len = dwmci_readl(host, DWMCI_STATUS); 263a65f51b9Shuang lin len = (len >> DWMCI_FIFO_SHIFT) & 264a65f51b9Shuang lin DWMCI_FIFO_MASK; 2652990e07aSXu Ziyuan len = min(size, len); 266bda599f7SShawn Lin if (!stride) { 267bda599f7SShawn Lin /* Legacy pio mode */ 268a65f51b9Shuang lin for (i = 0; i < len; i++) 269bda599f7SShawn Lin *buf++ = dwmci_readl(host, DWMCI_DATA); 270bda599f7SShawn Lin goto read_again; 271bda599f7SShawn Lin } 272bda599f7SShawn Lin 273bda599f7SShawn Lin /* dwmci_memcpy_fromio now bursts 256 Bytes once */ 274bda599f7SShawn Lin if (len < MAX_STRIDE) 275bda599f7SShawn Lin continue; 276bda599f7SShawn Lin 277bda599f7SShawn Lin for (i = 0; i < len / MAX_STRIDE; i++) { 278bda599f7SShawn Lin dwmci_memcpy_fromio(buf, host->ioaddr + DWMCI_DATA); 279bda599f7SShawn Lin buf += MAX_STRIDE; 280bda599f7SShawn Lin } 281bda599f7SShawn Lin 282bda599f7SShawn Lin len = i * MAX_STRIDE; 283bda599f7SShawn Lin read_again: 2842b429033SJacob Chen size = size > len ? (size - len) : 0; 2852b429033SJacob Chen } 286d8db857fSShawn Lin 287d8db857fSShawn Lin dwmci_writel(host, DWMCI_RINTSTS, 288d8db857fSShawn Lin mask & (DWMCI_INTMSK_RXDR | DWMCI_INTMSK_DTO)); 2892b429033SJacob Chen } else if (data->flags == MMC_DATA_WRITE && 2902b429033SJacob Chen (mask & DWMCI_INTMSK_TXDR)) { 2912b429033SJacob Chen while (size) { 292a65f51b9Shuang lin len = dwmci_readl(host, DWMCI_STATUS); 293a65f51b9Shuang lin len = fifo_depth - ((len >> 294a65f51b9Shuang lin DWMCI_FIFO_SHIFT) & 295a65f51b9Shuang lin DWMCI_FIFO_MASK); 2962990e07aSXu Ziyuan len = min(size, len); 297bda599f7SShawn Lin if (!stride) { 298a65f51b9Shuang lin for (i = 0; i < len; i++) 299a65f51b9Shuang lin dwmci_writel(host, DWMCI_DATA, 300a65f51b9Shuang lin *buf++); 301bda599f7SShawn Lin goto write_again; 302bda599f7SShawn Lin } 303bda599f7SShawn Lin /* dwmci_memcpy_toio now bursts 256 Bytes once */ 304bda599f7SShawn Lin if (len < MAX_STRIDE) 305bda599f7SShawn Lin continue; 306bda599f7SShawn Lin 307bda599f7SShawn Lin for (i = 0; i < len / MAX_STRIDE; i++) { 308bda599f7SShawn Lin dwmci_memcpy_toio(buf, host->ioaddr + DWMCI_DATA); 309bda599f7SShawn Lin buf += MAX_STRIDE; 310bda599f7SShawn Lin } 311bda599f7SShawn Lin 312bda599f7SShawn Lin len = i * MAX_STRIDE; 313bda599f7SShawn Lin write_again: 3142b429033SJacob Chen size = size > len ? (size - len) : 0; 3152b429033SJacob Chen } 316a65f51b9Shuang lin dwmci_writel(host, DWMCI_RINTSTS, 317a65f51b9Shuang lin DWMCI_INTMSK_TXDR); 318a65f51b9Shuang lin } 319a65f51b9Shuang lin } 320a65f51b9Shuang lin 321f382eb83Shuang lin /* Data arrived correctly. */ 322f382eb83Shuang lin if (mask & DWMCI_INTMSK_DTO) { 323f382eb83Shuang lin ret = 0; 324f382eb83Shuang lin break; 325f382eb83Shuang lin } 326f382eb83Shuang lin 327f382eb83Shuang lin /* Check for timeout. */ 328f382eb83Shuang lin if (get_timer(start) > timeout) { 329f382eb83Shuang lin debug("%s: Timeout waiting for data!\n", 330f382eb83Shuang lin __func__); 331915ffa52SJaehoon Chung ret = -ETIMEDOUT; 332f382eb83Shuang lin break; 333f382eb83Shuang lin } 334f382eb83Shuang lin } 335f382eb83Shuang lin 336f382eb83Shuang lin dwmci_writel(host, DWMCI_RINTSTS, mask); 337f382eb83Shuang lin 338f382eb83Shuang lin return ret; 339f382eb83Shuang lin } 340f382eb83Shuang lin 341757bff49SJaehoon Chung static int dwmci_set_transfer_mode(struct dwmci_host *host, 342757bff49SJaehoon Chung struct mmc_data *data) 343757bff49SJaehoon Chung { 344757bff49SJaehoon Chung unsigned long mode; 345757bff49SJaehoon Chung 346757bff49SJaehoon Chung mode = DWMCI_CMD_DATA_EXP; 347757bff49SJaehoon Chung if (data->flags & MMC_DATA_WRITE) 348757bff49SJaehoon Chung mode |= DWMCI_CMD_RW; 349757bff49SJaehoon Chung 350757bff49SJaehoon Chung return mode; 351757bff49SJaehoon Chung } 352757bff49SJaehoon Chung 353e7881d85SSimon Glass #ifdef CONFIG_DM_MMC 3545628347fSJaehoon Chung static int dwmci_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, 355691272feSSimon Glass struct mmc_data *data) 356691272feSSimon Glass { 357691272feSSimon Glass struct mmc *mmc = mmc_get_mmc_dev(dev); 358691272feSSimon Glass #else 359757bff49SJaehoon Chung static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, 360757bff49SJaehoon Chung struct mmc_data *data) 361757bff49SJaehoon Chung { 362691272feSSimon Glass #endif 36393bfd616SPantelis Antoniou struct dwmci_host *host = mmc->priv; 3642136d226SMischa Jonker ALLOC_CACHE_ALIGN_BUFFER(struct dwmci_idmac, cur_idmac, 36521bd5761SMischa Jonker data ? DIV_ROUND_UP(data->blocks, 8) : 0); 366bbd0d600SZiyuan Xu int ret = 0, flags = 0; 36702ebd42cSXu Ziyuan unsigned int timeout = 500; 368757bff49SJaehoon Chung u32 mask, ctrl; 3699c50e35fSAmar ulong start = get_timer(0); 3702a7a210eSAlexey Brodkin struct bounce_buffer bbstate; 371757bff49SJaehoon Chung 372757bff49SJaehoon Chung while (dwmci_readl(host, DWMCI_STATUS) & DWMCI_BUSY) { 3739c50e35fSAmar if (get_timer(start) > timeout) { 3741c87ffe8SSimon Glass debug("%s: Timeout on data busy\n", __func__); 375915ffa52SJaehoon Chung return -ETIMEDOUT; 376757bff49SJaehoon Chung } 377757bff49SJaehoon Chung } 378757bff49SJaehoon Chung 379757bff49SJaehoon Chung dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_ALL); 380757bff49SJaehoon Chung 3812a7a210eSAlexey Brodkin if (data) { 382a65f51b9Shuang lin if (host->fifo_mode) { 383a65f51b9Shuang lin dwmci_writel(host, DWMCI_BLKSIZ, data->blocksize); 384a65f51b9Shuang lin dwmci_writel(host, DWMCI_BYTCNT, 385a65f51b9Shuang lin data->blocksize * data->blocks); 386a65f51b9Shuang lin dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET); 387a65f51b9Shuang lin } else { 3882a7a210eSAlexey Brodkin if (data->flags == MMC_DATA_READ) { 3898cdfda89SMarek Vasut ret = bounce_buffer_start(&bbstate, 3908cdfda89SMarek Vasut (void*)data->dest, 3912a7a210eSAlexey Brodkin data->blocksize * 3922a7a210eSAlexey Brodkin data->blocks, GEN_BB_WRITE); 3932a7a210eSAlexey Brodkin } else { 3948cdfda89SMarek Vasut ret = bounce_buffer_start(&bbstate, 3958cdfda89SMarek Vasut (void*)data->src, 3962a7a210eSAlexey Brodkin data->blocksize * 3972a7a210eSAlexey Brodkin data->blocks, GEN_BB_READ); 3982a7a210eSAlexey Brodkin } 3998cdfda89SMarek Vasut 4008cdfda89SMarek Vasut if (ret) 4018cdfda89SMarek Vasut return ret; 4028cdfda89SMarek Vasut 4032a7a210eSAlexey Brodkin dwmci_prepare_data(host, data, cur_idmac, 4042a7a210eSAlexey Brodkin bbstate.bounce_buffer); 4052a7a210eSAlexey Brodkin } 406a65f51b9Shuang lin } 407757bff49SJaehoon Chung 408757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CMDARG, cmd->cmdarg); 409757bff49SJaehoon Chung 410757bff49SJaehoon Chung if (data) 411757bff49SJaehoon Chung flags = dwmci_set_transfer_mode(host, data); 412757bff49SJaehoon Chung 413757bff49SJaehoon Chung if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) 414757bff49SJaehoon Chung return -1; 415757bff49SJaehoon Chung 416757bff49SJaehoon Chung if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) 417757bff49SJaehoon Chung flags |= DWMCI_CMD_ABORT_STOP; 4185135be73SYifeng Zhao else if (cmd->cmdidx == MMC_CMD_GO_IDLE_STATE) 4195135be73SYifeng Zhao flags |= SDMMC_CMD_INIT | DWMCI_CMD_ABORT_STOP; 420757bff49SJaehoon Chung else 421757bff49SJaehoon Chung flags |= DWMCI_CMD_PRV_DAT_WAIT; 422757bff49SJaehoon Chung 423757bff49SJaehoon Chung if (cmd->resp_type & MMC_RSP_PRESENT) { 424757bff49SJaehoon Chung flags |= DWMCI_CMD_RESP_EXP; 425757bff49SJaehoon Chung if (cmd->resp_type & MMC_RSP_136) 426757bff49SJaehoon Chung flags |= DWMCI_CMD_RESP_LENGTH; 427757bff49SJaehoon Chung } 428757bff49SJaehoon Chung 429757bff49SJaehoon Chung if (cmd->resp_type & MMC_RSP_CRC) 430757bff49SJaehoon Chung flags |= DWMCI_CMD_CHECK_CRC; 431757bff49SJaehoon Chung 432757bff49SJaehoon Chung flags |= (cmd->cmdidx | DWMCI_CMD_START | DWMCI_CMD_USE_HOLD_REG); 433757bff49SJaehoon Chung 434757bff49SJaehoon Chung debug("Sending CMD%d\n",cmd->cmdidx); 435757bff49SJaehoon Chung 436757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CMD, flags); 437757bff49SJaehoon Chung 438bbd0d600SZiyuan Xu timeout = dwmci_get_cto(host); 439bbd0d600SZiyuan Xu start = get_timer(0); 440bbd0d600SZiyuan Xu do { 441757bff49SJaehoon Chung mask = dwmci_readl(host, DWMCI_RINTSTS); 442757bff49SJaehoon Chung if (mask & DWMCI_INTMSK_CDONE) { 443757bff49SJaehoon Chung if (!data) 444757bff49SJaehoon Chung dwmci_writel(host, DWMCI_RINTSTS, mask); 445757bff49SJaehoon Chung break; 446757bff49SJaehoon Chung } 447bbd0d600SZiyuan Xu } while (!(get_timer(start) > timeout)); 448757bff49SJaehoon Chung 449bbd0d600SZiyuan Xu if (get_timer(start) > timeout) { 4501c87ffe8SSimon Glass debug("%s: Timeout.\n", __func__); 451915ffa52SJaehoon Chung return -ETIMEDOUT; 452f33c9305SPavel Machek } 453757bff49SJaehoon Chung 454757bff49SJaehoon Chung if (mask & DWMCI_INTMSK_RTO) { 455f33c9305SPavel Machek /* 456f33c9305SPavel Machek * Timeout here is not necessarily fatal. (e)MMC cards 457f33c9305SPavel Machek * will splat here when they receive CMD55 as they do 458f33c9305SPavel Machek * not support this command and that is exactly the way 459f33c9305SPavel Machek * to tell them apart from SD cards. Thus, this output 460f33c9305SPavel Machek * below shall be debug(). eMMC cards also do not favor 461f33c9305SPavel Machek * CMD8, please keep that in mind. 462f33c9305SPavel Machek */ 463f33c9305SPavel Machek debug("%s: Response Timeout.\n", __func__); 464915ffa52SJaehoon Chung return -ETIMEDOUT; 465757bff49SJaehoon Chung } else if (mask & DWMCI_INTMSK_RE) { 4661c87ffe8SSimon Glass debug("%s: Response Error.\n", __func__); 4671c87ffe8SSimon Glass return -EIO; 468757bff49SJaehoon Chung } 469757bff49SJaehoon Chung 470757bff49SJaehoon Chung 471757bff49SJaehoon Chung if (cmd->resp_type & MMC_RSP_PRESENT) { 472757bff49SJaehoon Chung if (cmd->resp_type & MMC_RSP_136) { 473757bff49SJaehoon Chung cmd->response[0] = dwmci_readl(host, DWMCI_RESP3); 474757bff49SJaehoon Chung cmd->response[1] = dwmci_readl(host, DWMCI_RESP2); 475757bff49SJaehoon Chung cmd->response[2] = dwmci_readl(host, DWMCI_RESP1); 476757bff49SJaehoon Chung cmd->response[3] = dwmci_readl(host, DWMCI_RESP0); 477757bff49SJaehoon Chung } else { 478757bff49SJaehoon Chung cmd->response[0] = dwmci_readl(host, DWMCI_RESP0); 479757bff49SJaehoon Chung } 480757bff49SJaehoon Chung } 481757bff49SJaehoon Chung 482757bff49SJaehoon Chung if (data) { 483a65f51b9Shuang lin ret = dwmci_data_transfer(host, data); 484757bff49SJaehoon Chung 485a65f51b9Shuang lin /* only dma mode need it */ 486a65f51b9Shuang lin if (!host->fifo_mode) { 487757bff49SJaehoon Chung ctrl = dwmci_readl(host, DWMCI_CTRL); 488757bff49SJaehoon Chung ctrl &= ~(DWMCI_DMA_EN); 489757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CTRL, ctrl); 4902a7a210eSAlexey Brodkin bounce_buffer_stop(&bbstate); 491757bff49SJaehoon Chung } 492a65f51b9Shuang lin } 493757bff49SJaehoon Chung 4949042d974SMarek Vasut return ret; 495757bff49SJaehoon Chung } 496757bff49SJaehoon Chung 49747f7fd3aSJason Zhu #ifdef CONFIG_SPL_BLK_READ_PREPARE 49847f7fd3aSJason Zhu #ifdef CONFIG_DM_MMC 49947f7fd3aSJason Zhu static int dwmci_send_cmd_prepare(struct udevice *dev, struct mmc_cmd *cmd, 50047f7fd3aSJason Zhu struct mmc_data *data) 50147f7fd3aSJason Zhu { 50247f7fd3aSJason Zhu struct mmc *mmc = mmc_get_mmc_dev(dev); 50347f7fd3aSJason Zhu #else 50447f7fd3aSJason Zhu static int dwmci_send_cmd_prepare(struct mmc *mmc, struct mmc_cmd *cmd, 50547f7fd3aSJason Zhu struct mmc_data *data) 50647f7fd3aSJason Zhu { 50747f7fd3aSJason Zhu #endif 50847f7fd3aSJason Zhu struct dwmci_host *host = mmc->priv; 50947f7fd3aSJason Zhu struct dwmci_idmac *cur_idmac; 510bbd0d600SZiyuan Xu int ret = 0, flags = 0; 51147f7fd3aSJason Zhu unsigned int timeout = 500; 51247f7fd3aSJason Zhu u32 mask; 51347f7fd3aSJason Zhu ulong start = get_timer(0); 51447f7fd3aSJason Zhu struct bounce_buffer bbstate; 51547f7fd3aSJason Zhu 51647f7fd3aSJason Zhu cur_idmac = malloc(ROUND(DIV_ROUND_UP(data->blocks, 8) * 51747f7fd3aSJason Zhu sizeof(struct dwmci_idmac), 51847f7fd3aSJason Zhu ARCH_DMA_MINALIGN) + ARCH_DMA_MINALIGN - 1); 51947f7fd3aSJason Zhu if (!cur_idmac) 52047f7fd3aSJason Zhu return -ENODATA; 52147f7fd3aSJason Zhu 52247f7fd3aSJason Zhu while (dwmci_readl(host, DWMCI_STATUS) & DWMCI_BUSY) { 52347f7fd3aSJason Zhu if (get_timer(start) > timeout) { 52447f7fd3aSJason Zhu debug("%s: Timeout on data busy\n", __func__); 52547f7fd3aSJason Zhu return -ETIMEDOUT; 52647f7fd3aSJason Zhu } 52747f7fd3aSJason Zhu } 52847f7fd3aSJason Zhu 52947f7fd3aSJason Zhu dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_ALL); 53047f7fd3aSJason Zhu 53147f7fd3aSJason Zhu if (data) { 53247f7fd3aSJason Zhu if (host->fifo_mode) { 53347f7fd3aSJason Zhu dwmci_writel(host, DWMCI_BLKSIZ, data->blocksize); 53447f7fd3aSJason Zhu dwmci_writel(host, DWMCI_BYTCNT, 53547f7fd3aSJason Zhu data->blocksize * data->blocks); 53647f7fd3aSJason Zhu dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET); 53747f7fd3aSJason Zhu } else { 53847f7fd3aSJason Zhu if (data->flags == MMC_DATA_READ) { 53947f7fd3aSJason Zhu bounce_buffer_start(&bbstate, (void *)data->dest, 54047f7fd3aSJason Zhu data->blocksize * 54147f7fd3aSJason Zhu data->blocks, GEN_BB_WRITE); 54247f7fd3aSJason Zhu } else { 54347f7fd3aSJason Zhu bounce_buffer_start(&bbstate, (void *)data->src, 54447f7fd3aSJason Zhu data->blocksize * 54547f7fd3aSJason Zhu data->blocks, GEN_BB_READ); 54647f7fd3aSJason Zhu } 54747f7fd3aSJason Zhu dwmci_prepare_data(host, data, cur_idmac, 54847f7fd3aSJason Zhu bbstate.bounce_buffer); 54947f7fd3aSJason Zhu } 55047f7fd3aSJason Zhu } 55147f7fd3aSJason Zhu 55247f7fd3aSJason Zhu dwmci_writel(host, DWMCI_CMDARG, cmd->cmdarg); 55347f7fd3aSJason Zhu 55447f7fd3aSJason Zhu if (data) 55547f7fd3aSJason Zhu flags = dwmci_set_transfer_mode(host, data); 55647f7fd3aSJason Zhu 55747f7fd3aSJason Zhu if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) 55847f7fd3aSJason Zhu return -1; 55947f7fd3aSJason Zhu 56047f7fd3aSJason Zhu if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) 56147f7fd3aSJason Zhu flags |= DWMCI_CMD_ABORT_STOP; 56247f7fd3aSJason Zhu else 56347f7fd3aSJason Zhu flags |= DWMCI_CMD_PRV_DAT_WAIT; 56447f7fd3aSJason Zhu 56547f7fd3aSJason Zhu if (cmd->resp_type & MMC_RSP_PRESENT) { 56647f7fd3aSJason Zhu flags |= DWMCI_CMD_RESP_EXP; 56747f7fd3aSJason Zhu if (cmd->resp_type & MMC_RSP_136) 56847f7fd3aSJason Zhu flags |= DWMCI_CMD_RESP_LENGTH; 56947f7fd3aSJason Zhu } 57047f7fd3aSJason Zhu 57147f7fd3aSJason Zhu if (cmd->resp_type & MMC_RSP_CRC) 57247f7fd3aSJason Zhu flags |= DWMCI_CMD_CHECK_CRC; 57347f7fd3aSJason Zhu 57447f7fd3aSJason Zhu flags |= (cmd->cmdidx | DWMCI_CMD_START | DWMCI_CMD_USE_HOLD_REG); 57547f7fd3aSJason Zhu 57647f7fd3aSJason Zhu debug("Sending CMD%d\n", cmd->cmdidx); 57747f7fd3aSJason Zhu 57847f7fd3aSJason Zhu dwmci_writel(host, DWMCI_CMD, flags); 57947f7fd3aSJason Zhu 580bbd0d600SZiyuan Xu timeout = dwmci_get_cto(host); 581bbd0d600SZiyuan Xu start = get_timer(0); 582bbd0d600SZiyuan Xu do { 58347f7fd3aSJason Zhu mask = dwmci_readl(host, DWMCI_RINTSTS); 58447f7fd3aSJason Zhu if (mask & DWMCI_INTMSK_CDONE) { 58547f7fd3aSJason Zhu if (!data) 58647f7fd3aSJason Zhu dwmci_writel(host, DWMCI_RINTSTS, mask); 58747f7fd3aSJason Zhu break; 58847f7fd3aSJason Zhu } 589bbd0d600SZiyuan Xu } while (!(get_timer(start) > timeout)); 59047f7fd3aSJason Zhu 591bbd0d600SZiyuan Xu if (get_timer(start) > timeout) { 59247f7fd3aSJason Zhu debug("%s: Timeout.\n", __func__); 59347f7fd3aSJason Zhu return -ETIMEDOUT; 59447f7fd3aSJason Zhu } 59547f7fd3aSJason Zhu 59647f7fd3aSJason Zhu if (mask & DWMCI_INTMSK_RTO) { 59747f7fd3aSJason Zhu /* 59847f7fd3aSJason Zhu * Timeout here is not necessarily fatal. (e)MMC cards 59947f7fd3aSJason Zhu * will splat here when they receive CMD55 as they do 60047f7fd3aSJason Zhu * not support this command and that is exactly the way 60147f7fd3aSJason Zhu * to tell them apart from SD cards. Thus, this output 60247f7fd3aSJason Zhu * below shall be debug(). eMMC cards also do not favor 60347f7fd3aSJason Zhu * CMD8, please keep that in mind. 60447f7fd3aSJason Zhu */ 60547f7fd3aSJason Zhu debug("%s: Response Timeout.\n", __func__); 60647f7fd3aSJason Zhu return -ETIMEDOUT; 60747f7fd3aSJason Zhu } else if (mask & DWMCI_INTMSK_RE) { 60847f7fd3aSJason Zhu debug("%s: Response Error.\n", __func__); 60947f7fd3aSJason Zhu return -EIO; 61047f7fd3aSJason Zhu } 61147f7fd3aSJason Zhu 61247f7fd3aSJason Zhu if (cmd->resp_type & MMC_RSP_PRESENT) { 61347f7fd3aSJason Zhu if (cmd->resp_type & MMC_RSP_136) { 61447f7fd3aSJason Zhu cmd->response[0] = dwmci_readl(host, DWMCI_RESP3); 61547f7fd3aSJason Zhu cmd->response[1] = dwmci_readl(host, DWMCI_RESP2); 61647f7fd3aSJason Zhu cmd->response[2] = dwmci_readl(host, DWMCI_RESP1); 61747f7fd3aSJason Zhu cmd->response[3] = dwmci_readl(host, DWMCI_RESP0); 61847f7fd3aSJason Zhu } else { 61947f7fd3aSJason Zhu cmd->response[0] = dwmci_readl(host, DWMCI_RESP0); 62047f7fd3aSJason Zhu } 62147f7fd3aSJason Zhu } 62247f7fd3aSJason Zhu 62347f7fd3aSJason Zhu return ret; 62447f7fd3aSJason Zhu } 62547f7fd3aSJason Zhu #endif 62647f7fd3aSJason Zhu 627757bff49SJaehoon Chung static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) 628757bff49SJaehoon Chung { 629757bff49SJaehoon Chung u32 div, status; 630757bff49SJaehoon Chung int timeout = 10000; 631757bff49SJaehoon Chung unsigned long sclk; 632757bff49SJaehoon Chung 63324527ef9SZiyuan Xu if (freq == 0) 634757bff49SJaehoon Chung return 0; 635757bff49SJaehoon Chung /* 636f33c9305SPavel Machek * If host->get_mmc_clk isn't defined, 637757bff49SJaehoon Chung * then assume that host->bus_hz is source clock value. 638f33c9305SPavel Machek * host->bus_hz should be set by user. 639757bff49SJaehoon Chung */ 640b44fe83aSJaehoon Chung if (host->get_mmc_clk) 641e3563f2eSSimon Glass sclk = host->get_mmc_clk(host, freq); 642757bff49SJaehoon Chung else if (host->bus_hz) 643757bff49SJaehoon Chung sclk = host->bus_hz; 644757bff49SJaehoon Chung else { 6451c87ffe8SSimon Glass debug("%s: Didn't get source clock value.\n", __func__); 646757bff49SJaehoon Chung return -EINVAL; 647757bff49SJaehoon Chung } 648757bff49SJaehoon Chung 649ddeaf211SJason Zhu if (sclk == 0) 650ddeaf211SJason Zhu return -EINVAL; 651ddeaf211SJason Zhu 6526ace153dSChin Liang See if (sclk == freq) 6536ace153dSChin Liang See div = 0; /* bypass mode */ 6546ace153dSChin Liang See else 655757bff49SJaehoon Chung div = DIV_ROUND_UP(sclk, 2 * freq); 656757bff49SJaehoon Chung 657757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CLKENA, 0); 658757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CLKSRC, 0); 659757bff49SJaehoon Chung 660757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CLKDIV, div); 661757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT | 662757bff49SJaehoon Chung DWMCI_CMD_UPD_CLK | DWMCI_CMD_START); 663757bff49SJaehoon Chung 664757bff49SJaehoon Chung do { 665757bff49SJaehoon Chung status = dwmci_readl(host, DWMCI_CMD); 666757bff49SJaehoon Chung if (timeout-- < 0) { 6671c87ffe8SSimon Glass debug("%s: Timeout!\n", __func__); 668757bff49SJaehoon Chung return -ETIMEDOUT; 669757bff49SJaehoon Chung } 670757bff49SJaehoon Chung } while (status & DWMCI_CMD_START); 671757bff49SJaehoon Chung 672757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CLKENA, DWMCI_CLKEN_ENABLE | 673757bff49SJaehoon Chung DWMCI_CLKEN_LOW_PWR); 674757bff49SJaehoon Chung 675757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT | 676757bff49SJaehoon Chung DWMCI_CMD_UPD_CLK | DWMCI_CMD_START); 677757bff49SJaehoon Chung 678757bff49SJaehoon Chung timeout = 10000; 679757bff49SJaehoon Chung do { 680757bff49SJaehoon Chung status = dwmci_readl(host, DWMCI_CMD); 681757bff49SJaehoon Chung if (timeout-- < 0) { 6821c87ffe8SSimon Glass debug("%s: Timeout!\n", __func__); 683757bff49SJaehoon Chung return -ETIMEDOUT; 684757bff49SJaehoon Chung } 685757bff49SJaehoon Chung } while (status & DWMCI_CMD_START); 686757bff49SJaehoon Chung 687757bff49SJaehoon Chung host->clock = freq; 688757bff49SJaehoon Chung 689757bff49SJaehoon Chung return 0; 690757bff49SJaehoon Chung } 691757bff49SJaehoon Chung 692e7881d85SSimon Glass #ifdef CONFIG_DM_MMC 693ba0e56e1SZiyuan Xu static bool dwmci_card_busy(struct udevice *dev) 694ba0e56e1SZiyuan Xu { 695ba0e56e1SZiyuan Xu struct mmc *mmc = mmc_get_mmc_dev(dev); 696ba0e56e1SZiyuan Xu #else 697ba0e56e1SZiyuan Xu static bool dwmci_card_busy(struct mmc *mmc) 698ba0e56e1SZiyuan Xu { 699ba0e56e1SZiyuan Xu #endif 700ba0e56e1SZiyuan Xu u32 status; 701ba0e56e1SZiyuan Xu struct dwmci_host *host = (struct dwmci_host *)mmc->priv; 702ba0e56e1SZiyuan Xu 703ba0e56e1SZiyuan Xu /* 704ba0e56e1SZiyuan Xu * Check the busy bit which is low when DAT[3:0] 705ba0e56e1SZiyuan Xu * (the data lines) are 0000 706ba0e56e1SZiyuan Xu */ 707ba0e56e1SZiyuan Xu status = dwmci_readl(host, DWMCI_STATUS); 708ba0e56e1SZiyuan Xu 709ba0e56e1SZiyuan Xu return !!(status & DWMCI_BUSY); 710ba0e56e1SZiyuan Xu } 711ba0e56e1SZiyuan Xu 712ba0e56e1SZiyuan Xu #ifdef CONFIG_DM_MMC 7138c921dceSZiyuan Xu static int dwmci_execute_tuning(struct udevice *dev, u32 opcode) 7148c921dceSZiyuan Xu { 7158c921dceSZiyuan Xu struct mmc *mmc = mmc_get_mmc_dev(dev); 7168c921dceSZiyuan Xu #else 7178c921dceSZiyuan Xu static int dwmci_execute_tuning(struct mmc *mmc, u32 opcode) 7188c921dceSZiyuan Xu { 7198c921dceSZiyuan Xu #endif 7208c921dceSZiyuan Xu struct dwmci_host *host = (struct dwmci_host *)mmc->priv; 7218c921dceSZiyuan Xu 7228c921dceSZiyuan Xu if (!host->execute_tuning) 7238c921dceSZiyuan Xu return -EIO; 7248c921dceSZiyuan Xu 7258c921dceSZiyuan Xu return host->execute_tuning(host, opcode); 7268c921dceSZiyuan Xu } 7278c921dceSZiyuan Xu 7288c921dceSZiyuan Xu #ifdef CONFIG_DM_MMC 7295628347fSJaehoon Chung static int dwmci_set_ios(struct udevice *dev) 730691272feSSimon Glass { 731691272feSSimon Glass struct mmc *mmc = mmc_get_mmc_dev(dev); 732691272feSSimon Glass #else 73307b0b9c0SJaehoon Chung static int dwmci_set_ios(struct mmc *mmc) 734757bff49SJaehoon Chung { 735691272feSSimon Glass #endif 736045bdcd0SJaehoon Chung struct dwmci_host *host = (struct dwmci_host *)mmc->priv; 737045bdcd0SJaehoon Chung u32 ctype, regs; 738757bff49SJaehoon Chung 739757bff49SJaehoon Chung debug("Buswidth = %d, clock: %d\n", mmc->bus_width, mmc->clock); 740757bff49SJaehoon Chung 741757bff49SJaehoon Chung dwmci_setup_bus(host, mmc->clock); 742757bff49SJaehoon Chung switch (mmc->bus_width) { 743757bff49SJaehoon Chung case 8: 744757bff49SJaehoon Chung ctype = DWMCI_CTYPE_8BIT; 745757bff49SJaehoon Chung break; 746757bff49SJaehoon Chung case 4: 747757bff49SJaehoon Chung ctype = DWMCI_CTYPE_4BIT; 748757bff49SJaehoon Chung break; 749757bff49SJaehoon Chung default: 750757bff49SJaehoon Chung ctype = DWMCI_CTYPE_1BIT; 751757bff49SJaehoon Chung break; 752757bff49SJaehoon Chung } 753757bff49SJaehoon Chung 754757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CTYPE, ctype); 755757bff49SJaehoon Chung 756045bdcd0SJaehoon Chung regs = dwmci_readl(host, DWMCI_UHS_REG); 757caa21a21SZiyuan Xu if (mmc_card_ddr(mmc)) 758045bdcd0SJaehoon Chung regs |= DWMCI_DDR_MODE; 759045bdcd0SJaehoon Chung else 760afc9e2b5SJaehoon Chung regs &= ~DWMCI_DDR_MODE; 761045bdcd0SJaehoon Chung 762045bdcd0SJaehoon Chung dwmci_writel(host, DWMCI_UHS_REG, regs); 763045bdcd0SJaehoon Chung 764757bff49SJaehoon Chung if (host->clksel) 765757bff49SJaehoon Chung host->clksel(host); 76607b0b9c0SJaehoon Chung 767691272feSSimon Glass return 0; 768757bff49SJaehoon Chung } 769757bff49SJaehoon Chung 770757bff49SJaehoon Chung static int dwmci_init(struct mmc *mmc) 771757bff49SJaehoon Chung { 77293bfd616SPantelis Antoniou struct dwmci_host *host = mmc->priv; 77339abf9c1SPaweł Jarosz uint32_t use_dma; 77433e40bacSJason Zhu uint32_t verid; 775757bff49SJaehoon Chung 776a32c3f92SYifeng Zhao #if defined(CONFIG_DM_GPIO) && (defined(CONFIG_SPL_GPIO_SUPPORT) || !defined(CONFIG_SPL_BUILD)) 777a32c3f92SYifeng Zhao struct gpio_desc pwr_en_gpio; 778a32c3f92SYifeng Zhao u32 delay_ms; 779a32c3f92SYifeng Zhao 780a32c3f92SYifeng Zhao if (mmc_getcd(mmc) == 1 && 781a32c3f92SYifeng Zhao !gpio_request_by_name(mmc->dev, "pwr-en-gpios", 0, &pwr_en_gpio, GPIOD_IS_OUT)) { 782a32c3f92SYifeng Zhao dm_gpio_set_value(&pwr_en_gpio, 0); 783a32c3f92SYifeng Zhao pinctrl_select_state(mmc->dev, "idle"); 784a32c3f92SYifeng Zhao delay_ms = dev_read_u32_default(mmc->dev, "power-off-delay-ms", 200); 785a32c3f92SYifeng Zhao mdelay(delay_ms); 786a32c3f92SYifeng Zhao dm_gpio_set_value(&pwr_en_gpio, 1); 787a32c3f92SYifeng Zhao pinctrl_select_state(mmc->dev, "default"); 788a32c3f92SYifeng Zhao dm_gpio_free(mmc->dev, &pwr_en_gpio); 789a32c3f92SYifeng Zhao } 790a32c3f92SYifeng Zhao #endif 791a32c3f92SYifeng Zhao 79218ab6755SJaehoon Chung if (host->board_init) 79318ab6755SJaehoon Chung host->board_init(host); 794204f7c39SJason Zhu #ifdef CONFIG_ARCH_ROCKCHIP 795204f7c39SJason Zhu if (host->dev_index == 0) 796757bff49SJaehoon Chung dwmci_writel(host, DWMCI_PWREN, 1); 797204f7c39SJason Zhu else if (host->dev_index == 1) 798f88a10fcSYifeng Zhao dwmci_writel(host, DWMCI_PWREN, CONFIG_MMC_DW_PWREN_VALUE); 799204f7c39SJason Zhu else 800204f7c39SJason Zhu dwmci_writel(host, DWMCI_PWREN, 1); 801204f7c39SJason Zhu #else 802204f7c39SJason Zhu dwmci_writel(host, DWMCI_PWREN, 1); 803204f7c39SJason Zhu #endif 804757bff49SJaehoon Chung 80533e40bacSJason Zhu verid = dwmci_readl(host, DWMCI_VERID) & 0x0000ffff; 80633e40bacSJason Zhu if (verid >= DW_MMC_240A) 80733e40bacSJason Zhu dwmci_writel(host, DWMCI_CARDTHRCTL, DWMCI_CDTHRCTRL_CONFIG); 80833e40bacSJason Zhu 809757bff49SJaehoon Chung if (!dwmci_wait_reset(host, DWMCI_RESET_ALL)) { 8101c87ffe8SSimon Glass debug("%s[%d] Fail-reset!!\n", __func__, __LINE__); 8111c87ffe8SSimon Glass return -EIO; 812757bff49SJaehoon Chung } 813757bff49SJaehoon Chung 81439abf9c1SPaweł Jarosz use_dma = SDMMC_GET_TRANS_MODE(dwmci_readl(host, DWMCI_HCON)); 81539abf9c1SPaweł Jarosz if (use_dma == DMA_INTERFACE_IDMA) { 81639abf9c1SPaweł Jarosz host->fifo_mode = 0; 81739abf9c1SPaweł Jarosz } else { 81839abf9c1SPaweł Jarosz host->fifo_mode = 1; 81939abf9c1SPaweł Jarosz } 82039abf9c1SPaweł Jarosz 8219c50e35fSAmar /* Enumerate at 400KHz */ 82293bfd616SPantelis Antoniou dwmci_setup_bus(host, mmc->cfg->f_min); 8239c50e35fSAmar 824757bff49SJaehoon Chung dwmci_writel(host, DWMCI_RINTSTS, 0xFFFFFFFF); 825757bff49SJaehoon Chung dwmci_writel(host, DWMCI_INTMASK, 0); 826757bff49SJaehoon Chung 827757bff49SJaehoon Chung dwmci_writel(host, DWMCI_TMOUT, 0xFFFFFFFF); 828757bff49SJaehoon Chung 829757bff49SJaehoon Chung dwmci_writel(host, DWMCI_IDINTEN, 0); 830757bff49SJaehoon Chung dwmci_writel(host, DWMCI_BMOD, 1); 831757bff49SJaehoon Chung 832760177dfSSimon Glass if (!host->fifoth_val) { 833760177dfSSimon Glass uint32_t fifo_size; 834760177dfSSimon Glass 835760177dfSSimon Glass fifo_size = dwmci_readl(host, DWMCI_FIFOTH); 836760177dfSSimon Glass fifo_size = ((fifo_size & RX_WMARK_MASK) >> RX_WMARK_SHIFT) + 1; 8375ef89808SJason Zhu host->fifoth_val = MSIZE(DWMCI_MSIZE) | 8385ef89808SJason Zhu RX_WMARK(fifo_size / 2 - 1) | 839760177dfSSimon Glass TX_WMARK(fifo_size / 2); 8409108b315SAlexey Brodkin } 841760177dfSSimon Glass dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val); 842757bff49SJaehoon Chung 843757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CLKENA, 0); 844757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CLKSRC, 0); 845757bff49SJaehoon Chung 846757bff49SJaehoon Chung return 0; 847757bff49SJaehoon Chung } 848757bff49SJaehoon Chung 8495743ef64SJason Zhu static int dwmci_get_cd(struct udevice *dev) 8505743ef64SJason Zhu { 8515743ef64SJason Zhu int ret = -1; 8523e4b5e88SYifeng Zhao struct mmc *mmc = mmc_get_mmc_dev(dev); 8533e4b5e88SYifeng Zhao struct dwmci_host *host = mmc->priv; 854dc58c997SJason Zhu 855dc58c997SJason Zhu #if defined(CONFIG_DM_GPIO) && (defined(CONFIG_SPL_GPIO_SUPPORT) || !defined(CONFIG_SPL_BUILD)) 8565743ef64SJason Zhu struct gpio_desc detect; 8575743ef64SJason Zhu 8585743ef64SJason Zhu ret = gpio_request_by_name(dev, "cd-gpios", 0, &detect, GPIOD_IS_IN); 8595743ef64SJason Zhu if (ret) { 8603e4b5e88SYifeng Zhao goto dw_mmc_cdetect; 8615743ef64SJason Zhu } 8625743ef64SJason Zhu 8635743ef64SJason Zhu ret = !dm_gpio_get_value(&detect); 864850d604aSJason Zhu dm_gpio_free(dev, &detect); 8653e4b5e88SYifeng Zhao return ret; 8663e4b5e88SYifeng Zhao dw_mmc_cdetect: 8675743ef64SJason Zhu #endif 8683e4b5e88SYifeng Zhao ret = (dwmci_readl(host, DWMCI_CDETECT) & (1 << 0)) == 0 ? 1 : 0; 8693e4b5e88SYifeng Zhao 8705743ef64SJason Zhu return ret; 8715743ef64SJason Zhu } 8725743ef64SJason Zhu 873e7881d85SSimon Glass #ifdef CONFIG_DM_MMC 874691272feSSimon Glass int dwmci_probe(struct udevice *dev) 875691272feSSimon Glass { 876691272feSSimon Glass struct mmc *mmc = mmc_get_mmc_dev(dev); 877691272feSSimon Glass 878691272feSSimon Glass return dwmci_init(mmc); 879691272feSSimon Glass } 880691272feSSimon Glass 881691272feSSimon Glass const struct dm_mmc_ops dm_dwmci_ops = { 882ba0e56e1SZiyuan Xu .card_busy = dwmci_card_busy, 883691272feSSimon Glass .send_cmd = dwmci_send_cmd, 88447f7fd3aSJason Zhu #ifdef CONFIG_SPL_BLK_READ_PREPARE 88547f7fd3aSJason Zhu .send_cmd_prepare = dwmci_send_cmd_prepare, 88647f7fd3aSJason Zhu #endif 887691272feSSimon Glass .set_ios = dwmci_set_ios, 8885743ef64SJason Zhu .get_cd = dwmci_get_cd, 8898c921dceSZiyuan Xu .execute_tuning = dwmci_execute_tuning, 890691272feSSimon Glass }; 891691272feSSimon Glass 892691272feSSimon Glass #else 893ab769f22SPantelis Antoniou static const struct mmc_ops dwmci_ops = { 894ba0e56e1SZiyuan Xu .card_busy = dwmci_card_busy, 895ab769f22SPantelis Antoniou .send_cmd = dwmci_send_cmd, 896ab769f22SPantelis Antoniou .set_ios = dwmci_set_ios, 8975743ef64SJason Zhu .get_cd = dwmci_get_cd, 898ab769f22SPantelis Antoniou .init = dwmci_init, 8998c921dceSZiyuan Xu .execute_tuning = dwmci_execute_tuning, 900ab769f22SPantelis Antoniou }; 901691272feSSimon Glass #endif 902ab769f22SPantelis Antoniou 903e5113c33SJaehoon Chung void dwmci_setup_cfg(struct mmc_config *cfg, struct dwmci_host *host, 904e5113c33SJaehoon Chung u32 max_clk, u32 min_clk) 9055e6ff810SSimon Glass { 906e5113c33SJaehoon Chung cfg->name = host->name; 907e7881d85SSimon Glass #ifndef CONFIG_DM_MMC 9085e6ff810SSimon Glass cfg->ops = &dwmci_ops; 909691272feSSimon Glass #endif 9105e6ff810SSimon Glass cfg->f_min = min_clk; 9115e6ff810SSimon Glass cfg->f_max = max_clk; 9125e6ff810SSimon Glass 9135e6ff810SSimon Glass cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; 9145e6ff810SSimon Glass 915e5113c33SJaehoon Chung cfg->host_caps = host->caps; 9165e6ff810SSimon Glass 91780ac95fcSZiyuan Xu switch (host->buswidth) { 91880ac95fcSZiyuan Xu case 8: 919c1cfa99bSZiyuan Xu cfg->host_caps |= MMC_MODE_8BIT | MMC_MODE_4BIT; 92080ac95fcSZiyuan Xu break; 92180ac95fcSZiyuan Xu case 4: 9225e6ff810SSimon Glass cfg->host_caps |= MMC_MODE_4BIT; 9235e6ff810SSimon Glass cfg->host_caps &= ~MMC_MODE_8BIT; 92480ac95fcSZiyuan Xu break; 92580ac95fcSZiyuan Xu case 1: 92680ac95fcSZiyuan Xu cfg->host_caps &= ~MMC_MODE_4BIT; 92780ac95fcSZiyuan Xu cfg->host_caps &= ~MMC_MODE_8BIT; 92880ac95fcSZiyuan Xu break; 92980ac95fcSZiyuan Xu default: 93080ac95fcSZiyuan Xu printf("Unsupported bus width: %d\n", host->buswidth); 93180ac95fcSZiyuan Xu break; 9325e6ff810SSimon Glass } 9335e6ff810SSimon Glass cfg->host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz; 9345e6ff810SSimon Glass 9355e6ff810SSimon Glass cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; 9365e6ff810SSimon Glass } 9375e6ff810SSimon Glass 9385e6ff810SSimon Glass #ifdef CONFIG_BLK 9395e6ff810SSimon Glass int dwmci_bind(struct udevice *dev, struct mmc *mmc, struct mmc_config *cfg) 9405e6ff810SSimon Glass { 9415e6ff810SSimon Glass return mmc_bind(dev, mmc, cfg); 9425e6ff810SSimon Glass } 9435e6ff810SSimon Glass #else 944757bff49SJaehoon Chung int add_dwmci(struct dwmci_host *host, u32 max_clk, u32 min_clk) 945757bff49SJaehoon Chung { 946e5113c33SJaehoon Chung dwmci_setup_cfg(&host->cfg, host, max_clk, min_clk); 947757bff49SJaehoon Chung 94893bfd616SPantelis Antoniou host->mmc = mmc_create(&host->cfg, host); 94993bfd616SPantelis Antoniou if (host->mmc == NULL) 95093bfd616SPantelis Antoniou return -1; 95193bfd616SPantelis Antoniou 95293bfd616SPantelis Antoniou return 0; 953757bff49SJaehoon Chung } 9545e6ff810SSimon Glass #endif 955