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 }