xref: /openbmc/u-boot/drivers/mmc/gen_atmel_mci.c (revision e8f80a5a)
1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
21592ef85SReinhard Meyer /*
31592ef85SReinhard Meyer  * Copyright (C) 2010
41592ef85SReinhard Meyer  * Rob Emanuele <rob@emanuele.us>
51592ef85SReinhard Meyer  * Reinhard Meyer, EMK Elektronik <reinhard.meyer@emk-elektronik.de>
61592ef85SReinhard Meyer  *
71592ef85SReinhard Meyer  * Original Driver:
81592ef85SReinhard Meyer  * Copyright (C) 2004-2006 Atmel Corporation
91592ef85SReinhard Meyer  */
101592ef85SReinhard Meyer 
111592ef85SReinhard Meyer #include <common.h>
12c86c0155SWenyou Yang #include <clk.h>
139d922450SSimon Glass #include <dm.h>
141592ef85SReinhard Meyer #include <mmc.h>
151592ef85SReinhard Meyer #include <part.h>
161592ef85SReinhard Meyer #include <malloc.h>
171592ef85SReinhard Meyer #include <asm/io.h>
181221ce45SMasahiro Yamada #include <linux/errno.h>
191592ef85SReinhard Meyer #include <asm/byteorder.h>
201592ef85SReinhard Meyer #include <asm/arch/clk.h>
21329f0f52SReinhard Meyer #include <asm/arch/hardware.h>
221592ef85SReinhard Meyer #include "atmel_mci.h"
231592ef85SReinhard Meyer 
241592ef85SReinhard Meyer #ifndef CONFIG_SYS_MMC_CLK_OD
251592ef85SReinhard Meyer # define CONFIG_SYS_MMC_CLK_OD	150000
261592ef85SReinhard Meyer #endif
271592ef85SReinhard Meyer 
281592ef85SReinhard Meyer #define MMC_DEFAULT_BLKLEN	512
291592ef85SReinhard Meyer 
301592ef85SReinhard Meyer #if defined(CONFIG_ATMEL_MCI_PORTB)
311592ef85SReinhard Meyer # define MCI_BUS 1
321592ef85SReinhard Meyer #else
331592ef85SReinhard Meyer # define MCI_BUS 0
341592ef85SReinhard Meyer #endif
351592ef85SReinhard Meyer 
36722b150eSWenyou.Yang@microchip.com #ifdef CONFIG_DM_MMC
37722b150eSWenyou.Yang@microchip.com struct atmel_mci_plat {
38722b150eSWenyou.Yang@microchip.com 	struct mmc		mmc;
396b75d359SMarek Vasut 	struct mmc_config	cfg;
406b75d359SMarek Vasut 	struct atmel_mci	*mci;
41722b150eSWenyou.Yang@microchip.com };
42722b150eSWenyou.Yang@microchip.com #endif
43722b150eSWenyou.Yang@microchip.com 
44722b150eSWenyou.Yang@microchip.com struct atmel_mci_priv {
45722b150eSWenyou.Yang@microchip.com #ifndef CONFIG_DM_MMC
46722b150eSWenyou.Yang@microchip.com 	struct mmc_config	cfg;
47722b150eSWenyou.Yang@microchip.com 	struct atmel_mci	*mci;
48722b150eSWenyou.Yang@microchip.com #endif
49877807e1SMarek Vasut 	unsigned int		initialized:1;
50b4670a0cSGregory CLEMENT 	unsigned int		curr_clk;
51c86c0155SWenyou Yang #ifdef CONFIG_DM_MMC
52c86c0155SWenyou Yang 	ulong		bus_clk_rate;
53c86c0155SWenyou Yang #endif
546b75d359SMarek Vasut };
556b75d359SMarek Vasut 
56aac4b69bSBo Shen /* Read Atmel MCI IP version */
atmel_mci_get_version(struct atmel_mci * mci)57aac4b69bSBo Shen static unsigned int atmel_mci_get_version(struct atmel_mci *mci)
58aac4b69bSBo Shen {
59aac4b69bSBo Shen 	return readl(&mci->version) & 0x00000fff;
60aac4b69bSBo Shen }
61aac4b69bSBo Shen 
621592ef85SReinhard Meyer /*
631592ef85SReinhard Meyer  * Print command and status:
641592ef85SReinhard Meyer  *
651592ef85SReinhard Meyer  * - always when DEBUG is defined
661592ef85SReinhard Meyer  * - on command errors
671592ef85SReinhard Meyer  */
dump_cmd(u32 cmdr,u32 arg,u32 status,const char * msg)681592ef85SReinhard Meyer static void dump_cmd(u32 cmdr, u32 arg, u32 status, const char* msg)
691592ef85SReinhard Meyer {
70b84c9c9aSMarek Vasut 	debug("gen_atmel_mci: CMDR %08x (%2u) ARGR %08x (SR: %08x) %s\n",
711592ef85SReinhard Meyer 	      cmdr, cmdr & 0x3F, arg, status, msg);
721592ef85SReinhard Meyer }
731592ef85SReinhard Meyer 
mci_set_blklen(atmel_mci_t * mci,int blklen)749b79dbd2SJean-Jacques Hiblot static inline void mci_set_blklen(atmel_mci_t *mci, int blklen)
759b79dbd2SJean-Jacques Hiblot {
769b79dbd2SJean-Jacques Hiblot 	unsigned int version = atmel_mci_get_version(mci);
779b79dbd2SJean-Jacques Hiblot 
789b79dbd2SJean-Jacques Hiblot 	blklen &= 0xfffc;
799b79dbd2SJean-Jacques Hiblot 
809b79dbd2SJean-Jacques Hiblot 	/* MCI IP version >= 0x200 has blkr */
819b79dbd2SJean-Jacques Hiblot 	if (version >= 0x200)
829b79dbd2SJean-Jacques Hiblot 		writel(MMCI_BFINS(BLKLEN, blklen, readl(&mci->blkr)),
839b79dbd2SJean-Jacques Hiblot 		       &mci->blkr);
849b79dbd2SJean-Jacques Hiblot 	else
859b79dbd2SJean-Jacques Hiblot 		writel(MMCI_BFINS(BLKLEN, blklen, readl(&mci->mr)), &mci->mr);
869b79dbd2SJean-Jacques Hiblot }
879b79dbd2SJean-Jacques Hiblot 
881592ef85SReinhard Meyer /* Setup for MCI Clock and Block Size */
89c86c0155SWenyou Yang #ifdef CONFIG_DM_MMC
mci_set_mode(struct udevice * dev,u32 hz,u32 blklen)90722b150eSWenyou.Yang@microchip.com static void mci_set_mode(struct udevice *dev, u32 hz, u32 blklen)
91c86c0155SWenyou Yang {
92722b150eSWenyou.Yang@microchip.com 	struct atmel_mci_plat *plat = dev_get_platdata(dev);
93722b150eSWenyou.Yang@microchip.com 	struct atmel_mci_priv *priv = dev_get_priv(dev);
94722b150eSWenyou.Yang@microchip.com 	struct mmc *mmc = &plat->mmc;
95c86c0155SWenyou Yang 	u32 bus_hz = priv->bus_clk_rate;
96722b150eSWenyou.Yang@microchip.com 	atmel_mci_t *mci = plat->mci;
97c86c0155SWenyou Yang #else
981592ef85SReinhard Meyer static void mci_set_mode(struct mmc *mmc, u32 hz, u32 blklen)
991592ef85SReinhard Meyer {
1006b75d359SMarek Vasut 	struct atmel_mci_priv *priv = mmc->priv;
1011592ef85SReinhard Meyer 	u32 bus_hz = get_mci_clk_rate();
102722b150eSWenyou.Yang@microchip.com 	atmel_mci_t *mci = priv->mci;
103c86c0155SWenyou Yang #endif
104c86c0155SWenyou Yang 
1051592ef85SReinhard Meyer 	u32 clkdiv = 255;
106cd60ebd4SBo Shen 	unsigned int version = atmel_mci_get_version(mci);
107cd60ebd4SBo Shen 	u32 clkodd = 0;
108cd60ebd4SBo Shen 	u32 mr;
1091592ef85SReinhard Meyer 
1101592ef85SReinhard Meyer 	debug("mci: bus_hz is %u, setting clock %u Hz, block size %u\n",
1111592ef85SReinhard Meyer 		bus_hz, hz, blklen);
1121592ef85SReinhard Meyer 	if (hz > 0) {
113cd60ebd4SBo Shen 		if (version >= 0x500) {
114cd60ebd4SBo Shen 			clkdiv = DIV_ROUND_UP(bus_hz, hz) - 2;
115cd60ebd4SBo Shen 			if (clkdiv > 511)
116cd60ebd4SBo Shen 				clkdiv = 511;
117cd60ebd4SBo Shen 
118cd60ebd4SBo Shen 			clkodd = clkdiv & 1;
119cd60ebd4SBo Shen 			clkdiv >>= 1;
120cd60ebd4SBo Shen 
121b84c9c9aSMarek Vasut 			debug("mci: setting clock %u Hz, block size %u\n",
122cd60ebd4SBo Shen 			      bus_hz / (clkdiv * 2 + clkodd + 2), blklen);
123cd60ebd4SBo Shen 		} else {
124cd60ebd4SBo Shen 			/* find clkdiv yielding a rate <= than requested */
1251592ef85SReinhard Meyer 			for (clkdiv = 0; clkdiv < 255; clkdiv++) {
1261592ef85SReinhard Meyer 				if ((bus_hz / (clkdiv + 1) / 2) <= hz)
1271592ef85SReinhard Meyer 					break;
1281592ef85SReinhard Meyer 			}
129b84c9c9aSMarek Vasut 			debug("mci: setting clock %u Hz, block size %u\n",
1301592ef85SReinhard Meyer 			      (bus_hz / (clkdiv + 1)) / 2, blklen);
1311592ef85SReinhard Meyer 
132cd60ebd4SBo Shen 		}
133cd60ebd4SBo Shen 	}
134b4670a0cSGregory CLEMENT 	if (version >= 0x500)
135b4670a0cSGregory CLEMENT 		priv->curr_clk = bus_hz / (clkdiv * 2 + clkodd + 2);
136b4670a0cSGregory CLEMENT 	else
137b4670a0cSGregory CLEMENT 		priv->curr_clk = (bus_hz / (clkdiv + 1)) / 2;
138cd60ebd4SBo Shen 
139cd60ebd4SBo Shen 	mr = MMCI_BF(CLKDIV, clkdiv);
140cd60ebd4SBo Shen 
141cd60ebd4SBo Shen 	/* MCI IP version >= 0x200 has R/WPROOF */
142cd60ebd4SBo Shen 	if (version >= 0x200)
143cd60ebd4SBo Shen 		mr |= MMCI_BIT(RDPROOF) | MMCI_BIT(WRPROOF);
144cd60ebd4SBo Shen 
1451db7377aSWu, Josh 	/*
146cd60ebd4SBo Shen 	 * MCI IP version >= 0x500 use bit 16 as clkodd.
147cd60ebd4SBo Shen 	 * MCI IP version < 0x500 use upper 16 bits for blklen.
1481db7377aSWu, Josh 	 */
149cd60ebd4SBo Shen 	if (version >= 0x500)
150cd60ebd4SBo Shen 		mr |= MMCI_BF(CLKODD, clkodd);
151cd60ebd4SBo Shen 
152cd60ebd4SBo Shen 	writel(mr, &mci->mr);
153cd60ebd4SBo Shen 
1549b79dbd2SJean-Jacques Hiblot 	mci_set_blklen(mci, blklen);
155cd60ebd4SBo Shen 
156da55c66eSBo Shen 	if (mmc->card_caps & mmc->cfg->host_caps & MMC_MODE_HS)
157da55c66eSBo Shen 		writel(MMCI_BIT(HSMODE), &mci->cfg);
158da55c66eSBo Shen 
159877807e1SMarek Vasut 	priv->initialized = 1;
1601592ef85SReinhard Meyer }
1611592ef85SReinhard Meyer 
1621592ef85SReinhard Meyer /* Return the CMDR with flags for a given command and data packet */
1631592ef85SReinhard Meyer static u32 mci_encode_cmd(
1641592ef85SReinhard Meyer 	struct mmc_cmd *cmd, struct mmc_data *data, u32* error_flags)
1651592ef85SReinhard Meyer {
1661592ef85SReinhard Meyer 	u32 cmdr = 0;
1671592ef85SReinhard Meyer 
1681592ef85SReinhard Meyer 	/* Default Flags for Errors */
1691592ef85SReinhard Meyer 	*error_flags |= (MMCI_BIT(DTOE) | MMCI_BIT(RDIRE) | MMCI_BIT(RENDE) |
1701592ef85SReinhard Meyer 		MMCI_BIT(RINDE) | MMCI_BIT(RTOE));
1711592ef85SReinhard Meyer 
1721592ef85SReinhard Meyer 	/* Default Flags for the Command */
1731592ef85SReinhard Meyer 	cmdr |= MMCI_BIT(MAXLAT);
1741592ef85SReinhard Meyer 
1751592ef85SReinhard Meyer 	if (data) {
1761592ef85SReinhard Meyer 		cmdr |= MMCI_BF(TRCMD, 1);
1771592ef85SReinhard Meyer 		if (data->blocks > 1)
1781592ef85SReinhard Meyer 			cmdr |= MMCI_BF(TRTYP, 1);
1791592ef85SReinhard Meyer 		if (data->flags & MMC_DATA_READ)
1801592ef85SReinhard Meyer 			cmdr |= MMCI_BIT(TRDIR);
1811592ef85SReinhard Meyer 	}
1821592ef85SReinhard Meyer 
1831592ef85SReinhard Meyer 	if (cmd->resp_type & MMC_RSP_CRC)
1841592ef85SReinhard Meyer 		*error_flags |= MMCI_BIT(RCRCE);
1851592ef85SReinhard Meyer 	if (cmd->resp_type & MMC_RSP_136)
1861592ef85SReinhard Meyer 		cmdr |= MMCI_BF(RSPTYP, 2);
1871592ef85SReinhard Meyer 	else if (cmd->resp_type & MMC_RSP_BUSY)
1881592ef85SReinhard Meyer 		cmdr |= MMCI_BF(RSPTYP, 3);
1891592ef85SReinhard Meyer 	else if (cmd->resp_type & MMC_RSP_PRESENT)
1901592ef85SReinhard Meyer 		cmdr |= MMCI_BF(RSPTYP, 1);
1911592ef85SReinhard Meyer 
1921592ef85SReinhard Meyer 	return cmdr | MMCI_BF(CMDNB, cmd->cmdidx);
1931592ef85SReinhard Meyer }
1941592ef85SReinhard Meyer 
1951592ef85SReinhard Meyer /* Entered into function pointer in mci_send_cmd */
1961592ef85SReinhard Meyer static u32 mci_data_read(atmel_mci_t *mci, u32* data, u32 error_flags)
1971592ef85SReinhard Meyer {
1981592ef85SReinhard Meyer 	u32 status;
1991592ef85SReinhard Meyer 
2001592ef85SReinhard Meyer 	do {
2011592ef85SReinhard Meyer 		status = readl(&mci->sr);
2021592ef85SReinhard Meyer 		if (status & (error_flags | MMCI_BIT(OVRE)))
2031592ef85SReinhard Meyer 			goto io_fail;
2041592ef85SReinhard Meyer 	} while (!(status & MMCI_BIT(RXRDY)));
2051592ef85SReinhard Meyer 
2061592ef85SReinhard Meyer 	if (status & MMCI_BIT(RXRDY)) {
2071592ef85SReinhard Meyer 		*data = readl(&mci->rdr);
2081592ef85SReinhard Meyer 		status = 0;
2091592ef85SReinhard Meyer 	}
2101592ef85SReinhard Meyer io_fail:
2111592ef85SReinhard Meyer 	return status;
2121592ef85SReinhard Meyer }
2131592ef85SReinhard Meyer 
2141592ef85SReinhard Meyer /* Entered into function pointer in mci_send_cmd */
2151592ef85SReinhard Meyer static u32 mci_data_write(atmel_mci_t *mci, u32* data, u32 error_flags)
2161592ef85SReinhard Meyer {
2171592ef85SReinhard Meyer 	u32 status;
2181592ef85SReinhard Meyer 
2191592ef85SReinhard Meyer 	do {
2201592ef85SReinhard Meyer 		status = readl(&mci->sr);
2211592ef85SReinhard Meyer 		if (status & (error_flags | MMCI_BIT(UNRE)))
2221592ef85SReinhard Meyer 			goto io_fail;
2231592ef85SReinhard Meyer 	} while (!(status & MMCI_BIT(TXRDY)));
2241592ef85SReinhard Meyer 
2251592ef85SReinhard Meyer 	if (status & MMCI_BIT(TXRDY)) {
2261592ef85SReinhard Meyer 		writel(*data, &mci->tdr);
2271592ef85SReinhard Meyer 		status = 0;
2281592ef85SReinhard Meyer 	}
2291592ef85SReinhard Meyer io_fail:
2301592ef85SReinhard Meyer 	return status;
2311592ef85SReinhard Meyer }
2321592ef85SReinhard Meyer 
2331592ef85SReinhard Meyer /*
2341592ef85SReinhard Meyer  * Entered into mmc structure during driver init
2351592ef85SReinhard Meyer  *
2361592ef85SReinhard Meyer  * Sends a command out on the bus and deals with the block data.
2371592ef85SReinhard Meyer  * Takes the mmc pointer, a command pointer, and an optional data pointer.
2381592ef85SReinhard Meyer  */
239c86c0155SWenyou Yang #ifdef CONFIG_DM_MMC
240c86c0155SWenyou Yang static int atmel_mci_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
241c86c0155SWenyou Yang 			      struct mmc_data *data)
242c86c0155SWenyou Yang {
243722b150eSWenyou.Yang@microchip.com 	struct atmel_mci_plat *plat = dev_get_platdata(dev);
244c86c0155SWenyou Yang 	struct atmel_mci_priv *priv = dev_get_priv(dev);
245722b150eSWenyou.Yang@microchip.com 	atmel_mci_t *mci = plat->mci;
246c86c0155SWenyou Yang #else
2471592ef85SReinhard Meyer static int
2481592ef85SReinhard Meyer mci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
2491592ef85SReinhard Meyer {
2506b75d359SMarek Vasut 	struct atmel_mci_priv *priv = mmc->priv;
2516b75d359SMarek Vasut 	atmel_mci_t *mci = priv->mci;
252722b150eSWenyou.Yang@microchip.com #endif
2531592ef85SReinhard Meyer 	u32 cmdr;
2541592ef85SReinhard Meyer 	u32 error_flags = 0;
2551592ef85SReinhard Meyer 	u32 status;
2561592ef85SReinhard Meyer 
257877807e1SMarek Vasut 	if (!priv->initialized) {
2581592ef85SReinhard Meyer 		puts ("MCI not initialized!\n");
259915ffa52SJaehoon Chung 		return -ECOMM;
2601592ef85SReinhard Meyer 	}
2611592ef85SReinhard Meyer 
2621592ef85SReinhard Meyer 	/* Figure out the transfer arguments */
2631592ef85SReinhard Meyer 	cmdr = mci_encode_cmd(cmd, data, &error_flags);
2641592ef85SReinhard Meyer 
2659b79dbd2SJean-Jacques Hiblot 	mci_set_blklen(mci, data->blocksize);
2669b79dbd2SJean-Jacques Hiblot 
2671db7377aSWu, Josh 	/* For multi blocks read/write, set the block register */
2681db7377aSWu, Josh 	if ((cmd->cmdidx == MMC_CMD_READ_MULTIPLE_BLOCK)
2691db7377aSWu, Josh 			|| (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK))
2709b79dbd2SJean-Jacques Hiblot 		writel(data->blocks | MMCI_BF(BLKLEN, data->blocksize),
2711db7377aSWu, Josh 		       &mci->blkr);
2721db7377aSWu, Josh 
2731592ef85SReinhard Meyer 	/* Send the command */
2741592ef85SReinhard Meyer 	writel(cmd->cmdarg, &mci->argr);
2751592ef85SReinhard Meyer 	writel(cmdr, &mci->cmdr);
2761592ef85SReinhard Meyer 
2771592ef85SReinhard Meyer #ifdef DEBUG
2781592ef85SReinhard Meyer 	dump_cmd(cmdr, cmd->cmdarg, 0, "DEBUG");
2791592ef85SReinhard Meyer #endif
2801592ef85SReinhard Meyer 
2811592ef85SReinhard Meyer 	/* Wait for the command to complete */
2821592ef85SReinhard Meyer 	while (!((status = readl(&mci->sr)) & MMCI_BIT(CMDRDY)));
2831592ef85SReinhard Meyer 
28493e3236cSBo Shen 	if ((status & error_flags) & MMCI_BIT(RTOE)) {
28593e3236cSBo Shen 		dump_cmd(cmdr, cmd->cmdarg, status, "Command Time Out");
286915ffa52SJaehoon Chung 		return -ETIMEDOUT;
28793e3236cSBo Shen 	} else if (status & error_flags) {
2881592ef85SReinhard Meyer 		dump_cmd(cmdr, cmd->cmdarg, status, "Command Failed");
289915ffa52SJaehoon Chung 		return -ECOMM;
2901592ef85SReinhard Meyer 	}
2911592ef85SReinhard Meyer 
2921592ef85SReinhard Meyer 	/* Copy the response to the response buffer */
2931592ef85SReinhard Meyer 	if (cmd->resp_type & MMC_RSP_136) {
2941592ef85SReinhard Meyer 		cmd->response[0] = readl(&mci->rspr);
2951592ef85SReinhard Meyer 		cmd->response[1] = readl(&mci->rspr1);
2961592ef85SReinhard Meyer 		cmd->response[2] = readl(&mci->rspr2);
2971592ef85SReinhard Meyer 		cmd->response[3] = readl(&mci->rspr3);
2981592ef85SReinhard Meyer 	} else
2991592ef85SReinhard Meyer 		cmd->response[0] = readl(&mci->rspr);
3001592ef85SReinhard Meyer 
3011592ef85SReinhard Meyer 	/* transfer all of the blocks */
3021592ef85SReinhard Meyer 	if (data) {
3031592ef85SReinhard Meyer 		u32 word_count, block_count;
3041592ef85SReinhard Meyer 		u32* ioptr;
3059b79dbd2SJean-Jacques Hiblot 		u32 i;
3061592ef85SReinhard Meyer 		u32 (*mci_data_op)
3071592ef85SReinhard Meyer 			(atmel_mci_t *mci, u32* data, u32 error_flags);
3081592ef85SReinhard Meyer 
3091592ef85SReinhard Meyer 		if (data->flags & MMC_DATA_READ) {
3101592ef85SReinhard Meyer 			mci_data_op = mci_data_read;
3111592ef85SReinhard Meyer 			ioptr = (u32*)data->dest;
3121592ef85SReinhard Meyer 		} else {
3131592ef85SReinhard Meyer 			mci_data_op = mci_data_write;
3141592ef85SReinhard Meyer 			ioptr = (u32*)data->src;
3151592ef85SReinhard Meyer 		}
3161592ef85SReinhard Meyer 
3171592ef85SReinhard Meyer 		status = 0;
3181592ef85SReinhard Meyer 		for (block_count = 0;
3191592ef85SReinhard Meyer 				block_count < data->blocks && !status;
3201592ef85SReinhard Meyer 				block_count++) {
3211592ef85SReinhard Meyer 			word_count = 0;
3221592ef85SReinhard Meyer 			do {
3231592ef85SReinhard Meyer 				status = mci_data_op(mci, ioptr, error_flags);
3241592ef85SReinhard Meyer 				word_count++;
3251592ef85SReinhard Meyer 				ioptr++;
3261592ef85SReinhard Meyer 			} while (!status && word_count < (data->blocksize/4));
3271592ef85SReinhard Meyer #ifdef DEBUG
3281592ef85SReinhard Meyer 			if (data->flags & MMC_DATA_READ)
3291592ef85SReinhard Meyer 			{
3309902c7b6SWu, Josh 				u32 cnt = word_count * 4;
3311592ef85SReinhard Meyer 				printf("Read Data:\n");
3329902c7b6SWu, Josh 				print_buffer(0, data->dest + cnt * block_count,
3339902c7b6SWu, Josh 					     1, cnt, 0);
3341592ef85SReinhard Meyer 			}
3351592ef85SReinhard Meyer #endif
3361592ef85SReinhard Meyer 			if (status) {
3371592ef85SReinhard Meyer 				dump_cmd(cmdr, cmd->cmdarg, status,
3381592ef85SReinhard Meyer 					"Data Transfer Failed");
339915ffa52SJaehoon Chung 				return -ECOMM;
3401592ef85SReinhard Meyer 			}
3411592ef85SReinhard Meyer 		}
3421592ef85SReinhard Meyer 
3431592ef85SReinhard Meyer 		/* Wait for Transfer End */
3441592ef85SReinhard Meyer 		i = 0;
3451592ef85SReinhard Meyer 		do {
3461592ef85SReinhard Meyer 			status = readl(&mci->sr);
3471592ef85SReinhard Meyer 
3481592ef85SReinhard Meyer 			if (status & error_flags) {
3491592ef85SReinhard Meyer 				dump_cmd(cmdr, cmd->cmdarg, status,
3501592ef85SReinhard Meyer 					"DTIP Wait Failed");
351915ffa52SJaehoon Chung 				return -ECOMM;
3521592ef85SReinhard Meyer 			}
3531592ef85SReinhard Meyer 			i++;
3541592ef85SReinhard Meyer 		} while ((status & MMCI_BIT(DTIP)) && i < 10000);
3551592ef85SReinhard Meyer 		if (status & MMCI_BIT(DTIP)) {
3561592ef85SReinhard Meyer 			dump_cmd(cmdr, cmd->cmdarg, status,
3571592ef85SReinhard Meyer 				"XFER DTIP never unset, ignoring");
3581592ef85SReinhard Meyer 		}
3591592ef85SReinhard Meyer 	}
3601592ef85SReinhard Meyer 
361b4670a0cSGregory CLEMENT 	/*
362b4670a0cSGregory CLEMENT 	 * After the switch command, wait for 8 clocks before the next
363b4670a0cSGregory CLEMENT 	 * command
364b4670a0cSGregory CLEMENT 	 */
365b4670a0cSGregory CLEMENT 	if (cmd->cmdidx == MMC_CMD_SWITCH)
366b4670a0cSGregory CLEMENT 		udelay(8*1000000 / priv->curr_clk); /* 8 clk in us */
367b4670a0cSGregory CLEMENT 
3681592ef85SReinhard Meyer 	return 0;
3691592ef85SReinhard Meyer }
3701592ef85SReinhard Meyer 
371c86c0155SWenyou Yang #ifdef CONFIG_DM_MMC
372c86c0155SWenyou Yang static int atmel_mci_set_ios(struct udevice *dev)
373c86c0155SWenyou Yang {
374722b150eSWenyou.Yang@microchip.com 	struct atmel_mci_plat *plat = dev_get_platdata(dev);
375c86c0155SWenyou Yang 	struct mmc *mmc = mmc_get_mmc_dev(dev);
376722b150eSWenyou.Yang@microchip.com 	atmel_mci_t *mci = plat->mci;
377c86c0155SWenyou Yang #else
3781592ef85SReinhard Meyer /* Entered into mmc structure during driver init */
37907b0b9c0SJaehoon Chung static int mci_set_ios(struct mmc *mmc)
3801592ef85SReinhard Meyer {
3816b75d359SMarek Vasut 	struct atmel_mci_priv *priv = mmc->priv;
3826b75d359SMarek Vasut 	atmel_mci_t *mci = priv->mci;
383722b150eSWenyou.Yang@microchip.com #endif
384aac4b69bSBo Shen 	int bus_width = mmc->bus_width;
385aac4b69bSBo Shen 	unsigned int version = atmel_mci_get_version(mci);
386aac4b69bSBo Shen 	int busw;
3871592ef85SReinhard Meyer 
3881592ef85SReinhard Meyer 	/* Set the clock speed */
389c86c0155SWenyou Yang #ifdef CONFIG_DM_MMC
390722b150eSWenyou.Yang@microchip.com 	mci_set_mode(dev, mmc->clock, MMC_DEFAULT_BLKLEN);
391c86c0155SWenyou Yang #else
3921592ef85SReinhard Meyer 	mci_set_mode(mmc, mmc->clock, MMC_DEFAULT_BLKLEN);
393c86c0155SWenyou Yang #endif
3941592ef85SReinhard Meyer 
3951592ef85SReinhard Meyer 	/*
3961592ef85SReinhard Meyer 	 * set the bus width and select slot for this interface
3971592ef85SReinhard Meyer 	 * there is no capability for multiple slots on the same interface yet
3981592ef85SReinhard Meyer 	 */
399aac4b69bSBo Shen 	if ((version & 0xf00) >= 0x300) {
400aac4b69bSBo Shen 		switch (bus_width) {
401aac4b69bSBo Shen 		case 8:
402aac4b69bSBo Shen 			busw = 3;
403aac4b69bSBo Shen 			break;
404aac4b69bSBo Shen 		case 4:
405aac4b69bSBo Shen 			busw = 2;
406aac4b69bSBo Shen 			break;
407aac4b69bSBo Shen 		default:
408aac4b69bSBo Shen 			busw = 0;
409aac4b69bSBo Shen 			break;
410aac4b69bSBo Shen 		}
411aac4b69bSBo Shen 
412aac4b69bSBo Shen 		writel(busw << 6 | MMCI_BF(SCDSEL, MCI_BUS), &mci->sdcr);
413aac4b69bSBo Shen 	} else {
414aac4b69bSBo Shen 		busw = (bus_width == 4) ? 1 : 0;
415aac4b69bSBo Shen 
416aac4b69bSBo Shen 		writel(busw << 7 | MMCI_BF(SCDSEL, MCI_BUS), &mci->sdcr);
417aac4b69bSBo Shen 	}
41807b0b9c0SJaehoon Chung 
41907b0b9c0SJaehoon Chung 	return 0;
4201592ef85SReinhard Meyer }
4211592ef85SReinhard Meyer 
422c86c0155SWenyou Yang #ifdef CONFIG_DM_MMC
423722b150eSWenyou.Yang@microchip.com static int atmel_mci_hw_init(struct udevice *dev)
424c86c0155SWenyou Yang {
425722b150eSWenyou.Yang@microchip.com 	struct atmel_mci_plat *plat = dev_get_platdata(dev);
426722b150eSWenyou.Yang@microchip.com 	atmel_mci_t *mci = plat->mci;
427c86c0155SWenyou Yang #else
4281592ef85SReinhard Meyer /* Entered into mmc structure during driver init */
4291592ef85SReinhard Meyer static int mci_init(struct mmc *mmc)
4301592ef85SReinhard Meyer {
4316b75d359SMarek Vasut 	struct atmel_mci_priv *priv = mmc->priv;
4326b75d359SMarek Vasut 	atmel_mci_t *mci = priv->mci;
433722b150eSWenyou.Yang@microchip.com #endif
4341592ef85SReinhard Meyer 
4351592ef85SReinhard Meyer 	/* Initialize controller */
4361592ef85SReinhard Meyer 	writel(MMCI_BIT(SWRST), &mci->cr);	/* soft reset */
4371592ef85SReinhard Meyer 	writel(MMCI_BIT(PWSDIS), &mci->cr);	/* disable power save */
4381592ef85SReinhard Meyer 	writel(MMCI_BIT(MCIEN), &mci->cr);	/* enable mci */
4392aed9d14SReinhard Meyer 	writel(MMCI_BF(SCDSEL, MCI_BUS), &mci->sdcr);	/* select port */
4401592ef85SReinhard Meyer 
4419924ca6eSWu, Josh 	/* This delay can be optimized, but stick with max value */
4429924ca6eSWu, Josh 	writel(0x7f, &mci->dtor);
4431592ef85SReinhard Meyer 	/* Disable Interrupts */
4441592ef85SReinhard Meyer 	writel(~0UL, &mci->idr);
4451592ef85SReinhard Meyer 
4461592ef85SReinhard Meyer 	/* Set default clocks and blocklen */
447c86c0155SWenyou Yang #ifdef CONFIG_DM_MMC
448722b150eSWenyou.Yang@microchip.com 	mci_set_mode(dev, CONFIG_SYS_MMC_CLK_OD, MMC_DEFAULT_BLKLEN);
449c86c0155SWenyou Yang #else
4501592ef85SReinhard Meyer 	mci_set_mode(mmc, CONFIG_SYS_MMC_CLK_OD, MMC_DEFAULT_BLKLEN);
451c86c0155SWenyou Yang #endif
4521592ef85SReinhard Meyer 
4531592ef85SReinhard Meyer 	return 0;
4541592ef85SReinhard Meyer }
4551592ef85SReinhard Meyer 
456c86c0155SWenyou Yang #ifndef CONFIG_DM_MMC
457ab769f22SPantelis Antoniou static const struct mmc_ops atmel_mci_ops = {
458ab769f22SPantelis Antoniou 	.send_cmd	= mci_send_cmd,
459ab769f22SPantelis Antoniou 	.set_ios	= mci_set_ios,
460ab769f22SPantelis Antoniou 	.init		= mci_init,
461ab769f22SPantelis Antoniou };
462ab769f22SPantelis Antoniou 
4631592ef85SReinhard Meyer /*
4641592ef85SReinhard Meyer  * This is the only exported function
4651592ef85SReinhard Meyer  *
4661592ef85SReinhard Meyer  * Call it with the MCI register base address
4671592ef85SReinhard Meyer  */
4681592ef85SReinhard Meyer int atmel_mci_init(void *regs)
4691592ef85SReinhard Meyer {
47093bfd616SPantelis Antoniou 	struct mmc *mmc;
47193bfd616SPantelis Antoniou 	struct mmc_config *cfg;
4726b75d359SMarek Vasut 	struct atmel_mci_priv *priv;
473aac4b69bSBo Shen 	unsigned int version;
4741592ef85SReinhard Meyer 
4756b75d359SMarek Vasut 	priv = calloc(1, sizeof(*priv));
4766b75d359SMarek Vasut 	if (!priv)
4776b75d359SMarek Vasut 		return -ENOMEM;
478aac4b69bSBo Shen 
4796b75d359SMarek Vasut 	cfg = &priv->cfg;
48093bfd616SPantelis Antoniou 
48193bfd616SPantelis Antoniou 	cfg->name = "mci";
48293bfd616SPantelis Antoniou 	cfg->ops = &atmel_mci_ops;
4831592ef85SReinhard Meyer 
4846b75d359SMarek Vasut 	priv->mci = (struct atmel_mci *)regs;
485877807e1SMarek Vasut 	priv->initialized = 0;
4866b75d359SMarek Vasut 
4871592ef85SReinhard Meyer 	/* need to be able to pass these in on a board by board basis */
48893bfd616SPantelis Antoniou 	cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
4896b75d359SMarek Vasut 	version = atmel_mci_get_version(priv->mci);
490da55c66eSBo Shen 	if ((version & 0xf00) >= 0x300) {
49193bfd616SPantelis Antoniou 		cfg->host_caps = MMC_MODE_8BIT;
492da55c66eSBo Shen 		cfg->host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz;
493da55c66eSBo Shen 	}
494aac4b69bSBo Shen 
49593bfd616SPantelis Antoniou 	cfg->host_caps |= MMC_MODE_4BIT;
496aac4b69bSBo Shen 
4971592ef85SReinhard Meyer 	/*
4981592ef85SReinhard Meyer 	 * min and max frequencies determined by
4991592ef85SReinhard Meyer 	 * max and min of clock divider
5001592ef85SReinhard Meyer 	 */
50193bfd616SPantelis Antoniou 	cfg->f_min = get_mci_clk_rate() / (2*256);
50293bfd616SPantelis Antoniou 	cfg->f_max = get_mci_clk_rate() / (2*1);
5031592ef85SReinhard Meyer 
50493bfd616SPantelis Antoniou 	cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
5058feafcc4SJohn Rigby 
5066b75d359SMarek Vasut 	mmc = mmc_create(cfg, priv);
50793bfd616SPantelis Antoniou 
50893bfd616SPantelis Antoniou 	if (mmc == NULL) {
5096b75d359SMarek Vasut 		free(priv);
5106b75d359SMarek Vasut 		return -ENODEV;
51193bfd616SPantelis Antoniou 	}
5126b75d359SMarek Vasut 	/* NOTE: possibly leaking the priv structure */
5131592ef85SReinhard Meyer 
5141592ef85SReinhard Meyer 	return 0;
5151592ef85SReinhard Meyer }
516c86c0155SWenyou Yang #endif
517c86c0155SWenyou Yang 
518c86c0155SWenyou Yang #ifdef CONFIG_DM_MMC
519c86c0155SWenyou Yang static const struct dm_mmc_ops atmel_mci_mmc_ops = {
520c86c0155SWenyou Yang 	.send_cmd = atmel_mci_send_cmd,
521c86c0155SWenyou Yang 	.set_ios = atmel_mci_set_ios,
522c86c0155SWenyou Yang };
523c86c0155SWenyou Yang 
524722b150eSWenyou.Yang@microchip.com static void atmel_mci_setup_cfg(struct udevice *dev)
525c86c0155SWenyou Yang {
526722b150eSWenyou.Yang@microchip.com 	struct atmel_mci_plat *plat = dev_get_platdata(dev);
527722b150eSWenyou.Yang@microchip.com 	struct atmel_mci_priv *priv = dev_get_priv(dev);
528c86c0155SWenyou Yang 	struct mmc_config *cfg;
529c86c0155SWenyou Yang 	u32 version;
530c86c0155SWenyou Yang 
531722b150eSWenyou.Yang@microchip.com 	cfg = &plat->cfg;
532c86c0155SWenyou Yang 	cfg->name = "Atmel mci";
533c86c0155SWenyou Yang 	cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
534c86c0155SWenyou Yang 
535c86c0155SWenyou Yang 	/*
536c86c0155SWenyou Yang 	 * If the version is above 3.0, the capabilities of the 8-bit
537c86c0155SWenyou Yang 	 * bus width and high speed are supported.
538c86c0155SWenyou Yang 	 */
539722b150eSWenyou.Yang@microchip.com 	version = atmel_mci_get_version(plat->mci);
540c86c0155SWenyou Yang 	if ((version & 0xf00) >= 0x300) {
541c86c0155SWenyou Yang 		cfg->host_caps = MMC_MODE_8BIT |
542c86c0155SWenyou Yang 				 MMC_MODE_HS | MMC_MODE_HS_52MHz;
543c86c0155SWenyou Yang 	}
544c86c0155SWenyou Yang 
545c86c0155SWenyou Yang 	cfg->host_caps |= MMC_MODE_4BIT;
546c86c0155SWenyou Yang 	cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
547c86c0155SWenyou Yang 	cfg->f_min = priv->bus_clk_rate / (2 * 256);
548c86c0155SWenyou Yang 	cfg->f_max = priv->bus_clk_rate / 2;
549c86c0155SWenyou Yang }
550c86c0155SWenyou Yang 
551c86c0155SWenyou Yang static int atmel_mci_enable_clk(struct udevice *dev)
552c86c0155SWenyou Yang {
553c86c0155SWenyou Yang 	struct atmel_mci_priv *priv = dev_get_priv(dev);
554c86c0155SWenyou Yang 	struct clk clk;
555c86c0155SWenyou Yang 	ulong clk_rate;
556c86c0155SWenyou Yang 	int ret = 0;
557c86c0155SWenyou Yang 
558c86c0155SWenyou Yang 	ret = clk_get_by_index(dev, 0, &clk);
559c86c0155SWenyou Yang 	if (ret) {
560c86c0155SWenyou Yang 		ret = -EINVAL;
561c86c0155SWenyou Yang 		goto failed;
562c86c0155SWenyou Yang 	}
563c86c0155SWenyou Yang 
564c86c0155SWenyou Yang 	ret = clk_enable(&clk);
565c86c0155SWenyou Yang 	if (ret)
566c86c0155SWenyou Yang 		goto failed;
567c86c0155SWenyou Yang 
568c86c0155SWenyou Yang 	clk_rate = clk_get_rate(&clk);
569c86c0155SWenyou Yang 	if (!clk_rate) {
570c86c0155SWenyou Yang 		ret = -EINVAL;
571c86c0155SWenyou Yang 		goto failed;
572c86c0155SWenyou Yang 	}
573c86c0155SWenyou Yang 
574c86c0155SWenyou Yang 	priv->bus_clk_rate = clk_rate;
575c86c0155SWenyou Yang 
576c86c0155SWenyou Yang failed:
577c86c0155SWenyou Yang 	clk_free(&clk);
578c86c0155SWenyou Yang 
579c86c0155SWenyou Yang 	return ret;
580c86c0155SWenyou Yang }
581c86c0155SWenyou Yang 
582c86c0155SWenyou Yang static int atmel_mci_probe(struct udevice *dev)
583c86c0155SWenyou Yang {
584c86c0155SWenyou Yang 	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
585722b150eSWenyou.Yang@microchip.com 	struct atmel_mci_plat *plat = dev_get_platdata(dev);
586c86c0155SWenyou Yang 	struct mmc *mmc;
587c86c0155SWenyou Yang 	int ret;
588c86c0155SWenyou Yang 
589c86c0155SWenyou Yang 	ret = atmel_mci_enable_clk(dev);
590c86c0155SWenyou Yang 	if (ret)
591c86c0155SWenyou Yang 		return ret;
592c86c0155SWenyou Yang 
593722b150eSWenyou.Yang@microchip.com 	plat->mci = (struct atmel_mci *)devfdt_get_addr_ptr(dev);
594c86c0155SWenyou Yang 
595722b150eSWenyou.Yang@microchip.com 	atmel_mci_setup_cfg(dev);
596c86c0155SWenyou Yang 
597722b150eSWenyou.Yang@microchip.com 	mmc = &plat->mmc;
598722b150eSWenyou.Yang@microchip.com 	mmc->cfg = &plat->cfg;
599c86c0155SWenyou Yang 	mmc->dev = dev;
600c86c0155SWenyou Yang 	upriv->mmc = mmc;
601c86c0155SWenyou Yang 
602722b150eSWenyou.Yang@microchip.com 	atmel_mci_hw_init(dev);
603c86c0155SWenyou Yang 
604c86c0155SWenyou Yang 	return 0;
605c86c0155SWenyou Yang }
606c86c0155SWenyou Yang 
607c86c0155SWenyou Yang static int atmel_mci_bind(struct udevice *dev)
608c86c0155SWenyou Yang {
609722b150eSWenyou.Yang@microchip.com 	struct atmel_mci_plat *plat = dev_get_platdata(dev);
610c86c0155SWenyou Yang 
611722b150eSWenyou.Yang@microchip.com 	return mmc_bind(dev, &plat->mmc, &plat->cfg);
612c86c0155SWenyou Yang }
613c86c0155SWenyou Yang 
614c86c0155SWenyou Yang static const struct udevice_id atmel_mci_ids[] = {
615c86c0155SWenyou Yang 	{ .compatible = "atmel,hsmci" },
616c86c0155SWenyou Yang 	{ }
617c86c0155SWenyou Yang };
618c86c0155SWenyou Yang 
619c86c0155SWenyou Yang U_BOOT_DRIVER(atmel_mci) = {
620c86c0155SWenyou Yang 	.name = "atmel-mci",
621c86c0155SWenyou Yang 	.id = UCLASS_MMC,
622c86c0155SWenyou Yang 	.of_match = atmel_mci_ids,
623c86c0155SWenyou Yang 	.bind = atmel_mci_bind,
624c86c0155SWenyou Yang 	.probe = atmel_mci_probe,
625722b150eSWenyou.Yang@microchip.com 	.platdata_auto_alloc_size = sizeof(struct atmel_mci_plat),
626c86c0155SWenyou Yang 	.priv_auto_alloc_size = sizeof(struct atmel_mci_priv),
627c86c0155SWenyou Yang 	.ops = &atmel_mci_mmc_ops,
628c86c0155SWenyou Yang };
629c86c0155SWenyou Yang #endif
630