Files
drivers/flash/transport_qspi_samd.go
deadprogram 8b3075fdba build: remove older format build tags
Signed-off-by: deadprogram <ron@hybridgroup.com>
2022-12-21 17:47:14 -05:00

248 lines
7.1 KiB
Go

//go:build atsamd51
package flash
import (
"device/sam"
"machine"
"runtime/volatile"
"unsafe"
)
// NewQSPI returns a pointer to a flash device that uses the QSPI peripheral to
// communicate with a serial memory chip.
func NewQSPI(cs, sck, d0, d1, d2, d3 machine.Pin) *Device {
return &Device{
trans: &qspiTransport{
cs: cs,
sck: sck,
d0: d0,
d1: d1,
d2: d2,
d3: d3,
},
}
}
// QSPI address space on SAMD51 is 0x04000000 to 0x05000000
const (
// Low address of the QSPI address space on SAMD51
qspi_AHB_LO = 0x04000000
// High address of the QSPI address space on SAMD51
qspi_AHB_HI = 0x05000000
// Instruction frame for running sending a command to the device
iframeRunCommand = 0x0 |
sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI |
sam.QSPI_INSTRFRAME_ADDRLEN_24BITS |
sam.QSPI_INSTRFRAME_INSTREN |
(sam.QSPI_INSTRFRAME_TFRTYPE_READ << sam.QSPI_INSTRFRAME_TFRTYPE_Pos)
// Instruction frame for running a command that returns data
iframeReadCommand = 0x0 |
sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI |
sam.QSPI_INSTRFRAME_ADDRLEN_24BITS |
sam.QSPI_INSTRFRAME_INSTREN |
sam.QSPI_INSTRFRAME_DATAEN |
(sam.QSPI_INSTRFRAME_TFRTYPE_READ << sam.QSPI_INSTRFRAME_TFRTYPE_Pos)
// Instruction frame to set up the device to read from memory
iframeReadMemory = 0x0 |
sam.QSPI_INSTRFRAME_WIDTH_QUAD_OUTPUT |
sam.QSPI_INSTRFRAME_ADDRLEN_24BITS |
sam.QSPI_INSTRFRAME_INSTREN |
sam.QSPI_INSTRFRAME_DATAEN |
sam.QSPI_INSTRFRAME_ADDREN |
(8 << sam.QSPI_INSTRFRAME_DUMMYLEN_Pos) |
(sam.QSPI_INSTRFRAME_TFRTYPE_READMEMORY << sam.QSPI_INSTRFRAME_TFRTYPE_Pos)
// Instruction frame for running a command that requires parameter data
iframeWriteCommand = 0x0 |
sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI |
sam.QSPI_INSTRFRAME_ADDRLEN_24BITS |
sam.QSPI_INSTRFRAME_INSTREN |
(sam.QSPI_INSTRFRAME_TFRTYPE_WRITE << sam.QSPI_INSTRFRAME_TFRTYPE_Pos)
// Instruction frame to set up the device for writing to memory
iframeWriteMemory = 0x0 |
sam.QSPI_INSTRFRAME_WIDTH_QUAD_OUTPUT |
sam.QSPI_INSTRFRAME_ADDRLEN_24BITS |
sam.QSPI_INSTRFRAME_INSTREN |
sam.QSPI_INSTRFRAME_ADDREN |
sam.QSPI_INSTRFRAME_DATAEN |
(sam.QSPI_INSTRFRAME_TFRTYPE_WRITEMEMORY << sam.QSPI_INSTRFRAME_TFRTYPE_Pos)
// Instruction frame for running an erase command that requires and address
iframeEraseCommand = 0x0 |
sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI |
sam.QSPI_INSTRFRAME_ADDRLEN_24BITS |
sam.QSPI_INSTRFRAME_INSTREN |
sam.QSPI_INSTRFRAME_ADDREN |
(sam.QSPI_INSTRFRAME_TFRTYPE_WRITE << sam.QSPI_INSTRFRAME_TFRTYPE_Pos)
)
type qspiTransport struct {
cs machine.Pin
sck machine.Pin
d0 machine.Pin
d1 machine.Pin
d2 machine.Pin
d3 machine.Pin
}
func (q qspiTransport) configure(config *DeviceConfig) {
// enable main clocks
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_QSPI_)
sam.MCLK.AHBMASK.SetBits(sam.MCLK_AHBMASK_QSPI_)
sam.MCLK.AHBMASK.ClearBits(sam.MCLK_AHBMASK_QSPI_2X_)
sam.QSPI.CTRLA.SetBits(sam.QSPI_CTRLA_SWRST)
// enable all pins to be PinCom
q.d0.Configure(machine.PinConfig{Mode: machine.PinCom})
q.d1.Configure(machine.PinConfig{Mode: machine.PinCom})
q.d2.Configure(machine.PinConfig{Mode: machine.PinCom})
q.d3.Configure(machine.PinConfig{Mode: machine.PinCom})
q.cs.Configure(machine.PinConfig{Mode: machine.PinCom})
q.sck.Configure(machine.PinConfig{Mode: machine.PinCom})
// start out with 4Mhz
// can ignore the error, 4Mhz is always a valid speed
_ = q.setClockSpeed(4e6)
// configure the CTRLB register
sam.QSPI.CTRLB.Set(sam.QSPI_CTRLB_MODE_MEMORY |
(sam.QSPI_CTRLB_DATALEN_8BITS << sam.QSPI_CTRLB_DATALEN_Pos) |
(sam.QSPI_CTRLB_CSMODE_LASTXFER << sam.QSPI_CTRLB_CSMODE_Pos))
// enable the peripheral
sam.QSPI.CTRLA.SetBits(sam.QSPI_CTRLA_ENABLE)
}
func (q qspiTransport) supportQuadMode() bool {
return true
}
func (q qspiTransport) setClockSpeed(hz uint32) error {
// The clock speed for the QSPI peripheral is controlled by a divider, so
// we can't set the requested speed exactly. Instead we will increment the
// divider until the speed is less than or equal to the speed requested.
for div, freq := uint32(1), machine.CPUFrequency(); div < 256; div++ {
if freq/div <= hz {
sam.QSPI.BAUD.Set(div << sam.QSPI_BAUD_BAUD_Pos)
return nil
}
}
return ErrInvalidClockSpeed
}
func (q qspiTransport) runCommand(cmd byte) (err error) {
q.runInstruction(cmd, iframeRunCommand)
q.endTransfer()
return
}
func (q qspiTransport) readCommand(cmd byte, buf []byte) (err error) {
q.disableAndClearCache()
q.runInstruction(cmd, iframeReadCommand)
q.readInto(buf, 0)
q.endTransfer()
q.enableCache()
return
}
func (q qspiTransport) readMemory(addr uint32, buf []byte) (err error) {
if (addr + uint32(len(buf))) > (qspi_AHB_HI - qspi_AHB_LO) {
return ErrInvalidAddrRange
}
q.disableAndClearCache()
q.runInstruction(cmdQuadRead, iframeReadMemory)
q.readInto(buf, addr)
q.endTransfer()
q.enableCache()
return
}
func (q qspiTransport) writeCommand(cmd byte, data []byte) (err error) {
var dataen uint32
if len(data) > 0 {
dataen = sam.QSPI_INSTRFRAME_DATAEN
}
q.disableAndClearCache()
q.runInstruction(cmd, iframeWriteCommand|dataen)
q.writeFrom(data, 0)
q.endTransfer()
q.enableCache()
return
}
func (q qspiTransport) writeMemory(addr uint32, data []byte) (err error) {
if (addr + uint32(len(data))) > (qspi_AHB_HI - qspi_AHB_LO) {
return ErrInvalidAddrRange
}
q.disableAndClearCache()
q.runInstruction(cmdQuadPageProgram, iframeWriteMemory)
q.writeFrom(data, addr)
q.endTransfer()
q.enableCache()
return
}
func (q qspiTransport) eraseCommand(cmd byte, addr uint32) (err error) {
q.disableAndClearCache()
sam.QSPI.INSTRADDR.Set(addr)
q.runInstruction(cmd, iframeEraseCommand)
q.endTransfer()
q.enableCache()
return
}
func (q qspiTransport) runInstruction(cmd byte, iframe uint32) {
sam.QSPI.INSTRCTRL.Set(uint32(cmd))
sam.QSPI.INSTRFRAME.Set(iframe)
sam.QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet
}
func (q qspiTransport) enableCache() {
sam.CMCC.CTRL.SetBits(sam.CMCC_CTRL_CEN)
}
func (q qspiTransport) disableAndClearCache() {
sam.CMCC.CTRL.ClearBits(sam.CMCC_CTRL_CEN)
for sam.CMCC.SR.HasBits(sam.CMCC_SR_CSTS) {
}
sam.CMCC.MAINT0.SetBits(sam.CMCC_MAINT0_INVALL)
}
func (q qspiTransport) endTransfer() {
sam.QSPI.CTRLA.Set(sam.QSPI_CTRLA_ENABLE | sam.QSPI_CTRLA_LASTXFER)
for !sam.QSPI.INTFLAG.HasBits(sam.QSPI_INTFLAG_INSTREND) {
}
sam.QSPI.INTFLAG.Set(sam.QSPI_INTFLAG_INSTREND)
}
func (q qspiTransport) readInto(buf []byte, addr uint32) {
var ptr = qspi_AHB_LO + uintptr(addr)
for i := range buf {
buf[i] = volatile.LoadUint8((*uint8)(unsafe.Pointer(ptr)))
ptr++
}
/* // NB(bcg): for some reason this reads data that results from commands in
// a different byte order than the loop above, but works fine for reading
// from memory. Oddly, the above loop seems to work fine in both cases.
ln := len(buf)
sl := (*[1 << 28]byte)(unsafe.Pointer(uintptr(qspi_AHB_LO + addr)))[:ln:ln]
copy(buf, sl)
*/
}
func (q qspiTransport) writeFrom(buf []byte, addr uint32) {
var ptr = qspi_AHB_LO + uintptr(addr)
for i := range buf {
volatile.StoreUint8((*uint8)(unsafe.Pointer(ptr)), buf[i])
ptr++
}
}