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  }