Amol Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 1 | /* |
Duy Truong | f3ac7b3 | 2013-02-13 01:07:28 -0800 | [diff] [blame] | 2 | * Copyright (c) 2011, The Linux Foundation. All rights reserved. |
Amol Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 3 | * |
| 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 Truong | f3ac7b3 | 2013-02-13 01:07:28 -0800 | [diff] [blame] | 13 | * * Neither the name of The Linux Foundation nor the names of its |
Amol Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 14 | * 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 Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 30 | #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 Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 41 | /* 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 | |
| 47 | extern void mdelay(unsigned msecs); |
| 48 | extern 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 | */ |
| 59 | static uint32_t adm_cmd_ptr_list[8] __attribute__ ((aligned(8))); |
Ajay Dudani | b01e506 | 2011-12-03 23:23:42 -0800 | [diff] [blame] | 60 | static uint32_t box_mode_entry[8] __attribute__ ((aligned(8))); |
Amol Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 61 | |
Ajay Dudani | b01e506 | 2011-12-03 23:23:42 -0800 | [diff] [blame] | 62 | adm_result_t adm_transfer_start(uint32_t adm_chn, uint32_t * cmd_ptr_list); |
Amol Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 63 | |
| 64 | /* CRCI - mmc slot mapping. */ |
| 65 | extern uint8_t sdc_crci_map[5]; |
| 66 | |
Amol Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 67 | /* 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 Dudani | b01e506 | 2011-12-03 23:23:42 -0800 | [diff] [blame] | 72 | adm_result_t |
| 73 | adm_transfer_mmc_data(unsigned char slot, |
| 74 | unsigned char *data_ptr, |
| 75 | unsigned int data_len, unsigned char direction) |
Amol Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 76 | { |
| 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 Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 84 | /* Make sure slot value is in the range 1..4 */ |
Ajay Dudani | b01e506 | 2011-12-03 23:23:42 -0800 | [diff] [blame] | 85 | ASSERT((slot >= 1) && (slot <= 4)); |
Amol Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 86 | |
Ajay Dudani | b01e506 | 2011-12-03 23:23:42 -0800 | [diff] [blame] | 87 | 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 Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 90 | |
| 91 | /* While there is data to be transferred */ |
Ajay Dudani | b01e506 | 2011-12-03 23:23:42 -0800 | [diff] [blame] | 92 | while (data_len) { |
| 93 | if (data_len <= MAX_ROW_LEN) { |
| 94 | row_len = data_len; |
Amol Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 95 | row_offset = 0; |
Ajay Dudani | b01e506 | 2011-12-03 23:23:42 -0800 | [diff] [blame] | 96 | row_num = 1; |
| 97 | } else { |
| 98 | row_len = MAX_ROW_LEN; |
Amol Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 99 | row_offset = MAX_ROW_LEN; |
Ajay Dudani | b01e506 | 2011-12-03 23:23:42 -0800 | [diff] [blame] | 100 | row_num = data_len / MAX_ROW_LEN; |
Amol Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 101 | |
| 102 | /* Limit the number of row to the max value allowed */ |
Ajay Dudani | b01e506 | 2011-12-03 23:23:42 -0800 | [diff] [blame] | 103 | if (row_num > MAX_ROW_NUM) { |
Amol Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 104 | 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 Dudani | b01e506 | 2011-12-03 23:23:42 -0800 | [diff] [blame] | 111 | box_mode_entry[0] = (ADM_CMD_LIST_LC | |
| 112 | (adm_crci_num << 3) | ADM_ADDR_MODE_BOX); |
Amol Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 113 | |
Ajay Dudani | b01e506 | 2011-12-03 23:23:42 -0800 | [diff] [blame] | 114 | 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 Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 132 | } |
| 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 Dudani | b01e506 | 2011-12-03 23:23:42 -0800 | [diff] [blame] | 137 | (((uint32_t) (&box_mode_entry[0])) >> |
| 138 | 3)); |
Amol Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 139 | |
| 140 | /* Start ADM transfer, this is a blocking call. */ |
| 141 | result = adm_transfer_start(ADM_CHN, adm_cmd_ptr_list); |
| 142 | |
Ajay Dudani | b01e506 | 2011-12-03 23:23:42 -0800 | [diff] [blame] | 143 | if (result != ADM_RESULT_SUCCESS) { |
Amol Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 144 | break; |
| 145 | } |
| 146 | |
| 147 | /* Update the data ptr and data len by the amount |
| 148 | * we just transferred. |
| 149 | */ |
Ajay Dudani | b01e506 | 2011-12-03 23:23:42 -0800 | [diff] [blame] | 150 | data_ptr += (row_len * row_num); |
| 151 | data_len -= (row_len * row_num); |
Amol Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 152 | } |
| 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 Dudani | b01e506 | 2011-12-03 23:23:42 -0800 | [diff] [blame] | 161 | adm_result_t adm_transfer_start(uint32_t adm_chn, uint32_t * cmd_ptr_list) |
Amol Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 162 | { |
| 163 | uint32_t reg_value; |
Ajay Dudani | b01e506 | 2011-12-03 23:23:42 -0800 | [diff] [blame] | 164 | uint32_t timeout = 1; |
Amol Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 165 | uint32_t delay_count = 100; |
| 166 | |
Amol Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 167 | /* 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 Dudani | b01e506 | 2011-12-03 23:23:42 -0800 | [diff] [blame] | 173 | writel(((uint32_t) cmd_ptr_list) >> 3, |
| 174 | ADM_REG_CMD_PTR(adm_chn, ADM_SD)); |
Amol Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 175 | |
| 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 Dudani | b01e506 | 2011-12-03 23:23:42 -0800 | [diff] [blame] | 181 | do { |
Amol Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 182 | reg_value = readl(ADM_REG_STATUS(adm_chn, ADM_SD)); |
Ajay Dudani | b01e506 | 2011-12-03 23:23:42 -0800 | [diff] [blame] | 183 | if ((reg_value & ADM_REG_STATUS__RSLT_VLD___M) != 0) { |
Amol Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 184 | timeout = 0; |
| 185 | break; |
| 186 | } |
| 187 | |
| 188 | /* 10ms wait */ |
| 189 | mdelay(10); |
| 190 | |
Ajay Dudani | b01e506 | 2011-12-03 23:23:42 -0800 | [diff] [blame] | 191 | } |
| 192 | while (delay_count--); |
Amol Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 193 | |
| 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 Dudani | b01e506 | 2011-12-03 23:23:42 -0800 | [diff] [blame] | 201 | if (timeout) { |
Amol Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 202 | return ADM_RESULT_TIMEOUT; |
Ajay Dudani | b01e506 | 2011-12-03 23:23:42 -0800 | [diff] [blame] | 203 | } else { |
Amol Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 204 | /* 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 Dudani | b01e506 | 2011-12-03 23:23:42 -0800 | [diff] [blame] | 208 | 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 Jadi | 84a546a | 2011-03-02 12:09:11 -0800 | [diff] [blame] | 211 | return ADM_RESULT_FAILURE; |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | return ADM_RESULT_SUCCESS; |
| 216 | } |