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> 111c87ffe8SSimon Glass #include <errno.h> 12757bff49SJaehoon Chung #include <malloc.h> 13cf92e05cSSimon Glass #include <memalign.h> 14757bff49SJaehoon Chung #include <mmc.h> 15757bff49SJaehoon Chung #include <dwmmc.h> 165743ef64SJason Zhu #ifdef CONFIG_DM_GPIO 175743ef64SJason Zhu #include <asm/gpio.h> 185743ef64SJason Zhu #include <asm-generic/gpio.h> 195743ef64SJason Zhu #endif 20757bff49SJaehoon Chung 21757bff49SJaehoon Chung #define PAGE_SIZE 4096 22757bff49SJaehoon Chung 23bda599f7SShawn Lin /* 24bda599f7SShawn Lin * Currently it supports read/write up to 8*8*4 Bytes per 25bda599f7SShawn Lin * stride as a burst mode. Please note that if you change 26bda599f7SShawn Lin * MAX_STRIDE, you should also update dwmci_memcpy_fromio 27bda599f7SShawn Lin * to augment the groups of {ldm, stm}. 28bda599f7SShawn Lin */ 29bda599f7SShawn Lin #define MAX_STRIDE 64 30bda599f7SShawn Lin #if CONFIG_ARM && CONFIG_CPU_V7 31bda599f7SShawn Lin void noinline dwmci_memcpy_fromio(void *buffer, void *fifo_addr) 32bda599f7SShawn Lin { 33bda599f7SShawn Lin __asm__ __volatile__ ( 34bda599f7SShawn Lin "push {r2, r3, r4, r5, r6, r7, r8, r9}\n" 35bda599f7SShawn Lin "ldm r1, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 36bda599f7SShawn Lin "stm r0!, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 37bda599f7SShawn Lin "ldm r1, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 38bda599f7SShawn Lin "stm r0!, {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 "pop {r2, r3, r4, r5, r6,r7,r8,r9}\n" 52bda599f7SShawn Lin :::"memory" 53bda599f7SShawn Lin ); 54bda599f7SShawn Lin } 55bda599f7SShawn Lin 56bda599f7SShawn Lin void noinline dwmci_memcpy_toio(void *buffer, void *fifo_addr) 57bda599f7SShawn Lin { 5812ee84b1SShawn Lin __asm__ __volatile__ ( 5912ee84b1SShawn Lin "push {r2, r3, r4, r5, r6, r7, r8, r9}\n" 6012ee84b1SShawn Lin "ldm r0!, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 6112ee84b1SShawn Lin "stm r1, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 6212ee84b1SShawn Lin "ldm r0!, {r2,r3,r4,r5,r6,r7,r8,r9}\n" 6312ee84b1SShawn Lin "stm r1, {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 "pop {r2, r3, r4, r5, r6,r7,r8,r9}\n" 7712ee84b1SShawn Lin :::"memory" 7812ee84b1SShawn Lin ); 79bda599f7SShawn Lin } 80bda599f7SShawn Lin #else 81bda599f7SShawn Lin void dwmci_memcpy_fromio(void *buffer, void *fifo_addr) {}; 82bda599f7SShawn Lin void dwmci_memcpy_toio(void *buffer, void *fifo_addr) {}; 83bda599f7SShawn Lin #endif 84757bff49SJaehoon Chung static int dwmci_wait_reset(struct dwmci_host *host, u32 value) 85757bff49SJaehoon Chung { 86757bff49SJaehoon Chung unsigned long timeout = 1000; 87757bff49SJaehoon Chung u32 ctrl; 88757bff49SJaehoon Chung 89757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CTRL, value); 90757bff49SJaehoon Chung 91757bff49SJaehoon Chung while (timeout--) { 92757bff49SJaehoon Chung ctrl = dwmci_readl(host, DWMCI_CTRL); 93757bff49SJaehoon Chung if (!(ctrl & DWMCI_RESET_ALL)) 94757bff49SJaehoon Chung return 1; 95757bff49SJaehoon Chung } 96757bff49SJaehoon Chung return 0; 97757bff49SJaehoon Chung } 98757bff49SJaehoon Chung 99757bff49SJaehoon Chung static void dwmci_set_idma_desc(struct dwmci_idmac *idmac, 100757bff49SJaehoon Chung u32 desc0, u32 desc1, u32 desc2) 101757bff49SJaehoon Chung { 102757bff49SJaehoon Chung struct dwmci_idmac *desc = idmac; 103757bff49SJaehoon Chung 104757bff49SJaehoon Chung desc->flags = desc0; 105757bff49SJaehoon Chung desc->cnt = desc1; 106757bff49SJaehoon Chung desc->addr = desc2; 10741f7be3cSPrabhakar Kushwaha desc->next_addr = (ulong)desc + sizeof(struct dwmci_idmac); 108757bff49SJaehoon Chung } 109757bff49SJaehoon Chung 110757bff49SJaehoon Chung static void dwmci_prepare_data(struct dwmci_host *host, 1112a7a210eSAlexey Brodkin struct mmc_data *data, 1122a7a210eSAlexey Brodkin struct dwmci_idmac *cur_idmac, 1132a7a210eSAlexey Brodkin void *bounce_buffer) 114757bff49SJaehoon Chung { 115757bff49SJaehoon Chung unsigned long ctrl; 116757bff49SJaehoon Chung unsigned int i = 0, flags, cnt, blk_cnt; 1172a7a210eSAlexey Brodkin ulong data_start, data_end; 118757bff49SJaehoon Chung 119757bff49SJaehoon Chung 120757bff49SJaehoon Chung blk_cnt = data->blocks; 121757bff49SJaehoon Chung 122757bff49SJaehoon Chung dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET); 123757bff49SJaehoon Chung 124757bff49SJaehoon Chung data_start = (ulong)cur_idmac; 12541f7be3cSPrabhakar Kushwaha dwmci_writel(host, DWMCI_DBADDR, (ulong)cur_idmac); 126757bff49SJaehoon Chung 127757bff49SJaehoon Chung do { 128757bff49SJaehoon Chung flags = DWMCI_IDMAC_OWN | DWMCI_IDMAC_CH ; 129757bff49SJaehoon Chung flags |= (i == 0) ? DWMCI_IDMAC_FS : 0; 130757bff49SJaehoon Chung if (blk_cnt <= 8) { 131757bff49SJaehoon Chung flags |= DWMCI_IDMAC_LD; 132757bff49SJaehoon Chung cnt = data->blocksize * blk_cnt; 133757bff49SJaehoon Chung } else 134757bff49SJaehoon Chung cnt = data->blocksize * 8; 135757bff49SJaehoon Chung 136757bff49SJaehoon Chung dwmci_set_idma_desc(cur_idmac, flags, cnt, 13741f7be3cSPrabhakar Kushwaha (ulong)bounce_buffer + (i * PAGE_SIZE)); 138757bff49SJaehoon Chung 13921bd5761SMischa Jonker if (blk_cnt <= 8) 140757bff49SJaehoon Chung break; 141757bff49SJaehoon Chung blk_cnt -= 8; 142757bff49SJaehoon Chung cur_idmac++; 143757bff49SJaehoon Chung i++; 144757bff49SJaehoon Chung } while(1); 145757bff49SJaehoon Chung 146757bff49SJaehoon Chung data_end = (ulong)cur_idmac; 147757bff49SJaehoon Chung flush_dcache_range(data_start, data_end + ARCH_DMA_MINALIGN); 148757bff49SJaehoon Chung 149757bff49SJaehoon Chung ctrl = dwmci_readl(host, DWMCI_CTRL); 150757bff49SJaehoon Chung ctrl |= DWMCI_IDMAC_EN | DWMCI_DMA_EN; 151757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CTRL, ctrl); 152757bff49SJaehoon Chung 153757bff49SJaehoon Chung ctrl = dwmci_readl(host, DWMCI_BMOD); 154757bff49SJaehoon Chung ctrl |= DWMCI_BMOD_IDMAC_FB | DWMCI_BMOD_IDMAC_EN; 155757bff49SJaehoon Chung dwmci_writel(host, DWMCI_BMOD, ctrl); 156757bff49SJaehoon Chung 157757bff49SJaehoon Chung dwmci_writel(host, DWMCI_BLKSIZ, data->blocksize); 158757bff49SJaehoon Chung dwmci_writel(host, DWMCI_BYTCNT, data->blocksize * data->blocks); 159757bff49SJaehoon Chung } 160757bff49SJaehoon Chung 16134d21c9aSJason Zhu static unsigned int dwmci_get_timeout(struct mmc *mmc, const unsigned int size) 16234d21c9aSJason Zhu { 16334d21c9aSJason Zhu unsigned int timeout; 16434d21c9aSJason Zhu 16534d21c9aSJason Zhu timeout = size * 8; /* counting in bits */ 16634d21c9aSJason Zhu timeout *= 10; /* wait 10 times as long */ 16734d21c9aSJason Zhu timeout /= mmc->clock; 16834d21c9aSJason Zhu timeout /= mmc->bus_width; 16934d21c9aSJason Zhu timeout *= 1000; /* counting in msec */ 17034d21c9aSJason Zhu timeout = (timeout < 10000) ? 10000 : timeout; 17134d21c9aSJason Zhu 17234d21c9aSJason Zhu return timeout; 17334d21c9aSJason Zhu } 17434d21c9aSJason Zhu 175a65f51b9Shuang lin static int dwmci_data_transfer(struct dwmci_host *host, struct mmc_data *data) 176f382eb83Shuang lin { 177f382eb83Shuang lin int ret = 0; 178f7c0370cSJason Zhu int reset_timeout = 100; 17934d21c9aSJason Zhu u32 timeout, status, ctrl, mask, size, i, len = 0; 180a65f51b9Shuang lin u32 *buf = NULL; 181f382eb83Shuang lin ulong start = get_timer(0); 182a65f51b9Shuang lin u32 fifo_depth = (((host->fifoth_val & RX_WMARK_MASK) >> 183a65f51b9Shuang lin RX_WMARK_SHIFT) + 1) * 2; 184bda599f7SShawn Lin bool stride; 185a65f51b9Shuang lin 18634d21c9aSJason Zhu size = data->blocksize * data->blocks; 187bda599f7SShawn Lin /* Still use legacy PIO mode if size < 512(128 * 4) Bytes */ 188bda599f7SShawn Lin stride = host->stride_pio && size > 128; 189a65f51b9Shuang lin if (data->flags == MMC_DATA_READ) 190a65f51b9Shuang lin buf = (unsigned int *)data->dest; 191a65f51b9Shuang lin else 192a65f51b9Shuang lin buf = (unsigned int *)data->src; 193f382eb83Shuang lin 19434d21c9aSJason Zhu timeout = dwmci_get_timeout(host->mmc, size); 19534d21c9aSJason Zhu size /= 4; 19634d21c9aSJason Zhu 197f382eb83Shuang lin for (;;) { 198f382eb83Shuang lin mask = dwmci_readl(host, DWMCI_RINTSTS); 199f382eb83Shuang lin /* Error during data transfer. */ 200f382eb83Shuang lin if (mask & (DWMCI_DATA_ERR | DWMCI_DATA_TOUT)) { 201f382eb83Shuang lin debug("%s: DATA ERROR!\n", __func__); 2020d797f18SZiyuan Xu dwmci_wait_reset(host, DWMCI_RESET_ALL); 2030d797f18SZiyuan Xu dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT | 2040d797f18SZiyuan Xu DWMCI_CMD_UPD_CLK | DWMCI_CMD_START); 2050d797f18SZiyuan Xu 2060d797f18SZiyuan Xu do { 2070d797f18SZiyuan Xu status = dwmci_readl(host, DWMCI_CMD); 208f7c0370cSJason Zhu if (reset_timeout-- < 0) 209f7c0370cSJason Zhu break; 210f7c0370cSJason Zhu udelay(100); 2110d797f18SZiyuan Xu } while (status & DWMCI_CMD_START); 2120d797f18SZiyuan Xu 2130d797f18SZiyuan Xu if (!host->fifo_mode) { 2140d797f18SZiyuan Xu ctrl = dwmci_readl(host, DWMCI_BMOD); 2150d797f18SZiyuan Xu ctrl |= DWMCI_BMOD_IDMAC_RESET; 2160d797f18SZiyuan Xu dwmci_writel(host, DWMCI_BMOD, ctrl); 2170d797f18SZiyuan Xu } 2180d797f18SZiyuan Xu 219f382eb83Shuang lin ret = -EINVAL; 220f382eb83Shuang lin break; 221f382eb83Shuang lin } 222f382eb83Shuang lin 223a65f51b9Shuang lin if (host->fifo_mode && size) { 224720724d0SXu Ziyuan len = 0; 2252b429033SJacob Chen if (data->flags == MMC_DATA_READ && 2262b429033SJacob Chen (mask & DWMCI_INTMSK_RXDR)) { 2272b429033SJacob Chen while (size) { 228a65f51b9Shuang lin len = dwmci_readl(host, DWMCI_STATUS); 229a65f51b9Shuang lin len = (len >> DWMCI_FIFO_SHIFT) & 230a65f51b9Shuang lin DWMCI_FIFO_MASK; 2312990e07aSXu Ziyuan len = min(size, len); 232bda599f7SShawn Lin if (!stride) { 233bda599f7SShawn Lin /* Legacy pio mode */ 234a65f51b9Shuang lin for (i = 0; i < len; i++) 235bda599f7SShawn Lin *buf++ = dwmci_readl(host, DWMCI_DATA); 236bda599f7SShawn Lin goto read_again; 237bda599f7SShawn Lin } 238bda599f7SShawn Lin 239bda599f7SShawn Lin /* dwmci_memcpy_fromio now bursts 256 Bytes once */ 240bda599f7SShawn Lin if (len < MAX_STRIDE) 241bda599f7SShawn Lin continue; 242bda599f7SShawn Lin 243bda599f7SShawn Lin for (i = 0; i < len / MAX_STRIDE; i++) { 244bda599f7SShawn Lin dwmci_memcpy_fromio(buf, host->ioaddr + DWMCI_DATA); 245bda599f7SShawn Lin buf += MAX_STRIDE; 246bda599f7SShawn Lin } 247bda599f7SShawn Lin 248bda599f7SShawn Lin len = i * MAX_STRIDE; 249bda599f7SShawn Lin read_again: 2502b429033SJacob Chen size = size > len ? (size - len) : 0; 2512b429033SJacob Chen } 252a65f51b9Shuang lin dwmci_writel(host, DWMCI_RINTSTS, 253a65f51b9Shuang lin DWMCI_INTMSK_RXDR); 2542b429033SJacob Chen } else if (data->flags == MMC_DATA_WRITE && 2552b429033SJacob Chen (mask & DWMCI_INTMSK_TXDR)) { 2562b429033SJacob Chen while (size) { 257a65f51b9Shuang lin len = dwmci_readl(host, DWMCI_STATUS); 258a65f51b9Shuang lin len = fifo_depth - ((len >> 259a65f51b9Shuang lin DWMCI_FIFO_SHIFT) & 260a65f51b9Shuang lin DWMCI_FIFO_MASK); 2612990e07aSXu Ziyuan len = min(size, len); 262bda599f7SShawn Lin if (!stride) { 263a65f51b9Shuang lin for (i = 0; i < len; i++) 264a65f51b9Shuang lin dwmci_writel(host, DWMCI_DATA, 265a65f51b9Shuang lin *buf++); 266bda599f7SShawn Lin goto write_again; 267bda599f7SShawn Lin } 268bda599f7SShawn Lin /* dwmci_memcpy_toio now bursts 256 Bytes once */ 269bda599f7SShawn Lin if (len < MAX_STRIDE) 270bda599f7SShawn Lin continue; 271bda599f7SShawn Lin 272bda599f7SShawn Lin for (i = 0; i < len / MAX_STRIDE; i++) { 273bda599f7SShawn Lin dwmci_memcpy_toio(buf, host->ioaddr + DWMCI_DATA); 274bda599f7SShawn Lin buf += MAX_STRIDE; 275bda599f7SShawn Lin } 276bda599f7SShawn Lin 277bda599f7SShawn Lin len = i * MAX_STRIDE; 278bda599f7SShawn Lin write_again: 2792b429033SJacob Chen size = size > len ? (size - len) : 0; 2802b429033SJacob Chen } 281a65f51b9Shuang lin dwmci_writel(host, DWMCI_RINTSTS, 282a65f51b9Shuang lin DWMCI_INTMSK_TXDR); 283a65f51b9Shuang lin } 284a65f51b9Shuang lin } 285a65f51b9Shuang lin 286f382eb83Shuang lin /* Data arrived correctly. */ 287f382eb83Shuang lin if (mask & DWMCI_INTMSK_DTO) { 288f382eb83Shuang lin ret = 0; 289f382eb83Shuang lin break; 290f382eb83Shuang lin } 291f382eb83Shuang lin 292f382eb83Shuang lin /* Check for timeout. */ 293f382eb83Shuang lin if (get_timer(start) > timeout) { 294f382eb83Shuang lin debug("%s: Timeout waiting for data!\n", 295f382eb83Shuang lin __func__); 296915ffa52SJaehoon Chung ret = -ETIMEDOUT; 297f382eb83Shuang lin break; 298f382eb83Shuang lin } 299f382eb83Shuang lin } 300f382eb83Shuang lin 301f382eb83Shuang lin dwmci_writel(host, DWMCI_RINTSTS, mask); 302f382eb83Shuang lin 303f382eb83Shuang lin return ret; 304f382eb83Shuang lin } 305f382eb83Shuang lin 306757bff49SJaehoon Chung static int dwmci_set_transfer_mode(struct dwmci_host *host, 307757bff49SJaehoon Chung struct mmc_data *data) 308757bff49SJaehoon Chung { 309757bff49SJaehoon Chung unsigned long mode; 310757bff49SJaehoon Chung 311757bff49SJaehoon Chung mode = DWMCI_CMD_DATA_EXP; 312757bff49SJaehoon Chung if (data->flags & MMC_DATA_WRITE) 313757bff49SJaehoon Chung mode |= DWMCI_CMD_RW; 314757bff49SJaehoon Chung 315757bff49SJaehoon Chung return mode; 316757bff49SJaehoon Chung } 317757bff49SJaehoon Chung 318e7881d85SSimon Glass #ifdef CONFIG_DM_MMC 3195628347fSJaehoon Chung static int dwmci_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, 320691272feSSimon Glass struct mmc_data *data) 321691272feSSimon Glass { 322691272feSSimon Glass struct mmc *mmc = mmc_get_mmc_dev(dev); 323691272feSSimon Glass #else 324757bff49SJaehoon Chung static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, 325757bff49SJaehoon Chung struct mmc_data *data) 326757bff49SJaehoon Chung { 327691272feSSimon Glass #endif 32893bfd616SPantelis Antoniou struct dwmci_host *host = mmc->priv; 3292136d226SMischa Jonker ALLOC_CACHE_ALIGN_BUFFER(struct dwmci_idmac, cur_idmac, 33021bd5761SMischa Jonker data ? DIV_ROUND_UP(data->blocks, 8) : 0); 3319042d974SMarek Vasut int ret = 0, flags = 0, i; 33202ebd42cSXu Ziyuan unsigned int timeout = 500; 3339b5b8b6eSAlexander Graf u32 retry = 100000; 334757bff49SJaehoon Chung u32 mask, ctrl; 3359c50e35fSAmar ulong start = get_timer(0); 3362a7a210eSAlexey Brodkin struct bounce_buffer bbstate; 337757bff49SJaehoon Chung 338757bff49SJaehoon Chung while (dwmci_readl(host, DWMCI_STATUS) & DWMCI_BUSY) { 3399c50e35fSAmar if (get_timer(start) > timeout) { 3401c87ffe8SSimon Glass debug("%s: Timeout on data busy\n", __func__); 341915ffa52SJaehoon Chung return -ETIMEDOUT; 342757bff49SJaehoon Chung } 343757bff49SJaehoon Chung } 344757bff49SJaehoon Chung 345757bff49SJaehoon Chung dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_ALL); 346757bff49SJaehoon Chung 3472a7a210eSAlexey Brodkin if (data) { 348a65f51b9Shuang lin if (host->fifo_mode) { 349a65f51b9Shuang lin dwmci_writel(host, DWMCI_BLKSIZ, data->blocksize); 350a65f51b9Shuang lin dwmci_writel(host, DWMCI_BYTCNT, 351a65f51b9Shuang lin data->blocksize * data->blocks); 352a65f51b9Shuang lin dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET); 353a65f51b9Shuang lin } else { 3542a7a210eSAlexey Brodkin if (data->flags == MMC_DATA_READ) { 3552a7a210eSAlexey Brodkin bounce_buffer_start(&bbstate, (void*)data->dest, 3562a7a210eSAlexey Brodkin data->blocksize * 3572a7a210eSAlexey Brodkin data->blocks, GEN_BB_WRITE); 3582a7a210eSAlexey Brodkin } else { 3592a7a210eSAlexey Brodkin bounce_buffer_start(&bbstate, (void*)data->src, 3602a7a210eSAlexey Brodkin data->blocksize * 3612a7a210eSAlexey Brodkin data->blocks, GEN_BB_READ); 3622a7a210eSAlexey Brodkin } 3632a7a210eSAlexey Brodkin dwmci_prepare_data(host, data, cur_idmac, 3642a7a210eSAlexey Brodkin bbstate.bounce_buffer); 3652a7a210eSAlexey Brodkin } 366a65f51b9Shuang lin } 367757bff49SJaehoon Chung 368757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CMDARG, cmd->cmdarg); 369757bff49SJaehoon Chung 370757bff49SJaehoon Chung if (data) 371757bff49SJaehoon Chung flags = dwmci_set_transfer_mode(host, data); 372757bff49SJaehoon Chung 373757bff49SJaehoon Chung if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) 374757bff49SJaehoon Chung return -1; 375757bff49SJaehoon Chung 376757bff49SJaehoon Chung if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) 377757bff49SJaehoon Chung flags |= DWMCI_CMD_ABORT_STOP; 378757bff49SJaehoon Chung else 379757bff49SJaehoon Chung flags |= DWMCI_CMD_PRV_DAT_WAIT; 380757bff49SJaehoon Chung 381757bff49SJaehoon Chung if (cmd->resp_type & MMC_RSP_PRESENT) { 382757bff49SJaehoon Chung flags |= DWMCI_CMD_RESP_EXP; 383757bff49SJaehoon Chung if (cmd->resp_type & MMC_RSP_136) 384757bff49SJaehoon Chung flags |= DWMCI_CMD_RESP_LENGTH; 385757bff49SJaehoon Chung } 386757bff49SJaehoon Chung 387757bff49SJaehoon Chung if (cmd->resp_type & MMC_RSP_CRC) 388757bff49SJaehoon Chung flags |= DWMCI_CMD_CHECK_CRC; 389757bff49SJaehoon Chung 390757bff49SJaehoon Chung flags |= (cmd->cmdidx | DWMCI_CMD_START | DWMCI_CMD_USE_HOLD_REG); 391757bff49SJaehoon Chung 392757bff49SJaehoon Chung debug("Sending CMD%d\n",cmd->cmdidx); 393757bff49SJaehoon Chung 394757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CMD, flags); 395757bff49SJaehoon Chung 396757bff49SJaehoon Chung for (i = 0; i < retry; i++) { 397757bff49SJaehoon Chung mask = dwmci_readl(host, DWMCI_RINTSTS); 398757bff49SJaehoon Chung if (mask & DWMCI_INTMSK_CDONE) { 399757bff49SJaehoon Chung if (!data) 400757bff49SJaehoon Chung dwmci_writel(host, DWMCI_RINTSTS, mask); 401757bff49SJaehoon Chung break; 402757bff49SJaehoon Chung } 403757bff49SJaehoon Chung } 404757bff49SJaehoon Chung 405f33c9305SPavel Machek if (i == retry) { 4061c87ffe8SSimon Glass debug("%s: Timeout.\n", __func__); 407915ffa52SJaehoon Chung return -ETIMEDOUT; 408f33c9305SPavel Machek } 409757bff49SJaehoon Chung 410757bff49SJaehoon Chung if (mask & DWMCI_INTMSK_RTO) { 411f33c9305SPavel Machek /* 412f33c9305SPavel Machek * Timeout here is not necessarily fatal. (e)MMC cards 413f33c9305SPavel Machek * will splat here when they receive CMD55 as they do 414f33c9305SPavel Machek * not support this command and that is exactly the way 415f33c9305SPavel Machek * to tell them apart from SD cards. Thus, this output 416f33c9305SPavel Machek * below shall be debug(). eMMC cards also do not favor 417f33c9305SPavel Machek * CMD8, please keep that in mind. 418f33c9305SPavel Machek */ 419f33c9305SPavel Machek debug("%s: Response Timeout.\n", __func__); 420915ffa52SJaehoon Chung return -ETIMEDOUT; 421757bff49SJaehoon Chung } else if (mask & DWMCI_INTMSK_RE) { 4221c87ffe8SSimon Glass debug("%s: Response Error.\n", __func__); 4231c87ffe8SSimon Glass return -EIO; 424757bff49SJaehoon Chung } 425757bff49SJaehoon Chung 426757bff49SJaehoon Chung 427757bff49SJaehoon Chung if (cmd->resp_type & MMC_RSP_PRESENT) { 428757bff49SJaehoon Chung if (cmd->resp_type & MMC_RSP_136) { 429757bff49SJaehoon Chung cmd->response[0] = dwmci_readl(host, DWMCI_RESP3); 430757bff49SJaehoon Chung cmd->response[1] = dwmci_readl(host, DWMCI_RESP2); 431757bff49SJaehoon Chung cmd->response[2] = dwmci_readl(host, DWMCI_RESP1); 432757bff49SJaehoon Chung cmd->response[3] = dwmci_readl(host, DWMCI_RESP0); 433757bff49SJaehoon Chung } else { 434757bff49SJaehoon Chung cmd->response[0] = dwmci_readl(host, DWMCI_RESP0); 435757bff49SJaehoon Chung } 436757bff49SJaehoon Chung } 437757bff49SJaehoon Chung 438757bff49SJaehoon Chung if (data) { 439a65f51b9Shuang lin ret = dwmci_data_transfer(host, data); 440757bff49SJaehoon Chung 441a65f51b9Shuang lin /* only dma mode need it */ 442a65f51b9Shuang lin if (!host->fifo_mode) { 443757bff49SJaehoon Chung ctrl = dwmci_readl(host, DWMCI_CTRL); 444757bff49SJaehoon Chung ctrl &= ~(DWMCI_DMA_EN); 445757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CTRL, ctrl); 4462a7a210eSAlexey Brodkin bounce_buffer_stop(&bbstate); 447757bff49SJaehoon Chung } 448a65f51b9Shuang lin } 449757bff49SJaehoon Chung 4509042d974SMarek Vasut return ret; 451757bff49SJaehoon Chung } 452757bff49SJaehoon Chung 45347f7fd3aSJason Zhu #ifdef CONFIG_SPL_BLK_READ_PREPARE 45447f7fd3aSJason Zhu #ifdef CONFIG_DM_MMC 45547f7fd3aSJason Zhu static int dwmci_send_cmd_prepare(struct udevice *dev, struct mmc_cmd *cmd, 45647f7fd3aSJason Zhu struct mmc_data *data) 45747f7fd3aSJason Zhu { 45847f7fd3aSJason Zhu struct mmc *mmc = mmc_get_mmc_dev(dev); 45947f7fd3aSJason Zhu #else 46047f7fd3aSJason Zhu static int dwmci_send_cmd_prepare(struct mmc *mmc, struct mmc_cmd *cmd, 46147f7fd3aSJason Zhu struct mmc_data *data) 46247f7fd3aSJason Zhu { 46347f7fd3aSJason Zhu #endif 46447f7fd3aSJason Zhu struct dwmci_host *host = mmc->priv; 46547f7fd3aSJason Zhu struct dwmci_idmac *cur_idmac; 46647f7fd3aSJason Zhu int ret = 0, flags = 0, i; 46747f7fd3aSJason Zhu unsigned int timeout = 500; 46847f7fd3aSJason Zhu u32 retry = 100000; 46947f7fd3aSJason Zhu u32 mask; 47047f7fd3aSJason Zhu ulong start = get_timer(0); 47147f7fd3aSJason Zhu struct bounce_buffer bbstate; 47247f7fd3aSJason Zhu 47347f7fd3aSJason Zhu cur_idmac = malloc(ROUND(DIV_ROUND_UP(data->blocks, 8) * 47447f7fd3aSJason Zhu sizeof(struct dwmci_idmac), 47547f7fd3aSJason Zhu ARCH_DMA_MINALIGN) + ARCH_DMA_MINALIGN - 1); 47647f7fd3aSJason Zhu if (!cur_idmac) 47747f7fd3aSJason Zhu return -ENODATA; 47847f7fd3aSJason Zhu 47947f7fd3aSJason Zhu while (dwmci_readl(host, DWMCI_STATUS) & DWMCI_BUSY) { 48047f7fd3aSJason Zhu if (get_timer(start) > timeout) { 48147f7fd3aSJason Zhu debug("%s: Timeout on data busy\n", __func__); 48247f7fd3aSJason Zhu return -ETIMEDOUT; 48347f7fd3aSJason Zhu } 48447f7fd3aSJason Zhu } 48547f7fd3aSJason Zhu 48647f7fd3aSJason Zhu dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_ALL); 48747f7fd3aSJason Zhu 48847f7fd3aSJason Zhu if (data) { 48947f7fd3aSJason Zhu if (host->fifo_mode) { 49047f7fd3aSJason Zhu dwmci_writel(host, DWMCI_BLKSIZ, data->blocksize); 49147f7fd3aSJason Zhu dwmci_writel(host, DWMCI_BYTCNT, 49247f7fd3aSJason Zhu data->blocksize * data->blocks); 49347f7fd3aSJason Zhu dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET); 49447f7fd3aSJason Zhu } else { 49547f7fd3aSJason Zhu if (data->flags == MMC_DATA_READ) { 49647f7fd3aSJason Zhu bounce_buffer_start(&bbstate, (void *)data->dest, 49747f7fd3aSJason Zhu data->blocksize * 49847f7fd3aSJason Zhu data->blocks, GEN_BB_WRITE); 49947f7fd3aSJason Zhu } else { 50047f7fd3aSJason Zhu bounce_buffer_start(&bbstate, (void *)data->src, 50147f7fd3aSJason Zhu data->blocksize * 50247f7fd3aSJason Zhu data->blocks, GEN_BB_READ); 50347f7fd3aSJason Zhu } 50447f7fd3aSJason Zhu dwmci_prepare_data(host, data, cur_idmac, 50547f7fd3aSJason Zhu bbstate.bounce_buffer); 50647f7fd3aSJason Zhu } 50747f7fd3aSJason Zhu } 50847f7fd3aSJason Zhu 50947f7fd3aSJason Zhu dwmci_writel(host, DWMCI_CMDARG, cmd->cmdarg); 51047f7fd3aSJason Zhu 51147f7fd3aSJason Zhu if (data) 51247f7fd3aSJason Zhu flags = dwmci_set_transfer_mode(host, data); 51347f7fd3aSJason Zhu 51447f7fd3aSJason Zhu if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) 51547f7fd3aSJason Zhu return -1; 51647f7fd3aSJason Zhu 51747f7fd3aSJason Zhu if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) 51847f7fd3aSJason Zhu flags |= DWMCI_CMD_ABORT_STOP; 51947f7fd3aSJason Zhu else 52047f7fd3aSJason Zhu flags |= DWMCI_CMD_PRV_DAT_WAIT; 52147f7fd3aSJason Zhu 52247f7fd3aSJason Zhu if (cmd->resp_type & MMC_RSP_PRESENT) { 52347f7fd3aSJason Zhu flags |= DWMCI_CMD_RESP_EXP; 52447f7fd3aSJason Zhu if (cmd->resp_type & MMC_RSP_136) 52547f7fd3aSJason Zhu flags |= DWMCI_CMD_RESP_LENGTH; 52647f7fd3aSJason Zhu } 52747f7fd3aSJason Zhu 52847f7fd3aSJason Zhu if (cmd->resp_type & MMC_RSP_CRC) 52947f7fd3aSJason Zhu flags |= DWMCI_CMD_CHECK_CRC; 53047f7fd3aSJason Zhu 53147f7fd3aSJason Zhu flags |= (cmd->cmdidx | DWMCI_CMD_START | DWMCI_CMD_USE_HOLD_REG); 53247f7fd3aSJason Zhu 53347f7fd3aSJason Zhu debug("Sending CMD%d\n", cmd->cmdidx); 53447f7fd3aSJason Zhu 53547f7fd3aSJason Zhu dwmci_writel(host, DWMCI_CMD, flags); 53647f7fd3aSJason Zhu 53747f7fd3aSJason Zhu for (i = 0; i < retry; i++) { 53847f7fd3aSJason Zhu mask = dwmci_readl(host, DWMCI_RINTSTS); 53947f7fd3aSJason Zhu if (mask & DWMCI_INTMSK_CDONE) { 54047f7fd3aSJason Zhu if (!data) 54147f7fd3aSJason Zhu dwmci_writel(host, DWMCI_RINTSTS, mask); 54247f7fd3aSJason Zhu break; 54347f7fd3aSJason Zhu } 54447f7fd3aSJason Zhu } 54547f7fd3aSJason Zhu 54647f7fd3aSJason Zhu if (i == retry) { 54747f7fd3aSJason Zhu debug("%s: Timeout.\n", __func__); 54847f7fd3aSJason Zhu return -ETIMEDOUT; 54947f7fd3aSJason Zhu } 55047f7fd3aSJason Zhu 55147f7fd3aSJason Zhu if (mask & DWMCI_INTMSK_RTO) { 55247f7fd3aSJason Zhu /* 55347f7fd3aSJason Zhu * Timeout here is not necessarily fatal. (e)MMC cards 55447f7fd3aSJason Zhu * will splat here when they receive CMD55 as they do 55547f7fd3aSJason Zhu * not support this command and that is exactly the way 55647f7fd3aSJason Zhu * to tell them apart from SD cards. Thus, this output 55747f7fd3aSJason Zhu * below shall be debug(). eMMC cards also do not favor 55847f7fd3aSJason Zhu * CMD8, please keep that in mind. 55947f7fd3aSJason Zhu */ 56047f7fd3aSJason Zhu debug("%s: Response Timeout.\n", __func__); 56147f7fd3aSJason Zhu return -ETIMEDOUT; 56247f7fd3aSJason Zhu } else if (mask & DWMCI_INTMSK_RE) { 56347f7fd3aSJason Zhu debug("%s: Response Error.\n", __func__); 56447f7fd3aSJason Zhu return -EIO; 56547f7fd3aSJason Zhu } 56647f7fd3aSJason Zhu 56747f7fd3aSJason Zhu if (cmd->resp_type & MMC_RSP_PRESENT) { 56847f7fd3aSJason Zhu if (cmd->resp_type & MMC_RSP_136) { 56947f7fd3aSJason Zhu cmd->response[0] = dwmci_readl(host, DWMCI_RESP3); 57047f7fd3aSJason Zhu cmd->response[1] = dwmci_readl(host, DWMCI_RESP2); 57147f7fd3aSJason Zhu cmd->response[2] = dwmci_readl(host, DWMCI_RESP1); 57247f7fd3aSJason Zhu cmd->response[3] = dwmci_readl(host, DWMCI_RESP0); 57347f7fd3aSJason Zhu } else { 57447f7fd3aSJason Zhu cmd->response[0] = dwmci_readl(host, DWMCI_RESP0); 57547f7fd3aSJason Zhu } 57647f7fd3aSJason Zhu } 57747f7fd3aSJason Zhu 57847f7fd3aSJason Zhu return ret; 57947f7fd3aSJason Zhu } 58047f7fd3aSJason Zhu #endif 58147f7fd3aSJason Zhu 582757bff49SJaehoon Chung static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) 583757bff49SJaehoon Chung { 584757bff49SJaehoon Chung u32 div, status; 585757bff49SJaehoon Chung int timeout = 10000; 586757bff49SJaehoon Chung unsigned long sclk; 587757bff49SJaehoon Chung 58824527ef9SZiyuan Xu if (freq == 0) 589757bff49SJaehoon Chung return 0; 590757bff49SJaehoon Chung /* 591f33c9305SPavel Machek * If host->get_mmc_clk isn't defined, 592757bff49SJaehoon Chung * then assume that host->bus_hz is source clock value. 593f33c9305SPavel Machek * host->bus_hz should be set by user. 594757bff49SJaehoon Chung */ 595b44fe83aSJaehoon Chung if (host->get_mmc_clk) 596e3563f2eSSimon Glass sclk = host->get_mmc_clk(host, freq); 597757bff49SJaehoon Chung else if (host->bus_hz) 598757bff49SJaehoon Chung sclk = host->bus_hz; 599757bff49SJaehoon Chung else { 6001c87ffe8SSimon Glass debug("%s: Didn't get source clock value.\n", __func__); 601757bff49SJaehoon Chung return -EINVAL; 602757bff49SJaehoon Chung } 603757bff49SJaehoon Chung 604*ddeaf211SJason Zhu if (sclk == 0) 605*ddeaf211SJason Zhu return -EINVAL; 606*ddeaf211SJason Zhu 6076ace153dSChin Liang See if (sclk == freq) 6086ace153dSChin Liang See div = 0; /* bypass mode */ 6096ace153dSChin Liang See else 610757bff49SJaehoon Chung div = DIV_ROUND_UP(sclk, 2 * freq); 611757bff49SJaehoon Chung 612757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CLKENA, 0); 613757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CLKSRC, 0); 614757bff49SJaehoon Chung 615757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CLKDIV, div); 616757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT | 617757bff49SJaehoon Chung DWMCI_CMD_UPD_CLK | DWMCI_CMD_START); 618757bff49SJaehoon Chung 619757bff49SJaehoon Chung do { 620757bff49SJaehoon Chung status = dwmci_readl(host, DWMCI_CMD); 621757bff49SJaehoon Chung if (timeout-- < 0) { 6221c87ffe8SSimon Glass debug("%s: Timeout!\n", __func__); 623757bff49SJaehoon Chung return -ETIMEDOUT; 624757bff49SJaehoon Chung } 625757bff49SJaehoon Chung } while (status & DWMCI_CMD_START); 626757bff49SJaehoon Chung 627757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CLKENA, DWMCI_CLKEN_ENABLE | 628757bff49SJaehoon Chung DWMCI_CLKEN_LOW_PWR); 629757bff49SJaehoon Chung 630757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT | 631757bff49SJaehoon Chung DWMCI_CMD_UPD_CLK | DWMCI_CMD_START); 632757bff49SJaehoon Chung 633757bff49SJaehoon Chung timeout = 10000; 634757bff49SJaehoon Chung do { 635757bff49SJaehoon Chung status = dwmci_readl(host, DWMCI_CMD); 636757bff49SJaehoon Chung if (timeout-- < 0) { 6371c87ffe8SSimon Glass debug("%s: Timeout!\n", __func__); 638757bff49SJaehoon Chung return -ETIMEDOUT; 639757bff49SJaehoon Chung } 640757bff49SJaehoon Chung } while (status & DWMCI_CMD_START); 641757bff49SJaehoon Chung 642757bff49SJaehoon Chung host->clock = freq; 643757bff49SJaehoon Chung 644757bff49SJaehoon Chung return 0; 645757bff49SJaehoon Chung } 646757bff49SJaehoon Chung 647e7881d85SSimon Glass #ifdef CONFIG_DM_MMC 648ba0e56e1SZiyuan Xu static bool dwmci_card_busy(struct udevice *dev) 649ba0e56e1SZiyuan Xu { 650ba0e56e1SZiyuan Xu struct mmc *mmc = mmc_get_mmc_dev(dev); 651ba0e56e1SZiyuan Xu #else 652ba0e56e1SZiyuan Xu static bool dwmci_card_busy(struct mmc *mmc) 653ba0e56e1SZiyuan Xu { 654ba0e56e1SZiyuan Xu #endif 655ba0e56e1SZiyuan Xu u32 status; 656ba0e56e1SZiyuan Xu struct dwmci_host *host = (struct dwmci_host *)mmc->priv; 657ba0e56e1SZiyuan Xu 658ba0e56e1SZiyuan Xu /* 659ba0e56e1SZiyuan Xu * Check the busy bit which is low when DAT[3:0] 660ba0e56e1SZiyuan Xu * (the data lines) are 0000 661ba0e56e1SZiyuan Xu */ 662ba0e56e1SZiyuan Xu status = dwmci_readl(host, DWMCI_STATUS); 663ba0e56e1SZiyuan Xu 664ba0e56e1SZiyuan Xu return !!(status & DWMCI_BUSY); 665ba0e56e1SZiyuan Xu } 666ba0e56e1SZiyuan Xu 667ba0e56e1SZiyuan Xu #ifdef CONFIG_DM_MMC 6688c921dceSZiyuan Xu static int dwmci_execute_tuning(struct udevice *dev, u32 opcode) 6698c921dceSZiyuan Xu { 6708c921dceSZiyuan Xu struct mmc *mmc = mmc_get_mmc_dev(dev); 6718c921dceSZiyuan Xu #else 6728c921dceSZiyuan Xu static int dwmci_execute_tuning(struct mmc *mmc, u32 opcode) 6738c921dceSZiyuan Xu { 6748c921dceSZiyuan Xu #endif 6758c921dceSZiyuan Xu struct dwmci_host *host = (struct dwmci_host *)mmc->priv; 6768c921dceSZiyuan Xu 6778c921dceSZiyuan Xu if (!host->execute_tuning) 6788c921dceSZiyuan Xu return -EIO; 6798c921dceSZiyuan Xu 6808c921dceSZiyuan Xu return host->execute_tuning(host, opcode); 6818c921dceSZiyuan Xu } 6828c921dceSZiyuan Xu 6838c921dceSZiyuan Xu #ifdef CONFIG_DM_MMC 6845628347fSJaehoon Chung static int dwmci_set_ios(struct udevice *dev) 685691272feSSimon Glass { 686691272feSSimon Glass struct mmc *mmc = mmc_get_mmc_dev(dev); 687691272feSSimon Glass #else 68807b0b9c0SJaehoon Chung static int dwmci_set_ios(struct mmc *mmc) 689757bff49SJaehoon Chung { 690691272feSSimon Glass #endif 691045bdcd0SJaehoon Chung struct dwmci_host *host = (struct dwmci_host *)mmc->priv; 692045bdcd0SJaehoon Chung u32 ctype, regs; 693757bff49SJaehoon Chung 694757bff49SJaehoon Chung debug("Buswidth = %d, clock: %d\n", mmc->bus_width, mmc->clock); 695757bff49SJaehoon Chung 696757bff49SJaehoon Chung dwmci_setup_bus(host, mmc->clock); 697757bff49SJaehoon Chung switch (mmc->bus_width) { 698757bff49SJaehoon Chung case 8: 699757bff49SJaehoon Chung ctype = DWMCI_CTYPE_8BIT; 700757bff49SJaehoon Chung break; 701757bff49SJaehoon Chung case 4: 702757bff49SJaehoon Chung ctype = DWMCI_CTYPE_4BIT; 703757bff49SJaehoon Chung break; 704757bff49SJaehoon Chung default: 705757bff49SJaehoon Chung ctype = DWMCI_CTYPE_1BIT; 706757bff49SJaehoon Chung break; 707757bff49SJaehoon Chung } 708757bff49SJaehoon Chung 709757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CTYPE, ctype); 710757bff49SJaehoon Chung 711045bdcd0SJaehoon Chung regs = dwmci_readl(host, DWMCI_UHS_REG); 712caa21a21SZiyuan Xu if (mmc_card_ddr(mmc)) 713045bdcd0SJaehoon Chung regs |= DWMCI_DDR_MODE; 714045bdcd0SJaehoon Chung else 715afc9e2b5SJaehoon Chung regs &= ~DWMCI_DDR_MODE; 716045bdcd0SJaehoon Chung 717045bdcd0SJaehoon Chung dwmci_writel(host, DWMCI_UHS_REG, regs); 718045bdcd0SJaehoon Chung 719757bff49SJaehoon Chung if (host->clksel) 720757bff49SJaehoon Chung host->clksel(host); 72107b0b9c0SJaehoon Chung 722691272feSSimon Glass return 0; 723757bff49SJaehoon Chung } 724757bff49SJaehoon Chung 725757bff49SJaehoon Chung static int dwmci_init(struct mmc *mmc) 726757bff49SJaehoon Chung { 72793bfd616SPantelis Antoniou struct dwmci_host *host = mmc->priv; 72839abf9c1SPaweł Jarosz uint32_t use_dma; 72933e40bacSJason Zhu uint32_t verid; 730757bff49SJaehoon Chung 73118ab6755SJaehoon Chung if (host->board_init) 73218ab6755SJaehoon Chung host->board_init(host); 733204f7c39SJason Zhu #ifdef CONFIG_ARCH_ROCKCHIP 734204f7c39SJason Zhu if (host->dev_index == 0) 735757bff49SJaehoon Chung dwmci_writel(host, DWMCI_PWREN, 1); 736204f7c39SJason Zhu else if (host->dev_index == 1) 737204f7c39SJason Zhu dwmci_writel(host, DWMCI_PWREN, 0); 738204f7c39SJason Zhu else 739204f7c39SJason Zhu dwmci_writel(host, DWMCI_PWREN, 1); 740204f7c39SJason Zhu #else 741204f7c39SJason Zhu dwmci_writel(host, DWMCI_PWREN, 1); 742204f7c39SJason Zhu #endif 743757bff49SJaehoon Chung 74433e40bacSJason Zhu verid = dwmci_readl(host, DWMCI_VERID) & 0x0000ffff; 74533e40bacSJason Zhu if (verid >= DW_MMC_240A) 74633e40bacSJason Zhu dwmci_writel(host, DWMCI_CARDTHRCTL, DWMCI_CDTHRCTRL_CONFIG); 74733e40bacSJason Zhu 748757bff49SJaehoon Chung if (!dwmci_wait_reset(host, DWMCI_RESET_ALL)) { 7491c87ffe8SSimon Glass debug("%s[%d] Fail-reset!!\n", __func__, __LINE__); 7501c87ffe8SSimon Glass return -EIO; 751757bff49SJaehoon Chung } 752757bff49SJaehoon Chung 75339abf9c1SPaweł Jarosz use_dma = SDMMC_GET_TRANS_MODE(dwmci_readl(host, DWMCI_HCON)); 75439abf9c1SPaweł Jarosz if (use_dma == DMA_INTERFACE_IDMA) { 75539abf9c1SPaweł Jarosz host->fifo_mode = 0; 75639abf9c1SPaweł Jarosz } else { 75739abf9c1SPaweł Jarosz host->fifo_mode = 1; 75839abf9c1SPaweł Jarosz } 75939abf9c1SPaweł Jarosz 7609c50e35fSAmar /* Enumerate at 400KHz */ 76193bfd616SPantelis Antoniou dwmci_setup_bus(host, mmc->cfg->f_min); 7629c50e35fSAmar 763757bff49SJaehoon Chung dwmci_writel(host, DWMCI_RINTSTS, 0xFFFFFFFF); 764757bff49SJaehoon Chung dwmci_writel(host, DWMCI_INTMASK, 0); 765757bff49SJaehoon Chung 766757bff49SJaehoon Chung dwmci_writel(host, DWMCI_TMOUT, 0xFFFFFFFF); 767757bff49SJaehoon Chung 768757bff49SJaehoon Chung dwmci_writel(host, DWMCI_IDINTEN, 0); 769757bff49SJaehoon Chung dwmci_writel(host, DWMCI_BMOD, 1); 770757bff49SJaehoon Chung 771760177dfSSimon Glass if (!host->fifoth_val) { 772760177dfSSimon Glass uint32_t fifo_size; 773760177dfSSimon Glass 774760177dfSSimon Glass fifo_size = dwmci_readl(host, DWMCI_FIFOTH); 775760177dfSSimon Glass fifo_size = ((fifo_size & RX_WMARK_MASK) >> RX_WMARK_SHIFT) + 1; 7765ef89808SJason Zhu host->fifoth_val = MSIZE(DWMCI_MSIZE) | 7775ef89808SJason Zhu RX_WMARK(fifo_size / 2 - 1) | 778760177dfSSimon Glass TX_WMARK(fifo_size / 2); 7799108b315SAlexey Brodkin } 780760177dfSSimon Glass dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val); 781757bff49SJaehoon Chung 782757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CLKENA, 0); 783757bff49SJaehoon Chung dwmci_writel(host, DWMCI_CLKSRC, 0); 784757bff49SJaehoon Chung 785757bff49SJaehoon Chung return 0; 786757bff49SJaehoon Chung } 787757bff49SJaehoon Chung 7885743ef64SJason Zhu static int dwmci_get_cd(struct udevice *dev) 7895743ef64SJason Zhu { 7905743ef64SJason Zhu int ret = -1; 7915743ef64SJason Zhu #ifndef CONFIG_SPL_BUILD 7925743ef64SJason Zhu #ifdef CONFIG_DM_GPIO 7935743ef64SJason Zhu struct gpio_desc detect; 7945743ef64SJason Zhu 7955743ef64SJason Zhu ret = gpio_request_by_name(dev, "cd-gpios", 0, &detect, GPIOD_IS_IN); 7965743ef64SJason Zhu if (ret) { 7975743ef64SJason Zhu return ret; 7985743ef64SJason Zhu } 7995743ef64SJason Zhu 8005743ef64SJason Zhu ret = !dm_gpio_get_value(&detect); 8015743ef64SJason Zhu #endif 8025743ef64SJason Zhu #endif 8035743ef64SJason Zhu return ret; 8045743ef64SJason Zhu } 8055743ef64SJason Zhu 806e7881d85SSimon Glass #ifdef CONFIG_DM_MMC 807691272feSSimon Glass int dwmci_probe(struct udevice *dev) 808691272feSSimon Glass { 809691272feSSimon Glass struct mmc *mmc = mmc_get_mmc_dev(dev); 810691272feSSimon Glass 811691272feSSimon Glass return dwmci_init(mmc); 812691272feSSimon Glass } 813691272feSSimon Glass 814691272feSSimon Glass const struct dm_mmc_ops dm_dwmci_ops = { 815ba0e56e1SZiyuan Xu .card_busy = dwmci_card_busy, 816691272feSSimon Glass .send_cmd = dwmci_send_cmd, 81747f7fd3aSJason Zhu #ifdef CONFIG_SPL_BLK_READ_PREPARE 81847f7fd3aSJason Zhu .send_cmd_prepare = dwmci_send_cmd_prepare, 81947f7fd3aSJason Zhu #endif 820691272feSSimon Glass .set_ios = dwmci_set_ios, 8215743ef64SJason Zhu .get_cd = dwmci_get_cd, 8228c921dceSZiyuan Xu .execute_tuning = dwmci_execute_tuning, 823691272feSSimon Glass }; 824691272feSSimon Glass 825691272feSSimon Glass #else 826ab769f22SPantelis Antoniou static const struct mmc_ops dwmci_ops = { 827ba0e56e1SZiyuan Xu .card_busy = dwmci_card_busy, 828ab769f22SPantelis Antoniou .send_cmd = dwmci_send_cmd, 829ab769f22SPantelis Antoniou .set_ios = dwmci_set_ios, 8305743ef64SJason Zhu .get_cd = dwmci_get_cd, 831ab769f22SPantelis Antoniou .init = dwmci_init, 8328c921dceSZiyuan Xu .execute_tuning = dwmci_execute_tuning, 833ab769f22SPantelis Antoniou }; 834691272feSSimon Glass #endif 835ab769f22SPantelis Antoniou 836e5113c33SJaehoon Chung void dwmci_setup_cfg(struct mmc_config *cfg, struct dwmci_host *host, 837e5113c33SJaehoon Chung u32 max_clk, u32 min_clk) 8385e6ff810SSimon Glass { 839e5113c33SJaehoon Chung cfg->name = host->name; 840e7881d85SSimon Glass #ifndef CONFIG_DM_MMC 8415e6ff810SSimon Glass cfg->ops = &dwmci_ops; 842691272feSSimon Glass #endif 8435e6ff810SSimon Glass cfg->f_min = min_clk; 8445e6ff810SSimon Glass cfg->f_max = max_clk; 8455e6ff810SSimon Glass 8465e6ff810SSimon Glass cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; 8475e6ff810SSimon Glass 848e5113c33SJaehoon Chung cfg->host_caps = host->caps; 8495e6ff810SSimon Glass 85080ac95fcSZiyuan Xu switch (host->buswidth) { 85180ac95fcSZiyuan Xu case 8: 852c1cfa99bSZiyuan Xu cfg->host_caps |= MMC_MODE_8BIT | MMC_MODE_4BIT; 85380ac95fcSZiyuan Xu break; 85480ac95fcSZiyuan Xu case 4: 8555e6ff810SSimon Glass cfg->host_caps |= MMC_MODE_4BIT; 8565e6ff810SSimon Glass cfg->host_caps &= ~MMC_MODE_8BIT; 85780ac95fcSZiyuan Xu break; 85880ac95fcSZiyuan Xu case 1: 85980ac95fcSZiyuan Xu cfg->host_caps &= ~MMC_MODE_4BIT; 86080ac95fcSZiyuan Xu cfg->host_caps &= ~MMC_MODE_8BIT; 86180ac95fcSZiyuan Xu break; 86280ac95fcSZiyuan Xu default: 86380ac95fcSZiyuan Xu printf("Unsupported bus width: %d\n", host->buswidth); 86480ac95fcSZiyuan Xu break; 8655e6ff810SSimon Glass } 8665e6ff810SSimon Glass cfg->host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz; 8675e6ff810SSimon Glass 8685e6ff810SSimon Glass cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; 8695e6ff810SSimon Glass } 8705e6ff810SSimon Glass 8715e6ff810SSimon Glass #ifdef CONFIG_BLK 8725e6ff810SSimon Glass int dwmci_bind(struct udevice *dev, struct mmc *mmc, struct mmc_config *cfg) 8735e6ff810SSimon Glass { 8745e6ff810SSimon Glass return mmc_bind(dev, mmc, cfg); 8755e6ff810SSimon Glass } 8765e6ff810SSimon Glass #else 877757bff49SJaehoon Chung int add_dwmci(struct dwmci_host *host, u32 max_clk, u32 min_clk) 878757bff49SJaehoon Chung { 879e5113c33SJaehoon Chung dwmci_setup_cfg(&host->cfg, host, max_clk, min_clk); 880757bff49SJaehoon Chung 88193bfd616SPantelis Antoniou host->mmc = mmc_create(&host->cfg, host); 88293bfd616SPantelis Antoniou if (host->mmc == NULL) 88393bfd616SPantelis Antoniou return -1; 88493bfd616SPantelis Antoniou 88593bfd616SPantelis Antoniou return 0; 886757bff49SJaehoon Chung } 8875e6ff810SSimon Glass #endif 888