04b5ea1dfda714eb0465173296e62047ea5f1f15
[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/io/mxcmci_host.h>
55 #include <cyg/io/mxcmci_core.h>
56 #include <cyg/io/mxcmci_mmc.h>
57 #include <cyg/hal/hal_soc.h>
58 #include <cyg/io/mxc_mmc.h>
59
60 host_register_ptr esdhc_base_pointer;
61 extern void mxc_mmc_init(unsigned int module_base);
62
63 static void esdhc_cmd_config(command_t *);
64 static int esdhc_wait_end_cmd_resp_intr(void);
65 static cyg_uint32 esdhc_check_response(void);
66 static void esdhc_wait_buf_rdy_intr(cyg_uint32, multi_single_block_select);
67 static void esdhc_wait_op_done_intr(cyg_uint32);
68 static cyg_uint32 esdhc_check_data(cyg_uint32, cyg_uint32, cyg_uint32);
69 static void esdhc_set_data_transfer_width(cyg_uint32 data_transfer_width);
70 static void esdhc_set_endianness(cyg_uint32 endian_mode);
71 static int esdhc_check_for_send_cmd(int data_present);
72
73 void host_reset(cyg_uint32 data_transfer_width, cyg_uint32 endian_mode)
74 {
75     int counter = 0;
76
77     /* Reset the entire host controller by writing 1 to RSTA bit of SYSCTRL Register */
78     esdhc_base_pointer->system_control |= ESDHC_SOFTWARE_RESET;
79
80     //use WDOG timer: 3 ms delay
81     hal_delay_us(3 * 1000);
82
83     /* Wait for clearance of CIHB and CDIHB Bits */
84     while (esdhc_base_pointer->present_state & ESDHC_CMD_INHIBIT) {
85         if (counter++ > 200) {
86             diag_printf
87                 ("%s: something goes wrong with the DSDHC and int is not received!\n",
88                  __FUNCTION__);
89             counter = 0;
90             break;
91         }
92     }
93
94     /* send 80 clock ticks for card to power up */
95     esdhc_base_pointer->system_control |= ESDHC_SOFTWARE_INIT;
96
97     /* Set data bus width of ESDCH */
98     esdhc_set_data_transfer_width(data_transfer_width);
99
100     /* Set Endianness of ESDHC */
101     esdhc_set_endianness(endian_mode);
102
103 }
104
105 void esdhc_softreset(cyg_uint32 mask)
106 {
107     //wait max timeout 100ms
108     cyg_uint32 timeout = 100;
109
110     esdhc_base_pointer->system_control |= mask;
111
112     /* hw clears the bit when it's done */
113     while (esdhc_base_pointer->system_control & mask) {
114         if (timeout == 0) {
115             flash_dprintf(FLASH_DEBUG_MAX,
116                       "%s:Reset 0x%X never complete!\n");
117             return;
118         }
119         timeout--;
120         hal_delay_us(100);
121     }
122 }
123
124 void host_init(cyg_uint32 base_address)
125 {
126     esdhc_base_pointer = (host_register_ptr) base_address;
127
128     flash_dprintf(FLASH_DEBUG_MAX, "%s: interface_esdc=%d\n", __FUNCTION__,
129               base_address);
130
131     mxc_mmc_init(base_address);
132 }
133
134 void host_cfg_clock(sdhc_freq_t frequency)
135 {
136     unsigned int timeout = 9000;
137     /* Enable ipg_perclk, HCLK enable, IPG Clock enable.  */
138     esdhc_base_pointer->system_control |= ESDHC_CLOCK_ENABLE;
139
140     esdhc_base_pointer->system_control |= 0xe0000;    //set timeout counter
141
142     /* Clear DTOCV SDCLKFS bits, clear SD clk enable bit to change frequency */
143     esdhc_base_pointer->system_control &= ESDHC_FREQ_MASK;
144
145     /* Disable SD clock */
146     esdhc_base_pointer->system_control &= ~ESDHC_ENABLE;
147
148     if (frequency == IDENTIFICATION_FREQ) {
149         /* Input frequecy to eSDHC is 36 MHZ */
150         /* PLL3 is the source of input frequency */
151         /*Set DTOCV and SDCLKFS bit to get SD_CLK of frequency below 400 KHZ (70.31 KHZ) */
152         esdhc_base_pointer->system_control |= ESDHC_IDENT_FREQ;
153     } else if (frequency == OPERATING_FREQ) {
154         /*Set DTOCV and SDCLKFS bit to get SD_CLK of frequency around 25 MHz.(18 MHz) */
155         esdhc_base_pointer->system_control |= ESDHC_OPERT_FREQ;
156     }
157
158     /* Wait for clock to be steady */
159     while (((esdhc_base_pointer->present_state & 0x8) == 0) && (timeout != 0)) {
160         timeout--;
161         hal_delay_us(10);
162     }
163
164     /* Enable SD clock */
165     esdhc_base_pointer->system_control |= ESDHC_ENABLE;
166 }
167
168 static void esdhc_set_data_transfer_width(cyg_uint32 data_transfer_width)
169 {
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
177 static void esdhc_set_endianness(cyg_uint32 endian_mode)
178 {
179
180     /* Set DWT bit of protocol control register according to bus_width */
181     esdhc_base_pointer->protocol_control |= endian_mode;
182
183 }
184
185 cyg_uint32 host_send_cmd(command_t * cmd)
186 {
187
188     /* Clear Interrupt status register */
189     esdhc_base_pointer->interrupt_status = ESDHC_CLEAR_INTERRUPT;
190     //esdhc_base_pointer->interrupt_status = 0x117f01ff;
191
192     /* Enable Interrupt */
193     esdhc_base_pointer->interrupt_status_enable |= ESDHC_INTERRUPT_ENABLE;
194     //esdhc_base_pointer->interrupt_status_enable |= 0x007f0123;
195
196 #if 0
197     if (esdhc_check_for_send_cmd(cmd->data_present)) {
198         diag_printf("Data/Cmd Line Busy.\n");
199         return FAIL;
200     }
201 #endif
202
203     /* Configure Command    */
204     esdhc_cmd_config(cmd);
205
206     /* Wait interrupt (END COMMAND RESPONSE)  */
207     //diag_printf("Wait for CMD Response.\n");
208     if (esdhc_wait_end_cmd_resp_intr()) {
209         diag_printf("Wait CMD (%d) RESPONSE TIMEOUT.\n", cmd->command);
210         return FAIL;
211     }
212     //Just test for Erase functionality:Lewis-20080505:
213     if (cmd->command == CMD38) {
214         flash_dprintf(FLASH_DEBUG_MAX, "%s:Check DAT0 status:\n",
215                   __FUNCTION__);
216         //while(((esdhc_base_pointer->present_state) & 0x01000004)){
217         //   flash_dprintf(FLASH_DEBUG_MAX,".");
218         //   hal_delay_us(1000);
219         //}
220         /* I'm not sure the minimum value of delay */
221         hal_delay_us(100000);
222         hal_delay_us(100000);
223         hal_delay_us(100000);
224         flash_dprintf(FLASH_DEBUG_MAX,
225                   "\nCheck DAT0 status DONE: present_state=%x\n",
226                   (cyg_uint32) (esdhc_base_pointer->present_state));
227     }
228
229     /* Mask all interrupts     */
230     //esdhc_base_pointer->interrupt_signal_enable =0;
231
232     /* Check if an error occured    */
233     return esdhc_check_response();
234 }
235
236 static void esdhc_cmd_config(command_t * cmd)
237 {
238     unsigned int transfer_type;
239
240     /* Write Command Argument in Command Argument Register */
241     esdhc_base_pointer->command_argument = cmd->arg;
242
243     /*    *Configure e-SDHC Register value according to Command    */
244     transfer_type = (((cmd->data_transfer) << DATA_TRANSFER_SHIFT) |
245              ((cmd->response_format) << RESPONSE_FORMAT_SHIFT) |
246              ((cmd->data_present) << DATA_PRESENT_SHIFT) |
247              ((cmd->crc_check) << CRC_CHECK_SHIFT) |
248              ((cmd->cmdindex_check) << CMD_INDEX_CHECK_SHIFT) |
249              ((cmd->command) << CMD_INDEX_SHIFT) |
250              ((cmd->
251                block_count_enable_check) <<
252               BLOCK_COUNT_ENABLE_SHIFT) | ((cmd->
253                             multi_single_block) <<
254                                MULTI_SINGLE_BLOCK_SELECT_SHIFT));
255
256     esdhc_base_pointer->command_transfer_type = transfer_type;
257
258     //diag_printf("arg: 0x%x | tp: 0x%x\n", esdhc_base_pointer->command_argument, esdhc_base_pointer->command_transfer_type);
259
260 }
261
262 static int esdhc_wait_end_cmd_resp_intr(void)
263 {
264     /* Wait interrupt (END COMMAND RESPONSE)  */
265     cyg_uint32 i = 50000;
266     while (!
267            ((esdhc_base_pointer->
268          interrupt_status) & ESDHC_STATUS_END_CMD_RESP_TIME_MSK) && i) {
269         i--;
270         hal_delay_us(10);
271         //diag_printf("0x%x\n", esdhc_base_pointer->interrupt_status);
272     }
273
274     if (!
275         ((esdhc_base_pointer->
276           interrupt_status) & ESDHC_STATUS_END_CMD_RESP_TIME_MSK)) {
277         //diag_printf("%s: can't get END COMMAND RESPONSE! Tried %d times\n", __FUNCTION__, (5000000-i));
278         return FAIL;
279     }
280
281     return SUCCESS;
282 }
283
284 static cyg_uint32 esdhc_check_response(void)
285 {
286     cyg_uint32 status = FAIL;
287
288     /* Check whether the interrupt is an END_CMD_RESP
289      * or a response time out or a CRC error
290      */
291     if ((esdhc_base_pointer->
292          interrupt_status & ESDHC_STATUS_END_CMD_RESP_MSK)
293         && !(esdhc_base_pointer->
294          interrupt_status & ESDHC_STATUS_TIME_OUT_RESP_MSK)
295         && !(esdhc_base_pointer->
296          interrupt_status & ESDHC_STATUS_RESP_CRC_ERR_MSK)
297         && !(esdhc_base_pointer->
298          interrupt_status & ESDHC_STATUS_RESP_INDEX_ERR_MSK)) {
299
300         status = SUCCESS;
301     } else {
302         //diag_printf("Warning: Check CMD response, Intr Status: 0x%x\n", esdhc_base_pointer->interrupt_status);
303         status = FAIL;
304     }
305
306     return status;
307
308 }
309
310 void host_read_response(command_response_t * cmd_resp)
311 {
312     /* get response values from e-SDHC CMDRSP registers. */
313     cmd_resp->cmd_rsp0 = (cyg_uint32) esdhc_base_pointer->command_response0;
314     cmd_resp->cmd_rsp1 = (cyg_uint32) esdhc_base_pointer->command_response1;
315     cmd_resp->cmd_rsp2 = (cyg_uint32) esdhc_base_pointer->command_response2;
316     cmd_resp->cmd_rsp3 = (cyg_uint32) esdhc_base_pointer->command_response3;
317 }
318
319 static void esdhc_wait_buf_rdy_intr(cyg_uint32 mask,
320                     multi_single_block_select
321                     multi_single_block)
322 {
323
324     /* Wait interrupt (BUF_READ_RDY)    */
325
326     cyg_uint32 i;
327     for (i = 3000; i > 0; i--) {
328         if (esdhc_base_pointer->interrupt_status & mask) {
329             break;
330         }
331         hal_delay_us(100);
332     }
333
334     if (multi_single_block == MULTIPLE
335         && esdhc_base_pointer->interrupt_status & mask)
336         esdhc_base_pointer->interrupt_status |= mask;
337     if (i == 0)
338         flash_dprintf(FLASH_DEBUG_MAX, "%s:Debug: tried %d times\n",
339                   __FUNCTION__, (3000 - i));
340
341 }
342
343 static void esdhc_wait_op_done_intr(cyg_uint32 transfer_mask)
344 {
345     /* Wait interrupt (Transfer Complete)    */
346
347     cyg_uint32 i;
348     while (!(esdhc_base_pointer->interrupt_status & transfer_mask)) ;
349
350     //diag_printf("Wait OP Done Failed.\n");
351     //flash_dprintf(FLASH_DEBUG_MAX,"%s:Debug: tried %d times\n", __FUNCTION__, (3001-i));
352
353 }
354
355 static cyg_uint32 esdhc_check_data(cyg_uint32 op_done_mask,
356                    cyg_uint32 read_time_out_mask,
357                    cyg_uint32 read_crc_err_mask)
358 {
359
360     cyg_uint32 status = FAIL;
361
362     /* Check whether the interrupt is an OP_DONE
363      * or a data time out or a CRC error     */
364     if ((esdhc_base_pointer->interrupt_status & op_done_mask) &&
365         !(esdhc_base_pointer->interrupt_status & read_time_out_mask) &&
366         !(esdhc_base_pointer->interrupt_status & read_crc_err_mask)) {
367         status = SUCCESS;
368     } else {
369         status = FAIL;
370         //diag_printf("Warning: Check data, interrupt_status=%X\n", (esdhc_base_pointer->interrupt_status));
371     }
372
373     return status;
374 }
375
376 void host_cfg_block(cyg_uint32 blk_len, cyg_uint32 nob)
377 {
378     /* Configre block Attributes register */
379     esdhc_base_pointer->block_attributes =
380         ((nob << 16) | (blk_len & 0xffff));
381
382     //diag_printf("nob: 0x%x, block_attributes: 0x%x\n", nob, esdhc_base_pointer->block_attributes);
383
384     /* Set Read Water Mark Level register */
385     esdhc_base_pointer->watermark_level = WRITE_READ_WATER_MARK_LEVEL;
386 }
387
388 cyg_uint32 host_data_read(cyg_uint32 * dest_ptr, cyg_uint32 read_len)
389 {
390     cyg_uint32 j, k;
391     cyg_uint32 status = FAIL;
392     unsigned int len = WRITE_READ_WATER_MARK_LEVEL & 0xff;
393     //int counter = 0;
394
395     /* Enable Interrupt */
396     esdhc_base_pointer->interrupt_status_enable |= ESDHC_INTERRUPT_ENABLE;
397
398     for (j = 0; j < read_len / (len * 4); j++) {
399         //StartCounter();
400         /* wait for read fifo full (equal or beyond the watermark) */
401         while (!(esdhc_base_pointer->present_state & (1 << 11))) ;
402
403         //counter = StopCounter();
404         //diag_printf("counter: 0x%x\n", counter);
405
406         for (k = 0; k < len; k++) {
407             *dest_ptr++ = esdhc_base_pointer->data_buffer_access;
408         }
409     }
410
411     /* Wait for transfer complete operation interrupt */
412     esdhc_wait_op_done_intr(ESDHC_STATUS_TRANSFER_COMPLETE_MSK);
413
414     /* Check for status errors */
415     status =
416         esdhc_check_data(ESDHC_STATUS_TRANSFER_COMPLETE_MSK,
417                  ESDHC_STATUS_TIME_OUT_READ, ESDHC_STATUS_READ_CRC_ERR_MSK);
418
419     return status;
420
421 }
422
423 cyg_uint32 host_data_write(cyg_uint32 * src_ptr, cyg_uint32 write_len)
424 {
425     cyg_uint32 i = 0, k;
426     cyg_uint32 status = FAIL;
427     unsigned int len = (WRITE_READ_WATER_MARK_LEVEL >> 16) & 0xff;
428     //cyg_uint32 counter = 0;
429
430     /* Enable Interrupt */
431     esdhc_base_pointer->interrupt_status_enable |= ESDHC_INTERRUPT_ENABLE;
432
433     //StartCounter();
434     for (i = 0; i < (write_len) / (len * 4); i++) {
435         /* wait for write fifo empty (equal or less than the watermark), BWEN */
436         while (!(esdhc_base_pointer->present_state & (1 << 10))) ;
437
438         for (k = 0; k < len; k++) {
439             esdhc_base_pointer->data_buffer_access = *src_ptr++;
440         }
441
442     }
443
444     /* Wait for transfer complete operation interrupt */
445     esdhc_wait_op_done_intr(ESDHC_STATUS_TRANSFER_COMPLETE_MSK);
446
447     //counter = StopCounter();
448     //diag_printf("0x%x\n", counter);
449
450     /* Check for status errors */
451     status =
452         esdhc_check_data(ESDHC_STATUS_TRANSFER_COMPLETE_MSK,
453                  ESDHC_STATUS_TIME_OUT_READ, ESDHC_STATUS_READ_CRC_ERR_MSK);
454
455     return status;
456
457 }
458
459 static int esdhc_check_for_send_cmd(int data_present)
460 {
461
462     int status = SUCCESS;
463     int counter;
464
465     /* Wait for the command line to be free (poll the CIHB bit of
466      * the present state register.
467      */
468     counter = 1000;
469     while (((esdhc_base_pointer->present_state & 0x1) == 0x1) && counter--) {
470         hal_delay_us(10);
471     }
472
473     if (!counter)
474         return FAIL;
475
476     /* Wait for the data line to be free (poll the CDIHB bit of
477      * the present state register.
478      */
479     counter = 1000;
480     if (data_present == DATA_PRESENT) {
481         while (((esdhc_base_pointer->present_state & 0x2) == 0x2) && counter--) {
482             hal_delay_us(10);
483         }
484
485     }
486
487     if (!counter)
488         return FAIL;
489
490     return status;
491 }