github.com/usbarmory/tamago@v0.0.0-20240508072735-8612bbe1e454/soc/nxp/usdhc/cmd.go (about) 1 // NXP Ultra Secured Digital Host Controller (uSDHC) driver 2 // https://github.com/usbarmory/tamago 3 // 4 // IP: https://www.mobiveil.com/esdhc/ 5 // 6 // Copyright (c) WithSecure Corporation 7 // https://foundry.withsecure.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/usbarmory/tamago/bits" 19 "github.com/usbarmory/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 // command index verification 173 bits.SetTo(&xfr, CMD_XFR_TYP_CICEN, params.cic) 174 // CRC verification 175 bits.SetTo(&xfr, CMD_XFR_TYP_CCCEN, params.ccc) 176 // dual data rate 177 bits.SetTo(&mix, MIX_CTRL_DDR_EN, hw.card.DDR) 178 179 // command completion 180 int_status := INT_STATUS_CC 181 182 if blocks > 0 { 183 // transfer completion 184 int_status = INT_STATUS_TC 185 // enable data presence 186 bits.Set(&xfr, CMD_XFR_TYP_DPSEL) 187 // enable DMA 188 bits.Set(&mix, MIX_CTRL_DMAEN) 189 // enable automatic CMD12 to stop transactions 190 bits.Set(&mix, MIX_CTRL_AC12EN) 191 // multiple blocks 192 bits.SetTo(&mix, MIX_CTRL_MSBSEL, blocks > 1) 193 // block count 194 bits.SetTo(&mix, MIX_CTRL_BCEN, blocks > 1) 195 } else { 196 bits.Clear(&xfr, CMD_XFR_TYP_DPSEL) 197 bits.Clear(&mix, MIX_CTRL_AC12EN) 198 bits.Clear(&mix, MIX_CTRL_BCEN) 199 bits.Clear(&mix, MIX_CTRL_DMAEN) 200 bits.Clear(&mix, MIX_CTRL_MSBSEL) 201 } 202 203 // set response type 204 bits.SetN(&xfr, CMD_XFR_TYP_RSPTYP, 0b11, params.res) 205 // set data transfer direction 206 bits.SetN(&mix, MIX_CTRL_DTDSEL, 1, params.dtd) 207 208 reg.Write(hw.mix_ctrl, mix) 209 reg.Write(hw.cmd_xfr, xfr) 210 211 // wait for completion 212 if !reg.WaitFor(timeout, hw.int_status, int_status, 1, 1) { 213 err = fmt.Errorf("CMD%d:timeout pres_state:%#x int_status:%#x", index, 214 reg.Read(hw.pres_state), 215 reg.Read(hw.int_status)) 216 // According to the IMX6FG flow chart we shouldn't return in 217 // case of error, but still go ahead and check status. 218 } 219 220 // mask all interrupts 221 reg.Write(hw.int_signal_en, 0) 222 223 // read status 224 status := reg.Read(hw.int_status) 225 226 // p3997, 58.5.3.5.4 Auto CMD12 Error, IMX6ULLRM 227 if (status >> 16) == ((1 << INT_STATUS_AC12E) >> 16) { 228 // retry once CMD12 if the Auto one fails 229 if err := hw.cmd(12, 0, 0, hw.writeTimeout); err == nil { 230 bits.Clear(&status, INT_STATUS_AC12E) 231 } 232 } 233 234 if (status >> 16) > 0 { 235 msg := fmt.Sprintf("pres_state:%#x int_status:%#x", reg.Read(hw.pres_state), status) 236 237 if bits.Get(&status, INT_STATUS_AC12E, 1) == 1 { 238 msg += fmt.Sprintf(" AC12:%#x", reg.Read(hw.ac12_err_status)) 239 } 240 241 err = fmt.Errorf("CMD%d:error %s", index, msg) 242 } 243 244 return 245 } 246 247 func (hw *USDHC) rsp(i int) uint32 { 248 if i > 3 { 249 return 0 250 } 251 252 return reg.Read(hw.cmd_rsp + uint32(i*4)) 253 } 254 255 func (hw *USDHC) rspVal(pos int, mask int) (val uint32) { 256 val = hw.rsp(pos/32) >> (pos % 32) 257 val &= uint32(mask) 258 return 259 } 260 261 func (hw *USDHC) waitState(state int, timeout time.Duration) (err error) { 262 start := time.Now() 263 264 for { 265 // CMD13 - SEND_STATUS - poll card status 266 if err = hw.cmd(13, hw.rca, 0, hw.writeTimeout); err != nil { 267 if time.Since(start) >= timeout { 268 return fmt.Errorf("error polling card status, %v", err) 269 } 270 271 continue 272 } 273 274 curState := (hw.rsp(0) >> STATUS_CURRENT_STATE) & 0b1111 275 276 if curState == uint32(state) { 277 break 278 } 279 280 if time.Since(start) >= timeout { 281 return fmt.Errorf("expected card state %d, got %d", state, curState) 282 } 283 } 284 285 return 286 }