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  }