diff --git a/drivers/Kconfig b/drivers/Kconfig index 20e77b4a97165..6767e79d8299c 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -16,6 +16,7 @@ source "drivers/i3c/Kconfig" source "drivers/spi/Kconfig" source "drivers/i2s/Kconfig" source "drivers/ipcc/Kconfig" +source "drivers/mbox/Kconfig" source "drivers/timers/Kconfig" source "drivers/analog/Kconfig" source "drivers/audio/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index adeb7a25f0252..7e27f64395c2a 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -42,6 +42,7 @@ include i2c/Make.defs include i2s/Make.defs include i3c/Make.defs include ipcc/Make.defs +include mbox/Make.defs include input/Make.defs include ioexpander/Make.defs include lcd/Make.defs diff --git a/drivers/mbox/CMakeLists.txt b/drivers/mbox/CMakeLists.txt new file mode 100644 index 0000000000000..8f2c5db8e0415 --- /dev/null +++ b/drivers/mbox/CMakeLists.txt @@ -0,0 +1,31 @@ +# ############################################################################## +# drivers/mbox/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# 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. +# +# ############################################################################## + +if(CONFIG_MBOX) + set(SRCS mbox.c) + + if(CONFIG_MBOX_PL320) + list(APPEND SRCS pl320.c) + endif() + + target_sources(drivers PRIVATE ${SRCS}) +endif() diff --git a/drivers/mbox/Kconfig b/drivers/mbox/Kconfig new file mode 100644 index 0000000000000..03b147abaafa0 --- /dev/null +++ b/drivers/mbox/Kconfig @@ -0,0 +1,27 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +menuconfig MBOX + bool "MBOX Driver Support" + default n + ---help--- + This selection enables building of the "upper-half" MBOX driver. + See include/nuttx/mbox/mbox.h for further MBOX driver information. + +if MBOX + +config MBOX_TRACE + bool "Enable MBOX trace debug" + default n + +menuconfig MBOX_PL320 + bool "PL320 Chip support" + default n + ---help--- + ARM PL320 Inter-Processor Communications Module (IPCM) driver. + The PL320 provides up to 32 mailbox channels for inter-core + messaging with interrupt-driven notification and acknowledgment. + +endif diff --git a/drivers/mbox/Make.defs b/drivers/mbox/Make.defs new file mode 100644 index 0000000000000..72ad737080545 --- /dev/null +++ b/drivers/mbox/Make.defs @@ -0,0 +1,38 @@ +############################################################################ +# drivers/mbox/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# 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 mailbox drivers + +ifeq ($(CONFIG_MBOX),y) + +CSRCS += mbox.c + +ifeq ($(CONFIG_MBOX_PL320),y) +CSRCS += pl320.c +endif + +# Include mbox build support + +DEPPATH += --dep-path mbox +VPATH += :mbox + +endif # CONFIG_MBOX diff --git a/drivers/mbox/mbox.c b/drivers/mbox/mbox.c new file mode 100644 index 0000000000000..4c5d648bcdf2f --- /dev/null +++ b/drivers/mbox/mbox.c @@ -0,0 +1,763 @@ +/**************************************************************************** + * drivers/mbox/mbox.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: mbox_enqueue_data + * + * Description: + * Enqueue the message data waiting to be sent to circbuf + * This is a buffering operation for non-blocking send. + ****************************************************************************/ + +static int mbox_enqueue_data(FAR struct mbox_chan_s *chan, + FAR const void *data, size_t size); + +/**************************************************************************** + * Name: mbox_dequeue_data + * + * Description: + * Dequeue the message data from circbuf to do actual send + * This is a buffering operation for non-blocking send. + ****************************************************************************/ + +static int mbox_dequeue_data(FAR struct mbox_chan_s *chan, FAR void *data, + FAR size_t *size); + +/**************************************************************************** + * Name: mbox_send_data + * + * Description: + * Submit data to the lower half and mark this channel as active. + ****************************************************************************/ + +static int mbox_send_data(FAR struct mbox_chan_s *chan, + FAR const void *data, size_t size, + clock_t timeout); + +/**************************************************************************** + * Name: submit_msg + * + * Description: + * Drain a message from circbuf of specific mbox channel, then call + * lower half ops->send() to perform actual message transmission. + ****************************************************************************/ + +static int submit_msg(FAR struct mbox_chan_s *chan); + +/**************************************************************************** + * Name: tx_expiry + * + * Description: + * Called when tx timer expires + ****************************************************************************/ + +static void tx_expiry(wdparm_t arg); + +/**************************************************************************** + * Name: notify_txdone + * + * Description: + * Notify that the mbox channel has completed TX. + ****************************************************************************/ + +static void notify_txdone(FAR struct mbox_chan_s *chan); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int mbox_enqueue_data(FAR struct mbox_chan_s *chan, + FAR const void *data, size_t size) +{ + if (size > MBOX_MAX_MSG_SIZE) + { + return -EMSGSIZE; + } + + if (circbuf_space(&chan->txbuf) < size + sizeof(size)) + { + return -ENOBUFS; + } + + circbuf_write(&chan->txbuf, (FAR void *)&size, sizeof(size_t)); + circbuf_write(&chan->txbuf, (FAR void *)data, size); + +#ifdef CONFIG_MBOX_TRACE + ++chan->stats.buffered; +#endif + return 0; +} + +static int mbox_dequeue_data(FAR struct mbox_chan_s *chan, FAR void *data, + FAR size_t *size) +{ + if (circbuf_is_empty(&chan->txbuf)) + { + return -ENODATA; + } + + circbuf_read(&chan->txbuf, (FAR void *)size, sizeof(size_t)); + circbuf_read(&chan->txbuf, (FAR void *)data, *size); + +#ifdef CONFIG_MBOX_TRACE + --chan->stats.buffered; +#endif + return 0; +} + +static int mbox_send_data(FAR struct mbox_chan_s *chan, + FAR const void *data, size_t size, + clock_t timeout) +{ + int ret; + + ret = chan->dev->ops->send(chan, data, size); + if (ret < 0) + { + return ret; + } + + chan->txstate = MBOX_TX_ACTIVE; + chan->timeout = timeout; + + if (timeout != 0 && timeout != MBOX_WAIT_FOREVER) + { + wd_start(&chan->timer, timeout, tx_expiry, (wdparm_t)chan); + } + +#ifdef CONFIG_MBOX_TRACE + ++chan->stats.sent; +#endif + return OK; +} + +static int submit_msg(FAR struct mbox_chan_s *chan) +{ + uint8_t data[MBOX_MAX_MSG_SIZE]; + size_t size = 0; + int ret; + + if (chan->txstate != MBOX_TX_IDLE) + { + return -EBUSY; + } + + ret = mbox_dequeue_data(chan, data, &size); + if (ret < 0) + { + return ret; + } + + return mbox_send_data(chan, data, size, MBOX_WAIT_FOREVER); +} + +static void notify_txdone(FAR struct mbox_chan_s *chan) +{ + if (chan->txwaiting) + { + chan->txwaiting = false; + nxsem_post(&chan->txsem); + } +} + +static void tx_expiry(wdparm_t arg) +{ + FAR struct mbox_chan_s *chan = (FAR struct mbox_chan_s *)arg; + irqstate_t flags; + + flags = enter_critical_section(); + + if (chan->txstate != MBOX_TX_ACTIVE) + { + leave_critical_section(flags); + return; + } + +#ifdef CONFIG_MBOX_TRACE + chan->stats.timeout++; +#endif + + /* txfinish is expected to clear any pending hardware completion state + * before the upper half reuses this channel. + */ + + if (chan->dev->ops->txfinish) + { + chan->dev->ops->txfinish(chan); + } + + chan->txstate = MBOX_TX_IDLE; + chan->txresult = -ETIMEDOUT; + notify_txdone(chan); + + if (!circbuf_is_empty(&chan->txbuf)) + { + submit_msg(chan); + } + + leave_critical_section(flags); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mbox_chan_tx_done + * + * Description: + * This function should be called upon receiving an ACK/completion (when + * triggered by an interrupt or when polling detects the txacked status) + * This function will first cancel tx timer because TX has completed. Then + * lower-half ops->txfinish() will be called to finish current + * transmission, clean related state and prepare for next transmission. + * If there are any remaining message in circbuf, it will be submitted to + * mailbox controller to send to remote. + * + * Input Parameters: + * chan - A pointer to the mbox channel which received ack + * + ****************************************************************************/ + +void mbox_chan_tx_done(FAR struct mbox_chan_s *chan) +{ + irqstate_t flags; + + flags = enter_critical_section(); + + if (!chan || chan->dir != MBOX_TX) + { + leave_critical_section(flags); + return; + } + + /* Cancel timer because ACK has reached */ + + if (WDOG_ISACTIVE(&chan->timer)) + { + wd_cancel(&chan->timer); + } + + /* Finish this transmission, clean related state for next transmit */ + + if (chan->dev->ops->txfinish) + { + chan->dev->ops->txfinish(chan); + } + +#ifdef CONFIG_MBOX_TRACE + ++chan->stats.acked; +#endif + + if (chan->txstate != MBOX_TX_ACTIVE) + { + leave_critical_section(flags); + return; + } + + chan->txstate = MBOX_TX_IDLE; + chan->txresult = OK; + + /* notify txdone to blocking API */ + + notify_txdone(chan); + + /* Send next data if ringbuf not empty */ + + if (!circbuf_is_empty(&chan->txbuf)) + { + submit_msg(chan); + } + + leave_critical_section(flags); + + return; +} + +/**************************************************************************** + * Name: mbox_chan_rx_data + * + * Description: + * This function should be called when RX happened (when triggered by an + * interrupt or when polling detects the rxavailable status) + * This function will call lower-half ops->recv to receive the data from + * remote and send acknowledge to remote sender. Finally, the user-defined + * callback function will be called to push data to the upper layer. + * + * Input Parameters: + * chan - A pointer to the mbox channel which rx happened + * + ****************************************************************************/ + +int mbox_chan_rx_data(FAR struct mbox_chan_s *chan) +{ + uint8_t data[MBOX_MAX_MSG_SIZE]; + int ret; + + if (!chan || chan->dir != MBOX_RX) + { + return -EINVAL; + } + + /* Receive data from lower-half ops */ + + ret = chan->dev->ops->recv(chan, data); + if (ret < 0) + { + if (chan->callback) + { + chan->callback(ret, chan, chan->priv, NULL, 0); + } + + return ret; + } + +#ifdef CONFIG_MBOX_TRACE + ++chan->stats.received; +#endif + + /* Send acknowledge to remote sender */ + + if (chan->dev->ops->acknowledge) + { + chan->dev->ops->acknowledge(chan, NULL, 0); + } + +#ifdef CONFIG_MBOX_TRACE + ++chan->stats.acked; +#endif + + /* Call user-defined callback */ + + if (chan->callback) + { + chan->callback(OK, chan, chan->priv, data, ret); + } + + return ret; +} + +/**************************************************************************** + * Name: mbox_get_chan + * + * Description: + * Get a mbox channel handle by platform-specific argument. + * + * Input Parameters: + * dev - A pointer to mbox dev + * arg - Platform-specific argument to index a channel + * + * Returned Value: + * Pointer to mbox channel on success; NULL on failure + * + ****************************************************************************/ + +FAR struct mbox_chan_s *mbox_get_chan(FAR struct mbox_dev_s *dev, + FAR void *arg) +{ + if (!dev || !dev->ops || !dev->ops->getchan) + { + return NULL; + } + + return MBOX_GET_CHAN(dev, arg); +} + +/**************************************************************************** + * Name: mbox_chan_init + * + * Description: + * Initialize the members of the specific mbox channel structure. + * This function could be called to initialize the base part of + * a platform-specific channel in lower-half initialization. + * + * Input Parameters: + * chan - A pointer to mbox channel to be initialized + * dev - A pointer to mbox dev + * dir - Mbox xfer direction TX or RX + * buffer - The internal buffer allocated for the txbuf of mbox_chan_s + * size - The size of the txbuf in mbox_chan_s + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +int mbox_chan_init(FAR struct mbox_chan_s *chan, FAR struct mbox_dev_s *dev, + enum mbox_direction_e dir, FAR void *buffer, size_t size) +{ + FAR const struct mbox_ops_s *ops; + int ret = 0; + bool sem_inited = false; + + if (!chan || !dev || !dev->ops) + { + return -EINVAL; + } + + ops = dev->ops; + + if (dir != MBOX_TX && dir != MBOX_RX) + { + return -EINVAL; + } + + if (dir == MBOX_TX && + (!ops->send || !ops->txready || !ops->txfinish || + !buffer || size == 0)) + { + return -EINVAL; + } + + if (dir == MBOX_RX && !ops->recv) + { + return -EINVAL; + } + + chan->dev = dev; + chan->dir = dir; + chan->timeout = MBOX_WAIT_FOREVER; + chan->txstate = MBOX_TX_IDLE; + chan->txwaiting = false; + chan->txresult = OK; + + if (dir == MBOX_TX) + { + ret = nxsem_init(&chan->txsem, 0, 0); + if (ret < 0) + { + goto fail; + } + + sem_inited = true; + + memset(buffer, 0, size); + ret = circbuf_init(&chan->txbuf, buffer, size); + if (ret < 0) + { + goto fail; + } + } + +#ifdef CONFIG_MBOX_TRACE + memset(&chan->stats, 0, sizeof(chan->stats)); +#endif + return 0; + +fail: + if (sem_inited) + { + nxsem_destroy(&chan->txsem); + } + + return ret; +} + +/**************************************************************************** + * Name: mbox_ticksend + * + * Description: + * Send a message through a specific send channel. + * + * Input Parameters: + * sender - A Pointer to a mbox sender + * data - Message Buffer to send + * size - Size of data in bytes + * timeout - The ticks to wait until the message is acknowledged or + * completed. If timeout is zero, the message is submitted or + * buffered without waiting. + * + * Returned Value: + * 0: success, <0: A negated errno + * -EINVAL: Invalid parameter + * -EIO: IO Error happened when send data + * -EAGAIN: Not txready now, please try again + * -ETIMEDOUT: Waiting for TX timeout + * + ****************************************************************************/ + +int mbox_ticksend(FAR struct mbox_sender_s *sender, FAR const void *data, + size_t size, clock_t timeout) +{ + FAR struct mbox_chan_s *chan; + irqstate_t flags; + int ret = 0; + + if (!sender || !sender->chan || !data) + { + return -EINVAL; + } + + if (size > MBOX_MAX_MSG_SIZE) + { + return -EMSGSIZE; + } + + chan = sender->chan; + + if (chan->dir != MBOX_TX) + { + return -EINVAL; + } + + flags = enter_critical_section(); + + if (timeout == 0) + { + if (chan->txstate != MBOX_TX_IDLE) + { + ret = mbox_enqueue_data(chan, data, size); + goto out; + } + + if (!chan->dev->ops->txready(chan)) + { + ret = -EAGAIN; + goto out; + } + + ret = mbox_send_data(chan, data, size, MBOX_WAIT_FOREVER); + goto out; + } + + /* Blocking send until timeout. If not ready, return -EAGAIN */ + + if (chan->txstate != MBOX_TX_IDLE || + !chan->dev->ops->txready(chan)) + { + ret = -EAGAIN; + goto out; + } + + chan->txwaiting = true; + chan->txresult = -EINPROGRESS; + + ret = mbox_send_data(chan, data, size, timeout); + if (ret < 0) + { + chan->txwaiting = false; + goto out; + } + + leave_critical_section(flags); + + /* Wait until tx_expiry() or mbox_chan_tx_done() completes this transfer. */ + + ret = nxsem_wait_uninterruptible(&chan->txsem); + if (ret < 0) + { + flags = enter_critical_section(); + chan->txwaiting = false; + leave_critical_section(flags); + return ret; + } + + flags = enter_critical_section(); + ret = chan->txresult; + leave_critical_section(flags); + return ret; + +out: + leave_critical_section(flags); + return ret; +} + +/**************************************************************************** + * Name: mbox_send + * + * Description: + * Send a message through a specific send channel. It will block until + * the message is acknowledged or completed. + * + * Input Parameters: + * sender - A Pointer to a mbox sender + * data - Message to send + * size - Bytes of data to send + * + * Returned Value: + * 0: success, <0: A negated errno + * -EINVAL: Invalid parameter + * -EIO: IO Error happened when send data + * -EAGAIN: Not txready now, please try again + * -ETIMEDOUT: Waiting for TX timeout + * + ****************************************************************************/ + +int mbox_send(FAR struct mbox_sender_s *sender, FAR const void *data, + size_t size) +{ + return mbox_ticksend(sender, data, size, MBOX_WAIT_FOREVER); +} + +/**************************************************************************** + * Name: mbox_register_rxcallback + * + * Description: + * Register a user-defined callback function to receiver channel. This + * callback function will be called when rxavailable. + * + * Input Parameters: + * receiver - Mbox receiver which binding to a rx channel + * callback - User-defined callback + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +int mbox_register_rxcallback(FAR struct mbox_receiver_s *receiver, + mbox_callback_t callback, FAR void *arg) +{ + if (!receiver || !receiver->chan || receiver->chan->dir != MBOX_RX) + { + return -EINVAL; + } + + return MBOX_REGISTER_CALLBACK(receiver->chan, callback, arg); +} + +/**************************************************************************** + * Name: mbox_unregister_rxcallback + * + * Description: + * Unregister a previously registered callback function from receiver + * channel. + * + * Input Parameters: + * receiver - Mbox receiver which binding to a rx channel + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +int mbox_unregister_rxcallback(FAR struct mbox_receiver_s *receiver) +{ + if (!receiver || !receiver->chan || receiver->chan->dir != MBOX_RX) + { + return -EINVAL; + } + + return MBOX_REGISTER_CALLBACK(receiver->chan, NULL, NULL); +} + +/**************************************************************************** + * Name: mbox_chan_deinit + * + * Description: + * Deinitialize a mbox channel, releasing the semaphore and circbuf + * resources that were allocated during mbox_chan_init. + * + * Input Parameters: + * chan - A pointer to mbox channel to be deinitialized + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +int mbox_chan_deinit(FAR struct mbox_chan_s *chan) +{ + if (!chan) + { + return -EINVAL; + } + + /* Only TX channels have txsem and txbuf initialized */ + + if (chan->dir == MBOX_TX) + { + if (WDOG_ISACTIVE(&chan->timer)) + { + wd_cancel(&chan->timer); + } + + nxsem_destroy(&chan->txsem); + circbuf_uninit(&chan->txbuf); + } + + chan->callback = NULL; + chan->dev = NULL; + + return 0; +} + +#ifdef CONFIG_MBOX_TRACE +/**************************************************************************** + * Name: mbox_chan_get_stats + * + * Description: + * This function is only visible when configure CONFIG_MBOX_TRACE. + * This function will retrieves the message sending and receiving + * statistics data of specific mbox channel and returns it to + * the caller. + * + * Input Parameters: + * chan - A pointer to the mbox channel which received ack + * stats - A pointer to the mbox_stats_s passed by caller + * + ****************************************************************************/ + +void mbox_chan_get_stats(FAR struct mbox_chan_s *chan, + FAR struct mbox_stats_s *stats) +{ + if (!chan || !stats) + { + return; + } + + *stats = chan->stats; +} +#endif diff --git a/drivers/mbox/pl320.c b/drivers/mbox/pl320.c new file mode 100644 index 0000000000000..4f6ebaba8b863 --- /dev/null +++ b/drivers/mbox/pl320.c @@ -0,0 +1,616 @@ +/**************************************************************************** + * drivers/mbox/pl320.c + * Driver for the PL320 mailbox + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define getreg32(a) (*(volatile uint32_t *)(a)) +#define putreg32(v, a) (*(volatile uint32_t *)(a) = (v)) + +#define IPCMXSOURCE(m) (((m) * 0x40)) +#define IPCMXDSET(m) (((m) * 0x40) + 0x004) +#define IPCMXDCLEAR(m) (((m) * 0x40) + 0x008) +#define IPCMXDSTATUS(m) (((m) * 0x40) + 0x00C) +#define IPCMXMODE(m) (((m) * 0x40) + 0x010) +#define IPCMXMSET(m) (((m) * 0x40) + 0x014) +#define IPCMXMCLEAR(m) (((m) * 0x40) + 0x018) +#define IPCMXMSTATUS(m) (((m) * 0x40) + 0x01C) +#define IPCMXSEND(m) (((m) * 0x40) + 0x020) +#define IPCMXDR(m, dr) (((m) * 0x40) + ((dr) * 4) + 0x024) + +#define IPCMMIS(irq) (((irq) * 8) + 0x800) +#define IPCMRIS(irq) (((irq) * 8) + 0x804) + +#define MBOX_MASK(n) (1U << (n)) +#define CHAN_MASK(n) (1U << (n)) + +#define IPCM_DR_SIZE (7) + +#define SEND_INACTIVE (0x0) +#define SEND_MSG (0x1) +#define SEND_ACK (0x2) + +/**************************************************************************** + * Private Type Definitions + ****************************************************************************/ + +struct pl320_dev_s +{ + struct mbox_dev_s base; + uintptr_t reg_base; + int ipcmint; + int irq_num; + struct pl320_chan_s *chan_map[PL320_MAX_CHAN_NUM]; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static struct mbox_chan_s *pl320_getchan(FAR struct mbox_dev_s *dev, + FAR void *arg); + +static int pl320_setup(FAR struct mbox_chan_s *chan, FAR void *arg); + +static int pl320_shutdown(FAR struct mbox_chan_s *chan); + +static int pl320_send(FAR struct mbox_chan_s *chan, + FAR const void *data, size_t size); + +static int pl320_recv(FAR struct mbox_chan_s *chan, FAR void *data); + +static int pl320_acknowledge(FAR struct mbox_chan_s *chan, + FAR const void *data, size_t size); + +static int pl320_txfinish(FAR struct mbox_chan_s *chan); + +static int pl320_setcallback(FAR struct mbox_chan_s *chan, + mbox_callback_t callback, FAR void *arg); + +static bool pl320_rxavailable(FAR struct mbox_chan_s *chan); + +static bool pl320_txready(FAR struct mbox_chan_s *chan); + +static bool pl320_txacked(FAR struct mbox_chan_s *chan); + +static int pl320_irq_handler(int irq, void *context, void *arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct mbox_ops_s g_pl320mbox_ops = +{ + .getchan = pl320_getchan, + .setup = pl320_setup, + .shutdown = pl320_shutdown, + .send = pl320_send, + .recv = pl320_recv, + .acknowledge = pl320_acknowledge, + .txfinish = pl320_txfinish, + .setcallback = pl320_setcallback, + .rxavailable = pl320_rxavailable, + .txready = pl320_txready, + .txacked = pl320_txacked +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pl320_getchan + * + * Description: + * Get pl320 channel handle by chan id + * + ****************************************************************************/ + +static struct mbox_chan_s *pl320_getchan(FAR struct mbox_dev_s *dev, + FAR void *arg) +{ + uint32_t ch = (uint32_t)(uintptr_t)arg; + FAR struct pl320_dev_s *priv; + + if (!dev || ch >= PL320_MAX_CHAN_NUM) + { + return NULL; + } + + priv = (FAR struct pl320_dev_s *)dev; + return (FAR struct mbox_chan_s *)priv->chan_map[ch]; +} + +/**************************************************************************** + * Name: pl320_setup + * + * Description: + * Set up a PL320 channel before transmission. Configure IPCMXSOURCE, + * IPCMXDEST and IPCMXMSET registers if the ctrl field is set. + * + ****************************************************************************/ + +static int pl320_setup(FAR struct mbox_chan_s *chan, FAR void *arg) +{ + FAR struct pl320_chan_s *priv_chan; + FAR struct pl320_dev_s *priv_dev; + + if (!chan) + { + return -EINVAL; + } + + priv_chan = (FAR struct pl320_chan_s *)chan; + priv_dev = (FAR struct pl320_dev_s *)chan->dev; + + if (priv_chan->ctrl) + { + int src = priv_chan->src; + int dst = priv_chan->dst; + int mbox = priv_chan->mbox; + + if (getreg32(priv_dev->reg_base + IPCMXSOURCE(mbox)) != 0) + { + putreg32(0x0, priv_dev->reg_base + IPCMXSOURCE(mbox)); + } + + putreg32(CHAN_MASK(src), priv_dev->reg_base + IPCMXSOURCE(mbox)); + putreg32(CHAN_MASK(dst), priv_dev->reg_base + IPCMXDSET(mbox)); + putreg32(CHAN_MASK(src) | CHAN_MASK(dst), + priv_dev->reg_base + IPCMXMSET(mbox)); + } + + return 0; +} + +/**************************************************************************** + * Name: pl320_shutdown + * + * Description: + * Release ownership of this channel by clearing IPCMXSOURCE + * + ****************************************************************************/ + +static int pl320_shutdown(FAR struct mbox_chan_s *chan) +{ + FAR struct pl320_chan_s *priv_chan; + FAR struct pl320_dev_s *priv_dev; + + if (!chan) + { + return -EINVAL; + } + + priv_chan = (FAR struct pl320_chan_s *)chan; + priv_dev = (FAR struct pl320_dev_s *)chan->dev; + + putreg32(0x0, priv_dev->reg_base + IPCMXSOURCE(priv_chan->mbox)); + return 0; +} + +/**************************************************************************** + * Name: pl320_send + * + * Description: + * Send a mailbox message over the PL320 mailbox. This function is + * attached to the virtual mbox_dev ".send()" pointer. This way every + * time a mailbox message is sent it is called. + * + * Input Parameters: + * chan - Pointer to the (virtual) mbox_chan_s. + * msg - Message to be send. + * + * Returned Value: + * 0 on success or a negative error. + * + ****************************************************************************/ + +static int pl320_send(FAR struct mbox_chan_s *chan, + FAR const void *data, size_t size) +{ + FAR struct pl320_chan_s *priv_chan; + FAR struct pl320_dev_s *priv_dev; + uint32_t buffer[IPCM_DR_SIZE]; + + if (!chan || !data) + { + return -EINVAL; + } + + if (size > PL320_MSG_SIZE) + { + return -EMSGSIZE; + } + + priv_chan = (FAR struct pl320_chan_s *)chan; + priv_dev = (FAR struct pl320_dev_s *)chan->dev; + + memset(buffer, 0, sizeof(buffer)); + memcpy(buffer, data, size); + for (size_t i = 0; i < IPCM_DR_SIZE; ++i) + { + putreg32(buffer[i], priv_dev->reg_base + IPCMXDR(priv_chan->mbox, i)); + } + + putreg32(SEND_MSG, priv_dev->reg_base + IPCMXSEND(priv_chan->mbox)); + return 0; +} + +/**************************************************************************** + * Name: pl320_recv + * + * Description: + * Recv a mailbox message over the PL320 mailbox. This function is + * called when rxavailable happened. + * + * Input Parameters: + * chan - Pointer to the (virtual) mbox_chan_s. + * msg - Message to hold the data read + * + * Returned Value: + * 0 on success or a negative error. + * + ****************************************************************************/ + +static int pl320_recv(FAR struct mbox_chan_s *chan, FAR void *data) +{ + FAR struct pl320_chan_s *priv_chan; + FAR struct pl320_dev_s *priv_dev; + FAR volatile uint32_t *buffer; + + if (!chan || !data) + { + return -EINVAL; + } + + priv_chan = (FAR struct pl320_chan_s *)chan; + priv_dev = (FAR struct pl320_dev_s *)chan->dev; + + buffer = (FAR volatile uint32_t *)data; + for (size_t i = 0; i < IPCM_DR_SIZE; ++i) + { + buffer[i] = getreg32(priv_dev->reg_base + IPCMXDR(priv_chan->mbox, i)); + } + + return IPCM_DR_SIZE * sizeof(uint32_t); +} + +/**************************************************************************** + * Name: pl320_acknowledge + * + * Description: + * Send an acknowledge to remote sender after received data. + * + ****************************************************************************/ + +static int pl320_acknowledge(FAR struct mbox_chan_s *chan, + FAR const void *data, size_t size) +{ + FAR struct pl320_chan_s *priv_chan; + FAR struct pl320_dev_s *priv_dev; + + if (!chan) + { + return -EINVAL; + } + + priv_chan = (FAR struct pl320_chan_s *)chan; + priv_dev = (FAR struct pl320_dev_s *)chan->dev; + + putreg32(SEND_ACK, priv_dev->reg_base + IPCMXSEND(priv_chan->mbox)); + return 0; +} + +/**************************************************************************** + * Name: pl320_txfinish + * + * Description: + * Finish the transmission and prepare for the next transfer. + * + ****************************************************************************/ + +static int pl320_txfinish(FAR struct mbox_chan_s *chan) +{ + FAR struct pl320_chan_s *priv_chan; + FAR struct pl320_dev_s *priv_dev; + + if (!chan) + { + return -EINVAL; + } + + priv_chan = (FAR struct pl320_chan_s *)chan; + priv_dev = (FAR struct pl320_dev_s *)chan->dev; + + putreg32(SEND_INACTIVE, priv_dev->reg_base + IPCMXSEND(priv_chan->mbox)); + return 0; +} + +/**************************************************************************** + * Name: pl320_setcallback + * + * Description: + * Register an receiving callback function to designated mailbox channel. + * The callback function will be saved in pl320_chans structure of + * the corresponding channel. + ****************************************************************************/ + +static int pl320_setcallback(FAR struct mbox_chan_s *chan, + mbox_callback_t callback, FAR void *arg) +{ + if (!chan) + { + return -EINVAL; + } + + chan->callback = callback; + chan->priv = arg; + return 0; +} + +/**************************************************************************** + * Name: pl320_rxavailable + * + * Description: + * Check if the channel is ready to receive data. + * + ****************************************************************************/ + +static bool pl320_rxavailable(FAR struct mbox_chan_s *chan) +{ + FAR struct pl320_chan_s *priv_chan; + FAR struct pl320_dev_s *priv_dev; + + if (!chan) + { + return false; + } + + priv_chan = (FAR struct pl320_chan_s *)chan; + priv_dev = (FAR struct pl320_dev_s *)chan->dev; + + return getreg32(priv_dev->reg_base + IPCMXSEND(priv_chan->mbox)) + == SEND_MSG; +} + +/**************************************************************************** + * Name: pl320_txready + * + * Description: + * Check if the channel is ready to transmit message. + * + ****************************************************************************/ + +static bool pl320_txready(FAR struct mbox_chan_s *chan) +{ + FAR struct pl320_chan_s *priv_chan; + FAR struct pl320_dev_s *priv_dev; + + if (!chan) + { + return false; + } + + priv_chan = (FAR struct pl320_chan_s *)chan; + priv_dev = (FAR struct pl320_dev_s *)chan->dev; + + return getreg32(priv_dev->reg_base + IPCMXSEND(priv_chan->mbox)) + == SEND_INACTIVE; +} + +/**************************************************************************** + * Name: pl320_txacked + * + * Description: + * Check if the channel is acked. + * + ****************************************************************************/ + +static bool pl320_txacked(FAR struct mbox_chan_s *chan) +{ + FAR struct pl320_chan_s *priv_chan; + FAR struct pl320_dev_s *priv_dev; + + if (!chan) + { + return false; + } + + priv_chan = (FAR struct pl320_chan_s *)chan; + priv_dev = (FAR struct pl320_dev_s *)chan->dev; + + return getreg32(priv_dev->reg_base + IPCMXSEND(priv_chan->mbox)) + == SEND_ACK; +} + +/**************************************************************************** + * Name: pl320_irq_handler + * + * Description: + * Interrupt handler, it happened when the IPCMXSEND register was set. + * + ****************************************************************************/ + +static int pl320_irq_handler(int irq, void *context, void *arg) +{ + FAR struct pl320_dev_s *dev = (FAR struct pl320_dev_s *)arg; + DEBUGASSERT(dev != NULL); + + uint32_t irq_stat = getreg32(dev->reg_base + IPCMMIS(dev->ipcmint)); + + for (int m = 0; m < PL320_MAX_MBOX_NUM; ++m) + { + /* Find the mailbox that triggered this interrupt */ + + if (irq_stat & MBOX_MASK(m)) + { + uint32_t src = getreg32(dev->reg_base + IPCMXSOURCE(m)); + uint32_t dst = getreg32(dev->reg_base + IPCMXDSTATUS(m)); + + FAR struct pl320_chan_s *chan = + (FAR struct pl320_chan_s *) + pl320_getchan(&dev->base, (FAR void *)(uintptr_t)m); + + if (!chan) + { + return -ENXIO; + } + + if (src & CHAN_MASK(dev->ipcmint)) + { + /* It's a TX channel, executing in ack irq */ + + mbox_chan_tx_done(&chan->base); + } + else if (dst & CHAN_MASK(dev->ipcmint)) + { + /* It's a RX channel */ + + mbox_chan_rx_data(&chan->base); + } + else + { + /* Unknown status */ + + return -EIO; + } + } + } + + return 0; +} + +/**************************************************************************** + * Name: pl320_initialize + * + * Description: + * Initialize the PL320 mailbox device. + * + * Input Parameters: + * chans - Pointer to PL320 channel configuration array. + * num_chans - Number of chans. + * reg_base - Register address of PL320 device. + * + * Returned Value: + * Common mbox_dev_s instance; NULL on failure. + * + ****************************************************************************/ + +FAR struct mbox_dev_s *pl320_initialize( + FAR const struct pl320_config_s *config, uintptr_t reg_base) +{ + FAR struct pl320_dev_s *priv; + size_t i; + + /* Sanity check */ + + DEBUGASSERT(config != NULL && config->chans != NULL + && config->num_chans <= PL320_MAX_CHAN_NUM && reg_base); + + /* Initialize the PL320 mailbox device structure */ + + priv = kmm_zalloc(sizeof(struct pl320_dev_s)); + + if (priv == NULL) + { + return NULL; + } + + priv->base.ops = &g_pl320mbox_ops; + priv->base.chans = (FAR struct mbox_chan_s *)config->chans; + priv->base.num_chans = config->num_chans; + + priv->reg_base = reg_base; + priv->ipcmint = config->ipcmint; + priv->irq_num = config->irq_num; + + /* Initialize the PL320 mailbox channels */ + + for (i = 0; i < config->num_chans; ++i) + { + FAR struct pl320_chan_s *chan = &(config->chans[i]); + + if (chan->mbox >= PL320_MAX_MBOX_NUM) + { + goto err_chan; + } + + priv->chan_map[chan->mbox] = chan; + chan->base.id = chan->mbox; + + enum mbox_direction_e dir = (chan->src == priv->ipcmint) ? + MBOX_TX : MBOX_RX; + + if (mbox_chan_init(&chan->base, &priv->base, + dir, chan->txbuf, chan->bufsize) != 0) + { + goto err_chan; + } + + pl320_setup(&chan->base, NULL); + } + + /* Initialize the IPCMINT interrupt */ + + if (irq_attach(priv->irq_num, pl320_irq_handler, priv)) + { + /* Could not attach the ISR to the interrupt */ + + goto err_irq; + } + + up_enable_irq(priv->irq_num); + + return &(priv->base); +err_irq: + i = config->num_chans; +err_chan: + while (i > 0) + { + mbox_chan_deinit(&config->chans[--i].base); + } + + kmm_free(priv); + return NULL; +} diff --git a/include/nuttx/mbox/mbox.h b/include/nuttx/mbox/mbox.h index 38a2b8a332d67..fb8fe93eba425 100644 --- a/include/nuttx/mbox/mbox.h +++ b/include/nuttx/mbox/mbox.h @@ -20,6 +20,80 @@ * ****************************************************************************/ +/* For the purposes of this driver, a hardware mbox is any controller that + * provides inter-processor communication in a multi-core system-on-chip + * architecture for data communication, event control and resource sharing. + * + * The Mbox framework is split into two parts: + * + * 1) A generic framework that provides the common Mbox interface to + * kernel level client code, and + * 2) A "lower half", platform-specific driver that implements the + * low-level controls to configure and control the mbox controller(s) + * for inter-processor communication. + * + * The lower-half mbox ops provide communication semantics as the following + * state machine: + * [s]: state [a]: action + * + * --------------------------------------------------------- + * | sender | receiver | + * --------------------------------------------------------- + * | (1) txready [s] -> | + * | \ | + * | \ | + * | -> (2) send [a] | + * | \ | + * | \ | + * | -> (3) rxavailable [s] | + * | | | + * | -> (4) recv [a] | + * | / | + * | / | + * | (5) ack [a] <- | + * | / | + * | / | + * | <- | + * | | | + * | (6) txacked [s] | + * | | | + * | (7) txfinish [a] | + * | | | + * | (8) txready [s] -> | + * | ...... | + * --------------------------------------------------------- + * + * The generic upper half expects TX channels to implement send, txready and + * txfinish, and to call mbox_chan_tx_done() when an ACK/completion is + * observed. On timeout, txfinish is expected to clear any pending hardware + * completion state before the upper half reuses the channel. A lower-half + * driver for hardware that does not have an explicit ACK can call + * mbox_chan_tx_done() when the hardware has completed the transfer. + * + * The upper-half part provides blocking and non-blocking sending methods and + * non-blocking receiving methods. It is designed based on the state transfer + * of lower-half ops as follows. + * + * ----------------------------------------------------------------- + * | TX | RX | + * ----------------------------------------------------------------- + * | / IRQ | / IRQ | + * | txready / | rxavailable / | + * | | \ | | \ | + * | | \ Polling | | \ Polling | + * | send | recv | + * | | | | | + * | | / IRQ | | | + * | wait for txacked / | acknowledge | + * | | \ | | | + * | | \ Polling | | | + * | txfinish | next rxavailable | + * | | | | + * | | | | + * | next txready | | + * ----------------------------------------------------------------- + */ + #ifndef __INCLUDE_NUTTX_MBOX_MBOX_H #define __INCLUDE_NUTTX_MBOX_MBOX_H @@ -27,44 +101,155 @@ * Included Files ****************************************************************************/ -#include +#include +#include #include #include +#include +#include + +#include +#include +#include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ +#define MBOX_MAX_MSG_SIZE (128) +#define MBOX_WAIT_FOREVER ((clock_t)-1) + /* Access macros ************************************************************/ /**************************************************************************** - * Name: MBOX_SEND + * Name: MBOX_GET_CHAN * * Description: - * Send a 32bits message to remote core from specific channel + * Get a mbox channel by a platform-specific argument. * * Input Parameters: * dev - Device-specific state data - * ch - Mbox specific channel - * msg - Message to send + * arg - Argument used to index mbox channel + * + * Returned Value: + * A pointer of specific channel + * + ****************************************************************************/ + +#define MBOX_GET_CHAN(d, a) ((d)->ops->getchan(d, a)) + +/**************************************************************************** + * Name: MBOX_SETUP + * + * Description: + * Setup specific channel before using it for transmitting data + * + * Input Parameters: + * chan - Mbox specific channel + * arg - Argument + * + * Returned Value: + * OK unless an error occurs. Then a negated errno value is returned + * + ****************************************************************************/ + +#define MBOX_SETUP(c, a) ((c)->dev->ops->setup(c, a)) + +/**************************************************************************** + * Name: MBOX_SHUTDOWN + * + * Description: + * Shutdown specific channel after transmitting messages + * + * Input Parameters: + * chan - Mbox specific channel * * Returned Value: * OK unless an error occurs. Then a negated errno value is returned * ****************************************************************************/ -#define MBOX_SEND(d,c,m) ((d)->ops->send(d,c,m)) +#define MBOX_SHUTDOWN(c) ((c)->dev->ops->shutdown(c)) + +/**************************************************************************** + * Name: MBOX_SEND + * + * Description: + * Send a message to remote core from specific channel + * + * Input Parameters: + * chan - Mbox specific channel + * data - Data buffer to send + * size - Size of data in bytes + * + * Returned Value: + * OK unless an error occurs. Then a negated errno value is returned + * + ****************************************************************************/ + +#define MBOX_SEND(c, d, s) ((c)->dev->ops->send(c, d, s)) + +/**************************************************************************** + * Name: MBOX_RECV + * + * Description: + * Recv a message from remote core by specific channel + * + * Input Parameters: + * chan - Mbox specific channel + * data - Data buffer to receive message + * + * Returned Value: + * Size of message data unless an error occurs. Then a negated errno value + * is returned + * + ****************************************************************************/ + +#define MBOX_RECV(c, d) ((c)->dev->ops->recv(c, d)) + +/**************************************************************************** + * Name: MBOX_ACKNOWLEDGE + * + * Description: + * Send acknowledge to remote core after receiving the message + * + * Input Parameters: + * chan - Mbox specific channel + * data - Data buffer of Acknowledge message + * size - Size of acknowledge message in bytes + * + * Returned Value: + * OK unless an error occurs. Then a negated errno value is returned + * + ****************************************************************************/ + +#define MBOX_ACKNOWLEDGE(c, d, s) ((c)->dev->ops->acknowledge(c, d, s)) + +/**************************************************************************** + * Name: MBOX_TX_FINISH + * + * Description: + * Finish a transmission process and prepare for the next transmission + * + * Input Parameters: + * chan - Mbox specific channel + * + * Returned Value: + * OK unless an error occurs. Then a negated errno value is returned + * + ****************************************************************************/ + +#define MBOX_TX_FINISH(c) ((c)->dev->ops->txfinish(c)) /**************************************************************************** * Name: MBOX_REGISTER_CALLBACK * * Description: - * Attach to receive a callback when something is received on MBOX + * Attach a callback which will be called when a message is received. * * Input Parameters: - * dev - Device-specific state data - * ch - Mbox specific channel + * chan - Mbox specific channel * callback - The function to be called when something has been received * arg - A caller provided value to return with the callback * @@ -73,44 +258,215 @@ * ****************************************************************************/ -#define MBOX_REGISTER_CALLBACK(d,c,cb,a) \ - ((d)->ops->registercallback(d,c,cb,a)) +#define MBOX_REGISTER_CALLBACK(c, cb, a) \ + ((c)->dev->ops->setcallback(c, cb, a)) /**************************************************************************** - * Name: MBOX_UNREGISTER_CALLBACK + * Name: MBOX_RX_AVAILABLE * * Description: - * Detach MBOX callback + * Check if the message is ready to be received on specific channel * * Input Parameters: - * dev - Device-specific state data - * ch - Mbox specific channel + * chan - Mbox specific channel * * Returned Value: - * OK unless an error occurs. Then a negated errno value is returned + * True if data is available; false otherwise * ****************************************************************************/ -#define MBOX_UNREGISTER_CALLBACK(d,c) \ - ((d)->ops->registercallback(d,c,NULL,NULL)) +#define MBOX_RX_AVAILABLE(c) ((c)->dev->ops->rxavailable(c)) + +/**************************************************************************** + * Name: MBOX_TX_READY + * + * Description: + * Check if the channel is ready for next message transfer + * + * Input Parameters: + * chan - Mbox specific channel + * + * Returned Value: + * True if the channel is ready; false otherwise + * + ****************************************************************************/ + +#define MBOX_TX_READY(c) ((c)->dev->ops->txready(c)) + +/**************************************************************************** + * Name: MBOX_TX_ACKED + * + * Description: + * Check if the channel is acked by remote receiver + * + * Input Parameters: + * chan - Mbox specific channel + * + * Returned Value: + * True if the channel has been acked; false otherwise + * + ****************************************************************************/ + +#define MBOX_TX_ACKED(c) ((c)->dev->ops->txacked(c)) /**************************************************************************** * Public Types ****************************************************************************/ +/* Transmission direction of mbox channel */ + +enum mbox_direction_e +{ + MBOX_RX = 0, + MBOX_TX = 1 +}; + +/* TX state of mbox channel */ + +enum mbox_txstate_e +{ + MBOX_TX_IDLE = 0, + MBOX_TX_ACTIVE +}; + +/* Forward declaration */ + +struct mbox_chan_s; + +/* Mbox callback type for notify that data is ready */ + +typedef CODE int (*mbox_callback_t)(int ret, + FAR struct mbox_chan_s *chan, + FAR void *arg, + FAR void *data, size_t size); + +#ifdef CONFIG_MBOX_TRACE +/* Statistics of mbox channel */ + +struct mbox_stats_s +{ + size_t timeout; /* Count of messages TX timeout */ + size_t sent; /* Count of message sent */ + size_t received; /* Count of message received */ + size_t acked; /* Count of message acknowledged */ + size_t buffered; /* Count of message in buffer */ +}; +#endif + +/* Forward declaration */ + struct mbox_dev_s; -typedef CODE int (*mbox_receive_t)(FAR void *arg, uintptr_t msg); + +/* Mbox channel structure */ + +struct mbox_chan_s +{ + int id; /* ID used to identify a channel */ + enum mbox_direction_e dir; /* Channel direction TX or RX */ + FAR struct mbox_dev_s *dev; /* Associated lower-half device */ + mbox_callback_t callback; /* User-defined callback */ + struct circbuf_s txbuf; /* Buffer of messages to be sent */ + sem_t txsem; /* TX semaphore */ + struct wdog_s timer; /* Timer for detecting TX timeout */ + clock_t timeout; /* Max time from sending to be acked */ + enum mbox_txstate_e txstate; /* TX state */ + bool txwaiting; /* A blocking sender is waiting */ + int txresult; /* Result of blocking TX */ +#ifdef CONFIG_MBOX_TRACE + struct mbox_stats_s stats; /* Stats of channel */ +#endif + FAR void *priv; /* Private data */ +}; + +/* This structure defines a wrapper when use a mbox channel for tx */ + +struct mbox_sender_s +{ + FAR struct mbox_chan_s *chan; /* Associated mbox channel */ + FAR void *priv; /* Private data */ +}; + +/* This structure defines a wrapper when use a mbox channel for rx */ + +struct mbox_receiver_s +{ + FAR struct mbox_chan_s *chan; /* Associated mbox channel */ + FAR void *priv; /* Private data */ +}; + +/* This structure is a set of operation functions used to call from the + * upper-half, generic Mbox driver into lower-half, platform-specific logic + * that supports the low-level functionality. + */ struct mbox_ops_s { - CODE int (*send)(FAR struct mbox_dev_s *dev, uint32_t ch, uintptr_t msg); - CODE int (*registercallback)(FAR struct mbox_dev_s *dev, uint32_t ch, - mbox_receive_t callback, FAR void *arg); + /* Get handle of specific mbox channel by a channel index */ + + CODE struct mbox_chan_s *(*getchan)(FAR struct mbox_dev_s *dev, + FAR void *arg); + + /* Request and configure the mbox channel. This method is called before + * using the mbox channel to transmit data. + */ + + CODE int (*setup)(FAR struct mbox_chan_s *chan, FAR void *arg); + + /* Release the mbox channel. This method is called when the mbox channel + * is closed. This method reverses the operations the setup method. + */ + + CODE int (*shutdown)(FAR struct mbox_chan_s *chan); + + /* Send message through the mbox channel */ + + CODE int (*send)(FAR struct mbox_chan_s *chan, FAR const void *data, + size_t size); + + /* Receive message through the mbox channel */ + + CODE int (*recv)(FAR struct mbox_chan_s *chan, FAR void *data); + + /* Send acknowledge message from receiver to the sender */ + + CODE int (*acknowledge)(FAR struct mbox_chan_s *chan, FAR const void *data, + size_t size); + + /* Finish a transmission process and prepare for the next transmission */ + + CODE int (*txfinish)(FAR struct mbox_chan_s *chan); + + /* Set user-defined callback for the mbox channel */ + + CODE int (*setcallback)(FAR struct mbox_chan_s *chan, + mbox_callback_t callback, FAR void *arg); + + /* This method is used to check if the message is ready to be received */ + + CODE bool (*rxavailable)(FAR struct mbox_chan_s *chan); + + /* This method is used to check if this channel is ready for next + * message transfer. + */ + + CODE bool (*txready)(FAR struct mbox_chan_s *chan); + + /* This method is used to check if this channel is acked by + * remote receiver. + */ + + CODE bool (*txacked)(FAR struct mbox_chan_s *chan); }; +/* This structure is the generic form of state structure used by lower half + * mbox driver. + */ + struct mbox_dev_s { FAR const struct mbox_ops_s *ops; + FAR struct mbox_chan_s *chans; + size_t num_chans; }; /**************************************************************************** @@ -125,6 +481,211 @@ extern "C" #define EXTERN extern #endif +/**************************************************************************** + * Name: mbox_get_chan + * + * Description: + * Get a mbox channel handle by platform-specific argument. + * + * Input Parameters: + * dev - A pointer to mbox dev + * arg - Platform-specific argument to index a channel + * + * Returned Value: + * Pointer to mbox channel on success; NULL on failure + * + ****************************************************************************/ + +FAR struct mbox_chan_s *mbox_get_chan(FAR struct mbox_dev_s *dev, + FAR void *arg); + +/**************************************************************************** + * Name: mbox_chan_init + * + * Description: + * Initialize the members of the specific mbox channel structure. + * This function could be called to initialize the base part of + * a platform-specific channel in lower-half initialization. + * + * Input Parameters: + * chan - A pointer to mbox channel to be initialized + * dev - A pointer to mbox dev + * dir - Mbox xfer direction TX or RX + * buffer - The internal buffer allocated for the txbuf of mbox_chan_s + * size - The size of the txbuf in mbox_chan_s + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +int mbox_chan_init(FAR struct mbox_chan_s *chan, FAR struct mbox_dev_s *dev, + enum mbox_direction_e dir, FAR void *buffer, size_t size); + +/**************************************************************************** + * Name: mbox_send + * + * Description: + * Send a message through a specific send channel. It will block until + * the message is acknowledged or completed. + * + * Input Parameters: + * sender - A Pointer to a mbox sender + * data - Message to send + * size - Bytes of data to send + * + * Returned Value: + * 0: success, <0: A negated errno + * -EINVAL: Invalid parameter + * -EIO: IO Error happened when send data + * -EAGAIN: Not txready now, please try again + * -ETIMEDOUT: Waiting for TX timeout + * + ****************************************************************************/ + +int mbox_send(FAR struct mbox_sender_s *sender, FAR const void *data, + size_t size); + +/**************************************************************************** + * Name: mbox_ticksend + * + * Description: + * Send a message through a specific send channel. + * + * Input Parameters: + * sender - A Pointer to a mbox sender + * data - Message Buffer to send + * size - Size of data in bytes + * timeout - The ticks to wait until the message is acknowledged or + * completed. If timeout is zero, the message is submitted or + * buffered without waiting. + * + * Returned Value: + * 0: success, <0: A negated errno + * -EINVAL: Invalid parameter + * -EIO: IO Error happened when send data + * -EAGAIN: Not txready now, please try again + * -ETIMEDOUT: Waiting for TX timeout + * + ****************************************************************************/ + +int mbox_ticksend(FAR struct mbox_sender_s *sender, FAR const void *data, + size_t size, clock_t timeout); + +/**************************************************************************** + * Name: mbox_register_rxcallback + * + * Description: + * Register a user-defined callback function to receiver channel. This + * callback function will be called when rxavailable. + * + * Input Parameters: + * receiver - Mbox receiver which binding to a rx channel + * callback - User-defined callback + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +int mbox_register_rxcallback(FAR struct mbox_receiver_s *receiver, + mbox_callback_t callback, FAR void *arg); + +/**************************************************************************** + * Name: mbox_unregister_rxcallback + * + * Description: + * Unregister a previously registered callback function from receiver + * channel. + * + * Input Parameters: + * receiver - Mbox receiver which binding to a rx channel + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +int mbox_unregister_rxcallback(FAR struct mbox_receiver_s *receiver); + +/**************************************************************************** + * Name: mbox_chan_rx_data + * + * Description: + * This function should be called when RX happened (when triggered by an + * interrupt or when polling detects the rxavailable status) + * This function will call lower-half ops->recv to receive the data from + * remote and send acknowledge to remote sender. Finally, the user-defined + * callback function will be called to push data to the upper layer. + * + * Input Parameters: + * chan - A pointer to the mbox channel which rx happened + * + * Returned Value: + * Size of received data on success, <0: A negated errno + * + ****************************************************************************/ + +int mbox_chan_rx_data(FAR struct mbox_chan_s *chan); + +/**************************************************************************** + * Name: mbox_chan_tx_done + * + * Description: + * This function should be called upon receiving an ACK/completion (when + * triggered by an interrupt or when polling detects the txacked status) + * This function will first cancel tx timer because TX has completed. Then + * lower-half ops->txfinish will be called to finish current transmission, + * clean related state and prepare for next transmission. + * If there are any remaining message in circbuf, it will be submitted to + * mailbox controller to send to remote. + * + * + * Input Parameters: + * chan - A pointer to the mbox channel which completed TX + * + ****************************************************************************/ + +void mbox_chan_tx_done(FAR struct mbox_chan_s *chan); + +#ifdef CONFIG_MBOX_TRACE + +/**************************************************************************** + * Name: mbox_chan_get_stats + * + * Description: + * This function is only visible when configure CONFIG_MBOX_TRACE. + * This function will retrieves the message sending and receiving + * statistics data of specific mbox channel and returns it to + * the caller. + * + * Input Parameters: + * chan - A pointer to the mbox channel + * stats - A pointer to the mbox_stats_s passed by caller + * + ****************************************************************************/ + +void mbox_chan_get_stats(FAR struct mbox_chan_s *chan, + FAR struct mbox_stats_s *stats); +#endif + +/**************************************************************************** + * Name: mbox_chan_deinit + * + * Description: + * Deinitialize a mbox channel, releasing the semaphore and circbuf + * resources that were allocated during mbox_chan_init. + * + * Input Parameters: + * chan - A pointer to mbox channel to be deinitialized + * + * Returned Value: + * 0: success, <0: A negated errno + * + ****************************************************************************/ + +int mbox_chan_deinit(FAR struct mbox_chan_s *chan); + #ifdef __cplusplus } #endif diff --git a/include/nuttx/mbox/pl320.h b/include/nuttx/mbox/pl320.h new file mode 100644 index 0000000000000..ba2eb2a5d4335 --- /dev/null +++ b/include/nuttx/mbox/pl320.h @@ -0,0 +1,95 @@ +/**************************************************************************** + * include/nuttx/mbox/pl320.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_MBOX_PL320_H +#define __INCLUDE_NUTTX_MBOX_PL320_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#ifdef CONFIG_MBOX_PL320 + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define PL320_MAX_MBOX_NUM (32) +#define PL320_MAX_CHAN_NUM (32) +#define PL320_MAX_DATA_BUF_SIZE (16) +#define PL320_MSG_SIZE (28) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* For the convenience of indexing channel, we use mbox id as + * channel id at the same time + */ + +struct pl320_chan_s +{ + struct mbox_chan_s base; /* Nested structure to allow casting + * as mbox_chan_s */ + uint8_t mbox; /* pl320 mailbox id */ + uint8_t src; /* pl320 source core */ + uint8_t dst; /* pl320 dest core */ + bool ctrl; /* Do channel initial configuration? */ + + /* Buffer to hold messages of base.msgbuf */ + + uint8_t *txbuf; + size_t bufsize; +}; + +struct pl320_config_s +{ + FAR struct pl320_chan_s *chans; /* Pointer to pl320 channels array */ + size_t num_chans; /* Number of pl320 channels */ + int ipcmint; /* ipcmint of local core */ + int irq_num; /* IRQ num */ +}; + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +FAR struct mbox_dev_s *pl320_initialize( + FAR const struct pl320_config_s *config, uintptr_t reg_base); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_MBOX_PL320 */ +#endif /* __INCLUDE_NUTTX_MBOX_PL320_H */