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