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