xref: /netbsd-src/sys/arch/evbarm/stand/boot2440/s3csdi.c (revision 54e7435b9e33705efd91a1d53320b262bfb3324c)
1 /*-
2  * Copyright (c) 2012 The NetBSD Foundation, Inc.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to The NetBSD Foundation
6  * by Paul Fleischer <paul@xpg.dk>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 #include "s3csdi.h"
30 
31 #include <arm/s3c2xx0/s3c2440reg.h>
32 
33 #include <lib/libsa/stand.h>
34 
35 #include <machine/int_mwgwtypes.h>
36 #include <machine/limits.h>
37 
38 #include <dev/sdmmc/sdmmcreg.h>
39 
40 #define SDI_REG(reg) (*(volatile uint32_t*)(S3C2440_SDI_BASE+reg))
41 
42 //#define SSSDI_DEBUG
43 #ifdef SSSDI_DEBUG
44 #define DPRINTF(s) do {printf s; } while (/*CONSTCOND*/0)
45 #else
46 #define DPRINTF(s) do {} while (/*CONSTCOND*/0)
47 #endif
48 
49 struct s3csdi_softc {
50 	int width;
51 };
52 
53 extern int pclk;
54 
55 static void sssdi_perform_pio_read(struct sdmmc_command *cmd);
56 //static void sssdi_perform_pio_write(struct sdmmc_command *cmd);
57 
58 static struct s3csdi_softc s3csdi_softc;
59 
60 int
s3csd_match(unsigned int tag)61 s3csd_match(unsigned int tag)
62 {
63 	printf("Found S3C2440 SD/MMC\n");
64 	return 1;
65 }
66 
67 void*
s3csd_init(unsigned int tag,uint32_t * caps)68 s3csd_init(unsigned int tag, uint32_t *caps)
69 {
70 	uint32_t data;
71 
72 	*caps = SMC_CAPS_4BIT_MODE;
73 
74 	DPRINTF(("CLKCON: 0x%X\n", *(volatile uint32_t*)(S3C2440_CLKMAN_BASE + CLKMAN_CLKCON)));
75 
76 	DPRINTF(("SDI_INT_MASK: 0x%X\n", SDI_REG(SDI_INT_MASK)));
77 
78 	SDI_REG(SDI_INT_MASK) = 0x0;
79 	SDI_REG(SDI_DTIMER) = 0x007FFFFF;
80 
81 	SDI_REG(SDI_CON) &= ~SDICON_ENCLK;
82 
83 	SDI_REG(SDI_CON) = SDICON_SD_RESET | SDICON_CTYP_SD;
84 
85 	/* Set GPG8 to input such that we can check if there is a card present
86 	 */
87 	data = *(volatile uint32_t*)(S3C2440_GPIO_BASE+GPIO_PGCON);
88 	data = GPIO_SET_FUNC(data, 8, 0x00);
89 	*(volatile uint32_t*)(S3C2440_GPIO_BASE+GPIO_PGCON) = data;
90 
91 	/* Check if a card is present */
92 	data = *(volatile uint32_t*)(S3C2440_GPIO_BASE+GPIO_PGDAT);
93 	if ( (data & (1<<8)) == (1<<8)) {
94 		printf("No card detected\n");
95 		/* Pin 8 is low when no card is inserted */
96 		return 0;
97 	}
98 	printf("Card detected\n");
99 
100 	s3csdi_softc.width = 1;
101 
102 	/* We have no private data to return, but 0 signals error */
103 	return (void*)0x01;
104 }
105 
106 int
s3csd_bus_clock(void * priv,int freq)107 s3csd_bus_clock(void *priv, int freq)
108 {
109 	int div;
110 	int clock_set = 0;
111 	int control;
112 	int clk = pclk/1000; /*Peripheral bus clock in KHz*/
113 
114 	/* Round peripheral bus clock down to nearest MHz */
115 	clk = (clk / 1000) * 1000;
116 
117 	control = SDI_REG(SDI_CON);
118 	SDI_REG(SDI_CON) = control & ~SDICON_ENCLK;
119 
120 
121 	/* If the frequency is zero just keep the clock disabled */
122 	if (freq == 0)
123 		return 0;
124 
125 	for (div = 1; div <= 256; div++) {
126 		if ( clk / div <= freq) {
127 			DPRINTF(("Using divisor %d: %d/%d = %d\n", div, clk,
128 				 div, clk/div));
129 			clock_set = 1;
130 			SDI_REG(SDI_PRE) = div-1;
131 			break;
132 		}
133 	}
134 
135 	if (clock_set) {
136 		SDI_REG(SDI_CON) = control | SDICON_ENCLK;
137 		if (div-1 != SDI_REG(SDI_PRE)) {
138 			return 1;
139 		}
140 
141 		sdmmc_delay(74000/freq);
142 		/* Wait for 74 SDCLK */
143 		/* 1/freq is the length of a clock cycle,
144 		   so we have to wait 1/freq * 74 .
145 		   74000 / freq should express the delay in us.
146 		 */
147 		return 0;
148 	} else {
149 		return 1;
150 	}
151 }
152 
153 #define SSSDI_TRANSFER_NONE  0
154 #define SSSDI_TRANSFER_READ  1
155 #define SSSDI_TRANSFER_WRITE 2
156 
157 void
s3csd_exec_cmd(void * priv,struct sdmmc_command * cmd)158 s3csd_exec_cmd(void *priv, struct sdmmc_command *cmd)
159 {
160 	uint32_t cmd_control;
161 	int status = 0;
162 	uint32_t data_status;
163 	int transfer = SSSDI_TRANSFER_NONE;
164 
165 	DPRINTF(("s3csd_exec_cmd\n"));
166 
167 	SDI_REG(SDI_DAT_FSTA) = 0xFFFFFFFF;
168 	SDI_REG(SDI_DAT_STA) = 0xFFFFFFFF;
169 	SDI_REG(SDI_CMD_STA) = 0xFFFFFFFF;
170 
171 	SDI_REG(SDI_CMD_ARG) = cmd->c_arg;
172 
173 	cmd_control = (cmd->c_opcode & SDICMDCON_CMD_MASK) |
174 	  SDICMDCON_HOST_CMD | SDICMDCON_CMST;
175 	if (cmd->c_flags & SCF_RSP_PRESENT)
176 		cmd_control |= SDICMDCON_WAIT_RSP;
177 	if (cmd->c_flags & SCF_RSP_136)
178 		cmd_control |= SDICMDCON_LONG_RSP;
179 
180 	if (cmd->c_datalen > 0 && cmd->c_data != NULL) {
181 		/* TODO: Ensure that the above condition matches the semantics
182 		         of SDICMDCON_WITH_DATA*/
183 		DPRINTF(("DATA, datalen: %d, blk_size: %d, offset: %d\n", cmd->c_datalen,
184 			 cmd->c_blklen, cmd->c_arg));
185 		cmd_control |= SDICMDCON_WITH_DATA;
186 	}
187 
188 	if (cmd->c_opcode == MMC_STOP_TRANSMISSION) {
189 		cmd_control |= SDICMDCON_ABORT_CMD;
190 	}
191 
192 	SDI_REG(SDI_DTIMER) = 0x007FFFFF;
193 	SDI_REG(SDI_BSIZE) = cmd->c_blklen;
194 
195 	if ( (cmd->c_flags & SCF_CMD_READ) &&
196 	     (cmd_control & SDICMDCON_WITH_DATA)) {
197 		uint32_t data_control;
198 		DPRINTF(("Reading %d bytes\n", cmd->c_datalen));
199 		transfer = SSSDI_TRANSFER_READ;
200 
201 		data_control = SDIDATCON_DATMODE_RECEIVE | SDIDATCON_RACMD |
202 		  SDIDATCON_DTST | SDIDATCON_BLKMODE |
203 		  ((cmd->c_datalen / cmd->c_blklen) & SDIDATCON_BLKNUM_MASK) |
204 		  SDIDATCON_DATA_WORD;
205 
206 
207 		if (s3csdi_softc.width == 4) {
208 			data_control |= SDIDATCON_WIDEBUS;
209 		}
210 
211 		SDI_REG(SDI_DAT_CON) = data_control;
212 	} else if (cmd_control & SDICMDCON_WITH_DATA) {
213 		/* Write data */
214 
215 		uint32_t data_control;
216 		DPRINTF(("Writing %d bytes\n", cmd->c_datalen));
217 		DPRINTF(("Requesting %d blocks\n",
218 			 cmd->c_datalen / cmd->c_blklen));
219 		transfer = SSSDI_TRANSFER_WRITE;
220 		data_control = SDIDATCON_DATMODE_TRANSMIT | SDIDATCON_BLKMODE |
221 		  SDIDATCON_TARSP | SDIDATCON_DTST |
222 		  ((cmd->c_datalen / cmd->c_blklen) & SDIDATCON_BLKNUM_MASK) |
223 		  SDIDATCON_DATA_WORD;
224 
225 /*		if (sc->width == 4) {
226 			data_control |= SDIDATCON_WIDEBUS;
227 			}*/
228 
229 		SDI_REG(SDI_DAT_CON) = data_control;
230 	}
231 
232 	DPRINTF(("SID_CMD_CON: 0x%X\n", cmd_control));
233 	/* Send command to SDI */
234 	SDI_REG(SDI_CMD_CON) = cmd_control;
235 	DPRINTF(("Status before cmd sent: 0x%X\n", SDI_REG(SDI_CMD_STA)));
236 	DPRINTF(("Waiting for command being sent\n"));
237 	while( !(SDI_REG(SDI_CMD_STA) & SDICMDSTA_CMD_SENT));
238 	DPRINTF(("Command has been sent\n"));
239 
240 	//SDI_REG(SDI_CMD_STA) |= SDICMDSTA_CMD_SENT;
241 
242 	if (!(cmd_control & SDICMDCON_WAIT_RSP)) {
243 		SDI_REG(SDI_CMD_STA) |= SDICMDSTA_CMD_SENT;
244 		cmd->c_flags |= SCF_ITSDONE;
245 		goto out;
246 	}
247 
248 	DPRINTF(("waiting for response\n"));
249 	while(1) {
250 		status = SDI_REG(SDI_CMD_STA);
251 		if (status & SDICMDSTA_RSP_FIN) {
252 			break;
253 		}
254 		if (status & SDICMDSTA_CMD_TIMEOUT) {
255 			break;
256 		}
257 	}
258 
259 	DPRINTF(("Status: 0x%X\n", status));
260 	if (status & SDICMDSTA_CMD_TIMEOUT) {
261 		cmd->c_error = ETIMEDOUT;
262 		DPRINTF(("Timeout waiting for response\n"));
263 		goto out;
264 	}
265 	DPRINTF(("Got Response\n"));
266 
267 	if (cmd->c_flags & SCF_RSP_136 ) {
268 		uint32_t w[4];
269 
270 		/* We store the response least significant word first */
271 		w[0] = SDI_REG(SDI_RSP3);
272 		w[1] = SDI_REG(SDI_RSP2);
273 		w[2] = SDI_REG(SDI_RSP1);
274 		w[3] = SDI_REG(SDI_RSP0);
275 
276 		/* The sdmmc subsystem expects that the response is delivered
277 		   without the lower 8 bits (CRC + '1' bit) */
278 		cmd->c_resp[0] = (w[0] >> 8) | ((w[1] & 0xFF) << 24);
279 		cmd->c_resp[1] = (w[1] >> 8) | ((w[2] & 0XFF) << 24);
280 		cmd->c_resp[2] = (w[2] >> 8) | ((w[3] & 0XFF) << 24);
281 		cmd->c_resp[3] = (w[3] >> 8);
282 
283 	} else {
284 		cmd->c_resp[0] = SDI_REG(SDI_RSP0);
285 		cmd->c_resp[1] = SDI_REG(SDI_RSP1);
286 	}
287 
288 	DPRINTF(("Response: %X %X %X %X\n",
289 		 cmd->c_resp[0],
290 		 cmd->c_resp[1],
291 		 cmd->c_resp[2],
292 		 cmd->c_resp[3]));
293 
294 	status = SDI_REG(SDI_DAT_CNT);
295 
296 	DPRINTF(("Remaining bytes of current block: %d\n",
297 		 SDIDATCNT_BLK_CNT(status)));
298 	DPRINTF(("Remaining Block Number          : %d\n",
299 		 SDIDATCNT_BLK_NUM_CNT(status)));
300 
301 	data_status = SDI_REG(SDI_DAT_STA);
302 
303 	DPRINTF(("SDI Data Status Register Before xfer: 0x%X\n", data_status));
304 
305 	if (data_status & SDIDATSTA_DATA_TIMEOUT) {
306 		cmd->c_error = ETIMEDOUT;
307 		DPRINTF(("Timeout waiting for data\n"));
308 		goto out;
309 	}
310 
311 
312 	if (transfer == SSSDI_TRANSFER_READ) {
313 		DPRINTF(("Waiting for transfer to complete\n"));
314 
315 		sssdi_perform_pio_read(cmd);
316 	} else if (transfer == SSSDI_TRANSFER_WRITE) {
317 
318 /*		DPRINTF(("PIO WRITE\n"));
319 		sssdi_perform_pio_write(sc, cmd);
320 
321 		if (cmd->c_error == ETIMEDOUT)
322 		goto out;*/
323 	}
324 
325 
326 	/* Response has been received, and any data transfer needed has been
327 	   performed */
328 	cmd->c_flags |= SCF_ITSDONE;
329 
330  out:
331 
332 	data_status = SDI_REG(SDI_DAT_STA);
333 	DPRINTF(("SDI Data Status Register after execute: 0x%X\n", data_status));
334 
335 	/* Clear status register. Their are cleared on the
336 	   next sssdi_exec_command  */
337 	SDI_REG(SDI_CMD_STA) =  0xFFFFFFFF;
338 	SDI_REG(SDI_DAT_CON) =  0x0;
339 }
340 
341 void
sssdi_perform_pio_read(struct sdmmc_command * cmd)342 sssdi_perform_pio_read(struct sdmmc_command *cmd)
343 {
344 	uint32_t status;
345 	uint32_t fifo_status;
346 	int count;
347 	uint32_t written;
348 	uint8_t *dest = (uint8_t*)cmd->c_data;
349 	int i;
350 
351 	written = 0;
352 
353 	while (written < cmd->c_datalen ) {
354 		/* Wait until the FIFO is full or has the final data.
355 		   In the latter case it might not get filled. */
356 		//status = sssdi_wait_intr(sc, SDI_FIFO_RX_FULL | SDI_FIFO_RX_LAST, 1000);
357 		//printf("Waiting for FIFO (got %d / %d)\n", written, cmd->c_datalen);
358 		do {
359 			status = SDI_REG(SDI_DAT_FSTA);
360 		} while( !(status & SDIDATFSTA_RF_FULL) && !(status & SDIDATFSTA_RF_LAST));
361 		//printf("Done\n");
362 
363 		fifo_status = SDI_REG(SDI_DAT_FSTA);
364 		count = SDIDATFSTA_FFCNT(fifo_status);
365 
366 		//printf("Writing %d bytes to %p\n", count, dest);
367 		for(i=0; i<count; i+=4) {
368 			uint32_t buf;
369 
370 			buf = SDI_REG(SDI_DAT_LI_W);
371 			*dest = (buf & 0xFF); dest++;
372 			*dest = (buf >> 8) & 0xFF; dest++;
373 			*dest = (buf >> 16) & 0xFF; dest++;
374 			*dest = (buf >> 24) & 0xFF; dest++;
375 			written += 4;
376 		}
377 	}
378 }
379 
380 #if 0
381 void
382 sssdi_perform_pio_write(struct sdmmc_command *cmd)
383 {
384 	uint32_t status;
385 	uint32_t fifo_status;
386 	int count;
387 	uint32_t written;
388 	uint32_t *dest = (uint32_t*)cmd->c_data;
389 
390 	written = 0;
391 
392 	while (written < cmd->c_datalen ) {
393 		/* Wait until the FIFO is full or has the final data.
394 		   In the latter case it might not get filled. */
395 		DPRINTF(("Waiting for FIFO to become empty\n"));
396 		status = sssdi_wait_intr(sc, SDI_FIFO_TX_EMPTY, 1000);
397 
398 		fifo_status = bus_space_read_4(sc->iot, sc->ioh, SDI_DAT_FSTA);
399 		DPRINTF(("PIO Write FIFO Status: 0x%X\n", fifo_status));
400 		count = 64-SDIDATFSTA_FFCNT(fifo_status);
401 
402 		status = bus_space_read_4(sc->iot, sc->ioh, SDI_DAT_CNT);
403 		DPRINTF(("Remaining bytes of current block: %d\n",
404 			 SDIDATCNT_BLK_CNT(status)));
405 		DPRINTF(("Remaining Block Number          : %d\n",
406 			 SDIDATCNT_BLK_NUM_CNT(status)));
407 
408 
409 		status = bus_space_read_4(sc->iot,sc->ioh, SDI_DAT_STA);
410 		DPRINTF(("PIO Write Data Status: 0x%X\n", status));
411 
412 		if (status & SDIDATSTA_DATA_TIMEOUT) {
413 			cmd->c_error = ETIMEDOUT;
414 			/* Acknowledge the timeout*/
415 			bus_space_write_4(sc->iot, sc->ioh, SDI_DAT_STA,
416 					  SDIDATSTA_DATA_TIMEOUT);
417 			printf("%s: Data timeout\n", device_xname(sc->dev));
418 			break;
419 		}
420 
421 		DPRINTF(("Filling FIFO with %d bytes\n", count));
422 		for(int i=0; i<count; i+=4) {
423 			bus_space_write_4(sc->iot, sc->ioh, SDI_DAT_LI_W, *dest);
424 			written += 4;
425 			dest++;
426 		}
427 	}
428 }
429 #endif
430 
431 int
s3csd_host_ocr(void * priv)432 s3csd_host_ocr(void *priv)
433 {
434 	return MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V;
435 }
436 
437 int
s3csd_bus_power(void * priv,int ocr)438 s3csd_bus_power(void *priv, int ocr)
439 {
440 	return 0;
441 }
442 
443 int
s3csd_bus_width(void * priv,int width)444 s3csd_bus_width(void *priv, int width)
445 {
446 	s3csdi_softc.width = width;
447 	return 0;
448 }
449 
450 int
s3csd_get_max_bus_clock(void * priv)451 s3csd_get_max_bus_clock(void *priv)
452 {
453 	return pclk / 1;
454 }
455