blob: a44f267775c7f02cd73e4d93c2381e700e70576c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <errno.h>
#include <os/os.h>
#include <ipc_nrf5340/ipc_nrf5340.h>
#include <nrfx.h>
#if MYNEWT_VAL(IPC_NRF5340_NET_GPIO)
#include <mcu/nrf5340_hal.h>
#include <bsp/bsp.h>
#endif
/* Currently this allows only for 1-1 connection. */
#define IPC_MAX_CHANS MYNEWT_VAL(IPC_NRF5340_CHANNELS)
#define IPC_BUF_SIZE MYNEWT_VAL(IPC_NRF5340_BUF_SZ)
#if MYNEWT_VAL(MCU_APP_CORE) && MYNEWT_VAL(NRF5340_EMBED_NET_CORE)
/*
* For combine build (app and net) network core image will be included in application
* image only if it is referenced. Those to symbols are used to force linker to
* include network core image.
* Those are used also to provide net core image data (and its size) to virtual
* flash driver that is used by bootloader to bring network image from application
* flash to network flash slot 1.
*/
extern uint8_t _binary_net_core_img_start;
extern uint8_t _binary_net_core_img_end;
#endif
struct ipc_channel {
ipc_nrf5340_recv_cb cb;
void *user_data;
};
struct ipc_shm {
volatile uint16_t head;
volatile uint16_t tail;
uint8_t buf[IPC_BUF_SIZE];
};
static struct ipc_channel ipcs[IPC_MAX_CHANS];
static struct ipc_shm __attribute__((section (".ipc"))) shms[IPC_MAX_CHANS];
static uint16_t
ipc_nrf5340_shm_get_data_length(uint16_t head, uint16_t tail)
{
return ((unsigned int)(head - tail)) % IPC_BUF_SIZE;
}
/* this function assumes that there is enough space for data */
static void
ipc_nrf5340_shm_write(struct ipc_shm *shm, const void *data, uint16_t data_len)
{
uint16_t head = shm->head;
uint16_t len;
len = min(data_len, IPC_BUF_SIZE - head);
memcpy(shm->buf + head, data, len);
/* copy second fragment */
if (data_len > len) {
memcpy(shm->buf, data + len, data_len - len);
}
head += data_len;
head %= IPC_BUF_SIZE;
shm->head = head;
}
static uint16_t
ipc_nrf5340_shm_read(struct ipc_shm *shm, void *buf, struct os_mbuf *om,
uint16_t len)
{
uint16_t head = shm->head;
uint16_t tail = shm->tail;
uint16_t frag_len;
len = min(ipc_nrf5340_shm_get_data_length(head, tail), len);
if (len == 0) {
return 0;
}
if (buf || om) {
if (head >= tail) {
if (buf) {
memcpy(buf, shm->buf + tail, len);
} else {
os_mbuf_append(om, shm->buf + tail, len);
}
} else {
/* in this case we may need to copy two fragments */
frag_len = min(len, IPC_BUF_SIZE - tail);
if (buf) {
memcpy(buf, shm->buf + tail, frag_len);
} else {
os_mbuf_append(om, shm->buf + tail, frag_len);
}
if (frag_len < len) {
assert(tail + frag_len == IPC_BUF_SIZE);
assert(len - frag_len <= head);
if (buf) {
memcpy(buf + frag_len, shm->buf, len - frag_len);
} else {
os_mbuf_append(om, shm->buf, len - frag_len);
}
}
}
}
tail += len;
tail %= IPC_BUF_SIZE;
shm->tail = tail;
return len;
}
static void
ipc_nrf5340_isr(void)
{
uint32_t irq_pend;
int i;
os_trace_isr_enter();
/* Handle only interrupts that were enabled */
irq_pend = NRF_IPC->INTPEND & NRF_IPC->INTEN;
for (i = 0; i < IPC_MAX_CHANS; i++) {
if (irq_pend & (0x1UL << i)) {
NRF_IPC->EVENTS_RECEIVE[i] = 0;
ipcs[i].cb(i, ipcs[i].user_data);
}
}
os_trace_isr_exit();
}
void
ipc_nrf5340_init(void)
{
int i;
#if MYNEWT_VAL(MCU_APP_CORE)
#if MYNEWT_VAL(IPC_NRF5340_NET_GPIO)
unsigned int gpios[] = { MYNEWT_VAL(IPC_NRF5340_NET_GPIO) };
NRF_GPIO_Type *nrf_gpio;
#endif
#if MYNEWT_VAL(MCU_APP_CORE) && MYNEWT_VAL(NRF5340_EMBED_NET_CORE)
/*
* Get network core image size and placement in application flash.
* Then pass those two values in NRF_IPC GPMEM registers to be used
* by virtual flash driver on network side.
*/
if (&_binary_net_core_img_end - &_binary_net_core_img_start > 32) {
NRF_IPC->GPMEM[0] = (uint32_t)&_binary_net_core_img_start;
NRF_IPC->GPMEM[1] = &_binary_net_core_img_end - &_binary_net_core_img_start;
}
#endif
/* Make sure network core if off when we set up IPC */
NRF_RESET_S->NETWORK.FORCEOFF = RESET_NETWORK_FORCEOFF_FORCEOFF_Hold;
memset(shms, 0, sizeof(shms));
#if MYNEWT_VAL(IPC_NRF5340_NET_GPIO)
/* Configure GPIOs for Networking Core */
for (i = 0; i < ARRAY_SIZE(gpios); i++) {
nrf_gpio = HAL_GPIO_PORT(gpios[i]);
nrf_gpio->PIN_CNF[HAL_GPIO_INDEX(gpios[i])] =
GPIO_PIN_CNF_MCUSEL_NetworkMCU << GPIO_PIN_CNF_MCUSEL_Pos;
}
#endif
#endif
#if MYNEWT_VAL(MCU_NET_CORE)
/*
* When network core IPCs starts it clears GPMEM from APP core registers
* So IPC nows that netcore is running.
* This is a workaround that is needed till application side code waits
* on IPC for network core controller to sent NOP first.
*/
#define NRF_APP_IPC_NS ((NRF_IPC_Type *)0x4002A000)
#define NRF_APP_IPC_S ((NRF_IPC_Type *)0x5002A000)
NRF_APP_IPC_S->GPMEM[0] = 0;
NRF_APP_IPC_S->GPMEM[1] = 0;
#endif
/* Enable IPC channels */
for (i = 0; i < IPC_MAX_CHANS; i++) {
NRF_IPC->SEND_CNF[i] = (0x01UL << i);
NRF_IPC->RECEIVE_CNF[i] = 0;
}
NRF_IPC->INTENCLR = 0xFFFF;
NVIC_ClearPendingIRQ(IPC_IRQn);
NVIC_SetVector(IPC_IRQn, (uint32_t)ipc_nrf5340_isr);
NVIC_EnableIRQ(IPC_IRQn);
#if MYNEWT_VAL(MCU_APP_CORE)
/* this allows netcore to access appcore RAM */
NRF_SPU_S->EXTDOMAIN[0].PERM = SPU_EXTDOMAIN_PERM_SECATTR_Secure << SPU_EXTDOMAIN_PERM_SECATTR_Pos;
/* Start Network Core */
NRF_RESET_S->NETWORK.FORCEOFF = RESET_NETWORK_FORCEOFF_FORCEOFF_Release;
#endif
#if MYNEWT_VAL(MCU_APP_CORE) && MYNEWT_VAL(NRF5340_EMBED_NET_CORE)
/*
* TODO: Remove below workaround:
* For now app core waits for NET core to start.
* It is needed for case when network core was updated with new
* image and due to several second delay caused by bootloader
* copy image application starts to send HCI reset requests via
* IPC and network HCI transport is not really ready to handle
* more then one command.
*/
if (&_binary_net_core_img_end - &_binary_net_core_img_start > 32) {
/*
* Application side prepared image for net core.
* When net core starts it's ipc_nrf5340_init() will clear those.
*/
while (NRF_IPC->GPMEM[1]);
}
#endif
}
void
ipc_nrf5340_recv(int channel, ipc_nrf5340_recv_cb cb, void *user_data)
{
assert(channel < IPC_MAX_CHANS);
if (cb) {
assert(ipcs[channel].cb == NULL);
ipcs[channel].cb = cb;
ipcs[channel].user_data = user_data;
NRF_IPC->RECEIVE_CNF[channel] = (0x1UL << channel);
NRF_IPC->INTENSET = (0x1UL << channel);
} else {
NRF_IPC->INTENCLR = (0x1UL << channel);
NRF_IPC->RECEIVE_CNF[channel] = 0;
ipcs[channel].cb = NULL;
ipcs[channel].user_data = NULL;
}
}
int
ipc_nrf5340_send(int channel, const void *data, uint16_t len)
{
struct ipc_shm *shm;
uint16_t frag_len;
uint16_t space;
assert(channel < IPC_MAX_CHANS);
shm = &shms[channel];
if (data && len) {
while (len) {
do {
space = IPC_BUF_SIZE - 1;
space -= ipc_nrf5340_shm_get_data_length(shm->head, shm->tail);
#if !MYNEWT_VAL(IPC_NRF5340_BLOCKING_WRITE)
/* assert since that will always fail for non-blocking write
* indicating use error
*/
assert(len < IPC_BUF_SIZE);
if (len > space) {
return SYS_ENOMEM;
}
#endif
} while (space == 0);
frag_len = min(len, space);
ipc_nrf5340_shm_write(shm, data, frag_len);
NRF_IPC->TASKS_SEND[channel] = 1;
data += frag_len;
len -= frag_len;
}
}
return 0;
}
uint16_t
ipc_nrf5340_read(int channel, void *buf, uint16_t len)
{
assert(channel < IPC_MAX_CHANS);
return ipc_nrf5340_shm_read(&shms[channel], buf, NULL, len);
}
uint16_t
ipc_nrf5340_read_om(int channel, struct os_mbuf *om, uint16_t len)
{
assert(channel < IPC_MAX_CHANS);
return ipc_nrf5340_shm_read(&shms[channel], NULL, om, len);
}
uint16_t
ipc_nrf5340_available(int channel)
{
assert(channel < IPC_MAX_CHANS);
return ipc_nrf5340_shm_get_data_length(shms[channel].head,
shms[channel].tail);
}
uint16_t
ipc_nrf5340_consume(int channel, uint16_t len)
{
assert(channel < IPC_MAX_CHANS);
return ipc_nrf5340_shm_read(&shms[channel], NULL, NULL, len);
}