xref: /openbmc/linux/drivers/mmc/host/alcor.c (revision 9f13caa4)
1c5413ad8SOleksij Rempel // SPDX-License-Identifier: GPL-2.0+
2c5413ad8SOleksij Rempel /*
3c5413ad8SOleksij Rempel  * Copyright (C) 2018 Oleksij Rempel <linux@rempel-privat.de>
4c5413ad8SOleksij Rempel  *
5c5413ad8SOleksij Rempel  * Driver for Alcor Micro AU6601 and AU6621 controllers
6c5413ad8SOleksij Rempel  */
7c5413ad8SOleksij Rempel 
8c5413ad8SOleksij Rempel /* Note: this driver was created without any documentation. Based
9c5413ad8SOleksij Rempel  * on sniffing, testing and in some cases mimic of original driver.
10c5413ad8SOleksij Rempel  * As soon as some one with documentation or more experience in SD/MMC, or
11c5413ad8SOleksij Rempel  * reverse engineering then me, please review this driver and question every
12c5413ad8SOleksij Rempel  * thing what I did. 2018 Oleksij Rempel <linux@rempel-privat.de>
13c5413ad8SOleksij Rempel  */
14c5413ad8SOleksij Rempel 
15c5413ad8SOleksij Rempel #include <linux/delay.h>
16c5413ad8SOleksij Rempel #include <linux/pci.h>
17c5413ad8SOleksij Rempel #include <linux/module.h>
18c5413ad8SOleksij Rempel #include <linux/io.h>
19c5413ad8SOleksij Rempel #include <linux/pm.h>
20c5413ad8SOleksij Rempel #include <linux/irq.h>
21c5413ad8SOleksij Rempel #include <linux/interrupt.h>
22c5413ad8SOleksij Rempel #include <linux/platform_device.h>
23c5413ad8SOleksij Rempel 
24c5413ad8SOleksij Rempel #include <linux/mmc/host.h>
25c5413ad8SOleksij Rempel #include <linux/mmc/mmc.h>
26c5413ad8SOleksij Rempel 
27c5413ad8SOleksij Rempel #include <linux/alcor_pci.h>
28c5413ad8SOleksij Rempel 
29c5413ad8SOleksij Rempel enum alcor_cookie {
30c5413ad8SOleksij Rempel 	COOKIE_UNMAPPED,
31c5413ad8SOleksij Rempel 	COOKIE_PRE_MAPPED,
32c5413ad8SOleksij Rempel 	COOKIE_MAPPED,
33c5413ad8SOleksij Rempel };
34c5413ad8SOleksij Rempel 
35c5413ad8SOleksij Rempel struct alcor_pll_conf {
36c5413ad8SOleksij Rempel 	unsigned int clk_src_freq;
37c5413ad8SOleksij Rempel 	unsigned int clk_src_reg;
38c5413ad8SOleksij Rempel 	unsigned int min_div;
39c5413ad8SOleksij Rempel 	unsigned int max_div;
40c5413ad8SOleksij Rempel };
41c5413ad8SOleksij Rempel 
42c5413ad8SOleksij Rempel struct alcor_sdmmc_host {
43c5413ad8SOleksij Rempel 	struct  device *dev;
44c5413ad8SOleksij Rempel 	struct alcor_pci_priv *alcor_pci;
45c5413ad8SOleksij Rempel 
46c5413ad8SOleksij Rempel 	struct mmc_request *mrq;
47c5413ad8SOleksij Rempel 	struct mmc_command *cmd;
48c5413ad8SOleksij Rempel 	struct mmc_data *data;
49c5413ad8SOleksij Rempel 	unsigned int dma_on:1;
50c5413ad8SOleksij Rempel 
51c5413ad8SOleksij Rempel 	struct mutex cmd_mutex;
52c5413ad8SOleksij Rempel 
53c5413ad8SOleksij Rempel 	struct delayed_work timeout_work;
54c5413ad8SOleksij Rempel 
55c5413ad8SOleksij Rempel 	struct sg_mapping_iter sg_miter;	/* SG state for PIO */
56c5413ad8SOleksij Rempel 	struct scatterlist *sg;
57c5413ad8SOleksij Rempel 	unsigned int blocks;		/* remaining PIO blocks */
58c5413ad8SOleksij Rempel 	int sg_count;
59c5413ad8SOleksij Rempel 
60c5413ad8SOleksij Rempel 	u32			irq_status_sd;
61c5413ad8SOleksij Rempel 	unsigned char		cur_power_mode;
62c5413ad8SOleksij Rempel };
63c5413ad8SOleksij Rempel 
64c5413ad8SOleksij Rempel static const struct alcor_pll_conf alcor_pll_cfg[] = {
65c5413ad8SOleksij Rempel 	/* MHZ,		CLK src,		max div, min div */
66c5413ad8SOleksij Rempel 	{ 31250000,	AU6601_CLK_31_25_MHZ,	1,	511},
67c5413ad8SOleksij Rempel 	{ 48000000,	AU6601_CLK_48_MHZ,	1,	511},
68c5413ad8SOleksij Rempel 	{125000000,	AU6601_CLK_125_MHZ,	1,	511},
69c5413ad8SOleksij Rempel 	{384000000,	AU6601_CLK_384_MHZ,	1,	511},
70c5413ad8SOleksij Rempel };
71c5413ad8SOleksij Rempel 
alcor_rmw8(struct alcor_sdmmc_host * host,unsigned int addr,u8 clear,u8 set)72c5413ad8SOleksij Rempel static inline void alcor_rmw8(struct alcor_sdmmc_host *host, unsigned int addr,
73c5413ad8SOleksij Rempel 			       u8 clear, u8 set)
74c5413ad8SOleksij Rempel {
75c5413ad8SOleksij Rempel 	struct alcor_pci_priv *priv = host->alcor_pci;
76c5413ad8SOleksij Rempel 	u32 var;
77c5413ad8SOleksij Rempel 
78c5413ad8SOleksij Rempel 	var = alcor_read8(priv, addr);
79c5413ad8SOleksij Rempel 	var &= ~clear;
80c5413ad8SOleksij Rempel 	var |= set;
81c5413ad8SOleksij Rempel 	alcor_write8(priv, var, addr);
82c5413ad8SOleksij Rempel }
83c5413ad8SOleksij Rempel 
84c5413ad8SOleksij Rempel /* As soon as irqs are masked, some status updates may be missed.
85c5413ad8SOleksij Rempel  * Use this with care.
86c5413ad8SOleksij Rempel  */
alcor_mask_sd_irqs(struct alcor_sdmmc_host * host)87c5413ad8SOleksij Rempel static inline void alcor_mask_sd_irqs(struct alcor_sdmmc_host *host)
88c5413ad8SOleksij Rempel {
89c5413ad8SOleksij Rempel 	struct alcor_pci_priv *priv = host->alcor_pci;
90c5413ad8SOleksij Rempel 
91c5413ad8SOleksij Rempel 	alcor_write32(priv, 0, AU6601_REG_INT_ENABLE);
92c5413ad8SOleksij Rempel }
93c5413ad8SOleksij Rempel 
alcor_unmask_sd_irqs(struct alcor_sdmmc_host * host)94c5413ad8SOleksij Rempel static inline void alcor_unmask_sd_irqs(struct alcor_sdmmc_host *host)
95c5413ad8SOleksij Rempel {
96c5413ad8SOleksij Rempel 	struct alcor_pci_priv *priv = host->alcor_pci;
97c5413ad8SOleksij Rempel 
98c5413ad8SOleksij Rempel 	alcor_write32(priv, AU6601_INT_CMD_MASK | AU6601_INT_DATA_MASK |
99c5413ad8SOleksij Rempel 		  AU6601_INT_CARD_INSERT | AU6601_INT_CARD_REMOVE |
100c5413ad8SOleksij Rempel 		  AU6601_INT_OVER_CURRENT_ERR,
101c5413ad8SOleksij Rempel 		  AU6601_REG_INT_ENABLE);
102c5413ad8SOleksij Rempel }
103c5413ad8SOleksij Rempel 
alcor_reset(struct alcor_sdmmc_host * host,u8 val)104c5413ad8SOleksij Rempel static void alcor_reset(struct alcor_sdmmc_host *host, u8 val)
105c5413ad8SOleksij Rempel {
106c5413ad8SOleksij Rempel 	struct alcor_pci_priv *priv = host->alcor_pci;
107c5413ad8SOleksij Rempel 	int i;
108c5413ad8SOleksij Rempel 
109c5413ad8SOleksij Rempel 	alcor_write8(priv, val | AU6601_BUF_CTRL_RESET,
110c5413ad8SOleksij Rempel 		      AU6601_REG_SW_RESET);
111c5413ad8SOleksij Rempel 	for (i = 0; i < 100; i++) {
112c5413ad8SOleksij Rempel 		if (!(alcor_read8(priv, AU6601_REG_SW_RESET) & val))
113c5413ad8SOleksij Rempel 			return;
114c5413ad8SOleksij Rempel 		udelay(50);
115c5413ad8SOleksij Rempel 	}
116c5413ad8SOleksij Rempel 	dev_err(host->dev, "%s: timeout\n", __func__);
117c5413ad8SOleksij Rempel }
118c5413ad8SOleksij Rempel 
119c671b6deSDaniel Drake /*
120c671b6deSDaniel Drake  * Perform DMA I/O of a single page.
121c671b6deSDaniel Drake  */
alcor_data_set_dma(struct alcor_sdmmc_host * host)122c5413ad8SOleksij Rempel static void alcor_data_set_dma(struct alcor_sdmmc_host *host)
123c5413ad8SOleksij Rempel {
124c5413ad8SOleksij Rempel 	struct alcor_pci_priv *priv = host->alcor_pci;
125566b6020SYueHaibing 	u32 addr;
126c5413ad8SOleksij Rempel 
127c5413ad8SOleksij Rempel 	if (!host->sg_count)
128c5413ad8SOleksij Rempel 		return;
129c5413ad8SOleksij Rempel 
130c5413ad8SOleksij Rempel 	if (!host->sg) {
131c5413ad8SOleksij Rempel 		dev_err(host->dev, "have blocks, but no SG\n");
132c5413ad8SOleksij Rempel 		return;
133c5413ad8SOleksij Rempel 	}
134c5413ad8SOleksij Rempel 
135c5413ad8SOleksij Rempel 	if (!sg_dma_len(host->sg)) {
136c5413ad8SOleksij Rempel 		dev_err(host->dev, "DMA SG len == 0\n");
137c5413ad8SOleksij Rempel 		return;
138c5413ad8SOleksij Rempel 	}
139c5413ad8SOleksij Rempel 
140c5413ad8SOleksij Rempel 
141c5413ad8SOleksij Rempel 	addr = (u32)sg_dma_address(host->sg);
142c5413ad8SOleksij Rempel 
143c5413ad8SOleksij Rempel 	alcor_write32(priv, addr, AU6601_REG_SDMA_ADDR);
144c5413ad8SOleksij Rempel 	host->sg = sg_next(host->sg);
145c5413ad8SOleksij Rempel 	host->sg_count--;
146c5413ad8SOleksij Rempel }
147c5413ad8SOleksij Rempel 
alcor_trigger_data_transfer(struct alcor_sdmmc_host * host)148157c99c5SDaniel Drake static void alcor_trigger_data_transfer(struct alcor_sdmmc_host *host)
149c5413ad8SOleksij Rempel {
150c5413ad8SOleksij Rempel 	struct alcor_pci_priv *priv = host->alcor_pci;
151c5413ad8SOleksij Rempel 	struct mmc_data *data = host->data;
152c5413ad8SOleksij Rempel 	u8 ctrl = 0;
153c5413ad8SOleksij Rempel 
154c5413ad8SOleksij Rempel 	if (data->flags & MMC_DATA_WRITE)
155c5413ad8SOleksij Rempel 		ctrl |= AU6601_DATA_WRITE;
156c5413ad8SOleksij Rempel 
157c5413ad8SOleksij Rempel 	if (data->host_cookie == COOKIE_MAPPED) {
158c671b6deSDaniel Drake 		/*
159c671b6deSDaniel Drake 		 * For DMA transfers, this function is called just once,
160c671b6deSDaniel Drake 		 * at the start of the operation. The hardware can only
161c671b6deSDaniel Drake 		 * perform DMA I/O on a single page at a time, so here
162c671b6deSDaniel Drake 		 * we kick off the transfer with the first page, and expect
163c671b6deSDaniel Drake 		 * subsequent pages to be transferred upon IRQ events
164c671b6deSDaniel Drake 		 * indicating that the single-page DMA was completed.
165c671b6deSDaniel Drake 		 */
166c5413ad8SOleksij Rempel 		alcor_data_set_dma(host);
167c5413ad8SOleksij Rempel 		ctrl |= AU6601_DATA_DMA_MODE;
168c5413ad8SOleksij Rempel 		host->dma_on = 1;
169c5413ad8SOleksij Rempel 		alcor_write32(priv, data->sg_count * 0x1000,
170c5413ad8SOleksij Rempel 			       AU6601_REG_BLOCK_SIZE);
171c5413ad8SOleksij Rempel 	} else {
172c671b6deSDaniel Drake 		/*
173c671b6deSDaniel Drake 		 * For PIO transfers, we break down each operation
174c671b6deSDaniel Drake 		 * into several sector-sized transfers. When one sector has
175c671b6deSDaniel Drake 		 * complete, the IRQ handler will call this function again
176c671b6deSDaniel Drake 		 * to kick off the transfer of the next sector.
177c671b6deSDaniel Drake 		 */
178c5413ad8SOleksij Rempel 		alcor_write32(priv, data->blksz, AU6601_REG_BLOCK_SIZE);
179c5413ad8SOleksij Rempel 	}
180c5413ad8SOleksij Rempel 
181c5413ad8SOleksij Rempel 	alcor_write8(priv, ctrl | AU6601_DATA_START_XFER,
182c5413ad8SOleksij Rempel 		      AU6601_DATA_XFER_CTRL);
183c5413ad8SOleksij Rempel }
184c5413ad8SOleksij Rempel 
alcor_trf_block_pio(struct alcor_sdmmc_host * host,bool read)185c5413ad8SOleksij Rempel static void alcor_trf_block_pio(struct alcor_sdmmc_host *host, bool read)
186c5413ad8SOleksij Rempel {
187c5413ad8SOleksij Rempel 	struct alcor_pci_priv *priv = host->alcor_pci;
188c5413ad8SOleksij Rempel 	size_t blksize, len;
189c5413ad8SOleksij Rempel 	u8 *buf;
190c5413ad8SOleksij Rempel 
191c5413ad8SOleksij Rempel 	if (!host->blocks)
192c5413ad8SOleksij Rempel 		return;
193c5413ad8SOleksij Rempel 
194c5413ad8SOleksij Rempel 	if (host->dma_on) {
195c5413ad8SOleksij Rempel 		dev_err(host->dev, "configured DMA but got PIO request.\n");
196c5413ad8SOleksij Rempel 		return;
197c5413ad8SOleksij Rempel 	}
198c5413ad8SOleksij Rempel 
199c5413ad8SOleksij Rempel 	if (!!(host->data->flags & MMC_DATA_READ) != read) {
200c5413ad8SOleksij Rempel 		dev_err(host->dev, "got unexpected direction %i != %i\n",
201c5413ad8SOleksij Rempel 			!!(host->data->flags & MMC_DATA_READ), read);
202c5413ad8SOleksij Rempel 	}
203c5413ad8SOleksij Rempel 
204c5413ad8SOleksij Rempel 	if (!sg_miter_next(&host->sg_miter))
205c5413ad8SOleksij Rempel 		return;
206c5413ad8SOleksij Rempel 
207c5413ad8SOleksij Rempel 	blksize = host->data->blksz;
208c5413ad8SOleksij Rempel 	len = min(host->sg_miter.length, blksize);
209c5413ad8SOleksij Rempel 
210c5413ad8SOleksij Rempel 	dev_dbg(host->dev, "PIO, %s block size: 0x%zx\n",
211c5413ad8SOleksij Rempel 		read ? "read" : "write", blksize);
212c5413ad8SOleksij Rempel 
213c5413ad8SOleksij Rempel 	host->sg_miter.consumed = len;
214c5413ad8SOleksij Rempel 	host->blocks--;
215c5413ad8SOleksij Rempel 
216c5413ad8SOleksij Rempel 	buf = host->sg_miter.addr;
217c5413ad8SOleksij Rempel 
218c5413ad8SOleksij Rempel 	if (read)
219c5413ad8SOleksij Rempel 		ioread32_rep(priv->iobase + AU6601_REG_BUFFER, buf, len >> 2);
220c5413ad8SOleksij Rempel 	else
221c5413ad8SOleksij Rempel 		iowrite32_rep(priv->iobase + AU6601_REG_BUFFER, buf, len >> 2);
222c5413ad8SOleksij Rempel 
223c5413ad8SOleksij Rempel 	sg_miter_stop(&host->sg_miter);
224c5413ad8SOleksij Rempel }
225c5413ad8SOleksij Rempel 
alcor_prepare_sg_miter(struct alcor_sdmmc_host * host)226c5413ad8SOleksij Rempel static void alcor_prepare_sg_miter(struct alcor_sdmmc_host *host)
227c5413ad8SOleksij Rempel {
228c5413ad8SOleksij Rempel 	unsigned int flags = SG_MITER_ATOMIC;
229c5413ad8SOleksij Rempel 	struct mmc_data *data = host->data;
230c5413ad8SOleksij Rempel 
231c5413ad8SOleksij Rempel 	if (data->flags & MMC_DATA_READ)
232c5413ad8SOleksij Rempel 		flags |= SG_MITER_TO_SG;
233c5413ad8SOleksij Rempel 	else
234c5413ad8SOleksij Rempel 		flags |= SG_MITER_FROM_SG;
235c5413ad8SOleksij Rempel 	sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags);
236c5413ad8SOleksij Rempel }
237c5413ad8SOleksij Rempel 
alcor_prepare_data(struct alcor_sdmmc_host * host,struct mmc_command * cmd)238c5413ad8SOleksij Rempel static void alcor_prepare_data(struct alcor_sdmmc_host *host,
239c5413ad8SOleksij Rempel 			       struct mmc_command *cmd)
240c5413ad8SOleksij Rempel {
241157c99c5SDaniel Drake 	struct alcor_pci_priv *priv = host->alcor_pci;
242c5413ad8SOleksij Rempel 	struct mmc_data *data = cmd->data;
243c5413ad8SOleksij Rempel 
244c5413ad8SOleksij Rempel 	if (!data)
245c5413ad8SOleksij Rempel 		return;
246c5413ad8SOleksij Rempel 
247c5413ad8SOleksij Rempel 
248c5413ad8SOleksij Rempel 	host->data = data;
249c5413ad8SOleksij Rempel 	host->data->bytes_xfered = 0;
250c5413ad8SOleksij Rempel 	host->blocks = data->blocks;
251c5413ad8SOleksij Rempel 	host->sg = data->sg;
252c5413ad8SOleksij Rempel 	host->sg_count = data->sg_count;
253c5413ad8SOleksij Rempel 	dev_dbg(host->dev, "prepare DATA: sg %i, blocks: %i\n",
254c5413ad8SOleksij Rempel 			host->sg_count, host->blocks);
255c5413ad8SOleksij Rempel 
256c5413ad8SOleksij Rempel 	if (data->host_cookie != COOKIE_MAPPED)
257c5413ad8SOleksij Rempel 		alcor_prepare_sg_miter(host);
258c5413ad8SOleksij Rempel 
259157c99c5SDaniel Drake 	alcor_write8(priv, 0, AU6601_DATA_XFER_CTRL);
260c5413ad8SOleksij Rempel }
261c5413ad8SOleksij Rempel 
alcor_send_cmd(struct alcor_sdmmc_host * host,struct mmc_command * cmd,bool set_timeout)262c5413ad8SOleksij Rempel static void alcor_send_cmd(struct alcor_sdmmc_host *host,
263c5413ad8SOleksij Rempel 			   struct mmc_command *cmd, bool set_timeout)
264c5413ad8SOleksij Rempel {
265c5413ad8SOleksij Rempel 	struct alcor_pci_priv *priv = host->alcor_pci;
266c5413ad8SOleksij Rempel 	unsigned long timeout = 0;
267c5413ad8SOleksij Rempel 	u8 ctrl = 0;
268c5413ad8SOleksij Rempel 
269c5413ad8SOleksij Rempel 	host->cmd = cmd;
270c5413ad8SOleksij Rempel 	alcor_prepare_data(host, cmd);
271c5413ad8SOleksij Rempel 
272c5413ad8SOleksij Rempel 	dev_dbg(host->dev, "send CMD. opcode: 0x%02x, arg; 0x%08x\n",
273c5413ad8SOleksij Rempel 		cmd->opcode, cmd->arg);
274c5413ad8SOleksij Rempel 	alcor_write8(priv, cmd->opcode | 0x40, AU6601_REG_CMD_OPCODE);
275c5413ad8SOleksij Rempel 	alcor_write32be(priv, cmd->arg, AU6601_REG_CMD_ARG);
276c5413ad8SOleksij Rempel 
277c5413ad8SOleksij Rempel 	switch (mmc_resp_type(cmd)) {
278c5413ad8SOleksij Rempel 	case MMC_RSP_NONE:
279c5413ad8SOleksij Rempel 		ctrl = AU6601_CMD_NO_RESP;
280c5413ad8SOleksij Rempel 		break;
281c5413ad8SOleksij Rempel 	case MMC_RSP_R1:
282c5413ad8SOleksij Rempel 		ctrl = AU6601_CMD_6_BYTE_CRC;
283c5413ad8SOleksij Rempel 		break;
284c5413ad8SOleksij Rempel 	case MMC_RSP_R1B:
285c5413ad8SOleksij Rempel 		ctrl = AU6601_CMD_6_BYTE_CRC | AU6601_CMD_STOP_WAIT_RDY;
286c5413ad8SOleksij Rempel 		break;
287c5413ad8SOleksij Rempel 	case MMC_RSP_R2:
288c5413ad8SOleksij Rempel 		ctrl = AU6601_CMD_17_BYTE_CRC;
289c5413ad8SOleksij Rempel 		break;
290c5413ad8SOleksij Rempel 	case MMC_RSP_R3:
291c5413ad8SOleksij Rempel 		ctrl = AU6601_CMD_6_BYTE_WO_CRC;
292c5413ad8SOleksij Rempel 		break;
293c5413ad8SOleksij Rempel 	default:
294c5413ad8SOleksij Rempel 		dev_err(host->dev, "%s: cmd->flag (0x%02x) is not valid\n",
295c081e7fdSKamlesh Gurudasani 			mmc_hostname(mmc_from_priv(host)), mmc_resp_type(cmd));
296c5413ad8SOleksij Rempel 		break;
297c5413ad8SOleksij Rempel 	}
298c5413ad8SOleksij Rempel 
299c5413ad8SOleksij Rempel 	if (set_timeout) {
300c5413ad8SOleksij Rempel 		if (!cmd->data && cmd->busy_timeout)
301c5413ad8SOleksij Rempel 			timeout = cmd->busy_timeout;
302c5413ad8SOleksij Rempel 		else
303c5413ad8SOleksij Rempel 			timeout = 10000;
304c5413ad8SOleksij Rempel 
305c5413ad8SOleksij Rempel 		schedule_delayed_work(&host->timeout_work,
306c5413ad8SOleksij Rempel 				      msecs_to_jiffies(timeout));
307c5413ad8SOleksij Rempel 	}
308c5413ad8SOleksij Rempel 
309c5413ad8SOleksij Rempel 	dev_dbg(host->dev, "xfer ctrl: 0x%02x; timeout: %lu\n", ctrl, timeout);
310c5413ad8SOleksij Rempel 	alcor_write8(priv, ctrl | AU6601_CMD_START_XFER,
311c5413ad8SOleksij Rempel 				 AU6601_CMD_XFER_CTRL);
312c5413ad8SOleksij Rempel }
313c5413ad8SOleksij Rempel 
alcor_request_complete(struct alcor_sdmmc_host * host,bool cancel_timeout)314c5413ad8SOleksij Rempel static void alcor_request_complete(struct alcor_sdmmc_host *host,
315c5413ad8SOleksij Rempel 				   bool cancel_timeout)
316c5413ad8SOleksij Rempel {
317c5413ad8SOleksij Rempel 	struct mmc_request *mrq;
318c5413ad8SOleksij Rempel 
319c5413ad8SOleksij Rempel 	/*
320c5413ad8SOleksij Rempel 	 * If this work gets rescheduled while running, it will
321c5413ad8SOleksij Rempel 	 * be run again afterwards but without any active request.
322c5413ad8SOleksij Rempel 	 */
323c5413ad8SOleksij Rempel 	if (!host->mrq)
324c5413ad8SOleksij Rempel 		return;
325c5413ad8SOleksij Rempel 
326c5413ad8SOleksij Rempel 	if (cancel_timeout)
327c5413ad8SOleksij Rempel 		cancel_delayed_work(&host->timeout_work);
328c5413ad8SOleksij Rempel 
329c5413ad8SOleksij Rempel 	mrq = host->mrq;
330c5413ad8SOleksij Rempel 
331c5413ad8SOleksij Rempel 	host->mrq = NULL;
332c5413ad8SOleksij Rempel 	host->cmd = NULL;
333c5413ad8SOleksij Rempel 	host->data = NULL;
334c5413ad8SOleksij Rempel 	host->dma_on = 0;
335c5413ad8SOleksij Rempel 
336c081e7fdSKamlesh Gurudasani 	mmc_request_done(mmc_from_priv(host), mrq);
337c5413ad8SOleksij Rempel }
338c5413ad8SOleksij Rempel 
alcor_finish_data(struct alcor_sdmmc_host * host)339c5413ad8SOleksij Rempel static void alcor_finish_data(struct alcor_sdmmc_host *host)
340c5413ad8SOleksij Rempel {
341c5413ad8SOleksij Rempel 	struct mmc_data *data;
342c5413ad8SOleksij Rempel 
343c5413ad8SOleksij Rempel 	data = host->data;
344c5413ad8SOleksij Rempel 	host->data = NULL;
345c5413ad8SOleksij Rempel 	host->dma_on = 0;
346c5413ad8SOleksij Rempel 
347c5413ad8SOleksij Rempel 	/*
348c5413ad8SOleksij Rempel 	 * The specification states that the block count register must
349c5413ad8SOleksij Rempel 	 * be updated, but it does not specify at what point in the
350c5413ad8SOleksij Rempel 	 * data flow. That makes the register entirely useless to read
351c5413ad8SOleksij Rempel 	 * back so we have to assume that nothing made it to the card
352c5413ad8SOleksij Rempel 	 * in the event of an error.
353c5413ad8SOleksij Rempel 	 */
354c5413ad8SOleksij Rempel 	if (data->error)
355c5413ad8SOleksij Rempel 		data->bytes_xfered = 0;
356c5413ad8SOleksij Rempel 	else
357c5413ad8SOleksij Rempel 		data->bytes_xfered = data->blksz * data->blocks;
358c5413ad8SOleksij Rempel 
359c5413ad8SOleksij Rempel 	/*
360c5413ad8SOleksij Rempel 	 * Need to send CMD12 if -
361c5413ad8SOleksij Rempel 	 * a) open-ended multiblock transfer (no CMD23)
362c5413ad8SOleksij Rempel 	 * b) error in multiblock transfer
363c5413ad8SOleksij Rempel 	 */
364c5413ad8SOleksij Rempel 	if (data->stop &&
365c5413ad8SOleksij Rempel 	    (data->error ||
366c5413ad8SOleksij Rempel 	     !host->mrq->sbc)) {
367c5413ad8SOleksij Rempel 
368c5413ad8SOleksij Rempel 		/*
369c5413ad8SOleksij Rempel 		 * The controller needs a reset of internal state machines
370c5413ad8SOleksij Rempel 		 * upon error conditions.
371c5413ad8SOleksij Rempel 		 */
372c5413ad8SOleksij Rempel 		if (data->error)
373c5413ad8SOleksij Rempel 			alcor_reset(host, AU6601_RESET_CMD | AU6601_RESET_DATA);
374c5413ad8SOleksij Rempel 
375c5413ad8SOleksij Rempel 		alcor_unmask_sd_irqs(host);
376c5413ad8SOleksij Rempel 		alcor_send_cmd(host, data->stop, false);
377c5413ad8SOleksij Rempel 		return;
378c5413ad8SOleksij Rempel 	}
379c5413ad8SOleksij Rempel 
380c5413ad8SOleksij Rempel 	alcor_request_complete(host, 1);
381c5413ad8SOleksij Rempel }
382c5413ad8SOleksij Rempel 
alcor_err_irq(struct alcor_sdmmc_host * host,u32 intmask)383c5413ad8SOleksij Rempel static void alcor_err_irq(struct alcor_sdmmc_host *host, u32 intmask)
384c5413ad8SOleksij Rempel {
385c5413ad8SOleksij Rempel 	dev_dbg(host->dev, "ERR IRQ %x\n", intmask);
386c5413ad8SOleksij Rempel 
387c5413ad8SOleksij Rempel 	if (host->cmd) {
388c5413ad8SOleksij Rempel 		if (intmask & AU6601_INT_CMD_TIMEOUT_ERR)
389c5413ad8SOleksij Rempel 			host->cmd->error = -ETIMEDOUT;
390c5413ad8SOleksij Rempel 		else
391c5413ad8SOleksij Rempel 			host->cmd->error = -EILSEQ;
392c5413ad8SOleksij Rempel 	}
393c5413ad8SOleksij Rempel 
394c5413ad8SOleksij Rempel 	if (host->data) {
395c5413ad8SOleksij Rempel 		if (intmask & AU6601_INT_DATA_TIMEOUT_ERR)
396c5413ad8SOleksij Rempel 			host->data->error = -ETIMEDOUT;
397c5413ad8SOleksij Rempel 		else
398c5413ad8SOleksij Rempel 			host->data->error = -EILSEQ;
399c5413ad8SOleksij Rempel 
400c5413ad8SOleksij Rempel 		host->data->bytes_xfered = 0;
401c5413ad8SOleksij Rempel 	}
402c5413ad8SOleksij Rempel 
403c5413ad8SOleksij Rempel 	alcor_reset(host, AU6601_RESET_CMD | AU6601_RESET_DATA);
404c5413ad8SOleksij Rempel 	alcor_request_complete(host, 1);
405c5413ad8SOleksij Rempel }
406c5413ad8SOleksij Rempel 
alcor_cmd_irq_done(struct alcor_sdmmc_host * host,u32 intmask)407c5413ad8SOleksij Rempel static int alcor_cmd_irq_done(struct alcor_sdmmc_host *host, u32 intmask)
408c5413ad8SOleksij Rempel {
409c5413ad8SOleksij Rempel 	struct alcor_pci_priv *priv = host->alcor_pci;
410c5413ad8SOleksij Rempel 
411c5413ad8SOleksij Rempel 	intmask &= AU6601_INT_CMD_END;
412c5413ad8SOleksij Rempel 
413c5413ad8SOleksij Rempel 	if (!intmask)
414c5413ad8SOleksij Rempel 		return true;
415c5413ad8SOleksij Rempel 
416c5413ad8SOleksij Rempel 	/* got CMD_END but no CMD is in progress, wake thread an process the
417c5413ad8SOleksij Rempel 	 * error
418c5413ad8SOleksij Rempel 	 */
419c5413ad8SOleksij Rempel 	if (!host->cmd)
420c5413ad8SOleksij Rempel 		return false;
421c5413ad8SOleksij Rempel 
422c5413ad8SOleksij Rempel 	if (host->cmd->flags & MMC_RSP_PRESENT) {
423c5413ad8SOleksij Rempel 		struct mmc_command *cmd = host->cmd;
424c5413ad8SOleksij Rempel 
425c5413ad8SOleksij Rempel 		cmd->resp[0] = alcor_read32be(priv, AU6601_REG_CMD_RSP0);
426c5413ad8SOleksij Rempel 		dev_dbg(host->dev, "RSP0: 0x%04x\n", cmd->resp[0]);
427c5413ad8SOleksij Rempel 		if (host->cmd->flags & MMC_RSP_136) {
428c5413ad8SOleksij Rempel 			cmd->resp[1] =
429c5413ad8SOleksij Rempel 				alcor_read32be(priv, AU6601_REG_CMD_RSP1);
430c5413ad8SOleksij Rempel 			cmd->resp[2] =
431c5413ad8SOleksij Rempel 				alcor_read32be(priv, AU6601_REG_CMD_RSP2);
432c5413ad8SOleksij Rempel 			cmd->resp[3] =
433c5413ad8SOleksij Rempel 				alcor_read32be(priv, AU6601_REG_CMD_RSP3);
434c5413ad8SOleksij Rempel 			dev_dbg(host->dev, "RSP1,2,3: 0x%04x 0x%04x 0x%04x\n",
435c5413ad8SOleksij Rempel 				cmd->resp[1], cmd->resp[2], cmd->resp[3]);
436c5413ad8SOleksij Rempel 		}
437c5413ad8SOleksij Rempel 
438c5413ad8SOleksij Rempel 	}
439c5413ad8SOleksij Rempel 
440c5413ad8SOleksij Rempel 	host->cmd->error = 0;
441c5413ad8SOleksij Rempel 
442c5413ad8SOleksij Rempel 	/* Processed actual command. */
443c5413ad8SOleksij Rempel 	if (!host->data)
444c5413ad8SOleksij Rempel 		return false;
445c5413ad8SOleksij Rempel 
446157c99c5SDaniel Drake 	alcor_trigger_data_transfer(host);
447c5413ad8SOleksij Rempel 	host->cmd = NULL;
448c5413ad8SOleksij Rempel 	return true;
449c5413ad8SOleksij Rempel }
450c5413ad8SOleksij Rempel 
alcor_cmd_irq_thread(struct alcor_sdmmc_host * host,u32 intmask)451c5413ad8SOleksij Rempel static void alcor_cmd_irq_thread(struct alcor_sdmmc_host *host, u32 intmask)
452c5413ad8SOleksij Rempel {
453c5413ad8SOleksij Rempel 	intmask &= AU6601_INT_CMD_END;
454c5413ad8SOleksij Rempel 
455c5413ad8SOleksij Rempel 	if (!intmask)
456c5413ad8SOleksij Rempel 		return;
457c5413ad8SOleksij Rempel 
458c5413ad8SOleksij Rempel 	if (!host->cmd && intmask & AU6601_INT_CMD_END) {
459c5413ad8SOleksij Rempel 		dev_dbg(host->dev, "Got command interrupt 0x%08x even though no command operation was in progress.\n",
460c5413ad8SOleksij Rempel 			intmask);
461c5413ad8SOleksij Rempel 	}
462c5413ad8SOleksij Rempel 
463c5413ad8SOleksij Rempel 	/* Processed actual command. */
464c5413ad8SOleksij Rempel 	if (!host->data)
465c5413ad8SOleksij Rempel 		alcor_request_complete(host, 1);
466c5413ad8SOleksij Rempel 	else
467157c99c5SDaniel Drake 		alcor_trigger_data_transfer(host);
468c5413ad8SOleksij Rempel 	host->cmd = NULL;
469c5413ad8SOleksij Rempel }
470c5413ad8SOleksij Rempel 
alcor_data_irq_done(struct alcor_sdmmc_host * host,u32 intmask)471c5413ad8SOleksij Rempel static int alcor_data_irq_done(struct alcor_sdmmc_host *host, u32 intmask)
472c5413ad8SOleksij Rempel {
473c5413ad8SOleksij Rempel 	u32 tmp;
474c5413ad8SOleksij Rempel 
475c5413ad8SOleksij Rempel 	intmask &= AU6601_INT_DATA_MASK;
476c5413ad8SOleksij Rempel 
477c5413ad8SOleksij Rempel 	/* nothing here to do */
478c5413ad8SOleksij Rempel 	if (!intmask)
479c5413ad8SOleksij Rempel 		return 1;
480c5413ad8SOleksij Rempel 
481c5413ad8SOleksij Rempel 	/* we was too fast and got DATA_END after it was processed?
482c5413ad8SOleksij Rempel 	 * lets ignore it for now.
483c5413ad8SOleksij Rempel 	 */
484c5413ad8SOleksij Rempel 	if (!host->data && intmask == AU6601_INT_DATA_END)
485c5413ad8SOleksij Rempel 		return 1;
486c5413ad8SOleksij Rempel 
487c5413ad8SOleksij Rempel 	/* looks like an error, so lets handle it. */
488c5413ad8SOleksij Rempel 	if (!host->data)
489c5413ad8SOleksij Rempel 		return 0;
490c5413ad8SOleksij Rempel 
491c5413ad8SOleksij Rempel 	tmp = intmask & (AU6601_INT_READ_BUF_RDY | AU6601_INT_WRITE_BUF_RDY
492c5413ad8SOleksij Rempel 			 | AU6601_INT_DMA_END);
493c5413ad8SOleksij Rempel 	switch (tmp) {
494c5413ad8SOleksij Rempel 	case 0:
495c5413ad8SOleksij Rempel 		break;
496c5413ad8SOleksij Rempel 	case AU6601_INT_READ_BUF_RDY:
497c5413ad8SOleksij Rempel 		alcor_trf_block_pio(host, true);
498c5413ad8SOleksij Rempel 		return 1;
499c5413ad8SOleksij Rempel 	case AU6601_INT_WRITE_BUF_RDY:
500c5413ad8SOleksij Rempel 		alcor_trf_block_pio(host, false);
501c5413ad8SOleksij Rempel 		return 1;
502c5413ad8SOleksij Rempel 	case AU6601_INT_DMA_END:
503c5413ad8SOleksij Rempel 		if (!host->sg_count)
504c5413ad8SOleksij Rempel 			break;
505c5413ad8SOleksij Rempel 
506c5413ad8SOleksij Rempel 		alcor_data_set_dma(host);
507c5413ad8SOleksij Rempel 		break;
508c5413ad8SOleksij Rempel 	default:
509c5413ad8SOleksij Rempel 		dev_err(host->dev, "Got READ_BUF_RDY and WRITE_BUF_RDY at same time\n");
510c5413ad8SOleksij Rempel 		break;
511c5413ad8SOleksij Rempel 	}
512c5413ad8SOleksij Rempel 
513157c99c5SDaniel Drake 	if (intmask & AU6601_INT_DATA_END) {
514157c99c5SDaniel Drake 		if (!host->dma_on && host->blocks) {
515157c99c5SDaniel Drake 			alcor_trigger_data_transfer(host);
516157c99c5SDaniel Drake 			return 1;
517157c99c5SDaniel Drake 		} else {
518c5413ad8SOleksij Rempel 			return 0;
519157c99c5SDaniel Drake 		}
520157c99c5SDaniel Drake 	}
521c5413ad8SOleksij Rempel 
522c5413ad8SOleksij Rempel 	return 1;
523c5413ad8SOleksij Rempel }
524c5413ad8SOleksij Rempel 
alcor_data_irq_thread(struct alcor_sdmmc_host * host,u32 intmask)525c5413ad8SOleksij Rempel static void alcor_data_irq_thread(struct alcor_sdmmc_host *host, u32 intmask)
526c5413ad8SOleksij Rempel {
527c5413ad8SOleksij Rempel 	intmask &= AU6601_INT_DATA_MASK;
528c5413ad8SOleksij Rempel 
529c5413ad8SOleksij Rempel 	if (!intmask)
530c5413ad8SOleksij Rempel 		return;
531c5413ad8SOleksij Rempel 
532c5413ad8SOleksij Rempel 	if (!host->data) {
533c5413ad8SOleksij Rempel 		dev_dbg(host->dev, "Got data interrupt 0x%08x even though no data operation was in progress.\n",
534c5413ad8SOleksij Rempel 			intmask);
535c5413ad8SOleksij Rempel 		alcor_reset(host, AU6601_RESET_DATA);
536c5413ad8SOleksij Rempel 		return;
537c5413ad8SOleksij Rempel 	}
538c5413ad8SOleksij Rempel 
539c5413ad8SOleksij Rempel 	if (alcor_data_irq_done(host, intmask))
540c5413ad8SOleksij Rempel 		return;
541c5413ad8SOleksij Rempel 
542c5413ad8SOleksij Rempel 	if ((intmask & AU6601_INT_DATA_END) || !host->blocks ||
543c5413ad8SOleksij Rempel 	    (host->dma_on && !host->sg_count))
544c5413ad8SOleksij Rempel 		alcor_finish_data(host);
545c5413ad8SOleksij Rempel }
546c5413ad8SOleksij Rempel 
alcor_cd_irq(struct alcor_sdmmc_host * host,u32 intmask)547c5413ad8SOleksij Rempel static void alcor_cd_irq(struct alcor_sdmmc_host *host, u32 intmask)
548c5413ad8SOleksij Rempel {
549c5413ad8SOleksij Rempel 	dev_dbg(host->dev, "card %s\n",
550c5413ad8SOleksij Rempel 		intmask & AU6601_INT_CARD_REMOVE ? "removed" : "inserted");
551c5413ad8SOleksij Rempel 
552c5413ad8SOleksij Rempel 	if (host->mrq) {
553c5413ad8SOleksij Rempel 		dev_dbg(host->dev, "cancel all pending tasks.\n");
554c5413ad8SOleksij Rempel 
555c5413ad8SOleksij Rempel 		if (host->data)
556c5413ad8SOleksij Rempel 			host->data->error = -ENOMEDIUM;
557c5413ad8SOleksij Rempel 
558c5413ad8SOleksij Rempel 		if (host->cmd)
559c5413ad8SOleksij Rempel 			host->cmd->error = -ENOMEDIUM;
560c5413ad8SOleksij Rempel 		else
561c5413ad8SOleksij Rempel 			host->mrq->cmd->error = -ENOMEDIUM;
562c5413ad8SOleksij Rempel 
563c5413ad8SOleksij Rempel 		alcor_request_complete(host, 1);
564c5413ad8SOleksij Rempel 	}
565c5413ad8SOleksij Rempel 
566c081e7fdSKamlesh Gurudasani 	mmc_detect_change(mmc_from_priv(host), msecs_to_jiffies(1));
567c5413ad8SOleksij Rempel }
568c5413ad8SOleksij Rempel 
alcor_irq_thread(int irq,void * d)569c5413ad8SOleksij Rempel static irqreturn_t alcor_irq_thread(int irq, void *d)
570c5413ad8SOleksij Rempel {
571c5413ad8SOleksij Rempel 	struct alcor_sdmmc_host *host = d;
572c5413ad8SOleksij Rempel 	irqreturn_t ret = IRQ_HANDLED;
573c5413ad8SOleksij Rempel 	u32 intmask, tmp;
574c5413ad8SOleksij Rempel 
575c5413ad8SOleksij Rempel 	mutex_lock(&host->cmd_mutex);
576c5413ad8SOleksij Rempel 
577c5413ad8SOleksij Rempel 	intmask = host->irq_status_sd;
578c5413ad8SOleksij Rempel 
579c5413ad8SOleksij Rempel 	/* some thing bad */
580c5413ad8SOleksij Rempel 	if (unlikely(!intmask || AU6601_INT_ALL_MASK == intmask)) {
581c5413ad8SOleksij Rempel 		dev_dbg(host->dev, "unexpected IRQ: 0x%04x\n", intmask);
582c5413ad8SOleksij Rempel 		ret = IRQ_NONE;
583c5413ad8SOleksij Rempel 		goto exit;
584c5413ad8SOleksij Rempel 	}
585c5413ad8SOleksij Rempel 
586c5413ad8SOleksij Rempel 	tmp = intmask & (AU6601_INT_CMD_MASK | AU6601_INT_DATA_MASK);
587c5413ad8SOleksij Rempel 	if (tmp) {
588c5413ad8SOleksij Rempel 		if (tmp & AU6601_INT_ERROR_MASK)
589c5413ad8SOleksij Rempel 			alcor_err_irq(host, tmp);
590c5413ad8SOleksij Rempel 		else {
591c5413ad8SOleksij Rempel 			alcor_cmd_irq_thread(host, tmp);
592c5413ad8SOleksij Rempel 			alcor_data_irq_thread(host, tmp);
593c5413ad8SOleksij Rempel 		}
594c5413ad8SOleksij Rempel 		intmask &= ~(AU6601_INT_CMD_MASK | AU6601_INT_DATA_MASK);
595c5413ad8SOleksij Rempel 	}
596c5413ad8SOleksij Rempel 
597c5413ad8SOleksij Rempel 	if (intmask & (AU6601_INT_CARD_INSERT | AU6601_INT_CARD_REMOVE)) {
598c5413ad8SOleksij Rempel 		alcor_cd_irq(host, intmask);
599c5413ad8SOleksij Rempel 		intmask &= ~(AU6601_INT_CARD_INSERT | AU6601_INT_CARD_REMOVE);
600c5413ad8SOleksij Rempel 	}
601c5413ad8SOleksij Rempel 
602c5413ad8SOleksij Rempel 	if (intmask & AU6601_INT_OVER_CURRENT_ERR) {
603c5413ad8SOleksij Rempel 		dev_warn(host->dev,
604c5413ad8SOleksij Rempel 			 "warning: over current detected!\n");
605c5413ad8SOleksij Rempel 		intmask &= ~AU6601_INT_OVER_CURRENT_ERR;
606c5413ad8SOleksij Rempel 	}
607c5413ad8SOleksij Rempel 
608c5413ad8SOleksij Rempel 	if (intmask)
609c5413ad8SOleksij Rempel 		dev_dbg(host->dev, "got not handled IRQ: 0x%04x\n", intmask);
610c5413ad8SOleksij Rempel 
611c5413ad8SOleksij Rempel exit:
612c5413ad8SOleksij Rempel 	mutex_unlock(&host->cmd_mutex);
613c5413ad8SOleksij Rempel 	alcor_unmask_sd_irqs(host);
614c5413ad8SOleksij Rempel 	return ret;
615c5413ad8SOleksij Rempel }
616c5413ad8SOleksij Rempel 
617c5413ad8SOleksij Rempel 
alcor_irq(int irq,void * d)618c5413ad8SOleksij Rempel static irqreturn_t alcor_irq(int irq, void *d)
619c5413ad8SOleksij Rempel {
620c5413ad8SOleksij Rempel 	struct alcor_sdmmc_host *host = d;
621c5413ad8SOleksij Rempel 	struct alcor_pci_priv *priv = host->alcor_pci;
622c5413ad8SOleksij Rempel 	u32 status, tmp;
623c5413ad8SOleksij Rempel 	irqreturn_t ret;
624c5413ad8SOleksij Rempel 	int cmd_done, data_done;
625c5413ad8SOleksij Rempel 
626c5413ad8SOleksij Rempel 	status = alcor_read32(priv, AU6601_REG_INT_STATUS);
627c5413ad8SOleksij Rempel 	if (!status)
628c5413ad8SOleksij Rempel 		return IRQ_NONE;
629c5413ad8SOleksij Rempel 
630c5413ad8SOleksij Rempel 	alcor_write32(priv, status, AU6601_REG_INT_STATUS);
631c5413ad8SOleksij Rempel 
632c5413ad8SOleksij Rempel 	tmp = status & (AU6601_INT_READ_BUF_RDY | AU6601_INT_WRITE_BUF_RDY
633c5413ad8SOleksij Rempel 			| AU6601_INT_DATA_END | AU6601_INT_DMA_END
634c5413ad8SOleksij Rempel 			| AU6601_INT_CMD_END);
635c5413ad8SOleksij Rempel 	if (tmp == status) {
636c5413ad8SOleksij Rempel 		cmd_done = alcor_cmd_irq_done(host, tmp);
637c5413ad8SOleksij Rempel 		data_done = alcor_data_irq_done(host, tmp);
638c5413ad8SOleksij Rempel 		/* use fast path for simple tasks */
639c5413ad8SOleksij Rempel 		if (cmd_done && data_done) {
640c5413ad8SOleksij Rempel 			ret = IRQ_HANDLED;
641c5413ad8SOleksij Rempel 			goto alcor_irq_done;
642c5413ad8SOleksij Rempel 		}
643c5413ad8SOleksij Rempel 	}
644c5413ad8SOleksij Rempel 
645c5413ad8SOleksij Rempel 	host->irq_status_sd = status;
646c5413ad8SOleksij Rempel 	ret = IRQ_WAKE_THREAD;
647c5413ad8SOleksij Rempel 	alcor_mask_sd_irqs(host);
648c5413ad8SOleksij Rempel alcor_irq_done:
649c5413ad8SOleksij Rempel 	return ret;
650c5413ad8SOleksij Rempel }
651c5413ad8SOleksij Rempel 
alcor_set_clock(struct alcor_sdmmc_host * host,unsigned int clock)652c5413ad8SOleksij Rempel static void alcor_set_clock(struct alcor_sdmmc_host *host, unsigned int clock)
653c5413ad8SOleksij Rempel {
654c5413ad8SOleksij Rempel 	struct alcor_pci_priv *priv = host->alcor_pci;
655c5413ad8SOleksij Rempel 	int i, diff = 0x7fffffff, tmp_clock = 0;
656c5413ad8SOleksij Rempel 	u16 clk_src = 0;
657c5413ad8SOleksij Rempel 	u8 clk_div = 0;
658c5413ad8SOleksij Rempel 
659c5413ad8SOleksij Rempel 	if (clock == 0) {
660c5413ad8SOleksij Rempel 		alcor_write16(priv, 0, AU6601_CLK_SELECT);
661c5413ad8SOleksij Rempel 		return;
662c5413ad8SOleksij Rempel 	}
663c5413ad8SOleksij Rempel 
664c5413ad8SOleksij Rempel 	for (i = 0; i < ARRAY_SIZE(alcor_pll_cfg); i++) {
665c5413ad8SOleksij Rempel 		unsigned int tmp_div, tmp_diff;
666c5413ad8SOleksij Rempel 		const struct alcor_pll_conf *cfg = &alcor_pll_cfg[i];
667c5413ad8SOleksij Rempel 
668c5413ad8SOleksij Rempel 		tmp_div = DIV_ROUND_UP(cfg->clk_src_freq, clock);
669c5413ad8SOleksij Rempel 		if (cfg->min_div > tmp_div || tmp_div > cfg->max_div)
670c5413ad8SOleksij Rempel 			continue;
671c5413ad8SOleksij Rempel 
672c5413ad8SOleksij Rempel 		tmp_clock = DIV_ROUND_UP(cfg->clk_src_freq, tmp_div);
673c5413ad8SOleksij Rempel 		tmp_diff = abs(clock - tmp_clock);
674c5413ad8SOleksij Rempel 
67542248a91SColin Ian King 		if (tmp_diff < diff) {
676c5413ad8SOleksij Rempel 			diff = tmp_diff;
677c5413ad8SOleksij Rempel 			clk_src = cfg->clk_src_reg;
678c5413ad8SOleksij Rempel 			clk_div = tmp_div;
679c5413ad8SOleksij Rempel 		}
680c5413ad8SOleksij Rempel 	}
681c5413ad8SOleksij Rempel 
682c5413ad8SOleksij Rempel 	clk_src |= ((clk_div - 1) << 8);
683c5413ad8SOleksij Rempel 	clk_src |= AU6601_CLK_ENABLE;
684c5413ad8SOleksij Rempel 
685c5413ad8SOleksij Rempel 	dev_dbg(host->dev, "set freq %d cal freq %d, use div %d, mod %x\n",
686c5413ad8SOleksij Rempel 			clock, tmp_clock, clk_div, clk_src);
687c5413ad8SOleksij Rempel 
688c5413ad8SOleksij Rempel 	alcor_write16(priv, clk_src, AU6601_CLK_SELECT);
689c5413ad8SOleksij Rempel 
690c5413ad8SOleksij Rempel }
691c5413ad8SOleksij Rempel 
alcor_set_timing(struct mmc_host * mmc,struct mmc_ios * ios)692c5413ad8SOleksij Rempel static void alcor_set_timing(struct mmc_host *mmc, struct mmc_ios *ios)
693c5413ad8SOleksij Rempel {
694c5413ad8SOleksij Rempel 	struct alcor_sdmmc_host *host = mmc_priv(mmc);
695c5413ad8SOleksij Rempel 
696c5413ad8SOleksij Rempel 	if (ios->timing == MMC_TIMING_LEGACY) {
697c5413ad8SOleksij Rempel 		alcor_rmw8(host, AU6601_CLK_DELAY,
698c5413ad8SOleksij Rempel 			    AU6601_CLK_POSITIVE_EDGE_ALL, 0);
699c5413ad8SOleksij Rempel 	} else {
700c5413ad8SOleksij Rempel 		alcor_rmw8(host, AU6601_CLK_DELAY,
701c5413ad8SOleksij Rempel 			    0, AU6601_CLK_POSITIVE_EDGE_ALL);
702c5413ad8SOleksij Rempel 	}
703c5413ad8SOleksij Rempel }
704c5413ad8SOleksij Rempel 
alcor_set_bus_width(struct mmc_host * mmc,struct mmc_ios * ios)705c5413ad8SOleksij Rempel static void alcor_set_bus_width(struct mmc_host *mmc, struct mmc_ios *ios)
706c5413ad8SOleksij Rempel {
707c5413ad8SOleksij Rempel 	struct alcor_sdmmc_host *host = mmc_priv(mmc);
708c5413ad8SOleksij Rempel 	struct alcor_pci_priv *priv = host->alcor_pci;
709c5413ad8SOleksij Rempel 
710c5413ad8SOleksij Rempel 	if (ios->bus_width == MMC_BUS_WIDTH_1) {
711c5413ad8SOleksij Rempel 		alcor_write8(priv, 0, AU6601_REG_BUS_CTRL);
712c5413ad8SOleksij Rempel 	} else if (ios->bus_width == MMC_BUS_WIDTH_4) {
713c5413ad8SOleksij Rempel 		alcor_write8(priv, AU6601_BUS_WIDTH_4BIT,
714c5413ad8SOleksij Rempel 			      AU6601_REG_BUS_CTRL);
715c5413ad8SOleksij Rempel 	} else
716c5413ad8SOleksij Rempel 		dev_err(host->dev, "Unknown BUS mode\n");
717c5413ad8SOleksij Rempel 
718c5413ad8SOleksij Rempel }
719c5413ad8SOleksij Rempel 
alcor_card_busy(struct mmc_host * mmc)720c5413ad8SOleksij Rempel static int alcor_card_busy(struct mmc_host *mmc)
721c5413ad8SOleksij Rempel {
722c5413ad8SOleksij Rempel 	struct alcor_sdmmc_host *host = mmc_priv(mmc);
723c5413ad8SOleksij Rempel 	struct alcor_pci_priv *priv = host->alcor_pci;
724c5413ad8SOleksij Rempel 	u8 status;
725c5413ad8SOleksij Rempel 
726c5413ad8SOleksij Rempel 	/* Check whether dat[0:3] low */
727c5413ad8SOleksij Rempel 	status = alcor_read8(priv, AU6601_DATA_PIN_STATE);
728c5413ad8SOleksij Rempel 
729c5413ad8SOleksij Rempel 	return !(status & AU6601_BUS_STAT_DAT_MASK);
730c5413ad8SOleksij Rempel }
731c5413ad8SOleksij Rempel 
alcor_get_cd(struct mmc_host * mmc)732c5413ad8SOleksij Rempel static int alcor_get_cd(struct mmc_host *mmc)
733c5413ad8SOleksij Rempel {
734c5413ad8SOleksij Rempel 	struct alcor_sdmmc_host *host = mmc_priv(mmc);
735c5413ad8SOleksij Rempel 	struct alcor_pci_priv *priv = host->alcor_pci;
736c5413ad8SOleksij Rempel 	u8 detect;
737c5413ad8SOleksij Rempel 
738c5413ad8SOleksij Rempel 	detect = alcor_read8(priv, AU6601_DETECT_STATUS)
739c5413ad8SOleksij Rempel 		& AU6601_DETECT_STATUS_M;
740c5413ad8SOleksij Rempel 	/* check if card is present then send command and data */
741c5413ad8SOleksij Rempel 	return (detect == AU6601_SD_DETECTED);
742c5413ad8SOleksij Rempel }
743c5413ad8SOleksij Rempel 
alcor_get_ro(struct mmc_host * mmc)744c5413ad8SOleksij Rempel static int alcor_get_ro(struct mmc_host *mmc)
745c5413ad8SOleksij Rempel {
746c5413ad8SOleksij Rempel 	struct alcor_sdmmc_host *host = mmc_priv(mmc);
747c5413ad8SOleksij Rempel 	struct alcor_pci_priv *priv = host->alcor_pci;
748c5413ad8SOleksij Rempel 	u8 status;
749c5413ad8SOleksij Rempel 
750c5413ad8SOleksij Rempel 	/* get write protect pin status */
751c5413ad8SOleksij Rempel 	status = alcor_read8(priv, AU6601_INTERFACE_MODE_CTRL);
752c5413ad8SOleksij Rempel 
753c5413ad8SOleksij Rempel 	return !!(status & AU6601_SD_CARD_WP);
754c5413ad8SOleksij Rempel }
755c5413ad8SOleksij Rempel 
alcor_request(struct mmc_host * mmc,struct mmc_request * mrq)756c5413ad8SOleksij Rempel static void alcor_request(struct mmc_host *mmc, struct mmc_request *mrq)
757c5413ad8SOleksij Rempel {
758c5413ad8SOleksij Rempel 	struct alcor_sdmmc_host *host = mmc_priv(mmc);
759c5413ad8SOleksij Rempel 
760c5413ad8SOleksij Rempel 	mutex_lock(&host->cmd_mutex);
761c5413ad8SOleksij Rempel 
762c5413ad8SOleksij Rempel 	host->mrq = mrq;
763c5413ad8SOleksij Rempel 
764c5413ad8SOleksij Rempel 	/* check if card is present then send command and data */
765c5413ad8SOleksij Rempel 	if (alcor_get_cd(mmc))
766c5413ad8SOleksij Rempel 		alcor_send_cmd(host, mrq->cmd, true);
767c5413ad8SOleksij Rempel 	else {
768c5413ad8SOleksij Rempel 		mrq->cmd->error = -ENOMEDIUM;
769c5413ad8SOleksij Rempel 		alcor_request_complete(host, 1);
770c5413ad8SOleksij Rempel 	}
771c5413ad8SOleksij Rempel 
772c5413ad8SOleksij Rempel 	mutex_unlock(&host->cmd_mutex);
773c5413ad8SOleksij Rempel }
774c5413ad8SOleksij Rempel 
alcor_pre_req(struct mmc_host * mmc,struct mmc_request * mrq)775c5413ad8SOleksij Rempel static void alcor_pre_req(struct mmc_host *mmc,
776c5413ad8SOleksij Rempel 			   struct mmc_request *mrq)
777c5413ad8SOleksij Rempel {
778c5413ad8SOleksij Rempel 	struct alcor_sdmmc_host *host = mmc_priv(mmc);
779c5413ad8SOleksij Rempel 	struct mmc_data *data = mrq->data;
780c5413ad8SOleksij Rempel 	struct mmc_command *cmd = mrq->cmd;
781c5413ad8SOleksij Rempel 	struct scatterlist *sg;
782c5413ad8SOleksij Rempel 	unsigned int i, sg_len;
783c5413ad8SOleksij Rempel 
784c5413ad8SOleksij Rempel 	if (!data || !cmd)
785c5413ad8SOleksij Rempel 		return;
786c5413ad8SOleksij Rempel 
787c5413ad8SOleksij Rempel 	data->host_cookie = COOKIE_UNMAPPED;
788c5413ad8SOleksij Rempel 
789c5413ad8SOleksij Rempel 	/* FIXME: looks like the DMA engine works only with CMD18 */
790e5a34b0cSDaniel Drake 	if (cmd->opcode != MMC_READ_MULTIPLE_BLOCK
791e5a34b0cSDaniel Drake 			&& cmd->opcode != MMC_WRITE_MULTIPLE_BLOCK)
792c5413ad8SOleksij Rempel 		return;
793c5413ad8SOleksij Rempel 	/*
794c5413ad8SOleksij Rempel 	 * We don't do DMA on "complex" transfers, i.e. with
795c671b6deSDaniel Drake 	 * non-word-aligned buffers or lengths. A future improvement
796c671b6deSDaniel Drake 	 * could be made to use temporary DMA bounce-buffers when these
797c671b6deSDaniel Drake 	 * requirements are not met.
798c671b6deSDaniel Drake 	 *
799c671b6deSDaniel Drake 	 * Also, we don't bother with all the DMA setup overhead for
800c671b6deSDaniel Drake 	 * short transfers.
801c5413ad8SOleksij Rempel 	 */
802c5413ad8SOleksij Rempel 	if (data->blocks * data->blksz < AU6601_MAX_DMA_BLOCK_SIZE)
803c5413ad8SOleksij Rempel 		return;
804c5413ad8SOleksij Rempel 
805c5413ad8SOleksij Rempel 	if (data->blksz & 3)
806c5413ad8SOleksij Rempel 		return;
807c5413ad8SOleksij Rempel 
808c5413ad8SOleksij Rempel 	for_each_sg(data->sg, sg, data->sg_len, i) {
809c5413ad8SOleksij Rempel 		if (sg->length != AU6601_MAX_DMA_BLOCK_SIZE)
810c5413ad8SOleksij Rempel 			return;
811c671b6deSDaniel Drake 		if (sg->offset != 0)
812c671b6deSDaniel Drake 			return;
813c5413ad8SOleksij Rempel 	}
814c5413ad8SOleksij Rempel 
815c5413ad8SOleksij Rempel 	/* This data might be unmapped at this time */
816c5413ad8SOleksij Rempel 
817c5413ad8SOleksij Rempel 	sg_len = dma_map_sg(host->dev, data->sg, data->sg_len,
818c5413ad8SOleksij Rempel 			    mmc_get_dma_dir(data));
819c5413ad8SOleksij Rempel 	if (sg_len)
820c5413ad8SOleksij Rempel 		data->host_cookie = COOKIE_MAPPED;
821c5413ad8SOleksij Rempel 
822c5413ad8SOleksij Rempel 	data->sg_count = sg_len;
823c5413ad8SOleksij Rempel }
824c5413ad8SOleksij Rempel 
alcor_post_req(struct mmc_host * mmc,struct mmc_request * mrq,int err)825c5413ad8SOleksij Rempel static void alcor_post_req(struct mmc_host *mmc,
826c5413ad8SOleksij Rempel 			    struct mmc_request *mrq,
827c5413ad8SOleksij Rempel 			    int err)
828c5413ad8SOleksij Rempel {
829c5413ad8SOleksij Rempel 	struct alcor_sdmmc_host *host = mmc_priv(mmc);
830c5413ad8SOleksij Rempel 	struct mmc_data *data = mrq->data;
831c5413ad8SOleksij Rempel 
832c5413ad8SOleksij Rempel 	if (!data)
833c5413ad8SOleksij Rempel 		return;
834c5413ad8SOleksij Rempel 
835c5413ad8SOleksij Rempel 	if (data->host_cookie == COOKIE_MAPPED) {
836c5413ad8SOleksij Rempel 		dma_unmap_sg(host->dev,
837c5413ad8SOleksij Rempel 			     data->sg,
838c5413ad8SOleksij Rempel 			     data->sg_len,
839c5413ad8SOleksij Rempel 			     mmc_get_dma_dir(data));
840c5413ad8SOleksij Rempel 	}
841c5413ad8SOleksij Rempel 
842c5413ad8SOleksij Rempel 	data->host_cookie = COOKIE_UNMAPPED;
843c5413ad8SOleksij Rempel }
844c5413ad8SOleksij Rempel 
alcor_set_power_mode(struct mmc_host * mmc,struct mmc_ios * ios)845c5413ad8SOleksij Rempel static void alcor_set_power_mode(struct mmc_host *mmc, struct mmc_ios *ios)
846c5413ad8SOleksij Rempel {
847c5413ad8SOleksij Rempel 	struct alcor_sdmmc_host *host = mmc_priv(mmc);
848c5413ad8SOleksij Rempel 	struct alcor_pci_priv *priv = host->alcor_pci;
849c5413ad8SOleksij Rempel 
850c5413ad8SOleksij Rempel 	switch (ios->power_mode) {
851c5413ad8SOleksij Rempel 	case MMC_POWER_OFF:
852c5413ad8SOleksij Rempel 		alcor_set_clock(host, ios->clock);
853c5413ad8SOleksij Rempel 		/* set all pins to input */
854c5413ad8SOleksij Rempel 		alcor_write8(priv, 0, AU6601_OUTPUT_ENABLE);
855c5413ad8SOleksij Rempel 		/* turn of VDD */
856c5413ad8SOleksij Rempel 		alcor_write8(priv, 0, AU6601_POWER_CONTROL);
857c5413ad8SOleksij Rempel 		break;
858c5413ad8SOleksij Rempel 	case MMC_POWER_UP:
859c5413ad8SOleksij Rempel 		break;
860c5413ad8SOleksij Rempel 	case MMC_POWER_ON:
861c5413ad8SOleksij Rempel 		/* This is most trickiest part. The order and timings of
862c5413ad8SOleksij Rempel 		 * instructions seems to play important role. Any changes may
863c5413ad8SOleksij Rempel 		 * confuse internal state engine if this HW.
864c5413ad8SOleksij Rempel 		 * FIXME: If we will ever get access to documentation, then this
865c5413ad8SOleksij Rempel 		 * part should be reviewed again.
866c5413ad8SOleksij Rempel 		 */
867c5413ad8SOleksij Rempel 
868c5413ad8SOleksij Rempel 		/* enable SD card mode */
869c5413ad8SOleksij Rempel 		alcor_write8(priv, AU6601_SD_CARD,
870c5413ad8SOleksij Rempel 			      AU6601_ACTIVE_CTRL);
871c5413ad8SOleksij Rempel 		/* set signal voltage to 3.3V */
872c5413ad8SOleksij Rempel 		alcor_write8(priv, 0, AU6601_OPT);
873c5413ad8SOleksij Rempel 		/* no documentation about clk delay, for now just try to mimic
874c5413ad8SOleksij Rempel 		 * original driver.
875c5413ad8SOleksij Rempel 		 */
876c5413ad8SOleksij Rempel 		alcor_write8(priv, 0x20, AU6601_CLK_DELAY);
877c5413ad8SOleksij Rempel 		/* set BUS width to 1 bit */
878c5413ad8SOleksij Rempel 		alcor_write8(priv, 0, AU6601_REG_BUS_CTRL);
879c5413ad8SOleksij Rempel 		/* set CLK first time */
880c5413ad8SOleksij Rempel 		alcor_set_clock(host, ios->clock);
881c5413ad8SOleksij Rempel 		/* power on VDD */
882c5413ad8SOleksij Rempel 		alcor_write8(priv, AU6601_SD_CARD,
883c5413ad8SOleksij Rempel 			      AU6601_POWER_CONTROL);
884c5413ad8SOleksij Rempel 		/* wait until the CLK will get stable */
885c5413ad8SOleksij Rempel 		mdelay(20);
886c5413ad8SOleksij Rempel 		/* set CLK again, mimic original driver. */
887c5413ad8SOleksij Rempel 		alcor_set_clock(host, ios->clock);
888c5413ad8SOleksij Rempel 
889c5413ad8SOleksij Rempel 		/* enable output */
890c5413ad8SOleksij Rempel 		alcor_write8(priv, AU6601_SD_CARD,
891c5413ad8SOleksij Rempel 			      AU6601_OUTPUT_ENABLE);
892c5413ad8SOleksij Rempel 		/* The clk will not work on au6621. We need to trigger data
893c5413ad8SOleksij Rempel 		 * transfer.
894c5413ad8SOleksij Rempel 		 */
895c5413ad8SOleksij Rempel 		alcor_write8(priv, AU6601_DATA_WRITE,
896c5413ad8SOleksij Rempel 			      AU6601_DATA_XFER_CTRL);
897c5413ad8SOleksij Rempel 		/* configure timeout. Not clear what exactly it means. */
898c5413ad8SOleksij Rempel 		alcor_write8(priv, 0x7d, AU6601_TIME_OUT_CTRL);
899c5413ad8SOleksij Rempel 		mdelay(100);
900c5413ad8SOleksij Rempel 		break;
901c5413ad8SOleksij Rempel 	default:
902c5413ad8SOleksij Rempel 		dev_err(host->dev, "Unknown power parameter\n");
903c5413ad8SOleksij Rempel 	}
904c5413ad8SOleksij Rempel }
905c5413ad8SOleksij Rempel 
alcor_set_ios(struct mmc_host * mmc,struct mmc_ios * ios)906c5413ad8SOleksij Rempel static void alcor_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
907c5413ad8SOleksij Rempel {
908c5413ad8SOleksij Rempel 	struct alcor_sdmmc_host *host = mmc_priv(mmc);
909c5413ad8SOleksij Rempel 
910c5413ad8SOleksij Rempel 	mutex_lock(&host->cmd_mutex);
911c5413ad8SOleksij Rempel 
912c5413ad8SOleksij Rempel 	dev_dbg(host->dev, "set ios. bus width: %x, power mode: %x\n",
913c5413ad8SOleksij Rempel 		ios->bus_width, ios->power_mode);
914c5413ad8SOleksij Rempel 
915c5413ad8SOleksij Rempel 	if (ios->power_mode != host->cur_power_mode) {
916c5413ad8SOleksij Rempel 		alcor_set_power_mode(mmc, ios);
917c5413ad8SOleksij Rempel 		host->cur_power_mode = ios->power_mode;
918c5413ad8SOleksij Rempel 	} else {
919c5413ad8SOleksij Rempel 		alcor_set_timing(mmc, ios);
920c5413ad8SOleksij Rempel 		alcor_set_bus_width(mmc, ios);
921c5413ad8SOleksij Rempel 		alcor_set_clock(host, ios->clock);
922c5413ad8SOleksij Rempel 	}
923c5413ad8SOleksij Rempel 
924c5413ad8SOleksij Rempel 	mutex_unlock(&host->cmd_mutex);
925c5413ad8SOleksij Rempel }
926c5413ad8SOleksij Rempel 
alcor_signal_voltage_switch(struct mmc_host * mmc,struct mmc_ios * ios)927c5413ad8SOleksij Rempel static int alcor_signal_voltage_switch(struct mmc_host *mmc,
928c5413ad8SOleksij Rempel 				       struct mmc_ios *ios)
929c5413ad8SOleksij Rempel {
930c5413ad8SOleksij Rempel 	struct alcor_sdmmc_host *host = mmc_priv(mmc);
931c5413ad8SOleksij Rempel 
932c5413ad8SOleksij Rempel 	mutex_lock(&host->cmd_mutex);
933c5413ad8SOleksij Rempel 
934c5413ad8SOleksij Rempel 	switch (ios->signal_voltage) {
935c5413ad8SOleksij Rempel 	case MMC_SIGNAL_VOLTAGE_330:
936c5413ad8SOleksij Rempel 		alcor_rmw8(host, AU6601_OPT, AU6601_OPT_SD_18V, 0);
937c5413ad8SOleksij Rempel 		break;
938c5413ad8SOleksij Rempel 	case MMC_SIGNAL_VOLTAGE_180:
939c5413ad8SOleksij Rempel 		alcor_rmw8(host, AU6601_OPT, 0, AU6601_OPT_SD_18V);
940c5413ad8SOleksij Rempel 		break;
941c5413ad8SOleksij Rempel 	default:
942c5413ad8SOleksij Rempel 		/* No signal voltage switch required */
943c5413ad8SOleksij Rempel 		break;
944c5413ad8SOleksij Rempel 	}
945c5413ad8SOleksij Rempel 
946c5413ad8SOleksij Rempel 	mutex_unlock(&host->cmd_mutex);
947c5413ad8SOleksij Rempel 	return 0;
948c5413ad8SOleksij Rempel }
949c5413ad8SOleksij Rempel 
950c5413ad8SOleksij Rempel static const struct mmc_host_ops alcor_sdc_ops = {
951c5413ad8SOleksij Rempel 	.card_busy	= alcor_card_busy,
952c5413ad8SOleksij Rempel 	.get_cd		= alcor_get_cd,
953c5413ad8SOleksij Rempel 	.get_ro		= alcor_get_ro,
954c5413ad8SOleksij Rempel 	.post_req	= alcor_post_req,
955c5413ad8SOleksij Rempel 	.pre_req	= alcor_pre_req,
956c5413ad8SOleksij Rempel 	.request	= alcor_request,
957c5413ad8SOleksij Rempel 	.set_ios	= alcor_set_ios,
958c5413ad8SOleksij Rempel 	.start_signal_voltage_switch = alcor_signal_voltage_switch,
959c5413ad8SOleksij Rempel };
960c5413ad8SOleksij Rempel 
alcor_timeout_timer(struct work_struct * work)961c5413ad8SOleksij Rempel static void alcor_timeout_timer(struct work_struct *work)
962c5413ad8SOleksij Rempel {
963c5413ad8SOleksij Rempel 	struct delayed_work *d = to_delayed_work(work);
964c5413ad8SOleksij Rempel 	struct alcor_sdmmc_host *host = container_of(d, struct alcor_sdmmc_host,
965c5413ad8SOleksij Rempel 						timeout_work);
966c5413ad8SOleksij Rempel 	mutex_lock(&host->cmd_mutex);
967c5413ad8SOleksij Rempel 
968c5413ad8SOleksij Rempel 	dev_dbg(host->dev, "triggered timeout\n");
969c5413ad8SOleksij Rempel 	if (host->mrq) {
970c5413ad8SOleksij Rempel 		dev_err(host->dev, "Timeout waiting for hardware interrupt.\n");
971c5413ad8SOleksij Rempel 
972c5413ad8SOleksij Rempel 		if (host->data) {
973c5413ad8SOleksij Rempel 			host->data->error = -ETIMEDOUT;
974c5413ad8SOleksij Rempel 		} else {
975c5413ad8SOleksij Rempel 			if (host->cmd)
976c5413ad8SOleksij Rempel 				host->cmd->error = -ETIMEDOUT;
977c5413ad8SOleksij Rempel 			else
978c5413ad8SOleksij Rempel 				host->mrq->cmd->error = -ETIMEDOUT;
979c5413ad8SOleksij Rempel 		}
980c5413ad8SOleksij Rempel 
981c5413ad8SOleksij Rempel 		alcor_reset(host, AU6601_RESET_CMD | AU6601_RESET_DATA);
982c5413ad8SOleksij Rempel 		alcor_request_complete(host, 0);
983c5413ad8SOleksij Rempel 	}
984c5413ad8SOleksij Rempel 
985c5413ad8SOleksij Rempel 	mutex_unlock(&host->cmd_mutex);
986c5413ad8SOleksij Rempel }
987c5413ad8SOleksij Rempel 
alcor_hw_init(struct alcor_sdmmc_host * host)988c5413ad8SOleksij Rempel static void alcor_hw_init(struct alcor_sdmmc_host *host)
989c5413ad8SOleksij Rempel {
990c5413ad8SOleksij Rempel 	struct alcor_pci_priv *priv = host->alcor_pci;
991c5413ad8SOleksij Rempel 	struct alcor_dev_cfg *cfg = priv->cfg;
992c5413ad8SOleksij Rempel 
993c5413ad8SOleksij Rempel 	/* FIXME: This part is a mimics HW init of original driver.
994c5413ad8SOleksij Rempel 	 * If we will ever get access to documentation, then this part
995c5413ad8SOleksij Rempel 	 * should be reviewed again.
996c5413ad8SOleksij Rempel 	 */
997c5413ad8SOleksij Rempel 
998c5413ad8SOleksij Rempel 	/* reset command state engine */
999c5413ad8SOleksij Rempel 	alcor_reset(host, AU6601_RESET_CMD);
1000c5413ad8SOleksij Rempel 
1001c5413ad8SOleksij Rempel 	alcor_write8(priv, 0, AU6601_DMA_BOUNDARY);
1002c5413ad8SOleksij Rempel 	/* enable sd card mode */
1003c5413ad8SOleksij Rempel 	alcor_write8(priv, AU6601_SD_CARD, AU6601_ACTIVE_CTRL);
1004c5413ad8SOleksij Rempel 
1005c5413ad8SOleksij Rempel 	/* set BUS width to 1 bit */
1006c5413ad8SOleksij Rempel 	alcor_write8(priv, 0, AU6601_REG_BUS_CTRL);
1007c5413ad8SOleksij Rempel 
1008c5413ad8SOleksij Rempel 	/* reset data state engine */
1009c5413ad8SOleksij Rempel 	alcor_reset(host, AU6601_RESET_DATA);
1010c5413ad8SOleksij Rempel 	/* Not sure if a voodoo with AU6601_DMA_BOUNDARY is really needed */
1011c5413ad8SOleksij Rempel 	alcor_write8(priv, 0, AU6601_DMA_BOUNDARY);
1012c5413ad8SOleksij Rempel 
1013c5413ad8SOleksij Rempel 	alcor_write8(priv, 0, AU6601_INTERFACE_MODE_CTRL);
1014c5413ad8SOleksij Rempel 	/* not clear what we are doing here. */
1015c5413ad8SOleksij Rempel 	alcor_write8(priv, 0x44, AU6601_PAD_DRIVE0);
1016c5413ad8SOleksij Rempel 	alcor_write8(priv, 0x44, AU6601_PAD_DRIVE1);
1017c5413ad8SOleksij Rempel 	alcor_write8(priv, 0x00, AU6601_PAD_DRIVE2);
1018c5413ad8SOleksij Rempel 
1019c5413ad8SOleksij Rempel 	/* for 6601 - dma_boundary; for 6621 - dma_page_cnt
1020c5413ad8SOleksij Rempel 	 * exact meaning of this register is not clear.
1021c5413ad8SOleksij Rempel 	 */
1022c5413ad8SOleksij Rempel 	alcor_write8(priv, cfg->dma, AU6601_DMA_BOUNDARY);
1023c5413ad8SOleksij Rempel 
1024c5413ad8SOleksij Rempel 	/* make sure all pins are set to input and VDD is off */
1025c5413ad8SOleksij Rempel 	alcor_write8(priv, 0, AU6601_OUTPUT_ENABLE);
1026c5413ad8SOleksij Rempel 	alcor_write8(priv, 0, AU6601_POWER_CONTROL);
1027c5413ad8SOleksij Rempel 
1028c5413ad8SOleksij Rempel 	alcor_write8(priv, AU6601_DETECT_EN, AU6601_DETECT_STATUS);
1029c5413ad8SOleksij Rempel 	/* now we should be safe to enable IRQs */
1030c5413ad8SOleksij Rempel 	alcor_unmask_sd_irqs(host);
1031c5413ad8SOleksij Rempel }
1032c5413ad8SOleksij Rempel 
alcor_hw_uninit(struct alcor_sdmmc_host * host)1033c5413ad8SOleksij Rempel static void alcor_hw_uninit(struct alcor_sdmmc_host *host)
1034c5413ad8SOleksij Rempel {
1035c5413ad8SOleksij Rempel 	struct alcor_pci_priv *priv = host->alcor_pci;
1036c5413ad8SOleksij Rempel 
1037c5413ad8SOleksij Rempel 	alcor_mask_sd_irqs(host);
1038c5413ad8SOleksij Rempel 	alcor_reset(host, AU6601_RESET_CMD | AU6601_RESET_DATA);
1039c5413ad8SOleksij Rempel 
1040c5413ad8SOleksij Rempel 	alcor_write8(priv, 0, AU6601_DETECT_STATUS);
1041c5413ad8SOleksij Rempel 
1042c5413ad8SOleksij Rempel 	alcor_write8(priv, 0, AU6601_OUTPUT_ENABLE);
1043c5413ad8SOleksij Rempel 	alcor_write8(priv, 0, AU6601_POWER_CONTROL);
1044c5413ad8SOleksij Rempel 
1045c5413ad8SOleksij Rempel 	alcor_write8(priv, 0, AU6601_OPT);
1046c5413ad8SOleksij Rempel }
1047c5413ad8SOleksij Rempel 
alcor_init_mmc(struct alcor_sdmmc_host * host)1048c5413ad8SOleksij Rempel static void alcor_init_mmc(struct alcor_sdmmc_host *host)
1049c5413ad8SOleksij Rempel {
1050c081e7fdSKamlesh Gurudasani 	struct mmc_host *mmc = mmc_from_priv(host);
1051c5413ad8SOleksij Rempel 
1052c5413ad8SOleksij Rempel 	mmc->f_min = AU6601_MIN_CLOCK;
1053c5413ad8SOleksij Rempel 	mmc->f_max = AU6601_MAX_CLOCK;
1054c5413ad8SOleksij Rempel 	mmc->ocr_avail = MMC_VDD_33_34;
1055c5413ad8SOleksij Rempel 	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED
1056c5413ad8SOleksij Rempel 		| MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50
1057c5413ad8SOleksij Rempel 		| MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50;
1058c5413ad8SOleksij Rempel 	mmc->caps2 = MMC_CAP2_NO_SDIO;
1059c5413ad8SOleksij Rempel 	mmc->ops = &alcor_sdc_ops;
1060c5413ad8SOleksij Rempel 
10615ea47691SDaniel Drake 	/* The hardware does DMA data transfer of 4096 bytes to/from a single
1062c671b6deSDaniel Drake 	 * buffer address. Scatterlists are not supported at the hardware
1063c671b6deSDaniel Drake 	 * level, however we can work with them at the driver level,
1064c671b6deSDaniel Drake 	 * provided that each segment is exactly 4096 bytes in size.
1065c671b6deSDaniel Drake 	 * Upon DMA completion of a single segment (signalled via IRQ), we
1066c671b6deSDaniel Drake 	 * immediately proceed to transfer the next segment from the
1067c671b6deSDaniel Drake 	 * scatterlist.
10685ea47691SDaniel Drake 	 *
1069c671b6deSDaniel Drake 	 * The overall request is limited to 240 sectors, matching the
1070c671b6deSDaniel Drake 	 * original vendor driver.
10715ea47691SDaniel Drake 	 */
1072c5413ad8SOleksij Rempel 	mmc->max_segs = AU6601_MAX_DMA_SEGMENTS;
1073c5413ad8SOleksij Rempel 	mmc->max_seg_size = AU6601_MAX_DMA_BLOCK_SIZE;
1074c671b6deSDaniel Drake 	mmc->max_blk_count = 240;
1075c671b6deSDaniel Drake 	mmc->max_req_size = mmc->max_blk_count * mmc->max_blk_size;
1076c671b6deSDaniel Drake 	dma_set_max_seg_size(host->dev, mmc->max_seg_size);
1077c5413ad8SOleksij Rempel }
1078c5413ad8SOleksij Rempel 
alcor_pci_sdmmc_drv_probe(struct platform_device * pdev)1079c5413ad8SOleksij Rempel static int alcor_pci_sdmmc_drv_probe(struct platform_device *pdev)
1080c5413ad8SOleksij Rempel {
1081c5413ad8SOleksij Rempel 	struct alcor_pci_priv *priv = pdev->dev.platform_data;
1082c5413ad8SOleksij Rempel 	struct mmc_host *mmc;
1083c5413ad8SOleksij Rempel 	struct alcor_sdmmc_host *host;
1084c5413ad8SOleksij Rempel 	int ret;
1085c5413ad8SOleksij Rempel 
1086c5413ad8SOleksij Rempel 	mmc = mmc_alloc_host(sizeof(*host), &pdev->dev);
1087c5413ad8SOleksij Rempel 	if (!mmc) {
1088c5413ad8SOleksij Rempel 		dev_err(&pdev->dev, "Can't allocate MMC\n");
1089c5413ad8SOleksij Rempel 		return -ENOMEM;
1090c5413ad8SOleksij Rempel 	}
1091c5413ad8SOleksij Rempel 
1092c5413ad8SOleksij Rempel 	host = mmc_priv(mmc);
1093c5413ad8SOleksij Rempel 	host->dev = &pdev->dev;
1094c5413ad8SOleksij Rempel 	host->cur_power_mode = MMC_POWER_UNDEFINED;
1095c5413ad8SOleksij Rempel 	host->alcor_pci = priv;
1096c5413ad8SOleksij Rempel 
1097c5413ad8SOleksij Rempel 	/* make sure irqs are disabled */
1098c5413ad8SOleksij Rempel 	alcor_write32(priv, 0, AU6601_REG_INT_ENABLE);
1099c5413ad8SOleksij Rempel 	alcor_write32(priv, 0, AU6601_MS_INT_ENABLE);
1100c5413ad8SOleksij Rempel 
1101c5413ad8SOleksij Rempel 	ret = devm_request_threaded_irq(&pdev->dev, priv->irq,
1102c5413ad8SOleksij Rempel 			alcor_irq, alcor_irq_thread, IRQF_SHARED,
1103c5413ad8SOleksij Rempel 			DRV_NAME_ALCOR_PCI_SDMMC, host);
1104c5413ad8SOleksij Rempel 
1105c5413ad8SOleksij Rempel 	if (ret) {
1106c5413ad8SOleksij Rempel 		dev_err(&pdev->dev, "Failed to get irq for data line\n");
11077c277dd2SChristophe JAILLET 		goto free_host;
1108c5413ad8SOleksij Rempel 	}
1109c5413ad8SOleksij Rempel 
1110c5413ad8SOleksij Rempel 	mutex_init(&host->cmd_mutex);
1111c5413ad8SOleksij Rempel 	INIT_DELAYED_WORK(&host->timeout_work, alcor_timeout_timer);
1112c5413ad8SOleksij Rempel 
1113c5413ad8SOleksij Rempel 	alcor_init_mmc(host);
1114c5413ad8SOleksij Rempel 	alcor_hw_init(host);
1115c5413ad8SOleksij Rempel 
1116c5413ad8SOleksij Rempel 	dev_set_drvdata(&pdev->dev, host);
1117e93d1468SYang Yingliang 	ret = mmc_add_host(mmc);
1118e93d1468SYang Yingliang 	if (ret)
1119e93d1468SYang Yingliang 		goto free_host;
1120e93d1468SYang Yingliang 
1121c5413ad8SOleksij Rempel 	return 0;
11227c277dd2SChristophe JAILLET 
11237c277dd2SChristophe JAILLET free_host:
11247c277dd2SChristophe JAILLET 	mmc_free_host(mmc);
11257c277dd2SChristophe JAILLET 	return ret;
1126c5413ad8SOleksij Rempel }
1127c5413ad8SOleksij Rempel 
alcor_pci_sdmmc_drv_remove(struct platform_device * pdev)1128*9f13caa4SYangtao Li static void alcor_pci_sdmmc_drv_remove(struct platform_device *pdev)
1129c5413ad8SOleksij Rempel {
1130c5413ad8SOleksij Rempel 	struct alcor_sdmmc_host *host = dev_get_drvdata(&pdev->dev);
1131c081e7fdSKamlesh Gurudasani 	struct mmc_host *mmc = mmc_from_priv(host);
1132c5413ad8SOleksij Rempel 
1133c5413ad8SOleksij Rempel 	if (cancel_delayed_work_sync(&host->timeout_work))
1134c5413ad8SOleksij Rempel 		alcor_request_complete(host, 0);
1135c5413ad8SOleksij Rempel 
1136c5413ad8SOleksij Rempel 	alcor_hw_uninit(host);
1137c081e7fdSKamlesh Gurudasani 	mmc_remove_host(mmc);
1138c081e7fdSKamlesh Gurudasani 	mmc_free_host(mmc);
1139c5413ad8SOleksij Rempel }
1140c5413ad8SOleksij Rempel 
1141c5413ad8SOleksij Rempel #ifdef CONFIG_PM_SLEEP
alcor_pci_sdmmc_suspend(struct device * dev)1142c5413ad8SOleksij Rempel static int alcor_pci_sdmmc_suspend(struct device *dev)
1143c5413ad8SOleksij Rempel {
1144c5413ad8SOleksij Rempel 	struct alcor_sdmmc_host *host = dev_get_drvdata(dev);
1145c5413ad8SOleksij Rempel 
1146c5413ad8SOleksij Rempel 	if (cancel_delayed_work_sync(&host->timeout_work))
1147c5413ad8SOleksij Rempel 		alcor_request_complete(host, 0);
1148c5413ad8SOleksij Rempel 
1149c5413ad8SOleksij Rempel 	alcor_hw_uninit(host);
1150c5413ad8SOleksij Rempel 
1151c5413ad8SOleksij Rempel 	return 0;
1152c5413ad8SOleksij Rempel }
1153c5413ad8SOleksij Rempel 
alcor_pci_sdmmc_resume(struct device * dev)1154c5413ad8SOleksij Rempel static int alcor_pci_sdmmc_resume(struct device *dev)
1155c5413ad8SOleksij Rempel {
1156c5413ad8SOleksij Rempel 	struct alcor_sdmmc_host *host = dev_get_drvdata(dev);
1157c5413ad8SOleksij Rempel 
1158c5413ad8SOleksij Rempel 	alcor_hw_init(host);
1159c5413ad8SOleksij Rempel 
1160c5413ad8SOleksij Rempel 	return 0;
1161c5413ad8SOleksij Rempel }
1162c5413ad8SOleksij Rempel #endif /* CONFIG_PM_SLEEP */
1163c5413ad8SOleksij Rempel 
1164c5413ad8SOleksij Rempel static SIMPLE_DEV_PM_OPS(alcor_mmc_pm_ops, alcor_pci_sdmmc_suspend,
1165c5413ad8SOleksij Rempel 			 alcor_pci_sdmmc_resume);
1166c5413ad8SOleksij Rempel 
1167c5413ad8SOleksij Rempel static const struct platform_device_id alcor_pci_sdmmc_ids[] = {
1168c5413ad8SOleksij Rempel 	{
1169c5413ad8SOleksij Rempel 		.name = DRV_NAME_ALCOR_PCI_SDMMC,
1170c5413ad8SOleksij Rempel 	}, {
1171c5413ad8SOleksij Rempel 		/* sentinel */
1172c5413ad8SOleksij Rempel 	}
1173c5413ad8SOleksij Rempel };
1174c5413ad8SOleksij Rempel MODULE_DEVICE_TABLE(platform, alcor_pci_sdmmc_ids);
1175c5413ad8SOleksij Rempel 
1176c5413ad8SOleksij Rempel static struct platform_driver alcor_pci_sdmmc_driver = {
1177c5413ad8SOleksij Rempel 	.probe		= alcor_pci_sdmmc_drv_probe,
1178*9f13caa4SYangtao Li 	.remove_new	= alcor_pci_sdmmc_drv_remove,
1179c5413ad8SOleksij Rempel 	.id_table	= alcor_pci_sdmmc_ids,
1180c5413ad8SOleksij Rempel 	.driver		= {
1181c5413ad8SOleksij Rempel 		.name	= DRV_NAME_ALCOR_PCI_SDMMC,
1182d86472aeSDouglas Anderson 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
1183c5413ad8SOleksij Rempel 		.pm	= &alcor_mmc_pm_ops
1184c5413ad8SOleksij Rempel 	},
1185c5413ad8SOleksij Rempel };
1186c5413ad8SOleksij Rempel module_platform_driver(alcor_pci_sdmmc_driver);
1187c5413ad8SOleksij Rempel 
1188c5413ad8SOleksij Rempel MODULE_AUTHOR("Oleksij Rempel <linux@rempel-privat.de>");
1189c5413ad8SOleksij Rempel MODULE_DESCRIPTION("PCI driver for Alcor Micro AU6601 Secure Digital Host Controller Interface");
1190c5413ad8SOleksij Rempel MODULE_LICENSE("GPL");
1191