diff --git a/arch/sim/Kconfig b/arch/sim/Kconfig index 52222891223..d343978ec42 100644 --- a/arch/sim/Kconfig +++ b/arch/sim/Kconfig @@ -562,6 +562,31 @@ endchoice endif +config SIM_SPI + bool "Simulated SPI port" + default n + select SPI + ---help--- + Build in support for simulated spi port + +if SIM_SPI + +choice + prompt "Simulated SPI Type" + default SIM_SPI_LINUX + +config SIM_SPI_LINUX + bool "Linux SPI Character Dev" + depends on HOST_LINUX + ---help--- + Attach a Linux SPI port via the character device + interface. To achieve a SPI port, it is recommended + to use a USB<>SPI device such as CH341A/B. + +endchoice + +endif + config SIM_UART_NUMBER int "The number of tty ports on sim platform, range is 0~4" default 0 diff --git a/arch/sim/src/Makefile b/arch/sim/src/Makefile index 65d460a3d03..c97dd0d648c 100644 --- a/arch/sim/src/Makefile +++ b/arch/sim/src/Makefile @@ -176,6 +176,10 @@ ifeq ($(CONFIG_SIM_I2CBUS_LINUX),y) HOSTSRCS += up_i2cbuslinux.c endif +ifeq ($(CONFIG_SIM_SPI_LINUX),y) + HOSTSRCS += up_spilinux.c +endif + ifeq ($(CONFIG_RPTUN),y) CSRCS += up_rptun.c endif diff --git a/arch/sim/src/sim/up_internal.h b/arch/sim/src/sim/up_internal.h index a0bfe4b9ccb..f8ef29cef1f 100644 --- a/arch/sim/src/sim/up_internal.h +++ b/arch/sim/src/sim/up_internal.h @@ -352,6 +352,13 @@ struct i2c_master_s *sim_i2cbus_initialize(int bus); int sim_i2cbus_uninitialize(struct i2c_master_s *dev); #endif +/* up_spi*.c ****************************************************************/ + +#ifdef CONFIG_SIM_SPI +struct spi_dev_s *sim_spi_initialize(const char *filename); +int sim_spi_uninitialize(struct spi_dev_s *dev); +#endif + /* Debug ********************************************************************/ #ifdef CONFIG_STACK_COLORATION diff --git a/arch/sim/src/sim/up_spi.h b/arch/sim/src/sim/up_spi.h new file mode 100644 index 00000000000..77947555b21 --- /dev/null +++ b/arch/sim/src/sim/up_spi.h @@ -0,0 +1,159 @@ +/**************************************************************************** + * arch/sim/src/sim/up_spi.h + * + * 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 _ARCH_SIM_SRC_SIM_SPI_H_ +#define _ARCH_SIM_SRC_SIM_SPI_H_ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#ifdef __SIM__ +#include "config.h" +#endif + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define HWFEAT_CS_INACTIVE (1 << 1) /* Force CS inactive after transfer. */ +#define HWFEAT_CS_ACTIVE (1 << 2) /* Force CS active after transfer. */ +#define HWFEAT_LSBFIRST (1 << 4) /* 1 for LSB 1st, default 0 for MSB 1st*/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* The types below (spi_dev_s, spi_ops_s, spi_mode_e, spi_hwfeatures_t and + * spi_mediachange_t) are the same as in nuttx/spi/spi.h. + */ + +enum spi_mode_e +{ + SPIDEV_MODE0 = 0, /* CPOL=0 CHPHA=0 */ + SPIDEV_MODE1, /* CPOL=0 CHPHA=1 */ + SPIDEV_MODE2, /* CPOL=1 CHPHA=0 */ + SPIDEV_MODE3, /* CPOL=1 CHPHA=1 */ + SPIDEV_MODETI, /* CPOL=0 CPHA=1 TI Synchronous Serial Frame Format */ +}; + +#ifdef CONFIG_SPI_HWFEATURES +typedef uint8_t spi_hwfeatures_t; +#endif + +typedef void (*spi_mediachange_t)(void *arg); + +struct spi_dev_s; +struct spi_ops_s +{ + int (*lock)(struct spi_dev_s *dev, bool lock); + void (*select)(struct spi_dev_s *dev, uint32_t devid, + bool selected); + uint32_t (*setfrequency)(struct spi_dev_s *dev, + uint32_t frequency); +#ifdef CONFIG_SPI_CS_DELAY_CONTROL + int (*setdelay)(struct spi_dev_s *dev, uint32_t a, + uint32_t b, uint32_t c); +#endif + void (*setmode)(struct spi_dev_s *dev, enum spi_mode_e mode); + void (*setbits)(struct spi_dev_s *dev, int nbits); +#ifdef CONFIG_SPI_HWFEATURES + int (*hwfeatures)(struct spi_dev_s *dev, + spi_hwfeatures_t features); +#endif + uint8_t (*status)(struct spi_dev_s *dev, uint32_t devid); +#ifdef CONFIG_SPI_CMDDATA + int (*cmddata)(struct spi_dev_s *dev, uint32_t devid, + bool cmd); + #endif + uint32_t (*send)(struct spi_dev_s *dev, uint32_t wd); +#ifdef CONFIG_SPI_EXCHANGE + void (*exchange)(struct spi_dev_s *dev, + const void *txbuffer, void *rxbuffer, + size_t nwords); +#else + void (*sndblock)(struct spi_dev_s *dev, + const void *buffer, size_t nwords); + void (*recvblock)(struct spi_dev_s *dev, void *buffer, + size_t nwords); +#endif +#ifdef CONFIG_SPI_TRIGGER + int (*trigger)(struct spi_dev_s *dev); +#endif + int (*registercallback)(struct spi_dev_s *dev, + spi_mediachange_t callback, void *arg); +}; + +struct spi_dev_s +{ + const struct spi_ops_s *ops; +}; + +/* NuttX SPI transaction struct (ref: spi_transfer.h). */ + +struct spi_trans_s +{ + /* SPI attributes for unique to this transaction */ + + bool deselect; /* De-select after transfer */ +#ifdef CONFIG_SPI_CMDDATA + bool cmd; /* true=command; false=data */ +#endif +#ifdef CONFIG_SPI_HWFEATURES + spi_hwfeatures_t hwfeat; /* H/W features to enable on this transfer */ +#endif + useconds_t delay; /* Microsecond delay after transfer */ + + /* These describe the single data transfer */ + + size_t nwords; /* Number of words in transfer */ + const void *txbuffer; /* Source buffer for TX transfer */ + void *rxbuffer; /* Sink buffer for RX transfer */ +}; + +/* NuttX SPI sequence of transactions (ref: spi_transfer.h). */ + +struct spi_sequence_s +{ + /* Properties that are fixed throughout the transfer */ + + uint32_t dev; /* See enum spi_devtype_e */ + uint8_t mode; /* See enum spi_mode_e */ + uint8_t nbits; /* Number of bits */ + uint8_t ntrans; /* Number of transactions */ + uint32_t frequency; /* SPI frequency (Hz) */ +#ifdef CONFIG_SPI_CS_DELAY_CONTROL + uint32_t a; /* Arguments to setdelay() */ + uint32_t b; + uint32_t c; +#endif + + /* A pointer to the list of transfers to be be performed. */ + + struct spi_trans_s *trans; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#endif /* _ARCH_SIM_SRC_SIM_SPI_H_ */ diff --git a/arch/sim/src/sim/up_spilinux.c b/arch/sim/src/sim/up_spilinux.c new file mode 100644 index 00000000000..488a0bb8be6 --- /dev/null +++ b/arch/sim/src/sim/up_spilinux.c @@ -0,0 +1,732 @@ +/**************************************************************************** + * arch/sim/src/sim/up_spilinux.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 + +#include + +#include "up_spi.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define ERROR(fmt, ...) \ + syslog(LOG_ERR, "up_spilinux: " fmt "\n", ##__VA_ARGS__) +#define INFO(fmt, ...) \ + syslog(LOG_ERR, "up_spilinux: " fmt "\n", ##__VA_ARGS__) +#define DEBUG(fmt, ...) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct linux_spi_dev_s +{ + const struct spi_ops_s *ops; /* SPI vtable */ + int file; /* SPI device file descriptor in Linux. */ +#ifdef CONFIG_SPI_HWFEATURES + spi_hwfeatures_t hwfeatures; /* Some hardware features. */ +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int linux_spi_lock(struct spi_dev_s *dev, bool lock); +static void linux_spi_select(struct spi_dev_s *dev, uint32_t devid, + bool selected); +static uint32_t linux_spi_setfrequency(struct spi_dev_s *dev, + uint32_t frequency); +#ifdef CONFIG_SPI_CS_DELAY_CONTROL +static int linux_spi_setdelay(struct spi_dev_s *dev, uint32_t a, uint32_t b, + uint32_t c); +#endif +static void linux_spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode); +static void linux_spi_setbits(struct spi_dev_s *dev, int nbits); +#ifdef CONFIG_SPI_HWFEATURES +static int linux_spi_hwfeatures(struct spi_dev_s *dev, + spi_hwfeatures_t features); +#endif +static uint8_t linux_spi_status(struct spi_dev_s *dev, uint32_t devid); +#ifdef CONFIG_SPI_CMDDATA +static int linux_spi_cmddata(struct spi_dev_s *dev, uint32_t devid, + bool cmd); +#endif +static uint32_t linux_spi_send(struct spi_dev_s *dev, uint32_t wd); +#ifdef CONFIG_SPI_EXCHANGE +static void linux_spi_exchange(struct spi_dev_s *dev, const void *txbuffer, + void *rxbuffer, size_t nwords); +#else +static void linux_spi_sndblock(struct spi_dev_s *dev, const void *buffer, + size_t nwords); +static void linux_spi_recvblock(struct spi_dev_s *dev, void *buffer, + size_t nwords); +#endif +#ifdef CONFIG_SPI_TRIGGER +static int linux_spi_trigger(struct spi_dev_s *dev); +#endif +static int linux_spi_registercallback(struct spi_dev_s *dev, + spi_mediachange_t callback, void *arg); +static int linux_spi_transfer(struct spi_dev_s *dev, const void *txbuffer, + void *rxbuffer, size_t nwords); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct spi_ops_s spi_linux_ops = +{ + /* The operations below are the same as those in nuttx/spi/spi.h. + * Some perations are dummy, merely for compatiablity with nuttx spi. + */ + + .lock = linux_spi_lock, /* Dummy for compatibility. */ + .select = linux_spi_select, /* Dummy for compatibility. */ + .setfrequency = linux_spi_setfrequency, /* Set max speed. */ +#ifdef CONFIG_SPI_CS_DELAY_CONTROL + .setdelay = linux_spi_setdelay, /* Dummy for compatibility. */ +#endif + .setmode = linux_spi_setmode, /* Set mode 0~3. */ + .setbits = linux_spi_setbits, /* Set bits per word. */ +#ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = linux_spi_hwfeatures, /* Dummy for compatibility. */ +#endif + .status = linux_spi_status, /* Dummy for compatibility. */ +#ifdef CONFIG_SPI_CMDDATA + .cmddata = linux_spi_cmddata, /* Dummy for compatibility. */ +#endif + .send = linux_spi_send, /* Send a word. */ +#ifdef CONFIG_SPI_EXCHANGE + .exchange = linux_spi_exchange, /* Send and receive words. */ +#else + .sndblock = linux_spi_sndblock, /* Send several words. */ + .recvblock = linux_spi_recvblock, /* Receive several words. */ +#endif +#ifdef CONFIG_SPI_TRIGGER + .trigger = linux_spi_trigger, /* Dummy for compatibility. */ +#endif + .registercallback = linux_spi_registercallback, /* Dummy for + * compatibility. */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: linux_spi_lock + * + * Description: + * Provide spi lock, used for getting exclusive access to the SPI bus. + * It's not supported by this driver nor a linux spi, and will directly + * return 0. + * + * Input Parameters: + * dev - A pointer to instance of Linux SPI device. + * lock - TRUE: lock, FALSE: unlock. + * + * Returned Value: + * 0 for success, since it's a necessary step for Nuttx SPI transfer. + ****************************************************************************/ + +static int linux_spi_lock(struct spi_dev_s *dev, bool lock) +{ + return 0; +} + +/**************************************************************************** + * Name: linux_spi_select + * + * Description: + * Provide spi select, used for selecting the device before a transfer. + * It's not supported by linux spi and will do nothing. For a Linux SPI + * device "spidevN.P", the N means bus number and P means CS number. When + * you choose the spidevN.P to attach to simulator, only CS_P will be + * selected and then de-selected automatically when you call a ioctl() + * operation with SPI_IOC_MESSAGE(x). + * + * Input Parameters: + * dev - A pointer to instance of Linux SPI device. + * devid - The CS id, + * selected - TRUE: slave selected, FALSE: slave de-selected + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void linux_spi_select(struct spi_dev_s *dev, uint32_t devid, + bool selected) +{ +} + +/**************************************************************************** + * Name: linux_spi_setfrequency + * + * Description: + * Provide spi setfrequency, used for set SPI clock frequency. + * Note that only MAX_SPEED_HZ could be configured out of a transfer for a + * Linux SPI port. The Linux SPI may set a exact frequecy using the value + * of spi_ioc_transfer.speed_hz when transferring. If the + * spi_ioc_transfer.speed_hz is 0, the MAX_SPEED_HZ is used. In practice, + * the real frequecy on the CLK wire will be affected by the hardware. + * + * Input Parameters: + * dev - A pointer to instance of Linux SPI device. + * frequency - The frequencey of SPI clock in Hz. + * + * Returned Value: + * Returns the actual frequency in Hz. + * + ****************************************************************************/ + +static uint32_t linux_spi_setfrequency(struct spi_dev_s *dev, + uint32_t frequency) +{ + struct linux_spi_dev_s *priv = (struct linux_spi_dev_s *)dev; + int file = priv->file; + uint32_t actualfreq; + + ioctl(file, SPI_IOC_WR_MAX_SPEED_HZ, &frequency); + ioctl(file, SPI_IOC_RD_MAX_SPEED_HZ, &actualfreq); + + return actualfreq; +} + +/**************************************************************************** + * Name: linux_spi_setdelay + * + * Description: + * Provide spi setdelay. + * It's not supported by this driver and will return error. DelayS between + * CS change and CLK is not supported by a Linux SPI. Delay between CS + * inactive and active again is set in spi_ioc_transfer.delay_usecs + * (precondition: spi_ioc_transfer.cs_change = true) when using + * ioctl(filep, SPI_IOC_MEASSAGE(N), &spi_ioc_transfer) for a Linux SPI. + * For a Nuttx SPI driver it's set in spi_trans_s.delay (precondition: call + * hwfeatures operation with HWFEAT_FORCE_CS_INACTIVE_AFTER_TRANSFER + * first). Thus csdelay need not to be set here. This optional operation + * should not be used (let SPI_CS_DELAY_CONTROL = n), or one will get an + * error. + * + * Input Parameters: + * dev - A pointer to instance of Linux SPI device. + * startdelay - The delay between CS active and first CLK + * stopdelay - The delay between last CLK and CS inactive + * csdelay - The delay between CS inactive and CS active again + * + * Returned Value: + * -ENOSYS for not supported. + * + ****************************************************************************/ + +#ifdef CONFIG_SPI_CS_DELAY_CONTROL +static int linux_spi_setdelay(struct spi_dev_s *dev, uint32_t startdelay, + uint32_t stopdelay, uint32_t csdelay) +{ + return -ENOSYS; +} +#endif + +/**************************************************************************** + * Name: linux_spi_setmode + * + * Description: + * Provide spi setmode. + * SPI mode defination in nuttx is almost the same to that in Linux. + * + * Input Parameters: + * dev - A pointer to instance of Linux SPI device. + * mode - The SPI mode requested + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void linux_spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode) +{ + struct linux_spi_dev_s *priv = (struct linux_spi_dev_s *)dev; + int file = priv->file; + uint8_t spilinuxmode; + + switch (mode) + { + case SPIDEV_MODE0: + { + spilinuxmode = SPI_MODE_0; + } + break; + + /* In fact SPIDEV_MODETI is equal to SPIDEV_MODE1 (CPOL=0 CPHA=1). */ + + case SPIDEV_MODETI: + + case SPIDEV_MODE1: + { + spilinuxmode = SPI_MODE_1; + } + break; + + case SPIDEV_MODE2: + { + spilinuxmode = SPI_MODE_2; + } + break; + + case SPIDEV_MODE3: + { + spilinuxmode = SPI_MODE_3; + } + break; + + default: + { + spilinuxmode = SPI_MODE_0; + } + break; + } + + ioctl(file, SPI_IOC_WR_MODE, &spilinuxmode); +} + +/**************************************************************************** + * Name: linux_spi_setbits + * + * Description: + * Provide spi setbits, used for set bits per word during a transfer. + * + * Input Parameters: + * dev - A pointer to instance of Linux SPI device. + * nbits - The number of bits in an SPI word. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void linux_spi_setbits(struct spi_dev_s *dev, int nbits) +{ + struct linux_spi_dev_s *priv = (struct linux_spi_dev_s *)dev; + int file = priv->file; + uint8_t bits_per_word = (uint8_t)nbits; + + ioctl(file, SPI_IOC_WR_BITS_PER_WORD, &bits_per_word); +} + +/**************************************************************************** + * Name: linux_spi_hwfeatures + * + * Description: + * Provide spi hwfeatures. + * Note that not all configurations are supported. + * + * Input Parameters: + * dev - A pointer to instance of Linux SPI device. + * features - Hardware feature flag. + * + * Returned Value: + * 0 for success, and negated errno for error. + * + ****************************************************************************/ + +#ifdef CONFIG_SPI_HWFEATURES +static int linux_spi_hwfeatures(struct spi_dev_s *dev, + spi_hwfeatures_t features) +{ + struct linux_spi_dev_s *priv = (struct linux_spi_dev_s *)dev; + int file = priv->file; + uint8_t lsb = 0; + + /* These are currently defined feature flags in nuttx/spi/spi.h: + * + * Bit 0: HWFEAT_CRCGENERATION + * Hardware CRC generation + * Bit 1: HWFEAT_FORCE_CS_INACTIVE_AFTER_TRANSFER + * CS rises after every Transmission, also if we provide new data + * immediately + * Bit 2: HWFEAT_FORCE_CS_ACTIVE_AFTER_TRANSFER + * CS does not rise automatically after a transmission, also if + * the spi runs out of data (for a long time) + * Bit 3: HWFEAT_ESCAPE_LASTXFER + * Do not set the LASTXFER-Bit at the last word of the next + * exchange, Flag is auto-resetting after the next LASTXFER + * condition. (see spi_exchange) + * Bit 4: HWFEAT_LSBFIRST + * Data transferred LSB first (default is MSB first) + * Bit 5: Turn deferred trigger mode on or off. Primarily used for DMA + * mode. If a transfer is deferred then the DMA will not actually + * be triggered until a subsequent call to SPI_TRIGGER to set it + * off. + * Among them, features on bit 1, 2, 4 is supported. + * And CS_ACTIVE/INACTIVE can not be set immediately until calling + * linux_spi_transfer. Here it's recorded in linux_spi_dev_s.hwfeatures + * for furture use. + */ + + priv->hwfeatures = features; + + /* MSB or LSB first can be set immediately here. */ + + if (features & HWFEAT_LSBFIRST) + { + lsb = 1; + } + + return ioctl(file, SPI_IOC_WR_LSB_FIRST, &lsb); +} +#endif + +/**************************************************************************** + * Name: linux_spi_status + * + * Description: + * Provide spi status. + * It's not supported by linux spi and will directly return. + * + * Input Parameters: + * dev - A pointer to instance of Linux SPI device. + * devid - The CS id, not supported in Linux SPI. For a Linux SPI device + * "spidevN.P", the N means bus number and P means CS number. + * + * Returned Value: + * 0 for no status to be reported. + * + ****************************************************************************/ + +static uint8_t linux_spi_status(struct spi_dev_s *dev, uint32_t devid) +{ + return 0; +} + +/**************************************************************************** + * Name: linux_spi_cmddata + * + * Description: + * Provide spi cmddata toggle. + * This need an additional out-of-band bit and is not supported by this + * driver. This operation should not be used (let SPI_CMDDATA = n). + * + * Input Parameters: + * dev - A pointer to instance of Linux SPI device. + * devid - The CS id, not supported in Linux SPI. For a Linux SPI device + * "spidevN.P", the N means bus number and P means CS number. + * cmd - TRUE: The following word is a command; FALSE: the following words + * are data. + * + * Returned Value: + * -ENOSYS for not supported. + * + ****************************************************************************/ + +#ifdef CONFIG_SPI_CMDDATA +static int linux_spi_cmddata(struct spi_dev_s *dev, uint32_t devid, bool cmd) +{ + return -ENOSYS; +} +#endif + +/**************************************************************************** + * Name: linux_spi_send + * + * Description: + * Provide spi send, used for sending one word. + * + * Input Parameters: + * dev - A pointer to instance of Linux SPI device. + * wd - The word to send. The size of the data is determined by the number + * of bits selected for the SPI interface. + * + * Returned Value: + * Received word value. + * + ****************************************************************************/ + +static uint32_t linux_spi_send(struct spi_dev_s *dev, uint32_t wd) +{ + uint32_t recvwd = 0; + + linux_spi_transfer(dev, &wd, &recvwd, 1); + + return recvwd; +} + +/**************************************************************************** + * Name: linux_spi_exchange + * + * Description: + * Provide spi exchange, used for transmit and receive N words. + * + * Input Parameters: + * dev - A pointer to instance of Linux SPI device. + * txbuffer - A pointer to the buffer in which to transmit data. + * rxbuffer - A pointer to the buffer in which to receive data. + * nwords - The length of data that can be transferred in the buffer + * in number of words. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef CONFIG_SPI_EXCHANGE +static void linux_spi_exchange(struct spi_dev_s *dev, const void *txbuffer, + void *rxbuffer, size_t nwords) +{ + linux_spi_transfer(dev, txbuffer, rxbuffer, nwords); +} +#endif + +/**************************************************************************** + * Name: linux_spi_sndblock + * + * Description: + * Provide spi sndblock, used for send several words. + * + * Input Parameters: + * dev - A pointer to instance of Linux SPI device. + * buffer - A pointer to the buffer in which to transmit data. + * nwords - The length of data that can be send in the buffer in number of + * words. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifndef CONFIG_SPI_EXCHANGE +static void linux_spi_sndblock(struct spi_dev_s *dev, const void *buffer, + size_t nwords) +{ + linux_spi_transfer(dev, txbuffer, NULL, nwords); +} +#endif + +/**************************************************************************** + * Name: linux_spi_recvblock + * + * Description: + * Provide spi recvblock, used for receive(read) several words. + * + * Input Parameters: + * dev - A pointer to instance of Linux SPI device. + * buffer - A pointer to the buffer in which to receive data. + * nwords - The length of data that can be received in the buffer in number + * of words. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifndef CONFIG_SPI_EXCHANGE +static void linux_spi_recvblock(struct spi_dev_s *dev, void *buffer, + size_t nwords) +{ + linux_spi_transfer(dev, NULL, rxbuffer, nwords); +} +#endif + +/**************************************************************************** + * Name: linux_spi_trigger + * + * Description: + * Provide spi trigger, to trigger a previously configured DMA transfer. + * It's not supported by this driver. This operation should not be used + * (let SPI_TRIGGER = n). + * + * Input Parameters: + * dev - A pointer to instance of Linux SPI device. + * + * Returned Value: + * -ENOSYS for not supported. + * + ****************************************************************************/ + +#ifdef CONFIG_SPI_TRIGGER +static int linux_spi_trigger(struct spi_dev_s *dev) +{ + return -ENOSYS; +} +#endif + +/**************************************************************************** + * Name: linux_spi_registercallback + * + * Description: + * Provide spi registercallback. + * It's not supported by linux spi and will directly return. + * + * Input Parameters: + * dev - A pointer to instance of Linux SPI device. + * callback - The function to call on the media change + * arg - A caller provided value to return with the callback + * + * Returned Value: + * -ENOSYS for not supported. + * + ****************************************************************************/ + +static int linux_spi_registercallback(struct spi_dev_s *dev, + spi_mediachange_t callback, void *arg) +{ + return -ENOSYS; +} + +/**************************************************************************** + * Name: linux_spi_transfer + * + * Description: + * Provide spi transfer as the base of linux_spi_send, linux_spi__exchange, + * linux_spi__sndblock and linux_spi_recvblock. + * + * Input Parameters: + * dev - A pointer to instance of Linux SPI device. + * txbuffer - A pointer to the buffer in which to transmit data. + * rxbuffer - A pointer to the buffer in which to receive data. + * nwords - The length of data that can be transferred in the buffer + * in number of words. + * + * Returned Value: + * Actual number of the words transferred. + * + ****************************************************************************/ + +static int linux_spi_transfer(struct spi_dev_s *dev, const void *txbuffer, + void *rxbuffer, size_t nwords) +{ + struct linux_spi_dev_s *priv = (struct linux_spi_dev_s *)dev; + int file = priv->file; + + /* Some members of struct spi_ioc_transfer transfer_data is default 0: + * @speed_hz = 0, thus it's ignored, MAX_SEPPD_HZ will be used. + * @bits_per_word = 0, thus it's ignored, BITS_PER_WORD will be used. + * @delay_usecs = 0, thus thers's no delay before next transfer. + */ + + struct spi_ioc_transfer transfer_data = { + .tx_buf = (unsigned long)txbuffer, /* Transmit buffer. */ + .rx_buf = (unsigned long)rxbuffer, /* Receive buffer. */ + .len = (uint32_t)nwords, /* Number of words. */ + .cs_change = false, /* In normal, CS remains selected. */ + }; + +#ifdef CONFIG_SPI_HWFEATURES + /* If g_hwfeatures is set as HWFEAT_CS_INACTIVE before, using + * linux_spi_hwfeatures, then that setting will take effect here. If + * g_hwfeatures is set as HWFEAT_CS_ACTIVE which is the normal case, there + * is no need to change transfer_data.cs_change. + */ + + if (priv->hwfeatures | HWFEAT_CS_INACTIVE) + { + transfer_data.cs_change = true; + } +#endif + + return ioctl(file, SPI_IOC_MESSAGE(1), &transfer_data); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sim_spi_initialize + * + * Description: + * Initialize one SPI port + * + * Input Parameters: + * filename - the name of SPI device in Linux, e.g. "spidev0.0". + * + * Returned Value: + * The pointer to the instance of Linux SPI device. + * + ****************************************************************************/ + +struct spi_dev_s *sim_spi_initialize(const char *filename) +{ + struct linux_spi_dev_s *priv; + + priv = (struct linux_spi_dev_s *)malloc(sizeof(priv)); + if (priv == NULL) + { + ERROR("Failed to allocate private spi master driver"); + return NULL; + } + + priv->file = open(filename, O_RDWR); + if (priv->file < 0) + { + ERROR("Failed to open %s: %d", filename, priv->file); + free(priv); + return NULL; + } + + priv->ops = &spi_linux_ops; +#ifdef CONFIG_SPI_HWFEATURES + priv->hwfeatures = 0; +#endif + + return (struct spi_dev_s *)priv; +} + +/**************************************************************************** + * Name: sim_spi_uninitialize + * + * Description: + * Uninitialize an SPI port + * + * Input Parameters: + * dev - A pointer to instance of Linux SPI device. + * + * Returned Value: + * 0 for OK. + * + ****************************************************************************/ + +int sim_spi_uninitialize(struct spi_dev_s *dev) +{ + struct linux_spi_dev_s *priv = (struct linux_spi_dev_s *)dev; + if (priv->file >= 0) + { + close(priv->file); + } + + free(priv); + return 0; +} diff --git a/boards/sim/sim/sim/Kconfig b/boards/sim/sim/sim/Kconfig index 4a138b09ee6..1b76943666b 100644 --- a/boards/sim/sim/sim/Kconfig +++ b/boards/sim/sim/sim/Kconfig @@ -58,7 +58,6 @@ config SIM_WTGAHRS2_UARTN ---help--- We can select the number accoding to which SIM_UARTX_NAME is uesd to sensor. This range is 0-4. -endif config SIM_I2CBUS_ID int "I2C host bus ID to attach to simulator" @@ -67,3 +66,13 @@ config SIM_I2CBUS_ID ---help--- This is the bus identifier that should be used by the host implementation to attach to the simulator driver. + +config SIM_SPIDEV_NAME + string "the name of SPI host dev to attach to simulator" + default "/dev/spidev0.0" + depends on SIM_SPI + ---help--- + This is the name of the SPI device on the host implementation to + attach to the simulator driver. + +endif diff --git a/boards/sim/sim/sim/src/sim_bringup.c b/boards/sim/sim/sim/src/sim_bringup.c index 4a701a1ed09..3aa4ae55351 100644 --- a/boards/sim/sim/sim/src/sim_bringup.c +++ b/boards/sim/sim/sim/src/sim_bringup.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -101,6 +102,9 @@ int sim_bringup(void) #ifdef CONFIG_MPU60X0_I2C FAR struct mpu_config_s *mpu_config; #endif +#ifdef CONFIG_SIM_SPI + FAR struct spi_dev_s *spidev; +#endif int ret = OK; @@ -405,6 +409,26 @@ int sim_bringup(void) #endif #endif +#ifdef CONFIG_SIM_SPI + spidev = sim_spi_initialize(CONFIG_SIM_SPIDEV_NAME); + if (spidev == NULL) + { + syslog(LOG_ERR, "ERROR: sim_spi_initialize failed.\n"); + } +#ifdef CONFIG_SYSTEM_SPITOOL + else + { + ret = spi_register(spidev, 0); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: Failed to register SPI%d driver: %d\n", + 0, ret); + sim_spi_uninitialize(spidev); + } + } +#endif /* CONFIG_SYSTEM_SPITOOL */ +#endif /* CONFIG_SIM_SPI */ + #if defined(CONFIG_INPUT_BUTTONS_LOWER) && defined(CONFIG_SIM_BUTTONS) ret = btn_lower_initialize("/dev/buttons"); if (ret < 0)