github.com/f-secure-foundry/tamago@v0.0.0-20220307101044-d73fcdd7f11b/soc/imx6/usdhc/cmd.go (about) 1 // NXP Ultra Secured Digital Host Controller (uSDHC) driver 2 // https://github.com/f-secure-foundry/tamago 3 // 4 // IP: https://www.mobiveil.com/esdhc/ 5 // 6 // Copyright (c) F-Secure Corporation 7 // https://foundry.f-secure.com 8 // 9 // Use of this source code is governed by the license 10 // that can be found in the LICENSE file. 11 12 package usdhc 13 14 import ( 15 "fmt" 16 "time" 17 18 "github.com/f-secure-foundry/tamago/bits" 19 "github.com/f-secure-foundry/tamago/internal/reg" 20 ) 21 22 // CMD constants 23 const ( 24 GO_IDLE_STATE = 0 25 26 // p127, 4.9.5 (Published RCA response), SD-PL-7.10 27 RCA_ADDR = 16 28 RCA_STATUS = 0 29 30 // p131, Table 4-42 : Card Status, SD-PL-7.10 31 // p160, Table 68 - Device Status, JESD84-B51 32 STATUS_CURRENT_STATE = 9 33 STATUS_SWITCH_ERROR = 7 34 STATUS_APP_CMD = 5 35 CURRENT_STATE_IDENT = 2 36 CURRENT_STATE_TRAN = 4 37 38 // data transfer direction 39 WRITE = 0 40 READ = 1 41 42 // response types 43 RSP_NONE = 0b00 44 RSP_136 = 0b01 45 RSP_48 = 0b10 46 RSP_48_CHECK_BUSY = 0b11 47 48 // SEND_CSD response contains CSD[127:8], 49 CSD_RSP_OFF = -8 50 51 DEFAULT_CMD_TIMEOUT = 10 * time.Millisecond 52 ) 53 54 type cmdParams struct { 55 // data transfer direction 56 dtd uint32 57 // response type 58 res uint32 59 // command index verification 60 cic bool 61 // CRC verification 62 ccc bool 63 } 64 65 var cmds = map[uint32]cmdParams{ 66 // CMD0 - GO_IDLE_STATE - reset card 67 0: {READ, RSP_NONE, false, false}, 68 // MMC: CMD1 - SEND_OP_COND - send operating conditions 69 1: {READ, RSP_48, false, false}, 70 // CMD2 - ALL_SEND_CID - get unique card identification 71 2: {READ, RSP_136, false, true}, 72 // SD: CMD3 - SEND_RELATIVE_ADDR - get relative card address (RCA) 73 // MMC: CMD3 - SET_RELATIVE_ADDR - set relative card address (RCA 74 3: {READ, RSP_48, true, true}, 75 // CMD6 - SWITCH - switch mode of operation 76 6: {READ, RSP_48_CHECK_BUSY, true, true}, 77 // CMD7 - SELECT/DESELECT CARD - enter transfer state 78 7: {READ, RSP_48_CHECK_BUSY, true, true}, 79 // SD: CMD8 - SEND_IF_COND - read device data 80 // MMC: CMD8 - SEND_EXT_CSD - read extended device data 81 8: {READ, RSP_48, true, true}, 82 // CMD9 - SEND_CSD - read device data 83 9: {READ, RSP_136, false, true}, 84 // SD: CMD11 - VOLTAGE_SWITCH - switch to 1.8V signaling 85 11: {READ, RSP_48, true, true}, 86 // CMD12 - STOP_TRANSMISSION - stop transmission 87 12: {READ, RSP_NONE, true, true}, 88 // CMD13 - SEND_STATUS - poll card status 89 13: {READ, RSP_48, true, true}, 90 // CMD16 - SET_BLOCKLEN - define the block length 91 16: {READ, RSP_48, true, true}, 92 // CMD18 - READ_MULTIPLE_BLOCK - read consecutive blocks 93 18: {READ, RSP_48, true, true}, 94 // CMD19 - send tuning block command, ignore responses 95 19: {READ, RSP_48, true, true}, 96 // CMD23 - SET_BLOCK_COUNT - define read/write block count 97 23: {READ, RSP_48, true, true}, 98 // CMD25 - WRITE_MULTIPLE_BLOCK - write consecutive blocks 99 25: {WRITE, RSP_48, true, true}, 100 // SD: ACMD41 - SD_SEND_OP_COND - read capacity information 101 41: {READ, RSP_48, false, false}, 102 // SD: CMD55 - APP_CMD - next command is application specific 103 55: {READ, RSP_48, true, true}, 104 } 105 106 // cmd sends an SD / MMC command as described in 107 // p349, 35.4.3 Send command to card flow chart, IMX6FG 108 func (hw *USDHC) cmd(index uint32, arg uint32, blocks uint32, timeout time.Duration) (err error) { 109 params, ok := cmds[index] 110 111 if !ok { 112 return fmt.Errorf("CMD%d unsupported", index) 113 } 114 115 if timeout == 0 { 116 timeout = DEFAULT_CMD_TIMEOUT 117 } 118 119 // clear interrupt status 120 reg.Write(hw.int_status, 0xffffffff) 121 122 // enable interrupt status 123 reg.Write(hw.int_status_en, 0xffffffff) 124 125 // wait for command inhibit to be clear 126 if !reg.WaitFor(timeout, hw.pres_state, PRES_STATE_CIHB, 1, 0) { 127 return fmt.Errorf("CMD%d command inhibit", index) 128 } 129 130 // wait for data inhibit to be clear 131 if blocks > 0 && !reg.WaitFor(timeout, hw.pres_state, PRES_STATE_CDIHB, 1, 0) { 132 return fmt.Errorf("CMD%d data inhibit", index) 133 } 134 135 // clear interrupts status 136 reg.Write(hw.int_status, 0xffffffff) 137 138 if params.dtd == WRITE && reg.Get(hw.pres_state, PRES_STATE_WPSPL, 1) == 0 { 139 // The uSDHC merely reports on WP, it doesn't really act on it 140 // despite IMX6ULLRM suggesting otherwise (e.g. p4017). 141 return fmt.Errorf("card is write protected") 142 } 143 144 defer func() { 145 if err != nil { 146 reg.Clear(hw.pres_state, PRES_STATE_CIHB) 147 reg.Clear(hw.pres_state, PRES_STATE_CDIHB) 148 reg.Set(hw.sys_ctrl, SYS_CTRL_RSTC) 149 } 150 }() 151 152 dmasel := uint32(DMASEL_NONE) 153 154 if blocks > 0 { 155 dmasel = DMASEL_ADMA2 156 reg.Write(hw.int_signal_en, 0xffffffff) 157 } 158 159 // select DMA mode 160 reg.SetN(hw.prot_ctrl, PROT_CTRL_DMASEL, 0b11, dmasel) 161 162 // set command arguments 163 reg.Write(hw.cmd_arg, arg) 164 165 xfr := reg.Read(hw.cmd_xfr) 166 mix := reg.Read(hw.mix_ctrl) 167 168 // set command index 169 bits.SetN(&xfr, CMD_XFR_TYP_CMDINX, 0b111111, index) 170 // clear special command types 171 bits.SetN(&xfr, CMD_XFR_TYP_CMDTYP, 0b11, 0) 172 173 // command index verification 174 if params.cic { 175 bits.Set(&xfr, CMD_XFR_TYP_CICEN) 176 } else { 177 bits.Clear(&xfr, CMD_XFR_TYP_CICEN) 178 } 179 180 // CRC verification 181 if params.ccc { 182 bits.Set(&xfr, CMD_XFR_TYP_CCCEN) 183 } else { 184 bits.Clear(&xfr, CMD_XFR_TYP_CCCEN) 185 } 186 187 if hw.card.DDR { 188 // enable dual data rate 189 bits.Set(&mix, MIX_CTRL_DDR_EN) 190 } else { 191 bits.Clear(&mix, MIX_CTRL_DDR_EN) 192 } 193 194 // command completion 195 int_status := INT_STATUS_CC 196 197 if blocks > 0 { 198 // transfer completion 199 int_status = INT_STATUS_TC 200 201 // enable data presence 202 bits.Set(&xfr, CMD_XFR_TYP_DPSEL) 203 // enable DMA 204 bits.Set(&mix, MIX_CTRL_DMAEN) 205 // enable automatic CMD12 to stop transactions 206 bits.Set(&mix, MIX_CTRL_AC12EN) 207 208 if blocks > 1 { 209 // multiple blocks 210 bits.Set(&mix, MIX_CTRL_MSBSEL) 211 // enable block count 212 bits.Set(&mix, MIX_CTRL_BCEN) 213 } else { 214 bits.Clear(&mix, MIX_CTRL_MSBSEL) 215 bits.Clear(&mix, MIX_CTRL_BCEN) 216 } 217 } else { 218 bits.Clear(&xfr, CMD_XFR_TYP_DPSEL) 219 bits.Clear(&mix, MIX_CTRL_AC12EN) 220 bits.Clear(&mix, MIX_CTRL_BCEN) 221 bits.Clear(&mix, MIX_CTRL_DMAEN) 222 bits.Clear(&mix, MIX_CTRL_MSBSEL) 223 } 224 225 // set response type 226 bits.SetN(&xfr, CMD_XFR_TYP_RSPTYP, 0b11, params.res) 227 // set data transfer direction 228 bits.SetN(&mix, MIX_CTRL_DTDSEL, 1, params.dtd) 229 230 reg.Write(hw.mix_ctrl, mix) 231 reg.Write(hw.cmd_xfr, xfr) 232 233 // wait for completion 234 if !reg.WaitFor(timeout, hw.int_status, int_status, 1, 1) { 235 err = fmt.Errorf("CMD%d:timeout pres_state:%#x int_status:%#x", index, 236 reg.Read(hw.pres_state), 237 reg.Read(hw.int_status)) 238 // According to the IMX6FG flow chart we shouldn't return in 239 // case of error, but still go ahead and check status. 240 } 241 242 // mask all interrupts 243 reg.Write(hw.int_signal_en, 0) 244 245 // read status 246 status := reg.Read(hw.int_status) 247 248 // p3997, 58.5.3.5.4 Auto CMD12 Error, IMX6ULLRM 249 if (status >> 16) == ((1 << INT_STATUS_AC12E) >> 16) { 250 // retry once CMD12 if the Auto one fails 251 if err := hw.cmd(12, 0, 0, hw.writeTimeout); err == nil { 252 bits.Clear(&status, INT_STATUS_AC12E) 253 } 254 } 255 256 if (status >> 16) > 0 { 257 msg := fmt.Sprintf("pres_state:%#x int_status:%#x", reg.Read(hw.pres_state), status) 258 259 if bits.Get(&status, INT_STATUS_AC12E, 1) == 1 { 260 msg += fmt.Sprintf(" AC12:%#x", reg.Read(hw.ac12_err_status)) 261 } 262 263 err = fmt.Errorf("CMD%d:error %s", index, msg) 264 } 265 266 return 267 } 268 269 func (hw *USDHC) rsp(i int) uint32 { 270 if i > 3 { 271 return 0 272 } 273 274 return reg.Read(hw.cmd_rsp + uint32(i*4)) 275 } 276 277 func (hw *USDHC) rspVal(pos int, mask int) (val uint32) { 278 val = hw.rsp(pos/32) >> (pos % 32) 279 val &= uint32(mask) 280 return 281 } 282 283 func (hw *USDHC) waitState(state int, timeout time.Duration) (err error) { 284 start := time.Now() 285 286 for { 287 // CMD13 - SEND_STATUS - poll card status 288 if err = hw.cmd(13, hw.rca, 0, hw.writeTimeout); err != nil { 289 if time.Since(start) >= timeout { 290 return fmt.Errorf("error polling card status, %v", err) 291 } 292 293 continue 294 } 295 296 curState := (hw.rsp(0) >> STATUS_CURRENT_STATE) & 0b1111 297 298 if curState == uint32(state) { 299 break 300 } 301 302 if time.Since(start) >= timeout { 303 return fmt.Errorf("expected card state %d, got %d", state, curState) 304 } 305 } 306 307 return 308 }