blob: ea8983f40801ca8ab0a7f0ab4d3f24d5bbc8d52b [file] [log] [blame]
Amol Jadi84a546a2011-03-02 12:09:11 -08001/*
Duy Truongf3ac7b32013-02-13 01:07:28 -08002 * Copyright (c) 2011, The Linux Foundation. All rights reserved.
Amol Jadi84a546a2011-03-02 12:09:11 -08003 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above
10 * copyright notice, this list of conditions and the following
11 * disclaimer in the documentation and/or other materials provided
12 * with the distribution.
Duy Truongf3ac7b32013-02-13 01:07:28 -080013 * * Neither the name of The Linux Foundation nor the names of its
Amol Jadi84a546a2011-03-02 12:09:11 -080014 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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
24 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
Amol Jadi84a546a2011-03-02 12:09:11 -080030#include <stdlib.h>
31#include <reg.h>
32
33#include "adm.h"
34#include <platform/adm.h>
35
36/* TODO: This ADM implementation is very specific for use by MMC.
37 * Replace with a generic ADM driver that can also be used
38 * by other peripherals such as usb/uart/nand/display etc.
39 */
40
Amol Jadi84a546a2011-03-02 12:09:11 -080041/* TODO:
42 * adm module shouldn't have to include mmc.h.
43 * clean this up when generic adm interface is implemented.
44 */
45#include "mmc.h"
46
47extern void mdelay(unsigned msecs);
48extern void dmb(void);
49
50/* limit the max_row_len to fifo size so that
51 * the same src and dst row attributes can be used.
52 */
53#define MAX_ROW_LEN MMC_BOOT_MCI_FIFO_SIZE
54#define MAX_ROW_NUM 0xFFFF
55
56/* Structures for use with ADM:
57 * Must be aligned on 8 byte boundary.
58 */
59static uint32_t adm_cmd_ptr_list[8] __attribute__ ((aligned(8)));
Ajay Dudanib01e5062011-12-03 23:23:42 -080060static uint32_t box_mode_entry[8] __attribute__ ((aligned(8)));
Amol Jadi84a546a2011-03-02 12:09:11 -080061
Ajay Dudanib01e5062011-12-03 23:23:42 -080062adm_result_t adm_transfer_start(uint32_t adm_chn, uint32_t * cmd_ptr_list);
Amol Jadi84a546a2011-03-02 12:09:11 -080063
64/* CRCI - mmc slot mapping. */
65extern uint8_t sdc_crci_map[5];
66
Amol Jadi84a546a2011-03-02 12:09:11 -080067/* TODO:
68 * This interface is very specific to MMC.
69 * We need a generic ADM interface that can be easily
70 * used by other modules such as usb/uart/nand.
71 */
Ajay Dudanib01e5062011-12-03 23:23:42 -080072adm_result_t
73adm_transfer_mmc_data(unsigned char slot,
74 unsigned char *data_ptr,
75 unsigned int data_len, unsigned char direction)
Amol Jadi84a546a2011-03-02 12:09:11 -080076{
77 uint32_t num_rows;
78 uint16_t row_len;
79 uint16_t row_offset;
80 uint32_t row_num;
81 uint32_t adm_crci_num;
82 adm_result_t result = ADM_RESULT_SUCCESS;
83
Amol Jadi84a546a2011-03-02 12:09:11 -080084 /* Make sure slot value is in the range 1..4 */
Ajay Dudanib01e5062011-12-03 23:23:42 -080085 ASSERT((slot >= 1) && (slot <= 4));
Amol Jadi84a546a2011-03-02 12:09:11 -080086
Ajay Dudanib01e5062011-12-03 23:23:42 -080087 adm_crci_num = sdc_crci_map[slot];
88 row_len = MMC_BOOT_MCI_FIFO_SIZE;
89 num_rows = data_len / MMC_BOOT_MCI_FIFO_SIZE;
Amol Jadi84a546a2011-03-02 12:09:11 -080090
91 /* While there is data to be transferred */
Ajay Dudanib01e5062011-12-03 23:23:42 -080092 while (data_len) {
93 if (data_len <= MAX_ROW_LEN) {
94 row_len = data_len;
Amol Jadi84a546a2011-03-02 12:09:11 -080095 row_offset = 0;
Ajay Dudanib01e5062011-12-03 23:23:42 -080096 row_num = 1;
97 } else {
98 row_len = MAX_ROW_LEN;
Amol Jadi84a546a2011-03-02 12:09:11 -080099 row_offset = MAX_ROW_LEN;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800100 row_num = data_len / MAX_ROW_LEN;
Amol Jadi84a546a2011-03-02 12:09:11 -0800101
102 /* Limit the number of row to the max value allowed */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800103 if (row_num > MAX_ROW_NUM) {
Amol Jadi84a546a2011-03-02 12:09:11 -0800104 row_num = MAX_ROW_NUM;
105 }
106 }
107
108 /* Program ADM registers and initiate data transfer */
109
110 /* Initialize the Box Mode command entry (single entry) */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800111 box_mode_entry[0] = (ADM_CMD_LIST_LC |
112 (adm_crci_num << 3) | ADM_ADDR_MODE_BOX);
Amol Jadi84a546a2011-03-02 12:09:11 -0800113
Ajay Dudanib01e5062011-12-03 23:23:42 -0800114 if (direction == ADM_MMC_READ) {
115 box_mode_entry[1] = MMC_BOOT_MCI_FIFO; /* SRC addr */
116 box_mode_entry[2] = (uint32_t) data_ptr; /* DST addr */
117 box_mode_entry[3] = ((row_len << 16) | /* SRC row len */
118 (row_len << 0)); /* DST row len */
119 box_mode_entry[4] = ((row_num << 16) | /* SRC row # */
120 (row_num << 0)); /* DST row # */
121 box_mode_entry[5] = ((0 << 16) | /* SRC offset */
122 (row_offset << 0)); /* DST offset */
123 } else {
124 box_mode_entry[1] = (uint32_t) data_ptr; /* SRC addr */
125 box_mode_entry[2] = MMC_BOOT_MCI_FIFO; /* DST addr */
126 box_mode_entry[3] = ((row_len << 16) | /* SRC row len */
127 (row_len << 0)); /* DST row len */
128 box_mode_entry[4] = ((row_num << 16) | /* SRC row # */
129 (row_num << 0)); /* DST row # */
130 box_mode_entry[5] = ((row_offset << 16) | /* SRC offset */
131 (0 << 0)); /* DST offset */
Amol Jadi84a546a2011-03-02 12:09:11 -0800132 }
133
134 /* Initialize the ADM Command Pointer List (single entry) */
135 adm_cmd_ptr_list[0] = (ADM_CMD_PTR_LP |
136 ADM_CMD_PTR_CMD_LIST |
Ajay Dudanib01e5062011-12-03 23:23:42 -0800137 (((uint32_t) (&box_mode_entry[0])) >>
138 3));
Amol Jadi84a546a2011-03-02 12:09:11 -0800139
140 /* Start ADM transfer, this is a blocking call. */
141 result = adm_transfer_start(ADM_CHN, adm_cmd_ptr_list);
142
Ajay Dudanib01e5062011-12-03 23:23:42 -0800143 if (result != ADM_RESULT_SUCCESS) {
Amol Jadi84a546a2011-03-02 12:09:11 -0800144 break;
145 }
146
147 /* Update the data ptr and data len by the amount
148 * we just transferred.
149 */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800150 data_ptr += (row_len * row_num);
151 data_len -= (row_len * row_num);
Amol Jadi84a546a2011-03-02 12:09:11 -0800152 }
153
154 return result;
155}
156
157/*
158 * Start the ADM data transfer and return the result of the transfer.
159 * Blocks until transfer is completed.
160 */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800161adm_result_t adm_transfer_start(uint32_t adm_chn, uint32_t * cmd_ptr_list)
Amol Jadi84a546a2011-03-02 12:09:11 -0800162{
163 uint32_t reg_value;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800164 uint32_t timeout = 1;
Amol Jadi84a546a2011-03-02 12:09:11 -0800165 uint32_t delay_count = 100;
166
Amol Jadi84a546a2011-03-02 12:09:11 -0800167 /* Memory barrier to ensure that all ADM command list structure
168 * writes have completed before starting the ADM transfer.
169 */
170 dmb();
171
172 /* Start the ADM transfer by writing the command ptr */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800173 writel(((uint32_t) cmd_ptr_list) >> 3,
174 ADM_REG_CMD_PTR(adm_chn, ADM_SD));
Amol Jadi84a546a2011-03-02 12:09:11 -0800175
176 /* Poll the status register to check for transfer complete.
177 * Bail out if transfer is not finished within 1 sec.
178 * Note: This time depends on the amount of data being transferred.
179 * Increase the delay_count if this is not sufficient.
180 */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800181 do {
Amol Jadi84a546a2011-03-02 12:09:11 -0800182 reg_value = readl(ADM_REG_STATUS(adm_chn, ADM_SD));
Ajay Dudanib01e5062011-12-03 23:23:42 -0800183 if ((reg_value & ADM_REG_STATUS__RSLT_VLD___M) != 0) {
Amol Jadi84a546a2011-03-02 12:09:11 -0800184 timeout = 0;
185 break;
186 }
187
188 /* 10ms wait */
189 mdelay(10);
190
Ajay Dudanib01e5062011-12-03 23:23:42 -0800191 }
192 while (delay_count--);
Amol Jadi84a546a2011-03-02 12:09:11 -0800193
194 /* Read out the IRQ register to clear the interrupt.
195 * Even though we are not using interrupts,
196 * kernel is not clearing the interupts during its
197 * ADM initialization, causing it to crash.
198 */
199 reg_value = readl(ADM_REG_IRQ(ADM_SD));
200
Ajay Dudanib01e5062011-12-03 23:23:42 -0800201 if (timeout) {
Amol Jadi84a546a2011-03-02 12:09:11 -0800202 return ADM_RESULT_TIMEOUT;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800203 } else {
Amol Jadi84a546a2011-03-02 12:09:11 -0800204 /* Get the result from the RSLT FIFO */
205 reg_value = readl(ADM_REG_RSLT(adm_chn, ADM_SD));
206
207 /* Check for any error */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800208 if (((reg_value & ADM_REG_RSLT__ERR___M) != 0) ||
209 ((reg_value & ADM_REG_RSLT__TPD___M) == 0) ||
210 ((reg_value & ADM_REG_RSLT__V___M) == 0)) {
Amol Jadi84a546a2011-03-02 12:09:11 -0800211 return ADM_RESULT_FAILURE;
212 }
213 }
214
215 return ADM_RESULT_SUCCESS;
216}