| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * MMCIF eMMC driver. | 
 | 3 |  * | 
 | 4 |  * Copyright (C) 2010 Renesas Solutions Corp. | 
 | 5 |  * Yusuke Goda <yusuke.goda.sx@renesas.com> | 
 | 6 |  * | 
 | 7 |  * This program is free software; you can redistribute it and/or modify | 
 | 8 |  * it under the terms of the GNU General Public License as published by | 
 | 9 |  * the Free Software Foundation; either version 2 of the License. | 
 | 10 |  * | 
 | 11 |  * | 
 | 12 |  * TODO | 
 | 13 |  *  1. DMA | 
 | 14 |  *  2. Power management | 
 | 15 |  *  3. Handle MMC errors better | 
 | 16 |  * | 
 | 17 |  */ | 
 | 18 |  | 
| Guennadi Liakhovetski | aa0787a | 2010-11-24 10:05:12 +0000 | [diff] [blame] | 19 | #include <linux/clk.h> | 
 | 20 | #include <linux/completion.h> | 
| Guennadi Liakhovetski | e47bf32 | 2010-11-24 10:05:18 +0000 | [diff] [blame] | 21 | #include <linux/delay.h> | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 22 | #include <linux/dma-mapping.h> | 
| Guennadi Liakhovetski | a782d68 | 2010-11-24 10:05:22 +0000 | [diff] [blame] | 23 | #include <linux/dmaengine.h> | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 24 | #include <linux/mmc/card.h> | 
 | 25 | #include <linux/mmc/core.h> | 
| Guennadi Liakhovetski | e47bf32 | 2010-11-24 10:05:18 +0000 | [diff] [blame] | 26 | #include <linux/mmc/host.h> | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 27 | #include <linux/mmc/mmc.h> | 
 | 28 | #include <linux/mmc/sdio.h> | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 29 | #include <linux/mmc/sh_mmcif.h> | 
| Guennadi Liakhovetski | a782d68 | 2010-11-24 10:05:22 +0000 | [diff] [blame] | 30 | #include <linux/pagemap.h> | 
| Guennadi Liakhovetski | e47bf32 | 2010-11-24 10:05:18 +0000 | [diff] [blame] | 31 | #include <linux/platform_device.h> | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 32 |  | 
 | 33 | #define DRIVER_NAME	"sh_mmcif" | 
 | 34 | #define DRIVER_VERSION	"2010-04-28" | 
 | 35 |  | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 36 | /* CE_CMD_SET */ | 
 | 37 | #define CMD_MASK		0x3f000000 | 
 | 38 | #define CMD_SET_RTYP_NO		((0 << 23) | (0 << 22)) | 
 | 39 | #define CMD_SET_RTYP_6B		((0 << 23) | (1 << 22)) /* R1/R1b/R3/R4/R5 */ | 
 | 40 | #define CMD_SET_RTYP_17B	((1 << 23) | (0 << 22)) /* R2 */ | 
 | 41 | #define CMD_SET_RBSY		(1 << 21) /* R1b */ | 
 | 42 | #define CMD_SET_CCSEN		(1 << 20) | 
 | 43 | #define CMD_SET_WDAT		(1 << 19) /* 1: on data, 0: no data */ | 
 | 44 | #define CMD_SET_DWEN		(1 << 18) /* 1: write, 0: read */ | 
 | 45 | #define CMD_SET_CMLTE		(1 << 17) /* 1: multi block trans, 0: single */ | 
 | 46 | #define CMD_SET_CMD12EN		(1 << 16) /* 1: CMD12 auto issue */ | 
 | 47 | #define CMD_SET_RIDXC_INDEX	((0 << 15) | (0 << 14)) /* index check */ | 
 | 48 | #define CMD_SET_RIDXC_BITS	((0 << 15) | (1 << 14)) /* check bits check */ | 
 | 49 | #define CMD_SET_RIDXC_NO	((1 << 15) | (0 << 14)) /* no check */ | 
 | 50 | #define CMD_SET_CRC7C		((0 << 13) | (0 << 12)) /* CRC7 check*/ | 
 | 51 | #define CMD_SET_CRC7C_BITS	((0 << 13) | (1 << 12)) /* check bits check*/ | 
 | 52 | #define CMD_SET_CRC7C_INTERNAL	((1 << 13) | (0 << 12)) /* internal CRC7 check*/ | 
 | 53 | #define CMD_SET_CRC16C		(1 << 10) /* 0: CRC16 check*/ | 
 | 54 | #define CMD_SET_CRCSTE		(1 << 8) /* 1: not receive CRC status */ | 
 | 55 | #define CMD_SET_TBIT		(1 << 7) /* 1: tran mission bit "Low" */ | 
 | 56 | #define CMD_SET_OPDM		(1 << 6) /* 1: open/drain */ | 
 | 57 | #define CMD_SET_CCSH		(1 << 5) | 
 | 58 | #define CMD_SET_DATW_1		((0 << 1) | (0 << 0)) /* 1bit */ | 
 | 59 | #define CMD_SET_DATW_4		((0 << 1) | (1 << 0)) /* 4bit */ | 
 | 60 | #define CMD_SET_DATW_8		((1 << 1) | (0 << 0)) /* 8bit */ | 
 | 61 |  | 
 | 62 | /* CE_CMD_CTRL */ | 
 | 63 | #define CMD_CTRL_BREAK		(1 << 0) | 
 | 64 |  | 
 | 65 | /* CE_BLOCK_SET */ | 
 | 66 | #define BLOCK_SIZE_MASK		0x0000ffff | 
 | 67 |  | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 68 | /* CE_INT */ | 
 | 69 | #define INT_CCSDE		(1 << 29) | 
 | 70 | #define INT_CMD12DRE		(1 << 26) | 
 | 71 | #define INT_CMD12RBE		(1 << 25) | 
 | 72 | #define INT_CMD12CRE		(1 << 24) | 
 | 73 | #define INT_DTRANE		(1 << 23) | 
 | 74 | #define INT_BUFRE		(1 << 22) | 
 | 75 | #define INT_BUFWEN		(1 << 21) | 
 | 76 | #define INT_BUFREN		(1 << 20) | 
 | 77 | #define INT_CCSRCV		(1 << 19) | 
 | 78 | #define INT_RBSYE		(1 << 17) | 
 | 79 | #define INT_CRSPE		(1 << 16) | 
 | 80 | #define INT_CMDVIO		(1 << 15) | 
 | 81 | #define INT_BUFVIO		(1 << 14) | 
 | 82 | #define INT_WDATERR		(1 << 11) | 
 | 83 | #define INT_RDATERR		(1 << 10) | 
 | 84 | #define INT_RIDXERR		(1 << 9) | 
 | 85 | #define INT_RSPERR		(1 << 8) | 
 | 86 | #define INT_CCSTO		(1 << 5) | 
 | 87 | #define INT_CRCSTO		(1 << 4) | 
 | 88 | #define INT_WDATTO		(1 << 3) | 
 | 89 | #define INT_RDATTO		(1 << 2) | 
 | 90 | #define INT_RBSYTO		(1 << 1) | 
 | 91 | #define INT_RSPTO		(1 << 0) | 
 | 92 | #define INT_ERR_STS		(INT_CMDVIO | INT_BUFVIO | INT_WDATERR |  \ | 
 | 93 | 				 INT_RDATERR | INT_RIDXERR | INT_RSPERR | \ | 
 | 94 | 				 INT_CCSTO | INT_CRCSTO | INT_WDATTO |	  \ | 
 | 95 | 				 INT_RDATTO | INT_RBSYTO | INT_RSPTO) | 
 | 96 |  | 
 | 97 | /* CE_INT_MASK */ | 
 | 98 | #define MASK_ALL		0x00000000 | 
 | 99 | #define MASK_MCCSDE		(1 << 29) | 
 | 100 | #define MASK_MCMD12DRE		(1 << 26) | 
 | 101 | #define MASK_MCMD12RBE		(1 << 25) | 
 | 102 | #define MASK_MCMD12CRE		(1 << 24) | 
 | 103 | #define MASK_MDTRANE		(1 << 23) | 
 | 104 | #define MASK_MBUFRE		(1 << 22) | 
 | 105 | #define MASK_MBUFWEN		(1 << 21) | 
 | 106 | #define MASK_MBUFREN		(1 << 20) | 
 | 107 | #define MASK_MCCSRCV		(1 << 19) | 
 | 108 | #define MASK_MRBSYE		(1 << 17) | 
 | 109 | #define MASK_MCRSPE		(1 << 16) | 
 | 110 | #define MASK_MCMDVIO		(1 << 15) | 
 | 111 | #define MASK_MBUFVIO		(1 << 14) | 
 | 112 | #define MASK_MWDATERR		(1 << 11) | 
 | 113 | #define MASK_MRDATERR		(1 << 10) | 
 | 114 | #define MASK_MRIDXERR		(1 << 9) | 
 | 115 | #define MASK_MRSPERR		(1 << 8) | 
 | 116 | #define MASK_MCCSTO		(1 << 5) | 
 | 117 | #define MASK_MCRCSTO		(1 << 4) | 
 | 118 | #define MASK_MWDATTO		(1 << 3) | 
 | 119 | #define MASK_MRDATTO		(1 << 2) | 
 | 120 | #define MASK_MRBSYTO		(1 << 1) | 
 | 121 | #define MASK_MRSPTO		(1 << 0) | 
 | 122 |  | 
 | 123 | /* CE_HOST_STS1 */ | 
 | 124 | #define STS1_CMDSEQ		(1 << 31) | 
 | 125 |  | 
 | 126 | /* CE_HOST_STS2 */ | 
 | 127 | #define STS2_CRCSTE		(1 << 31) | 
 | 128 | #define STS2_CRC16E		(1 << 30) | 
 | 129 | #define STS2_AC12CRCE		(1 << 29) | 
 | 130 | #define STS2_RSPCRC7E		(1 << 28) | 
 | 131 | #define STS2_CRCSTEBE		(1 << 27) | 
 | 132 | #define STS2_RDATEBE		(1 << 26) | 
 | 133 | #define STS2_AC12REBE		(1 << 25) | 
 | 134 | #define STS2_RSPEBE		(1 << 24) | 
 | 135 | #define STS2_AC12IDXE		(1 << 23) | 
 | 136 | #define STS2_RSPIDXE		(1 << 22) | 
 | 137 | #define STS2_CCSTO		(1 << 15) | 
 | 138 | #define STS2_RDATTO		(1 << 14) | 
 | 139 | #define STS2_DATBSYTO		(1 << 13) | 
 | 140 | #define STS2_CRCSTTO		(1 << 12) | 
 | 141 | #define STS2_AC12BSYTO		(1 << 11) | 
 | 142 | #define STS2_RSPBSYTO		(1 << 10) | 
 | 143 | #define STS2_AC12RSPTO		(1 << 9) | 
 | 144 | #define STS2_RSPTO		(1 << 8) | 
 | 145 | #define STS2_CRC_ERR		(STS2_CRCSTE | STS2_CRC16E |		\ | 
 | 146 | 				 STS2_AC12CRCE | STS2_RSPCRC7E | STS2_CRCSTEBE) | 
 | 147 | #define STS2_TIMEOUT_ERR	(STS2_CCSTO | STS2_RDATTO |		\ | 
 | 148 | 				 STS2_DATBSYTO | STS2_CRCSTTO |		\ | 
 | 149 | 				 STS2_AC12BSYTO | STS2_RSPBSYTO |	\ | 
 | 150 | 				 STS2_AC12RSPTO | STS2_RSPTO) | 
 | 151 |  | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 152 | #define CLKDEV_EMMC_DATA	52000000 /* 52MHz */ | 
 | 153 | #define CLKDEV_MMC_DATA		20000000 /* 20MHz */ | 
 | 154 | #define CLKDEV_INIT		400000   /* 400 KHz */ | 
 | 155 |  | 
 | 156 | struct sh_mmcif_host { | 
 | 157 | 	struct mmc_host *mmc; | 
 | 158 | 	struct mmc_data *data; | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 159 | 	struct platform_device *pd; | 
 | 160 | 	struct clk *hclk; | 
 | 161 | 	unsigned int clk; | 
 | 162 | 	int bus_width; | 
| Guennadi Liakhovetski | aa0787a | 2010-11-24 10:05:12 +0000 | [diff] [blame] | 163 | 	bool sd_error; | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 164 | 	long timeout; | 
 | 165 | 	void __iomem *addr; | 
| Guennadi Liakhovetski | aa0787a | 2010-11-24 10:05:12 +0000 | [diff] [blame] | 166 | 	struct completion intr_wait; | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 167 |  | 
| Guennadi Liakhovetski | a782d68 | 2010-11-24 10:05:22 +0000 | [diff] [blame] | 168 | 	/* DMA support */ | 
 | 169 | 	struct dma_chan		*chan_rx; | 
 | 170 | 	struct dma_chan		*chan_tx; | 
 | 171 | 	struct completion	dma_complete; | 
 | 172 | 	unsigned int            dma_sglen; | 
 | 173 | }; | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 174 |  | 
 | 175 | static inline void sh_mmcif_bitset(struct sh_mmcif_host *host, | 
 | 176 | 					unsigned int reg, u32 val) | 
 | 177 | { | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 178 | 	writel(val | readl(host->addr + reg), host->addr + reg); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 179 | } | 
 | 180 |  | 
 | 181 | static inline void sh_mmcif_bitclr(struct sh_mmcif_host *host, | 
 | 182 | 					unsigned int reg, u32 val) | 
 | 183 | { | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 184 | 	writel(~val & readl(host->addr + reg), host->addr + reg); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 185 | } | 
 | 186 |  | 
| Guennadi Liakhovetski | a782d68 | 2010-11-24 10:05:22 +0000 | [diff] [blame] | 187 | static void mmcif_dma_complete(void *arg) | 
 | 188 | { | 
 | 189 | 	struct sh_mmcif_host *host = arg; | 
 | 190 | 	dev_dbg(&host->pd->dev, "Command completed\n"); | 
 | 191 |  | 
 | 192 | 	if (WARN(!host->data, "%s: NULL data in DMA completion!\n", | 
 | 193 | 		 dev_name(&host->pd->dev))) | 
 | 194 | 		return; | 
 | 195 |  | 
 | 196 | 	if (host->data->flags & MMC_DATA_READ) | 
 | 197 | 		dma_unmap_sg(&host->pd->dev, host->data->sg, host->dma_sglen, | 
 | 198 | 			     DMA_FROM_DEVICE); | 
 | 199 | 	else | 
 | 200 | 		dma_unmap_sg(&host->pd->dev, host->data->sg, host->dma_sglen, | 
 | 201 | 			     DMA_TO_DEVICE); | 
 | 202 |  | 
 | 203 | 	complete(&host->dma_complete); | 
 | 204 | } | 
 | 205 |  | 
 | 206 | static void sh_mmcif_start_dma_rx(struct sh_mmcif_host *host) | 
 | 207 | { | 
 | 208 | 	struct scatterlist *sg = host->data->sg; | 
 | 209 | 	struct dma_async_tx_descriptor *desc = NULL; | 
 | 210 | 	struct dma_chan *chan = host->chan_rx; | 
 | 211 | 	dma_cookie_t cookie = -EINVAL; | 
 | 212 | 	int ret; | 
 | 213 |  | 
 | 214 | 	ret = dma_map_sg(&host->pd->dev, sg, host->data->sg_len, DMA_FROM_DEVICE); | 
 | 215 | 	if (ret > 0) { | 
 | 216 | 		host->dma_sglen = ret; | 
 | 217 | 		desc = chan->device->device_prep_slave_sg(chan, sg, ret, | 
 | 218 | 			DMA_FROM_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | 
 | 219 | 	} | 
 | 220 |  | 
 | 221 | 	if (desc) { | 
 | 222 | 		desc->callback = mmcif_dma_complete; | 
 | 223 | 		desc->callback_param = host; | 
 | 224 | 		cookie = desc->tx_submit(desc); | 
 | 225 | 		if (cookie < 0) { | 
 | 226 | 			desc = NULL; | 
 | 227 | 			ret = cookie; | 
 | 228 | 		} else { | 
 | 229 | 			sh_mmcif_bitset(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN); | 
 | 230 | 			chan->device->device_issue_pending(chan); | 
 | 231 | 		} | 
 | 232 | 	} | 
 | 233 | 	dev_dbg(&host->pd->dev, "%s(): mapped %d -> %d, cookie %d\n", | 
 | 234 | 		__func__, host->data->sg_len, ret, cookie); | 
 | 235 |  | 
 | 236 | 	if (!desc) { | 
 | 237 | 		/* DMA failed, fall back to PIO */ | 
 | 238 | 		if (ret >= 0) | 
 | 239 | 			ret = -EIO; | 
 | 240 | 		host->chan_rx = NULL; | 
 | 241 | 		host->dma_sglen = 0; | 
 | 242 | 		dma_release_channel(chan); | 
 | 243 | 		/* Free the Tx channel too */ | 
 | 244 | 		chan = host->chan_tx; | 
 | 245 | 		if (chan) { | 
 | 246 | 			host->chan_tx = NULL; | 
 | 247 | 			dma_release_channel(chan); | 
 | 248 | 		} | 
 | 249 | 		dev_warn(&host->pd->dev, | 
 | 250 | 			 "DMA failed: %d, falling back to PIO\n", ret); | 
 | 251 | 		sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN | BUF_ACC_DMAWEN); | 
 | 252 | 	} | 
 | 253 |  | 
 | 254 | 	dev_dbg(&host->pd->dev, "%s(): desc %p, cookie %d, sg[%d]\n", __func__, | 
 | 255 | 		desc, cookie, host->data->sg_len); | 
 | 256 | } | 
 | 257 |  | 
 | 258 | static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host) | 
 | 259 | { | 
 | 260 | 	struct scatterlist *sg = host->data->sg; | 
 | 261 | 	struct dma_async_tx_descriptor *desc = NULL; | 
 | 262 | 	struct dma_chan *chan = host->chan_tx; | 
 | 263 | 	dma_cookie_t cookie = -EINVAL; | 
 | 264 | 	int ret; | 
 | 265 |  | 
 | 266 | 	ret = dma_map_sg(&host->pd->dev, sg, host->data->sg_len, DMA_TO_DEVICE); | 
 | 267 | 	if (ret > 0) { | 
 | 268 | 		host->dma_sglen = ret; | 
 | 269 | 		desc = chan->device->device_prep_slave_sg(chan, sg, ret, | 
 | 270 | 			DMA_TO_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | 
 | 271 | 	} | 
 | 272 |  | 
 | 273 | 	if (desc) { | 
 | 274 | 		desc->callback = mmcif_dma_complete; | 
 | 275 | 		desc->callback_param = host; | 
 | 276 | 		cookie = desc->tx_submit(desc); | 
 | 277 | 		if (cookie < 0) { | 
 | 278 | 			desc = NULL; | 
 | 279 | 			ret = cookie; | 
 | 280 | 		} else { | 
 | 281 | 			sh_mmcif_bitset(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAWEN); | 
 | 282 | 			chan->device->device_issue_pending(chan); | 
 | 283 | 		} | 
 | 284 | 	} | 
 | 285 | 	dev_dbg(&host->pd->dev, "%s(): mapped %d -> %d, cookie %d\n", | 
 | 286 | 		__func__, host->data->sg_len, ret, cookie); | 
 | 287 |  | 
 | 288 | 	if (!desc) { | 
 | 289 | 		/* DMA failed, fall back to PIO */ | 
 | 290 | 		if (ret >= 0) | 
 | 291 | 			ret = -EIO; | 
 | 292 | 		host->chan_tx = NULL; | 
 | 293 | 		host->dma_sglen = 0; | 
 | 294 | 		dma_release_channel(chan); | 
 | 295 | 		/* Free the Rx channel too */ | 
 | 296 | 		chan = host->chan_rx; | 
 | 297 | 		if (chan) { | 
 | 298 | 			host->chan_rx = NULL; | 
 | 299 | 			dma_release_channel(chan); | 
 | 300 | 		} | 
 | 301 | 		dev_warn(&host->pd->dev, | 
 | 302 | 			 "DMA failed: %d, falling back to PIO\n", ret); | 
 | 303 | 		sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN | BUF_ACC_DMAWEN); | 
 | 304 | 	} | 
 | 305 |  | 
 | 306 | 	dev_dbg(&host->pd->dev, "%s(): desc %p, cookie %d\n", __func__, | 
 | 307 | 		desc, cookie); | 
 | 308 | } | 
 | 309 |  | 
 | 310 | static bool sh_mmcif_filter(struct dma_chan *chan, void *arg) | 
 | 311 | { | 
 | 312 | 	dev_dbg(chan->device->dev, "%s: slave data %p\n", __func__, arg); | 
 | 313 | 	chan->private = arg; | 
 | 314 | 	return true; | 
 | 315 | } | 
 | 316 |  | 
 | 317 | static void sh_mmcif_request_dma(struct sh_mmcif_host *host, | 
 | 318 | 				 struct sh_mmcif_plat_data *pdata) | 
 | 319 | { | 
 | 320 | 	host->dma_sglen = 0; | 
 | 321 |  | 
 | 322 | 	/* We can only either use DMA for both Tx and Rx or not use it at all */ | 
 | 323 | 	if (pdata->dma) { | 
 | 324 | 		dma_cap_mask_t mask; | 
 | 325 |  | 
 | 326 | 		dma_cap_zero(mask); | 
 | 327 | 		dma_cap_set(DMA_SLAVE, mask); | 
 | 328 |  | 
 | 329 | 		host->chan_tx = dma_request_channel(mask, sh_mmcif_filter, | 
 | 330 | 						    &pdata->dma->chan_priv_tx); | 
 | 331 | 		dev_dbg(&host->pd->dev, "%s: TX: got channel %p\n", __func__, | 
 | 332 | 			host->chan_tx); | 
 | 333 |  | 
 | 334 | 		if (!host->chan_tx) | 
 | 335 | 			return; | 
 | 336 |  | 
 | 337 | 		host->chan_rx = dma_request_channel(mask, sh_mmcif_filter, | 
 | 338 | 						    &pdata->dma->chan_priv_rx); | 
 | 339 | 		dev_dbg(&host->pd->dev, "%s: RX: got channel %p\n", __func__, | 
 | 340 | 			host->chan_rx); | 
 | 341 |  | 
 | 342 | 		if (!host->chan_rx) { | 
 | 343 | 			dma_release_channel(host->chan_tx); | 
 | 344 | 			host->chan_tx = NULL; | 
 | 345 | 			return; | 
 | 346 | 		} | 
 | 347 |  | 
 | 348 | 		init_completion(&host->dma_complete); | 
 | 349 | 	} | 
 | 350 | } | 
 | 351 |  | 
 | 352 | static void sh_mmcif_release_dma(struct sh_mmcif_host *host) | 
 | 353 | { | 
 | 354 | 	sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN | BUF_ACC_DMAWEN); | 
 | 355 | 	/* Descriptors are freed automatically */ | 
 | 356 | 	if (host->chan_tx) { | 
 | 357 | 		struct dma_chan *chan = host->chan_tx; | 
 | 358 | 		host->chan_tx = NULL; | 
 | 359 | 		dma_release_channel(chan); | 
 | 360 | 	} | 
 | 361 | 	if (host->chan_rx) { | 
 | 362 | 		struct dma_chan *chan = host->chan_rx; | 
 | 363 | 		host->chan_rx = NULL; | 
 | 364 | 		dma_release_channel(chan); | 
 | 365 | 	} | 
 | 366 |  | 
 | 367 | 	host->dma_sglen = 0; | 
 | 368 | } | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 369 |  | 
 | 370 | static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk) | 
 | 371 | { | 
 | 372 | 	struct sh_mmcif_plat_data *p = host->pd->dev.platform_data; | 
 | 373 |  | 
 | 374 | 	sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE); | 
 | 375 | 	sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR); | 
 | 376 |  | 
 | 377 | 	if (!clk) | 
 | 378 | 		return; | 
 | 379 | 	if (p->sup_pclk && clk == host->clk) | 
 | 380 | 		sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_SUP_PCLK); | 
 | 381 | 	else | 
 | 382 | 		sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR & | 
 | 383 | 			(ilog2(__rounddown_pow_of_two(host->clk / clk)) << 16)); | 
 | 384 |  | 
 | 385 | 	sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE); | 
 | 386 | } | 
 | 387 |  | 
 | 388 | static void sh_mmcif_sync_reset(struct sh_mmcif_host *host) | 
 | 389 | { | 
 | 390 | 	u32 tmp; | 
 | 391 |  | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 392 | 	tmp = 0x010f0000 & sh_mmcif_readl(host->addr, MMCIF_CE_CLK_CTRL); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 393 |  | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 394 | 	sh_mmcif_writel(host->addr, MMCIF_CE_VERSION, SOFT_RST_ON); | 
 | 395 | 	sh_mmcif_writel(host->addr, MMCIF_CE_VERSION, SOFT_RST_OFF); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 396 | 	sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, tmp | | 
 | 397 | 		SRSPTO_256 | SRBSYTO_29 | SRWDTO_29 | SCCSTO_29); | 
 | 398 | 	/* byte swap on */ | 
 | 399 | 	sh_mmcif_bitset(host, MMCIF_CE_BUF_ACC, BUF_ACC_ATYP); | 
 | 400 | } | 
 | 401 |  | 
 | 402 | static int sh_mmcif_error_manage(struct sh_mmcif_host *host) | 
 | 403 | { | 
 | 404 | 	u32 state1, state2; | 
 | 405 | 	int ret, timeout = 10000000; | 
 | 406 |  | 
| Guennadi Liakhovetski | aa0787a | 2010-11-24 10:05:12 +0000 | [diff] [blame] | 407 | 	host->sd_error = false; | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 408 |  | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 409 | 	state1 = sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS1); | 
 | 410 | 	state2 = sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS2); | 
| Guennadi Liakhovetski | e47bf32 | 2010-11-24 10:05:18 +0000 | [diff] [blame] | 411 | 	dev_dbg(&host->pd->dev, "ERR HOST_STS1 = %08x\n", state1); | 
 | 412 | 	dev_dbg(&host->pd->dev, "ERR HOST_STS2 = %08x\n", state2); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 413 |  | 
 | 414 | 	if (state1 & STS1_CMDSEQ) { | 
 | 415 | 		sh_mmcif_bitset(host, MMCIF_CE_CMD_CTRL, CMD_CTRL_BREAK); | 
 | 416 | 		sh_mmcif_bitset(host, MMCIF_CE_CMD_CTRL, ~CMD_CTRL_BREAK); | 
 | 417 | 		while (1) { | 
 | 418 | 			timeout--; | 
 | 419 | 			if (timeout < 0) { | 
| Guennadi Liakhovetski | e47bf32 | 2010-11-24 10:05:18 +0000 | [diff] [blame] | 420 | 				dev_err(&host->pd->dev, | 
 | 421 | 					"Forceed end of command sequence timeout err\n"); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 422 | 				return -EIO; | 
 | 423 | 			} | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 424 | 			if (!(sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS1) | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 425 | 								& STS1_CMDSEQ)) | 
 | 426 | 				break; | 
 | 427 | 			mdelay(1); | 
 | 428 | 		} | 
 | 429 | 		sh_mmcif_sync_reset(host); | 
| Guennadi Liakhovetski | e47bf32 | 2010-11-24 10:05:18 +0000 | [diff] [blame] | 430 | 		dev_dbg(&host->pd->dev, "Forced end of command sequence\n"); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 431 | 		return -EIO; | 
 | 432 | 	} | 
 | 433 |  | 
 | 434 | 	if (state2 & STS2_CRC_ERR) { | 
| Guennadi Liakhovetski | e47bf32 | 2010-11-24 10:05:18 +0000 | [diff] [blame] | 435 | 		dev_dbg(&host->pd->dev, ": Happened CRC error\n"); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 436 | 		ret = -EIO; | 
 | 437 | 	} else if (state2 & STS2_TIMEOUT_ERR) { | 
| Guennadi Liakhovetski | e47bf32 | 2010-11-24 10:05:18 +0000 | [diff] [blame] | 438 | 		dev_dbg(&host->pd->dev, ": Happened Timeout error\n"); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 439 | 		ret = -ETIMEDOUT; | 
 | 440 | 	} else { | 
| Guennadi Liakhovetski | e47bf32 | 2010-11-24 10:05:18 +0000 | [diff] [blame] | 441 | 		dev_dbg(&host->pd->dev, ": Happened End/Index error\n"); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 442 | 		ret = -EIO; | 
 | 443 | 	} | 
 | 444 | 	return ret; | 
 | 445 | } | 
 | 446 |  | 
 | 447 | static int sh_mmcif_single_read(struct sh_mmcif_host *host, | 
 | 448 | 					struct mmc_request *mrq) | 
 | 449 | { | 
 | 450 | 	struct mmc_data *data = mrq->data; | 
 | 451 | 	long time; | 
 | 452 | 	u32 blocksize, i, *p = sg_virt(data->sg); | 
 | 453 |  | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 454 | 	/* buf read enable */ | 
 | 455 | 	sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); | 
| Guennadi Liakhovetski | aa0787a | 2010-11-24 10:05:12 +0000 | [diff] [blame] | 456 | 	time = wait_for_completion_interruptible_timeout(&host->intr_wait, | 
 | 457 | 			host->timeout); | 
 | 458 | 	if (time <= 0 || host->sd_error) | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 459 | 		return sh_mmcif_error_manage(host); | 
 | 460 |  | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 461 | 	blocksize = (BLOCK_SIZE_MASK & | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 462 | 			sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET)) + 3; | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 463 | 	for (i = 0; i < blocksize / 4; i++) | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 464 | 		*p++ = sh_mmcif_readl(host->addr, MMCIF_CE_DATA); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 465 |  | 
 | 466 | 	/* buffer read end */ | 
 | 467 | 	sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFRE); | 
| Guennadi Liakhovetski | aa0787a | 2010-11-24 10:05:12 +0000 | [diff] [blame] | 468 | 	time = wait_for_completion_interruptible_timeout(&host->intr_wait, | 
 | 469 | 			host->timeout); | 
 | 470 | 	if (time <= 0 || host->sd_error) | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 471 | 		return sh_mmcif_error_manage(host); | 
 | 472 |  | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 473 | 	return 0; | 
 | 474 | } | 
 | 475 |  | 
 | 476 | static int sh_mmcif_multi_read(struct sh_mmcif_host *host, | 
 | 477 | 					struct mmc_request *mrq) | 
 | 478 | { | 
 | 479 | 	struct mmc_data *data = mrq->data; | 
 | 480 | 	long time; | 
 | 481 | 	u32 blocksize, i, j, sec, *p; | 
 | 482 |  | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 483 | 	blocksize = BLOCK_SIZE_MASK & sh_mmcif_readl(host->addr, | 
 | 484 | 						     MMCIF_CE_BLOCK_SET); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 485 | 	for (j = 0; j < data->sg_len; j++) { | 
 | 486 | 		p = sg_virt(data->sg); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 487 | 		for (sec = 0; sec < data->sg->length / blocksize; sec++) { | 
 | 488 | 			sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); | 
 | 489 | 			/* buf read enable */ | 
| Guennadi Liakhovetski | aa0787a | 2010-11-24 10:05:12 +0000 | [diff] [blame] | 490 | 			time = wait_for_completion_interruptible_timeout(&host->intr_wait, | 
 | 491 | 				host->timeout); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 492 |  | 
| Guennadi Liakhovetski | aa0787a | 2010-11-24 10:05:12 +0000 | [diff] [blame] | 493 | 			if (time <= 0 || host->sd_error) | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 494 | 				return sh_mmcif_error_manage(host); | 
 | 495 |  | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 496 | 			for (i = 0; i < blocksize / 4; i++) | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 497 | 				*p++ = sh_mmcif_readl(host->addr, | 
 | 498 | 						      MMCIF_CE_DATA); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 499 | 		} | 
 | 500 | 		if (j < data->sg_len - 1) | 
 | 501 | 			data->sg++; | 
 | 502 | 	} | 
 | 503 | 	return 0; | 
 | 504 | } | 
 | 505 |  | 
 | 506 | static int sh_mmcif_single_write(struct sh_mmcif_host *host, | 
 | 507 | 					struct mmc_request *mrq) | 
 | 508 | { | 
 | 509 | 	struct mmc_data *data = mrq->data; | 
 | 510 | 	long time; | 
 | 511 | 	u32 blocksize, i, *p = sg_virt(data->sg); | 
 | 512 |  | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 513 | 	sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); | 
 | 514 |  | 
 | 515 | 	/* buf write enable */ | 
| Guennadi Liakhovetski | aa0787a | 2010-11-24 10:05:12 +0000 | [diff] [blame] | 516 | 	time = wait_for_completion_interruptible_timeout(&host->intr_wait, | 
 | 517 | 			host->timeout); | 
 | 518 | 	if (time <= 0 || host->sd_error) | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 519 | 		return sh_mmcif_error_manage(host); | 
 | 520 |  | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 521 | 	blocksize = (BLOCK_SIZE_MASK & | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 522 | 			sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET)) + 3; | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 523 | 	for (i = 0; i < blocksize / 4; i++) | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 524 | 		sh_mmcif_writel(host->addr, MMCIF_CE_DATA, *p++); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 525 |  | 
 | 526 | 	/* buffer write end */ | 
 | 527 | 	sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MDTRANE); | 
 | 528 |  | 
| Guennadi Liakhovetski | aa0787a | 2010-11-24 10:05:12 +0000 | [diff] [blame] | 529 | 	time = wait_for_completion_interruptible_timeout(&host->intr_wait, | 
 | 530 | 			host->timeout); | 
 | 531 | 	if (time <= 0 || host->sd_error) | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 532 | 		return sh_mmcif_error_manage(host); | 
 | 533 |  | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 534 | 	return 0; | 
 | 535 | } | 
 | 536 |  | 
 | 537 | static int sh_mmcif_multi_write(struct sh_mmcif_host *host, | 
 | 538 | 						struct mmc_request *mrq) | 
 | 539 | { | 
 | 540 | 	struct mmc_data *data = mrq->data; | 
 | 541 | 	long time; | 
 | 542 | 	u32 i, sec, j, blocksize, *p; | 
 | 543 |  | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 544 | 	blocksize = BLOCK_SIZE_MASK & sh_mmcif_readl(host->addr, | 
 | 545 | 						     MMCIF_CE_BLOCK_SET); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 546 |  | 
 | 547 | 	for (j = 0; j < data->sg_len; j++) { | 
 | 548 | 		p = sg_virt(data->sg); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 549 | 		for (sec = 0; sec < data->sg->length / blocksize; sec++) { | 
 | 550 | 			sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); | 
 | 551 | 			/* buf write enable*/ | 
| Guennadi Liakhovetski | aa0787a | 2010-11-24 10:05:12 +0000 | [diff] [blame] | 552 | 			time = wait_for_completion_interruptible_timeout(&host->intr_wait, | 
 | 553 | 				host->timeout); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 554 |  | 
| Guennadi Liakhovetski | aa0787a | 2010-11-24 10:05:12 +0000 | [diff] [blame] | 555 | 			if (time <= 0 || host->sd_error) | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 556 | 				return sh_mmcif_error_manage(host); | 
 | 557 |  | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 558 | 			for (i = 0; i < blocksize / 4; i++) | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 559 | 				sh_mmcif_writel(host->addr, | 
 | 560 | 						MMCIF_CE_DATA, *p++); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 561 | 		} | 
 | 562 | 		if (j < data->sg_len - 1) | 
 | 563 | 			data->sg++; | 
 | 564 | 	} | 
 | 565 | 	return 0; | 
 | 566 | } | 
 | 567 |  | 
 | 568 | static void sh_mmcif_get_response(struct sh_mmcif_host *host, | 
 | 569 | 						struct mmc_command *cmd) | 
 | 570 | { | 
 | 571 | 	if (cmd->flags & MMC_RSP_136) { | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 572 | 		cmd->resp[0] = sh_mmcif_readl(host->addr, MMCIF_CE_RESP3); | 
 | 573 | 		cmd->resp[1] = sh_mmcif_readl(host->addr, MMCIF_CE_RESP2); | 
 | 574 | 		cmd->resp[2] = sh_mmcif_readl(host->addr, MMCIF_CE_RESP1); | 
 | 575 | 		cmd->resp[3] = sh_mmcif_readl(host->addr, MMCIF_CE_RESP0); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 576 | 	} else | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 577 | 		cmd->resp[0] = sh_mmcif_readl(host->addr, MMCIF_CE_RESP0); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 578 | } | 
 | 579 |  | 
 | 580 | static void sh_mmcif_get_cmd12response(struct sh_mmcif_host *host, | 
 | 581 | 						struct mmc_command *cmd) | 
 | 582 | { | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 583 | 	cmd->resp[0] = sh_mmcif_readl(host->addr, MMCIF_CE_RESP_CMD12); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 584 | } | 
 | 585 |  | 
 | 586 | static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host, | 
 | 587 | 		struct mmc_request *mrq, struct mmc_command *cmd, u32 opc) | 
 | 588 | { | 
 | 589 | 	u32 tmp = 0; | 
 | 590 |  | 
 | 591 | 	/* Response Type check */ | 
 | 592 | 	switch (mmc_resp_type(cmd)) { | 
 | 593 | 	case MMC_RSP_NONE: | 
 | 594 | 		tmp |= CMD_SET_RTYP_NO; | 
 | 595 | 		break; | 
 | 596 | 	case MMC_RSP_R1: | 
 | 597 | 	case MMC_RSP_R1B: | 
 | 598 | 	case MMC_RSP_R3: | 
 | 599 | 		tmp |= CMD_SET_RTYP_6B; | 
 | 600 | 		break; | 
 | 601 | 	case MMC_RSP_R2: | 
 | 602 | 		tmp |= CMD_SET_RTYP_17B; | 
 | 603 | 		break; | 
 | 604 | 	default: | 
| Guennadi Liakhovetski | e47bf32 | 2010-11-24 10:05:18 +0000 | [diff] [blame] | 605 | 		dev_err(&host->pd->dev, "Unsupported response type.\n"); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 606 | 		break; | 
 | 607 | 	} | 
 | 608 | 	switch (opc) { | 
 | 609 | 	/* RBSY */ | 
 | 610 | 	case MMC_SWITCH: | 
 | 611 | 	case MMC_STOP_TRANSMISSION: | 
 | 612 | 	case MMC_SET_WRITE_PROT: | 
 | 613 | 	case MMC_CLR_WRITE_PROT: | 
 | 614 | 	case MMC_ERASE: | 
 | 615 | 	case MMC_GEN_CMD: | 
 | 616 | 		tmp |= CMD_SET_RBSY; | 
 | 617 | 		break; | 
 | 618 | 	} | 
 | 619 | 	/* WDAT / DATW */ | 
 | 620 | 	if (host->data) { | 
 | 621 | 		tmp |= CMD_SET_WDAT; | 
 | 622 | 		switch (host->bus_width) { | 
 | 623 | 		case MMC_BUS_WIDTH_1: | 
 | 624 | 			tmp |= CMD_SET_DATW_1; | 
 | 625 | 			break; | 
 | 626 | 		case MMC_BUS_WIDTH_4: | 
 | 627 | 			tmp |= CMD_SET_DATW_4; | 
 | 628 | 			break; | 
 | 629 | 		case MMC_BUS_WIDTH_8: | 
 | 630 | 			tmp |= CMD_SET_DATW_8; | 
 | 631 | 			break; | 
 | 632 | 		default: | 
| Guennadi Liakhovetski | e47bf32 | 2010-11-24 10:05:18 +0000 | [diff] [blame] | 633 | 			dev_err(&host->pd->dev, "Unsupported bus width.\n"); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 634 | 			break; | 
 | 635 | 		} | 
 | 636 | 	} | 
 | 637 | 	/* DWEN */ | 
 | 638 | 	if (opc == MMC_WRITE_BLOCK || opc == MMC_WRITE_MULTIPLE_BLOCK) | 
 | 639 | 		tmp |= CMD_SET_DWEN; | 
 | 640 | 	/* CMLTE/CMD12EN */ | 
 | 641 | 	if (opc == MMC_READ_MULTIPLE_BLOCK || opc == MMC_WRITE_MULTIPLE_BLOCK) { | 
 | 642 | 		tmp |= CMD_SET_CMLTE | CMD_SET_CMD12EN; | 
 | 643 | 		sh_mmcif_bitset(host, MMCIF_CE_BLOCK_SET, | 
 | 644 | 					mrq->data->blocks << 16); | 
 | 645 | 	} | 
 | 646 | 	/* RIDXC[1:0] check bits */ | 
 | 647 | 	if (opc == MMC_SEND_OP_COND || opc == MMC_ALL_SEND_CID || | 
 | 648 | 	    opc == MMC_SEND_CSD || opc == MMC_SEND_CID) | 
 | 649 | 		tmp |= CMD_SET_RIDXC_BITS; | 
 | 650 | 	/* RCRC7C[1:0] check bits */ | 
 | 651 | 	if (opc == MMC_SEND_OP_COND) | 
 | 652 | 		tmp |= CMD_SET_CRC7C_BITS; | 
 | 653 | 	/* RCRC7C[1:0] internal CRC7 */ | 
 | 654 | 	if (opc == MMC_ALL_SEND_CID || | 
 | 655 | 		opc == MMC_SEND_CSD || opc == MMC_SEND_CID) | 
 | 656 | 		tmp |= CMD_SET_CRC7C_INTERNAL; | 
 | 657 |  | 
 | 658 | 	return opc = ((opc << 24) | tmp); | 
 | 659 | } | 
 | 660 |  | 
| Guennadi Liakhovetski | e47bf32 | 2010-11-24 10:05:18 +0000 | [diff] [blame] | 661 | static int sh_mmcif_data_trans(struct sh_mmcif_host *host, | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 662 | 				struct mmc_request *mrq, u32 opc) | 
 | 663 | { | 
| Guennadi Liakhovetski | e47bf32 | 2010-11-24 10:05:18 +0000 | [diff] [blame] | 664 | 	int ret; | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 665 |  | 
 | 666 | 	switch (opc) { | 
 | 667 | 	case MMC_READ_MULTIPLE_BLOCK: | 
 | 668 | 		ret = sh_mmcif_multi_read(host, mrq); | 
 | 669 | 		break; | 
 | 670 | 	case MMC_WRITE_MULTIPLE_BLOCK: | 
 | 671 | 		ret = sh_mmcif_multi_write(host, mrq); | 
 | 672 | 		break; | 
 | 673 | 	case MMC_WRITE_BLOCK: | 
 | 674 | 		ret = sh_mmcif_single_write(host, mrq); | 
 | 675 | 		break; | 
 | 676 | 	case MMC_READ_SINGLE_BLOCK: | 
 | 677 | 	case MMC_SEND_EXT_CSD: | 
 | 678 | 		ret = sh_mmcif_single_read(host, mrq); | 
 | 679 | 		break; | 
 | 680 | 	default: | 
| Guennadi Liakhovetski | e47bf32 | 2010-11-24 10:05:18 +0000 | [diff] [blame] | 681 | 		dev_err(&host->pd->dev, "UNSUPPORTED CMD = d'%08d\n", opc); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 682 | 		ret = -EINVAL; | 
 | 683 | 		break; | 
 | 684 | 	} | 
 | 685 | 	return ret; | 
 | 686 | } | 
 | 687 |  | 
 | 688 | static void sh_mmcif_start_cmd(struct sh_mmcif_host *host, | 
 | 689 | 			struct mmc_request *mrq, struct mmc_command *cmd) | 
 | 690 | { | 
 | 691 | 	long time; | 
 | 692 | 	int ret = 0, mask = 0; | 
 | 693 | 	u32 opc = cmd->opcode; | 
 | 694 |  | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 695 | 	switch (opc) { | 
 | 696 | 	/* respons busy check */ | 
 | 697 | 	case MMC_SWITCH: | 
 | 698 | 	case MMC_STOP_TRANSMISSION: | 
 | 699 | 	case MMC_SET_WRITE_PROT: | 
 | 700 | 	case MMC_CLR_WRITE_PROT: | 
 | 701 | 	case MMC_ERASE: | 
 | 702 | 	case MMC_GEN_CMD: | 
 | 703 | 		mask = MASK_MRBSYE; | 
 | 704 | 		break; | 
 | 705 | 	default: | 
 | 706 | 		mask = MASK_MCRSPE; | 
 | 707 | 		break; | 
 | 708 | 	} | 
 | 709 | 	mask |=	MASK_MCMDVIO | MASK_MBUFVIO | MASK_MWDATERR | | 
 | 710 | 		MASK_MRDATERR | MASK_MRIDXERR | MASK_MRSPERR | | 
 | 711 | 		MASK_MCCSTO | MASK_MCRCSTO | MASK_MWDATTO | | 
 | 712 | 		MASK_MRDATTO | MASK_MRBSYTO | MASK_MRSPTO; | 
 | 713 |  | 
 | 714 | 	if (host->data) { | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 715 | 		sh_mmcif_writel(host->addr, MMCIF_CE_BLOCK_SET, 0); | 
 | 716 | 		sh_mmcif_writel(host->addr, MMCIF_CE_BLOCK_SET, | 
 | 717 | 				mrq->data->blksz); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 718 | 	} | 
 | 719 | 	opc = sh_mmcif_set_cmd(host, mrq, cmd, opc); | 
 | 720 |  | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 721 | 	sh_mmcif_writel(host->addr, MMCIF_CE_INT, 0xD80430C0); | 
 | 722 | 	sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, mask); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 723 | 	/* set arg */ | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 724 | 	sh_mmcif_writel(host->addr, MMCIF_CE_ARG, cmd->arg); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 725 | 	/* set cmd */ | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 726 | 	sh_mmcif_writel(host->addr, MMCIF_CE_CMD_SET, opc); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 727 |  | 
| Guennadi Liakhovetski | aa0787a | 2010-11-24 10:05:12 +0000 | [diff] [blame] | 728 | 	time = wait_for_completion_interruptible_timeout(&host->intr_wait, | 
 | 729 | 		host->timeout); | 
 | 730 | 	if (time <= 0) { | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 731 | 		cmd->error = sh_mmcif_error_manage(host); | 
 | 732 | 		return; | 
 | 733 | 	} | 
 | 734 | 	if (host->sd_error) { | 
 | 735 | 		switch (cmd->opcode) { | 
 | 736 | 		case MMC_ALL_SEND_CID: | 
 | 737 | 		case MMC_SELECT_CARD: | 
 | 738 | 		case MMC_APP_CMD: | 
 | 739 | 			cmd->error = -ETIMEDOUT; | 
 | 740 | 			break; | 
 | 741 | 		default: | 
| Guennadi Liakhovetski | e47bf32 | 2010-11-24 10:05:18 +0000 | [diff] [blame] | 742 | 			dev_dbg(&host->pd->dev, "Cmd(d'%d) err\n", | 
 | 743 | 					cmd->opcode); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 744 | 			cmd->error = sh_mmcif_error_manage(host); | 
 | 745 | 			break; | 
 | 746 | 		} | 
| Guennadi Liakhovetski | aa0787a | 2010-11-24 10:05:12 +0000 | [diff] [blame] | 747 | 		host->sd_error = false; | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 748 | 		return; | 
 | 749 | 	} | 
 | 750 | 	if (!(cmd->flags & MMC_RSP_PRESENT)) { | 
| Guennadi Liakhovetski | e47bf32 | 2010-11-24 10:05:18 +0000 | [diff] [blame] | 751 | 		cmd->error = 0; | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 752 | 		return; | 
 | 753 | 	} | 
| Guennadi Liakhovetski | aa0787a | 2010-11-24 10:05:12 +0000 | [diff] [blame] | 754 | 	sh_mmcif_get_response(host, cmd); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 755 | 	if (host->data) { | 
| Guennadi Liakhovetski | a782d68 | 2010-11-24 10:05:22 +0000 | [diff] [blame] | 756 | 		if (!host->dma_sglen) { | 
 | 757 | 			ret = sh_mmcif_data_trans(host, mrq, cmd->opcode); | 
 | 758 | 		} else { | 
 | 759 | 			long time = | 
 | 760 | 				wait_for_completion_interruptible_timeout(&host->dma_complete, | 
 | 761 | 									  host->timeout); | 
 | 762 | 			if (!time) | 
 | 763 | 				ret = -ETIMEDOUT; | 
 | 764 | 			else if (time < 0) | 
 | 765 | 				ret = time; | 
 | 766 | 			sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC, | 
 | 767 | 					BUF_ACC_DMAREN | BUF_ACC_DMAWEN); | 
 | 768 | 			host->dma_sglen = 0; | 
 | 769 | 		} | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 770 | 		if (ret < 0) | 
 | 771 | 			mrq->data->bytes_xfered = 0; | 
 | 772 | 		else | 
 | 773 | 			mrq->data->bytes_xfered = | 
 | 774 | 				mrq->data->blocks * mrq->data->blksz; | 
 | 775 | 	} | 
 | 776 | 	cmd->error = ret; | 
 | 777 | } | 
 | 778 |  | 
 | 779 | static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host, | 
 | 780 | 		struct mmc_request *mrq, struct mmc_command *cmd) | 
 | 781 | { | 
 | 782 | 	long time; | 
 | 783 |  | 
 | 784 | 	if (mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK) | 
 | 785 | 		sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MCMD12DRE); | 
 | 786 | 	else if (mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK) | 
 | 787 | 		sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MCMD12RBE); | 
 | 788 | 	else { | 
| Guennadi Liakhovetski | e47bf32 | 2010-11-24 10:05:18 +0000 | [diff] [blame] | 789 | 		dev_err(&host->pd->dev, "unsupported stop cmd\n"); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 790 | 		cmd->error = sh_mmcif_error_manage(host); | 
 | 791 | 		return; | 
 | 792 | 	} | 
 | 793 |  | 
| Guennadi Liakhovetski | aa0787a | 2010-11-24 10:05:12 +0000 | [diff] [blame] | 794 | 	time = wait_for_completion_interruptible_timeout(&host->intr_wait, | 
 | 795 | 			host->timeout); | 
 | 796 | 	if (time <= 0 || host->sd_error) { | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 797 | 		cmd->error = sh_mmcif_error_manage(host); | 
 | 798 | 		return; | 
 | 799 | 	} | 
 | 800 | 	sh_mmcif_get_cmd12response(host, cmd); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 801 | 	cmd->error = 0; | 
 | 802 | } | 
 | 803 |  | 
 | 804 | static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) | 
 | 805 | { | 
 | 806 | 	struct sh_mmcif_host *host = mmc_priv(mmc); | 
 | 807 |  | 
 | 808 | 	switch (mrq->cmd->opcode) { | 
 | 809 | 	/* MMCIF does not support SD/SDIO command */ | 
 | 810 | 	case SD_IO_SEND_OP_COND: | 
 | 811 | 	case MMC_APP_CMD: | 
 | 812 | 		mrq->cmd->error = -ETIMEDOUT; | 
 | 813 | 		mmc_request_done(mmc, mrq); | 
 | 814 | 		return; | 
 | 815 | 	case MMC_SEND_EXT_CSD: /* = SD_SEND_IF_COND (8) */ | 
 | 816 | 		if (!mrq->data) { | 
 | 817 | 			/* send_if_cond cmd (not support) */ | 
 | 818 | 			mrq->cmd->error = -ETIMEDOUT; | 
 | 819 | 			mmc_request_done(mmc, mrq); | 
 | 820 | 			return; | 
 | 821 | 		} | 
 | 822 | 		break; | 
 | 823 | 	default: | 
 | 824 | 		break; | 
 | 825 | 	} | 
 | 826 | 	host->data = mrq->data; | 
| Guennadi Liakhovetski | a782d68 | 2010-11-24 10:05:22 +0000 | [diff] [blame] | 827 | 	if (mrq->data) { | 
 | 828 | 		if (mrq->data->flags & MMC_DATA_READ) { | 
 | 829 | 			if (host->chan_rx) | 
 | 830 | 				sh_mmcif_start_dma_rx(host); | 
 | 831 | 		} else { | 
 | 832 | 			if (host->chan_tx) | 
 | 833 | 				sh_mmcif_start_dma_tx(host); | 
 | 834 | 		} | 
 | 835 | 	} | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 836 | 	sh_mmcif_start_cmd(host, mrq, mrq->cmd); | 
 | 837 | 	host->data = NULL; | 
 | 838 |  | 
 | 839 | 	if (mrq->cmd->error != 0) { | 
 | 840 | 		mmc_request_done(mmc, mrq); | 
 | 841 | 		return; | 
 | 842 | 	} | 
 | 843 | 	if (mrq->stop) | 
 | 844 | 		sh_mmcif_stop_cmd(host, mrq, mrq->stop); | 
 | 845 | 	mmc_request_done(mmc, mrq); | 
 | 846 | } | 
 | 847 |  | 
 | 848 | static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | 
 | 849 | { | 
 | 850 | 	struct sh_mmcif_host *host = mmc_priv(mmc); | 
 | 851 | 	struct sh_mmcif_plat_data *p = host->pd->dev.platform_data; | 
 | 852 |  | 
 | 853 | 	if (ios->power_mode == MMC_POWER_OFF) { | 
 | 854 | 		/* clock stop */ | 
 | 855 | 		sh_mmcif_clock_control(host, 0); | 
 | 856 | 		if (p->down_pwr) | 
 | 857 | 			p->down_pwr(host->pd); | 
 | 858 | 		return; | 
 | 859 | 	} else if (ios->power_mode == MMC_POWER_UP) { | 
 | 860 | 		if (p->set_pwr) | 
 | 861 | 			p->set_pwr(host->pd, ios->power_mode); | 
 | 862 | 	} | 
 | 863 |  | 
 | 864 | 	if (ios->clock) | 
 | 865 | 		sh_mmcif_clock_control(host, ios->clock); | 
 | 866 |  | 
 | 867 | 	host->bus_width = ios->bus_width; | 
 | 868 | } | 
 | 869 |  | 
| Arnd Hannemann | 777271d | 2010-08-24 17:27:01 +0200 | [diff] [blame] | 870 | static int sh_mmcif_get_cd(struct mmc_host *mmc) | 
 | 871 | { | 
 | 872 | 	struct sh_mmcif_host *host = mmc_priv(mmc); | 
 | 873 | 	struct sh_mmcif_plat_data *p = host->pd->dev.platform_data; | 
 | 874 |  | 
 | 875 | 	if (!p->get_cd) | 
 | 876 | 		return -ENOSYS; | 
 | 877 | 	else | 
 | 878 | 		return p->get_cd(host->pd); | 
 | 879 | } | 
 | 880 |  | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 881 | static struct mmc_host_ops sh_mmcif_ops = { | 
 | 882 | 	.request	= sh_mmcif_request, | 
 | 883 | 	.set_ios	= sh_mmcif_set_ios, | 
| Arnd Hannemann | 777271d | 2010-08-24 17:27:01 +0200 | [diff] [blame] | 884 | 	.get_cd		= sh_mmcif_get_cd, | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 885 | }; | 
 | 886 |  | 
 | 887 | static void sh_mmcif_detect(struct mmc_host *mmc) | 
 | 888 | { | 
 | 889 | 	mmc_detect_change(mmc, 0); | 
 | 890 | } | 
 | 891 |  | 
 | 892 | static irqreturn_t sh_mmcif_intr(int irq, void *dev_id) | 
 | 893 | { | 
 | 894 | 	struct sh_mmcif_host *host = dev_id; | 
| Guennadi Liakhovetski | aa0787a | 2010-11-24 10:05:12 +0000 | [diff] [blame] | 895 | 	u32 state; | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 896 | 	int err = 0; | 
 | 897 |  | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 898 | 	state = sh_mmcif_readl(host->addr, MMCIF_CE_INT); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 899 |  | 
 | 900 | 	if (state & INT_RBSYE) { | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 901 | 		sh_mmcif_writel(host->addr, MMCIF_CE_INT, | 
 | 902 | 				~(INT_RBSYE | INT_CRSPE)); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 903 | 		sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MRBSYE); | 
 | 904 | 	} else if (state & INT_CRSPE) { | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 905 | 		sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_CRSPE); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 906 | 		sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCRSPE); | 
 | 907 | 	} else if (state & INT_BUFREN) { | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 908 | 		sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_BUFREN); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 909 | 		sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); | 
 | 910 | 	} else if (state & INT_BUFWEN) { | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 911 | 		sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_BUFWEN); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 912 | 		sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); | 
 | 913 | 	} else if (state & INT_CMD12DRE) { | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 914 | 		sh_mmcif_writel(host->addr, MMCIF_CE_INT, | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 915 | 			~(INT_CMD12DRE | INT_CMD12RBE | | 
 | 916 | 			  INT_CMD12CRE | INT_BUFRE)); | 
 | 917 | 		sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCMD12DRE); | 
 | 918 | 	} else if (state & INT_BUFRE) { | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 919 | 		sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_BUFRE); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 920 | 		sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFRE); | 
 | 921 | 	} else if (state & INT_DTRANE) { | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 922 | 		sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_DTRANE); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 923 | 		sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MDTRANE); | 
 | 924 | 	} else if (state & INT_CMD12RBE) { | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 925 | 		sh_mmcif_writel(host->addr, MMCIF_CE_INT, | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 926 | 				~(INT_CMD12RBE | INT_CMD12CRE)); | 
 | 927 | 		sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCMD12RBE); | 
 | 928 | 	} else if (state & INT_ERR_STS) { | 
 | 929 | 		/* err interrupts */ | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 930 | 		sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 931 | 		sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state); | 
 | 932 | 		err = 1; | 
 | 933 | 	} else { | 
| Guennadi Liakhovetski | e47bf32 | 2010-11-24 10:05:18 +0000 | [diff] [blame] | 934 | 		dev_dbg(&host->pd->dev, "Not support int\n"); | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 935 | 		sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 936 | 		sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state); | 
 | 937 | 		err = 1; | 
 | 938 | 	} | 
 | 939 | 	if (err) { | 
| Guennadi Liakhovetski | aa0787a | 2010-11-24 10:05:12 +0000 | [diff] [blame] | 940 | 		host->sd_error = true; | 
| Guennadi Liakhovetski | e47bf32 | 2010-11-24 10:05:18 +0000 | [diff] [blame] | 941 | 		dev_dbg(&host->pd->dev, "int err state = %08x\n", state); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 942 | 	} | 
| Guennadi Liakhovetski | aa0787a | 2010-11-24 10:05:12 +0000 | [diff] [blame] | 943 | 	if (state & ~(INT_CMD12RBE | INT_CMD12CRE)) | 
 | 944 | 		complete(&host->intr_wait); | 
 | 945 | 	else | 
 | 946 | 		dev_dbg(&host->pd->dev, "Unexpected IRQ 0x%x\n", state); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 947 |  | 
 | 948 | 	return IRQ_HANDLED; | 
 | 949 | } | 
 | 950 |  | 
 | 951 | static int __devinit sh_mmcif_probe(struct platform_device *pdev) | 
 | 952 | { | 
 | 953 | 	int ret = 0, irq[2]; | 
 | 954 | 	struct mmc_host *mmc; | 
| Guennadi Liakhovetski | e47bf32 | 2010-11-24 10:05:18 +0000 | [diff] [blame] | 955 | 	struct sh_mmcif_host *host; | 
 | 956 | 	struct sh_mmcif_plat_data *pd; | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 957 | 	struct resource *res; | 
 | 958 | 	void __iomem *reg; | 
 | 959 | 	char clk_name[8]; | 
 | 960 |  | 
 | 961 | 	irq[0] = platform_get_irq(pdev, 0); | 
 | 962 | 	irq[1] = platform_get_irq(pdev, 1); | 
 | 963 | 	if (irq[0] < 0 || irq[1] < 0) { | 
| Guennadi Liakhovetski | e47bf32 | 2010-11-24 10:05:18 +0000 | [diff] [blame] | 964 | 		dev_err(&pdev->dev, "Get irq error\n"); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 965 | 		return -ENXIO; | 
 | 966 | 	} | 
 | 967 | 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
 | 968 | 	if (!res) { | 
 | 969 | 		dev_err(&pdev->dev, "platform_get_resource error.\n"); | 
 | 970 | 		return -ENXIO; | 
 | 971 | 	} | 
 | 972 | 	reg = ioremap(res->start, resource_size(res)); | 
 | 973 | 	if (!reg) { | 
 | 974 | 		dev_err(&pdev->dev, "ioremap error.\n"); | 
 | 975 | 		return -ENOMEM; | 
 | 976 | 	} | 
| Guennadi Liakhovetski | e47bf32 | 2010-11-24 10:05:18 +0000 | [diff] [blame] | 977 | 	pd = pdev->dev.platform_data; | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 978 | 	if (!pd) { | 
 | 979 | 		dev_err(&pdev->dev, "sh_mmcif plat data error.\n"); | 
 | 980 | 		ret = -ENXIO; | 
 | 981 | 		goto clean_up; | 
 | 982 | 	} | 
 | 983 | 	mmc = mmc_alloc_host(sizeof(struct sh_mmcif_host), &pdev->dev); | 
 | 984 | 	if (!mmc) { | 
 | 985 | 		ret = -ENOMEM; | 
 | 986 | 		goto clean_up; | 
 | 987 | 	} | 
 | 988 | 	host		= mmc_priv(mmc); | 
 | 989 | 	host->mmc	= mmc; | 
 | 990 | 	host->addr	= reg; | 
 | 991 | 	host->timeout	= 1000; | 
 | 992 |  | 
 | 993 | 	snprintf(clk_name, sizeof(clk_name), "mmc%d", pdev->id); | 
 | 994 | 	host->hclk = clk_get(&pdev->dev, clk_name); | 
 | 995 | 	if (IS_ERR(host->hclk)) { | 
 | 996 | 		dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); | 
 | 997 | 		ret = PTR_ERR(host->hclk); | 
 | 998 | 		goto clean_up1; | 
 | 999 | 	} | 
 | 1000 | 	clk_enable(host->hclk); | 
 | 1001 | 	host->clk = clk_get_rate(host->hclk); | 
 | 1002 | 	host->pd = pdev; | 
 | 1003 |  | 
| Guennadi Liakhovetski | aa0787a | 2010-11-24 10:05:12 +0000 | [diff] [blame] | 1004 | 	init_completion(&host->intr_wait); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 1005 |  | 
 | 1006 | 	mmc->ops = &sh_mmcif_ops; | 
 | 1007 | 	mmc->f_max = host->clk; | 
 | 1008 | 	/* close to 400KHz */ | 
 | 1009 | 	if (mmc->f_max < 51200000) | 
 | 1010 | 		mmc->f_min = mmc->f_max / 128; | 
 | 1011 | 	else if (mmc->f_max < 102400000) | 
 | 1012 | 		mmc->f_min = mmc->f_max / 256; | 
 | 1013 | 	else | 
 | 1014 | 		mmc->f_min = mmc->f_max / 512; | 
 | 1015 | 	if (pd->ocr) | 
 | 1016 | 		mmc->ocr_avail = pd->ocr; | 
 | 1017 | 	mmc->caps = MMC_CAP_MMC_HIGHSPEED; | 
 | 1018 | 	if (pd->caps) | 
 | 1019 | 		mmc->caps |= pd->caps; | 
| Guennadi Liakhovetski | a782d68 | 2010-11-24 10:05:22 +0000 | [diff] [blame] | 1020 | 	mmc->max_segs = 32; | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 1021 | 	mmc->max_blk_size = 512; | 
| Guennadi Liakhovetski | a782d68 | 2010-11-24 10:05:22 +0000 | [diff] [blame] | 1022 | 	mmc->max_req_size = PAGE_CACHE_SIZE * mmc->max_segs; | 
 | 1023 | 	mmc->max_blk_count = mmc->max_req_size / mmc->max_blk_size; | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 1024 | 	mmc->max_seg_size = mmc->max_req_size; | 
 | 1025 |  | 
 | 1026 | 	sh_mmcif_sync_reset(host); | 
 | 1027 | 	platform_set_drvdata(pdev, host); | 
| Guennadi Liakhovetski | a782d68 | 2010-11-24 10:05:22 +0000 | [diff] [blame] | 1028 |  | 
 | 1029 | 	/* See if we also get DMA */ | 
 | 1030 | 	sh_mmcif_request_dma(host, pd); | 
 | 1031 |  | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 1032 | 	mmc_add_host(mmc); | 
 | 1033 |  | 
 | 1034 | 	ret = request_irq(irq[0], sh_mmcif_intr, 0, "sh_mmc:error", host); | 
 | 1035 | 	if (ret) { | 
| Guennadi Liakhovetski | e47bf32 | 2010-11-24 10:05:18 +0000 | [diff] [blame] | 1036 | 		dev_err(&pdev->dev, "request_irq error (sh_mmc:error)\n"); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 1037 | 		goto clean_up2; | 
 | 1038 | 	} | 
 | 1039 | 	ret = request_irq(irq[1], sh_mmcif_intr, 0, "sh_mmc:int", host); | 
 | 1040 | 	if (ret) { | 
 | 1041 | 		free_irq(irq[0], host); | 
| Guennadi Liakhovetski | e47bf32 | 2010-11-24 10:05:18 +0000 | [diff] [blame] | 1042 | 		dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n"); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 1043 | 		goto clean_up2; | 
 | 1044 | 	} | 
 | 1045 |  | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 1046 | 	sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 1047 | 	sh_mmcif_detect(host->mmc); | 
 | 1048 |  | 
| Guennadi Liakhovetski | e47bf32 | 2010-11-24 10:05:18 +0000 | [diff] [blame] | 1049 | 	dev_info(&pdev->dev, "driver version %s\n", DRIVER_VERSION); | 
 | 1050 | 	dev_dbg(&pdev->dev, "chip ver H'%04x\n", | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 1051 | 		sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0x0000ffff); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 1052 | 	return ret; | 
 | 1053 |  | 
 | 1054 | clean_up2: | 
 | 1055 | 	clk_disable(host->hclk); | 
 | 1056 | clean_up1: | 
 | 1057 | 	mmc_free_host(mmc); | 
 | 1058 | clean_up: | 
 | 1059 | 	if (reg) | 
 | 1060 | 		iounmap(reg); | 
 | 1061 | 	return ret; | 
 | 1062 | } | 
 | 1063 |  | 
 | 1064 | static int __devexit sh_mmcif_remove(struct platform_device *pdev) | 
 | 1065 | { | 
 | 1066 | 	struct sh_mmcif_host *host = platform_get_drvdata(pdev); | 
 | 1067 | 	int irq[2]; | 
 | 1068 |  | 
| Guennadi Liakhovetski | aa0787a | 2010-11-24 10:05:12 +0000 | [diff] [blame] | 1069 | 	mmc_remove_host(host->mmc); | 
| Guennadi Liakhovetski | a782d68 | 2010-11-24 10:05:22 +0000 | [diff] [blame] | 1070 | 	sh_mmcif_release_dma(host); | 
| Guennadi Liakhovetski | aa0787a | 2010-11-24 10:05:12 +0000 | [diff] [blame] | 1071 |  | 
 | 1072 | 	if (host->addr) | 
 | 1073 | 		iounmap(host->addr); | 
 | 1074 |  | 
| Magnus Damm | 487d9fc | 2010-05-18 14:42:51 +0000 | [diff] [blame] | 1075 | 	sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 1076 |  | 
 | 1077 | 	irq[0] = platform_get_irq(pdev, 0); | 
 | 1078 | 	irq[1] = platform_get_irq(pdev, 1); | 
 | 1079 |  | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 1080 | 	free_irq(irq[0], host); | 
 | 1081 | 	free_irq(irq[1], host); | 
 | 1082 |  | 
| Guennadi Liakhovetski | aa0787a | 2010-11-24 10:05:12 +0000 | [diff] [blame] | 1083 | 	platform_set_drvdata(pdev, NULL); | 
 | 1084 |  | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 1085 | 	clk_disable(host->hclk); | 
 | 1086 | 	mmc_free_host(host->mmc); | 
 | 1087 |  | 
 | 1088 | 	return 0; | 
 | 1089 | } | 
 | 1090 |  | 
 | 1091 | static struct platform_driver sh_mmcif_driver = { | 
 | 1092 | 	.probe		= sh_mmcif_probe, | 
 | 1093 | 	.remove		= sh_mmcif_remove, | 
 | 1094 | 	.driver		= { | 
 | 1095 | 		.name	= DRIVER_NAME, | 
 | 1096 | 	}, | 
 | 1097 | }; | 
 | 1098 |  | 
 | 1099 | static int __init sh_mmcif_init(void) | 
 | 1100 | { | 
 | 1101 | 	return platform_driver_register(&sh_mmcif_driver); | 
 | 1102 | } | 
 | 1103 |  | 
 | 1104 | static void __exit sh_mmcif_exit(void) | 
 | 1105 | { | 
 | 1106 | 	platform_driver_unregister(&sh_mmcif_driver); | 
 | 1107 | } | 
 | 1108 |  | 
 | 1109 | module_init(sh_mmcif_init); | 
 | 1110 | module_exit(sh_mmcif_exit); | 
 | 1111 |  | 
 | 1112 |  | 
 | 1113 | MODULE_DESCRIPTION("SuperH on-chip MMC/eMMC interface driver"); | 
 | 1114 | MODULE_LICENSE("GPL"); | 
| Guennadi Liakhovetski | aa0787a | 2010-11-24 10:05:12 +0000 | [diff] [blame] | 1115 | MODULE_ALIAS("platform:" DRIVER_NAME); | 
| Yusuke Goda | fdc50a9 | 2010-05-26 14:41:59 -0700 | [diff] [blame] | 1116 | MODULE_AUTHOR("Yusuke Goda <yusuke.goda.sx@renesas.com>"); |