tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/flash/transport_qspi_samd.go (about)

     1  //go:build atsamd51
     2  
     3  package flash
     4  
     5  import (
     6  	"device/sam"
     7  	"machine"
     8  	"runtime/volatile"
     9  	"unsafe"
    10  )
    11  
    12  // NewQSPI returns a pointer to a flash device that uses the QSPI peripheral to
    13  // communicate with a serial memory chip.
    14  func NewQSPI(cs, sck, d0, d1, d2, d3 machine.Pin) *Device {
    15  	return &Device{
    16  		trans: &qspiTransport{
    17  			cs:  cs,
    18  			sck: sck,
    19  			d0:  d0,
    20  			d1:  d1,
    21  			d2:  d2,
    22  			d3:  d3,
    23  		},
    24  	}
    25  }
    26  
    27  // QSPI address space on SAMD51 is 0x04000000 to 0x05000000
    28  const (
    29  	// Low address of the QSPI address space on SAMD51
    30  	qspi_AHB_LO = 0x04000000
    31  
    32  	// High address of the QSPI address space on SAMD51
    33  	qspi_AHB_HI = 0x05000000
    34  
    35  	// Instruction frame for running sending a command to the device
    36  	iframeRunCommand = 0x0 |
    37  		sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI |
    38  		sam.QSPI_INSTRFRAME_ADDRLEN_24BITS |
    39  		sam.QSPI_INSTRFRAME_INSTREN |
    40  		(sam.QSPI_INSTRFRAME_TFRTYPE_READ << sam.QSPI_INSTRFRAME_TFRTYPE_Pos)
    41  
    42  	// Instruction frame for running a command that returns data
    43  	iframeReadCommand = 0x0 |
    44  		sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI |
    45  		sam.QSPI_INSTRFRAME_ADDRLEN_24BITS |
    46  		sam.QSPI_INSTRFRAME_INSTREN |
    47  		sam.QSPI_INSTRFRAME_DATAEN |
    48  		(sam.QSPI_INSTRFRAME_TFRTYPE_READ << sam.QSPI_INSTRFRAME_TFRTYPE_Pos)
    49  
    50  	// Instruction frame to set up the device to read from memory
    51  	iframeReadMemory = 0x0 |
    52  		sam.QSPI_INSTRFRAME_WIDTH_QUAD_OUTPUT |
    53  		sam.QSPI_INSTRFRAME_ADDRLEN_24BITS |
    54  		sam.QSPI_INSTRFRAME_INSTREN |
    55  		sam.QSPI_INSTRFRAME_DATAEN |
    56  		sam.QSPI_INSTRFRAME_ADDREN |
    57  		(8 << sam.QSPI_INSTRFRAME_DUMMYLEN_Pos) |
    58  		(sam.QSPI_INSTRFRAME_TFRTYPE_READMEMORY << sam.QSPI_INSTRFRAME_TFRTYPE_Pos)
    59  
    60  	// Instruction frame for running a command that requires parameter data
    61  	iframeWriteCommand = 0x0 |
    62  		sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI |
    63  		sam.QSPI_INSTRFRAME_ADDRLEN_24BITS |
    64  		sam.QSPI_INSTRFRAME_INSTREN |
    65  		(sam.QSPI_INSTRFRAME_TFRTYPE_WRITE << sam.QSPI_INSTRFRAME_TFRTYPE_Pos)
    66  
    67  	// Instruction frame to set up the device for writing to memory
    68  	iframeWriteMemory = 0x0 |
    69  		sam.QSPI_INSTRFRAME_WIDTH_QUAD_OUTPUT |
    70  		sam.QSPI_INSTRFRAME_ADDRLEN_24BITS |
    71  		sam.QSPI_INSTRFRAME_INSTREN |
    72  		sam.QSPI_INSTRFRAME_ADDREN |
    73  		sam.QSPI_INSTRFRAME_DATAEN |
    74  		(sam.QSPI_INSTRFRAME_TFRTYPE_WRITEMEMORY << sam.QSPI_INSTRFRAME_TFRTYPE_Pos)
    75  
    76  	// Instruction frame for running an erase command that requires and address
    77  	iframeEraseCommand = 0x0 |
    78  		sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI |
    79  		sam.QSPI_INSTRFRAME_ADDRLEN_24BITS |
    80  		sam.QSPI_INSTRFRAME_INSTREN |
    81  		sam.QSPI_INSTRFRAME_ADDREN |
    82  		(sam.QSPI_INSTRFRAME_TFRTYPE_WRITE << sam.QSPI_INSTRFRAME_TFRTYPE_Pos)
    83  )
    84  
    85  type qspiTransport struct {
    86  	cs  machine.Pin
    87  	sck machine.Pin
    88  	d0  machine.Pin
    89  	d1  machine.Pin
    90  	d2  machine.Pin
    91  	d3  machine.Pin
    92  }
    93  
    94  func (q qspiTransport) configure(config *DeviceConfig) {
    95  
    96  	// enable main clocks
    97  	sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_QSPI_)
    98  	sam.MCLK.AHBMASK.SetBits(sam.MCLK_AHBMASK_QSPI_)
    99  	sam.MCLK.AHBMASK.ClearBits(sam.MCLK_AHBMASK_QSPI_2X_)
   100  
   101  	sam.QSPI.CTRLA.SetBits(sam.QSPI_CTRLA_SWRST)
   102  
   103  	// enable all pins to be PinCom
   104  	q.d0.Configure(machine.PinConfig{Mode: machine.PinCom})
   105  	q.d1.Configure(machine.PinConfig{Mode: machine.PinCom})
   106  	q.d2.Configure(machine.PinConfig{Mode: machine.PinCom})
   107  	q.d3.Configure(machine.PinConfig{Mode: machine.PinCom})
   108  	q.cs.Configure(machine.PinConfig{Mode: machine.PinCom})
   109  	q.sck.Configure(machine.PinConfig{Mode: machine.PinCom})
   110  
   111  	// start out with 4Mhz
   112  	// can ignore the error, 4Mhz is always a valid speed
   113  	_ = q.setClockSpeed(4e6)
   114  
   115  	// configure the CTRLB register
   116  	sam.QSPI.CTRLB.Set(sam.QSPI_CTRLB_MODE_MEMORY |
   117  		(sam.QSPI_CTRLB_DATALEN_8BITS << sam.QSPI_CTRLB_DATALEN_Pos) |
   118  		(sam.QSPI_CTRLB_CSMODE_LASTXFER << sam.QSPI_CTRLB_CSMODE_Pos))
   119  
   120  	// enable the peripheral
   121  	sam.QSPI.CTRLA.SetBits(sam.QSPI_CTRLA_ENABLE)
   122  }
   123  
   124  func (q qspiTransport) supportQuadMode() bool {
   125  	return true
   126  }
   127  
   128  func (q qspiTransport) setClockSpeed(hz uint32) error {
   129  	// The clock speed for the QSPI peripheral is controlled by a divider, so
   130  	// we can't set the requested speed exactly. Instead we will increment the
   131  	// divider until the speed is less than or equal to the speed requested.
   132  	for div, freq := uint32(1), machine.CPUFrequency(); div < 256; div++ {
   133  		if freq/div <= hz {
   134  			sam.QSPI.BAUD.Set(div << sam.QSPI_BAUD_BAUD_Pos)
   135  			return nil
   136  		}
   137  	}
   138  	return ErrInvalidClockSpeed
   139  }
   140  
   141  func (q qspiTransport) runCommand(cmd byte) (err error) {
   142  	q.runInstruction(cmd, iframeRunCommand)
   143  	q.endTransfer()
   144  	return
   145  }
   146  
   147  func (q qspiTransport) readCommand(cmd byte, buf []byte) (err error) {
   148  	q.disableAndClearCache()
   149  	q.runInstruction(cmd, iframeReadCommand)
   150  	q.readInto(buf, 0)
   151  	q.endTransfer()
   152  	q.enableCache()
   153  	return
   154  }
   155  
   156  func (q qspiTransport) readMemory(addr uint32, buf []byte) (err error) {
   157  	if (addr + uint32(len(buf))) > (qspi_AHB_HI - qspi_AHB_LO) {
   158  		return ErrInvalidAddrRange
   159  	}
   160  	q.disableAndClearCache()
   161  	q.runInstruction(cmdQuadRead, iframeReadMemory)
   162  	q.readInto(buf, addr)
   163  	q.endTransfer()
   164  	q.enableCache()
   165  	return
   166  }
   167  
   168  func (q qspiTransport) writeCommand(cmd byte, data []byte) (err error) {
   169  	var dataen uint32
   170  	if len(data) > 0 {
   171  		dataen = sam.QSPI_INSTRFRAME_DATAEN
   172  	}
   173  	q.disableAndClearCache()
   174  	q.runInstruction(cmd, iframeWriteCommand|dataen)
   175  	q.writeFrom(data, 0)
   176  	q.endTransfer()
   177  	q.enableCache()
   178  	return
   179  }
   180  
   181  func (q qspiTransport) writeMemory(addr uint32, data []byte) (err error) {
   182  	if (addr + uint32(len(data))) > (qspi_AHB_HI - qspi_AHB_LO) {
   183  		return ErrInvalidAddrRange
   184  	}
   185  	q.disableAndClearCache()
   186  	q.runInstruction(cmdQuadPageProgram, iframeWriteMemory)
   187  	q.writeFrom(data, addr)
   188  	q.endTransfer()
   189  	q.enableCache()
   190  	return
   191  }
   192  
   193  func (q qspiTransport) eraseCommand(cmd byte, addr uint32) (err error) {
   194  	q.disableAndClearCache()
   195  	sam.QSPI.INSTRADDR.Set(addr)
   196  	q.runInstruction(cmd, iframeEraseCommand)
   197  	q.endTransfer()
   198  	q.enableCache()
   199  	return
   200  }
   201  
   202  func (q qspiTransport) runInstruction(cmd byte, iframe uint32) {
   203  	sam.QSPI.INSTRCTRL.Set(uint32(cmd))
   204  	sam.QSPI.INSTRFRAME.Set(iframe)
   205  	sam.QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet
   206  }
   207  
   208  func (q qspiTransport) enableCache() {
   209  	sam.CMCC.CTRL.SetBits(sam.CMCC_CTRL_CEN)
   210  }
   211  
   212  func (q qspiTransport) disableAndClearCache() {
   213  	sam.CMCC.CTRL.ClearBits(sam.CMCC_CTRL_CEN)
   214  	for sam.CMCC.SR.HasBits(sam.CMCC_SR_CSTS) {
   215  	}
   216  	sam.CMCC.MAINT0.SetBits(sam.CMCC_MAINT0_INVALL)
   217  }
   218  
   219  func (q qspiTransport) endTransfer() {
   220  	sam.QSPI.CTRLA.Set(sam.QSPI_CTRLA_ENABLE | sam.QSPI_CTRLA_LASTXFER)
   221  	for !sam.QSPI.INTFLAG.HasBits(sam.QSPI_INTFLAG_INSTREND) {
   222  	}
   223  	sam.QSPI.INTFLAG.Set(sam.QSPI_INTFLAG_INSTREND)
   224  }
   225  
   226  func (q qspiTransport) readInto(buf []byte, addr uint32) {
   227  	var ptr = qspi_AHB_LO + uintptr(addr)
   228  	for i := range buf {
   229  		buf[i] = volatile.LoadUint8((*uint8)(unsafe.Pointer(ptr)))
   230  		ptr++
   231  	}
   232  	/* // NB(bcg): for some reason this reads data that results from commands in
   233  	   // a different byte order than the loop above, but works fine for reading
   234  	   // from memory. Oddly, the above loop seems to work fine in both cases.
   235  		ln := len(buf)
   236  		sl := (*[1 << 28]byte)(unsafe.Pointer(uintptr(qspi_AHB_LO + addr)))[:ln:ln]
   237  		copy(buf, sl)
   238  	*/
   239  }
   240  
   241  func (q qspiTransport) writeFrom(buf []byte, addr uint32) {
   242  	var ptr = qspi_AHB_LO + uintptr(addr)
   243  	for i := range buf {
   244  		volatile.StoreUint8((*uint8)(unsafe.Pointer(ptr)), buf[i])
   245  		ptr++
   246  	}
   247  }