Commit | Line | Data |
---|---|---|
97fb5e8d | 1 | // SPDX-License-Identifier: GPL-2.0-only |
a4080225 | 2 | /* Copyright (c) 2015, The Linux Foundation. All rights reserved. |
a4080225 VG |
3 | */ |
4 | ||
5 | #include <linux/delay.h> | |
6 | #include <linux/highmem.h> | |
7 | #include <linux/io.h> | |
8 | #include <linux/module.h> | |
9 | #include <linux/dma-mapping.h> | |
10 | #include <linux/slab.h> | |
11 | #include <linux/scatterlist.h> | |
12 | #include <linux/platform_device.h> | |
13 | #include <linux/ktime.h> | |
14 | ||
15 | #include <linux/mmc/mmc.h> | |
16 | #include <linux/mmc/host.h> | |
17 | #include <linux/mmc/card.h> | |
18 | ||
19 | #include "cqhci.h" | |
20 | ||
21 | #define DCMD_SLOT 31 | |
22 | #define NUM_SLOTS 32 | |
23 | ||
24 | struct cqhci_slot { | |
25 | struct mmc_request *mrq; | |
26 | unsigned int flags; | |
27 | #define CQHCI_EXTERNAL_TIMEOUT BIT(0) | |
28 | #define CQHCI_COMPLETED BIT(1) | |
29 | #define CQHCI_HOST_CRC BIT(2) | |
30 | #define CQHCI_HOST_TIMEOUT BIT(3) | |
31 | #define CQHCI_HOST_OTHER BIT(4) | |
32 | }; | |
33 | ||
34 | static inline u8 *get_desc(struct cqhci_host *cq_host, u8 tag) | |
35 | { | |
36 | return cq_host->desc_base + (tag * cq_host->slot_sz); | |
37 | } | |
38 | ||
39 | static inline u8 *get_link_desc(struct cqhci_host *cq_host, u8 tag) | |
40 | { | |
41 | u8 *desc = get_desc(cq_host, tag); | |
42 | ||
43 | return desc + cq_host->task_desc_len; | |
44 | } | |
45 | ||
46 | static inline dma_addr_t get_trans_desc_dma(struct cqhci_host *cq_host, u8 tag) | |
47 | { | |
48 | return cq_host->trans_desc_dma_base + | |
49 | (cq_host->mmc->max_segs * tag * | |
50 | cq_host->trans_desc_len); | |
51 | } | |
52 | ||
53 | static inline u8 *get_trans_desc(struct cqhci_host *cq_host, u8 tag) | |
54 | { | |
55 | return cq_host->trans_desc_base + | |
56 | (cq_host->trans_desc_len * cq_host->mmc->max_segs * tag); | |
57 | } | |
58 | ||
59 | static void setup_trans_desc(struct cqhci_host *cq_host, u8 tag) | |
60 | { | |
61 | u8 *link_temp; | |
62 | dma_addr_t trans_temp; | |
63 | ||
64 | link_temp = get_link_desc(cq_host, tag); | |
65 | trans_temp = get_trans_desc_dma(cq_host, tag); | |
66 | ||
67 | memset(link_temp, 0, cq_host->link_desc_len); | |
68 | if (cq_host->link_desc_len > 8) | |
69 | *(link_temp + 8) = 0; | |
70 | ||
71 | if (tag == DCMD_SLOT && (cq_host->mmc->caps2 & MMC_CAP2_CQE_DCMD)) { | |
72 | *link_temp = CQHCI_VALID(0) | CQHCI_ACT(0) | CQHCI_END(1); | |
73 | return; | |
74 | } | |
75 | ||
76 | *link_temp = CQHCI_VALID(1) | CQHCI_ACT(0x6) | CQHCI_END(0); | |
77 | ||
78 | if (cq_host->dma64) { | |
79 | __le64 *data_addr = (__le64 __force *)(link_temp + 4); | |
80 | ||
81 | data_addr[0] = cpu_to_le64(trans_temp); | |
82 | } else { | |
83 | __le32 *data_addr = (__le32 __force *)(link_temp + 4); | |
84 | ||
85 | data_addr[0] = cpu_to_le32(trans_temp); | |
86 | } | |
87 | } | |
88 | ||
89 | static void cqhci_set_irqs(struct cqhci_host *cq_host, u32 set) | |
90 | { | |
91 | cqhci_writel(cq_host, set, CQHCI_ISTE); | |
92 | cqhci_writel(cq_host, set, CQHCI_ISGE); | |
93 | } | |
94 | ||
95 | #define DRV_NAME "cqhci" | |
96 | ||
97 | #define CQHCI_DUMP(f, x...) \ | |
98 | pr_err("%s: " DRV_NAME ": " f, mmc_hostname(mmc), ## x) | |
99 | ||
100 | static void cqhci_dumpregs(struct cqhci_host *cq_host) | |
101 | { | |
102 | struct mmc_host *mmc = cq_host->mmc; | |
103 | ||
104 | CQHCI_DUMP("============ CQHCI REGISTER DUMP ===========\n"); | |
105 | ||
106 | CQHCI_DUMP("Caps: 0x%08x | Version: 0x%08x\n", | |
107 | cqhci_readl(cq_host, CQHCI_CAP), | |
108 | cqhci_readl(cq_host, CQHCI_VER)); | |
109 | CQHCI_DUMP("Config: 0x%08x | Control: 0x%08x\n", | |
110 | cqhci_readl(cq_host, CQHCI_CFG), | |
111 | cqhci_readl(cq_host, CQHCI_CTL)); | |
112 | CQHCI_DUMP("Int stat: 0x%08x | Int enab: 0x%08x\n", | |
113 | cqhci_readl(cq_host, CQHCI_IS), | |
114 | cqhci_readl(cq_host, CQHCI_ISTE)); | |
115 | CQHCI_DUMP("Int sig: 0x%08x | Int Coal: 0x%08x\n", | |
116 | cqhci_readl(cq_host, CQHCI_ISGE), | |
117 | cqhci_readl(cq_host, CQHCI_IC)); | |
118 | CQHCI_DUMP("TDL base: 0x%08x | TDL up32: 0x%08x\n", | |
119 | cqhci_readl(cq_host, CQHCI_TDLBA), | |
120 | cqhci_readl(cq_host, CQHCI_TDLBAU)); | |
121 | CQHCI_DUMP("Doorbell: 0x%08x | TCN: 0x%08x\n", | |
122 | cqhci_readl(cq_host, CQHCI_TDBR), | |
123 | cqhci_readl(cq_host, CQHCI_TCN)); | |
124 | CQHCI_DUMP("Dev queue: 0x%08x | Dev Pend: 0x%08x\n", | |
125 | cqhci_readl(cq_host, CQHCI_DQS), | |
126 | cqhci_readl(cq_host, CQHCI_DPT)); | |
127 | CQHCI_DUMP("Task clr: 0x%08x | SSC1: 0x%08x\n", | |
128 | cqhci_readl(cq_host, CQHCI_TCLR), | |
129 | cqhci_readl(cq_host, CQHCI_SSC1)); | |
130 | CQHCI_DUMP("SSC2: 0x%08x | DCMD rsp: 0x%08x\n", | |
131 | cqhci_readl(cq_host, CQHCI_SSC2), | |
132 | cqhci_readl(cq_host, CQHCI_CRDCT)); | |
133 | CQHCI_DUMP("RED mask: 0x%08x | TERRI: 0x%08x\n", | |
134 | cqhci_readl(cq_host, CQHCI_RMEM), | |
135 | cqhci_readl(cq_host, CQHCI_TERRI)); | |
136 | CQHCI_DUMP("Resp idx: 0x%08x | Resp arg: 0x%08x\n", | |
137 | cqhci_readl(cq_host, CQHCI_CRI), | |
138 | cqhci_readl(cq_host, CQHCI_CRA)); | |
139 | ||
140 | if (cq_host->ops->dumpregs) | |
141 | cq_host->ops->dumpregs(mmc); | |
142 | else | |
143 | CQHCI_DUMP(": ===========================================\n"); | |
144 | } | |
145 | ||
146 | /** | |
147 | * The allocated descriptor table for task, link & transfer descritors | |
148 | * looks like: | |
149 | * |----------| | |
150 | * |task desc | |->|----------| | |
151 | * |----------| | |trans desc| | |
152 | * |link desc-|->| |----------| | |
153 | * |----------| . | |
154 | * . . | |
155 | * no. of slots max-segs | |
156 | * . |----------| | |
157 | * |----------| | |
158 | * The idea here is to create the [task+trans] table and mark & point the | |
159 | * link desc to the transfer desc table on a per slot basis. | |
160 | */ | |
161 | static int cqhci_host_alloc_tdl(struct cqhci_host *cq_host) | |
162 | { | |
163 | int i = 0; | |
164 | ||
165 | /* task descriptor can be 64/128 bit irrespective of arch */ | |
166 | if (cq_host->caps & CQHCI_TASK_DESC_SZ_128) { | |
167 | cqhci_writel(cq_host, cqhci_readl(cq_host, CQHCI_CFG) | | |
168 | CQHCI_TASK_DESC_SZ, CQHCI_CFG); | |
169 | cq_host->task_desc_len = 16; | |
170 | } else { | |
171 | cq_host->task_desc_len = 8; | |
172 | } | |
173 | ||
174 | /* | |
175 | * 96 bits length of transfer desc instead of 128 bits which means | |
176 | * ADMA would expect next valid descriptor at the 96th bit | |
177 | * or 128th bit | |
178 | */ | |
179 | if (cq_host->dma64) { | |
180 | if (cq_host->quirks & CQHCI_QUIRK_SHORT_TXFR_DESC_SZ) | |
181 | cq_host->trans_desc_len = 12; | |
182 | else | |
183 | cq_host->trans_desc_len = 16; | |
184 | cq_host->link_desc_len = 16; | |
185 | } else { | |
186 | cq_host->trans_desc_len = 8; | |
187 | cq_host->link_desc_len = 8; | |
188 | } | |
189 | ||
190 | /* total size of a slot: 1 task & 1 transfer (link) */ | |
191 | cq_host->slot_sz = cq_host->task_desc_len + cq_host->link_desc_len; | |
192 | ||
193 | cq_host->desc_size = cq_host->slot_sz * cq_host->num_slots; | |
194 | ||
195 | cq_host->data_size = cq_host->trans_desc_len * cq_host->mmc->max_segs * | |
27ec9dc1 | 196 | cq_host->mmc->cqe_qdepth; |
a4080225 VG |
197 | |
198 | pr_debug("%s: cqhci: desc_size: %zu data_sz: %zu slot-sz: %d\n", | |
199 | mmc_hostname(cq_host->mmc), cq_host->desc_size, cq_host->data_size, | |
200 | cq_host->slot_sz); | |
201 | ||
202 | /* | |
203 | * allocate a dma-mapped chunk of memory for the descriptors | |
204 | * allocate a dma-mapped chunk of memory for link descriptors | |
205 | * setup each link-desc memory offset per slot-number to | |
206 | * the descriptor table. | |
207 | */ | |
208 | cq_host->desc_base = dmam_alloc_coherent(mmc_dev(cq_host->mmc), | |
209 | cq_host->desc_size, | |
210 | &cq_host->desc_dma_base, | |
211 | GFP_KERNEL); | |
d07e9fad AL |
212 | if (!cq_host->desc_base) |
213 | return -ENOMEM; | |
214 | ||
a4080225 VG |
215 | cq_host->trans_desc_base = dmam_alloc_coherent(mmc_dev(cq_host->mmc), |
216 | cq_host->data_size, | |
217 | &cq_host->trans_desc_dma_base, | |
218 | GFP_KERNEL); | |
d07e9fad AL |
219 | if (!cq_host->trans_desc_base) { |
220 | dmam_free_coherent(mmc_dev(cq_host->mmc), cq_host->desc_size, | |
221 | cq_host->desc_base, | |
222 | cq_host->desc_dma_base); | |
223 | cq_host->desc_base = NULL; | |
224 | cq_host->desc_dma_base = 0; | |
a4080225 | 225 | return -ENOMEM; |
d07e9fad | 226 | } |
a4080225 VG |
227 | |
228 | pr_debug("%s: cqhci: desc-base: 0x%p trans-base: 0x%p\n desc_dma 0x%llx trans_dma: 0x%llx\n", | |
229 | mmc_hostname(cq_host->mmc), cq_host->desc_base, cq_host->trans_desc_base, | |
230 | (unsigned long long)cq_host->desc_dma_base, | |
231 | (unsigned long long)cq_host->trans_desc_dma_base); | |
232 | ||
233 | for (; i < (cq_host->num_slots); i++) | |
234 | setup_trans_desc(cq_host, i); | |
235 | ||
236 | return 0; | |
237 | } | |
238 | ||
239 | static void __cqhci_enable(struct cqhci_host *cq_host) | |
240 | { | |
241 | struct mmc_host *mmc = cq_host->mmc; | |
242 | u32 cqcfg; | |
243 | ||
244 | cqcfg = cqhci_readl(cq_host, CQHCI_CFG); | |
245 | ||
246 | /* Configuration must not be changed while enabled */ | |
247 | if (cqcfg & CQHCI_ENABLE) { | |
248 | cqcfg &= ~CQHCI_ENABLE; | |
249 | cqhci_writel(cq_host, cqcfg, CQHCI_CFG); | |
250 | } | |
251 | ||
252 | cqcfg &= ~(CQHCI_DCMD | CQHCI_TASK_DESC_SZ); | |
253 | ||
254 | if (mmc->caps2 & MMC_CAP2_CQE_DCMD) | |
255 | cqcfg |= CQHCI_DCMD; | |
256 | ||
257 | if (cq_host->caps & CQHCI_TASK_DESC_SZ_128) | |
258 | cqcfg |= CQHCI_TASK_DESC_SZ; | |
259 | ||
260 | cqhci_writel(cq_host, cqcfg, CQHCI_CFG); | |
261 | ||
262 | cqhci_writel(cq_host, lower_32_bits(cq_host->desc_dma_base), | |
263 | CQHCI_TDLBA); | |
264 | cqhci_writel(cq_host, upper_32_bits(cq_host->desc_dma_base), | |
265 | CQHCI_TDLBAU); | |
266 | ||
267 | cqhci_writel(cq_host, cq_host->rca, CQHCI_SSC2); | |
268 | ||
269 | cqhci_set_irqs(cq_host, 0); | |
270 | ||
271 | cqcfg |= CQHCI_ENABLE; | |
272 | ||
273 | cqhci_writel(cq_host, cqcfg, CQHCI_CFG); | |
274 | ||
275 | mmc->cqe_on = true; | |
276 | ||
277 | if (cq_host->ops->enable) | |
278 | cq_host->ops->enable(mmc); | |
279 | ||
280 | /* Ensure all writes are done before interrupts are enabled */ | |
281 | wmb(); | |
282 | ||
283 | cqhci_set_irqs(cq_host, CQHCI_IS_MASK); | |
284 | ||
285 | cq_host->activated = true; | |
286 | } | |
287 | ||
288 | static void __cqhci_disable(struct cqhci_host *cq_host) | |
289 | { | |
290 | u32 cqcfg; | |
291 | ||
292 | cqcfg = cqhci_readl(cq_host, CQHCI_CFG); | |
293 | cqcfg &= ~CQHCI_ENABLE; | |
294 | cqhci_writel(cq_host, cqcfg, CQHCI_CFG); | |
295 | ||
296 | cq_host->mmc->cqe_on = false; | |
297 | ||
298 | cq_host->activated = false; | |
299 | } | |
300 | ||
301 | int cqhci_suspend(struct mmc_host *mmc) | |
302 | { | |
303 | struct cqhci_host *cq_host = mmc->cqe_private; | |
304 | ||
305 | if (cq_host->enabled) | |
306 | __cqhci_disable(cq_host); | |
307 | ||
308 | return 0; | |
309 | } | |
310 | EXPORT_SYMBOL(cqhci_suspend); | |
311 | ||
312 | int cqhci_resume(struct mmc_host *mmc) | |
313 | { | |
314 | /* Re-enable is done upon first request */ | |
315 | return 0; | |
316 | } | |
317 | EXPORT_SYMBOL(cqhci_resume); | |
318 | ||
319 | static int cqhci_enable(struct mmc_host *mmc, struct mmc_card *card) | |
320 | { | |
321 | struct cqhci_host *cq_host = mmc->cqe_private; | |
322 | int err; | |
323 | ||
324 | if (cq_host->enabled) | |
325 | return 0; | |
326 | ||
327 | cq_host->rca = card->rca; | |
328 | ||
329 | err = cqhci_host_alloc_tdl(cq_host); | |
330 | if (err) | |
331 | return err; | |
332 | ||
333 | __cqhci_enable(cq_host); | |
334 | ||
335 | cq_host->enabled = true; | |
336 | ||
337 | #ifdef DEBUG | |
338 | cqhci_dumpregs(cq_host); | |
339 | #endif | |
340 | return 0; | |
341 | } | |
342 | ||
343 | /* CQHCI is idle and should halt immediately, so set a small timeout */ | |
344 | #define CQHCI_OFF_TIMEOUT 100 | |
345 | ||
346 | static void cqhci_off(struct mmc_host *mmc) | |
347 | { | |
348 | struct cqhci_host *cq_host = mmc->cqe_private; | |
349 | ktime_t timeout; | |
350 | bool timed_out; | |
351 | u32 reg; | |
352 | ||
353 | if (!cq_host->enabled || !mmc->cqe_on || cq_host->recovery_halt) | |
354 | return; | |
355 | ||
356 | if (cq_host->ops->disable) | |
357 | cq_host->ops->disable(mmc, false); | |
358 | ||
359 | cqhci_writel(cq_host, CQHCI_HALT, CQHCI_CTL); | |
360 | ||
361 | timeout = ktime_add_us(ktime_get(), CQHCI_OFF_TIMEOUT); | |
362 | while (1) { | |
363 | timed_out = ktime_compare(ktime_get(), timeout) > 0; | |
364 | reg = cqhci_readl(cq_host, CQHCI_CTL); | |
365 | if ((reg & CQHCI_HALT) || timed_out) | |
366 | break; | |
367 | } | |
368 | ||
369 | if (timed_out) | |
370 | pr_err("%s: cqhci: CQE stuck on\n", mmc_hostname(mmc)); | |
371 | else | |
372 | pr_debug("%s: cqhci: CQE off\n", mmc_hostname(mmc)); | |
373 | ||
374 | mmc->cqe_on = false; | |
375 | } | |
376 | ||
377 | static void cqhci_disable(struct mmc_host *mmc) | |
378 | { | |
379 | struct cqhci_host *cq_host = mmc->cqe_private; | |
380 | ||
381 | if (!cq_host->enabled) | |
382 | return; | |
383 | ||
384 | cqhci_off(mmc); | |
385 | ||
386 | __cqhci_disable(cq_host); | |
387 | ||
388 | dmam_free_coherent(mmc_dev(mmc), cq_host->data_size, | |
389 | cq_host->trans_desc_base, | |
390 | cq_host->trans_desc_dma_base); | |
391 | ||
392 | dmam_free_coherent(mmc_dev(mmc), cq_host->desc_size, | |
393 | cq_host->desc_base, | |
394 | cq_host->desc_dma_base); | |
395 | ||
396 | cq_host->trans_desc_base = NULL; | |
397 | cq_host->desc_base = NULL; | |
398 | ||
399 | cq_host->enabled = false; | |
400 | } | |
401 | ||
402 | static void cqhci_prep_task_desc(struct mmc_request *mrq, | |
403 | u64 *data, bool intr) | |
404 | { | |
405 | u32 req_flags = mrq->data->flags; | |
406 | ||
407 | *data = CQHCI_VALID(1) | | |
408 | CQHCI_END(1) | | |
409 | CQHCI_INT(intr) | | |
410 | CQHCI_ACT(0x5) | | |
411 | CQHCI_FORCED_PROG(!!(req_flags & MMC_DATA_FORCED_PRG)) | | |
412 | CQHCI_DATA_TAG(!!(req_flags & MMC_DATA_DAT_TAG)) | | |
413 | CQHCI_DATA_DIR(!!(req_flags & MMC_DATA_READ)) | | |
414 | CQHCI_PRIORITY(!!(req_flags & MMC_DATA_PRIO)) | | |
415 | CQHCI_QBAR(!!(req_flags & MMC_DATA_QBR)) | | |
416 | CQHCI_REL_WRITE(!!(req_flags & MMC_DATA_REL_WR)) | | |
417 | CQHCI_BLK_COUNT(mrq->data->blocks) | | |
418 | CQHCI_BLK_ADDR((u64)mrq->data->blk_addr); | |
419 | ||
420 | pr_debug("%s: cqhci: tag %d task descriptor 0x016%llx\n", | |
421 | mmc_hostname(mrq->host), mrq->tag, (unsigned long long)*data); | |
422 | } | |
423 | ||
424 | static int cqhci_dma_map(struct mmc_host *host, struct mmc_request *mrq) | |
425 | { | |
426 | int sg_count; | |
427 | struct mmc_data *data = mrq->data; | |
428 | ||
429 | if (!data) | |
430 | return -EINVAL; | |
431 | ||
432 | sg_count = dma_map_sg(mmc_dev(host), data->sg, | |
433 | data->sg_len, | |
434 | (data->flags & MMC_DATA_WRITE) ? | |
435 | DMA_TO_DEVICE : DMA_FROM_DEVICE); | |
436 | if (!sg_count) { | |
437 | pr_err("%s: sg-len: %d\n", __func__, data->sg_len); | |
438 | return -ENOMEM; | |
439 | } | |
440 | ||
441 | return sg_count; | |
442 | } | |
443 | ||
444 | static void cqhci_set_tran_desc(u8 *desc, dma_addr_t addr, int len, bool end, | |
445 | bool dma64) | |
446 | { | |
447 | __le32 *attr = (__le32 __force *)desc; | |
448 | ||
449 | *attr = (CQHCI_VALID(1) | | |
450 | CQHCI_END(end ? 1 : 0) | | |
451 | CQHCI_INT(0) | | |
452 | CQHCI_ACT(0x4) | | |
453 | CQHCI_DAT_LENGTH(len)); | |
454 | ||
455 | if (dma64) { | |
456 | __le64 *dataddr = (__le64 __force *)(desc + 4); | |
457 | ||
458 | dataddr[0] = cpu_to_le64(addr); | |
459 | } else { | |
460 | __le32 *dataddr = (__le32 __force *)(desc + 4); | |
461 | ||
462 | dataddr[0] = cpu_to_le32(addr); | |
463 | } | |
464 | } | |
465 | ||
466 | static int cqhci_prep_tran_desc(struct mmc_request *mrq, | |
467 | struct cqhci_host *cq_host, int tag) | |
468 | { | |
469 | struct mmc_data *data = mrq->data; | |
470 | int i, sg_count, len; | |
471 | bool end = false; | |
472 | bool dma64 = cq_host->dma64; | |
473 | dma_addr_t addr; | |
474 | u8 *desc; | |
475 | struct scatterlist *sg; | |
476 | ||
477 | sg_count = cqhci_dma_map(mrq->host, mrq); | |
478 | if (sg_count < 0) { | |
479 | pr_err("%s: %s: unable to map sg lists, %d\n", | |
480 | mmc_hostname(mrq->host), __func__, sg_count); | |
481 | return sg_count; | |
482 | } | |
483 | ||
484 | desc = get_trans_desc(cq_host, tag); | |
485 | ||
486 | for_each_sg(data->sg, sg, sg_count, i) { | |
487 | addr = sg_dma_address(sg); | |
488 | len = sg_dma_len(sg); | |
489 | ||
490 | if ((i+1) == sg_count) | |
491 | end = true; | |
492 | cqhci_set_tran_desc(desc, addr, len, end, dma64); | |
493 | desc += cq_host->trans_desc_len; | |
494 | } | |
495 | ||
496 | return 0; | |
497 | } | |
498 | ||
499 | static void cqhci_prep_dcmd_desc(struct mmc_host *mmc, | |
500 | struct mmc_request *mrq) | |
501 | { | |
502 | u64 *task_desc = NULL; | |
503 | u64 data = 0; | |
504 | u8 resp_type; | |
505 | u8 *desc; | |
506 | __le64 *dataddr; | |
507 | struct cqhci_host *cq_host = mmc->cqe_private; | |
508 | u8 timing; | |
509 | ||
510 | if (!(mrq->cmd->flags & MMC_RSP_PRESENT)) { | |
511 | resp_type = 0x0; | |
512 | timing = 0x1; | |
513 | } else { | |
514 | if (mrq->cmd->flags & MMC_RSP_R1B) { | |
515 | resp_type = 0x3; | |
516 | timing = 0x0; | |
517 | } else { | |
518 | resp_type = 0x2; | |
519 | timing = 0x1; | |
520 | } | |
521 | } | |
522 | ||
523 | task_desc = (__le64 __force *)get_desc(cq_host, cq_host->dcmd_slot); | |
524 | memset(task_desc, 0, cq_host->task_desc_len); | |
525 | data |= (CQHCI_VALID(1) | | |
526 | CQHCI_END(1) | | |
527 | CQHCI_INT(1) | | |
528 | CQHCI_QBAR(1) | | |
529 | CQHCI_ACT(0x5) | | |
530 | CQHCI_CMD_INDEX(mrq->cmd->opcode) | | |
531 | CQHCI_CMD_TIMING(timing) | CQHCI_RESP_TYPE(resp_type)); | |
c46d089a SK |
532 | if (cq_host->ops->update_dcmd_desc) |
533 | cq_host->ops->update_dcmd_desc(mmc, mrq, &data); | |
a4080225 VG |
534 | *task_desc |= data; |
535 | desc = (u8 *)task_desc; | |
536 | pr_debug("%s: cqhci: dcmd: cmd: %d timing: %d resp: %d\n", | |
537 | mmc_hostname(mmc), mrq->cmd->opcode, timing, resp_type); | |
538 | dataddr = (__le64 __force *)(desc + 4); | |
539 | dataddr[0] = cpu_to_le64((u64)mrq->cmd->arg); | |
540 | ||
541 | } | |
542 | ||
543 | static void cqhci_post_req(struct mmc_host *host, struct mmc_request *mrq) | |
544 | { | |
545 | struct mmc_data *data = mrq->data; | |
546 | ||
547 | if (data) { | |
548 | dma_unmap_sg(mmc_dev(host), data->sg, data->sg_len, | |
549 | (data->flags & MMC_DATA_READ) ? | |
550 | DMA_FROM_DEVICE : DMA_TO_DEVICE); | |
551 | } | |
552 | } | |
553 | ||
554 | static inline int cqhci_tag(struct mmc_request *mrq) | |
555 | { | |
556 | return mrq->cmd ? DCMD_SLOT : mrq->tag; | |
557 | } | |
558 | ||
559 | static int cqhci_request(struct mmc_host *mmc, struct mmc_request *mrq) | |
560 | { | |
561 | int err = 0; | |
562 | u64 data = 0; | |
563 | u64 *task_desc = NULL; | |
564 | int tag = cqhci_tag(mrq); | |
565 | struct cqhci_host *cq_host = mmc->cqe_private; | |
566 | unsigned long flags; | |
567 | ||
568 | if (!cq_host->enabled) { | |
569 | pr_err("%s: cqhci: not enabled\n", mmc_hostname(mmc)); | |
570 | return -EINVAL; | |
571 | } | |
572 | ||
573 | /* First request after resume has to re-enable */ | |
574 | if (!cq_host->activated) | |
575 | __cqhci_enable(cq_host); | |
576 | ||
577 | if (!mmc->cqe_on) { | |
578 | cqhci_writel(cq_host, 0, CQHCI_CTL); | |
579 | mmc->cqe_on = true; | |
580 | pr_debug("%s: cqhci: CQE on\n", mmc_hostname(mmc)); | |
581 | if (cqhci_readl(cq_host, CQHCI_CTL) && CQHCI_HALT) { | |
582 | pr_err("%s: cqhci: CQE failed to exit halt state\n", | |
583 | mmc_hostname(mmc)); | |
584 | } | |
585 | if (cq_host->ops->enable) | |
586 | cq_host->ops->enable(mmc); | |
587 | } | |
588 | ||
589 | if (mrq->data) { | |
590 | task_desc = (__le64 __force *)get_desc(cq_host, tag); | |
591 | cqhci_prep_task_desc(mrq, &data, 1); | |
592 | *task_desc = cpu_to_le64(data); | |
593 | err = cqhci_prep_tran_desc(mrq, cq_host, tag); | |
594 | if (err) { | |
595 | pr_err("%s: cqhci: failed to setup tx desc: %d\n", | |
596 | mmc_hostname(mmc), err); | |
597 | return err; | |
598 | } | |
599 | } else { | |
600 | cqhci_prep_dcmd_desc(mmc, mrq); | |
601 | } | |
602 | ||
603 | spin_lock_irqsave(&cq_host->lock, flags); | |
604 | ||
605 | if (cq_host->recovery_halt) { | |
606 | err = -EBUSY; | |
607 | goto out_unlock; | |
608 | } | |
609 | ||
610 | cq_host->slot[tag].mrq = mrq; | |
611 | cq_host->slot[tag].flags = 0; | |
612 | ||
613 | cq_host->qcnt += 1; | |
614 | ||
615 | cqhci_writel(cq_host, 1 << tag, CQHCI_TDBR); | |
616 | if (!(cqhci_readl(cq_host, CQHCI_TDBR) & (1 << tag))) | |
617 | pr_debug("%s: cqhci: doorbell not set for tag %d\n", | |
618 | mmc_hostname(mmc), tag); | |
619 | out_unlock: | |
620 | spin_unlock_irqrestore(&cq_host->lock, flags); | |
621 | ||
622 | if (err) | |
623 | cqhci_post_req(mmc, mrq); | |
624 | ||
625 | return err; | |
626 | } | |
627 | ||
628 | static void cqhci_recovery_needed(struct mmc_host *mmc, struct mmc_request *mrq, | |
629 | bool notify) | |
630 | { | |
631 | struct cqhci_host *cq_host = mmc->cqe_private; | |
632 | ||
633 | if (!cq_host->recovery_halt) { | |
634 | cq_host->recovery_halt = true; | |
635 | pr_debug("%s: cqhci: recovery needed\n", mmc_hostname(mmc)); | |
636 | wake_up(&cq_host->wait_queue); | |
637 | if (notify && mrq->recovery_notifier) | |
638 | mrq->recovery_notifier(mrq); | |
639 | } | |
640 | } | |
641 | ||
642 | static unsigned int cqhci_error_flags(int error1, int error2) | |
643 | { | |
644 | int error = error1 ? error1 : error2; | |
645 | ||
646 | switch (error) { | |
647 | case -EILSEQ: | |
648 | return CQHCI_HOST_CRC; | |
649 | case -ETIMEDOUT: | |
650 | return CQHCI_HOST_TIMEOUT; | |
651 | default: | |
652 | return CQHCI_HOST_OTHER; | |
653 | } | |
654 | } | |
655 | ||
656 | static void cqhci_error_irq(struct mmc_host *mmc, u32 status, int cmd_error, | |
657 | int data_error) | |
658 | { | |
659 | struct cqhci_host *cq_host = mmc->cqe_private; | |
660 | struct cqhci_slot *slot; | |
661 | u32 terri; | |
662 | int tag; | |
663 | ||
664 | spin_lock(&cq_host->lock); | |
665 | ||
666 | terri = cqhci_readl(cq_host, CQHCI_TERRI); | |
667 | ||
668 | pr_debug("%s: cqhci: error IRQ status: 0x%08x cmd error %d data error %d TERRI: 0x%08x\n", | |
669 | mmc_hostname(mmc), status, cmd_error, data_error, terri); | |
670 | ||
671 | /* Forget about errors when recovery has already been triggered */ | |
672 | if (cq_host->recovery_halt) | |
673 | goto out_unlock; | |
674 | ||
675 | if (!cq_host->qcnt) { | |
676 | WARN_ONCE(1, "%s: cqhci: error when idle. IRQ status: 0x%08x cmd error %d data error %d TERRI: 0x%08x\n", | |
677 | mmc_hostname(mmc), status, cmd_error, data_error, | |
678 | terri); | |
679 | goto out_unlock; | |
680 | } | |
681 | ||
682 | if (CQHCI_TERRI_C_VALID(terri)) { | |
683 | tag = CQHCI_TERRI_C_TASK(terri); | |
684 | slot = &cq_host->slot[tag]; | |
685 | if (slot->mrq) { | |
686 | slot->flags = cqhci_error_flags(cmd_error, data_error); | |
687 | cqhci_recovery_needed(mmc, slot->mrq, true); | |
688 | } | |
689 | } | |
690 | ||
691 | if (CQHCI_TERRI_D_VALID(terri)) { | |
692 | tag = CQHCI_TERRI_D_TASK(terri); | |
693 | slot = &cq_host->slot[tag]; | |
694 | if (slot->mrq) { | |
695 | slot->flags = cqhci_error_flags(data_error, cmd_error); | |
696 | cqhci_recovery_needed(mmc, slot->mrq, true); | |
697 | } | |
698 | } | |
699 | ||
700 | if (!cq_host->recovery_halt) { | |
701 | /* | |
702 | * The only way to guarantee forward progress is to mark at | |
703 | * least one task in error, so if none is indicated, pick one. | |
704 | */ | |
705 | for (tag = 0; tag < NUM_SLOTS; tag++) { | |
706 | slot = &cq_host->slot[tag]; | |
707 | if (!slot->mrq) | |
708 | continue; | |
709 | slot->flags = cqhci_error_flags(data_error, cmd_error); | |
710 | cqhci_recovery_needed(mmc, slot->mrq, true); | |
711 | break; | |
712 | } | |
713 | } | |
714 | ||
715 | out_unlock: | |
716 | spin_unlock(&cq_host->lock); | |
717 | } | |
718 | ||
719 | static void cqhci_finish_mrq(struct mmc_host *mmc, unsigned int tag) | |
720 | { | |
721 | struct cqhci_host *cq_host = mmc->cqe_private; | |
722 | struct cqhci_slot *slot = &cq_host->slot[tag]; | |
723 | struct mmc_request *mrq = slot->mrq; | |
724 | struct mmc_data *data; | |
725 | ||
726 | if (!mrq) { | |
727 | WARN_ONCE(1, "%s: cqhci: spurious TCN for tag %d\n", | |
728 | mmc_hostname(mmc), tag); | |
729 | return; | |
730 | } | |
731 | ||
732 | /* No completions allowed during recovery */ | |
733 | if (cq_host->recovery_halt) { | |
734 | slot->flags |= CQHCI_COMPLETED; | |
735 | return; | |
736 | } | |
737 | ||
738 | slot->mrq = NULL; | |
739 | ||
740 | cq_host->qcnt -= 1; | |
741 | ||
742 | data = mrq->data; | |
743 | if (data) { | |
744 | if (data->error) | |
745 | data->bytes_xfered = 0; | |
746 | else | |
747 | data->bytes_xfered = data->blksz * data->blocks; | |
748 | } | |
749 | ||
750 | mmc_cqe_request_done(mmc, mrq); | |
751 | } | |
752 | ||
753 | irqreturn_t cqhci_irq(struct mmc_host *mmc, u32 intmask, int cmd_error, | |
754 | int data_error) | |
755 | { | |
756 | u32 status; | |
757 | unsigned long tag = 0, comp_status; | |
758 | struct cqhci_host *cq_host = mmc->cqe_private; | |
759 | ||
760 | status = cqhci_readl(cq_host, CQHCI_IS); | |
761 | cqhci_writel(cq_host, status, CQHCI_IS); | |
762 | ||
763 | pr_debug("%s: cqhci: IRQ status: 0x%08x\n", mmc_hostname(mmc), status); | |
764 | ||
765 | if ((status & CQHCI_IS_RED) || cmd_error || data_error) | |
766 | cqhci_error_irq(mmc, status, cmd_error, data_error); | |
767 | ||
768 | if (status & CQHCI_IS_TCC) { | |
769 | /* read TCN and complete the request */ | |
770 | comp_status = cqhci_readl(cq_host, CQHCI_TCN); | |
771 | cqhci_writel(cq_host, comp_status, CQHCI_TCN); | |
772 | pr_debug("%s: cqhci: TCN: 0x%08lx\n", | |
773 | mmc_hostname(mmc), comp_status); | |
774 | ||
775 | spin_lock(&cq_host->lock); | |
776 | ||
777 | for_each_set_bit(tag, &comp_status, cq_host->num_slots) { | |
778 | /* complete the corresponding mrq */ | |
779 | pr_debug("%s: cqhci: completing tag %lu\n", | |
780 | mmc_hostname(mmc), tag); | |
781 | cqhci_finish_mrq(mmc, tag); | |
782 | } | |
783 | ||
784 | if (cq_host->waiting_for_idle && !cq_host->qcnt) { | |
785 | cq_host->waiting_for_idle = false; | |
786 | wake_up(&cq_host->wait_queue); | |
787 | } | |
788 | ||
789 | spin_unlock(&cq_host->lock); | |
790 | } | |
791 | ||
792 | if (status & CQHCI_IS_TCL) | |
793 | wake_up(&cq_host->wait_queue); | |
794 | ||
795 | if (status & CQHCI_IS_HAC) | |
796 | wake_up(&cq_host->wait_queue); | |
797 | ||
798 | return IRQ_HANDLED; | |
799 | } | |
800 | EXPORT_SYMBOL(cqhci_irq); | |
801 | ||
802 | static bool cqhci_is_idle(struct cqhci_host *cq_host, int *ret) | |
803 | { | |
804 | unsigned long flags; | |
805 | bool is_idle; | |
806 | ||
807 | spin_lock_irqsave(&cq_host->lock, flags); | |
808 | is_idle = !cq_host->qcnt || cq_host->recovery_halt; | |
809 | *ret = cq_host->recovery_halt ? -EBUSY : 0; | |
810 | cq_host->waiting_for_idle = !is_idle; | |
811 | spin_unlock_irqrestore(&cq_host->lock, flags); | |
812 | ||
813 | return is_idle; | |
814 | } | |
815 | ||
816 | static int cqhci_wait_for_idle(struct mmc_host *mmc) | |
817 | { | |
818 | struct cqhci_host *cq_host = mmc->cqe_private; | |
819 | int ret; | |
820 | ||
821 | wait_event(cq_host->wait_queue, cqhci_is_idle(cq_host, &ret)); | |
822 | ||
823 | return ret; | |
824 | } | |
825 | ||
826 | static bool cqhci_timeout(struct mmc_host *mmc, struct mmc_request *mrq, | |
827 | bool *recovery_needed) | |
828 | { | |
829 | struct cqhci_host *cq_host = mmc->cqe_private; | |
830 | int tag = cqhci_tag(mrq); | |
831 | struct cqhci_slot *slot = &cq_host->slot[tag]; | |
832 | unsigned long flags; | |
833 | bool timed_out; | |
834 | ||
835 | spin_lock_irqsave(&cq_host->lock, flags); | |
836 | timed_out = slot->mrq == mrq; | |
837 | if (timed_out) { | |
838 | slot->flags |= CQHCI_EXTERNAL_TIMEOUT; | |
839 | cqhci_recovery_needed(mmc, mrq, false); | |
840 | *recovery_needed = cq_host->recovery_halt; | |
841 | } | |
842 | spin_unlock_irqrestore(&cq_host->lock, flags); | |
843 | ||
844 | if (timed_out) { | |
845 | pr_err("%s: cqhci: timeout for tag %d\n", | |
846 | mmc_hostname(mmc), tag); | |
847 | cqhci_dumpregs(cq_host); | |
848 | } | |
849 | ||
850 | return timed_out; | |
851 | } | |
852 | ||
853 | static bool cqhci_tasks_cleared(struct cqhci_host *cq_host) | |
854 | { | |
855 | return !(cqhci_readl(cq_host, CQHCI_CTL) & CQHCI_CLEAR_ALL_TASKS); | |
856 | } | |
857 | ||
858 | static bool cqhci_clear_all_tasks(struct mmc_host *mmc, unsigned int timeout) | |
859 | { | |
860 | struct cqhci_host *cq_host = mmc->cqe_private; | |
861 | bool ret; | |
862 | u32 ctl; | |
863 | ||
864 | cqhci_set_irqs(cq_host, CQHCI_IS_TCL); | |
865 | ||
866 | ctl = cqhci_readl(cq_host, CQHCI_CTL); | |
867 | ctl |= CQHCI_CLEAR_ALL_TASKS; | |
868 | cqhci_writel(cq_host, ctl, CQHCI_CTL); | |
869 | ||
870 | wait_event_timeout(cq_host->wait_queue, cqhci_tasks_cleared(cq_host), | |
871 | msecs_to_jiffies(timeout) + 1); | |
872 | ||
873 | cqhci_set_irqs(cq_host, 0); | |
874 | ||
875 | ret = cqhci_tasks_cleared(cq_host); | |
876 | ||
877 | if (!ret) | |
878 | pr_debug("%s: cqhci: Failed to clear tasks\n", | |
879 | mmc_hostname(mmc)); | |
880 | ||
881 | return ret; | |
882 | } | |
883 | ||
884 | static bool cqhci_halted(struct cqhci_host *cq_host) | |
885 | { | |
886 | return cqhci_readl(cq_host, CQHCI_CTL) & CQHCI_HALT; | |
887 | } | |
888 | ||
889 | static bool cqhci_halt(struct mmc_host *mmc, unsigned int timeout) | |
890 | { | |
891 | struct cqhci_host *cq_host = mmc->cqe_private; | |
892 | bool ret; | |
893 | u32 ctl; | |
894 | ||
895 | if (cqhci_halted(cq_host)) | |
896 | return true; | |
897 | ||
898 | cqhci_set_irqs(cq_host, CQHCI_IS_HAC); | |
899 | ||
900 | ctl = cqhci_readl(cq_host, CQHCI_CTL); | |
901 | ctl |= CQHCI_HALT; | |
902 | cqhci_writel(cq_host, ctl, CQHCI_CTL); | |
903 | ||
904 | wait_event_timeout(cq_host->wait_queue, cqhci_halted(cq_host), | |
905 | msecs_to_jiffies(timeout) + 1); | |
906 | ||
907 | cqhci_set_irqs(cq_host, 0); | |
908 | ||
909 | ret = cqhci_halted(cq_host); | |
910 | ||
911 | if (!ret) | |
912 | pr_debug("%s: cqhci: Failed to halt\n", mmc_hostname(mmc)); | |
913 | ||
914 | return ret; | |
915 | } | |
916 | ||
917 | /* | |
918 | * After halting we expect to be able to use the command line. We interpret the | |
919 | * failure to halt to mean the data lines might still be in use (and the upper | |
920 | * layers will need to send a STOP command), so we set the timeout based on a | |
921 | * generous command timeout. | |
922 | */ | |
923 | #define CQHCI_START_HALT_TIMEOUT 5 | |
924 | ||
925 | static void cqhci_recovery_start(struct mmc_host *mmc) | |
926 | { | |
927 | struct cqhci_host *cq_host = mmc->cqe_private; | |
928 | ||
929 | pr_debug("%s: cqhci: %s\n", mmc_hostname(mmc), __func__); | |
930 | ||
931 | WARN_ON(!cq_host->recovery_halt); | |
932 | ||
933 | cqhci_halt(mmc, CQHCI_START_HALT_TIMEOUT); | |
934 | ||
935 | if (cq_host->ops->disable) | |
936 | cq_host->ops->disable(mmc, true); | |
937 | ||
938 | mmc->cqe_on = false; | |
939 | } | |
940 | ||
941 | static int cqhci_error_from_flags(unsigned int flags) | |
942 | { | |
943 | if (!flags) | |
944 | return 0; | |
945 | ||
946 | /* CRC errors might indicate re-tuning so prefer to report that */ | |
947 | if (flags & CQHCI_HOST_CRC) | |
948 | return -EILSEQ; | |
949 | ||
950 | if (flags & (CQHCI_EXTERNAL_TIMEOUT | CQHCI_HOST_TIMEOUT)) | |
951 | return -ETIMEDOUT; | |
952 | ||
953 | return -EIO; | |
954 | } | |
955 | ||
956 | static void cqhci_recover_mrq(struct cqhci_host *cq_host, unsigned int tag) | |
957 | { | |
958 | struct cqhci_slot *slot = &cq_host->slot[tag]; | |
959 | struct mmc_request *mrq = slot->mrq; | |
960 | struct mmc_data *data; | |
961 | ||
962 | if (!mrq) | |
963 | return; | |
964 | ||
965 | slot->mrq = NULL; | |
966 | ||
967 | cq_host->qcnt -= 1; | |
968 | ||
969 | data = mrq->data; | |
970 | if (data) { | |
971 | data->bytes_xfered = 0; | |
972 | data->error = cqhci_error_from_flags(slot->flags); | |
973 | } else { | |
974 | mrq->cmd->error = cqhci_error_from_flags(slot->flags); | |
975 | } | |
976 | ||
977 | mmc_cqe_request_done(cq_host->mmc, mrq); | |
978 | } | |
979 | ||
980 | static void cqhci_recover_mrqs(struct cqhci_host *cq_host) | |
981 | { | |
982 | int i; | |
983 | ||
984 | for (i = 0; i < cq_host->num_slots; i++) | |
985 | cqhci_recover_mrq(cq_host, i); | |
986 | } | |
987 | ||
988 | /* | |
989 | * By now the command and data lines should be unused so there is no reason for | |
990 | * CQHCI to take a long time to halt, but if it doesn't halt there could be | |
991 | * problems clearing tasks, so be generous. | |
992 | */ | |
993 | #define CQHCI_FINISH_HALT_TIMEOUT 20 | |
994 | ||
995 | /* CQHCI could be expected to clear it's internal state pretty quickly */ | |
996 | #define CQHCI_CLEAR_TIMEOUT 20 | |
997 | ||
998 | static void cqhci_recovery_finish(struct mmc_host *mmc) | |
999 | { | |
1000 | struct cqhci_host *cq_host = mmc->cqe_private; | |
1001 | unsigned long flags; | |
1002 | u32 cqcfg; | |
1003 | bool ok; | |
1004 | ||
1005 | pr_debug("%s: cqhci: %s\n", mmc_hostname(mmc), __func__); | |
1006 | ||
1007 | WARN_ON(!cq_host->recovery_halt); | |
1008 | ||
1009 | ok = cqhci_halt(mmc, CQHCI_FINISH_HALT_TIMEOUT); | |
1010 | ||
1011 | if (!cqhci_clear_all_tasks(mmc, CQHCI_CLEAR_TIMEOUT)) | |
1012 | ok = false; | |
1013 | ||
1014 | /* | |
1015 | * The specification contradicts itself, by saying that tasks cannot be | |
1016 | * cleared if CQHCI does not halt, but if CQHCI does not halt, it should | |
1017 | * be disabled/re-enabled, but not to disable before clearing tasks. | |
1018 | * Have a go anyway. | |
1019 | */ | |
1020 | if (!ok) { | |
1021 | pr_debug("%s: cqhci: disable / re-enable\n", mmc_hostname(mmc)); | |
1022 | cqcfg = cqhci_readl(cq_host, CQHCI_CFG); | |
1023 | cqcfg &= ~CQHCI_ENABLE; | |
1024 | cqhci_writel(cq_host, cqcfg, CQHCI_CFG); | |
1025 | cqcfg |= CQHCI_ENABLE; | |
1026 | cqhci_writel(cq_host, cqcfg, CQHCI_CFG); | |
1027 | /* Be sure that there are no tasks */ | |
1028 | ok = cqhci_halt(mmc, CQHCI_FINISH_HALT_TIMEOUT); | |
1029 | if (!cqhci_clear_all_tasks(mmc, CQHCI_CLEAR_TIMEOUT)) | |
1030 | ok = false; | |
1031 | WARN_ON(!ok); | |
1032 | } | |
1033 | ||
1034 | cqhci_recover_mrqs(cq_host); | |
1035 | ||
1036 | WARN_ON(cq_host->qcnt); | |
1037 | ||
1038 | spin_lock_irqsave(&cq_host->lock, flags); | |
1039 | cq_host->qcnt = 0; | |
1040 | cq_host->recovery_halt = false; | |
1041 | mmc->cqe_on = false; | |
1042 | spin_unlock_irqrestore(&cq_host->lock, flags); | |
1043 | ||
1044 | /* Ensure all writes are done before interrupts are re-enabled */ | |
1045 | wmb(); | |
1046 | ||
1047 | cqhci_writel(cq_host, CQHCI_IS_HAC | CQHCI_IS_TCL, CQHCI_IS); | |
1048 | ||
1049 | cqhci_set_irqs(cq_host, CQHCI_IS_MASK); | |
1050 | ||
1051 | pr_debug("%s: cqhci: recovery done\n", mmc_hostname(mmc)); | |
1052 | } | |
1053 | ||
1054 | static const struct mmc_cqe_ops cqhci_cqe_ops = { | |
1055 | .cqe_enable = cqhci_enable, | |
1056 | .cqe_disable = cqhci_disable, | |
1057 | .cqe_request = cqhci_request, | |
1058 | .cqe_post_req = cqhci_post_req, | |
1059 | .cqe_off = cqhci_off, | |
1060 | .cqe_wait_for_idle = cqhci_wait_for_idle, | |
1061 | .cqe_timeout = cqhci_timeout, | |
1062 | .cqe_recovery_start = cqhci_recovery_start, | |
1063 | .cqe_recovery_finish = cqhci_recovery_finish, | |
1064 | }; | |
1065 | ||
1066 | struct cqhci_host *cqhci_pltfm_init(struct platform_device *pdev) | |
1067 | { | |
1068 | struct cqhci_host *cq_host; | |
1069 | struct resource *cqhci_memres = NULL; | |
1070 | ||
1071 | /* check and setup CMDQ interface */ | |
1072 | cqhci_memres = platform_get_resource_byname(pdev, IORESOURCE_MEM, | |
1073 | "cqhci_mem"); | |
1074 | if (!cqhci_memres) { | |
1075 | dev_dbg(&pdev->dev, "CMDQ not supported\n"); | |
1076 | return ERR_PTR(-EINVAL); | |
1077 | } | |
1078 | ||
1079 | cq_host = devm_kzalloc(&pdev->dev, sizeof(*cq_host), GFP_KERNEL); | |
1080 | if (!cq_host) | |
1081 | return ERR_PTR(-ENOMEM); | |
1082 | cq_host->mmio = devm_ioremap(&pdev->dev, | |
1083 | cqhci_memres->start, | |
1084 | resource_size(cqhci_memres)); | |
1085 | if (!cq_host->mmio) { | |
1086 | dev_err(&pdev->dev, "failed to remap cqhci regs\n"); | |
1087 | return ERR_PTR(-EBUSY); | |
1088 | } | |
1089 | dev_dbg(&pdev->dev, "CMDQ ioremap: done\n"); | |
1090 | ||
1091 | return cq_host; | |
1092 | } | |
1093 | EXPORT_SYMBOL(cqhci_pltfm_init); | |
1094 | ||
1095 | static unsigned int cqhci_ver_major(struct cqhci_host *cq_host) | |
1096 | { | |
1097 | return CQHCI_VER_MAJOR(cqhci_readl(cq_host, CQHCI_VER)); | |
1098 | } | |
1099 | ||
1100 | static unsigned int cqhci_ver_minor(struct cqhci_host *cq_host) | |
1101 | { | |
1102 | u32 ver = cqhci_readl(cq_host, CQHCI_VER); | |
1103 | ||
1104 | return CQHCI_VER_MINOR1(ver) * 10 + CQHCI_VER_MINOR2(ver); | |
1105 | } | |
1106 | ||
1107 | int cqhci_init(struct cqhci_host *cq_host, struct mmc_host *mmc, | |
1108 | bool dma64) | |
1109 | { | |
1110 | int err; | |
1111 | ||
1112 | cq_host->dma64 = dma64; | |
1113 | cq_host->mmc = mmc; | |
1114 | cq_host->mmc->cqe_private = cq_host; | |
1115 | ||
1116 | cq_host->num_slots = NUM_SLOTS; | |
1117 | cq_host->dcmd_slot = DCMD_SLOT; | |
1118 | ||
1119 | mmc->cqe_ops = &cqhci_cqe_ops; | |
1120 | ||
1121 | mmc->cqe_qdepth = NUM_SLOTS; | |
1122 | if (mmc->caps2 & MMC_CAP2_CQE_DCMD) | |
1123 | mmc->cqe_qdepth -= 1; | |
1124 | ||
1125 | cq_host->slot = devm_kcalloc(mmc_dev(mmc), cq_host->num_slots, | |
1126 | sizeof(*cq_host->slot), GFP_KERNEL); | |
1127 | if (!cq_host->slot) { | |
1128 | err = -ENOMEM; | |
1129 | goto out_err; | |
1130 | } | |
1131 | ||
1132 | spin_lock_init(&cq_host->lock); | |
1133 | ||
1134 | init_completion(&cq_host->halt_comp); | |
1135 | init_waitqueue_head(&cq_host->wait_queue); | |
1136 | ||
1137 | pr_info("%s: CQHCI version %u.%02u\n", | |
1138 | mmc_hostname(mmc), cqhci_ver_major(cq_host), | |
1139 | cqhci_ver_minor(cq_host)); | |
1140 | ||
1141 | return 0; | |
1142 | ||
1143 | out_err: | |
1144 | pr_err("%s: CQHCI version %u.%02u failed to initialize, error %d\n", | |
1145 | mmc_hostname(mmc), cqhci_ver_major(cq_host), | |
1146 | cqhci_ver_minor(cq_host), err); | |
1147 | return err; | |
1148 | } | |
1149 | EXPORT_SYMBOL(cqhci_init); | |
1150 | ||
1151 | MODULE_AUTHOR("Venkat Gopalakrishnan <venkatg@codeaurora.org>"); | |
1152 | MODULE_DESCRIPTION("Command Queue Host Controller Interface driver"); | |
1153 | MODULE_LICENSE("GPL v2"); |