1 From 0aae8f3fefc67bc07b7e4e42f885ef661f0921ab Mon Sep 17 00:00:00 2001
2 From: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
3 Date: Fri, 19 May 2017 14:25:38 +0900
4 Subject: [PATCH 1/4] Add rcar-sdhi DMA support
6 Signed-off-by: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
8 drivers/dma/Makefile | 1 +
9 drivers/dma/sh_dma.c | 306 ++++++++++++++++++++++++++++++++++++++++++++++++++
10 drivers/mmc/sh_sdhi.c | 158 +++++++++++++++++++++++++-
11 drivers/mmc/sh_sdhi.h | 5 +
12 include/sh_dma.h | 58 ++++++++++
13 5 files changed, 524 insertions(+), 4 deletions(-)
14 create mode 100644 drivers/dma/sh_dma.c
15 create mode 100644 include/sh_dma.h
17 diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
18 index 5d864b5..1129fc3 100644
19 --- a/drivers/dma/Makefile
20 +++ b/drivers/dma/Makefile
21 @@ -29,6 +29,7 @@ COBJS-$(CONFIG_FSLDMAFEC) += MCD_tasksInit.o MCD_dmaApi.o MCD_tasks.o
22 COBJS-$(CONFIG_APBH_DMA) += apbh_dma.o
23 COBJS-$(CONFIG_FSL_DMA) += fsl_dma.o
24 COBJS-$(CONFIG_OMAP3_DMA) += omap3_dma.o
25 +COBJS-$(CONFIG_SH_DMA) += sh_dma.o
28 SRCS := $(COBJS:.o=.c)
29 diff --git a/drivers/dma/sh_dma.c b/drivers/dma/sh_dma.c
31 index 0000000..0af2480
33 +++ b/drivers/dma/sh_dma.c
38 + * Copyright (C) 2014 Cogent Embedded, Inc.
40 + * This program is free software; you can redistribute it and/or
41 + * modify it under the terms of the GNU General Public License as
42 + * published by the Free Software Foundation; either version 2 of
43 + * the License, or (at your option) any later version.
45 + * This program is distributed in the hope that it will be useful,
46 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
47 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
48 + * GNU General Public License for more details.
54 +#include <linux/list.h>
61 + struct list_head list;
73 +#define SH_DMA_MAX_TC 0x1000000
74 +#define SH_DMA_MAX_CHAN 32
75 +#define SH_DMA_CHAN_OFFSET 0x8000
76 +#define SH_DMA_CHAN_SIZE 0x80
78 +/* Global registers */
79 +#define SH_DMAISTA 0x20
80 +#define SH_DMASEC 0x30
81 +#define SH_DMAOR 0x60
82 +#define SH_DMACHCL 0x80
83 +#define SH_DMADPSEC 0xA0
85 +/* DMA operation register bits */
86 +#define SH_DMAOR_DME (0x1 << 0)
88 +/* Channel registers */
89 +#define SH_DMASAR 0x00
90 +#define SH_DMADAR 0x04
91 +#define SH_DMATCR 0x08
92 +#define SH_DMACHCR 0x0C
93 +#define SH_DMATSR 0x28
94 +#define SH_DMATCRB 0x18
95 +#define SH_DMATSRB 0x38
96 +#define SH_DMACHCRB 0x1C
97 +#define SH_DMARS 0x40
98 +#define SH_DMABUFCR 0x48
99 +#define SH_DMADPBASE 0x50
100 +#define SH_DMADPCR 0x54
101 +#define SH_DMAFIXSAR 0x10
102 +#define SH_DMAFIXDAR 0x14
103 +#define SH_DMAFIXDPBASE 0x60
105 +/* Channel control register bits */
106 +#define SH_DMACHCR_SM(v) (((v) & 0x3) << 12)
107 +#define SH_DMACHCR_SM_MASK (0x3 << 12)
108 +#define SH_DMACHCR_DM(v) (((v) & 0x3) << 14)
109 +#define SH_DMACHCR_DM_MASK (0x3 << 14)
110 +#define SH_DMACHCR_TS_1 (0x0 << 3 | 0x0 << 20)
111 +#define SH_DMACHCR_TS_2 (0x1 << 3 | 0x0 << 20)
112 +#define SH_DMACHCR_TS_4 (0x2 << 3 | 0x0 << 20)
113 +#define SH_DMACHCR_TS_8 (0x3 << 3 | 0x1 << 20)
114 +#define SH_DMACHCR_TS_16 (0x3 << 3 | 0x0 << 20)
115 +#define SH_DMACHCR_TS_32 (0x0 << 3 | 0x1 << 20)
116 +#define SH_DMACHCR_TS_64 (0x1 << 3 | 0x1 << 20)
117 +#define SH_DMACHCR_TS_MASK (0x3 << 3 | 0x3 << 20)
118 +#define SH_DMACHCR_RS_AUTO (0x4 << 8)
119 +#define SH_DMACHCR_RS_SEL (0x8 << 8)
120 +#define SH_DMACHCR_RS_MASK (0xf << 8)
121 +#define SH_DMACHCR_CAE (0x1 << 31)
122 +#define SH_DMACHCR_TE (0x1 << 1)
123 +#define SH_DMACHCR_DE (0x1 << 0)
125 +#define sh_dma_writel(d, r, v) writel((v), (d)->base + (r))
126 +#define sh_dma_readl(d, r) readl((d)->base + (r))
127 +#define sh_dma_writew(d, r, v) writew((v), (d)->base + (r))
128 +#define sh_dma_readw(d, r) readw((d)->base + (r))
130 +static LIST_HEAD(sh_dma_list);
132 +struct sh_dma *sh_dma_add(u32 base, u32 nch)
134 + struct list_head *entry;
135 + struct sh_dma *dma;
138 + if (nch > SH_DMA_MAX_CHAN)
141 + mask = (1 << nch) - 1;
142 + list_for_each(entry, &sh_dma_list) {
143 + dma = list_entry(entry, struct sh_dma, list);
144 + if (dma->base == base) {
145 + if (nch > dma->nch) {
146 + mask &= ~((1 << dma->nch) - 1);
147 + sh_dma_writel(dma, SH_DMACHCL, mask);
154 + dma = malloc(sizeof(*dma));
161 + sh_dma_writel(dma, SH_DMACHCL, mask);
162 + sh_dma_writew(dma, SH_DMAOR, SH_DMAOR_DME);
163 + list_add(&dma->list, &sh_dma_list);
167 +void sh_dma_chan_src(struct sh_dma_chan *chan, u32 src)
169 + sh_dma_writel(chan, SH_DMASAR, src);
172 +void sh_dma_chan_dst(struct sh_dma_chan *chan, u32 dst)
174 + sh_dma_writel(chan, SH_DMADAR, dst);
177 +void sh_dma_chan_cfg(struct sh_dma_chan *chan, u8 midrid, u8 sm, u8 dm)
181 + sh_dma_writew(chan, SH_DMARS, midrid);
182 + val = sh_dma_readl(chan, SH_DMACHCR);
183 + val &= ~(SH_DMACHCR_RS_MASK |
184 + SH_DMACHCR_SM_MASK | SH_DMACHCR_DM_MASK);
185 + val |= midrid ? SH_DMACHCR_RS_SEL : SH_DMACHCR_RS_AUTO;
186 + val |= SH_DMACHCR_SM(sm) | SH_DMACHCR_DM(dm);
187 + sh_dma_writel(chan, SH_DMACHCR, val);
190 +void sh_dma_chan_start(struct sh_dma_chan *chan, u32 ts, u8 bs)
197 + val = (ts + (1 << bs) - 1) >> bs;
198 + val = val < SH_DMA_MAX_TC ? val : 0x0;
199 + sh_dma_writel(chan, SH_DMATCR, val);
203 + chan->rounds = val;
205 + val = sh_dma_readl(chan, SH_DMACHCR);
207 + val &= ~(SH_DMACHCR_TE | SH_DMACHCR_TS_MASK);
208 + val |= SH_DMACHCR_DE;
212 + val |= SH_DMACHCR_TS_1;
215 + val |= SH_DMACHCR_TS_2;
218 + val |= SH_DMACHCR_TS_4;
221 + val |= SH_DMACHCR_TS_8;
224 + val |= SH_DMACHCR_TS_16;
227 + val |= SH_DMACHCR_TS_32;
230 + val |= SH_DMACHCR_TS_64;
234 + sh_dma_writel(chan, SH_DMACHCR, val);
237 +void sh_dma_chan_stop(struct sh_dma_chan *chan)
245 + val = sh_dma_readl(chan, SH_DMACHCR);
246 + val &= ~(SH_DMACHCR_CAE | SH_DMACHCR_TE | SH_DMACHCR_DE);
247 + sh_dma_writel(chan, SH_DMACHCR, val);
249 + val = sh_dma_readl(chan, SH_DMACHCR);
250 + } while (val & SH_DMACHCR_DE);
253 +int sh_dma_chan_wait(struct sh_dma_chan *chan)
256 + u32 timeout = 10000000;
260 + val = sh_dma_readl(chan, SH_DMACHCR);
261 + val &= SH_DMACHCR_CAE | SH_DMACHCR_TE | SH_DMACHCR_DE;
262 + if (val == (SH_DMACHCR_TE | SH_DMACHCR_DE))
272 + if (!(val & SH_DMACHCR_DE))
273 + return chan->ts ? -EINTR : 0;
275 + if (val & SH_DMACHCR_CAE) {
280 + val = chan->rounds < SH_DMA_MAX_TC ? chan->rounds : SH_DMA_MAX_TC;
281 + val = chan->rounds - val;
283 + puts("abnormal end\n");
284 + sh_dma_chan_start(chan, val << chan->bs, 0);
289 + sh_dma_chan_stop(chan);
293 +void sh_dma_chan_clr(struct sh_dma_chan *chan)
299 + sh_dma_writel(chan->dma, SH_DMACHCL, 1 << chan->num);
302 +struct sh_dma_chan *sh_dma_chan_init(struct sh_dma *dma, int ch)
304 + struct sh_dma_chan *chan;
311 + ch = ffz(dma->mask);
314 + if (!dma || ch > dma->nch)
318 + if (dma->mask & mask)
321 + chan = malloc(sizeof(*chan));
327 + chan->base = dma->base + SH_DMA_CHAN_OFFSET + ch * SH_DMA_CHAN_SIZE;
329 + sh_dma_chan_clr(chan);
334 +void sh_dma_chan_release(struct sh_dma_chan *chan)
336 + struct sh_dma *dma = chan->dma;
338 + dma->mask &= ~(1 << chan->num);
341 diff --git a/drivers/mmc/sh_sdhi.c b/drivers/mmc/sh_sdhi.c
342 index ddad43a..80dc7a8 100644
343 --- a/drivers/mmc/sh_sdhi.c
344 +++ b/drivers/mmc/sh_sdhi.c
350 #include <asm/errno.h>
355 #define DRIVER_NAME "sh-sdhi"
357 +#ifdef CONFIG_SH_DMA
359 +#ifdef CONFIG_SYS_DCACHE_OFF
360 +static inline void sh_sdhi_invalidate_dcache(u32 addr, int len) { }
361 +#else /* CONFIG_SYS_DCACHE_OFF */
362 +#define DCACHE_LINE_MASK (ARCH_DMA_MINALIGN - 1)
364 +static void sh_sdhi_invalidate_dcache(u32 addr, int len)
368 + start = addr & ~DCACHE_LINE_MASK;
369 + if (start != addr) {
370 + end = start + ARCH_DMA_MINALIGN;
371 + flush_dcache_range(start, end);
377 + if (len >= ARCH_DMA_MINALIGN) {
378 + end = (start + len) & ~DCACHE_LINE_MASK;
379 + invalidate_dcache_range(start, end);
381 + len &= DCACHE_LINE_MASK;
386 + end = start + ARCH_DMA_MINALIGN;
387 + flush_dcache_range(start, end);
390 +#endif /* CONFIG_SYS_DCACHE_OFF */
392 +static void sh_sdhi_dma_init(struct sdhi_host *host)
394 + struct sh_dma *dma;
396 + dma = sh_dma_add(CONFIG_SH_SYS_DMAL_BASE, CONFIG_SH_SYS_DMAL_NCH);
400 + host->dma_rx = sh_dma_chan_init(dma, 1);
404 + sh_dma_chan_cfg(host->dma_rx, SH_DMA_SDHI0_RX,
405 + SH_DMA_AM_FIX, SH_DMA_AM_INC);
406 + sh_dma_chan_src(host->dma_rx,
407 + host->addr + (SDHI_BUF0 << host->bus_shift) +
411 +static void sh_sdhi_dma_release(struct sdhi_host *host)
413 + if (host->dma_rx) {
414 + sh_dma_chan_release(host->dma_rx);
415 + host->dma_rx = NULL;
419 +static void sh_sdhi_start_dma_rx(struct sdhi_host *host,
420 + struct mmc_data *data)
423 + u32 blocksize = data->blocksize;
424 + sh_sdhi_dma_init(host);
425 + sdhi_writew(host, SDHI_SD_DMACR, 0xa0);
426 + sdhi_writew(host, SDHI_CC_EXT_MODE, (1 << 1));
428 + sh_sdhi_invalidate_dcache((u32)data->dest, blocksize);
430 + sh_dma_chan_dst(host->dma_rx, (u32)data->dest);
432 + /* sh_sdhi_bitset(BUF_ACC_DMAREN, &host->regs->ce_buf_acc); */
434 + /* MMCIF is capable to transfer only 4 bytes at a time,
435 + * provide size order as a param */
436 + blocksize = sdhi_readw(host, SDHI_SIZE);
437 + sh_dma_chan_start(host->dma_rx, blocksize, 1);
440 + ret = sh_dma_chan_wait(host->dma_rx);
441 + } while (ret == -EAGAIN);
442 + sdhi_writew(host, SDHI_CC_EXT_MODE, 0x0);
443 + sh_dma_chan_clr(host->dma_rx);
444 + sh_sdhi_dma_release(host);
447 +static void sdhi_dma_transfer(struct sdhi_host *host,
448 + struct mmc_data *data)
450 + sh_sdhi_start_dma_rx(host, data);
454 +#else /* CONFIG_SH_DMA */
455 +static inline void sh_sdhi_dma_init(struct sdhi_host *host) { }
456 +static inline void sh_sdhi_dma_release(struct sdhi_host *host) { }
457 +static inline void sh_sdhi_start_dma_rx(struct sdhi_host *host,
458 + struct mmc_data *data) { }
460 +#endif /* CONFIG_SH_DMA */
462 static void *mmc_priv(struct mmc *mmc)
464 return (void *)mmc->priv;
465 @@ -253,7 +357,9 @@ static int sdhi_single_read(struct sdhi_host *host, struct mmc_data *data)
469 +#ifndef CONFIG_SH_DMA
470 unsigned short blocksize, i;
472 unsigned short *p = (unsigned short *)data->dest;
474 if ((unsigned long)p & 0x00000001) {
475 @@ -272,10 +378,14 @@ static int sdhi_single_read(struct sdhi_host *host, struct mmc_data *data)
476 return sdhi_error_manage(host);
479 +#ifdef CONFIG_SH_DMA
480 + sdhi_dma_transfer(host, data);
482 blocksize = sdhi_readw(host, SDHI_SIZE);
483 for (i = 0; i < blocksize / 2; i++)
484 *p++ = sdhi_readw(host, SDHI_BUF0);
487 time = sdhi_wait_interrupt_flag(host);
488 if (time == 0 || g_sd_error[ch] != 0)
489 return sdhi_error_manage(host);
490 @@ -537,7 +647,6 @@ static int sdhi_start_cmd(struct sdhi_host *host,
493 sdhi_writew(host, SDHI_CMD, (unsigned short)(opc & CMD_MASK));
495 g_wait_int[host->ch] = 0;
496 sdhi_writew(host, SDHI_INFO1_MASK,
497 ~INFO1M_RESP_END & sdhi_readw(host, SDHI_INFO1_MASK));
498 @@ -546,7 +655,6 @@ static int sdhi_start_cmd(struct sdhi_host *host,
499 INFO2M_END_ERROR | INFO2M_TIMEOUT |
500 INFO2M_RESP_TIMEOUT | INFO2M_ILA) &
501 sdhi_readw(host, SDHI_INFO2_MASK));
503 time = sdhi_wait_interrupt_flag(host);
505 return sdhi_error_manage(host);
506 @@ -578,7 +686,6 @@ static int sdhi_start_cmd(struct sdhi_host *host,
509 ret = sdhi_data_trans(host, data, opc);
511 pr_debug("ret = %d, resp = %08x, %08x, %08x, %08x\n",
512 ret, cmd->response[0], cmd->response[1],
513 cmd->response[2], cmd->response[3]);
514 @@ -697,3 +804,46 @@ int sdhi_mmc_init(unsigned long addr, int ch)
519 +int sdhi_warmup_sdio(struct mmc *mmc)
521 + struct mmc_cmd cmd;
527 + mmc->bus_width = 1;
528 + mmc->clock = mmc->f_min;
532 + cmd.cmdidx = MMC_CMD_GO_IDLE_STATE;
533 + cmd.resp_type = MMC_RSP_NONE;
535 + err = sdhi_request(mmc, &cmd, NULL);
539 + cmd.resp_type = MMC_RSP_R4;
541 + err = sdhi_request(mmc, &cmd, NULL);
544 + ocr = cmd.response[0];
547 + cmd.resp_type = MMC_RSP_R4;
549 + err = sdhi_request(mmc, &cmd, NULL);
552 + printf("SDIO OCR:%08x\n", cmd.response[0]);
555 + printf("cmd: CMD%02d err = %d, resp = %08x, %08x, %08x, %08x\n",
556 + err, cmd.cmdidx, cmd.response[0], cmd.response[1],
557 + cmd.response[2], cmd.response[3]);
561 diff --git a/drivers/mmc/sh_sdhi.h b/drivers/mmc/sh_sdhi.h
562 index 4deded2..7b5d421 100644
563 --- a/drivers/mmc/sh_sdhi.h
564 +++ b/drivers/mmc/sh_sdhi.h
571 #define SDHI_CMD (0x0000 >> 1)
572 #define SDHI_PORTSEL (0x0004 >> 1)
573 #define SDHI_ARG0 (0x0008 >> 1)
574 @@ -181,6 +183,9 @@ struct sdhi_host {
575 unsigned int power_mode;
578 +#ifdef CONFIG_SH_DMA
579 + struct sh_dma_chan *dma_rx;
583 static unsigned short g_wait_int[CONFIG_MMC_SH_SDHI_NR_CHANNEL];
584 diff --git a/include/sh_dma.h b/include/sh_dma.h
586 index 0000000..3f35c3a
588 +++ b/include/sh_dma.h
590 +#ifndef __SH_DMA_H__
591 +#define __SH_DMA_H__
593 +#include <asm/types.h>
596 +#define SH_DMA_MMCIF0_RX 0xD2
597 +#define SH_DMA_SDHI0_RX 0xCE
600 +#define SH_DMA_AM_FIX 0
601 +#define SH_DMA_AM_INC 1
602 +#define SH_DMA_AM_DEC 2
607 +#ifdef CONFIG_SH_DMA
608 +struct sh_dma *sh_dma_add(u32 base, u32 nch);
609 +struct sh_dma_chan *sh_dma_chan_init(struct sh_dma *dma, int ch);
610 +void sh_dma_chan_release(struct sh_dma_chan *chan);
612 +void sh_dma_chan_src(struct sh_dma_chan *chan, u32 src);
613 +void sh_dma_chan_dst(struct sh_dma_chan *chan, u32 dst);
614 +void sh_dma_chan_cfg(struct sh_dma_chan *chan, u8 midrid, u8 sm, u8 dm);
615 +void sh_dma_chan_start(struct sh_dma_chan *chan, u32 ts, u8 bs);
616 +void sh_dma_chan_stop(struct sh_dma_chan *chan);
617 +int sh_dma_chan_wait(struct sh_dma_chan *chan);
618 +void sh_dma_chan_clr(struct sh_dma_chan *chan);
620 +static inline struct sh_dma *sh_dma_add(u32 base, u32 nch)
625 +static inline struct sh_dma_chan *sh_dma_chan_init(struct sh_dma *dma,
631 +static inline void sh_dma_chan_release(struct sh_dma_chan *chan) { }
632 +static inline void sh_dma_chan_src(struct sh_dma_chan *chan, u32 src) { }
633 +static inline void sh_dma_chan_dst(struct sh_dma_chan *chan, u32 dst) { }
634 +static inline void sh_dma_chan_cfg(struct sh_dma_chan *chan,
635 + u8 midrid, u8 sm, u8 dm) { }
636 +static inline void sh_dma_chan_start(struct sh_dma_chan *chan,
638 +static inline void sh_dma_chan_stop(struct sh_dma_chan *chan) { }
639 +static inline int sh_dma_chan_wait(struct sh_dma_chan *chan)
644 +static inline void sh_dma_chan_clr(struct sh_dma_chan *chan) { }
647 +#endif /* __SH_DMA_H__ */