github.com/usbarmory/tamago@v0.0.0-20240508072735-8612bbe1e454/soc/bcm2835/mailbox.go (about) 1 // BCM2835 SoC Mailbox support 2 // https://github.com/usbarmory/tamago 3 // 4 // Use of this source code is governed by the license 5 // that can be found in the LICENSE file. 6 7 // 8 // Mailboxes are used for inter-processor communication, in particular 9 // the VideoCore processor. 10 // 11 12 package bcm2835 13 14 import ( 15 "encoding/binary" 16 "fmt" 17 "runtime" 18 "sync" 19 20 "github.com/usbarmory/tamago/dma" 21 "github.com/usbarmory/tamago/internal/reg" 22 ) 23 24 // We reserve the 'gap' above excStack and below TEXT segment start for 25 // mailbox usage. There is nothing requiring use of this region, but it 26 // is convenient since it is always available. Other regions could be 27 // used by adjusting ramSize to provide space at top of address range. 28 const ( 29 MAILBOX_REGION_BASE = 0xC000 30 MAILBOX_REGION_SIZE = 0x4000 31 ) 32 33 // Registers for using mailbox 34 const ( 35 MAILBOX_BASE = 0xB880 36 MAILBOX_READ_REG = MAILBOX_BASE + 0x00 37 MAILBOX_STATUS_REG = MAILBOX_BASE + 0x18 38 MAILBOX_WRITE_REG = MAILBOX_BASE + 0x20 39 MAILBOX_FULL = 0x80000000 40 MAILBOX_EMPTY = 0x40000000 41 ) 42 43 type mailbox struct { 44 Region *dma.Region 45 Mutex sync.Mutex 46 } 47 48 // Mailbox provides access to the BCM2835 mailbox used to communicate with 49 // the VideoCore CPU 50 var Mailbox = mailbox{} 51 52 func init() { 53 // We don't use this region for DMA, but dma package provides a convenient 54 // block allocation system. 55 Mailbox.Region, _ = dma.NewRegion(MAILBOX_REGION_BASE|DRAM_FLAG_NOCACHE, MAILBOX_REGION_SIZE, false) 56 } 57 58 type MailboxTag struct { 59 ID uint32 60 Buffer []byte 61 } 62 63 type MailboxMessage struct { 64 MinSize int 65 Code uint32 66 Tags []MailboxTag 67 } 68 69 func (m *MailboxMessage) Error() bool { 70 return m.Code == 0x80000001 71 } 72 73 func (m *MailboxMessage) Tag(code uint32) *MailboxTag { 74 for i, tag := range m.Tags { 75 if tag.ID&0x7FFFFFFF == code&0x7FFFFFFF { 76 return &m.Tags[i] 77 } 78 } 79 80 return nil 81 } 82 83 // Call exchanges message via a mailbox channel 84 // 85 // The caller is responsible for ensuring the 'tags' in the 86 // message have sufficient buffer allocated for the response 87 // expected. The response replaces the input message. 88 func (mb *mailbox) Call(channel int, message *MailboxMessage) { 89 size := 8 // Message Header 90 for _, tag := range message.Tags { 91 // 3 word tag header + tag data (padded to 32-bits) 92 size += int(12 + (uint32(len(tag.Buffer)+3) & 0xFFFFFFFC)) 93 } 94 95 size += 4 // null tag 96 97 // Allow client to request bigger buffer for response 98 if size < message.MinSize { 99 size = message.MinSize 100 } 101 102 // Allocate temporary location-fixed buffer 103 addr, buf := mb.Region.Reserve(size, 16) 104 defer mb.Region.Release(addr) 105 106 binary.LittleEndian.PutUint32(buf[0:], uint32(size)) 107 binary.LittleEndian.PutUint32(buf[4:], 0) 108 109 offset := 8 110 for _, tag := range message.Tags { 111 binary.LittleEndian.PutUint32(buf[offset:], tag.ID) 112 binary.LittleEndian.PutUint32(buf[offset+4:], uint32(len(tag.Buffer))) 113 binary.LittleEndian.PutUint32(buf[offset+8:], 0) 114 copy(buf[offset+12:], tag.Buffer) 115 116 offset += int(12 + (uint32(len(tag.Buffer)+3) & 0xFFFFFFFC)) 117 } 118 119 // terminating null tag 120 binary.LittleEndian.PutUint32(buf[offset:], 0x0) 121 122 mb.exchangeMessage(channel, uint32(addr)) 123 124 message.Tags = make([]MailboxTag, 0, len(message.Tags)) 125 message.Code = binary.LittleEndian.Uint32(buf[4:]) 126 offset = 8 127 128 for offset < len(buf) { 129 tag := MailboxTag{} 130 tag.ID = binary.LittleEndian.Uint32(buf[offset:]) 131 132 // Terminating null tag 133 if tag.ID == 0 { 134 break 135 } 136 137 len := binary.LittleEndian.Uint32(buf[offset+4:]) 138 139 if len > uint32(size-offset) { 140 panic("malformed mailbox response, over-sized tag") 141 } 142 143 tag.Buffer = make([]byte, len) 144 copy(tag.Buffer, buf[offset+12:]) 145 146 // Move to next tag 147 offset += int(12 + (len+3)&0xFFFFFFFC) 148 message.Tags = append(message.Tags, tag) 149 } 150 } 151 152 func (mb *mailbox) exchangeMessage(channel int, addr uint32) { 153 if (addr & 0xF) != 0 { 154 panic("Mailbox message must be 16-byte aligned") 155 } 156 157 // For now, hold a global lock so only 1 outstanding mailbox 158 // message at any time. 159 mb.Mutex.Lock() 160 defer mb.Mutex.Unlock() 161 162 // Wait for space to send 163 for (reg.Read(peripheralBase+MAILBOX_STATUS_REG) & MAILBOX_FULL) != 0 { 164 runtime.Gosched() 165 } 166 167 // Send 168 reg.Write(peripheralBase+MAILBOX_WRITE_REG, uint32(channel&0xF)|uint32(addr&0xFFFFFFF0)) 169 170 // Wait for response 171 for (reg.Read(peripheralBase+MAILBOX_STATUS_REG) & MAILBOX_EMPTY) != 0 { 172 runtime.Gosched() 173 } 174 175 // Read response 176 data := reg.Read(peripheralBase + MAILBOX_READ_REG) 177 178 // Ensure response corresponds to request (note response data over-writes request data) 179 if (data & 0xF) != uint32(channel&0xF) { 180 panic(fmt.Sprintf("overlapping messages, got response for channel %d, expecting %d", data&0xF, channel&0xF)) 181 } 182 if (data & 0xFFFFFFF0) != (addr & 0xFFFFFFF0) { 183 panic(fmt.Sprintf("overlapping messages, got response for channel %d, expecting %d", data&0xFFFFFFF0, addr&0xFFFFFFF0)) 184 } 185 }