paul@258 | 1 | /* |
paul@258 | 2 | * MSC (MMC/SD controller) peripheral support. |
paul@258 | 3 | * |
paul@258 | 4 | * Copyright (C) 2024 Paul Boddie <paul@boddie.org.uk> |
paul@258 | 5 | * |
paul@258 | 6 | * This program is free software; you can redistribute it and/or |
paul@258 | 7 | * modify it under the terms of the GNU General Public License as |
paul@258 | 8 | * published by the Free Software Foundation; either version 2 of |
paul@258 | 9 | * the License, or (at your option) any later version. |
paul@258 | 10 | * |
paul@258 | 11 | * This program is distributed in the hope that it will be useful, |
paul@258 | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
paul@258 | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
paul@258 | 14 | * GNU General Public License for more details. |
paul@258 | 15 | * |
paul@258 | 16 | * You should have received a copy of the GNU General Public License |
paul@258 | 17 | * along with this program; if not, write to the Free Software |
paul@258 | 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, |
paul@258 | 19 | * Boston, MA 02110-1301, USA |
paul@258 | 20 | */ |
paul@258 | 21 | |
paul@258 | 22 | #include <l4/devices/hw_mmio_register_block.h> |
paul@258 | 23 | #include <l4/sys/irq.h> |
paul@258 | 24 | #include <l4/util/util.h> |
paul@258 | 25 | |
paul@258 | 26 | #include <stdio.h> |
paul@258 | 27 | |
paul@258 | 28 | #include "msc-common.h" |
paul@258 | 29 | |
paul@258 | 30 | |
paul@258 | 31 | |
paul@258 | 32 | // Register locations for each channel. |
paul@258 | 33 | |
paul@258 | 34 | enum Regs : unsigned |
paul@258 | 35 | { |
paul@258 | 36 | Msc_control = 0x000, // MSC_CTRL |
paul@258 | 37 | Msc_status = 0x004, // MSC_STAT |
paul@258 | 38 | Msc_clock_rate = 0x008, // MSC_CLKRT |
paul@258 | 39 | Msc_command_data_control = 0x00c, // MSC_CMDAT |
paul@258 | 40 | Msc_response_timeout = 0x010, // MSC_RESTO |
paul@258 | 41 | Msc_read_timeout = 0x014, // MSC_RDTO |
paul@258 | 42 | Msc_block_length = 0x018, // MSC_BLKLEN |
paul@258 | 43 | Msc_block_count = 0x01c, // MSC_NOB |
paul@258 | 44 | Msc_block_success_count = 0x020, // MSC_SNOB |
paul@258 | 45 | Msc_interrupt_mask = 0x024, // MSC_IMASK |
paul@258 | 46 | Msc_interrupt_flag = 0x028, // MSC_IFLG/MSC_IREG |
paul@258 | 47 | Msc_command_index = 0x02c, // MSC_CMD |
paul@258 | 48 | Msc_command_argument = 0x030, // MSC_ARG |
paul@258 | 49 | Msc_response_fifo = 0x034, // MSC_RES |
paul@258 | 50 | Msc_recv_data_fifo = 0x038, // MSC_RXFIFO |
paul@258 | 51 | Msc_trans_data_fifo = 0x03c, // MSC_TXFIFO |
paul@258 | 52 | |
paul@258 | 53 | // JZ4780/X1600 only. |
paul@258 | 54 | |
paul@258 | 55 | Msc_low_power_mode = 0x040, // MSC_LPM |
paul@258 | 56 | Msc_dma_control = 0x044, // MSC_DMAC |
paul@258 | 57 | Msc_dma_descriptor_address = 0x048, // MSC_DMANDA |
paul@258 | 58 | Msc_dma_data_address = 0x04c, // MSC_DMADA |
paul@258 | 59 | Msc_dma_data_length = 0x050, // MSC_DMALEN |
paul@258 | 60 | Msc_dma_command = 0x054, // MSC_DMACMD |
paul@258 | 61 | Msc_control2 = 0x058, // MSC_CTRL2 |
paul@258 | 62 | Msc_rtfifo_data_counter = 0x05c, // MSC_RTCNT |
paul@258 | 63 | |
paul@258 | 64 | // Channel block size/offset. |
paul@258 | 65 | |
paul@258 | 66 | Msc_channel_offset = 0x10000, |
paul@258 | 67 | }; |
paul@258 | 68 | |
paul@258 | 69 | // Field definitions. |
paul@258 | 70 | |
paul@258 | 71 | enum Control_bits : unsigned |
paul@258 | 72 | { |
paul@258 | 73 | // JZ4780/X1600 only. |
paul@258 | 74 | |
paul@258 | 75 | Control_send_ccsd = 0x8000, // SEND_CCSD |
paul@258 | 76 | Control_send_ccsd_automatically = 0x4000, // SEND_CCSD |
paul@258 | 77 | |
paul@258 | 78 | // Common. |
paul@258 | 79 | |
paul@258 | 80 | Control_exit_multiple = 0x0080, // EXIT_MULTIPLE |
paul@258 | 81 | Control_exit_transfer = 0x0040, // EXIT_TRANSFER |
paul@258 | 82 | Control_start_read_wait = 0x0020, // START_READ_WAIT |
paul@258 | 83 | Control_stop_read_wait = 0x0010, // STOP_READ_WAIT |
paul@258 | 84 | Control_reset = 0x0008, // RESET |
paul@258 | 85 | Control_start_operation = 0x0004, // START_OP |
paul@258 | 86 | |
paul@258 | 87 | Control_clock_control_field_mask = 0x3, // CLOCK_CTRL |
paul@258 | 88 | Control_clock_control_start = 2, |
paul@258 | 89 | Control_clock_control_stop = 1, |
paul@258 | 90 | Control_clock_control_field_shift = 0, |
paul@258 | 91 | }; |
paul@258 | 92 | |
paul@258 | 93 | enum Control2_bits : unsigned |
paul@258 | 94 | { |
paul@258 | 95 | // JZ4780/X1600 only. |
paul@258 | 96 | |
paul@258 | 97 | Control2_pin_level_polarity_field_mask = 0x1f, // PIP |
paul@258 | 98 | Control2_pin_level_polarity_field_shift = 24, |
paul@258 | 99 | |
paul@258 | 100 | // JZ4780 only. |
paul@258 | 101 | |
paul@258 | 102 | Control2_reset_enable = 0x00800000, // RST_EN |
paul@258 | 103 | |
paul@258 | 104 | // JZ4780/X1600 only. |
paul@258 | 105 | |
paul@258 | 106 | Control2_stop_read_operation_mode = 0x00000010, // STPRM |
paul@258 | 107 | |
paul@258 | 108 | // JZ4780 only. |
paul@258 | 109 | |
paul@258 | 110 | Control2_signal_voltage_change = 0x00000008, // SVC |
paul@258 | 111 | |
paul@258 | 112 | // JZ4780/X1600 only. |
paul@258 | 113 | |
paul@258 | 114 | Control2_speed_mode_field_mask = 0x7, // SMS |
paul@258 | 115 | Control2_speed_mode_default = 0, // = 0 |
paul@258 | 116 | Control2_speed_mode_high = 1, // = 1 |
paul@258 | 117 | Control2_speed_mode_sdr12 = 2, // = 2 |
paul@258 | 118 | Control2_speed_mode_sdr25 = 3, // = 3 |
paul@258 | 119 | Control2_speed_mode_sdr50 = 4, // = 4 |
paul@258 | 120 | Control2_speed_mode_field_shift = 0, |
paul@258 | 121 | }; |
paul@258 | 122 | |
paul@258 | 123 | enum Status_bits : unsigned |
paul@258 | 124 | { |
paul@258 | 125 | // JZ4780/X1600 only. |
paul@258 | 126 | |
paul@258 | 127 | Status_auto_cmd12_done = 0x80000000, // AUTO_CMD12_DONE |
paul@258 | 128 | |
paul@258 | 129 | // JZ4780 only. |
paul@258 | 130 | |
paul@258 | 131 | Status_auto_cmd23_done = 0x40000000, // AUTO_CMD23_DONE |
paul@258 | 132 | Status_signal_voltage_change = 0x20000000, // SVS |
paul@258 | 133 | |
paul@258 | 134 | // JZ4780/X1600 only. |
paul@258 | 135 | |
paul@258 | 136 | Status_pin_level_field_mask = 0x1f, // PIN_LEVEL |
paul@258 | 137 | Status_pin_level_field_shift = 24, |
paul@258 | 138 | |
paul@258 | 139 | Status_boot_crc_error = 0x00100000, // BCE |
paul@258 | 140 | Status_boot_data_end = 0x00080000, // BDE |
paul@258 | 141 | Status_boot_ack_error = 0x00040000, // BAE |
paul@258 | 142 | Status_boot_ack_received = 0x00020000, // BAR |
paul@258 | 143 | Status_dma_end = 0x00010000, // DMAEND |
paul@258 | 144 | |
paul@258 | 145 | // Common. |
paul@258 | 146 | |
paul@258 | 147 | Status_resetting = 0x8000, // IS_RESETTING |
paul@258 | 148 | Status_sdio_interrupt_active = 0x4000, // SDIO_INT_ACTIVE |
paul@258 | 149 | Status_programming_done = 0x2000, // PRG_DONE |
paul@258 | 150 | Status_data_transfer_done = 0x1000, // DATA_TRAN_DONE |
paul@258 | 151 | Status_end_command_response = 0x0800, // END_CMD_RES |
paul@258 | 152 | Status_data_fifo_almost_full = 0x0400, // DATA_FIFO_AFULL |
paul@258 | 153 | Status_read_wait = 0x0200, // IS_READWAIT |
paul@258 | 154 | Status_clock_enabled = 0x0100, // CLK_EN |
paul@258 | 155 | Status_data_fifo_full = 0x0080, // DATA_FIFO_FULL |
paul@258 | 156 | Status_data_fifo_empty = 0x0040, // DATA_FIFO_EMPTY |
paul@258 | 157 | Status_response_crc_error = 0x0020, // CRC_RES_ERR |
paul@258 | 158 | Status_read_crc_error = 0x0010, // CRC_READ_ERROR |
paul@258 | 159 | Status_write_crc_error_no_status = 0x0008, // CRC_WRITE_ERROR (2) |
paul@258 | 160 | Status_write_crc_error_data = 0x0004, // CRC_WRITE_ERROR (1) |
paul@258 | 161 | Status_timeout_response = 0x0002, // TIME_OUT_RES |
paul@258 | 162 | Status_timeout_read = 0x0001, // TIME_OUT_READ |
paul@258 | 163 | }; |
paul@258 | 164 | |
paul@258 | 165 | enum Clock_rate_bits : unsigned |
paul@258 | 166 | { |
paul@258 | 167 | Clock_rate_field_mask = 0x7, // CLK_RATE |
paul@258 | 168 | Clock_rate_field_shift = 0, |
paul@258 | 169 | }; |
paul@258 | 170 | |
paul@258 | 171 | enum Command_data_control_bits : unsigned |
paul@258 | 172 | { |
paul@258 | 173 | // JZ4780/X1600 only. |
paul@258 | 174 | |
paul@258 | 175 | Cdc_ccs_expected = 0x80000000, // CCS_EXPECTED |
paul@258 | 176 | Cdc_read_ce_ata = 0x40000000, // READ_CEATA |
paul@258 | 177 | Cdc_disable_boot = 0x08000000, // DIS_BOOT |
paul@258 | 178 | Cdc_expect_boot_ack = 0x02000000, // EXP_BOOT_ACK |
paul@258 | 179 | Cdc_alternative_boot_mode = 0x01000000, // BOOT_MODE |
paul@258 | 180 | |
paul@258 | 181 | // JZ4780 only. |
paul@258 | 182 | |
paul@258 | 183 | Cdc_auto_cmd23 = 0x00040000, // AUTO_CMD23 |
paul@258 | 184 | |
paul@258 | 185 | // JZ4780/X1600 only. |
paul@258 | 186 | |
paul@258 | 187 | Cdc_sdio_interrupt_2cycle = 0x00020000, // SDIO_PRDT |
paul@258 | 188 | Cdc_auto_cmd12 = 0x00010000, // AUTO_CMD12 |
paul@258 | 189 | |
paul@258 | 190 | Cdc_recv_fifo_level_field_mask = 0x3, // RTRG |
paul@258 | 191 | Cdc_fifo_level_16 = 0, |
paul@258 | 192 | Cdc_fifo_level_32 = 1, |
paul@258 | 193 | Cdc_fifo_level_64 = 2, |
paul@258 | 194 | Cdc_fifo_level_96 = 3, |
paul@258 | 195 | Cdc_recv_fifo_level_field_shift = 14, |
paul@258 | 196 | |
paul@258 | 197 | Cdc_trans_fifo_level_field_mask = 0x3, // TTRG |
paul@258 | 198 | Cdc_trans_fifo_level_field_shift = 12, |
paul@258 | 199 | |
paul@258 | 200 | // Common. |
paul@258 | 201 | |
paul@258 | 202 | Cdc_io_abort = 0x0800, // IO_ABORT |
paul@258 | 203 | |
paul@258 | 204 | Cdc_bus_width_field_mask = 0x3, // BUS_WIDTH |
paul@258 | 205 | Cdc_bus_width_field_1bit = 0, // = 0 |
paul@258 | 206 | Cdc_bus_width_field_4bit = 2, // = 2 |
paul@258 | 207 | Cdc_bus_width_field_shift = 9, |
paul@258 | 208 | |
paul@258 | 209 | // JZ4740 only. |
paul@258 | 210 | |
paul@258 | 211 | Cdc_dma_enable = 0x0100, // DMA_EN |
paul@258 | 212 | Cdc_dma_disable = 0x0000, |
paul@258 | 213 | |
paul@258 | 214 | // Common. |
paul@258 | 215 | |
paul@258 | 216 | Cdc_init_sequence = 0x0080, // INIT |
paul@258 | 217 | |
paul@258 | 218 | Cdc_expect_busy = 0x0040, // BUSY |
paul@258 | 219 | Cdc_do_not_expect_busy = 0x0000, |
paul@258 | 220 | |
paul@258 | 221 | Cdc_stream_block = 0x0020, // STREAM_BLOCK |
paul@258 | 222 | Cdc_not_stream_block = 0x0000, |
paul@258 | 223 | |
paul@258 | 224 | Cdc_write_operation = 0x0010, // WRITE_READ |
paul@258 | 225 | Cdc_read_operation = 0x0000, |
paul@258 | 226 | |
paul@258 | 227 | Cdc_data_with_command = 0x0008, // DATA_EN |
paul@258 | 228 | Cdc_no_data_with_command = 0x0000, |
paul@258 | 229 | |
paul@258 | 230 | Cdc_response_format_field_mask = 0x7, // RESPONSE_FORMAT |
paul@258 | 231 | Cdc_response_format_field_shift = 0, |
paul@258 | 232 | }; |
paul@258 | 233 | |
paul@258 | 234 | enum Response_timeout_bits : unsigned |
paul@258 | 235 | { |
paul@258 | 236 | // NOTE: 16-bit value in the JZ4780. |
paul@258 | 237 | // NOTE: 32-bit value in the X1600. |
paul@258 | 238 | |
paul@258 | 239 | Response_timeout_mask = 0x000000ff, // RES_TO |
paul@258 | 240 | }; |
paul@258 | 241 | |
paul@258 | 242 | enum Read_timeout_bits : unsigned |
paul@258 | 243 | { |
paul@258 | 244 | // NOTE: 16-bit value prior to the JZ4780/X1600. |
paul@258 | 245 | |
paul@258 | 246 | Read_timeout_mask = 0xffffffff, // READ_TO |
paul@258 | 247 | }; |
paul@258 | 248 | |
paul@258 | 249 | enum Block_length_bits : unsigned |
paul@258 | 250 | { |
paul@258 | 251 | // NOTE: 16-bit value in the JZ4780/X1600. |
paul@258 | 252 | |
paul@258 | 253 | Block_length_mask = 0x00000fff, // BLK_LEN |
paul@258 | 254 | }; |
paul@258 | 255 | |
paul@258 | 256 | enum Block_count_bits : unsigned |
paul@258 | 257 | { |
paul@258 | 258 | Block_count_mask = 0x0000ffff, // NOB/SNOB |
paul@258 | 259 | }; |
paul@258 | 260 | |
paul@258 | 261 | // Interrupt mask/flag bits. |
paul@258 | 262 | |
paul@258 | 263 | enum Interrupt_bits : unsigned |
paul@258 | 264 | { |
paul@258 | 265 | // X1600 only. |
paul@258 | 266 | |
paul@258 | 267 | Int_dma_data_done = 0x80000000, // DMA_DATA_DONE |
paul@258 | 268 | |
paul@258 | 269 | // JZ4780 only. |
paul@258 | 270 | |
paul@258 | 271 | Int_auto_cmd23_done = 0x40000000, // AUTO_CMD23_DONE |
paul@258 | 272 | Int_signal_voltage_change = 0x20000000, // SVS |
paul@258 | 273 | |
paul@258 | 274 | // JZ4780/X1600 only. |
paul@258 | 275 | |
paul@258 | 276 | Int_pin_level_field_mask = 0x1f, // PIN_LEVEL |
paul@258 | 277 | Int_pin_level_field_shift = 24, |
paul@258 | 278 | |
paul@258 | 279 | // X1600 only. |
paul@258 | 280 | |
paul@258 | 281 | Int_write_request_all_done = 0x00800000, // WR_ALL_DONE |
paul@258 | 282 | |
paul@258 | 283 | // JZ4780/X1600 only. |
paul@258 | 284 | |
paul@258 | 285 | Int_boot_crc_error = 0x00100000, // BCE |
paul@258 | 286 | Int_boot_data_end = 0x00080000, // BDE |
paul@258 | 287 | Int_boot_ack_error = 0x00040000, // BAE |
paul@258 | 288 | Int_boot_ack_received = 0x00020000, // BAR |
paul@258 | 289 | Int_dma_end = 0x00010000, // DMAEND |
paul@258 | 290 | Int_auto_cmd12_done = 0x00008000, // AUTO_CMD12_DONE |
paul@258 | 291 | Int_data_fifo_full = 0x00004000, // DATA_FIFO_FULL |
paul@258 | 292 | Int_data_fifo_empty = 0x00002000, // DATA_FIFO_EMP |
paul@258 | 293 | Int_crc_response_error = 0x00001000, // CRC_RES_ERR |
paul@258 | 294 | Int_crc_read_error = 0x00000800, // CRC_READ_ERR |
paul@258 | 295 | Int_crc_write_error = 0x00000400, // CRC_WRITE_ERR |
paul@258 | 296 | Int_response_timeout = 0x00000200, // TIME_OUT_RES |
paul@258 | 297 | Int_read_timeout = 0x00000100, // TIME_OUT_READ |
paul@258 | 298 | |
paul@258 | 299 | // Common. |
paul@258 | 300 | |
paul@258 | 301 | Int_sdio = 0x80, // SDIO |
paul@258 | 302 | Int_trans_fifo_write_request = 0x40, // TXFIFO_WR_REQ |
paul@258 | 303 | Int_recv_fifo_read_request = 0x20, // RXFIFO_RD_REQ |
paul@258 | 304 | Int_end_command_response = 0x04, // END_CMD_RES |
paul@258 | 305 | Int_programming_done = 0x02, // PRG_DONE |
paul@258 | 306 | Int_data_transfer_done = 0x01, // DATA_TRAN_DONE |
paul@258 | 307 | }; |
paul@258 | 308 | |
paul@258 | 309 | enum Command_index_bits : unsigned |
paul@258 | 310 | { |
paul@258 | 311 | Command_index_mask = 0x0000003f, // CMD_INDEX |
paul@258 | 312 | }; |
paul@258 | 313 | |
paul@258 | 314 | enum Command_argument_bits : unsigned |
paul@258 | 315 | { |
paul@258 | 316 | Command_argument_mask = 0xffffffff, // ARG |
paul@258 | 317 | }; |
paul@258 | 318 | |
paul@258 | 319 | enum Response_fifo_bits : unsigned |
paul@258 | 320 | { |
paul@258 | 321 | Response_fifo_mask = 0x0000ffff, // DATA |
paul@258 | 322 | }; |
paul@258 | 323 | |
paul@258 | 324 | enum Recv_data_fifo_bits : unsigned |
paul@258 | 325 | { |
paul@258 | 326 | Recv_data_fifo_mask = 0xffffffff, // DATA |
paul@258 | 327 | }; |
paul@258 | 328 | |
paul@258 | 329 | enum Trans_data_fifo_bits : unsigned |
paul@258 | 330 | { |
paul@258 | 331 | Trans_data_fifo_mask = 0xffffffff, // DATA |
paul@258 | 332 | }; |
paul@258 | 333 | |
paul@258 | 334 | enum Low_power_mode_bits : unsigned |
paul@258 | 335 | { |
paul@258 | 336 | Low_power_mode_enable = 0x00000001, // LPM |
paul@258 | 337 | }; |
paul@258 | 338 | |
paul@258 | 339 | enum Dma_control_bits : unsigned |
paul@258 | 340 | { |
paul@258 | 341 | Dma_mode_specify_transfer_length = 0x80, // MODE_SEL |
paul@258 | 342 | |
paul@258 | 343 | Dma_address_offset_field_mask = 0x3, // AOFST |
paul@258 | 344 | Dma_address_offset_field_shift = 5, |
paul@258 | 345 | |
paul@258 | 346 | Dma_align_enable = 0x10, // ALIGNEN |
paul@258 | 347 | |
paul@258 | 348 | Dma_burst_type_field_mask = 0x3, // INCR |
paul@258 | 349 | Dma_burst_type_incr16 = 0, |
paul@258 | 350 | Dma_burst_type_incr32 = 1, |
paul@258 | 351 | Dma_burst_type_incr64 = 2, |
paul@258 | 352 | Dma_burst_type_field_shift = 2, |
paul@258 | 353 | |
paul@258 | 354 | Dma_select_common_dma = 0x02, // DMASEL |
paul@258 | 355 | Dma_select_special_dma = 0x00, |
paul@258 | 356 | |
paul@258 | 357 | Dma_enable = 0x01, // DMAEN |
paul@258 | 358 | Dma_disable = 0x00, |
paul@258 | 359 | }; |
paul@258 | 360 | |
paul@258 | 361 | |
paul@258 | 362 | |
paul@258 | 363 | // Command indexes. |
paul@258 | 364 | |
paul@258 | 365 | enum Command_index : unsigned |
paul@258 | 366 | { |
paul@258 | 367 | Command_go_idle_state = 0, |
paul@258 | 368 | Command_send_op_cond = 1, |
paul@258 | 369 | Command_all_send_cid = 2, |
paul@258 | 370 | Command_send_relative_addr = 3, // SD |
paul@258 | 371 | Command_set_relative_addr = 3, // MMC |
paul@258 | 372 | Command_set_dsr = 4, |
paul@258 | 373 | Command_io_send_op_cond = 5, // SDIO |
paul@258 | 374 | Command_select_deselect_card = 7, |
paul@258 | 375 | Command_send_if_cond = 8, |
paul@258 | 376 | Command_send_csd = 9, |
paul@258 | 377 | Command_send_cid = 10, |
paul@258 | 378 | Command_read_dat_until_stop = 11, |
paul@258 | 379 | Command_stop_transmission = 12, |
paul@258 | 380 | Command_send_status = 13, |
paul@258 | 381 | Command_go_inactive_state = 15, |
paul@258 | 382 | Command_set_blocklen = 16, |
paul@258 | 383 | Command_read_single_block = 17, |
paul@258 | 384 | Command_read_multiple_block = 18, |
paul@258 | 385 | Command_write_dat_until_stop = 20, |
paul@258 | 386 | Command_set_block_count = 23, |
paul@258 | 387 | Command_write_block = 24, |
paul@258 | 388 | Command_write_multiple_block = 25, |
paul@258 | 389 | Command_program_cid = 26, |
paul@258 | 390 | Command_program_csd = 27, |
paul@258 | 391 | Command_set_write_prot = 28, |
paul@258 | 392 | Command_clr_write_prot = 29, |
paul@258 | 393 | Command_send_write_prot = 30, |
paul@258 | 394 | Command_tag_sector_start = 32, |
paul@258 | 395 | Command_tag_sector_end = 33, |
paul@258 | 396 | Command_untag_sector = 34, |
paul@258 | 397 | Command_tag_erase_group_start = 35, |
paul@258 | 398 | Command_tag_erase_group_end = 36, |
paul@258 | 399 | Command_untag_erase_group = 37, |
paul@258 | 400 | Command_erase = 38, |
paul@258 | 401 | Command_fast_io = 39, |
paul@258 | 402 | Command_go_irq_state = 40, |
paul@258 | 403 | Command_lock_unlock = 42, |
paul@258 | 404 | Command_io_rw_direct = 52, // SDIO |
paul@258 | 405 | Command_app_cmd = 55, |
paul@258 | 406 | Command_gen_cmd = 56, |
paul@258 | 407 | }; |
paul@258 | 408 | |
paul@258 | 409 | // Application-specific command indexes, used by first issuing Command_app_cmd. |
paul@258 | 410 | |
paul@258 | 411 | enum App_command_index : unsigned |
paul@258 | 412 | { |
paul@258 | 413 | App_command_set_bus_width = 6, |
paul@258 | 414 | App_command_sd_status = 13, |
paul@258 | 415 | App_command_send_num_wr_blocks = 22, |
paul@258 | 416 | App_command_set_wr_block_erase_count = 23, |
paul@258 | 417 | App_command_sd_send_op_cond = 41, |
paul@258 | 418 | App_command_set_clr_card_detect = 42, |
paul@258 | 419 | App_command_send_scr = 51, |
paul@258 | 420 | App_command_read_ocr = 58, |
paul@258 | 421 | }; |
paul@258 | 422 | |
paul@258 | 423 | enum Bus_width_bits : unsigned |
paul@258 | 424 | { |
paul@258 | 425 | Bus_width_1bit = 0, |
paul@258 | 426 | Bus_width_4bit = 2, |
paul@258 | 427 | }; |
paul@258 | 428 | |
paul@258 | 429 | // Command response sizes in 16-bit units. |
paul@258 | 430 | |
paul@258 | 431 | enum Response_sizes : unsigned |
paul@258 | 432 | { |
paul@258 | 433 | Response_size_R1 = 3, |
paul@258 | 434 | Response_size_R2 = 8, // omits the CRC and end bit |
paul@258 | 435 | Response_size_R3 = 3, |
paul@258 | 436 | Response_size_R4 = 3, |
paul@258 | 437 | Response_size_R5 = 3, |
paul@258 | 438 | Response_size_R6 = 3, |
paul@258 | 439 | Response_size_R7 = 3, |
paul@258 | 440 | }; |
paul@258 | 441 | |
paul@258 | 442 | // SD_SEND_OP_COND argument flags. |
paul@258 | 443 | |
paul@258 | 444 | enum Ocr_argument_flags : unsigned |
paul@258 | 445 | { |
paul@258 | 446 | Ocr_high_capacity_storage = 0x40000000, |
paul@258 | 447 | }; |
paul@258 | 448 | |
paul@258 | 449 | // SD_SEND_OP_COND response flags (R3). |
paul@258 | 450 | |
paul@258 | 451 | enum Ocr_response_flags : unsigned |
paul@258 | 452 | { |
paul@258 | 453 | Ocr_card_powered_up = 0x80000000, |
paul@258 | 454 | }; |
paul@258 | 455 | |
paul@258 | 456 | // R1 status flags. |
paul@258 | 457 | |
paul@258 | 458 | enum R1_status_flags : unsigned |
paul@258 | 459 | { |
paul@258 | 460 | R1_status_error_mask = 0xffff0000, |
paul@258 | 461 | }; |
paul@258 | 462 | |
paul@258 | 463 | |
paul@258 | 464 | |
paul@258 | 465 | // MMC response structures. |
paul@262 | 466 | // These are 16-bit aligned to permit conversion to 16-bit arrays. |
paul@258 | 467 | |
paul@258 | 468 | struct R1 |
paul@258 | 469 | { |
paul@258 | 470 | uint8_t end_crc; |
paul@258 | 471 | uint32_t status; |
paul@258 | 472 | uint8_t index:6, trans_start:2; |
paul@262 | 473 | } __attribute__((packed,aligned(__alignof__(uint16_t)))); |
paul@258 | 474 | |
paul@258 | 475 | struct R2 |
paul@258 | 476 | { |
paul@258 | 477 | // uint8_t end_crc; (not retrieved) |
paul@258 | 478 | |
paul@258 | 479 | union |
paul@258 | 480 | { |
paul@258 | 481 | uint8_t raw[15]; |
paul@258 | 482 | struct CID cid; |
paul@258 | 483 | struct CSD csd; |
paul@258 | 484 | } payload; |
paul@258 | 485 | |
paul@258 | 486 | uint8_t reserved_trans_start; |
paul@262 | 487 | } __attribute__((packed,aligned(__alignof__(uint16_t)))); |
paul@258 | 488 | |
paul@258 | 489 | struct R3 |
paul@258 | 490 | { |
paul@258 | 491 | uint8_t end_reserved; |
paul@258 | 492 | uint32_t ocr; |
paul@258 | 493 | uint8_t reserved_trans_start; |
paul@262 | 494 | } __attribute__((packed,aligned(__alignof__(uint16_t)))); |
paul@258 | 495 | |
paul@258 | 496 | // SDIO response structures. |
paul@258 | 497 | |
paul@258 | 498 | struct R4 |
paul@258 | 499 | { |
paul@258 | 500 | uint8_t end_reserved; |
paul@258 | 501 | uint32_t ocr:24, stuff:3, memory_present:1, number_io_functions:3, ready:1; |
paul@258 | 502 | uint8_t reserved_trans_start; |
paul@262 | 503 | } __attribute__((packed,aligned(__alignof__(uint16_t)))); |
paul@258 | 504 | |
paul@258 | 505 | struct R5 |
paul@258 | 506 | { |
paul@258 | 507 | uint8_t end_crc; |
paul@258 | 508 | uint8_t data; |
paul@258 | 509 | uint8_t out_of_range:1, invalid_function_number:1, reserved:1, error:1, |
paul@258 | 510 | io_current_state:2, illegal_command:1, crc_error:1; |
paul@258 | 511 | uint16_t stuff; |
paul@258 | 512 | uint8_t index:6, trans_start:2; |
paul@262 | 513 | } __attribute__((packed,aligned(__alignof__(uint16_t)))); |
paul@258 | 514 | |
paul@258 | 515 | struct R6 |
paul@258 | 516 | { |
paul@258 | 517 | uint8_t end_crc; |
paul@258 | 518 | uint16_t status; |
paul@258 | 519 | uint16_t rca; |
paul@258 | 520 | uint8_t index:6, trans_start:2; |
paul@262 | 521 | } __attribute__((packed,aligned(__alignof__(uint16_t)))); |
paul@258 | 522 | |
paul@258 | 523 | struct R7 |
paul@258 | 524 | { |
paul@258 | 525 | uint8_t end_crc; |
paul@258 | 526 | |
paul@258 | 527 | union |
paul@258 | 528 | { |
paul@258 | 529 | uint32_t check:8, voltage:4, reserved:20; |
paul@258 | 530 | uint32_t raw; |
paul@258 | 531 | } check_voltage; |
paul@258 | 532 | |
paul@258 | 533 | uint8_t index:6, trans_start:2; |
paul@262 | 534 | } __attribute__((packed,aligned(__alignof__(uint16_t)))); |
paul@258 | 535 | |
paul@258 | 536 | |
paul@258 | 537 | |
paul@258 | 538 | // Command frame: |
paul@258 | 539 | // byte: start (1), direction (1), command (6) |
paul@258 | 540 | // 4 bytes: argument |
paul@258 | 541 | // byte: CRC (7), end (1) |
paul@258 | 542 | |
paul@258 | 543 | // IO_RW_DIRECT argument: |
paul@258 | 544 | // Argument MSB to LSB: R/W (1), function number (3), read after write flag (1), |
paul@258 | 545 | // stuff (1), register address (17), stuff (1), |
paul@258 | 546 | // write data or stuff (8) |
paul@258 | 547 | // 0x88000c08: W, function = 0, read after write, register address = 6 (CCCR), |
paul@258 | 548 | // data = 8 (reset) |
paul@258 | 549 | |
paul@258 | 550 | const uint32_t Io_rw_direct_reset = 0x88000c08; |
paul@258 | 551 | |
paul@258 | 552 | // (IO_)SEND_OP_COND argument and default voltage range expected in R3, R4: |
paul@258 | 553 | // Argument MSB to LSB: stuff (8), voltage range (16), reserved (8) |
paul@258 | 554 | // 0x00ff8000: voltage range 2.7 - 3.6V |
paul@258 | 555 | |
paul@258 | 556 | const uint32_t Ocr_default_voltage_range = 0x00ff8000; |
paul@258 | 557 | |
paul@258 | 558 | // SEND_IF_COND argument and default voltage range expected in R7: |
paul@258 | 559 | // Argument MSB to LSB: stuff (20), voltage supplied (4), check (8) |
paul@258 | 560 | // 0x000001aa: voltage range 2.7 - 3.6V, check = 0b10101010 |
paul@258 | 561 | |
paul@258 | 562 | const uint32_t If_cond_default_voltage_range = 0x000001aa; |
paul@258 | 563 | |
paul@258 | 564 | |
paul@258 | 565 | |
paul@259 | 566 | // Utilities. |
paul@259 | 567 | |
paul@259 | 568 | static enum Command_data_control_bits |
paul@259 | 569 | encode_bus_width(uint8_t width) |
paul@259 | 570 | { |
paul@259 | 571 | switch (width) |
paul@259 | 572 | { |
paul@259 | 573 | case 4: return Cdc_bus_width_field_4bit; |
paul@259 | 574 | case 1: return Cdc_bus_width_field_1bit; |
paul@259 | 575 | default: return Cdc_bus_width_field_1bit; |
paul@259 | 576 | } |
paul@259 | 577 | } |
paul@259 | 578 | |
paul@259 | 579 | |
paul@259 | 580 | |
paul@258 | 581 | // Channel abstraction. |
paul@258 | 582 | |
paul@258 | 583 | Msc_channel::Msc_channel(l4_addr_t msc_start, l4_addr_t addr, l4_cap_idx_t irq) |
paul@258 | 584 | : _msc_start(msc_start), _irq(irq) |
paul@258 | 585 | { |
paul@258 | 586 | _regs = new Hw::Mmio_register_block<32>(addr); |
paul@258 | 587 | } |
paul@258 | 588 | |
paul@258 | 589 | Msc_channel::~Msc_channel() |
paul@258 | 590 | { |
paul@258 | 591 | } |
paul@258 | 592 | |
paul@258 | 593 | // Utility methods. |
paul@258 | 594 | // NOTE: Also defined in the CPM abstraction, should be consolidated. |
paul@258 | 595 | |
paul@258 | 596 | uint32_t |
paul@258 | 597 | Msc_channel::get_field(uint32_t reg, uint32_t mask, uint8_t shift) |
paul@258 | 598 | { |
paul@258 | 599 | return (_regs[reg] & (mask << shift)) >> shift; |
paul@258 | 600 | } |
paul@258 | 601 | |
paul@258 | 602 | void |
paul@258 | 603 | Msc_channel::set_field(uint32_t reg, uint32_t mask, uint8_t shift, uint32_t value) |
paul@258 | 604 | { |
paul@258 | 605 | _regs[reg] = (_regs[reg] & (~(mask << shift))) | ((mask & value) << shift); |
paul@258 | 606 | } |
paul@258 | 607 | |
paul@258 | 608 | bool |
paul@258 | 609 | Msc_channel::command_will_write(uint8_t index) |
paul@258 | 610 | { |
paul@258 | 611 | // NOTE: Probably incomplete coverage. |
paul@258 | 612 | |
paul@258 | 613 | switch (index) |
paul@258 | 614 | { |
paul@258 | 615 | case Command_write_dat_until_stop: return true; |
paul@258 | 616 | case Command_write_block: return true; |
paul@258 | 617 | case Command_write_multiple_block: return true; |
paul@258 | 618 | case Command_program_cid: return true; |
paul@258 | 619 | case Command_program_csd: return true; |
paul@258 | 620 | case Command_lock_unlock: return true; |
paul@258 | 621 | default: return false; |
paul@258 | 622 | } |
paul@258 | 623 | } |
paul@258 | 624 | |
paul@258 | 625 | bool |
paul@259 | 626 | Msc_channel::app_command_will_write(uint8_t index) |
paul@259 | 627 | { |
paul@259 | 628 | // NOTE: Probably incomplete coverage. |
paul@259 | 629 | |
paul@259 | 630 | (void) index; |
paul@259 | 631 | |
paul@259 | 632 | return false; |
paul@259 | 633 | } |
paul@259 | 634 | |
paul@259 | 635 | bool |
paul@258 | 636 | Msc_channel::command_with_data(uint8_t index) |
paul@258 | 637 | { |
paul@258 | 638 | // NOTE: Probably incomplete coverage. |
paul@258 | 639 | |
paul@258 | 640 | switch (index) |
paul@258 | 641 | { |
paul@258 | 642 | case Command_read_dat_until_stop: return true; |
paul@258 | 643 | case Command_read_single_block: return true; |
paul@258 | 644 | case Command_read_multiple_block: return true; |
paul@258 | 645 | case Command_write_dat_until_stop: return true; |
paul@258 | 646 | case Command_write_block: return true; |
paul@258 | 647 | case Command_write_multiple_block: return true; |
paul@258 | 648 | case Command_program_cid: return true; |
paul@258 | 649 | case Command_program_csd: return true; |
paul@258 | 650 | case Command_lock_unlock: return true; |
paul@258 | 651 | default: return false; |
paul@258 | 652 | } |
paul@258 | 653 | } |
paul@258 | 654 | |
paul@258 | 655 | bool |
paul@259 | 656 | Msc_channel::app_command_with_data(uint8_t index) |
paul@259 | 657 | { |
paul@259 | 658 | // NOTE: Probably incomplete coverage. |
paul@259 | 659 | |
paul@259 | 660 | switch (index) |
paul@259 | 661 | { |
paul@259 | 662 | case App_command_sd_status: return true; |
paul@259 | 663 | case App_command_send_num_wr_blocks: return true; |
paul@259 | 664 | case App_command_send_scr: return true; |
paul@259 | 665 | default: return false; |
paul@259 | 666 | } |
paul@259 | 667 | } |
paul@259 | 668 | |
paul@259 | 669 | bool |
paul@258 | 670 | Msc_channel::command_uses_busy(uint8_t index) |
paul@258 | 671 | { |
paul@258 | 672 | // NOTE: Probably incomplete coverage. |
paul@258 | 673 | |
paul@258 | 674 | switch (index) |
paul@258 | 675 | { |
paul@258 | 676 | case Command_select_deselect_card: return true; |
paul@258 | 677 | case Command_stop_transmission: return true; |
paul@258 | 678 | default: return false; |
paul@258 | 679 | } |
paul@258 | 680 | } |
paul@258 | 681 | |
paul@259 | 682 | bool |
paul@259 | 683 | Msc_channel::app_command_uses_busy(uint8_t index) |
paul@259 | 684 | { |
paul@259 | 685 | // NOTE: Probably incomplete coverage. |
paul@259 | 686 | |
paul@259 | 687 | (void) index; |
paul@259 | 688 | |
paul@259 | 689 | return false; |
paul@259 | 690 | } |
paul@259 | 691 | |
paul@258 | 692 | uint8_t |
paul@258 | 693 | Msc_channel::get_response_format(uint8_t index) |
paul@258 | 694 | { |
paul@258 | 695 | // NOTE: Probably incomplete coverage. |
paul@258 | 696 | |
paul@258 | 697 | switch (index) |
paul@258 | 698 | { |
paul@258 | 699 | // Common commands without response. |
paul@258 | 700 | |
paul@258 | 701 | case Command_go_idle_state: return 0; |
paul@258 | 702 | case Command_set_dsr: return 0; |
paul@258 | 703 | case Command_go_inactive_state: return 0; |
paul@258 | 704 | |
paul@258 | 705 | // Common commands with response. |
paul@258 | 706 | |
paul@258 | 707 | case Command_send_op_cond: return 3; |
paul@258 | 708 | case Command_all_send_cid: return 2; |
paul@258 | 709 | case Command_send_csd: return 2; |
paul@258 | 710 | case Command_send_cid: return 2; |
paul@258 | 711 | |
paul@258 | 712 | // SDIO only. |
paul@258 | 713 | |
paul@258 | 714 | case Command_io_send_op_cond: return 4; |
paul@258 | 715 | case Command_io_rw_direct: return 5; |
paul@258 | 716 | |
paul@258 | 717 | // SDMEM only. |
paul@258 | 718 | |
paul@258 | 719 | case Command_send_relative_addr: return 6; |
paul@258 | 720 | case Command_send_if_cond: return 7; |
paul@258 | 721 | |
paul@258 | 722 | // All other commands. |
paul@258 | 723 | |
paul@258 | 724 | default: return 1; |
paul@258 | 725 | } |
paul@258 | 726 | } |
paul@258 | 727 | |
paul@258 | 728 | uint8_t |
paul@258 | 729 | Msc_channel::get_app_response_format(uint8_t index) |
paul@258 | 730 | { |
paul@258 | 731 | // NOTE: Probably incomplete coverage. |
paul@258 | 732 | |
paul@258 | 733 | switch (index) |
paul@258 | 734 | { |
paul@258 | 735 | // SDMEM only. |
paul@258 | 736 | |
paul@258 | 737 | case App_command_sd_send_op_cond: return 3; |
paul@258 | 738 | |
paul@258 | 739 | // All other commands. |
paul@258 | 740 | |
paul@258 | 741 | default: return 1; |
paul@258 | 742 | } |
paul@258 | 743 | } |
paul@258 | 744 | |
paul@258 | 745 | // Read a response directly from the FIFO. |
paul@258 | 746 | |
paul@258 | 747 | void |
paul@258 | 748 | Msc_channel::read_response(uint16_t *buffer, uint8_t units) |
paul@258 | 749 | { |
paul@258 | 750 | uint8_t unit = units; |
paul@258 | 751 | |
paul@258 | 752 | while (unit > 0) |
paul@258 | 753 | { |
paul@258 | 754 | uint32_t data = _regs[Msc_response_fifo]; |
paul@258 | 755 | |
paul@258 | 756 | // Ignore the upper byte of the last unit in small transfers since it is the |
paul@258 | 757 | // lower byte from the previous unit not shifted out of the register. |
paul@258 | 758 | |
paul@258 | 759 | unit--; |
paul@258 | 760 | |
paul@258 | 761 | if ((unit == 0) && (units == 3)) |
paul@258 | 762 | buffer[unit] = (data & 0xff) << 8; |
paul@258 | 763 | else |
paul@258 | 764 | buffer[unit] = data; |
paul@258 | 765 | } |
paul@258 | 766 | } |
paul@258 | 767 | |
paul@258 | 768 | void |
paul@258 | 769 | Msc_channel::ack_irq(uint32_t flags) |
paul@258 | 770 | { |
paul@258 | 771 | // Clear the flags by setting them. |
paul@258 | 772 | |
paul@258 | 773 | _regs[Msc_interrupt_flag] = _regs[Msc_interrupt_flag] | flags; |
paul@258 | 774 | } |
paul@258 | 775 | |
paul@258 | 776 | void |
paul@258 | 777 | Msc_channel::unmask_irq(uint32_t flags) |
paul@258 | 778 | { |
paul@258 | 779 | ack_irq(flags); |
paul@258 | 780 | |
paul@258 | 781 | if (_regs[Msc_interrupt_mask] & flags) |
paul@258 | 782 | _regs[Msc_interrupt_mask] = _regs[Msc_interrupt_mask] & ~flags; |
paul@258 | 783 | } |
paul@258 | 784 | |
paul@258 | 785 | void |
paul@258 | 786 | Msc_channel::reset() |
paul@258 | 787 | { |
paul@258 | 788 | _regs[Msc_control] = _regs[Msc_control] | Control_reset; |
paul@258 | 789 | |
paul@258 | 790 | // NOTE: X1600 and other recent SoCs only. |
paul@258 | 791 | |
paul@258 | 792 | _regs[Msc_control] = _regs[Msc_control] & ~Control_reset; |
paul@258 | 793 | |
paul@258 | 794 | // Sufficient for other SoCs... |
paul@258 | 795 | |
paul@258 | 796 | while (_regs[Msc_status] & Status_resetting); |
paul@258 | 797 | } |
paul@258 | 798 | |
paul@258 | 799 | void |
paul@258 | 800 | Msc_channel::start_clock() |
paul@258 | 801 | { |
paul@258 | 802 | set_field(Msc_control, Control_clock_control_field_mask, |
paul@258 | 803 | Control_clock_control_field_shift, Control_clock_control_start); |
paul@258 | 804 | |
paul@258 | 805 | while (!(_regs[Msc_status] & Status_clock_enabled)); |
paul@258 | 806 | } |
paul@258 | 807 | |
paul@258 | 808 | void |
paul@258 | 809 | Msc_channel::stop_clock() |
paul@258 | 810 | { |
paul@258 | 811 | set_field(Msc_control, Control_clock_control_field_mask, |
paul@258 | 812 | Control_clock_control_field_shift, Control_clock_control_stop); |
paul@258 | 813 | |
paul@258 | 814 | while (_regs[Msc_status] & Status_clock_enabled); |
paul@258 | 815 | } |
paul@258 | 816 | |
paul@258 | 817 | // Send an application-specific command. |
paul@258 | 818 | |
paul@258 | 819 | bool |
paul@258 | 820 | Msc_channel::send_app_command(uint8_t index, uint32_t arg) |
paul@258 | 821 | { |
paul@262 | 822 | if (!send_command(Command_app_cmd, _cards[_card].rca << 16)) |
paul@258 | 823 | return false; |
paul@258 | 824 | |
paul@259 | 825 | return send_command(index, arg, get_app_response_format(index), |
paul@259 | 826 | app_command_with_data(index), |
paul@259 | 827 | app_command_will_write(index), |
paul@259 | 828 | app_command_uses_busy(index)); |
paul@258 | 829 | } |
paul@258 | 830 | |
paul@258 | 831 | // Send a common MMC/SD command. |
paul@258 | 832 | |
paul@258 | 833 | bool |
paul@258 | 834 | Msc_channel::send_command(uint8_t index, uint32_t arg) |
paul@258 | 835 | { |
paul@258 | 836 | return send_command(index, arg, get_response_format(index), |
paul@259 | 837 | command_with_data(index), |
paul@259 | 838 | command_will_write(index), |
paul@258 | 839 | command_uses_busy(index)); |
paul@258 | 840 | } |
paul@258 | 841 | |
paul@258 | 842 | // Initiate a command having the given index and using the given argument, |
paul@258 | 843 | // employing the specified response format and involving a data transfer if |
paul@258 | 844 | // indicated. |
paul@258 | 845 | |
paul@258 | 846 | bool |
paul@258 | 847 | Msc_channel::send_command(uint8_t index, uint32_t arg, uint8_t response_format, |
paul@258 | 848 | bool data, bool write, bool busy) |
paul@258 | 849 | { |
paul@258 | 850 | stop_clock(); |
paul@258 | 851 | |
paul@258 | 852 | // Enable DMA for data transfers. |
paul@258 | 853 | // NOTE: Needed for JZ4780 and later SoCs. |
paul@258 | 854 | |
paul@258 | 855 | _regs[Msc_dma_control] = (data ? Dma_select_common_dma | Dma_enable : Dma_disable); |
paul@258 | 856 | |
paul@258 | 857 | // Set the command index and argument. |
paul@258 | 858 | |
paul@258 | 859 | _regs[Msc_command_index] = index; |
paul@258 | 860 | _regs[Msc_command_argument] = arg; |
paul@258 | 861 | |
paul@258 | 862 | // Configure the response format and data bus width. |
paul@258 | 863 | |
paul@258 | 864 | set_field(Msc_command_data_control, Cdc_response_format_field_mask, |
paul@258 | 865 | Cdc_response_format_field_shift, response_format); |
paul@258 | 866 | |
paul@258 | 867 | // NOTE: May need to set the SD bus width. |
paul@258 | 868 | |
paul@258 | 869 | set_field(Msc_command_data_control, Cdc_bus_width_field_mask, |
paul@262 | 870 | Cdc_bus_width_field_shift, encode_bus_width(_cards[_card].bus_width)); |
paul@258 | 871 | |
paul@258 | 872 | set_field(Msc_command_data_control, Cdc_recv_fifo_level_field_mask, |
paul@258 | 873 | Cdc_recv_fifo_level_field_shift, Cdc_fifo_level_16); |
paul@258 | 874 | |
paul@258 | 875 | set_field(Msc_command_data_control, Cdc_trans_fifo_level_field_mask, |
paul@258 | 876 | Cdc_trans_fifo_level_field_shift, Cdc_fifo_level_16); |
paul@258 | 877 | |
paul@258 | 878 | // Set and clear control bits appropriate to the command. |
paul@258 | 879 | // NOTE: Pre-JZ4780 SoCs enable DMA in this register. |
paul@258 | 880 | |
paul@258 | 881 | _regs[Msc_command_data_control] = _regs[Msc_command_data_control] | |
paul@258 | 882 | // (data ? Cdc_dma_enable : Cdc_dma_disable) | |
paul@258 | 883 | (busy ? Cdc_expect_busy : Cdc_do_not_expect_busy) | |
paul@258 | 884 | (data ? Cdc_data_with_command : Cdc_no_data_with_command) | |
paul@258 | 885 | (write ? Cdc_write_operation : Cdc_read_operation); |
paul@258 | 886 | |
paul@258 | 887 | _regs[Msc_command_data_control] = _regs[Msc_command_data_control] & |
paul@258 | 888 | ~( |
paul@258 | 889 | // (data ? Cdc_dma_disable : Cdc_dma_enable) | |
paul@258 | 890 | (busy ? Cdc_do_not_expect_busy : Cdc_expect_busy) | |
paul@258 | 891 | (data ? Cdc_no_data_with_command : Cdc_data_with_command) | |
paul@258 | 892 | (write ? Cdc_read_operation : Cdc_write_operation) | |
paul@258 | 893 | Cdc_stream_block | Cdc_init_sequence); |
paul@258 | 894 | |
paul@258 | 895 | // Unmask interrupts, start the clock, then initiate the command. |
paul@258 | 896 | |
paul@258 | 897 | uint32_t flags = Int_end_command_response | Int_response_timeout; |
paul@258 | 898 | |
paul@258 | 899 | unmask_irq(flags); |
paul@258 | 900 | start_clock(); |
paul@258 | 901 | |
paul@258 | 902 | _regs[Msc_control] = _regs[Msc_control] | Control_start_operation; |
paul@258 | 903 | |
paul@258 | 904 | // Wait for command completion. |
paul@258 | 905 | |
paul@258 | 906 | if (!wait_for_irq(flags)) |
paul@258 | 907 | return false; |
paul@258 | 908 | |
paul@258 | 909 | // Determine whether a timeout occurred. |
paul@258 | 910 | |
paul@261 | 911 | bool have_response = !((_regs[Msc_interrupt_flag] & Int_response_timeout) || |
paul@261 | 912 | (_regs[Msc_status] & Status_response_crc_error) || |
paul@261 | 913 | (_regs[Msc_status] & Status_timeout_response)); |
paul@258 | 914 | |
paul@261 | 915 | // Acknowledge the response interrupts and return the status. |
paul@258 | 916 | |
paul@258 | 917 | ack_irq(flags); |
paul@258 | 918 | return have_response; |
paul@258 | 919 | } |
paul@258 | 920 | |
paul@262 | 921 | // Wait indefinitely for an interrupt request, returning true if one was delivered. |
paul@262 | 922 | |
paul@262 | 923 | bool |
paul@262 | 924 | Msc_channel::wait_for_irq(uint32_t flags) |
paul@262 | 925 | { |
paul@262 | 926 | return !l4_error(l4_irq_receive(_irq, L4_IPC_NEVER)) && |
paul@262 | 927 | (_regs[Msc_interrupt_flag] & flags); |
paul@262 | 928 | } |
paul@262 | 929 | |
paul@262 | 930 | // Wait up to the given timeout (in microseconds) for an interrupt request, |
paul@262 | 931 | // returning true if one was delivered. |
paul@262 | 932 | |
paul@262 | 933 | bool |
paul@262 | 934 | Msc_channel::wait_for_irq(uint32_t flags, unsigned int timeout) |
paul@262 | 935 | { |
paul@262 | 936 | return !l4_error(l4_irq_receive(_irq, l4_timeout(L4_IPC_TIMEOUT_NEVER, |
paul@262 | 937 | l4util_micros2l4to(timeout)))) && |
paul@262 | 938 | (_regs[Msc_interrupt_flag] & flags); |
paul@262 | 939 | } |
paul@262 | 940 | |
paul@262 | 941 | |
paul@262 | 942 | |
paul@258 | 943 | // Check the voltage range of the SD card, potentially establishing that it is |
paul@258 | 944 | // a high capacity card. Return false if the voltage range is incompatible. |
paul@258 | 945 | |
paul@258 | 946 | bool |
paul@258 | 947 | Msc_channel::check_sd() |
paul@258 | 948 | { |
paul@262 | 949 | struct R7 r7; |
paul@258 | 950 | |
paul@258 | 951 | // Send an interface condition command. |
paul@258 | 952 | // A card may not respond to this command. |
paul@258 | 953 | |
paul@258 | 954 | if (!send_command(Command_send_if_cond, If_cond_default_voltage_range)) |
paul@258 | 955 | return true; |
paul@258 | 956 | |
paul@262 | 957 | read_response((uint16_t *) &r7, Response_size_R7); |
paul@258 | 958 | |
paul@258 | 959 | // Reject any card not supporting the default voltage range. |
paul@258 | 960 | |
paul@262 | 961 | if (r7.check_voltage.raw != If_cond_default_voltage_range) |
paul@258 | 962 | return false; |
paul@258 | 963 | |
paul@258 | 964 | return true; |
paul@258 | 965 | } |
paul@258 | 966 | |
paul@258 | 967 | // Check the voltage range of the SDIO card, inactivating it if incompatible. |
paul@258 | 968 | |
paul@258 | 969 | void |
paul@258 | 970 | Msc_channel::init_sdio() |
paul@258 | 971 | { |
paul@262 | 972 | struct R4 r4; |
paul@258 | 973 | uint32_t ocr = 0; |
paul@258 | 974 | |
paul@258 | 975 | // Reset any SDIO card or IO unit in a combined memory/IO card. |
paul@258 | 976 | // A non-SDIO card may not respond to this command. |
paul@258 | 977 | |
paul@258 | 978 | if (!send_command(Command_io_rw_direct, Io_rw_direct_reset)) |
paul@258 | 979 | return; |
paul@258 | 980 | |
paul@258 | 981 | // Attempt to assert the operating conditions. |
paul@258 | 982 | |
paul@258 | 983 | do |
paul@258 | 984 | { |
paul@258 | 985 | // Obtain OCR (operating conditions register) values for any IO card. |
paul@258 | 986 | // Without a response, the card may have inactivated itself due to voltage |
paul@258 | 987 | // range incompatibility reasons. |
paul@258 | 988 | |
paul@258 | 989 | if (!send_command(Command_io_send_op_cond, ocr)) |
paul@258 | 990 | return; |
paul@258 | 991 | |
paul@262 | 992 | read_response((uint16_t *) &r4, Response_size_R4); |
paul@258 | 993 | |
paul@258 | 994 | // Finish if no IO functions provided. |
paul@258 | 995 | // NOTE: Should only need to check this the first time. |
paul@258 | 996 | |
paul@262 | 997 | if (r4.number_io_functions == 0) |
paul@258 | 998 | return; |
paul@258 | 999 | |
paul@262 | 1000 | if (r4.ocr != Ocr_default_voltage_range) |
paul@258 | 1001 | { |
paul@258 | 1002 | ocr = Ocr_default_voltage_range; |
paul@258 | 1003 | continue; |
paul@258 | 1004 | } |
paul@258 | 1005 | } |
paul@262 | 1006 | while (!r4.ready); |
paul@258 | 1007 | } |
paul@258 | 1008 | |
paul@258 | 1009 | void |
paul@258 | 1010 | Msc_channel::init_sdmem() |
paul@258 | 1011 | { |
paul@262 | 1012 | struct R3 r3; |
paul@258 | 1013 | |
paul@258 | 1014 | // Incorporate the HCS bit into the OCR for SDMEM. |
paul@258 | 1015 | |
paul@258 | 1016 | uint32_t ocr = Ocr_high_capacity_storage; |
paul@258 | 1017 | |
paul@258 | 1018 | do |
paul@258 | 1019 | { |
paul@258 | 1020 | if (!send_app_command(App_command_sd_send_op_cond, ocr)) |
paul@258 | 1021 | return; |
paul@258 | 1022 | |
paul@262 | 1023 | read_response((uint16_t *) &r3, Response_size_R3); |
paul@258 | 1024 | |
paul@262 | 1025 | if (r3.ocr != Ocr_default_voltage_range) |
paul@258 | 1026 | { |
paul@258 | 1027 | ocr = Ocr_default_voltage_range | Ocr_high_capacity_storage; |
paul@258 | 1028 | continue; |
paul@258 | 1029 | } |
paul@258 | 1030 | } |
paul@262 | 1031 | while (!(r3.ocr & Ocr_card_powered_up)); |
paul@258 | 1032 | } |
paul@258 | 1033 | |
paul@258 | 1034 | void |
paul@258 | 1035 | Msc_channel::init_mmc() |
paul@258 | 1036 | { |
paul@258 | 1037 | // Obtain OCR (operating conditions register) values for each card using |
paul@258 | 1038 | // send_op_cond command variants without argument, or assert operating |
paul@258 | 1039 | // conditions with argument to avoid handling card responses. Where responses |
paul@258 | 1040 | // are solicited, the host must determine a suitable argument and reissue the |
paul@258 | 1041 | // command. |
paul@258 | 1042 | |
paul@262 | 1043 | struct R3 r3; |
paul@258 | 1044 | uint32_t ocr = 0; |
paul@258 | 1045 | |
paul@258 | 1046 | do |
paul@258 | 1047 | { |
paul@258 | 1048 | if (!send_command(Command_send_op_cond, ocr)) |
paul@258 | 1049 | return; |
paul@258 | 1050 | |
paul@262 | 1051 | read_response((uint16_t *) &r3, Response_size_R3); |
paul@258 | 1052 | |
paul@262 | 1053 | if (r3.ocr != Ocr_default_voltage_range) |
paul@258 | 1054 | { |
paul@258 | 1055 | ocr = Ocr_default_voltage_range; |
paul@258 | 1056 | continue; |
paul@258 | 1057 | } |
paul@258 | 1058 | } |
paul@262 | 1059 | while (!(r3.ocr & Ocr_card_powered_up)); |
paul@258 | 1060 | } |
paul@258 | 1061 | |
paul@258 | 1062 | void |
paul@258 | 1063 | Msc_channel::identify_cards() |
paul@258 | 1064 | { |
paul@262 | 1065 | struct R2 r2; |
paul@262 | 1066 | struct R6 r6; |
paul@258 | 1067 | |
paul@262 | 1068 | _num_cards = 0; |
paul@258 | 1069 | |
paul@258 | 1070 | while (send_command(Command_all_send_cid, 0)) |
paul@258 | 1071 | { |
paul@262 | 1072 | read_response((uint16_t *) &r2, Response_size_R2); |
paul@258 | 1073 | |
paul@262 | 1074 | _cards[_num_cards].cid = r2.payload.cid; |
paul@258 | 1075 | |
paul@262 | 1076 | printf("card: %d\n", _num_cards); |
paul@262 | 1077 | printf("date: %d %d\n", r2.payload.cid.month, r2.payload.cid.year); |
paul@262 | 1078 | printf("serial: %d\n", r2.payload.cid.serial); |
paul@262 | 1079 | printf("revision: %d\n", r2.payload.cid.revision); |
paul@262 | 1080 | printf("name: %c%c%c%c%c\n", r2.payload.cid.name[4], r2.payload.cid.name[3], |
paul@262 | 1081 | r2.payload.cid.name[2], r2.payload.cid.name[1], |
paul@262 | 1082 | r2.payload.cid.name[0]); |
paul@262 | 1083 | printf("oem: %d\n", r2.payload.cid.oem); |
paul@262 | 1084 | printf("manufacturer: %d\n", r2.payload.cid.manufacturer); |
paul@258 | 1085 | |
paul@258 | 1086 | // Try and obtain a card-issued address. |
paul@258 | 1087 | |
paul@258 | 1088 | if (send_command(Command_send_relative_addr, 0)) |
paul@258 | 1089 | { |
paul@262 | 1090 | read_response((uint16_t *) &r6, Response_size_R6); |
paul@262 | 1091 | _cards[_num_cards].rca = r6.rca; |
paul@258 | 1092 | } |
paul@258 | 1093 | |
paul@258 | 1094 | // Try and assign an address. |
paul@258 | 1095 | // Employ 1-based relative addressing. |
paul@258 | 1096 | |
paul@262 | 1097 | else if (send_command(Command_set_relative_addr, _num_cards + 1)) |
paul@262 | 1098 | _cards[_num_cards].rca = _num_cards + 1; |
paul@258 | 1099 | |
paul@258 | 1100 | // Otherwise, stop identification. |
paul@258 | 1101 | |
paul@258 | 1102 | else |
paul@258 | 1103 | return; |
paul@258 | 1104 | |
paul@262 | 1105 | // Set the default bus width to be determined. |
paul@259 | 1106 | |
paul@262 | 1107 | _cards[_num_cards].bus_width = 0; |
paul@259 | 1108 | |
paul@262 | 1109 | _num_cards++; |
paul@258 | 1110 | } |
paul@258 | 1111 | } |
paul@258 | 1112 | |
paul@258 | 1113 | void |
paul@258 | 1114 | Msc_channel::query_cards() |
paul@258 | 1115 | { |
paul@262 | 1116 | struct R2 r2; |
paul@262 | 1117 | struct R3 r3; |
paul@258 | 1118 | uint8_t card; |
paul@258 | 1119 | |
paul@262 | 1120 | for (card = 0; card < _num_cards; card++) |
paul@258 | 1121 | { |
paul@258 | 1122 | // Employ 1-based relative addressing. |
paul@258 | 1123 | |
paul@262 | 1124 | if (!send_command(Command_send_csd, _cards[card].rca << 16)) |
paul@258 | 1125 | return; |
paul@258 | 1126 | |
paul@262 | 1127 | read_response((uint16_t *) &r2, Response_size_R2); |
paul@258 | 1128 | |
paul@262 | 1129 | _cards[card].csd = r2.payload.csd; |
paul@258 | 1130 | |
paul@263 | 1131 | struct CSD *csd = &_cards[card].csd; |
paul@263 | 1132 | |
paul@258 | 1133 | printf("card: %d\n", card); |
paul@263 | 1134 | printf("csd: %d\n", csd->csd); |
paul@263 | 1135 | printf("copy: %s\n", csd->copy ? "copied" : "original"); |
paul@263 | 1136 | printf("card command classes: %03x\n", csd->card_command_classes); |
paul@263 | 1137 | printf("device (size multiplier): %d %d\n", csd->device_size + 1, |
paul@263 | 1138 | 1 << (csd->device_size_multiplier + 2)); |
paul@263 | 1139 | printf("device size: %d\n", (1 << csd->read_blocklen) * |
paul@263 | 1140 | (csd->device_size + 1) * |
paul@263 | 1141 | (1 << (csd->device_size_multiplier + 2))); |
paul@263 | 1142 | printf("transfer speed: %d MHz\n", csd->tran_speed == 0x32 ? 25 : 50); |
paul@263 | 1143 | printf("format group: %d %d\n", csd->format, r2.payload.csd.format_group); |
paul@263 | 1144 | printf("write time factor: %d\n", 1 << csd->write_time_factor); |
paul@263 | 1145 | printf("write protect (temp perm): %s %s\n", csd->temp_write_prot ? "yes" : "no", |
paul@263 | 1146 | csd->perm_write_prot ? "yes" : "no"); |
paul@263 | 1147 | printf("write protect group (enable size): %s %d\n", csd->write_prot_group_enable ? "yes" : "no", |
paul@263 | 1148 | csd->write_prot_group_size + 1); |
paul@263 | 1149 | printf("write block (partial length): %s %d\n", csd->write_block_partial ? "yes" : "no", |
paul@263 | 1150 | 1 << csd->write_blocklen); |
paul@263 | 1151 | printf("read block (partial length): %s %d\n", csd->read_block_partial ? "yes" : "no", |
paul@263 | 1152 | 1 << csd->read_blocklen); |
paul@263 | 1153 | printf("erase: sector single: %d %s\n", csd->erase_sector_size + 1, |
paul@263 | 1154 | csd->erase_single_block_enable ? "yes" : "no"); |
paul@263 | 1155 | printf("misalign: read write: %s %s\n", csd->read_block_misalign ? "yes" : "no", |
paul@263 | 1156 | csd->write_block_misalign ? "yes" : "no"); |
paul@263 | 1157 | printf("max read current (min max): %d %d\n", csd->max_read_current_min, |
paul@263 | 1158 | csd->max_read_current_max); |
paul@263 | 1159 | printf("max write current (min max): %d %d\n", csd->max_write_current_min, |
paul@263 | 1160 | csd->max_write_current_max); |
paul@263 | 1161 | printf("read access time (1 2): %d %d\n", csd->data_read_access_time_1, |
paul@263 | 1162 | csd->data_read_access_time_2); |
paul@263 | 1163 | printf("DSR: %s\n", csd->dsr_implemented ? "yes" : "no"); |
paul@262 | 1164 | |
paul@262 | 1165 | // Query the OCR again now that we can associate it with a specific card. |
paul@262 | 1166 | |
paul@262 | 1167 | if (!send_app_command(App_command_read_ocr, 0)) |
paul@262 | 1168 | return; |
paul@262 | 1169 | |
paul@262 | 1170 | read_response((uint16_t *) &r3, Response_size_R3); |
paul@262 | 1171 | |
paul@262 | 1172 | _cards[card].ocr = r3.ocr; |
paul@258 | 1173 | } |
paul@258 | 1174 | } |
paul@258 | 1175 | |
paul@262 | 1176 | |
paul@262 | 1177 | |
paul@263 | 1178 | // Enable the controller and identify cards. |
paul@263 | 1179 | |
paul@263 | 1180 | void |
paul@263 | 1181 | Msc_channel::enable() |
paul@263 | 1182 | { |
paul@263 | 1183 | // NOTE: X1600 and other recent SoCs only. |
paul@263 | 1184 | |
paul@263 | 1185 | _regs[Msc_low_power_mode] = _regs[Msc_low_power_mode] & ~Low_power_mode_enable; |
paul@263 | 1186 | |
paul@263 | 1187 | stop_clock(); |
paul@263 | 1188 | reset(); |
paul@263 | 1189 | |
paul@263 | 1190 | // Slow the clock for initialisation. |
paul@263 | 1191 | // NOTE: Should use the CPM module to deduce the appropriate divider value. |
paul@263 | 1192 | |
paul@263 | 1193 | set_field(Msc_clock_rate, Clock_rate_field_mask, Clock_rate_field_shift, 7); |
paul@263 | 1194 | |
paul@263 | 1195 | send_command(Command_go_idle_state, 0); |
paul@263 | 1196 | |
paul@263 | 1197 | if (check_sd()) |
paul@263 | 1198 | { |
paul@263 | 1199 | init_sdio(); |
paul@263 | 1200 | init_sdmem(); |
paul@263 | 1201 | } |
paul@263 | 1202 | |
paul@263 | 1203 | init_mmc(); |
paul@263 | 1204 | identify_cards(); |
paul@263 | 1205 | query_cards(); |
paul@263 | 1206 | |
paul@263 | 1207 | // Restore the clock. |
paul@263 | 1208 | // NOTE: Should use the CPM module to deduce the appropriate divider value. |
paul@263 | 1209 | |
paul@263 | 1210 | set_field(Msc_clock_rate, Clock_rate_field_mask, Clock_rate_field_shift, 1); |
paul@263 | 1211 | |
paul@263 | 1212 | // Initially, no card is selected. |
paul@263 | 1213 | |
paul@263 | 1214 | _card = -1; |
paul@263 | 1215 | } |
paul@263 | 1216 | |
paul@263 | 1217 | // Obtain the card details. |
paul@263 | 1218 | |
paul@263 | 1219 | struct msc_card * |
paul@263 | 1220 | Msc_channel::get_cards() |
paul@263 | 1221 | { |
paul@263 | 1222 | return _cards; |
paul@263 | 1223 | } |
paul@263 | 1224 | |
paul@263 | 1225 | // Return the number of active cards. |
paul@263 | 1226 | |
paul@263 | 1227 | uint8_t |
paul@263 | 1228 | Msc_channel::num_cards() |
paul@263 | 1229 | { |
paul@263 | 1230 | return _num_cards; |
paul@263 | 1231 | } |
paul@263 | 1232 | |
paul@262 | 1233 | // Receive data from the selected card. |
paul@262 | 1234 | |
paul@258 | 1235 | uint32_t |
paul@258 | 1236 | Msc_channel::recv_data(l4re_dma_space_dma_addr_t paddr, uint32_t count) |
paul@258 | 1237 | { |
paul@261 | 1238 | uint32_t flags = Int_data_transfer_done; |
paul@261 | 1239 | |
paul@261 | 1240 | unmask_irq(flags); |
paul@261 | 1241 | |
paul@261 | 1242 | uint32_t to_transfer = transfer(_msc_start + Msc_recv_data_fifo, paddr, true, count); |
paul@261 | 1243 | |
paul@261 | 1244 | wait_for_irq(flags); |
paul@261 | 1245 | ack_irq(flags); |
paul@261 | 1246 | |
paul@261 | 1247 | if (!to_transfer || |
paul@261 | 1248 | (_regs[Msc_status] & Status_read_crc_error) || |
paul@261 | 1249 | (_regs[Msc_status] & Status_timeout_read)) |
paul@261 | 1250 | return 0; |
paul@261 | 1251 | |
paul@261 | 1252 | return to_transfer; |
paul@258 | 1253 | } |
paul@258 | 1254 | |
paul@262 | 1255 | // Send data to the selected card. |
paul@262 | 1256 | |
paul@258 | 1257 | uint32_t |
paul@258 | 1258 | Msc_channel::send_data(l4re_dma_space_dma_addr_t paddr, uint32_t count) |
paul@258 | 1259 | { |
paul@261 | 1260 | uint32_t flags = Int_data_transfer_done; |
paul@261 | 1261 | |
paul@261 | 1262 | unmask_irq(flags); |
paul@261 | 1263 | |
paul@261 | 1264 | uint32_t to_transfer = transfer(paddr, _msc_start + Msc_trans_data_fifo, false, count); |
paul@261 | 1265 | |
paul@261 | 1266 | wait_for_irq(flags); |
paul@261 | 1267 | ack_irq(flags); |
paul@261 | 1268 | |
paul@261 | 1269 | if (!to_transfer || |
paul@261 | 1270 | (_regs[Msc_status] & Status_write_crc_error_data) || |
paul@261 | 1271 | (_regs[Msc_status] & Status_write_crc_error_no_status)) |
paul@261 | 1272 | return 0; |
paul@261 | 1273 | |
paul@261 | 1274 | return to_transfer; |
paul@258 | 1275 | } |
paul@258 | 1276 | |
paul@262 | 1277 | // Read blocks from the indicated card into a memory region. |
paul@262 | 1278 | |
paul@258 | 1279 | uint32_t |
paul@261 | 1280 | Msc_channel::read_blocks(uint8_t card, l4re_dma_space_dma_addr_t paddr, |
paul@261 | 1281 | uint32_t block_address, uint32_t block_count) |
paul@258 | 1282 | { |
paul@262 | 1283 | uint32_t block_size = 1 << _cards[card].csd.read_blocklen; |
paul@262 | 1284 | struct R1 r1; |
paul@258 | 1285 | |
paul@258 | 1286 | // Select the requested card. |
paul@258 | 1287 | |
paul@262 | 1288 | if (_card != card) |
paul@258 | 1289 | { |
paul@262 | 1290 | if (!send_command(Command_select_deselect_card, _cards[card].rca << 16)) |
paul@258 | 1291 | return 0; |
paul@258 | 1292 | |
paul@262 | 1293 | read_response((uint16_t *) &r1, Response_size_R1); |
paul@258 | 1294 | |
paul@262 | 1295 | if (r1.status & R1_status_error_mask) |
paul@258 | 1296 | return 0; |
paul@258 | 1297 | |
paul@262 | 1298 | _card = card; |
paul@258 | 1299 | } |
paul@258 | 1300 | |
paul@259 | 1301 | // NOTE: SMEM cards should allow bus width setting with the SCR register |
paul@259 | 1302 | // NOTE: describing the permitted values. |
paul@258 | 1303 | // NOTE: SDIO cards have their bus width set in CCCR via CMD52. |
paul@258 | 1304 | |
paul@262 | 1305 | if (!_cards[card].bus_width) |
paul@259 | 1306 | { |
paul@259 | 1307 | if (send_app_command(App_command_set_bus_width, Bus_width_4bit)) |
paul@262 | 1308 | _cards[card].bus_width = 4; |
paul@259 | 1309 | else |
paul@262 | 1310 | _cards[card].bus_width = 1; |
paul@259 | 1311 | } |
paul@259 | 1312 | |
paul@258 | 1313 | if (!send_command(Command_set_blocklen, block_size)) |
paul@258 | 1314 | return 0; |
paul@258 | 1315 | |
paul@262 | 1316 | read_response((uint16_t *) &r1, Response_size_R1); |
paul@258 | 1317 | |
paul@262 | 1318 | if (r1.status & R1_status_error_mask) |
paul@258 | 1319 | return 0; |
paul@258 | 1320 | |
paul@261 | 1321 | // NOTE: Consider issuing a predefined block count command to any multiple |
paul@261 | 1322 | // NOTE: block operation, at least for cards that support it. |
paul@261 | 1323 | |
paul@258 | 1324 | // Apply block count and size properties to the issued command. |
paul@258 | 1325 | |
paul@261 | 1326 | _regs[Msc_block_count] = block_count; |
paul@258 | 1327 | _regs[Msc_block_length] = block_size; |
paul@258 | 1328 | |
paul@263 | 1329 | // Where CCS = 0, byte addressing is used. Otherwise, block addressing is used. |
paul@263 | 1330 | |
paul@263 | 1331 | uint32_t address; |
paul@263 | 1332 | |
paul@263 | 1333 | if (_cards[card].ocr & Ocr_high_capacity_storage) |
paul@263 | 1334 | address = block_address; |
paul@263 | 1335 | else |
paul@263 | 1336 | address = block_address * block_size; |
paul@258 | 1337 | |
paul@261 | 1338 | if (!send_command(block_count == 1 ? Command_read_single_block |
paul@263 | 1339 | : Command_read_multiple_block, address)) |
paul@258 | 1340 | return 0; |
paul@258 | 1341 | |
paul@262 | 1342 | read_response((uint16_t *) &r1, Response_size_R1); |
paul@258 | 1343 | |
paul@262 | 1344 | if (r1.status & R1_status_error_mask) |
paul@258 | 1345 | return 0; |
paul@258 | 1346 | |
paul@261 | 1347 | // NOTE: Use Msc_block_success_count instead. |
paul@261 | 1348 | |
paul@261 | 1349 | uint32_t transferred = recv_data(paddr, block_size * block_count); |
paul@261 | 1350 | |
paul@261 | 1351 | if (block_count > 1) |
paul@261 | 1352 | send_command(Command_stop_transmission, 0); |
paul@261 | 1353 | |
paul@261 | 1354 | return transferred; |
paul@258 | 1355 | } |
paul@258 | 1356 | |
paul@258 | 1357 | |
paul@258 | 1358 | |
paul@258 | 1359 | // Peripheral abstraction. |
paul@258 | 1360 | |
paul@258 | 1361 | Msc_chip::Msc_chip(l4_addr_t msc_start, l4_addr_t start, l4_addr_t end) |
paul@258 | 1362 | : _msc_start(msc_start), _start(start), _end(end) |
paul@258 | 1363 | { |
paul@258 | 1364 | } |