1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * drivers/mmc/sh_sdhi.c
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * SD/MMC driver for Renesas rmobile ARM SoCs.
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * Copyright (C) 2011,2013-2017 Renesas Electronics Corporation
7*4882a593Smuzhiyun * Copyright (C) 2014 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
8*4882a593Smuzhiyun * Copyright (C) 2008-2009 Renesas Solutions Corp.
9*4882a593Smuzhiyun *
10*4882a593Smuzhiyun * SPDX-License-Identifier: GPL-2.0
11*4882a593Smuzhiyun */
12*4882a593Smuzhiyun
13*4882a593Smuzhiyun #include <common.h>
14*4882a593Smuzhiyun #include <malloc.h>
15*4882a593Smuzhiyun #include <mmc.h>
16*4882a593Smuzhiyun #include <dm.h>
17*4882a593Smuzhiyun #include <linux/errno.h>
18*4882a593Smuzhiyun #include <linux/compat.h>
19*4882a593Smuzhiyun #include <linux/io.h>
20*4882a593Smuzhiyun #include <linux/sizes.h>
21*4882a593Smuzhiyun #include <asm/arch/rmobile.h>
22*4882a593Smuzhiyun #include <asm/arch/sh_sdhi.h>
23*4882a593Smuzhiyun #include <clk.h>
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun #define DRIVER_NAME "sh-sdhi"
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun struct sh_sdhi_host {
28*4882a593Smuzhiyun void __iomem *addr;
29*4882a593Smuzhiyun int ch;
30*4882a593Smuzhiyun int bus_shift;
31*4882a593Smuzhiyun unsigned long quirks;
32*4882a593Smuzhiyun unsigned char wait_int;
33*4882a593Smuzhiyun unsigned char sd_error;
34*4882a593Smuzhiyun unsigned char detect_waiting;
35*4882a593Smuzhiyun unsigned char app_cmd;
36*4882a593Smuzhiyun };
37*4882a593Smuzhiyun
sh_sdhi_writeq(struct sh_sdhi_host * host,int reg,u64 val)38*4882a593Smuzhiyun static inline void sh_sdhi_writeq(struct sh_sdhi_host *host, int reg, u64 val)
39*4882a593Smuzhiyun {
40*4882a593Smuzhiyun writeq(val, host->addr + (reg << host->bus_shift));
41*4882a593Smuzhiyun }
42*4882a593Smuzhiyun
sh_sdhi_readq(struct sh_sdhi_host * host,int reg)43*4882a593Smuzhiyun static inline u64 sh_sdhi_readq(struct sh_sdhi_host *host, int reg)
44*4882a593Smuzhiyun {
45*4882a593Smuzhiyun return readq(host->addr + (reg << host->bus_shift));
46*4882a593Smuzhiyun }
47*4882a593Smuzhiyun
sh_sdhi_writew(struct sh_sdhi_host * host,int reg,u16 val)48*4882a593Smuzhiyun static inline void sh_sdhi_writew(struct sh_sdhi_host *host, int reg, u16 val)
49*4882a593Smuzhiyun {
50*4882a593Smuzhiyun writew(val, host->addr + (reg << host->bus_shift));
51*4882a593Smuzhiyun }
52*4882a593Smuzhiyun
sh_sdhi_readw(struct sh_sdhi_host * host,int reg)53*4882a593Smuzhiyun static inline u16 sh_sdhi_readw(struct sh_sdhi_host *host, int reg)
54*4882a593Smuzhiyun {
55*4882a593Smuzhiyun return readw(host->addr + (reg << host->bus_shift));
56*4882a593Smuzhiyun }
57*4882a593Smuzhiyun
sh_sdhi_detect(struct sh_sdhi_host * host)58*4882a593Smuzhiyun static void sh_sdhi_detect(struct sh_sdhi_host *host)
59*4882a593Smuzhiyun {
60*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_OPTION,
61*4882a593Smuzhiyun OPT_BUS_WIDTH_1 | sh_sdhi_readw(host, SDHI_OPTION));
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun host->detect_waiting = 0;
64*4882a593Smuzhiyun }
65*4882a593Smuzhiyun
sh_sdhi_intr(void * dev_id)66*4882a593Smuzhiyun static int sh_sdhi_intr(void *dev_id)
67*4882a593Smuzhiyun {
68*4882a593Smuzhiyun struct sh_sdhi_host *host = dev_id;
69*4882a593Smuzhiyun int state1 = 0, state2 = 0;
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun state1 = sh_sdhi_readw(host, SDHI_INFO1);
72*4882a593Smuzhiyun state2 = sh_sdhi_readw(host, SDHI_INFO2);
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun debug("%s: state1 = %x, state2 = %x\n", __func__, state1, state2);
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun /* CARD Insert */
77*4882a593Smuzhiyun if (state1 & INFO1_CARD_IN) {
78*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_CARD_IN);
79*4882a593Smuzhiyun if (!host->detect_waiting) {
80*4882a593Smuzhiyun host->detect_waiting = 1;
81*4882a593Smuzhiyun sh_sdhi_detect(host);
82*4882a593Smuzhiyun }
83*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END |
84*4882a593Smuzhiyun INFO1M_ACCESS_END | INFO1M_CARD_IN |
85*4882a593Smuzhiyun INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
86*4882a593Smuzhiyun return -EAGAIN;
87*4882a593Smuzhiyun }
88*4882a593Smuzhiyun /* CARD Removal */
89*4882a593Smuzhiyun if (state1 & INFO1_CARD_RE) {
90*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_CARD_RE);
91*4882a593Smuzhiyun if (!host->detect_waiting) {
92*4882a593Smuzhiyun host->detect_waiting = 1;
93*4882a593Smuzhiyun sh_sdhi_detect(host);
94*4882a593Smuzhiyun }
95*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END |
96*4882a593Smuzhiyun INFO1M_ACCESS_END | INFO1M_CARD_RE |
97*4882a593Smuzhiyun INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
98*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_SDIO_INFO1_MASK, SDIO_INFO1M_ON);
99*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_SDIO_MODE, SDIO_MODE_OFF);
100*4882a593Smuzhiyun return -EAGAIN;
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun if (state2 & INFO2_ALL_ERR) {
104*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO2,
105*4882a593Smuzhiyun (unsigned short)~(INFO2_ALL_ERR));
106*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO2_MASK,
107*4882a593Smuzhiyun INFO2M_ALL_ERR |
108*4882a593Smuzhiyun sh_sdhi_readw(host, SDHI_INFO2_MASK));
109*4882a593Smuzhiyun host->sd_error = 1;
110*4882a593Smuzhiyun host->wait_int = 1;
111*4882a593Smuzhiyun return 0;
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun /* Respons End */
114*4882a593Smuzhiyun if (state1 & INFO1_RESP_END) {
115*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_RESP_END);
116*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO1_MASK,
117*4882a593Smuzhiyun INFO1M_RESP_END |
118*4882a593Smuzhiyun sh_sdhi_readw(host, SDHI_INFO1_MASK));
119*4882a593Smuzhiyun host->wait_int = 1;
120*4882a593Smuzhiyun return 0;
121*4882a593Smuzhiyun }
122*4882a593Smuzhiyun /* SD_BUF Read Enable */
123*4882a593Smuzhiyun if (state2 & INFO2_BRE_ENABLE) {
124*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO2, ~INFO2_BRE_ENABLE);
125*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO2_MASK,
126*4882a593Smuzhiyun INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ |
127*4882a593Smuzhiyun sh_sdhi_readw(host, SDHI_INFO2_MASK));
128*4882a593Smuzhiyun host->wait_int = 1;
129*4882a593Smuzhiyun return 0;
130*4882a593Smuzhiyun }
131*4882a593Smuzhiyun /* SD_BUF Write Enable */
132*4882a593Smuzhiyun if (state2 & INFO2_BWE_ENABLE) {
133*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO2, ~INFO2_BWE_ENABLE);
134*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO2_MASK,
135*4882a593Smuzhiyun INFO2_BWE_ENABLE | INFO2M_BUF_ILL_WRITE |
136*4882a593Smuzhiyun sh_sdhi_readw(host, SDHI_INFO2_MASK));
137*4882a593Smuzhiyun host->wait_int = 1;
138*4882a593Smuzhiyun return 0;
139*4882a593Smuzhiyun }
140*4882a593Smuzhiyun /* Access End */
141*4882a593Smuzhiyun if (state1 & INFO1_ACCESS_END) {
142*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_ACCESS_END);
143*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO1_MASK,
144*4882a593Smuzhiyun INFO1_ACCESS_END |
145*4882a593Smuzhiyun sh_sdhi_readw(host, SDHI_INFO1_MASK));
146*4882a593Smuzhiyun host->wait_int = 1;
147*4882a593Smuzhiyun return 0;
148*4882a593Smuzhiyun }
149*4882a593Smuzhiyun return -EAGAIN;
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun
sh_sdhi_wait_interrupt_flag(struct sh_sdhi_host * host)152*4882a593Smuzhiyun static int sh_sdhi_wait_interrupt_flag(struct sh_sdhi_host *host)
153*4882a593Smuzhiyun {
154*4882a593Smuzhiyun int timeout = 10000000;
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun while (1) {
157*4882a593Smuzhiyun timeout--;
158*4882a593Smuzhiyun if (timeout < 0) {
159*4882a593Smuzhiyun debug(DRIVER_NAME": %s timeout\n", __func__);
160*4882a593Smuzhiyun return 0;
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun if (!sh_sdhi_intr(host))
164*4882a593Smuzhiyun break;
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun udelay(1); /* 1 usec */
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun return 1; /* Return value: NOT 0 = complete waiting */
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun
sh_sdhi_clock_control(struct sh_sdhi_host * host,unsigned long clk)172*4882a593Smuzhiyun static int sh_sdhi_clock_control(struct sh_sdhi_host *host, unsigned long clk)
173*4882a593Smuzhiyun {
174*4882a593Smuzhiyun u32 clkdiv, i, timeout;
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun if (sh_sdhi_readw(host, SDHI_INFO2) & (1 << 14)) {
177*4882a593Smuzhiyun printf(DRIVER_NAME": Busy state ! Cannot change the clock\n");
178*4882a593Smuzhiyun return -EBUSY;
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_CLK_CTRL,
182*4882a593Smuzhiyun ~CLK_ENABLE & sh_sdhi_readw(host, SDHI_CLK_CTRL));
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun if (clk == 0)
185*4882a593Smuzhiyun return -EIO;
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun clkdiv = 0x80;
188*4882a593Smuzhiyun i = CONFIG_SH_SDHI_FREQ >> (0x8 + 1);
189*4882a593Smuzhiyun for (; clkdiv && clk >= (i << 1); (clkdiv >>= 1))
190*4882a593Smuzhiyun i <<= 1;
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_CLK_CTRL, clkdiv);
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun timeout = 100000;
195*4882a593Smuzhiyun /* Waiting for SD Bus busy to be cleared */
196*4882a593Smuzhiyun while (timeout--) {
197*4882a593Smuzhiyun if ((sh_sdhi_readw(host, SDHI_INFO2) & 0x2000))
198*4882a593Smuzhiyun break;
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun if (timeout)
202*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_CLK_CTRL,
203*4882a593Smuzhiyun CLK_ENABLE | sh_sdhi_readw(host, SDHI_CLK_CTRL));
204*4882a593Smuzhiyun else
205*4882a593Smuzhiyun return -EBUSY;
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun return 0;
208*4882a593Smuzhiyun }
209*4882a593Smuzhiyun
sh_sdhi_sync_reset(struct sh_sdhi_host * host)210*4882a593Smuzhiyun static int sh_sdhi_sync_reset(struct sh_sdhi_host *host)
211*4882a593Smuzhiyun {
212*4882a593Smuzhiyun u32 timeout;
213*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_SOFT_RST, SOFT_RST_ON);
214*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_SOFT_RST, SOFT_RST_OFF);
215*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_CLK_CTRL,
216*4882a593Smuzhiyun CLK_ENABLE | sh_sdhi_readw(host, SDHI_CLK_CTRL));
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun timeout = 100000;
219*4882a593Smuzhiyun while (timeout--) {
220*4882a593Smuzhiyun if (!(sh_sdhi_readw(host, SDHI_INFO2) & INFO2_CBUSY))
221*4882a593Smuzhiyun break;
222*4882a593Smuzhiyun udelay(100);
223*4882a593Smuzhiyun }
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun if (!timeout)
226*4882a593Smuzhiyun return -EBUSY;
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun if (host->quirks & SH_SDHI_QUIRK_16BIT_BUF)
229*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_HOST_MODE, 1);
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun return 0;
232*4882a593Smuzhiyun }
233*4882a593Smuzhiyun
sh_sdhi_error_manage(struct sh_sdhi_host * host)234*4882a593Smuzhiyun static int sh_sdhi_error_manage(struct sh_sdhi_host *host)
235*4882a593Smuzhiyun {
236*4882a593Smuzhiyun unsigned short e_state1, e_state2;
237*4882a593Smuzhiyun int ret;
238*4882a593Smuzhiyun
239*4882a593Smuzhiyun host->sd_error = 0;
240*4882a593Smuzhiyun host->wait_int = 0;
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun e_state1 = sh_sdhi_readw(host, SDHI_ERR_STS1);
243*4882a593Smuzhiyun e_state2 = sh_sdhi_readw(host, SDHI_ERR_STS2);
244*4882a593Smuzhiyun if (e_state2 & ERR_STS2_SYS_ERROR) {
245*4882a593Smuzhiyun if (e_state2 & ERR_STS2_RES_STOP_TIMEOUT)
246*4882a593Smuzhiyun ret = -ETIMEDOUT;
247*4882a593Smuzhiyun else
248*4882a593Smuzhiyun ret = -EILSEQ;
249*4882a593Smuzhiyun debug("%s: ERR_STS2 = %04x\n",
250*4882a593Smuzhiyun DRIVER_NAME, sh_sdhi_readw(host, SDHI_ERR_STS2));
251*4882a593Smuzhiyun sh_sdhi_sync_reset(host);
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO1_MASK,
254*4882a593Smuzhiyun INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
255*4882a593Smuzhiyun return ret;
256*4882a593Smuzhiyun }
257*4882a593Smuzhiyun if (e_state1 & ERR_STS1_CRC_ERROR || e_state1 & ERR_STS1_CMD_ERROR)
258*4882a593Smuzhiyun ret = -EILSEQ;
259*4882a593Smuzhiyun else
260*4882a593Smuzhiyun ret = -ETIMEDOUT;
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun debug("%s: ERR_STS1 = %04x\n",
263*4882a593Smuzhiyun DRIVER_NAME, sh_sdhi_readw(host, SDHI_ERR_STS1));
264*4882a593Smuzhiyun sh_sdhi_sync_reset(host);
265*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO1_MASK,
266*4882a593Smuzhiyun INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
267*4882a593Smuzhiyun return ret;
268*4882a593Smuzhiyun }
269*4882a593Smuzhiyun
sh_sdhi_single_read(struct sh_sdhi_host * host,struct mmc_data * data)270*4882a593Smuzhiyun static int sh_sdhi_single_read(struct sh_sdhi_host *host, struct mmc_data *data)
271*4882a593Smuzhiyun {
272*4882a593Smuzhiyun long time;
273*4882a593Smuzhiyun unsigned short blocksize, i;
274*4882a593Smuzhiyun unsigned short *p = (unsigned short *)data->dest;
275*4882a593Smuzhiyun u64 *q = (u64 *)data->dest;
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun if ((unsigned long)p & 0x00000001) {
278*4882a593Smuzhiyun debug(DRIVER_NAME": %s: The data pointer is unaligned.",
279*4882a593Smuzhiyun __func__);
280*4882a593Smuzhiyun return -EIO;
281*4882a593Smuzhiyun }
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun host->wait_int = 0;
284*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO2_MASK,
285*4882a593Smuzhiyun ~(INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ) &
286*4882a593Smuzhiyun sh_sdhi_readw(host, SDHI_INFO2_MASK));
287*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO1_MASK,
288*4882a593Smuzhiyun ~INFO1M_ACCESS_END &
289*4882a593Smuzhiyun sh_sdhi_readw(host, SDHI_INFO1_MASK));
290*4882a593Smuzhiyun time = sh_sdhi_wait_interrupt_flag(host);
291*4882a593Smuzhiyun if (time == 0 || host->sd_error != 0)
292*4882a593Smuzhiyun return sh_sdhi_error_manage(host);
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun host->wait_int = 0;
295*4882a593Smuzhiyun blocksize = sh_sdhi_readw(host, SDHI_SIZE);
296*4882a593Smuzhiyun if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
297*4882a593Smuzhiyun for (i = 0; i < blocksize / 8; i++)
298*4882a593Smuzhiyun *q++ = sh_sdhi_readq(host, SDHI_BUF0);
299*4882a593Smuzhiyun else
300*4882a593Smuzhiyun for (i = 0; i < blocksize / 2; i++)
301*4882a593Smuzhiyun *p++ = sh_sdhi_readw(host, SDHI_BUF0);
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun time = sh_sdhi_wait_interrupt_flag(host);
304*4882a593Smuzhiyun if (time == 0 || host->sd_error != 0)
305*4882a593Smuzhiyun return sh_sdhi_error_manage(host);
306*4882a593Smuzhiyun
307*4882a593Smuzhiyun host->wait_int = 0;
308*4882a593Smuzhiyun return 0;
309*4882a593Smuzhiyun }
310*4882a593Smuzhiyun
sh_sdhi_multi_read(struct sh_sdhi_host * host,struct mmc_data * data)311*4882a593Smuzhiyun static int sh_sdhi_multi_read(struct sh_sdhi_host *host, struct mmc_data *data)
312*4882a593Smuzhiyun {
313*4882a593Smuzhiyun long time;
314*4882a593Smuzhiyun unsigned short blocksize, i, sec;
315*4882a593Smuzhiyun unsigned short *p = (unsigned short *)data->dest;
316*4882a593Smuzhiyun u64 *q = (u64 *)data->dest;
317*4882a593Smuzhiyun
318*4882a593Smuzhiyun if ((unsigned long)p & 0x00000001) {
319*4882a593Smuzhiyun debug(DRIVER_NAME": %s: The data pointer is unaligned.",
320*4882a593Smuzhiyun __func__);
321*4882a593Smuzhiyun return -EIO;
322*4882a593Smuzhiyun }
323*4882a593Smuzhiyun
324*4882a593Smuzhiyun debug("%s: blocks = %d, blocksize = %d\n",
325*4882a593Smuzhiyun __func__, data->blocks, data->blocksize);
326*4882a593Smuzhiyun
327*4882a593Smuzhiyun host->wait_int = 0;
328*4882a593Smuzhiyun for (sec = 0; sec < data->blocks; sec++) {
329*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO2_MASK,
330*4882a593Smuzhiyun ~(INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ) &
331*4882a593Smuzhiyun sh_sdhi_readw(host, SDHI_INFO2_MASK));
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun time = sh_sdhi_wait_interrupt_flag(host);
334*4882a593Smuzhiyun if (time == 0 || host->sd_error != 0)
335*4882a593Smuzhiyun return sh_sdhi_error_manage(host);
336*4882a593Smuzhiyun
337*4882a593Smuzhiyun host->wait_int = 0;
338*4882a593Smuzhiyun blocksize = sh_sdhi_readw(host, SDHI_SIZE);
339*4882a593Smuzhiyun if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
340*4882a593Smuzhiyun for (i = 0; i < blocksize / 8; i++)
341*4882a593Smuzhiyun *q++ = sh_sdhi_readq(host, SDHI_BUF0);
342*4882a593Smuzhiyun else
343*4882a593Smuzhiyun for (i = 0; i < blocksize / 2; i++)
344*4882a593Smuzhiyun *p++ = sh_sdhi_readw(host, SDHI_BUF0);
345*4882a593Smuzhiyun }
346*4882a593Smuzhiyun
347*4882a593Smuzhiyun return 0;
348*4882a593Smuzhiyun }
349*4882a593Smuzhiyun
sh_sdhi_single_write(struct sh_sdhi_host * host,struct mmc_data * data)350*4882a593Smuzhiyun static int sh_sdhi_single_write(struct sh_sdhi_host *host,
351*4882a593Smuzhiyun struct mmc_data *data)
352*4882a593Smuzhiyun {
353*4882a593Smuzhiyun long time;
354*4882a593Smuzhiyun unsigned short blocksize, i;
355*4882a593Smuzhiyun const unsigned short *p = (const unsigned short *)data->src;
356*4882a593Smuzhiyun const u64 *q = (const u64 *)data->src;
357*4882a593Smuzhiyun
358*4882a593Smuzhiyun if ((unsigned long)p & 0x00000001) {
359*4882a593Smuzhiyun debug(DRIVER_NAME": %s: The data pointer is unaligned.",
360*4882a593Smuzhiyun __func__);
361*4882a593Smuzhiyun return -EIO;
362*4882a593Smuzhiyun }
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun debug("%s: blocks = %d, blocksize = %d\n",
365*4882a593Smuzhiyun __func__, data->blocks, data->blocksize);
366*4882a593Smuzhiyun
367*4882a593Smuzhiyun host->wait_int = 0;
368*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO2_MASK,
369*4882a593Smuzhiyun ~(INFO2M_BWE_ENABLE | INFO2M_BUF_ILL_WRITE) &
370*4882a593Smuzhiyun sh_sdhi_readw(host, SDHI_INFO2_MASK));
371*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO1_MASK,
372*4882a593Smuzhiyun ~INFO1M_ACCESS_END &
373*4882a593Smuzhiyun sh_sdhi_readw(host, SDHI_INFO1_MASK));
374*4882a593Smuzhiyun
375*4882a593Smuzhiyun time = sh_sdhi_wait_interrupt_flag(host);
376*4882a593Smuzhiyun if (time == 0 || host->sd_error != 0)
377*4882a593Smuzhiyun return sh_sdhi_error_manage(host);
378*4882a593Smuzhiyun
379*4882a593Smuzhiyun host->wait_int = 0;
380*4882a593Smuzhiyun blocksize = sh_sdhi_readw(host, SDHI_SIZE);
381*4882a593Smuzhiyun if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
382*4882a593Smuzhiyun for (i = 0; i < blocksize / 8; i++)
383*4882a593Smuzhiyun sh_sdhi_writeq(host, SDHI_BUF0, *q++);
384*4882a593Smuzhiyun else
385*4882a593Smuzhiyun for (i = 0; i < blocksize / 2; i++)
386*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_BUF0, *p++);
387*4882a593Smuzhiyun
388*4882a593Smuzhiyun time = sh_sdhi_wait_interrupt_flag(host);
389*4882a593Smuzhiyun if (time == 0 || host->sd_error != 0)
390*4882a593Smuzhiyun return sh_sdhi_error_manage(host);
391*4882a593Smuzhiyun
392*4882a593Smuzhiyun host->wait_int = 0;
393*4882a593Smuzhiyun return 0;
394*4882a593Smuzhiyun }
395*4882a593Smuzhiyun
sh_sdhi_multi_write(struct sh_sdhi_host * host,struct mmc_data * data)396*4882a593Smuzhiyun static int sh_sdhi_multi_write(struct sh_sdhi_host *host, struct mmc_data *data)
397*4882a593Smuzhiyun {
398*4882a593Smuzhiyun long time;
399*4882a593Smuzhiyun unsigned short i, sec, blocksize;
400*4882a593Smuzhiyun const unsigned short *p = (const unsigned short *)data->src;
401*4882a593Smuzhiyun const u64 *q = (const u64 *)data->src;
402*4882a593Smuzhiyun
403*4882a593Smuzhiyun debug("%s: blocks = %d, blocksize = %d\n",
404*4882a593Smuzhiyun __func__, data->blocks, data->blocksize);
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun host->wait_int = 0;
407*4882a593Smuzhiyun for (sec = 0; sec < data->blocks; sec++) {
408*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO2_MASK,
409*4882a593Smuzhiyun ~(INFO2M_BWE_ENABLE | INFO2M_BUF_ILL_WRITE) &
410*4882a593Smuzhiyun sh_sdhi_readw(host, SDHI_INFO2_MASK));
411*4882a593Smuzhiyun
412*4882a593Smuzhiyun time = sh_sdhi_wait_interrupt_flag(host);
413*4882a593Smuzhiyun if (time == 0 || host->sd_error != 0)
414*4882a593Smuzhiyun return sh_sdhi_error_manage(host);
415*4882a593Smuzhiyun
416*4882a593Smuzhiyun host->wait_int = 0;
417*4882a593Smuzhiyun blocksize = sh_sdhi_readw(host, SDHI_SIZE);
418*4882a593Smuzhiyun if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
419*4882a593Smuzhiyun for (i = 0; i < blocksize / 8; i++)
420*4882a593Smuzhiyun sh_sdhi_writeq(host, SDHI_BUF0, *q++);
421*4882a593Smuzhiyun else
422*4882a593Smuzhiyun for (i = 0; i < blocksize / 2; i++)
423*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_BUF0, *p++);
424*4882a593Smuzhiyun }
425*4882a593Smuzhiyun
426*4882a593Smuzhiyun return 0;
427*4882a593Smuzhiyun }
428*4882a593Smuzhiyun
sh_sdhi_get_response(struct sh_sdhi_host * host,struct mmc_cmd * cmd)429*4882a593Smuzhiyun static void sh_sdhi_get_response(struct sh_sdhi_host *host, struct mmc_cmd *cmd)
430*4882a593Smuzhiyun {
431*4882a593Smuzhiyun unsigned short i, j, cnt = 1;
432*4882a593Smuzhiyun unsigned short resp[8];
433*4882a593Smuzhiyun
434*4882a593Smuzhiyun if (cmd->resp_type & MMC_RSP_136) {
435*4882a593Smuzhiyun cnt = 4;
436*4882a593Smuzhiyun resp[0] = sh_sdhi_readw(host, SDHI_RSP00);
437*4882a593Smuzhiyun resp[1] = sh_sdhi_readw(host, SDHI_RSP01);
438*4882a593Smuzhiyun resp[2] = sh_sdhi_readw(host, SDHI_RSP02);
439*4882a593Smuzhiyun resp[3] = sh_sdhi_readw(host, SDHI_RSP03);
440*4882a593Smuzhiyun resp[4] = sh_sdhi_readw(host, SDHI_RSP04);
441*4882a593Smuzhiyun resp[5] = sh_sdhi_readw(host, SDHI_RSP05);
442*4882a593Smuzhiyun resp[6] = sh_sdhi_readw(host, SDHI_RSP06);
443*4882a593Smuzhiyun resp[7] = sh_sdhi_readw(host, SDHI_RSP07);
444*4882a593Smuzhiyun
445*4882a593Smuzhiyun /* SDHI REGISTER SPECIFICATION */
446*4882a593Smuzhiyun for (i = 7, j = 6; i > 0; i--) {
447*4882a593Smuzhiyun resp[i] = (resp[i] << 8) & 0xff00;
448*4882a593Smuzhiyun resp[i] |= (resp[j--] >> 8) & 0x00ff;
449*4882a593Smuzhiyun }
450*4882a593Smuzhiyun resp[0] = (resp[0] << 8) & 0xff00;
451*4882a593Smuzhiyun } else {
452*4882a593Smuzhiyun resp[0] = sh_sdhi_readw(host, SDHI_RSP00);
453*4882a593Smuzhiyun resp[1] = sh_sdhi_readw(host, SDHI_RSP01);
454*4882a593Smuzhiyun }
455*4882a593Smuzhiyun
456*4882a593Smuzhiyun #if defined(__BIG_ENDIAN_BITFIELD)
457*4882a593Smuzhiyun if (cnt == 4) {
458*4882a593Smuzhiyun cmd->response[0] = (resp[6] << 16) | resp[7];
459*4882a593Smuzhiyun cmd->response[1] = (resp[4] << 16) | resp[5];
460*4882a593Smuzhiyun cmd->response[2] = (resp[2] << 16) | resp[3];
461*4882a593Smuzhiyun cmd->response[3] = (resp[0] << 16) | resp[1];
462*4882a593Smuzhiyun } else {
463*4882a593Smuzhiyun cmd->response[0] = (resp[0] << 16) | resp[1];
464*4882a593Smuzhiyun }
465*4882a593Smuzhiyun #else
466*4882a593Smuzhiyun if (cnt == 4) {
467*4882a593Smuzhiyun cmd->response[0] = (resp[7] << 16) | resp[6];
468*4882a593Smuzhiyun cmd->response[1] = (resp[5] << 16) | resp[4];
469*4882a593Smuzhiyun cmd->response[2] = (resp[3] << 16) | resp[2];
470*4882a593Smuzhiyun cmd->response[3] = (resp[1] << 16) | resp[0];
471*4882a593Smuzhiyun } else {
472*4882a593Smuzhiyun cmd->response[0] = (resp[1] << 16) | resp[0];
473*4882a593Smuzhiyun }
474*4882a593Smuzhiyun #endif /* __BIG_ENDIAN_BITFIELD */
475*4882a593Smuzhiyun }
476*4882a593Smuzhiyun
sh_sdhi_set_cmd(struct sh_sdhi_host * host,struct mmc_data * data,unsigned short opc)477*4882a593Smuzhiyun static unsigned short sh_sdhi_set_cmd(struct sh_sdhi_host *host,
478*4882a593Smuzhiyun struct mmc_data *data, unsigned short opc)
479*4882a593Smuzhiyun {
480*4882a593Smuzhiyun if (host->app_cmd) {
481*4882a593Smuzhiyun if (!data)
482*4882a593Smuzhiyun host->app_cmd = 0;
483*4882a593Smuzhiyun return opc | BIT(6);
484*4882a593Smuzhiyun }
485*4882a593Smuzhiyun
486*4882a593Smuzhiyun switch (opc) {
487*4882a593Smuzhiyun case MMC_CMD_SWITCH:
488*4882a593Smuzhiyun return opc | (data ? 0x1c00 : 0x40);
489*4882a593Smuzhiyun case MMC_CMD_SEND_EXT_CSD:
490*4882a593Smuzhiyun return opc | (data ? 0x1c00 : 0);
491*4882a593Smuzhiyun case MMC_CMD_SEND_OP_COND:
492*4882a593Smuzhiyun return opc | 0x0700;
493*4882a593Smuzhiyun case MMC_CMD_APP_CMD:
494*4882a593Smuzhiyun host->app_cmd = 1;
495*4882a593Smuzhiyun default:
496*4882a593Smuzhiyun return opc;
497*4882a593Smuzhiyun }
498*4882a593Smuzhiyun }
499*4882a593Smuzhiyun
sh_sdhi_data_trans(struct sh_sdhi_host * host,struct mmc_data * data,unsigned short opc)500*4882a593Smuzhiyun static unsigned short sh_sdhi_data_trans(struct sh_sdhi_host *host,
501*4882a593Smuzhiyun struct mmc_data *data, unsigned short opc)
502*4882a593Smuzhiyun {
503*4882a593Smuzhiyun if (host->app_cmd) {
504*4882a593Smuzhiyun host->app_cmd = 0;
505*4882a593Smuzhiyun switch (opc) {
506*4882a593Smuzhiyun case SD_CMD_APP_SEND_SCR:
507*4882a593Smuzhiyun case SD_CMD_APP_SD_STATUS:
508*4882a593Smuzhiyun return sh_sdhi_single_read(host, data);
509*4882a593Smuzhiyun default:
510*4882a593Smuzhiyun printf(DRIVER_NAME": SD: NOT SUPPORT APP CMD = d'%04d\n",
511*4882a593Smuzhiyun opc);
512*4882a593Smuzhiyun return -EINVAL;
513*4882a593Smuzhiyun }
514*4882a593Smuzhiyun } else {
515*4882a593Smuzhiyun switch (opc) {
516*4882a593Smuzhiyun case MMC_CMD_WRITE_MULTIPLE_BLOCK:
517*4882a593Smuzhiyun return sh_sdhi_multi_write(host, data);
518*4882a593Smuzhiyun case MMC_CMD_READ_MULTIPLE_BLOCK:
519*4882a593Smuzhiyun return sh_sdhi_multi_read(host, data);
520*4882a593Smuzhiyun case MMC_CMD_WRITE_SINGLE_BLOCK:
521*4882a593Smuzhiyun return sh_sdhi_single_write(host, data);
522*4882a593Smuzhiyun case MMC_CMD_READ_SINGLE_BLOCK:
523*4882a593Smuzhiyun case MMC_CMD_SWITCH:
524*4882a593Smuzhiyun case MMC_CMD_SEND_EXT_CSD:;
525*4882a593Smuzhiyun return sh_sdhi_single_read(host, data);
526*4882a593Smuzhiyun default:
527*4882a593Smuzhiyun printf(DRIVER_NAME": SD: NOT SUPPORT CMD = d'%04d\n", opc);
528*4882a593Smuzhiyun return -EINVAL;
529*4882a593Smuzhiyun }
530*4882a593Smuzhiyun }
531*4882a593Smuzhiyun }
532*4882a593Smuzhiyun
sh_sdhi_start_cmd(struct sh_sdhi_host * host,struct mmc_data * data,struct mmc_cmd * cmd)533*4882a593Smuzhiyun static int sh_sdhi_start_cmd(struct sh_sdhi_host *host,
534*4882a593Smuzhiyun struct mmc_data *data, struct mmc_cmd *cmd)
535*4882a593Smuzhiyun {
536*4882a593Smuzhiyun long time;
537*4882a593Smuzhiyun unsigned short shcmd, opc = cmd->cmdidx;
538*4882a593Smuzhiyun int ret = 0;
539*4882a593Smuzhiyun unsigned long timeout;
540*4882a593Smuzhiyun
541*4882a593Smuzhiyun debug("opc = %d, arg = %x, resp_type = %x\n",
542*4882a593Smuzhiyun opc, cmd->cmdarg, cmd->resp_type);
543*4882a593Smuzhiyun
544*4882a593Smuzhiyun if (opc == MMC_CMD_STOP_TRANSMISSION) {
545*4882a593Smuzhiyun /* SDHI sends the STOP command automatically by STOP reg */
546*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO1_MASK, ~INFO1M_ACCESS_END &
547*4882a593Smuzhiyun sh_sdhi_readw(host, SDHI_INFO1_MASK));
548*4882a593Smuzhiyun
549*4882a593Smuzhiyun time = sh_sdhi_wait_interrupt_flag(host);
550*4882a593Smuzhiyun if (time == 0 || host->sd_error != 0)
551*4882a593Smuzhiyun return sh_sdhi_error_manage(host);
552*4882a593Smuzhiyun
553*4882a593Smuzhiyun sh_sdhi_get_response(host, cmd);
554*4882a593Smuzhiyun return 0;
555*4882a593Smuzhiyun }
556*4882a593Smuzhiyun
557*4882a593Smuzhiyun if (data) {
558*4882a593Smuzhiyun if ((opc == MMC_CMD_READ_MULTIPLE_BLOCK) ||
559*4882a593Smuzhiyun opc == MMC_CMD_WRITE_MULTIPLE_BLOCK) {
560*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_STOP, STOP_SEC_ENABLE);
561*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_SECCNT, data->blocks);
562*4882a593Smuzhiyun }
563*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_SIZE, data->blocksize);
564*4882a593Smuzhiyun }
565*4882a593Smuzhiyun
566*4882a593Smuzhiyun shcmd = sh_sdhi_set_cmd(host, data, opc);
567*4882a593Smuzhiyun
568*4882a593Smuzhiyun /*
569*4882a593Smuzhiyun * U-Boot cannot use interrupt.
570*4882a593Smuzhiyun * So this flag may not be clear by timing
571*4882a593Smuzhiyun */
572*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_RESP_END);
573*4882a593Smuzhiyun
574*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO1_MASK,
575*4882a593Smuzhiyun INFO1M_RESP_END | sh_sdhi_readw(host, SDHI_INFO1_MASK));
576*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_ARG0,
577*4882a593Smuzhiyun (unsigned short)(cmd->cmdarg & ARG0_MASK));
578*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_ARG1,
579*4882a593Smuzhiyun (unsigned short)((cmd->cmdarg >> 16) & ARG1_MASK));
580*4882a593Smuzhiyun
581*4882a593Smuzhiyun timeout = 100000;
582*4882a593Smuzhiyun /* Waiting for SD Bus busy to be cleared */
583*4882a593Smuzhiyun while (timeout--) {
584*4882a593Smuzhiyun if ((sh_sdhi_readw(host, SDHI_INFO2) & 0x2000))
585*4882a593Smuzhiyun break;
586*4882a593Smuzhiyun }
587*4882a593Smuzhiyun
588*4882a593Smuzhiyun host->wait_int = 0;
589*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO1_MASK,
590*4882a593Smuzhiyun ~INFO1M_RESP_END & sh_sdhi_readw(host, SDHI_INFO1_MASK));
591*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO2_MASK,
592*4882a593Smuzhiyun ~(INFO2M_CMD_ERROR | INFO2M_CRC_ERROR |
593*4882a593Smuzhiyun INFO2M_END_ERROR | INFO2M_TIMEOUT |
594*4882a593Smuzhiyun INFO2M_RESP_TIMEOUT | INFO2M_ILA) &
595*4882a593Smuzhiyun sh_sdhi_readw(host, SDHI_INFO2_MASK));
596*4882a593Smuzhiyun
597*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_CMD, (unsigned short)(shcmd & CMD_MASK));
598*4882a593Smuzhiyun time = sh_sdhi_wait_interrupt_flag(host);
599*4882a593Smuzhiyun if (!time) {
600*4882a593Smuzhiyun host->app_cmd = 0;
601*4882a593Smuzhiyun return sh_sdhi_error_manage(host);
602*4882a593Smuzhiyun }
603*4882a593Smuzhiyun
604*4882a593Smuzhiyun if (host->sd_error) {
605*4882a593Smuzhiyun switch (cmd->cmdidx) {
606*4882a593Smuzhiyun case MMC_CMD_ALL_SEND_CID:
607*4882a593Smuzhiyun case MMC_CMD_SELECT_CARD:
608*4882a593Smuzhiyun case SD_CMD_SEND_IF_COND:
609*4882a593Smuzhiyun case MMC_CMD_APP_CMD:
610*4882a593Smuzhiyun ret = -ETIMEDOUT;
611*4882a593Smuzhiyun break;
612*4882a593Smuzhiyun default:
613*4882a593Smuzhiyun debug(DRIVER_NAME": Cmd(d'%d) err\n", opc);
614*4882a593Smuzhiyun debug(DRIVER_NAME": cmdidx = %d\n", cmd->cmdidx);
615*4882a593Smuzhiyun ret = sh_sdhi_error_manage(host);
616*4882a593Smuzhiyun break;
617*4882a593Smuzhiyun }
618*4882a593Smuzhiyun host->sd_error = 0;
619*4882a593Smuzhiyun host->wait_int = 0;
620*4882a593Smuzhiyun host->app_cmd = 0;
621*4882a593Smuzhiyun return ret;
622*4882a593Smuzhiyun }
623*4882a593Smuzhiyun
624*4882a593Smuzhiyun if (sh_sdhi_readw(host, SDHI_INFO1) & INFO1_RESP_END) {
625*4882a593Smuzhiyun host->app_cmd = 0;
626*4882a593Smuzhiyun return -EINVAL;
627*4882a593Smuzhiyun }
628*4882a593Smuzhiyun
629*4882a593Smuzhiyun if (host->wait_int) {
630*4882a593Smuzhiyun sh_sdhi_get_response(host, cmd);
631*4882a593Smuzhiyun host->wait_int = 0;
632*4882a593Smuzhiyun }
633*4882a593Smuzhiyun
634*4882a593Smuzhiyun if (data)
635*4882a593Smuzhiyun ret = sh_sdhi_data_trans(host, data, opc);
636*4882a593Smuzhiyun
637*4882a593Smuzhiyun debug("ret = %d, resp = %08x, %08x, %08x, %08x\n",
638*4882a593Smuzhiyun ret, cmd->response[0], cmd->response[1],
639*4882a593Smuzhiyun cmd->response[2], cmd->response[3]);
640*4882a593Smuzhiyun return ret;
641*4882a593Smuzhiyun }
642*4882a593Smuzhiyun
sh_sdhi_send_cmd_common(struct sh_sdhi_host * host,struct mmc_cmd * cmd,struct mmc_data * data)643*4882a593Smuzhiyun static int sh_sdhi_send_cmd_common(struct sh_sdhi_host *host,
644*4882a593Smuzhiyun struct mmc_cmd *cmd, struct mmc_data *data)
645*4882a593Smuzhiyun {
646*4882a593Smuzhiyun host->sd_error = 0;
647*4882a593Smuzhiyun
648*4882a593Smuzhiyun return sh_sdhi_start_cmd(host, data, cmd);
649*4882a593Smuzhiyun }
650*4882a593Smuzhiyun
sh_sdhi_set_ios_common(struct sh_sdhi_host * host,struct mmc * mmc)651*4882a593Smuzhiyun static int sh_sdhi_set_ios_common(struct sh_sdhi_host *host, struct mmc *mmc)
652*4882a593Smuzhiyun {
653*4882a593Smuzhiyun int ret;
654*4882a593Smuzhiyun
655*4882a593Smuzhiyun ret = sh_sdhi_clock_control(host, mmc->clock);
656*4882a593Smuzhiyun if (ret)
657*4882a593Smuzhiyun return -EINVAL;
658*4882a593Smuzhiyun
659*4882a593Smuzhiyun if (mmc->bus_width == 8)
660*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_OPTION,
661*4882a593Smuzhiyun OPT_BUS_WIDTH_8 | (~OPT_BUS_WIDTH_M &
662*4882a593Smuzhiyun sh_sdhi_readw(host, SDHI_OPTION)));
663*4882a593Smuzhiyun else if (mmc->bus_width == 4)
664*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_OPTION,
665*4882a593Smuzhiyun OPT_BUS_WIDTH_4 | (~OPT_BUS_WIDTH_M &
666*4882a593Smuzhiyun sh_sdhi_readw(host, SDHI_OPTION)));
667*4882a593Smuzhiyun else
668*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_OPTION,
669*4882a593Smuzhiyun OPT_BUS_WIDTH_1 | (~OPT_BUS_WIDTH_M &
670*4882a593Smuzhiyun sh_sdhi_readw(host, SDHI_OPTION)));
671*4882a593Smuzhiyun
672*4882a593Smuzhiyun debug("clock = %d, buswidth = %d\n", mmc->clock, mmc->bus_width);
673*4882a593Smuzhiyun
674*4882a593Smuzhiyun return 0;
675*4882a593Smuzhiyun }
676*4882a593Smuzhiyun
sh_sdhi_initialize_common(struct sh_sdhi_host * host)677*4882a593Smuzhiyun static int sh_sdhi_initialize_common(struct sh_sdhi_host *host)
678*4882a593Smuzhiyun {
679*4882a593Smuzhiyun int ret = sh_sdhi_sync_reset(host);
680*4882a593Smuzhiyun
681*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_PORTSEL, USE_1PORT);
682*4882a593Smuzhiyun
683*4882a593Smuzhiyun #if defined(__BIG_ENDIAN_BITFIELD)
684*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_EXT_SWAP, SET_SWAP);
685*4882a593Smuzhiyun #endif
686*4882a593Smuzhiyun
687*4882a593Smuzhiyun sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END |
688*4882a593Smuzhiyun INFO1M_ACCESS_END | INFO1M_CARD_RE |
689*4882a593Smuzhiyun INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
690*4882a593Smuzhiyun
691*4882a593Smuzhiyun return ret;
692*4882a593Smuzhiyun }
693*4882a593Smuzhiyun
694*4882a593Smuzhiyun #ifndef CONFIG_DM_MMC
mmc_priv(struct mmc * mmc)695*4882a593Smuzhiyun static void *mmc_priv(struct mmc *mmc)
696*4882a593Smuzhiyun {
697*4882a593Smuzhiyun return (void *)mmc->priv;
698*4882a593Smuzhiyun }
699*4882a593Smuzhiyun
sh_sdhi_send_cmd(struct mmc * mmc,struct mmc_cmd * cmd,struct mmc_data * data)700*4882a593Smuzhiyun static int sh_sdhi_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
701*4882a593Smuzhiyun struct mmc_data *data)
702*4882a593Smuzhiyun {
703*4882a593Smuzhiyun struct sh_sdhi_host *host = mmc_priv(mmc);
704*4882a593Smuzhiyun
705*4882a593Smuzhiyun return sh_sdhi_send_cmd_common(host, cmd, data);
706*4882a593Smuzhiyun }
707*4882a593Smuzhiyun
sh_sdhi_set_ios(struct mmc * mmc)708*4882a593Smuzhiyun static int sh_sdhi_set_ios(struct mmc *mmc)
709*4882a593Smuzhiyun {
710*4882a593Smuzhiyun struct sh_sdhi_host *host = mmc_priv(mmc);
711*4882a593Smuzhiyun
712*4882a593Smuzhiyun return sh_sdhi_set_ios_common(host, mmc);
713*4882a593Smuzhiyun }
714*4882a593Smuzhiyun
sh_sdhi_initialize(struct mmc * mmc)715*4882a593Smuzhiyun static int sh_sdhi_initialize(struct mmc *mmc)
716*4882a593Smuzhiyun {
717*4882a593Smuzhiyun struct sh_sdhi_host *host = mmc_priv(mmc);
718*4882a593Smuzhiyun
719*4882a593Smuzhiyun return sh_sdhi_initialize_common(host);
720*4882a593Smuzhiyun }
721*4882a593Smuzhiyun
722*4882a593Smuzhiyun static const struct mmc_ops sh_sdhi_ops = {
723*4882a593Smuzhiyun .send_cmd = sh_sdhi_send_cmd,
724*4882a593Smuzhiyun .set_ios = sh_sdhi_set_ios,
725*4882a593Smuzhiyun .init = sh_sdhi_initialize,
726*4882a593Smuzhiyun };
727*4882a593Smuzhiyun
728*4882a593Smuzhiyun #ifdef CONFIG_RCAR_GEN3
729*4882a593Smuzhiyun static struct mmc_config sh_sdhi_cfg = {
730*4882a593Smuzhiyun .name = DRIVER_NAME,
731*4882a593Smuzhiyun .ops = &sh_sdhi_ops,
732*4882a593Smuzhiyun .f_min = CLKDEV_INIT,
733*4882a593Smuzhiyun .f_max = CLKDEV_HS_DATA,
734*4882a593Smuzhiyun .voltages = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34,
735*4882a593Smuzhiyun .host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT | MMC_MODE_HS |
736*4882a593Smuzhiyun MMC_MODE_HS_52MHz,
737*4882a593Smuzhiyun .part_type = PART_TYPE_DOS,
738*4882a593Smuzhiyun .b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT,
739*4882a593Smuzhiyun };
740*4882a593Smuzhiyun #else
741*4882a593Smuzhiyun static struct mmc_config sh_sdhi_cfg = {
742*4882a593Smuzhiyun .name = DRIVER_NAME,
743*4882a593Smuzhiyun .ops = &sh_sdhi_ops,
744*4882a593Smuzhiyun .f_min = CLKDEV_INIT,
745*4882a593Smuzhiyun .f_max = CLKDEV_HS_DATA,
746*4882a593Smuzhiyun .voltages = MMC_VDD_32_33 | MMC_VDD_33_34,
747*4882a593Smuzhiyun .host_caps = MMC_MODE_4BIT | MMC_MODE_HS,
748*4882a593Smuzhiyun .part_type = PART_TYPE_DOS,
749*4882a593Smuzhiyun .b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT,
750*4882a593Smuzhiyun };
751*4882a593Smuzhiyun #endif
752*4882a593Smuzhiyun
sh_sdhi_init(unsigned long addr,int ch,unsigned long quirks)753*4882a593Smuzhiyun int sh_sdhi_init(unsigned long addr, int ch, unsigned long quirks)
754*4882a593Smuzhiyun {
755*4882a593Smuzhiyun int ret = 0;
756*4882a593Smuzhiyun struct mmc *mmc;
757*4882a593Smuzhiyun struct sh_sdhi_host *host = NULL;
758*4882a593Smuzhiyun
759*4882a593Smuzhiyun if (ch >= CONFIG_SYS_SH_SDHI_NR_CHANNEL)
760*4882a593Smuzhiyun return -ENODEV;
761*4882a593Smuzhiyun
762*4882a593Smuzhiyun host = malloc(sizeof(struct sh_sdhi_host));
763*4882a593Smuzhiyun if (!host)
764*4882a593Smuzhiyun return -ENOMEM;
765*4882a593Smuzhiyun
766*4882a593Smuzhiyun mmc = mmc_create(&sh_sdhi_cfg, host);
767*4882a593Smuzhiyun if (!mmc) {
768*4882a593Smuzhiyun ret = -1;
769*4882a593Smuzhiyun goto error;
770*4882a593Smuzhiyun }
771*4882a593Smuzhiyun
772*4882a593Smuzhiyun host->ch = ch;
773*4882a593Smuzhiyun host->addr = (void __iomem *)addr;
774*4882a593Smuzhiyun host->quirks = quirks;
775*4882a593Smuzhiyun
776*4882a593Smuzhiyun if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
777*4882a593Smuzhiyun host->bus_shift = 2;
778*4882a593Smuzhiyun else if (host->quirks & SH_SDHI_QUIRK_16BIT_BUF)
779*4882a593Smuzhiyun host->bus_shift = 1;
780*4882a593Smuzhiyun
781*4882a593Smuzhiyun return ret;
782*4882a593Smuzhiyun error:
783*4882a593Smuzhiyun if (host)
784*4882a593Smuzhiyun free(host);
785*4882a593Smuzhiyun return ret;
786*4882a593Smuzhiyun }
787*4882a593Smuzhiyun
788*4882a593Smuzhiyun #else
789*4882a593Smuzhiyun
790*4882a593Smuzhiyun struct sh_sdhi_plat {
791*4882a593Smuzhiyun struct mmc_config cfg;
792*4882a593Smuzhiyun struct mmc mmc;
793*4882a593Smuzhiyun };
794*4882a593Smuzhiyun
sh_sdhi_dm_send_cmd(struct udevice * dev,struct mmc_cmd * cmd,struct mmc_data * data)795*4882a593Smuzhiyun int sh_sdhi_dm_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
796*4882a593Smuzhiyun struct mmc_data *data)
797*4882a593Smuzhiyun {
798*4882a593Smuzhiyun struct sh_sdhi_host *host = dev_get_priv(dev);
799*4882a593Smuzhiyun
800*4882a593Smuzhiyun return sh_sdhi_send_cmd_common(host, cmd, data);
801*4882a593Smuzhiyun }
802*4882a593Smuzhiyun
sh_sdhi_dm_set_ios(struct udevice * dev)803*4882a593Smuzhiyun int sh_sdhi_dm_set_ios(struct udevice *dev)
804*4882a593Smuzhiyun {
805*4882a593Smuzhiyun struct sh_sdhi_host *host = dev_get_priv(dev);
806*4882a593Smuzhiyun struct mmc *mmc = mmc_get_mmc_dev(dev);
807*4882a593Smuzhiyun
808*4882a593Smuzhiyun return sh_sdhi_set_ios_common(host, mmc);
809*4882a593Smuzhiyun }
810*4882a593Smuzhiyun
811*4882a593Smuzhiyun static const struct dm_mmc_ops sh_sdhi_dm_ops = {
812*4882a593Smuzhiyun .send_cmd = sh_sdhi_dm_send_cmd,
813*4882a593Smuzhiyun .set_ios = sh_sdhi_dm_set_ios,
814*4882a593Smuzhiyun };
815*4882a593Smuzhiyun
sh_sdhi_dm_bind(struct udevice * dev)816*4882a593Smuzhiyun static int sh_sdhi_dm_bind(struct udevice *dev)
817*4882a593Smuzhiyun {
818*4882a593Smuzhiyun struct sh_sdhi_plat *plat = dev_get_platdata(dev);
819*4882a593Smuzhiyun
820*4882a593Smuzhiyun return mmc_bind(dev, &plat->mmc, &plat->cfg);
821*4882a593Smuzhiyun }
822*4882a593Smuzhiyun
sh_sdhi_dm_probe(struct udevice * dev)823*4882a593Smuzhiyun static int sh_sdhi_dm_probe(struct udevice *dev)
824*4882a593Smuzhiyun {
825*4882a593Smuzhiyun struct sh_sdhi_plat *plat = dev_get_platdata(dev);
826*4882a593Smuzhiyun struct sh_sdhi_host *host = dev_get_priv(dev);
827*4882a593Smuzhiyun struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
828*4882a593Smuzhiyun struct clk sh_sdhi_clk;
829*4882a593Smuzhiyun const u32 quirks = dev_get_driver_data(dev);
830*4882a593Smuzhiyun fdt_addr_t base;
831*4882a593Smuzhiyun int ret;
832*4882a593Smuzhiyun
833*4882a593Smuzhiyun base = devfdt_get_addr(dev);
834*4882a593Smuzhiyun if (base == FDT_ADDR_T_NONE)
835*4882a593Smuzhiyun return -EINVAL;
836*4882a593Smuzhiyun
837*4882a593Smuzhiyun host->addr = devm_ioremap(dev, base, SZ_2K);
838*4882a593Smuzhiyun if (!host->addr)
839*4882a593Smuzhiyun return -ENOMEM;
840*4882a593Smuzhiyun
841*4882a593Smuzhiyun ret = clk_get_by_index(dev, 0, &sh_sdhi_clk);
842*4882a593Smuzhiyun if (ret) {
843*4882a593Smuzhiyun debug("failed to get clock, ret=%d\n", ret);
844*4882a593Smuzhiyun return ret;
845*4882a593Smuzhiyun }
846*4882a593Smuzhiyun
847*4882a593Smuzhiyun ret = clk_enable(&sh_sdhi_clk);
848*4882a593Smuzhiyun if (ret) {
849*4882a593Smuzhiyun debug("failed to enable clock, ret=%d\n", ret);
850*4882a593Smuzhiyun return ret;
851*4882a593Smuzhiyun }
852*4882a593Smuzhiyun
853*4882a593Smuzhiyun host->quirks = quirks;
854*4882a593Smuzhiyun
855*4882a593Smuzhiyun if (host->quirks & SH_SDHI_QUIRK_64BIT_BUF)
856*4882a593Smuzhiyun host->bus_shift = 2;
857*4882a593Smuzhiyun else if (host->quirks & SH_SDHI_QUIRK_16BIT_BUF)
858*4882a593Smuzhiyun host->bus_shift = 1;
859*4882a593Smuzhiyun
860*4882a593Smuzhiyun plat->cfg.name = dev->name;
861*4882a593Smuzhiyun plat->cfg.host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS;
862*4882a593Smuzhiyun
863*4882a593Smuzhiyun switch (fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "bus-width",
864*4882a593Smuzhiyun 1)) {
865*4882a593Smuzhiyun case 8:
866*4882a593Smuzhiyun plat->cfg.host_caps |= MMC_MODE_8BIT;
867*4882a593Smuzhiyun break;
868*4882a593Smuzhiyun case 4:
869*4882a593Smuzhiyun plat->cfg.host_caps |= MMC_MODE_4BIT;
870*4882a593Smuzhiyun break;
871*4882a593Smuzhiyun case 1:
872*4882a593Smuzhiyun break;
873*4882a593Smuzhiyun default:
874*4882a593Smuzhiyun dev_err(dev, "Invalid \"bus-width\" value\n");
875*4882a593Smuzhiyun return -EINVAL;
876*4882a593Smuzhiyun }
877*4882a593Smuzhiyun
878*4882a593Smuzhiyun sh_sdhi_initialize_common(host);
879*4882a593Smuzhiyun
880*4882a593Smuzhiyun plat->cfg.voltages = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34;
881*4882a593Smuzhiyun plat->cfg.f_min = CLKDEV_INIT;
882*4882a593Smuzhiyun plat->cfg.f_max = CLKDEV_HS_DATA;
883*4882a593Smuzhiyun plat->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
884*4882a593Smuzhiyun
885*4882a593Smuzhiyun upriv->mmc = &plat->mmc;
886*4882a593Smuzhiyun
887*4882a593Smuzhiyun return 0;
888*4882a593Smuzhiyun }
889*4882a593Smuzhiyun
890*4882a593Smuzhiyun static const struct udevice_id sh_sdhi_sd_match[] = {
891*4882a593Smuzhiyun { .compatible = "renesas,sdhi-r8a7795", .data = SH_SDHI_QUIRK_64BIT_BUF },
892*4882a593Smuzhiyun { .compatible = "renesas,sdhi-r8a7796", .data = SH_SDHI_QUIRK_64BIT_BUF },
893*4882a593Smuzhiyun { /* sentinel */ }
894*4882a593Smuzhiyun };
895*4882a593Smuzhiyun
896*4882a593Smuzhiyun U_BOOT_DRIVER(sh_sdhi_mmc) = {
897*4882a593Smuzhiyun .name = "sh-sdhi-mmc",
898*4882a593Smuzhiyun .id = UCLASS_MMC,
899*4882a593Smuzhiyun .of_match = sh_sdhi_sd_match,
900*4882a593Smuzhiyun .bind = sh_sdhi_dm_bind,
901*4882a593Smuzhiyun .probe = sh_sdhi_dm_probe,
902*4882a593Smuzhiyun .priv_auto_alloc_size = sizeof(struct sh_sdhi_host),
903*4882a593Smuzhiyun .platdata_auto_alloc_size = sizeof(struct sh_sdhi_plat),
904*4882a593Smuzhiyun .ops = &sh_sdhi_dm_ops,
905*4882a593Smuzhiyun };
906*4882a593Smuzhiyun #endif
907