From 48270639585842660e0344c1d3e7f38b341ebf1b Mon Sep 17 00:00:00 2001 From: liaoao Date: Wed, 5 Jun 2024 21:50:52 +0800 Subject: [PATCH] rpmsg_port_spi: add spi slave support The rpmsg port spi slave version support Signed-off-by: liaoao --- drivers/rpmsg/CMakeLists.txt | 4 + drivers/rpmsg/Kconfig | 9 +- drivers/rpmsg/Make.defs | 4 + drivers/rpmsg/rpmsg_port_spi_slave.c | 606 +++++++++++++++++++++++++++ include/nuttx/rpmsg/rpmsg_port.h | 31 +- 5 files changed, 652 insertions(+), 2 deletions(-) create mode 100644 drivers/rpmsg/rpmsg_port_spi_slave.c diff --git a/drivers/rpmsg/CMakeLists.txt b/drivers/rpmsg/CMakeLists.txt index e50720d3b9..3eadd86258 100644 --- a/drivers/rpmsg/CMakeLists.txt +++ b/drivers/rpmsg/CMakeLists.txt @@ -37,6 +37,10 @@ if(CONFIG_RPMSG) list(APPEND SRCS rpmsg_port_spi.c) endif() + if(CONFIG_RPMSG_PORT_SPI_SLAVE) + list(APPEND SRCS rpmsg_port_spi_slave.c) + endif() + if(CONFIG_RPMSG_VIRTIO) list(APPEND SRCS rpmsg_virtio.c) endif() diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig index 69047c10a3..a20c8b2a4a 100644 --- a/drivers/rpmsg/Kconfig +++ b/drivers/rpmsg/Kconfig @@ -44,7 +44,14 @@ config RPMSG_PORT_SPI ---help--- Rpmsg SPI Port driver used for cross chip communication. -if RPMSG_PORT_SPI +config RPMSG_PORT_SPI_SLAVE + bool "Rpmsg SPI Slave Port Driver Support" + default n + select RPMSG_PORT + ---help--- + Rpmsg SPI Slave Port driver used for cross chip communication. + +if RPMSG_PORT_SPI || RPMSG_PORT_SPI_SLAVE config RPMSG_PORT_SPI_THREAD_PRIORITY int "Rpmsg SPI Port Thread Priority" diff --git a/drivers/rpmsg/Make.defs b/drivers/rpmsg/Make.defs index 21094174b8..0b068520c2 100644 --- a/drivers/rpmsg/Make.defs +++ b/drivers/rpmsg/Make.defs @@ -41,6 +41,10 @@ ifeq ($(CONFIG_RPMSG_PORT_SPI),y) CSRCS += rpmsg_port_spi.c endif +ifeq ($(CONFIG_RPMSG_PORT_SPI_SLAVE),y) +CSRCS += rpmsg_port_spi_slave.c +endif + ifeq ($(CONFIG_RPMSG_VIRTIO),y) CSRCS += rpmsg_virtio.c CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)openamp$(DELIM)open-amp$(DELIM)lib diff --git a/drivers/rpmsg/rpmsg_port_spi_slave.c b/drivers/rpmsg/rpmsg_port_spi_slave.c new file mode 100644 index 0000000000..148f9f1833 --- /dev/null +++ b/drivers/rpmsg/rpmsg_port_spi_slave.c @@ -0,0 +1,606 @@ +/**************************************************************************** + * drivers/rpmsg/rpmsg_port_spi_slave.c + * + * 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 "rpmsg_port.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifdef CONFIG_RPMSG_PORT_SPI_CRC +# define rpmsg_port_spi_crc16(hdr) crc16((FAR uint8_t *)&(hdr)->cmd, \ + (hdr)->len - sizeof((hdr)->crc)) +#else +# define rpmsg_port_spi_crc16(hdr) 0 +#endif + +#define RPMSG_SPI_PORT_UNCONNECTED UINT16_MAX + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +enum rpmsg_port_spi_cmd_e +{ + RPMSG_PORT_SPI_CMD_CONNECT = 0x01, + RPMSG_PORT_SPI_CMD_AVAIL, + RPMSG_PORT_SPI_CMD_DATA, +}; + +struct rpmsg_port_spi_s +{ + struct rpmsg_port_s port; + FAR struct spi_slave_ctrlr_s *spictrlr; + struct spi_slave_dev_s spislv; + FAR struct ioexpander_dev_s *ioe; + + /* GPIOs used for handshake */ + + uint8_t mreq; + uint8_t sreq; + + /* Reserved for cmd send */ + + FAR struct rpmsg_port_header_s *cmdhdr; + + /* Used for sync data state between sreq_handler and complete_handler */ + + FAR struct rpmsg_port_header_s *txhdr; + FAR struct rpmsg_port_header_s *rxhdr; + + rpmsg_port_rx_cb_t rxcb; + + /* Used for flow control */ + + uint16_t txavail; + uint16_t rxavail; + uint16_t rxthres; + + atomic_int transferring; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void rpmsg_port_spi_notify_tx_ready(FAR struct rpmsg_port_s *port); +static void rpmsg_port_spi_notify_rx_free(FAR struct rpmsg_port_s *port); +static void rpmsg_port_spi_register_cb(FAR struct rpmsg_port_s *port, + rpmsg_port_rx_cb_t callback); +static void rpmsg_port_spi_slave_select(FAR struct spi_slave_dev_s *dev, + bool selected); +static void rpmsg_port_spi_slave_cmddata(FAR struct spi_slave_dev_s *dev, + bool data); +static size_t rpmsg_port_spi_slave_getdata(FAR struct spi_slave_dev_s *dev, + FAR const void **data); +static size_t rpmsg_port_spi_slave_receive(FAR struct spi_slave_dev_s *dev, + FAR const void *data, + size_t nwords); +static void rpmsg_port_spi_slave_notify(FAR struct spi_slave_dev_s *dev, + spi_slave_state_t state); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct rpmsg_port_ops_s g_rpmsg_port_spi_ops = +{ + rpmsg_port_spi_notify_tx_ready, + rpmsg_port_spi_notify_rx_free, + rpmsg_port_spi_register_cb, +}; + +static const struct spi_slave_devops_s g_rpmsg_port_spi_slave_ops = +{ + rpmsg_port_spi_slave_select, /* select */ + rpmsg_port_spi_slave_cmddata, /* cmddata */ + rpmsg_port_spi_slave_getdata, /* getdata */ + rpmsg_port_spi_slave_receive, /* receive */ + rpmsg_port_spi_slave_notify, /* notify */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: rpmsg_port_spi_exchange + ****************************************************************************/ + +void rpmsg_port_spi_exchange(FAR struct rpmsg_port_spi_s *rpspi) +{ + FAR struct rpmsg_port_header_s *txhdr; + + if (atomic_fetch_add(&rpspi->transferring, 1)) + { + return; + } + + if (rpspi->txavail == RPMSG_SPI_PORT_UNCONNECTED) + { + txhdr = rpspi->cmdhdr; + txhdr->cmd = RPMSG_PORT_SPI_CMD_CONNECT; + strlcpy((FAR char *)(txhdr + 1), rpspi->port.cpuname, RPMSG_NAME_SIZE); + } + else if (rpspi->txavail > 0 && + rpmsg_port_queue_nused(&rpspi->port.txq) > 0) + { + txhdr = rpmsg_port_queue_get_buffer(&rpspi->port.txq, false); + DEBUGASSERT(txhdr != NULL); + + txhdr->cmd = RPMSG_PORT_SPI_CMD_DATA; + rpspi->txhdr = txhdr; + } + else + { + txhdr = rpspi->cmdhdr; + txhdr->cmd = RPMSG_PORT_SPI_CMD_AVAIL; + } + + txhdr->avail = rpmsg_port_queue_navail(&rpspi->port.rxq); + txhdr->avail = txhdr->avail > 1 ? txhdr->avail - 1 : 0; + txhdr->crc = rpmsg_port_spi_crc16(txhdr); + + rpmsginfo("send cmd:%u avail:%u\n", txhdr->cmd, txhdr->avail); + + SPIS_CTRLR_ENQUEUE(rpspi->spictrlr, txhdr, rpspi->cmdhdr->len); + IOEXP_WRITEPIN(rpspi->ioe, rpspi->sreq, 1); + + rpspi->rxavail = txhdr->avail; +} + +/**************************************************************************** + * Name: rpmsg_port_spi_notify_tx_ready + ****************************************************************************/ + +static void rpmsg_port_spi_notify_tx_ready(FAR struct rpmsg_port_s *port) +{ + FAR struct rpmsg_port_spi_s *rpspi = + container_of(port, struct rpmsg_port_spi_s, port); + + rpmsg_port_spi_exchange(rpspi); +} + +/**************************************************************************** + * Name: rpmsg_port_spi_notify_rx_free + ****************************************************************************/ + +static void rpmsg_port_spi_notify_rx_free(FAR struct rpmsg_port_s *port) +{ + FAR struct rpmsg_port_spi_s *rpspi = + container_of(port, struct rpmsg_port_spi_s, port); + + if (rpmsg_port_queue_navail(&port->rxq) - rpspi->rxavail >= rpspi->rxthres) + { + rpmsg_port_spi_exchange(rpspi); + } +} + +/**************************************************************************** + * Name: rpmsg_port_spi_register_cb + ****************************************************************************/ + +static void rpmsg_port_spi_register_cb(FAR struct rpmsg_port_s *port, + rpmsg_port_rx_cb_t callback) +{ + FAR struct rpmsg_port_spi_s *rpspi = + container_of(port, struct rpmsg_port_spi_s, port); + + rpspi->rxcb = callback; +} + +/**************************************************************************** + * Name: rpmsg_port_spi_slave_select + ****************************************************************************/ + +static void rpmsg_port_spi_slave_select(FAR struct spi_slave_dev_s *dev, + bool selected) +{ + rpmsginfo("sdev: %p CS: %s\n", dev, selected ? "select" : "free"); +} + +/**************************************************************************** + * Name: rpmsg_port_spi_slave_cmddata + ****************************************************************************/ + +static void rpmsg_port_spi_slave_cmddata(FAR struct spi_slave_dev_s *dev, + bool data) +{ + rpmsginfo("sdev: %p CMD: %s\n", dev, data ? "data" : "command"); +} + +/**************************************************************************** + * Name: rpmsg_port_spi_slave_getdata + ****************************************************************************/ + +static size_t rpmsg_port_spi_slave_getdata(FAR struct spi_slave_dev_s *dev, + FAR const void **data) +{ + FAR struct rpmsg_port_spi_s *rpspi = + container_of(dev, struct rpmsg_port_spi_s, spislv); + + *data = rpspi->rxhdr; + return rpspi->cmdhdr->len; +} + +/**************************************************************************** + * Name: rpmsg_port_spi_slave_receive + ****************************************************************************/ + +static size_t rpmsg_port_spi_slave_receive(FAR struct spi_slave_dev_s *dev, + FAR const void *data, + size_t nwords) +{ + return nwords; +} + +/**************************************************************************** + * Name: rpmsg_port_spi_slave_notify + ****************************************************************************/ + +static void rpmsg_port_spi_slave_notify(FAR struct spi_slave_dev_s *dev, + spi_slave_state_t state) +{ + FAR struct rpmsg_port_spi_s *rpspi = + container_of(dev, struct rpmsg_port_spi_s, spislv); + uint16_t avail; + + IOEXP_WRITEPIN(rpspi->ioe, rpspi->sreq, 0); + SPIS_CTRLR_QPOLL(rpspi->spictrlr); + + avail = rpspi->rxhdr->avail; + rpmsginfo("received cmd:%u avail:%u\n", rpspi->rxhdr->cmd, avail); + + if (rpspi->txhdr != NULL) + { + rpmsg_port_queue_return_buffer(&rpspi->port.txq, rpspi->txhdr); + rpspi->txhdr = NULL; + } + + if (rpspi->rxhdr->crc != 0) + { + uint16_t crc = rpmsg_port_spi_crc16(rpspi->rxhdr); + + if (crc != 0 && rpspi->rxhdr->crc != crc) + { + rpmsgerr("crc check fail received: %u calculated: %u\n", + rpspi->rxhdr->crc, crc); + goto out; + } + } + + /* Skip any data received when connection is not established until a + * connect req data packet has been received. + */ + + if (rpspi->txavail == RPMSG_SPI_PORT_UNCONNECTED && + rpspi->rxhdr->cmd != RPMSG_PORT_SPI_CMD_CONNECT) + { + return; + } + + if (rpspi->rxhdr->cmd != RPMSG_PORT_SPI_CMD_AVAIL) + { + rpmsg_port_queue_add_buffer(&rpspi->port.rxq, rpspi->rxhdr); + rpspi->rxhdr = rpmsg_port_queue_get_available_buffer( + &rpspi->port.rxq, false); + DEBUGASSERT(rpspi->rxhdr != NULL); + } + + /* Do nothing when two sides are not connected. */ + + if (rpspi->txavail == RPMSG_SPI_PORT_UNCONNECTED) + { + return; + } + + rpspi->txavail = avail; + +out: + if (atomic_exchange(&rpspi->transferring, 0) > 1 || + (rpspi->txavail > 0 && rpmsg_port_queue_nused(&rpspi->port.txq) > 0)) + { + rpmsg_port_spi_exchange(rpspi); + } +} + +/**************************************************************************** + * Name: rpmsg_port_spi_process_packet + ****************************************************************************/ + +static void +rpmsg_port_spi_process_packet(FAR struct rpmsg_port_spi_s *rpspi, + FAR struct rpmsg_port_header_s *rxhdr) +{ + rpmsginfo("received cmd: %u avail: %u", rxhdr->cmd, rxhdr->avail); + + switch (rxhdr->cmd) + { + case RPMSG_PORT_SPI_CMD_CONNECT: + if (rpspi->txavail != RPMSG_SPI_PORT_UNCONNECTED) + { + rpmsg_port_unregister(&rpspi->port); + rpspi->txavail = RPMSG_SPI_PORT_UNCONNECTED; + } + else + { + rpspi->txavail = rxhdr->avail; + rpmsg_port_register(&rpspi->port, (FAR const char *)(rxhdr + 1)); + } + + rpmsg_port_queue_return_buffer(&rpspi->port.rxq, rxhdr); + break; + + case RPMSG_PORT_SPI_CMD_DATA: + rpspi->rxcb(&rpspi->port, rxhdr); + break; + + default: + rpmsgerr("received a unexpected frame, dropped\n"); + rpmsg_port_queue_return_buffer(&rpspi->port.rxq, rxhdr); + break; + } +} + +/**************************************************************************** + * Name: rpmsg_port_spi_mreq_handler + ****************************************************************************/ + +static int rpmsg_port_spi_mreq_handler(FAR struct ioexpander_dev_s *dev, + ioe_pinset_t pinset, FAR void *arg) +{ + FAR struct rpmsg_port_spi_s *rpspi = arg; + + rpmsginfo("received a mreq\n"); + rpmsg_port_spi_exchange(rpspi); + return 0; +} + +/**************************************************************************** + * Name: rpmsg_port_spi_connect + ****************************************************************************/ + +static void rpmsg_port_spi_connect(FAR struct rpmsg_port_spi_s *rpspi) +{ + bool val; + + IOEXP_READPIN(rpspi->ioe, rpspi->mreq, &val); + if (val) + { + rpmsg_port_spi_mreq_handler(NULL, 0, rpspi); + } + else + { + IOEXP_WRITEPIN(rpspi->ioe, rpspi->sreq, 1); + } +} + +/**************************************************************************** + * Name: rpmsg_port_spi_thread + ****************************************************************************/ + +static int rpmsg_port_spi_thread(int argc, FAR char *argv[]) +{ + FAR struct rpmsg_port_spi_s *rpspi = + (FAR struct rpmsg_port_spi_s *)((uintptr_t)strtoul(argv[2], NULL, 16)); + FAR struct rpmsg_port_queue_s *queue = &rpspi->port.rxq; + FAR struct rpmsg_port_header_s *rxhdr; + + rpmsg_port_spi_connect(rpspi); + for (; ; ) + { + while ((rxhdr = rpmsg_port_queue_get_buffer(queue, true)) != NULL) + { + rpmsg_port_spi_process_packet(rpspi, rxhdr); + } + } + + return 0; +} + +/**************************************************************************** + * Name: rpmsg_port_spi_gpio_init + ****************************************************************************/ + +static int +rpmsg_port_spi_init_gpio(FAR struct ioexpander_dev_s *ioe, + FAR uint8_t *gpio, uint8_t pin, int invert, + ioe_callback_t callback, FAR void *args) +{ + int direction = callback ? + IOEXPANDER_DIRECTION_IN_PULLDOWN : IOEXPANDER_DIRECTION_OUT; + int ret; + + ret = IOEXP_SETOPTION(ioe, pin, IOEXPANDER_OPTION_INVERT, + (FAR void *)invert); + if (ret < 0) + { + rpmsgerr("gpio set invert option error: %d\n", ret); + return ret; + } + + ret = IOEXP_SETDIRECTION(ioe, pin, direction); + if (ret < 0) + { + rpmsgerr("gpio set direction %d error: %d\n", direction, ret); + return ret; + } + + if (direction == IOEXPANDER_DIRECTION_IN_PULLDOWN) + { + int intcfg = invert == IOEXPANDER_VAL_INVERT ? + IOEXPANDER_VAL_FALLING : IOEXPANDER_VAL_RISING; + FAR void *ptr; + + ret = IOEXP_SETOPTION(ioe, pin, IOEXPANDER_OPTION_INTCFG, + (FAR void *)intcfg); + if (ret < 0) + { + rpmsgerr("gpio set int option %d error: %d\n", intcfg, ret); + return ret; + } + + ptr = IOEP_ATTACH(ioe, pin, callback, args); + if (ptr == NULL) + { + rpmsgerr("gpio attach error: %d\n", ret); + return -EINVAL; + } + } + + *gpio = pin; + return ret; +} + +/**************************************************************************** + * Name: rpmsg_port_spi_init_hardware + ****************************************************************************/ + +static int +rpmsg_port_spi_init_hardware(FAR struct rpmsg_port_spi_s *rpspi, + FAR const struct rpmsg_port_spi_config_s *spicfg, + FAR struct spi_slave_ctrlr_s *spictrlr, FAR struct ioexpander_dev_s *ioe) +{ + int ret; + + if (spictrlr == NULL || ioe == NULL || spicfg == NULL) + { + rpmsgerr("invalid params\n"); + return -EINVAL; + } + + /* Init mreq gpio */ + + ret = rpmsg_port_spi_init_gpio(ioe, &rpspi->mreq, spicfg->mreq_pin, + spicfg->mreq_invert, + rpmsg_port_spi_mreq_handler, rpspi); + if (ret < 0) + { + rpmsgerr("mreq init failed\n"); + return ret; + } + + /* Init sreq gpio */ + + ret = rpmsg_port_spi_init_gpio(ioe, &rpspi->sreq, spicfg->sreq_pin, + spicfg->sreq_invert, NULL, NULL); + if (ret < 0) + { + rpmsgerr("sreq init failed\n"); + return ret; + } + + rpspi->ioe = ioe; + rpspi->spictrlr = spictrlr; + rpspi->spislv.ops = &g_rpmsg_port_spi_slave_ops; + SPIS_CTRLR_BIND(spictrlr, &rpspi->spislv, spicfg->mode, 8); + + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: rpmsg_port_spi_slave_initialize + ****************************************************************************/ + +int +rpmsg_port_spi_slave_initialize(FAR const struct rpmsg_port_config_s *cfg, + FAR const struct rpmsg_port_spi_config_s *spicfg, + FAR struct spi_slave_ctrlr_s *spictrlr, FAR struct ioexpander_dev_s *ioe) +{ + FAR struct rpmsg_port_spi_s *rpspi; + FAR char *argv[3]; + char arg1[32]; + int ret; + + rpspi = kmm_zalloc(sizeof(*rpspi)); + if (rpspi == NULL) + { + rpmsgerr("malloc rpmsg spi failed\n"); + return -ENOMEM; + } + + ret = rpmsg_port_spi_init_hardware(rpspi, spicfg, spictrlr, ioe); + if (ret < 0) + { + rpmsgerr("rpmsg port spi hardware init failed\n"); + goto rpmsg_err; + } + + DEBUGASSERT(cfg->txlen == cfg->rxlen); + ret = rpmsg_port_initialize(&rpspi->port, cfg, &g_rpmsg_port_spi_ops); + if (ret < 0) + { + rpmsgerr("rpmsg port initialize failed\n"); + goto rpmsg_err; + } + + /* Always reserve one buffer for sending/receiving cmd packet */ + + rpspi->cmdhdr = rpmsg_port_queue_get_available_buffer( + &rpspi->port.txq, true); + rpspi->rxhdr = rpmsg_port_queue_get_available_buffer( + &rpspi->port.rxq, true); + DEBUGASSERT(rpspi->cmdhdr != NULL && rpspi->rxhdr != NULL); + + rpspi->txavail = RPMSG_SPI_PORT_UNCONNECTED; + rpspi->rxthres = rpmsg_port_queue_navail(&rpspi->port.rxq) * + CONFIG_RPMSG_PORT_SPI_RX_THRESHOLD / 100; + + snprintf(arg1, sizeof(arg1), "%p", rpspi); + argv[0] = (FAR char *)cfg->remotecpu; + argv[1] = arg1; + argv[2] = NULL; + ret = kthread_create("rpmsg-spi-slv", + CONFIG_RPMSG_PORT_SPI_THREAD_PRIORITY, + CONFIG_RPMSG_PORT_SPI_THREAD_STACKSIZE, + rpmsg_port_spi_thread, argv); + if (ret < 0) + { + rpmsgerr("rpmsg port spi create thread failed\n"); + goto thread_err; + } + + return 0; + +thread_err: + rpmsg_port_uninitialize(&rpspi->port); +rpmsg_err: + kmm_free(rpspi); + return ret; +} diff --git a/include/nuttx/rpmsg/rpmsg_port.h b/include/nuttx/rpmsg/rpmsg_port.h index a6a8f4d8f2..d0d6da8760 100644 --- a/include/nuttx/rpmsg/rpmsg_port.h +++ b/include/nuttx/rpmsg/rpmsg_port.h @@ -30,6 +30,7 @@ #include #include +#include #ifdef CONFIG_RPMSG_PORT @@ -77,7 +78,7 @@ struct rpmsg_port_spi_config_s int mreq_invert; int sreq_invert; /* Pin options described in ioexpander.h */ - enum spi_mode_e mode; + int mode; /* Mode of enum spi_mode_e */ uint32_t devid; /* Device ID of enum spi_devtype_e */ uint32_t freq; /* SPI frequency (Hz) */ }; @@ -123,6 +124,34 @@ rpmsg_port_spi_initialize(FAR const struct rpmsg_port_config_s *cfg, #endif +#ifdef CONFIG_RPMSG_PORT_SPI_SLAVE + +/**************************************************************************** + * Name: rpmsg_port_spi_slave_initialize + * + * Description: + * Initialize a rpmsg_port_spi_slave device to communicate between two + * chips. + * + * Input Parameters: + * cfg - Configuration of buffers needed for communication. + * spicfg - SPI device's configuration. + * spictrlr - SPI slave controller used for transfer data between two + * chips. + * ioe - ioexpander used to config gpios. + * + * Returned Value: + * Zero on success or an negative value on failure. + * + ****************************************************************************/ + +int +rpmsg_port_spi_slave_initialize(FAR const struct rpmsg_port_config_s *cfg, + FAR const struct rpmsg_port_spi_config_s *spicfg, + FAR struct spi_slave_ctrlr_s *spictrlr, FAR struct ioexpander_dev_s *ioe); + +#endif + #ifdef __cplusplus } #endif