Skip to content

Commit

Permalink
Add IPM (Inter-Processor-Mailbox) driver to cores/arduino
Browse files Browse the repository at this point in the history
This allows the mailbox to be used for Serial (instead of polling shared
memory). The CurieMailbox library, in the codk-m-corelibs branch, will
not require any API changes, but will now just be a thin wrapper around
the methods in mailbox.cpp
  • Loading branch information
eriknyquist committed May 17, 2017
1 parent c58480c commit 3d2e408
Show file tree
Hide file tree
Showing 7 changed files with 210 additions and 171 deletions.
163 changes: 163 additions & 0 deletions cores/arduino/mailbox.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
#include <string.h>
#include "interrupt.h"
#include "scss_registers.h"
#include "mailbox.h"

#define CHANNEL_STS_MASK 0x1
#define CHANNEL_INT_MASK 0x2
#define CTRL_WORD_MASK 0x7FFFFFFF
#define CHALL_STATUS_MASK 0xFFFF
#define CHANNEL_STS_BITS (CHANNEL_STS_MASK | CHANNEL_INT_MASK)

/* Mailbox channel status register */
#define IO_REG_MAILBOX_CHALL_STS (SCSS_REGISTER_BASE + 0xAC0)

typedef struct mbox_channel mbox_channel_t;
typedef struct mbox_intmask mbox_intmask_t;

/* Represents the registers for a single mailbox channel */
struct mbox_channel {
uint32_t ctrl;
uint32_t data[CHANNEL_DATA_WORDS];
uint32_t sts;
};

/* Mailbox interrupt mask; right now we only care about the
* second byte, for SS mailbox interrupts (one bit per mailbox channel) */
struct mbox_intmask {
uint8_t lmt_intmask;
uint8_t ss_intmask;
uint8_t lmt_halt;
uint8_t ss_halt;
};

static volatile mbox_intmask_t *intmask;
static volatile mbox_channel_t *mbox;
static void (*callbacks[NUM_MAILBOX_CHANNELS])(CurieMailboxMsg);

void mailbox_register (int channel, void(*callback)(CurieMailboxMsg))
{
if (channel >= 0 && channel < NUM_MAILBOX_CHANNELS) {
callbacks[channel] = callback;
}
}

void mailbox_unregister (int channel)
{
if (channel >= 0 && channel < NUM_MAILBOX_CHANNELS) {
callbacks[channel] = 0;
}
}

void mailbox_disable_receive (int channel)
{
intmask->ss_intmask |= 1 << channel;
}

void mailbox_enable_receive (int channel)
{
intmask->ss_intmask &= ~(1 << channel);
}

static void do_callback (int channel, CurieMailboxMsg& msg)
{
void (*cb)(CurieMailboxMsg) = callbacks[channel];
if (cb) {
cb(msg);
}
}

static void mailbox_read (int channel, CurieMailboxMsg& msg)
{
unsigned int i;

/* Copy channel data into CurieMailboxMsg object */
msg.id = mbox[channel].ctrl & CTRL_WORD_MASK;
msg.channel = channel;

for (i = 0; i < CHANNEL_DATA_WORDS; ++i) {
msg.data[i] = mbox[channel].data[i];
}

/* Clear channel status & interrupt flags */
mbox[channel].sts |= CHANNEL_STS_BITS;
}

void mailbox_write (CurieMailboxMsg& msg)
{
int i;
uint32_t key;

/* Can't write if channel status flag is set */
while ((mbox[msg.channel].sts & CHANNEL_STS_MASK));
key = interrupt_lock();

/* Poplate channel payload */
mbox[msg.channel].ctrl |= (msg.id & CTRL_WORD_MASK);
for (i = 0; i < CHANNEL_DATA_WORDS; ++i) {
mbox[msg.channel].data[i] = msg.data[i];
}

/* Trigger interupt to host */
mbox[msg.channel].ctrl |= ~(CTRL_WORD_MASK);

/* Wait for HW to set the channel status bit */
while (!(mbox[msg.channel].sts & CHANNEL_STS_MASK));

/* Wait for destination processor to clear channel status bit */
while ((mbox[msg.channel].sts & CHANNEL_STS_MASK));
interrupt_unlock(key);
}

static uint16_t get_chall_sts (void)
{
return MMIO_REG_VAL(IO_REG_MAILBOX_CHALL_STS) & CHALL_STATUS_MASK;
}

static void mailbox_isr (void)
{
int i;
uint32_t sts;
CurieMailboxMsg msg;

sts = get_chall_sts();
/* Get channel number */
for (i = 0; i < NUM_MAILBOX_CHANNELS; ++i) {
if (sts & (1 << (i * 2 + 1))) {
break;
}
}

mailbox_read(i, msg);
do_callback(i, msg);
}

static void mailbox_hardware_init (void)
{
int i;

for (i = 0; i < NUM_MAILBOX_CHANNELS; ++i) {
mbox[i].sts &= ~(CHANNEL_STS_BITS);
}
}

static void mailbox_interrupts_init (bool master)
{
interrupt_disable(SOC_MBOX_INTERRUPT);

/* Mask SS mailbox interrupts for all channels;
* Unmasking is done by enableReceive */
intmask->ss_intmask = 0xFF;

if (master) mailbox_hardware_init();
interrupt_connect(SOC_MBOX_INTERRUPT, mailbox_isr);
interrupt_enable(SOC_MBOX_INTERRUPT);
}

void mailbox_init (bool master)
{
intmask = (mbox_intmask_t *)IO_REG_MAILBOX_INT_MASK;
mbox = (mbox_channel_t *)IO_REG_MAILBOX_BASE;
memset(callbacks, 0, sizeof(callbacks));
mailbox_interrupts_init(master);
}
21 changes: 21 additions & 0 deletions cores/arduino/mailbox.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#ifndef _MAILBOX_H_
#define _MAILBOX_H_

#define NUM_MAILBOX_CHANNELS 8
#define CHANNEL_DATA_WORDS 4

class CurieMailboxMsg {
public:
uint32_t data[CHANNEL_DATA_WORDS];
uint32_t id;
int channel = 0;
};

void mailbox_init (bool master);
void mailbox_register (int channel, void(*callback)(CurieMailboxMsg));
void mailbox_unregister (int channel);
void mailbox_enable_receive (int channel);
void mailbox_disable_receive (int channel);
void mailbox_write(CurieMailboxMsg& msg);

#endif
26 changes: 6 additions & 20 deletions libraries/CurieMailbox/README.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,27 +34,13 @@ can fill. Here's how you might populate a CurieMailboxMsg;
API reference:


void CurieMailbox::begin (bool master)
void CurieMailbox::begin ()

Enable interrupts for the mailbox, and optionally initialise the
mailbox hardware.
Enable interrupts for the mailbox, and set up the mailbox hardware for use

Params:

bool master: flag indicating whether mailbox hardware should be initialised.
The first processor to access the mailbox should do this; if in doubt, set
to false (default is false, no initialisation).



void CurieMailbox::begin (void)

Enable interrupts for the mailbox, and do not initialise the hardware.

Params:

None.

None


void CurieMailbox::enableReceive (unsigned int channel)
Expand All @@ -77,9 +63,9 @@ void CurieMailbox::disableReceive (unsigned int channel)

Params:

int channel: integer value (0-7) representing the channel to be enabled.
If the passed value does not represent a valid channel, then the highest
channel will be used instead.
int channel: integer value between 0 and CurieMailbox.numChannels,
representing the channel to be enabled. If the passed value does not
represent a valid channel, then the highest channel will be used instead.


int CurieMailbox::available (void)
Expand Down
Loading

0 comments on commit 3d2e408

Please sign in to comment.