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