TX51 pre-release
[karo-tx-redboot.git] / packages / devs / flash / arm / mxc / v2_0 / src / mxcmci_host.c
1 // ==========================================================================
2 //
3 //   mxcmci_host.c
4 //   (c) 2008, Freescale
5 //
6 //   MMC card driver for MXC platform
7 //
8 // ==========================================================================
9 //####ECOSGPLCOPYRIGHTBEGIN####
10 // -------------------------------------------
11 // This file is part of eCos, the Embedded Configurable Operating System.
12 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
13 //
14 // eCos is free software; you can redistribute it and/or modify it under
15 // the terms of the GNU General Public License as published by the Free
16 // Software Foundation; either version 2 or (at your option) any later version.
17 //
18 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
19 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
21 // for more details.
22 //
23 // You should have received a copy of the GNU General Public License along
24 // with eCos; if not, write to the Free Software Foundation, Inc.,
25 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 //
27 // As a special exception, if other files instantiate templates or use macros
28 // or inline functions from this file, or you compile this file and link it
29 // with other works to produce a work based on this file, this file does not
30 // by itself cause the resulting work to be covered by the GNU General Public
31 // License. However the source code for this file must still be made available
32 // in accordance with section (3) of the GNU General Public License.
33 //
34 // This exception does not invalidate any other reasons why a work based on
35 // this file might be covered by the GNU General Public License.
36 //
37 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
38 // at http://sources.redhat.com/ecos/ecos-license/
39 // -------------------------------------------
40 //####ECOSGPLCOPYRIGHTEND####
41 //==========================================================================
42 //#####DESCRIPTIONBEGIN####
43 //
44 // Author(s):    Lewis Liu <weizhi.liu@freescale.com>
45 // Contributors: Lewis Liu <weizhi.liu@freescale.com>
46 // Date:         2008-05-13 Initial version
47 // Purpose:
48 // Description:
49 //
50 //####DESCRIPTIONEND####
51 //
52 //==========================================================================
53
54 #include <cyg/infra/diag.h>
55 #include <cyg/io/mxcmci_host.h>
56 #include <cyg/io/mxcmci_core.h>
57 #include <cyg/io/mxcmci_mmc.h>
58 #include <cyg/hal/hal_soc.h>
59 #include <cyg/io/mxc_mmc.h>
60
61 host_register_ptr esdhc_base_pointer;
62 extern void mxc_mmc_init(unsigned int module_base);
63
64 static void esdhc_cmd_config(command_t *);
65 static int esdhc_wait_end_cmd_resp_intr(void);
66 static cyg_uint32 esdhc_check_response(void);
67 static void esdhc_wait_buf_rdy_intr(cyg_uint32, multi_single_block_select);
68 static void esdhc_wait_op_done_intr(cyg_uint32);
69 static cyg_uint32 esdhc_check_data(cyg_uint32, cyg_uint32, cyg_uint32);
70 static void esdhc_set_data_transfer_width(cyg_uint32 data_transfer_width);
71 static void esdhc_set_endianness(cyg_uint32 endian_mode);
72 static int esdhc_check_for_send_cmd(int data_present);
73
74 void host_reset(cyg_uint32 data_transfer_width, cyg_uint32 endian_mode)
75 {
76     int counter = 0;
77
78     /* Reset the entire host controller by writing 1 to RSTA bit of SYSCTRL Register */
79     esdhc_base_pointer->system_control |= ESDHC_SOFTWARE_RESET;
80
81     //use WDOG timer: 3 ms delay
82     hal_delay_us(3 * 1000);
83
84     /* Wait for clearance of CIHB and CDIHB Bits */
85     while (esdhc_base_pointer->present_state & ESDHC_CMD_INHIBIT) {
86         if (counter++ > 200) {
87             diag_printf
88                 ("%s: something goes wrong with the DSDHC and int is not received!\n",
89                  __FUNCTION__);
90             counter = 0;
91             break;
92         }
93     }
94
95     /* send 80 clock ticks for card to power up */
96     esdhc_base_pointer->system_control |= ESDHC_SOFTWARE_INIT;
97
98     /* Set data bus width of ESDCH */
99     esdhc_set_data_transfer_width(data_transfer_width);
100
101     /* Set Endianness of ESDHC */
102     esdhc_set_endianness(endian_mode);
103
104 }
105
106 void esdhc_softreset(cyg_uint32 mask)
107 {
108         //wait max timeout 100ms
109         cyg_uint32 timeout = 100;
110
111         esdhc_base_pointer->system_control |= mask;
112
113         /* hw clears the bit when it's done */
114         while (esdhc_base_pointer->system_control & mask) {
115                 if (timeout == 0) {
116                         flash_dprintf(FLASH_DEBUG_MAX, "%s: Reset did not complete\n",
117                                                 __FUNCTION__);
118                         return;
119                 }
120                 timeout--;
121                 hal_delay_us(100);
122         }
123 }
124
125 void host_init(cyg_uint32 base_address)
126 {
127     esdhc_base_pointer = (host_register_ptr) base_address;
128
129     flash_dprintf(FLASH_DEBUG_MAX, "%s: interface_esdc=%d\n", __FUNCTION__,
130               base_address);
131
132     mxc_mmc_init(base_address);
133 }
134
135 void host_cfg_clock(sdhc_freq_t frequency)
136 {
137     unsigned int timeout = 9000;
138     /* Enable ipg_perclk, HCLK enable, IPG Clock enable.  */
139     esdhc_base_pointer->system_control |= ESDHC_CLOCK_ENABLE;
140
141     esdhc_base_pointer->system_control |= 0xe0000;    //set timeout counter
142
143     /* Clear DTOCV SDCLKFS bits, clear SD clk enable bit to change frequency */
144     esdhc_base_pointer->system_control &= ESDHC_FREQ_MASK;
145
146     /* Disable SD clock */
147     esdhc_base_pointer->system_control &= ~ESDHC_ENABLE;
148
149     if (frequency == IDENTIFICATION_FREQ) {
150         /* Input frequecy to eSDHC is 36 MHZ */
151         /* PLL3 is the source of input frequency */
152         /*Set DTOCV and SDCLKFS bit to get SD_CLK of frequency below 400 KHZ (70.31 KHZ) */
153         esdhc_base_pointer->system_control |= ESDHC_IDENT_FREQ;
154     } else if (frequency == OPERATING_FREQ) {
155         /*Set DTOCV and SDCLKFS bit to get SD_CLK of frequency around 25 MHz.(18 MHz) */
156         esdhc_base_pointer->system_control |= ESDHC_OPERT_FREQ;
157     }
158
159     /* Wait for clock to be steady */
160     while (((esdhc_base_pointer->present_state & 0x8) == 0) && (timeout != 0)) {
161         timeout--;
162         hal_delay_us(10);
163     }
164
165     /* Enable SD clock */
166     esdhc_base_pointer->system_control |= ESDHC_ENABLE;
167 }
168
169 static void esdhc_set_data_transfer_width(cyg_uint32 data_transfer_width)
170 {
171         /* Set DWT bit of protocol control register according to bus_width */
172         esdhc_base_pointer->protocol_control &= ~0x6;
173         esdhc_base_pointer->protocol_control |= data_transfer_width;
174 }
175
176 static void esdhc_set_endianness(cyg_uint32 endian_mode)
177 {
178         /* Set DWT bit of protocol control register according to bus_width */
179         esdhc_base_pointer->protocol_control |= endian_mode;
180 }
181
182 cyg_uint32 host_send_cmd(command_t * cmd)
183 {
184
185         /* Clear Interrupt status register */
186         esdhc_base_pointer->interrupt_status = ESDHC_CLEAR_INTERRUPT;
187         //esdhc_base_pointer->interrupt_status = 0x117f01ff;
188
189         /* Enable Interrupt */
190         esdhc_base_pointer->interrupt_status_enable |= ESDHC_INTERRUPT_ENABLE;
191         //esdhc_base_pointer->interrupt_status_enable |= 0x007f0123;
192
193 #if 0
194         if (esdhc_check_for_send_cmd(cmd->data_present)) {
195                 diag_printf("Data/Cmd Line Busy.\n");
196                 return FAIL;
197         }
198 #endif
199
200         /* Configure Command    */
201         esdhc_cmd_config(cmd);
202
203         /* Wait interrupt (END COMMAND RESPONSE)  */
204         //diag_printf("Wait for CMD Response.\n");
205         if (esdhc_wait_end_cmd_resp_intr()) {
206                 diag_printf("Wait CMD (%d) RESPONSE TIMEOUT.\n", cmd->command);
207                 return FAIL;
208         }
209         //Just test for Erase functionality:Lewis-20080505:
210         if (cmd->command == CMD38) {
211                 flash_dprintf(FLASH_DEBUG_MAX, "%s:Check DAT0 status:\n",
212                                         __FUNCTION__);
213                 //while(((esdhc_base_pointer->present_state) & 0x01000004)){
214                 //   flash_dprintf(FLASH_DEBUG_MAX,".");
215                 //   hal_delay_us(1000);
216                 //}
217                 /* I'm not sure the minimum value of delay */
218                 hal_delay_us(100000);
219                 hal_delay_us(100000);
220                 hal_delay_us(100000);
221                 flash_dprintf(FLASH_DEBUG_MAX,
222                                         "\nCheck DAT0 status DONE: present_state=%x\n",
223                                         (cyg_uint32) (esdhc_base_pointer->present_state));
224         }
225
226         /* Mask all interrupts     */
227         //esdhc_base_pointer->interrupt_signal_enable =0;
228
229         /* Check if an error occured    */
230         return esdhc_check_response();
231 }
232
233 static void esdhc_cmd_config(command_t * cmd)
234 {
235         unsigned int transfer_type;
236
237         /* Write Command Argument in Command Argument Register */
238         esdhc_base_pointer->command_argument = cmd->arg;
239
240         /*    *Configure e-SDHC Register value according to Command    */
241         transfer_type = (((cmd->data_transfer) << DATA_TRANSFER_SHIFT) |
242                                         ((cmd->response_format) << RESPONSE_FORMAT_SHIFT) |
243                                         ((cmd->data_present) << DATA_PRESENT_SHIFT) |
244                                         ((cmd->crc_check) << CRC_CHECK_SHIFT) |
245                                         ((cmd->cmdindex_check) << CMD_INDEX_CHECK_SHIFT) |
246                                         ((cmd->command) << CMD_INDEX_SHIFT) |
247                                         ((cmd->
248                                                 block_count_enable_check) <<
249                                                 BLOCK_COUNT_ENABLE_SHIFT) | ((cmd->
250                                                                                                                                                                 multi_single_block) <<
251                                                                                                         MULTI_SINGLE_BLOCK_SELECT_SHIFT));
252
253         esdhc_base_pointer->command_transfer_type = transfer_type;
254
255         //diag_printf("arg: 0x%x | tp: 0x%x\n", esdhc_base_pointer->command_argument, esdhc_base_pointer->command_transfer_type);
256 }
257
258 static int esdhc_wait_end_cmd_resp_intr(void)
259 {
260         /* Wait interrupt (END COMMAND RESPONSE)  */
261         cyg_uint32 i = 50000;
262         while (!
263                 ((esdhc_base_pointer->
264                         interrupt_status) & ESDHC_STATUS_END_CMD_RESP_TIME_MSK) && i) {
265                 i--;
266                 hal_delay_us(10);
267                 //diag_printf("0x%x\n", esdhc_base_pointer->interrupt_status);
268         }
269
270         if (!
271                 ((esdhc_base_pointer->
272                         interrupt_status) & ESDHC_STATUS_END_CMD_RESP_TIME_MSK)) {
273                 //diag_printf("%s: can't get END COMMAND RESPONSE! Tried %d times\n", __FUNCTION__, (5000000-i));
274                 return FAIL;
275         }
276
277         return SUCCESS;
278 }
279
280 static cyg_uint32 esdhc_check_response(void)
281 {
282         cyg_uint32 status = FAIL;
283
284         /* Check whether the interrupt is an END_CMD_RESP
285          * or a response time out or a CRC error
286          */
287         if ((esdhc_base_pointer->
288                         interrupt_status & ESDHC_STATUS_END_CMD_RESP_MSK)
289                 && !(esdhc_base_pointer->
290                         interrupt_status & ESDHC_STATUS_TIME_OUT_RESP_MSK)
291                 && !(esdhc_base_pointer->
292                         interrupt_status & ESDHC_STATUS_RESP_CRC_ERR_MSK)
293                 && !(esdhc_base_pointer->
294                         interrupt_status & ESDHC_STATUS_RESP_INDEX_ERR_MSK)) {
295
296                 status = SUCCESS;
297         } else {
298                 //diag_printf("Warning: Check CMD response, Intr Status: 0x%x\n", esdhc_base_pointer->interrupt_status);
299                 status = FAIL;
300         }
301
302         return status;
303 }
304
305 void host_read_response(command_response_t * cmd_resp)
306 {
307         /* get response values from e-SDHC CMDRSP registers. */
308         cmd_resp->cmd_rsp0 = (cyg_uint32) esdhc_base_pointer->command_response0;
309         cmd_resp->cmd_rsp1 = (cyg_uint32) esdhc_base_pointer->command_response1;
310         cmd_resp->cmd_rsp2 = (cyg_uint32) esdhc_base_pointer->command_response2;
311         cmd_resp->cmd_rsp3 = (cyg_uint32) esdhc_base_pointer->command_response3;
312 }
313
314 static void __attribute__((unused)) esdhc_wait_buf_rdy_intr(cyg_uint32 mask,
315                                                                         multi_single_block_select
316                                                                         multi_single_block)
317 {
318
319         /* Wait interrupt (BUF_READ_RDY)    */
320
321         cyg_uint32 i;
322         for (i = 3000; i > 0; i--) {
323                 if (esdhc_base_pointer->interrupt_status & mask) {
324                         break;
325                 }
326                 hal_delay_us(100);
327         }
328
329         if (multi_single_block == MULTIPLE
330                 && esdhc_base_pointer->interrupt_status & mask)
331                 esdhc_base_pointer->interrupt_status |= mask;
332         if (i == 0)
333                 flash_dprintf(FLASH_DEBUG_MAX, "%s:Debug: tried %d times\n",
334                                         __FUNCTION__, (3000 - i));
335
336 }
337
338 static void esdhc_wait_op_done_intr(cyg_uint32 transfer_mask)
339 {
340         /* Wait interrupt (Transfer Complete)    */
341
342         while (!(esdhc_base_pointer->interrupt_status & transfer_mask)) ;
343
344         //diag_printf("Wait OP Done Failed.\n");
345         //flash_dprintf(FLASH_DEBUG_MAX,"%s:Debug: tried %d times\n", __FUNCTION__, (3001-i));
346 }
347
348 static cyg_uint32 esdhc_check_data(cyg_uint32 op_done_mask,
349                    cyg_uint32 read_time_out_mask,
350                    cyg_uint32 read_crc_err_mask)
351 {
352
353     cyg_uint32 status = FAIL;
354
355     /* Check whether the interrupt is an OP_DONE
356      * or a data time out or a CRC error     */
357     if ((esdhc_base_pointer->interrupt_status & op_done_mask) &&
358         !(esdhc_base_pointer->interrupt_status & read_time_out_mask) &&
359         !(esdhc_base_pointer->interrupt_status & read_crc_err_mask)) {
360         status = SUCCESS;
361     } else {
362         status = FAIL;
363         //diag_printf("Warning: Check data, interrupt_status=%X\n", (esdhc_base_pointer->interrupt_status));
364     }
365
366     return status;
367 }
368
369 void host_cfg_block(cyg_uint32 blk_len, cyg_uint32 nob)
370 {
371     /* Configre block Attributes register */
372     esdhc_base_pointer->block_attributes =
373         ((nob << 16) | (blk_len & 0xffff));
374
375     //diag_printf("nob: 0x%x, block_attributes: 0x%x\n", nob, esdhc_base_pointer->block_attributes);
376
377     /* Set Read Water Mark Level register */
378     esdhc_base_pointer->watermark_level = WRITE_READ_WATER_MARK_LEVEL;
379 }
380
381 cyg_uint32 host_data_read(cyg_uint32 * dest_ptr, cyg_uint32 read_len)
382 {
383     cyg_uint32 j, k;
384     cyg_uint32 status = FAIL;
385     unsigned int len = WRITE_READ_WATER_MARK_LEVEL & 0xff;
386     //int counter = 0;
387
388     /* Enable Interrupt */
389     esdhc_base_pointer->interrupt_status_enable |= ESDHC_INTERRUPT_ENABLE;
390
391     for (j = 0; j < read_len / (len * 4); j++) {
392         //StartCounter();
393         /* wait for read fifo full (equal or beyond the watermark) */
394         while (!(esdhc_base_pointer->present_state & (1 << 11))) ;
395
396         //counter = StopCounter();
397         //diag_printf("counter: 0x%x\n", counter);
398
399         for (k = 0; k < len; k++) {
400             *dest_ptr++ = esdhc_base_pointer->data_buffer_access;
401         }
402     }
403
404     /* Wait for transfer complete operation interrupt */
405     esdhc_wait_op_done_intr(ESDHC_STATUS_TRANSFER_COMPLETE_MSK);
406
407     /* Check for status errors */
408     status =
409         esdhc_check_data(ESDHC_STATUS_TRANSFER_COMPLETE_MSK,
410                  ESDHC_STATUS_TIME_OUT_READ, ESDHC_STATUS_READ_CRC_ERR_MSK);
411
412     return status;
413
414 }
415
416 cyg_uint32 host_data_write(cyg_uint32 * src_ptr, cyg_uint32 write_len)
417 {
418     cyg_uint32 i = 0, k;
419     cyg_uint32 status = FAIL;
420     unsigned int len = (WRITE_READ_WATER_MARK_LEVEL >> 16) & 0xff;
421     //cyg_uint32 counter = 0;
422
423     /* Enable Interrupt */
424     esdhc_base_pointer->interrupt_status_enable |= ESDHC_INTERRUPT_ENABLE;
425
426     //StartCounter();
427     for (i = 0; i < (write_len) / (len * 4); i++) {
428         /* wait for write fifo empty (equal or less than the watermark), BWEN */
429         while (!(esdhc_base_pointer->present_state & (1 << 10))) ;
430
431         for (k = 0; k < len; k++) {
432             esdhc_base_pointer->data_buffer_access = *src_ptr++;
433         }
434
435     }
436
437     /* Wait for transfer complete operation interrupt */
438     esdhc_wait_op_done_intr(ESDHC_STATUS_TRANSFER_COMPLETE_MSK);
439
440     //counter = StopCounter();
441     //diag_printf("0x%x\n", counter);
442
443     /* Check for status errors */
444     status =
445         esdhc_check_data(ESDHC_STATUS_TRANSFER_COMPLETE_MSK,
446                  ESDHC_STATUS_TIME_OUT_READ, ESDHC_STATUS_READ_CRC_ERR_MSK);
447
448     return status;
449
450 }
451
452 static int __attribute__((unused)) esdhc_check_for_send_cmd(int data_present)
453 {
454
455     int status = SUCCESS;
456     int counter;
457
458     /* Wait for the command line to be free (poll the CIHB bit of
459      * the present state register.
460      */
461     counter = 1000;
462     while (((esdhc_base_pointer->present_state & 0x1) == 0x1) && counter--) {
463         hal_delay_us(10);
464     }
465
466     if (!counter)
467         return FAIL;
468
469     /* Wait for the data line to be free (poll the CDIHB bit of
470      * the present state register.
471      */
472     counter = 1000;
473     if (data_present == DATA_PRESENT) {
474         while (((esdhc_base_pointer->present_state & 0x2) == 0x2) && counter--) {
475             hal_delay_us(10);
476         }
477
478     }
479
480     if (!counter)
481         return FAIL;
482
483     return status;
484 }