xref: /openbmc/u-boot/drivers/mmc/gen_atmel_mci.c (revision 9b79dbd2)
11592ef85SReinhard Meyer /*
21592ef85SReinhard Meyer  * Copyright (C) 2010
31592ef85SReinhard Meyer  * Rob Emanuele <rob@emanuele.us>
41592ef85SReinhard Meyer  * Reinhard Meyer, EMK Elektronik <reinhard.meyer@emk-elektronik.de>
51592ef85SReinhard Meyer  *
61592ef85SReinhard Meyer  * Original Driver:
71592ef85SReinhard Meyer  * Copyright (C) 2004-2006 Atmel Corporation
81592ef85SReinhard Meyer  *
91a459660SWolfgang Denk  * SPDX-License-Identifier:	GPL-2.0+
101592ef85SReinhard Meyer  */
111592ef85SReinhard Meyer 
121592ef85SReinhard Meyer #include <common.h>
13c86c0155SWenyou Yang #include <clk.h>
149d922450SSimon Glass #include <dm.h>
151592ef85SReinhard Meyer #include <mmc.h>
161592ef85SReinhard Meyer #include <part.h>
171592ef85SReinhard Meyer #include <malloc.h>
181592ef85SReinhard Meyer #include <asm/io.h>
191221ce45SMasahiro Yamada #include <linux/errno.h>
201592ef85SReinhard Meyer #include <asm/byteorder.h>
211592ef85SReinhard Meyer #include <asm/arch/clk.h>
22329f0f52SReinhard Meyer #include <asm/arch/hardware.h>
231592ef85SReinhard Meyer #include "atmel_mci.h"
241592ef85SReinhard Meyer 
25c86c0155SWenyou Yang DECLARE_GLOBAL_DATA_PTR;
26c86c0155SWenyou Yang 
271592ef85SReinhard Meyer #ifndef CONFIG_SYS_MMC_CLK_OD
281592ef85SReinhard Meyer # define CONFIG_SYS_MMC_CLK_OD	150000
291592ef85SReinhard Meyer #endif
301592ef85SReinhard Meyer 
311592ef85SReinhard Meyer #define MMC_DEFAULT_BLKLEN	512
321592ef85SReinhard Meyer 
331592ef85SReinhard Meyer #if defined(CONFIG_ATMEL_MCI_PORTB)
341592ef85SReinhard Meyer # define MCI_BUS 1
351592ef85SReinhard Meyer #else
361592ef85SReinhard Meyer # define MCI_BUS 0
371592ef85SReinhard Meyer #endif
381592ef85SReinhard Meyer 
39722b150eSWenyou.Yang@microchip.com #ifdef CONFIG_DM_MMC
40722b150eSWenyou.Yang@microchip.com struct atmel_mci_plat {
41722b150eSWenyou.Yang@microchip.com 	struct mmc		mmc;
426b75d359SMarek Vasut 	struct mmc_config	cfg;
436b75d359SMarek Vasut 	struct atmel_mci	*mci;
44722b150eSWenyou.Yang@microchip.com };
45722b150eSWenyou.Yang@microchip.com #endif
46722b150eSWenyou.Yang@microchip.com 
47722b150eSWenyou.Yang@microchip.com struct atmel_mci_priv {
48722b150eSWenyou.Yang@microchip.com #ifndef CONFIG_DM_MMC
49722b150eSWenyou.Yang@microchip.com 	struct mmc_config	cfg;
50722b150eSWenyou.Yang@microchip.com 	struct atmel_mci	*mci;
51722b150eSWenyou.Yang@microchip.com #endif
52877807e1SMarek Vasut 	unsigned int		initialized:1;
53b4670a0cSGregory CLEMENT 	unsigned int		curr_clk;
54c86c0155SWenyou Yang #ifdef CONFIG_DM_MMC
55c86c0155SWenyou Yang 	ulong		bus_clk_rate;
56c86c0155SWenyou Yang #endif
576b75d359SMarek Vasut };
586b75d359SMarek Vasut 
59aac4b69bSBo Shen /* Read Atmel MCI IP version */
60aac4b69bSBo Shen static unsigned int atmel_mci_get_version(struct atmel_mci *mci)
61aac4b69bSBo Shen {
62aac4b69bSBo Shen 	return readl(&mci->version) & 0x00000fff;
63aac4b69bSBo Shen }
64aac4b69bSBo Shen 
651592ef85SReinhard Meyer /*
661592ef85SReinhard Meyer  * Print command and status:
671592ef85SReinhard Meyer  *
681592ef85SReinhard Meyer  * - always when DEBUG is defined
691592ef85SReinhard Meyer  * - on command errors
701592ef85SReinhard Meyer  */
711592ef85SReinhard Meyer static void dump_cmd(u32 cmdr, u32 arg, u32 status, const char* msg)
721592ef85SReinhard Meyer {
73b84c9c9aSMarek Vasut 	debug("gen_atmel_mci: CMDR %08x (%2u) ARGR %08x (SR: %08x) %s\n",
741592ef85SReinhard Meyer 	      cmdr, cmdr & 0x3F, arg, status, msg);
751592ef85SReinhard Meyer }
761592ef85SReinhard Meyer 
77*9b79dbd2SJean-Jacques Hiblot static inline void mci_set_blklen(atmel_mci_t *mci, int blklen)
78*9b79dbd2SJean-Jacques Hiblot {
79*9b79dbd2SJean-Jacques Hiblot 	unsigned int version = atmel_mci_get_version(mci);
80*9b79dbd2SJean-Jacques Hiblot 
81*9b79dbd2SJean-Jacques Hiblot 	blklen &= 0xfffc;
82*9b79dbd2SJean-Jacques Hiblot 
83*9b79dbd2SJean-Jacques Hiblot 	/* MCI IP version >= 0x200 has blkr */
84*9b79dbd2SJean-Jacques Hiblot 	if (version >= 0x200)
85*9b79dbd2SJean-Jacques Hiblot 		writel(MMCI_BFINS(BLKLEN, blklen, readl(&mci->blkr)),
86*9b79dbd2SJean-Jacques Hiblot 		       &mci->blkr);
87*9b79dbd2SJean-Jacques Hiblot 	else
88*9b79dbd2SJean-Jacques Hiblot 		writel(MMCI_BFINS(BLKLEN, blklen, readl(&mci->mr)), &mci->mr);
89*9b79dbd2SJean-Jacques Hiblot }
90*9b79dbd2SJean-Jacques Hiblot 
911592ef85SReinhard Meyer /* Setup for MCI Clock and Block Size */
92c86c0155SWenyou Yang #ifdef CONFIG_DM_MMC
93722b150eSWenyou.Yang@microchip.com static void mci_set_mode(struct udevice *dev, u32 hz, u32 blklen)
94c86c0155SWenyou Yang {
95722b150eSWenyou.Yang@microchip.com 	struct atmel_mci_plat *plat = dev_get_platdata(dev);
96722b150eSWenyou.Yang@microchip.com 	struct atmel_mci_priv *priv = dev_get_priv(dev);
97722b150eSWenyou.Yang@microchip.com 	struct mmc *mmc = &plat->mmc;
98c86c0155SWenyou Yang 	u32 bus_hz = priv->bus_clk_rate;
99722b150eSWenyou.Yang@microchip.com 	atmel_mci_t *mci = plat->mci;
100c86c0155SWenyou Yang #else
1011592ef85SReinhard Meyer static void mci_set_mode(struct mmc *mmc, u32 hz, u32 blklen)
1021592ef85SReinhard Meyer {
1036b75d359SMarek Vasut 	struct atmel_mci_priv *priv = mmc->priv;
1041592ef85SReinhard Meyer 	u32 bus_hz = get_mci_clk_rate();
105722b150eSWenyou.Yang@microchip.com 	atmel_mci_t *mci = priv->mci;
106c86c0155SWenyou Yang #endif
107c86c0155SWenyou Yang 
1081592ef85SReinhard Meyer 	u32 clkdiv = 255;
109cd60ebd4SBo Shen 	unsigned int version = atmel_mci_get_version(mci);
110cd60ebd4SBo Shen 	u32 clkodd = 0;
111cd60ebd4SBo Shen 	u32 mr;
1121592ef85SReinhard Meyer 
1131592ef85SReinhard Meyer 	debug("mci: bus_hz is %u, setting clock %u Hz, block size %u\n",
1141592ef85SReinhard Meyer 		bus_hz, hz, blklen);
1151592ef85SReinhard Meyer 	if (hz > 0) {
116cd60ebd4SBo Shen 		if (version >= 0x500) {
117cd60ebd4SBo Shen 			clkdiv = DIV_ROUND_UP(bus_hz, hz) - 2;
118cd60ebd4SBo Shen 			if (clkdiv > 511)
119cd60ebd4SBo Shen 				clkdiv = 511;
120cd60ebd4SBo Shen 
121cd60ebd4SBo Shen 			clkodd = clkdiv & 1;
122cd60ebd4SBo Shen 			clkdiv >>= 1;
123cd60ebd4SBo Shen 
124b84c9c9aSMarek Vasut 			debug("mci: setting clock %u Hz, block size %u\n",
125cd60ebd4SBo Shen 			      bus_hz / (clkdiv * 2 + clkodd + 2), blklen);
126cd60ebd4SBo Shen 		} else {
127cd60ebd4SBo Shen 			/* find clkdiv yielding a rate <= than requested */
1281592ef85SReinhard Meyer 			for (clkdiv = 0; clkdiv < 255; clkdiv++) {
1291592ef85SReinhard Meyer 				if ((bus_hz / (clkdiv + 1) / 2) <= hz)
1301592ef85SReinhard Meyer 					break;
1311592ef85SReinhard Meyer 			}
132b84c9c9aSMarek Vasut 			debug("mci: setting clock %u Hz, block size %u\n",
1331592ef85SReinhard Meyer 			      (bus_hz / (clkdiv + 1)) / 2, blklen);
1341592ef85SReinhard Meyer 
135cd60ebd4SBo Shen 		}
136cd60ebd4SBo Shen 	}
137b4670a0cSGregory CLEMENT 	if (version >= 0x500)
138b4670a0cSGregory CLEMENT 		priv->curr_clk = bus_hz / (clkdiv * 2 + clkodd + 2);
139b4670a0cSGregory CLEMENT 	else
140b4670a0cSGregory CLEMENT 		priv->curr_clk = (bus_hz / (clkdiv + 1)) / 2;
141cd60ebd4SBo Shen 
142cd60ebd4SBo Shen 	mr = MMCI_BF(CLKDIV, clkdiv);
143cd60ebd4SBo Shen 
144cd60ebd4SBo Shen 	/* MCI IP version >= 0x200 has R/WPROOF */
145cd60ebd4SBo Shen 	if (version >= 0x200)
146cd60ebd4SBo Shen 		mr |= MMCI_BIT(RDPROOF) | MMCI_BIT(WRPROOF);
147cd60ebd4SBo Shen 
1481db7377aSWu, Josh 	/*
149cd60ebd4SBo Shen 	 * MCI IP version >= 0x500 use bit 16 as clkodd.
150cd60ebd4SBo Shen 	 * MCI IP version < 0x500 use upper 16 bits for blklen.
1511db7377aSWu, Josh 	 */
152cd60ebd4SBo Shen 	if (version >= 0x500)
153cd60ebd4SBo Shen 		mr |= MMCI_BF(CLKODD, clkodd);
154cd60ebd4SBo Shen 
155cd60ebd4SBo Shen 	writel(mr, &mci->mr);
156cd60ebd4SBo Shen 
157*9b79dbd2SJean-Jacques Hiblot 	mci_set_blklen(mci, blklen);
158cd60ebd4SBo Shen 
159da55c66eSBo Shen 	if (mmc->card_caps & mmc->cfg->host_caps & MMC_MODE_HS)
160da55c66eSBo Shen 		writel(MMCI_BIT(HSMODE), &mci->cfg);
161da55c66eSBo Shen 
162877807e1SMarek Vasut 	priv->initialized = 1;
1631592ef85SReinhard Meyer }
1641592ef85SReinhard Meyer 
1651592ef85SReinhard Meyer /* Return the CMDR with flags for a given command and data packet */
1661592ef85SReinhard Meyer static u32 mci_encode_cmd(
1671592ef85SReinhard Meyer 	struct mmc_cmd *cmd, struct mmc_data *data, u32* error_flags)
1681592ef85SReinhard Meyer {
1691592ef85SReinhard Meyer 	u32 cmdr = 0;
1701592ef85SReinhard Meyer 
1711592ef85SReinhard Meyer 	/* Default Flags for Errors */
1721592ef85SReinhard Meyer 	*error_flags |= (MMCI_BIT(DTOE) | MMCI_BIT(RDIRE) | MMCI_BIT(RENDE) |
1731592ef85SReinhard Meyer 		MMCI_BIT(RINDE) | MMCI_BIT(RTOE));
1741592ef85SReinhard Meyer 
1751592ef85SReinhard Meyer 	/* Default Flags for the Command */
1761592ef85SReinhard Meyer 	cmdr |= MMCI_BIT(MAXLAT);
1771592ef85SReinhard Meyer 
1781592ef85SReinhard Meyer 	if (data) {
1791592ef85SReinhard Meyer 		cmdr |= MMCI_BF(TRCMD, 1);
1801592ef85SReinhard Meyer 		if (data->blocks > 1)
1811592ef85SReinhard Meyer 			cmdr |= MMCI_BF(TRTYP, 1);
1821592ef85SReinhard Meyer 		if (data->flags & MMC_DATA_READ)
1831592ef85SReinhard Meyer 			cmdr |= MMCI_BIT(TRDIR);
1841592ef85SReinhard Meyer 	}
1851592ef85SReinhard Meyer 
1861592ef85SReinhard Meyer 	if (cmd->resp_type & MMC_RSP_CRC)
1871592ef85SReinhard Meyer 		*error_flags |= MMCI_BIT(RCRCE);
1881592ef85SReinhard Meyer 	if (cmd->resp_type & MMC_RSP_136)
1891592ef85SReinhard Meyer 		cmdr |= MMCI_BF(RSPTYP, 2);
1901592ef85SReinhard Meyer 	else if (cmd->resp_type & MMC_RSP_BUSY)
1911592ef85SReinhard Meyer 		cmdr |= MMCI_BF(RSPTYP, 3);
1921592ef85SReinhard Meyer 	else if (cmd->resp_type & MMC_RSP_PRESENT)
1931592ef85SReinhard Meyer 		cmdr |= MMCI_BF(RSPTYP, 1);
1941592ef85SReinhard Meyer 
1951592ef85SReinhard Meyer 	return cmdr | MMCI_BF(CMDNB, cmd->cmdidx);
1961592ef85SReinhard Meyer }
1971592ef85SReinhard Meyer 
1981592ef85SReinhard Meyer /* Entered into function pointer in mci_send_cmd */
1991592ef85SReinhard Meyer static u32 mci_data_read(atmel_mci_t *mci, u32* data, u32 error_flags)
2001592ef85SReinhard Meyer {
2011592ef85SReinhard Meyer 	u32 status;
2021592ef85SReinhard Meyer 
2031592ef85SReinhard Meyer 	do {
2041592ef85SReinhard Meyer 		status = readl(&mci->sr);
2051592ef85SReinhard Meyer 		if (status & (error_flags | MMCI_BIT(OVRE)))
2061592ef85SReinhard Meyer 			goto io_fail;
2071592ef85SReinhard Meyer 	} while (!(status & MMCI_BIT(RXRDY)));
2081592ef85SReinhard Meyer 
2091592ef85SReinhard Meyer 	if (status & MMCI_BIT(RXRDY)) {
2101592ef85SReinhard Meyer 		*data = readl(&mci->rdr);
2111592ef85SReinhard Meyer 		status = 0;
2121592ef85SReinhard Meyer 	}
2131592ef85SReinhard Meyer io_fail:
2141592ef85SReinhard Meyer 	return status;
2151592ef85SReinhard Meyer }
2161592ef85SReinhard Meyer 
2171592ef85SReinhard Meyer /* Entered into function pointer in mci_send_cmd */
2181592ef85SReinhard Meyer static u32 mci_data_write(atmel_mci_t *mci, u32* data, u32 error_flags)
2191592ef85SReinhard Meyer {
2201592ef85SReinhard Meyer 	u32 status;
2211592ef85SReinhard Meyer 
2221592ef85SReinhard Meyer 	do {
2231592ef85SReinhard Meyer 		status = readl(&mci->sr);
2241592ef85SReinhard Meyer 		if (status & (error_flags | MMCI_BIT(UNRE)))
2251592ef85SReinhard Meyer 			goto io_fail;
2261592ef85SReinhard Meyer 	} while (!(status & MMCI_BIT(TXRDY)));
2271592ef85SReinhard Meyer 
2281592ef85SReinhard Meyer 	if (status & MMCI_BIT(TXRDY)) {
2291592ef85SReinhard Meyer 		writel(*data, &mci->tdr);
2301592ef85SReinhard Meyer 		status = 0;
2311592ef85SReinhard Meyer 	}
2321592ef85SReinhard Meyer io_fail:
2331592ef85SReinhard Meyer 	return status;
2341592ef85SReinhard Meyer }
2351592ef85SReinhard Meyer 
2361592ef85SReinhard Meyer /*
2371592ef85SReinhard Meyer  * Entered into mmc structure during driver init
2381592ef85SReinhard Meyer  *
2391592ef85SReinhard Meyer  * Sends a command out on the bus and deals with the block data.
2401592ef85SReinhard Meyer  * Takes the mmc pointer, a command pointer, and an optional data pointer.
2411592ef85SReinhard Meyer  */
242c86c0155SWenyou Yang #ifdef CONFIG_DM_MMC
243c86c0155SWenyou Yang static int atmel_mci_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
244c86c0155SWenyou Yang 			      struct mmc_data *data)
245c86c0155SWenyou Yang {
246722b150eSWenyou.Yang@microchip.com 	struct atmel_mci_plat *plat = dev_get_platdata(dev);
247c86c0155SWenyou Yang 	struct atmel_mci_priv *priv = dev_get_priv(dev);
248722b150eSWenyou.Yang@microchip.com 	atmel_mci_t *mci = plat->mci;
249c86c0155SWenyou Yang #else
2501592ef85SReinhard Meyer static int
2511592ef85SReinhard Meyer mci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
2521592ef85SReinhard Meyer {
2536b75d359SMarek Vasut 	struct atmel_mci_priv *priv = mmc->priv;
2546b75d359SMarek Vasut 	atmel_mci_t *mci = priv->mci;
255722b150eSWenyou.Yang@microchip.com #endif
2561592ef85SReinhard Meyer 	u32 cmdr;
2571592ef85SReinhard Meyer 	u32 error_flags = 0;
2581592ef85SReinhard Meyer 	u32 status;
2591592ef85SReinhard Meyer 
260877807e1SMarek Vasut 	if (!priv->initialized) {
2611592ef85SReinhard Meyer 		puts ("MCI not initialized!\n");
262915ffa52SJaehoon Chung 		return -ECOMM;
2631592ef85SReinhard Meyer 	}
2641592ef85SReinhard Meyer 
2651592ef85SReinhard Meyer 	/* Figure out the transfer arguments */
2661592ef85SReinhard Meyer 	cmdr = mci_encode_cmd(cmd, data, &error_flags);
2671592ef85SReinhard Meyer 
268*9b79dbd2SJean-Jacques Hiblot 	mci_set_blklen(mci, data->blocksize);
269*9b79dbd2SJean-Jacques Hiblot 
2701db7377aSWu, Josh 	/* For multi blocks read/write, set the block register */
2711db7377aSWu, Josh 	if ((cmd->cmdidx == MMC_CMD_READ_MULTIPLE_BLOCK)
2721db7377aSWu, Josh 			|| (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK))
273*9b79dbd2SJean-Jacques Hiblot 		writel(data->blocks | MMCI_BF(BLKLEN, data->blocksize),
2741db7377aSWu, Josh 		       &mci->blkr);
2751db7377aSWu, Josh 
2761592ef85SReinhard Meyer 	/* Send the command */
2771592ef85SReinhard Meyer 	writel(cmd->cmdarg, &mci->argr);
2781592ef85SReinhard Meyer 	writel(cmdr, &mci->cmdr);
2791592ef85SReinhard Meyer 
2801592ef85SReinhard Meyer #ifdef DEBUG
2811592ef85SReinhard Meyer 	dump_cmd(cmdr, cmd->cmdarg, 0, "DEBUG");
2821592ef85SReinhard Meyer #endif
2831592ef85SReinhard Meyer 
2841592ef85SReinhard Meyer 	/* Wait for the command to complete */
2851592ef85SReinhard Meyer 	while (!((status = readl(&mci->sr)) & MMCI_BIT(CMDRDY)));
2861592ef85SReinhard Meyer 
28793e3236cSBo Shen 	if ((status & error_flags) & MMCI_BIT(RTOE)) {
28893e3236cSBo Shen 		dump_cmd(cmdr, cmd->cmdarg, status, "Command Time Out");
289915ffa52SJaehoon Chung 		return -ETIMEDOUT;
29093e3236cSBo Shen 	} else if (status & error_flags) {
2911592ef85SReinhard Meyer 		dump_cmd(cmdr, cmd->cmdarg, status, "Command Failed");
292915ffa52SJaehoon Chung 		return -ECOMM;
2931592ef85SReinhard Meyer 	}
2941592ef85SReinhard Meyer 
2951592ef85SReinhard Meyer 	/* Copy the response to the response buffer */
2961592ef85SReinhard Meyer 	if (cmd->resp_type & MMC_RSP_136) {
2971592ef85SReinhard Meyer 		cmd->response[0] = readl(&mci->rspr);
2981592ef85SReinhard Meyer 		cmd->response[1] = readl(&mci->rspr1);
2991592ef85SReinhard Meyer 		cmd->response[2] = readl(&mci->rspr2);
3001592ef85SReinhard Meyer 		cmd->response[3] = readl(&mci->rspr3);
3011592ef85SReinhard Meyer 	} else
3021592ef85SReinhard Meyer 		cmd->response[0] = readl(&mci->rspr);
3031592ef85SReinhard Meyer 
3041592ef85SReinhard Meyer 	/* transfer all of the blocks */
3051592ef85SReinhard Meyer 	if (data) {
3061592ef85SReinhard Meyer 		u32 word_count, block_count;
3071592ef85SReinhard Meyer 		u32* ioptr;
308*9b79dbd2SJean-Jacques Hiblot 		u32 i;
3091592ef85SReinhard Meyer 		u32 (*mci_data_op)
3101592ef85SReinhard Meyer 			(atmel_mci_t *mci, u32* data, u32 error_flags);
3111592ef85SReinhard Meyer 
3121592ef85SReinhard Meyer 		if (data->flags & MMC_DATA_READ) {
3131592ef85SReinhard Meyer 			mci_data_op = mci_data_read;
3141592ef85SReinhard Meyer 			ioptr = (u32*)data->dest;
3151592ef85SReinhard Meyer 		} else {
3161592ef85SReinhard Meyer 			mci_data_op = mci_data_write;
3171592ef85SReinhard Meyer 			ioptr = (u32*)data->src;
3181592ef85SReinhard Meyer 		}
3191592ef85SReinhard Meyer 
3201592ef85SReinhard Meyer 		status = 0;
3211592ef85SReinhard Meyer 		for (block_count = 0;
3221592ef85SReinhard Meyer 				block_count < data->blocks && !status;
3231592ef85SReinhard Meyer 				block_count++) {
3241592ef85SReinhard Meyer 			word_count = 0;
3251592ef85SReinhard Meyer 			do {
3261592ef85SReinhard Meyer 				status = mci_data_op(mci, ioptr, error_flags);
3271592ef85SReinhard Meyer 				word_count++;
3281592ef85SReinhard Meyer 				ioptr++;
3291592ef85SReinhard Meyer 			} while (!status && word_count < (data->blocksize/4));
3301592ef85SReinhard Meyer #ifdef DEBUG
3311592ef85SReinhard Meyer 			if (data->flags & MMC_DATA_READ)
3321592ef85SReinhard Meyer 			{
3339902c7b6SWu, Josh 				u32 cnt = word_count * 4;
3341592ef85SReinhard Meyer 				printf("Read Data:\n");
3359902c7b6SWu, Josh 				print_buffer(0, data->dest + cnt * block_count,
3369902c7b6SWu, Josh 					     1, cnt, 0);
3371592ef85SReinhard Meyer 			}
3381592ef85SReinhard Meyer #endif
3391592ef85SReinhard Meyer 			if (status) {
3401592ef85SReinhard Meyer 				dump_cmd(cmdr, cmd->cmdarg, status,
3411592ef85SReinhard Meyer 					"Data Transfer Failed");
342915ffa52SJaehoon Chung 				return -ECOMM;
3431592ef85SReinhard Meyer 			}
3441592ef85SReinhard Meyer 		}
3451592ef85SReinhard Meyer 
3461592ef85SReinhard Meyer 		/* Wait for Transfer End */
3471592ef85SReinhard Meyer 		i = 0;
3481592ef85SReinhard Meyer 		do {
3491592ef85SReinhard Meyer 			status = readl(&mci->sr);
3501592ef85SReinhard Meyer 
3511592ef85SReinhard Meyer 			if (status & error_flags) {
3521592ef85SReinhard Meyer 				dump_cmd(cmdr, cmd->cmdarg, status,
3531592ef85SReinhard Meyer 					"DTIP Wait Failed");
354915ffa52SJaehoon Chung 				return -ECOMM;
3551592ef85SReinhard Meyer 			}
3561592ef85SReinhard Meyer 			i++;
3571592ef85SReinhard Meyer 		} while ((status & MMCI_BIT(DTIP)) && i < 10000);
3581592ef85SReinhard Meyer 		if (status & MMCI_BIT(DTIP)) {
3591592ef85SReinhard Meyer 			dump_cmd(cmdr, cmd->cmdarg, status,
3601592ef85SReinhard Meyer 				"XFER DTIP never unset, ignoring");
3611592ef85SReinhard Meyer 		}
3621592ef85SReinhard Meyer 	}
3631592ef85SReinhard Meyer 
364b4670a0cSGregory CLEMENT 	/*
365b4670a0cSGregory CLEMENT 	 * After the switch command, wait for 8 clocks before the next
366b4670a0cSGregory CLEMENT 	 * command
367b4670a0cSGregory CLEMENT 	 */
368b4670a0cSGregory CLEMENT 	if (cmd->cmdidx == MMC_CMD_SWITCH)
369b4670a0cSGregory CLEMENT 		udelay(8*1000000 / priv->curr_clk); /* 8 clk in us */
370b4670a0cSGregory CLEMENT 
3711592ef85SReinhard Meyer 	return 0;
3721592ef85SReinhard Meyer }
3731592ef85SReinhard Meyer 
374c86c0155SWenyou Yang #ifdef CONFIG_DM_MMC
375c86c0155SWenyou Yang static int atmel_mci_set_ios(struct udevice *dev)
376c86c0155SWenyou Yang {
377722b150eSWenyou.Yang@microchip.com 	struct atmel_mci_plat *plat = dev_get_platdata(dev);
378c86c0155SWenyou Yang 	struct mmc *mmc = mmc_get_mmc_dev(dev);
379722b150eSWenyou.Yang@microchip.com 	atmel_mci_t *mci = plat->mci;
380c86c0155SWenyou Yang #else
3811592ef85SReinhard Meyer /* Entered into mmc structure during driver init */
38207b0b9c0SJaehoon Chung static int mci_set_ios(struct mmc *mmc)
3831592ef85SReinhard Meyer {
3846b75d359SMarek Vasut 	struct atmel_mci_priv *priv = mmc->priv;
3856b75d359SMarek Vasut 	atmel_mci_t *mci = priv->mci;
386722b150eSWenyou.Yang@microchip.com #endif
387aac4b69bSBo Shen 	int bus_width = mmc->bus_width;
388aac4b69bSBo Shen 	unsigned int version = atmel_mci_get_version(mci);
389aac4b69bSBo Shen 	int busw;
3901592ef85SReinhard Meyer 
3911592ef85SReinhard Meyer 	/* Set the clock speed */
392c86c0155SWenyou Yang #ifdef CONFIG_DM_MMC
393722b150eSWenyou.Yang@microchip.com 	mci_set_mode(dev, mmc->clock, MMC_DEFAULT_BLKLEN);
394c86c0155SWenyou Yang #else
3951592ef85SReinhard Meyer 	mci_set_mode(mmc, mmc->clock, MMC_DEFAULT_BLKLEN);
396c86c0155SWenyou Yang #endif
3971592ef85SReinhard Meyer 
3981592ef85SReinhard Meyer 	/*
3991592ef85SReinhard Meyer 	 * set the bus width and select slot for this interface
4001592ef85SReinhard Meyer 	 * there is no capability for multiple slots on the same interface yet
4011592ef85SReinhard Meyer 	 */
402aac4b69bSBo Shen 	if ((version & 0xf00) >= 0x300) {
403aac4b69bSBo Shen 		switch (bus_width) {
404aac4b69bSBo Shen 		case 8:
405aac4b69bSBo Shen 			busw = 3;
406aac4b69bSBo Shen 			break;
407aac4b69bSBo Shen 		case 4:
408aac4b69bSBo Shen 			busw = 2;
409aac4b69bSBo Shen 			break;
410aac4b69bSBo Shen 		default:
411aac4b69bSBo Shen 			busw = 0;
412aac4b69bSBo Shen 			break;
413aac4b69bSBo Shen 		}
414aac4b69bSBo Shen 
415aac4b69bSBo Shen 		writel(busw << 6 | MMCI_BF(SCDSEL, MCI_BUS), &mci->sdcr);
416aac4b69bSBo Shen 	} else {
417aac4b69bSBo Shen 		busw = (bus_width == 4) ? 1 : 0;
418aac4b69bSBo Shen 
419aac4b69bSBo Shen 		writel(busw << 7 | MMCI_BF(SCDSEL, MCI_BUS), &mci->sdcr);
420aac4b69bSBo Shen 	}
42107b0b9c0SJaehoon Chung 
42207b0b9c0SJaehoon Chung 	return 0;
4231592ef85SReinhard Meyer }
4241592ef85SReinhard Meyer 
425c86c0155SWenyou Yang #ifdef CONFIG_DM_MMC
426722b150eSWenyou.Yang@microchip.com static int atmel_mci_hw_init(struct udevice *dev)
427c86c0155SWenyou Yang {
428722b150eSWenyou.Yang@microchip.com 	struct atmel_mci_plat *plat = dev_get_platdata(dev);
429722b150eSWenyou.Yang@microchip.com 	atmel_mci_t *mci = plat->mci;
430c86c0155SWenyou Yang #else
4311592ef85SReinhard Meyer /* Entered into mmc structure during driver init */
4321592ef85SReinhard Meyer static int mci_init(struct mmc *mmc)
4331592ef85SReinhard Meyer {
4346b75d359SMarek Vasut 	struct atmel_mci_priv *priv = mmc->priv;
4356b75d359SMarek Vasut 	atmel_mci_t *mci = priv->mci;
436722b150eSWenyou.Yang@microchip.com #endif
4371592ef85SReinhard Meyer 
4381592ef85SReinhard Meyer 	/* Initialize controller */
4391592ef85SReinhard Meyer 	writel(MMCI_BIT(SWRST), &mci->cr);	/* soft reset */
4401592ef85SReinhard Meyer 	writel(MMCI_BIT(PWSDIS), &mci->cr);	/* disable power save */
4411592ef85SReinhard Meyer 	writel(MMCI_BIT(MCIEN), &mci->cr);	/* enable mci */
4422aed9d14SReinhard Meyer 	writel(MMCI_BF(SCDSEL, MCI_BUS), &mci->sdcr);	/* select port */
4431592ef85SReinhard Meyer 
4449924ca6eSWu, Josh 	/* This delay can be optimized, but stick with max value */
4459924ca6eSWu, Josh 	writel(0x7f, &mci->dtor);
4461592ef85SReinhard Meyer 	/* Disable Interrupts */
4471592ef85SReinhard Meyer 	writel(~0UL, &mci->idr);
4481592ef85SReinhard Meyer 
4491592ef85SReinhard Meyer 	/* Set default clocks and blocklen */
450c86c0155SWenyou Yang #ifdef CONFIG_DM_MMC
451722b150eSWenyou.Yang@microchip.com 	mci_set_mode(dev, CONFIG_SYS_MMC_CLK_OD, MMC_DEFAULT_BLKLEN);
452c86c0155SWenyou Yang #else
4531592ef85SReinhard Meyer 	mci_set_mode(mmc, CONFIG_SYS_MMC_CLK_OD, MMC_DEFAULT_BLKLEN);
454c86c0155SWenyou Yang #endif
4551592ef85SReinhard Meyer 
4561592ef85SReinhard Meyer 	return 0;
4571592ef85SReinhard Meyer }
4581592ef85SReinhard Meyer 
459c86c0155SWenyou Yang #ifndef CONFIG_DM_MMC
460ab769f22SPantelis Antoniou static const struct mmc_ops atmel_mci_ops = {
461ab769f22SPantelis Antoniou 	.send_cmd	= mci_send_cmd,
462ab769f22SPantelis Antoniou 	.set_ios	= mci_set_ios,
463ab769f22SPantelis Antoniou 	.init		= mci_init,
464ab769f22SPantelis Antoniou };
465ab769f22SPantelis Antoniou 
4661592ef85SReinhard Meyer /*
4671592ef85SReinhard Meyer  * This is the only exported function
4681592ef85SReinhard Meyer  *
4691592ef85SReinhard Meyer  * Call it with the MCI register base address
4701592ef85SReinhard Meyer  */
4711592ef85SReinhard Meyer int atmel_mci_init(void *regs)
4721592ef85SReinhard Meyer {
47393bfd616SPantelis Antoniou 	struct mmc *mmc;
47493bfd616SPantelis Antoniou 	struct mmc_config *cfg;
4756b75d359SMarek Vasut 	struct atmel_mci_priv *priv;
476aac4b69bSBo Shen 	unsigned int version;
4771592ef85SReinhard Meyer 
4786b75d359SMarek Vasut 	priv = calloc(1, sizeof(*priv));
4796b75d359SMarek Vasut 	if (!priv)
4806b75d359SMarek Vasut 		return -ENOMEM;
481aac4b69bSBo Shen 
4826b75d359SMarek Vasut 	cfg = &priv->cfg;
48393bfd616SPantelis Antoniou 
48493bfd616SPantelis Antoniou 	cfg->name = "mci";
48593bfd616SPantelis Antoniou 	cfg->ops = &atmel_mci_ops;
4861592ef85SReinhard Meyer 
4876b75d359SMarek Vasut 	priv->mci = (struct atmel_mci *)regs;
488877807e1SMarek Vasut 	priv->initialized = 0;
4896b75d359SMarek Vasut 
4901592ef85SReinhard Meyer 	/* need to be able to pass these in on a board by board basis */
49193bfd616SPantelis Antoniou 	cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
4926b75d359SMarek Vasut 	version = atmel_mci_get_version(priv->mci);
493da55c66eSBo Shen 	if ((version & 0xf00) >= 0x300) {
49493bfd616SPantelis Antoniou 		cfg->host_caps = MMC_MODE_8BIT;
495da55c66eSBo Shen 		cfg->host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz;
496da55c66eSBo Shen 	}
497aac4b69bSBo Shen 
49893bfd616SPantelis Antoniou 	cfg->host_caps |= MMC_MODE_4BIT;
499aac4b69bSBo Shen 
5001592ef85SReinhard Meyer 	/*
5011592ef85SReinhard Meyer 	 * min and max frequencies determined by
5021592ef85SReinhard Meyer 	 * max and min of clock divider
5031592ef85SReinhard Meyer 	 */
50493bfd616SPantelis Antoniou 	cfg->f_min = get_mci_clk_rate() / (2*256);
50593bfd616SPantelis Antoniou 	cfg->f_max = get_mci_clk_rate() / (2*1);
5061592ef85SReinhard Meyer 
50793bfd616SPantelis Antoniou 	cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
5088feafcc4SJohn Rigby 
5096b75d359SMarek Vasut 	mmc = mmc_create(cfg, priv);
51093bfd616SPantelis Antoniou 
51193bfd616SPantelis Antoniou 	if (mmc == NULL) {
5126b75d359SMarek Vasut 		free(priv);
5136b75d359SMarek Vasut 		return -ENODEV;
51493bfd616SPantelis Antoniou 	}
5156b75d359SMarek Vasut 	/* NOTE: possibly leaking the priv structure */
5161592ef85SReinhard Meyer 
5171592ef85SReinhard Meyer 	return 0;
5181592ef85SReinhard Meyer }
519c86c0155SWenyou Yang #endif
520c86c0155SWenyou Yang 
521c86c0155SWenyou Yang #ifdef CONFIG_DM_MMC
522c86c0155SWenyou Yang static const struct dm_mmc_ops atmel_mci_mmc_ops = {
523c86c0155SWenyou Yang 	.send_cmd = atmel_mci_send_cmd,
524c86c0155SWenyou Yang 	.set_ios = atmel_mci_set_ios,
525c86c0155SWenyou Yang };
526c86c0155SWenyou Yang 
527722b150eSWenyou.Yang@microchip.com static void atmel_mci_setup_cfg(struct udevice *dev)
528c86c0155SWenyou Yang {
529722b150eSWenyou.Yang@microchip.com 	struct atmel_mci_plat *plat = dev_get_platdata(dev);
530722b150eSWenyou.Yang@microchip.com 	struct atmel_mci_priv *priv = dev_get_priv(dev);
531c86c0155SWenyou Yang 	struct mmc_config *cfg;
532c86c0155SWenyou Yang 	u32 version;
533c86c0155SWenyou Yang 
534722b150eSWenyou.Yang@microchip.com 	cfg = &plat->cfg;
535c86c0155SWenyou Yang 	cfg->name = "Atmel mci";
536c86c0155SWenyou Yang 	cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
537c86c0155SWenyou Yang 
538c86c0155SWenyou Yang 	/*
539c86c0155SWenyou Yang 	 * If the version is above 3.0, the capabilities of the 8-bit
540c86c0155SWenyou Yang 	 * bus width and high speed are supported.
541c86c0155SWenyou Yang 	 */
542722b150eSWenyou.Yang@microchip.com 	version = atmel_mci_get_version(plat->mci);
543c86c0155SWenyou Yang 	if ((version & 0xf00) >= 0x300) {
544c86c0155SWenyou Yang 		cfg->host_caps = MMC_MODE_8BIT |
545c86c0155SWenyou Yang 				 MMC_MODE_HS | MMC_MODE_HS_52MHz;
546c86c0155SWenyou Yang 	}
547c86c0155SWenyou Yang 
548c86c0155SWenyou Yang 	cfg->host_caps |= MMC_MODE_4BIT;
549c86c0155SWenyou Yang 	cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
550c86c0155SWenyou Yang 	cfg->f_min = priv->bus_clk_rate / (2 * 256);
551c86c0155SWenyou Yang 	cfg->f_max = priv->bus_clk_rate / 2;
552c86c0155SWenyou Yang }
553c86c0155SWenyou Yang 
554c86c0155SWenyou Yang static int atmel_mci_enable_clk(struct udevice *dev)
555c86c0155SWenyou Yang {
556c86c0155SWenyou Yang 	struct atmel_mci_priv *priv = dev_get_priv(dev);
557c86c0155SWenyou Yang 	struct clk clk;
558c86c0155SWenyou Yang 	ulong clk_rate;
559c86c0155SWenyou Yang 	int ret = 0;
560c86c0155SWenyou Yang 
561c86c0155SWenyou Yang 	ret = clk_get_by_index(dev, 0, &clk);
562c86c0155SWenyou Yang 	if (ret) {
563c86c0155SWenyou Yang 		ret = -EINVAL;
564c86c0155SWenyou Yang 		goto failed;
565c86c0155SWenyou Yang 	}
566c86c0155SWenyou Yang 
567c86c0155SWenyou Yang 	ret = clk_enable(&clk);
568c86c0155SWenyou Yang 	if (ret)
569c86c0155SWenyou Yang 		goto failed;
570c86c0155SWenyou Yang 
571c86c0155SWenyou Yang 	clk_rate = clk_get_rate(&clk);
572c86c0155SWenyou Yang 	if (!clk_rate) {
573c86c0155SWenyou Yang 		ret = -EINVAL;
574c86c0155SWenyou Yang 		goto failed;
575c86c0155SWenyou Yang 	}
576c86c0155SWenyou Yang 
577c86c0155SWenyou Yang 	priv->bus_clk_rate = clk_rate;
578c86c0155SWenyou Yang 
579c86c0155SWenyou Yang failed:
580c86c0155SWenyou Yang 	clk_free(&clk);
581c86c0155SWenyou Yang 
582c86c0155SWenyou Yang 	return ret;
583c86c0155SWenyou Yang }
584c86c0155SWenyou Yang 
585c86c0155SWenyou Yang static int atmel_mci_probe(struct udevice *dev)
586c86c0155SWenyou Yang {
587c86c0155SWenyou Yang 	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
588722b150eSWenyou.Yang@microchip.com 	struct atmel_mci_plat *plat = dev_get_platdata(dev);
589c86c0155SWenyou Yang 	struct mmc *mmc;
590c86c0155SWenyou Yang 	int ret;
591c86c0155SWenyou Yang 
592c86c0155SWenyou Yang 	ret = atmel_mci_enable_clk(dev);
593c86c0155SWenyou Yang 	if (ret)
594c86c0155SWenyou Yang 		return ret;
595c86c0155SWenyou Yang 
596722b150eSWenyou.Yang@microchip.com 	plat->mci = (struct atmel_mci *)devfdt_get_addr_ptr(dev);
597c86c0155SWenyou Yang 
598722b150eSWenyou.Yang@microchip.com 	atmel_mci_setup_cfg(dev);
599c86c0155SWenyou Yang 
600722b150eSWenyou.Yang@microchip.com 	mmc = &plat->mmc;
601722b150eSWenyou.Yang@microchip.com 	mmc->cfg = &plat->cfg;
602c86c0155SWenyou Yang 	mmc->dev = dev;
603c86c0155SWenyou Yang 	upriv->mmc = mmc;
604c86c0155SWenyou Yang 
605722b150eSWenyou.Yang@microchip.com 	atmel_mci_hw_init(dev);
606c86c0155SWenyou Yang 
607c86c0155SWenyou Yang 	return 0;
608c86c0155SWenyou Yang }
609c86c0155SWenyou Yang 
610c86c0155SWenyou Yang static int atmel_mci_bind(struct udevice *dev)
611c86c0155SWenyou Yang {
612722b150eSWenyou.Yang@microchip.com 	struct atmel_mci_plat *plat = dev_get_platdata(dev);
613c86c0155SWenyou Yang 
614722b150eSWenyou.Yang@microchip.com 	return mmc_bind(dev, &plat->mmc, &plat->cfg);
615c86c0155SWenyou Yang }
616c86c0155SWenyou Yang 
617c86c0155SWenyou Yang static const struct udevice_id atmel_mci_ids[] = {
618c86c0155SWenyou Yang 	{ .compatible = "atmel,hsmci" },
619c86c0155SWenyou Yang 	{ }
620c86c0155SWenyou Yang };
621c86c0155SWenyou Yang 
622c86c0155SWenyou Yang U_BOOT_DRIVER(atmel_mci) = {
623c86c0155SWenyou Yang 	.name = "atmel-mci",
624c86c0155SWenyou Yang 	.id = UCLASS_MMC,
625c86c0155SWenyou Yang 	.of_match = atmel_mci_ids,
626c86c0155SWenyou Yang 	.bind = atmel_mci_bind,
627c86c0155SWenyou Yang 	.probe = atmel_mci_probe,
628722b150eSWenyou.Yang@microchip.com 	.platdata_auto_alloc_size = sizeof(struct atmel_mci_plat),
629c86c0155SWenyou Yang 	.priv_auto_alloc_size = sizeof(struct atmel_mci_priv),
630c86c0155SWenyou Yang 	.ops = &atmel_mci_mmc_ops,
631c86c0155SWenyou Yang };
632c86c0155SWenyou Yang #endif
633