diff --git a/Documentation/platforms/risc-v/mpfs/index.rst b/Documentation/platforms/risc-v/mpfs/index.rst index cafbe2a336..8b4d6d040a 100644 --- a/Documentation/platforms/risc-v/mpfs/index.rst +++ b/Documentation/platforms/risc-v/mpfs/index.rst @@ -57,7 +57,7 @@ Peripheral Support NOTES ============ ======= ===== GPIO Yes MMUART Yes Uart mode only -SPI No +SPI Yes I2C No Timers No Watchdog No @@ -79,4 +79,4 @@ Supported Boards boards/*/* - \ No newline at end of file + diff --git a/arch/risc-v/src/mpfs/Kconfig b/arch/risc-v/src/mpfs/Kconfig index 3531c9772f..0756cc23f4 100755 --- a/arch/risc-v/src/mpfs/Kconfig +++ b/arch/risc-v/src/mpfs/Kconfig @@ -50,6 +50,14 @@ config MPFS_HAVE_UART4 # These are the peripheral selections proper +config MPFS_SPI0 + bool "SPI 0" + default n + +config MPFS_SPI1 + bool "SPI 1" + default n + config MPFS_UART0 bool "UART 0" default n diff --git a/arch/risc-v/src/mpfs/Make.defs b/arch/risc-v/src/mpfs/Make.defs index c2bfc098ea..2267b24752 100755 --- a/arch/risc-v/src/mpfs/Make.defs +++ b/arch/risc-v/src/mpfs/Make.defs @@ -61,3 +61,7 @@ CMN_UASRCS += riscv_signal_handler.S CHIP_CSRCS += mpfs_userspace.c endif + +ifeq ($(CONFIG_SPI),y) +CHIP_CSRCS += mpfs_spi.c +endif diff --git a/arch/risc-v/src/mpfs/hardware/mpfs_spi.h b/arch/risc-v/src/mpfs/hardware/mpfs_spi.h new file mode 100755 index 0000000000..8b50e39264 --- /dev/null +++ b/arch/risc-v/src/mpfs/hardware/mpfs_spi.h @@ -0,0 +1,129 @@ +/**************************************************************************** + * arch/risc-v/src/mpfs/hardware/mpfs_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_RISCV_SRC_MPFS_HARDWARE_MPFS_SPI_H +#define __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_SPI_H + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* CONTROL register */ + +#define MPFS_SPI_RESET (1 << 31) +#define MPFS_SPI_OENOFF (1 << 30) +#define MPFS_SPI_BIGFIFO (1 << 29) +#define MPFS_SPI_CLKMODE (1 << 28) +#define MPFS_SPI_FRAMEURUN (1 << 27) +#define MPFS_SPI_SPS (1 << 26) +#define MPFS_SPI_SPH (1 << 25) +#define MPFS_SPI_SPO (1 << 24) +#define MPFS_SPI_FRAMECNT (0xffff << 8) +#define MPFS_SPI_INTTXTURUN (1 << 7) +#define MPFS_SPI_INTRXOVRFLOW (1 << 6) +#define MPFS_SPI_INTTXDATA (1 << 5) +#define MPFS_SPI_INTRXDATA (1 << 4) +#define MPFS_SPI_TRANSFPRTL (1 << 2) | (1 << 3) +#define MPFS_SPI_MODE (1 << 1) +#define MPFS_SPI_ENABLE (1 << 0) + +/* FRAMESIZE register */ + +#define MPFS_SPI_FRAMESIZE (0x3F) + +/* STATUS register */ + +#define MPFS_SPI_ACTIVE (1 << 14) +#define MPFS_SPI_SSEL (1 << 13) +#define MPFS_SPI_FRAMESTART (1 << 12) +#define MPFS_SPI_TXFIFOEMPNXT (1 << 11) +#define MPFS_SPI_TXFIFOEMP (1 << 10) +#define MPFS_SPI_TXFIFOFULNXT (1 << 9) +#define MPFS_SPI_TXFIFOFUL (1 << 8) +#define MPFS_SPI_RXFIFOEMPNXT (1 << 7) +#define MPFS_SPI_RXFIFOEMP (1 << 6) +#define MPFS_SPI_RXFIFOFULNXT (1 << 5) +#define MPFS_SPI_RXFIFOFUL (1 << 4) +#define MPFS_SPI_TXUNDERRUN (1 << 3) +#define MPFS_SPI_RXOVERFLOW (1 << 2) +#define MPFS_SPI_RXDATRCED (1 << 1) +#define MPFS_SPI_TXDATSENT (1 << 0) + +/* INT_CLEAR register */ + +#define MPFS_SPI_SSEND (1 << 5) +#define MPFS_SPI_CMDINT (1 << 4) +#define MPFS_SPI_TXCHUNDRUN (1 << 3) +#define MPFS_SPI_RXCHOVRFLW (1 << 2) +#define MPFS_SPI_RXRDONECLR (1 << 1) +#define MPFS_SPI_TXDONECLR (1 << 0) + +/* INTMASK register */ + +#define MPFS_SPI_SSENDMSKINT (1 << 5) +#define MPFS_SPI_CMDMSKINT (1 << 4) +#define MPFS_SPI_TXCHUNDDMSKINT (1 << 3) +#define MPFS_SPI_RXCHOVRFMSKINT (1 << 2) +#define MPFS_SPI_RXRDYMSKINT (1 << 1) +#define MPFS_SPI_TXDONEMSKINT (1 << 0) + +/* INTRAW register */ + +#define MPFS_SPI_TXCHUNDR (1 << 3) +#define MPFS_SPI_RXOVRFLW (1 << 2) +#define MPFS_SPI_RXRDY (1 << 1) +#define MPFS_SPI_TXDONE (1 << 0) + +/* CONTROL2 register */ + +#define MPFS_SPI_INTEN_SSEND (1 << 5) +#define MPFS_SPI_INTEN_CMD (1 << 4) +#define MPFS_SPI_DISFRMCNT (1 << 2) +#define MPFS_SPI_AUTOPOLL (1 << 1) +#define MPFS_SPI_AUTOSTATUS (1 << 0) + +/* COMMAND register */ + +#define MPFS_SPI_TXNOW (1 << 6) +#define MPFS_SPI_AUTOSTALL (1 << 5) +#define MPFS_SPI_CLRFRAMECNT (1 << 4) +#define MPFS_SPI_TXFIFORST (1 << 3) +#define MPFS_SPI_RXFIFORST (1 << 2) +#define MPFS_SPI_AUTOEMPTY (1 << 1) +#define MPFS_SPI_AUTOFILL (1 << 0) + +/* HWSTATUS register */ + +#define MPFS_SPI_USER (1 << 2) | (1 << 3) +#define MPFS_SPI_TXBUSY (1 << 1) +#define MPFS_SPI_RXBUSY (1 << 0) + +/* STAT8 register */ + +#define MPFS_SPI_S8_ACTIVEL (1 << 6) +#define MPFS_SPI_S8_SSEL (1 << 6) +#define MPFS_SPI_S8_TXUNDERRUN (1 << 5) +#define MPFS_SPI_S8_RXOVERFLOW (1 << 4) +#define MPFS_SPI_S8_TXFIFOFUL (1 << 3) +#define MPFS_SPI_S8_RXFIFOEMP (1 << 2) +#define MPFS_SPI_S8_DONE (1 << 1) +#define MPFS_SPI_S8_FRAMESTART (1 << 0) + +#endif /* __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_SPI_H */ diff --git a/arch/risc-v/src/mpfs/mpfs_spi.c b/arch/risc-v/src/mpfs/mpfs_spi.c new file mode 100644 index 0000000000..ffcb476f41 --- /dev/null +++ b/arch/risc-v/src/mpfs/mpfs_spi.c @@ -0,0 +1,1519 @@ +/**************************************************************************** + * arch/risc-v/src/mpfs/mpfs_spi.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 +#include + +#include + +#include "mpfs_spi.h" +#include "hardware/mpfs_spi.h" +#include "hardware/mpfs_sysreg.h" +#include "riscv_arch.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MPFS_SPI_FREQ_DEFAULT 4000000 + +#define MPFS_SYSREG_SOFT_RESET_CR (MPFS_SYSREG_BASE + \ + MPFS_SYSREG_SOFT_RESET_CR_OFFSET) +#define MPFS_SYSREG_SUBBLK_CLOCK_CR (MPFS_SYSREG_BASE + \ + MPFS_SYSREG_SUBBLK_CLOCK_CR_OFFSET) + +#define MPFS_SPI_CONTROL_OFFSET 0x00 +#define MPFS_SPI_FRAMESIZE_OFFSET 0x04 +#define MPFS_SPI_STATUS_OFFSET 0x08 +#define MPFS_SPI_INT_CLEAR_OFFSET 0x0C +#define MPFS_SPI_RX_DATA_OFFSET 0x10 +#define MPFS_SPI_TX_DATA_OFFSET 0x14 +#define MPFS_SPI_CLK_GEN_OFFSET 0x18 +#define MPFS_SPI_SLAVE_SELECT_OFFSET 0x1C +#define MPFS_SPI_INTMASK_OFFSET 0x20 +#define MPFS_SPI_INTRAW_OFFSET 0x24 +#define MPFS_SPI_CONTROL2_OFFSET 0x28 +#define MPFS_SPI_COMMAND_OFFSET 0x2C +#define MPFS_SPI_PKTSIZE_OFFSET 0x30 +#define MPFS_SPI_CMD_SIZE_OFFSET 0x34 +#define MPFS_SPI_HWSTATUS_OFFSET 0x38 +#define MPFS_SPI_STATS_OFFSET 0x3C +#define MPFS_SPI_CTRL0_OFFSET 0x40 +#define MPFS_SPI_CTRL1_OFFSET 0x44 +#define MPFS_SPI_CTRL2_OFFSET 0x48 +#define MPFS_SPI_CTRL3_OFFSET 0x4C +#define MPFS_SPI_FRAMESUP_OFFSET 0x50 + +#define MPFS_SPI_CONTROL (priv->hw_base + MPFS_SPI_CONTROL_OFFSET) +#define MPFS_SPI_FSIZE (priv->hw_base + MPFS_SPI_FRAMESIZE_OFFSET) +#define MPFS_SPI_STATUS (priv->hw_base + MPFS_SPI_STATUS_OFFSET) +#define MPFS_SPI_RX_DATA (priv->hw_base + MPFS_SPI_RX_DATA_OFFSET) +#define MPFS_SPI_TX_DATA (priv->hw_base + MPFS_SPI_TX_DATA_OFFSET) +#define MPFS_SPI_INT_CLEAR (priv->hw_base + MPFS_SPI_INT_CLEAR_OFFSET) +#define MPFS_SPI_CLK_GEN (priv->hw_base + MPFS_SPI_CLK_GEN_OFFSET) +#define MPFS_SPI_SSELECT (priv->hw_base + MPFS_SPI_SLAVE_SELECT_OFFSET) +#define MPFS_SPI_INTMASK (priv->hw_base + MPFS_SPI_INTMASK_OFFSET) +#define MPFS_SPI_CONTROL2 (priv->hw_base + MPFS_SPI_CONTROL2_OFFSET) +#define MPFS_SPI_COMMAND (priv->hw_base + MPFS_SPI_COMMAND_OFFSET) +#define MPFS_SPI_PKTSIZE (priv->hw_base + MPFS_SPI_PKTSIZE_OFFSET) +#define MPFS_SPI_CMD_SIZE (priv->hw_base + MPFS_SPI_CMD_SIZE_OFFSET) +#define MPFS_SPI_FRAMESUP (priv->hw_base + MPFS_SPI_FRAMESUP_OFFSET) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* SPI Device hardware configuration */ + +struct mpfs_spi_config_s +{ + uint32_t clk_freq; /* SPI clock frequency */ + enum spi_mode_e mode; /* SPI default mode */ + bool use_irq; /* Use DMA */ +}; + +struct mpfs_spi_priv_s +{ + struct spi_dev_s spi_dev; + const struct mpfs_spi_config_s *config; /* Port configuration */ + + uintptr_t hw_base; /* Bus base address */ + uint16_t plic_irq; /* Platform IRQ */ + + int refs; /* Reference count */ + int enabled; /* Enable flag */ + const int id; /* SPI0 or SPI1 id */ + uint32_t devid; /* SPI CS device 0..7 */ + + sem_t sem_excl; /* Bus usage semaphore */ + sem_t sem_isr; /* Interrupt wait semaphore */ + + uint32_t frequency; /* Requested clock frequency */ + uint32_t actual; /* Actual clock frequency */ + uint32_t txwords; /* Words for TX */ + const void *txbuf; /* TX buffer */ + uint32_t tx_pos; /* TX position */ + + uint32_t rxwords; /* Words for RX */ + void *rxbuf; /* RX buffer */ + uint32_t rx_pos; /* RX position */ + + uint32_t fifosize; /* Fifo size */ + uint32_t fifolevel; /* Fifo IRQ level */ + int error; /* Detected error */ + + enum spi_mode_e mode; /* Actual SPI hardware mode */ + + uint8_t nbits; /* Bits per transaction */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int mpfs_spi_lock(struct spi_dev_s *dev, bool lock); + +static void mpfs_spi_select(struct spi_dev_s *dev, uint32_t devid, + bool selected); + +static uint32_t mpfs_spi_setfrequency(struct spi_dev_s *dev, + uint32_t frequency); +static void mpfs_spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode); +static void mpfs_spi_setbits(struct spi_dev_s *dev, int nbits); +#ifdef CONFIG_SPI_HWFEATURES +static int mpfs_spi_hwfeatures(struct spi_dev_s *dev, + spi_hwfeatures_t features); +#endif +static uint8_t mpfs_spi_status(struct spi_dev_s *dev, uint32_t devid); +#ifdef CONFIG_SPI_CMDDATA +static int mpfs_spi_cmddata(struct spi_dev_s *dev, + uint32_t devid, bool cmd); +#endif +static uint32_t mpfs_spi_send(struct spi_dev_s *dev, uint32_t wd); +static void mpfs_spi_exchange(struct spi_dev_s *dev, const void *txbuffer, + void *rxbuffer, size_t nwords); +#ifndef CONFIG_SPI_EXCHANGE +static void mpfs_spi_sndblock(struct spi_dev_s *dev, const void *txbuffer, + size_t nwords); +static void mpfs_spi_recvblock(struct spi_dev_s *dev, void *rxbuffer, + size_t nwords); +#endif +#ifdef CONFIG_SPI_TRIGGER +static int mpfs_spi_trigger(struct spi_dev_s *dev); +#endif +static void mpfs_spi_enable(struct mpfs_spi_priv_s *priv, uint8_t enable); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct mpfs_spi_config_s mpfs_spi_config = +{ + .clk_freq = MPFS_SPI_FREQ_DEFAULT, + .mode = SPIDEV_MODE0, + .use_irq = true, +}; + +static const struct spi_ops_s mpfs_spi_ops = +{ + .lock = mpfs_spi_lock, + .select = mpfs_spi_select, + .setfrequency = mpfs_spi_setfrequency, +#ifdef CONFIG_SPI_CS_DELAY_CONTROL + .setdelay = mpfs_spi_setdelay, +#endif + .setmode = mpfs_spi_setmode, + .setbits = mpfs_spi_setbits, +#ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = mpfs_spi_hwfeatures, +#endif + .status = mpfs_spi_status, +#ifdef CONFIG_SPI_CMDDATA + .cmddata = mpfs_spi_cmddata, +#endif + .send = mpfs_spi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = mpfs_spi_exchange, +#else + .sndblock = mpfs_spi_sndblock, + .recvblock = mpfs_spi_recvblock, +#endif +#ifdef CONFIG_SPI_TRIGGER + .trigger = mpfs_spi_trigger, +#endif + .registercallback = NULL, +}; + +#ifdef CONFIG_MPFS_SPI0 +static struct mpfs_spi_priv_s g_mpfs_spi0_priv = +{ + .spi_dev = + { + .ops = &mpfs_spi_ops + }, + .config = &mpfs_spi_config, + .hw_base = MPFS_SPI0_LO_BASE, + .plic_irq = MPFS_IRQ_SPI0, + .id = 0, + .devid = 0 +}; +#endif /* CONFIG_MPFS_SPI0 */ +#ifdef CONFIG_MPFS_SPI1 +static struct mpfs_spi_priv_s g_mpfs_spi1_priv = +{ + .spi_dev = + { + .ops = &mpfs_spi_ops + }, + .config = &mpfs_spi_config, + .hw_base = MPFS_SPI1_LO_BASE, + .plic_irq = MPFS_IRQ_SPI1, + .id = 1, + .devid = 1 +}; + +#endif /* CONFIG_MPFS_SPI1 */ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mpfs_spi_rxoverflow_recover + * + * Description: + * Recover from RX overflow condition. This resets the whole SPI device. + * + * Input Parameters: + * priv - Private SPI device structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_spi_rxoverflow_recover(struct mpfs_spi_priv_s *priv) +{ + uint32_t control_reg; + uint32_t clk_gen; + uint32_t frame_size; + uint32_t control2; + uint32_t packet_size; + uint32_t cmd_size; + uint32_t slave_select; + + /* Read current SPI hardware block configuration */ + + control_reg = getreg32(MPFS_SPI_CONTROL); + clk_gen = getreg32(MPFS_SPI_CLK_GEN); + frame_size = getreg32(MPFS_SPI_FRAMESIZE); + control2 = getreg32(MPFS_SPI_CONTROL2); + packet_size = getreg32(MPFS_SPI_PKTSIZE); + cmd_size = getreg32(MPFS_SPI_CMD_SIZE); + slave_select = getreg32(MPFS_SPI_SSELECT); + + /* Reset the SPI hardware block */ + + modifyreg32(MPFS_SPI_CONTROL, 0, MPFS_SPI_RESET); + modifyreg32(MPFS_SPI_CONTROL, MPFS_SPI_RESET, 0); + + mpfs_spi_enable(priv, 0); + + /* Restore SPI hardware block configuration */ + + putreg32(control_reg, MPFS_SPI_CONTROL); + putreg32(clk_gen, MPFS_SPI_CLK_GEN); + putreg32(frame_size, MPFS_SPI_FRAMESIZE); + + mpfs_spi_enable(priv, 1); + + putreg32(control2, MPFS_SPI_CONTROL2); + putreg32(packet_size, MPFS_SPI_PKTSIZE); + putreg32(cmd_size, MPFS_SPI_CMD_SIZE); + putreg32(slave_select, MPFS_SPI_SSELECT); +} + +/**************************************************************************** + * Name: mpfs_spi_lock + * + * Description: + * Lock or unlock the SPI device + * + * Input Parameters: + * priv - Private SPI device structure + * lock - true: Lock spi bus, false: unlock SPI bus + * + * Returned Value: + * The result of lock or unlock the SPI device + * + ****************************************************************************/ + +static int mpfs_spi_lock(struct spi_dev_s *dev, bool lock) +{ + struct mpfs_spi_priv_s *priv = (struct mpfs_spi_priv_s *)dev; + int ret; + + if (lock) + { + ret = nxsem_wait_uninterruptible(&priv->sem_excl); + } + else + { + ret = nxsem_post(&priv->sem_excl); + } + + return ret; +} + +/**************************************************************************** + * Name: mpfs_spi_select + * + * Description: + * Enable/disable the SPI chip select. + * + * Input Parameters: + * priv - Private SPI device structure + * devid - Identifies the device to select + * selected - true: slave selected, false: slave de-selected + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_spi_select(struct spi_dev_s *dev, uint32_t devid, + bool selected) +{ + struct mpfs_spi_priv_s *priv = (struct mpfs_spi_priv_s *)dev; + + priv->devid = devid & 0x1f; + + if (selected) + { + modifyreg32(MPFS_SPI_SSELECT, 0, 1 << (priv->devid)); + } + else + { + modifyreg32(MPFS_SPI_SSELECT, 1 << (priv->devid), 0); + } + + spiinfo("devid: %u, CS: %s\n", devid, selected ? "select" : "free"); +} + +/**************************************************************************** + * Name: mpfs_spi_setfrequency + * + * Description: + * Set the SPI frequency. + * + * Input Parameters: + * dev - Device-specific state data + * frequency - The SPI frequency requested + * + * Returned Value: + * Returns the current actual frequency selected + * + ****************************************************************************/ + +static uint32_t mpfs_spi_setfrequency(struct spi_dev_s *dev, + uint32_t frequency) +{ + struct mpfs_spi_priv_s *priv = (struct mpfs_spi_priv_s *)dev; + uint32_t divider; + + DEBUGASSERT(frequency > 0); + + if (priv->enabled) + { + modifyreg32(MPFS_SPI_CONTROL, MPFS_SPI_ENABLE, 0); + } + + priv->frequency = frequency; + divider = (MPFS_MSS_APB_AHB_CLK / frequency) >> 1; + priv->actual = (uint32_t)frequency * divider; + + DEBUGASSERT(divider >= 2u && divider <= 512u); + + putreg32(divider, MPFS_SPI_CLK_GEN); + + spiinfo("frequency=%u, actual=%u\n", priv->frequency, priv->actual); + + if (priv->enabled) + { + modifyreg32(MPFS_SPI_CONTROL, MPFS_SPI_ENABLE, MPFS_SPI_ENABLE); + } + + return priv->actual; +} + +/**************************************************************************** + * Name: mpfs_spi_setmode + * + * Description: + * Set the SPI mode. + * + * Input Parameters: + * dev - Device-specific state data + * mode - The SPI mode requested + * + * Returned Value: + * none + * + ****************************************************************************/ + +static void mpfs_spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode) +{ + struct mpfs_spi_priv_s *priv = (struct mpfs_spi_priv_s *)dev; + + spiinfo("mode=%d\n", mode); + + switch (mode) + { + case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */ + modifyreg32(MPFS_SPI_CONTROL, MPFS_SPI_SPO | MPFS_SPI_SPH, 0); + break; + + case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */ + modifyreg32(MPFS_SPI_CONTROL, MPFS_SPI_SPO, MPFS_SPI_SPH); + break; + + case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */ + modifyreg32(MPFS_SPI_CONTROL, MPFS_SPI_SPH, MPFS_SPI_SPO); + break; + + case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */ + modifyreg32(MPFS_SPI_CONTROL, 0, MPFS_SPI_SPO | MPFS_SPI_SPH); + break; + + default: + return; + } + + priv->mode = mode; + + /* SPS bit ensures the slave select remains asserted between frames, + * applicable to SPIDEV_MODEx:s. + */ + + modifyreg32(MPFS_SPI_CONTROL, 0, MPFS_SPI_SPS); +} + +/**************************************************************************** + * Name: mpfs_spi_setbits + * + * Description: + * Set the number of bits per word. + * + * Input Parameters: + * dev - Device-specific state data + * nbits - The number of bits in an SPI word. + * + * Returned Value: + * none + * + ****************************************************************************/ + +static void mpfs_spi_setbits(struct spi_dev_s *dev, int nbits) +{ + struct mpfs_spi_priv_s *priv = (struct mpfs_spi_priv_s *)dev; + + spiinfo("nbits=%d\n", nbits); + + /* SPI must be disabled for FS change to apply */ + + if (priv->enabled) + { + modifyreg32(MPFS_SPI_CONTROL, MPFS_SPI_ENABLE, 0); + } + + modifyreg32(MPFS_SPI_FSIZE, MPFS_SPI_FRAMESIZE, nbits); + + if (priv->enabled) + { + modifyreg32(MPFS_SPI_CONTROL, MPFS_SPI_ENABLE, MPFS_SPI_ENABLE); + } + + priv->nbits = nbits; +} + +/**************************************************************************** + * Name: mpfs_spi_status + * + * Description: + * Get SPI/MMC status. Optional. + * + * Input Parameters: + * dev - Device-specific state data + * devid - Identifies the device to report status on + * + * Returned Value: + * Returns a bitset of status values (see SPI_STATUS_* defines) + * + ****************************************************************************/ + +static uint8_t mpfs_spi_status(struct spi_dev_s *dev, uint32_t devid) +{ + uint8_t status = 0; + + return status; +} + +/**************************************************************************** + * Name: mpfs_spi_cmddata + * + * Description: + * Some devices require an additional out-of-band bit to specify if the + * next word sent to the device is a command or data. This feature is not + * implemented. + * + * Input Parameters: + * dev - Device-specific state data + * cmd - TRUE: The following word is a command; FALSE: the following words + * are data. + * + * Returned Value: + * OK unless an error occurs. Then a negated errno value is returned + * + ****************************************************************************/ + +#ifdef CONFIG_SPI_CMDDATA +static int mpfs_spi_cmddata(struct spi_dev_s *dev, + uint32_t devid, bool cmd) +{ + spierr("SPI cmddata not supported\n"); + DEBUGPANIC(); + + return -1; +} +#endif + +/**************************************************************************** + * Name: mpfs_spi_hwfeatures + * + * Description: + * Set hardware-specific feature flags. + * + * Input Parameters: + * dev - Device-specific state data + * features - H/W feature flags + * + * Returned Value: + * Zero (OK) if the selected H/W features are enabled; A negated errno + * value if any H/W feature is not supportable. + * + ****************************************************************************/ + +#ifdef CONFIG_SPI_HWFEATURES +static int mpfs_spi_hwfeatures(struct spi_dev_s *dev, + spi_hwfeatures_t features) +{ + /* Other H/W features are not supported */ + + spierr("SPI hardware specific feature not supported\n"); + DEBUGPANIC(); + + return -1; +} +#endif + +/**************************************************************************** + * Name: mpfs_spi_sem_waitdone + * + * Description: + * Wait for a transfer to complete. + * + * Parameters: + * priv - Pointer to the internal driver state structure. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mpfs_spi_sem_waitdone(struct mpfs_spi_priv_s *priv) +{ + struct timespec abstime; + int ret; + + clock_gettime(CLOCK_REALTIME, &abstime); + + abstime.tv_sec += 10; + abstime.tv_nsec += 0; + + ret = nxsem_timedwait_uninterruptible(&priv->sem_isr, &abstime); + + return ret; +} + +/**************************************************************************** + * Name: mpfs_spi_load_tx_fifo + * + * Description: + * Fill up the TX fifo with nwords (in 8 or 16-bit). + * + * Input Parameters: + * priv - SPI private state data + * txbuffer - A pointer to the buffer of data to be sent + * nwords - the length of data that to be exchanged in units of words. + * The wordsize is determined by the number of bits-per-word + * selected for the SPI interface. If nbits <= 8, the data is + * packed into uint8_t's; if nbits >8, the data is packed into + * uint16_t's + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_spi_load_tx_fifo(struct mpfs_spi_priv_s *priv, + const void *txbuffer, + uint32_t nwords) +{ + uint32_t tx_fifo_full; + uint16_t *data16; + uint8_t *data8; + int i; + + DEBUGASSERT(nwords > 0); + + data16 = (uint16_t *)txbuffer; + data8 = (uint8_t *)txbuffer; + + for (i = 0; i < nwords; i++) + { + if (txbuffer) + { + if (priv->nbits == 8) + { + putreg32((uint32_t)data8[priv->tx_pos], MPFS_SPI_TX_DATA); + } + else + { + putreg32((uint32_t)data16[priv->tx_pos], MPFS_SPI_TX_DATA); + } + } + else + { + putreg32(0, MPFS_SPI_TX_DATA); + } + + priv->tx_pos++; + tx_fifo_full = getreg32(MPFS_SPI_STATUS) & MPFS_SPI_TXFIFOFUL; + + DEBUGASSERT(tx_fifo_full == 0); + } +} + +/**************************************************************************** + * Name: mpfs_spi_unload_rx_fifo + * + * Description: + * Unload the RX fifo with nwords -number of elements. That is, read the + * specified number of elements from the RX fifo. + * + * Input Parameters: + * priv - SPI private state data + * txbuffer - A pointer to the buffer of data for receiving data + * nwords - the length of data that to be exchanged in units of words. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_spi_unload_rx_fifo(struct mpfs_spi_priv_s *priv, + void *rxbuffer, + uint32_t nwords) +{ + uint32_t rx_fifo_empty; + uint16_t *data16; + uint8_t *data8; + int i; + + DEBUGASSERT(nwords > 0); + + data16 = (uint16_t *)rxbuffer; + data8 = (uint8_t *)rxbuffer; + + for (i = 0; i < nwords; i++) + { + rx_fifo_empty = getreg32(MPFS_SPI_STATUS) & MPFS_SPI_RXFIFOEMP; + DEBUGASSERT(rx_fifo_empty == 0); + + if (rxbuffer) + { + if (priv->nbits == 8) + { + data8[priv->rx_pos] = getreg32(MPFS_SPI_RX_DATA); + } + else + { + data16[priv->rx_pos] = getreg32(MPFS_SPI_RX_DATA); + } + } + else + { + getreg32(MPFS_SPI_RX_DATA); + } + + priv->rx_pos++; + + DEBUGASSERT(priv->rx_pos <= priv->rxwords); + } +} + +/**************************************************************************** + * Name: mpfs_spi_irq_exchange + * + * Description: + * Exchange a block of data from SPI by DMA. + * + * Input Parameters: + * priv - SPI private state data + * txbuffer - A pointer to the buffer of data to be sent + * rxbuffer - A pointer to the buffer in which to receive data + * nwords - the length of data that to be exchanged in units of words. + * The wordsize is determined by the number of bits-per-word + * selected for the SPI interface. If nbits <= 8, the data is + * packed into uint8_t's; if nbits >8, the data is packed into + * uint16_t's + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_spi_irq_exchange(struct mpfs_spi_priv_s *priv, + const void *txbuffer, + void *rxbuffer, uint32_t nwords) +{ + uint32_t status; + + DEBUGASSERT(nwords >= 1u && nwords < 0xffffu); + + /* SPI fifo clear */ + + modifyreg32(MPFS_SPI_COMMAND, 0, MPFS_SPI_TXFIFORST | MPFS_SPI_RXFIFORST); + + /* Recover from RX overflow condition if present */ + + status = getreg32(MPFS_SPI_STATUS); + if (status & MPFS_SPI_RXOVERFLOW) + { + mpfs_spi_rxoverflow_recover(priv); + } + + DEBUGASSERT(priv->nbits == 8 || priv->nbits == 16); + + priv->fifosize = (256 / priv->nbits); + priv->fifolevel = priv->fifosize / 2; + + priv->txwords = nwords; + priv->txbuf = txbuffer; + priv->tx_pos = 0; + + priv->rxwords = nwords; + priv->rxbuf = rxbuffer; + priv->rx_pos = 0; + priv->error = 0; + + putreg32(0, MPFS_SPI_FRAMESUP); + + if (nwords > priv->fifosize) + { + modifyreg32(MPFS_SPI_CONTROL, MPFS_SPI_FRAMECNT, + MPFS_SPI_FRAMECNT & (priv->fifolevel << 8)); + mpfs_spi_load_tx_fifo(priv, txbuffer, priv->fifolevel); + } + else + { + modifyreg32(MPFS_SPI_CONTROL, MPFS_SPI_FRAMECNT, + MPFS_SPI_FRAMECNT & (nwords << 8)); + mpfs_spi_load_tx_fifo(priv, txbuffer, nwords); + } + + /* Enable TX, RX, underrun and overflow interrupts */ + + putreg32(MPFS_SPI_INT_CLEAR, MPFS_SPI_TXCHUNDRUN | + MPFS_SPI_RXCHOVRFLW | + MPFS_SPI_RXRDONECLR | + MPFS_SPI_TXDONECLR); + + modifyreg32(MPFS_SPI_CONTROL, 0, MPFS_SPI_INTTXTURUN | + MPFS_SPI_INTRXOVRFLOW | + MPFS_SPI_INTRXDATA | + MPFS_SPI_INTTXDATA); + + if (mpfs_spi_sem_waitdone(priv) < 0) + { + spiinfo("Message timed out\n"); + priv->error = -ETIMEDOUT; + } + + modifyreg32(MPFS_SPI_CONTROL, MPFS_SPI_INTTXTURUN | + MPFS_SPI_INTRXOVRFLOW | + MPFS_SPI_INTRXDATA | + MPFS_SPI_INTTXDATA, + 0); + + putreg32(MPFS_SPI_INT_CLEAR, MPFS_SPI_TXCHUNDRUN | + MPFS_SPI_RXCHOVRFLW | + MPFS_SPI_RXRDONECLR | + MPFS_SPI_TXDONECLR); +} + +/**************************************************************************** + * Name: mpfs_spi_poll_send + * + * Description: + * Exchange one word on SPI by polling mode. + * + * Input Parameters: + * priv - SPI private state data + * 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 value + * + ****************************************************************************/ + +static uint32_t mpfs_spi_poll_send(struct mpfs_spi_priv_s *priv, + uint32_t wd) +{ + uint32_t max_tx_cnt = 10000; + uint32_t max_rx_cnt = 10000; + uint32_t tx_done = 0; + uint32_t rx_ready = 0; + uint32_t rx_fifo_empty; + + uint32_t val = 0; + + /* Write data to tx fifo */ + + putreg32(wd, MPFS_SPI_TX_DATA); + + /* Wait until TX data is sent */ + + while (!tx_done && max_tx_cnt) + { + tx_done = getreg32(MPFS_SPI_STATUS) & MPFS_SPI_TXDATSENT; + max_tx_cnt--; + } + + /* Wait for rx data */ + + while (!rx_ready && max_rx_cnt) + { + rx_ready = getreg32(MPFS_SPI_STATUS) & MPFS_SPI_RXDATRCED; + max_rx_cnt--; + } + + val = getreg32(MPFS_SPI_RX_DATA); + + /* Flush the RX fifo just in case */ + + rx_fifo_empty = getreg32(MPFS_SPI_STATUS) & MPFS_SPI_RXFIFOEMP; + while (!rx_fifo_empty) + { + getreg32(MPFS_SPI_RX_DATA); + rx_fifo_empty = getreg32(MPFS_SPI_STATUS) & MPFS_SPI_RXFIFOEMP; + } + + spiinfo("send=%x and recv=%x\n", wd, val); + + return val; +} + +/**************************************************************************** + * Name: mpfs_spi_send + * + * Description: + * Exchange one word on SPI. + * + * Input Parameters: + * dev - Device-specific state data + * 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 value + * + ****************************************************************************/ + +static uint32_t mpfs_spi_send(struct spi_dev_s *dev, uint32_t wd) +{ + struct mpfs_spi_priv_s *priv = (struct mpfs_spi_priv_s *)dev; + uint32_t rd; + + rd = mpfs_spi_poll_send(priv, wd); + + return rd; +} + +/**************************************************************************** + * Name: mpfs_spi_poll_exchange + * + * Description: + * Exchange a block of data from SPI. + * + * Input Parameters: + * priv - SPI private state data + * txbuffer - A pointer to the buffer of data to be sent + * rxbuffer - A pointer to the buffer in which to receive data + * nwords - the length of data that to be exchanged in units of words. + * The wordsize is determined by the number of bits-per-word + * selected for the SPI interface. If nbits <= 8, the data is + * packed into uint8_t's; if nbits >8, the data is packed into + * uint16_t's + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_spi_poll_exchange(struct mpfs_spi_priv_s *priv, + const void *txbuffer, + void *rxbuffer, size_t nwords) +{ + uint32_t w_wd = 0xffff; + uint32_t r_wd; + uint32_t status; + int i; + + /* SPI fifo clear */ + + modifyreg32(MPFS_SPI_COMMAND, 0, MPFS_SPI_TXFIFORST | MPFS_SPI_RXFIFORST); + + /* Recover from RX overflow condition if present */ + + status = getreg32(MPFS_SPI_STATUS); + if (status & MPFS_SPI_RXOVERFLOW) + { + mpfs_spi_rxoverflow_recover(priv); + } + + putreg32(0, MPFS_SPI_FRAMESUP); + modifyreg32(MPFS_SPI_CONTROL, MPFS_SPI_FRAMECNT, + MPFS_SPI_FRAMECNT & (nwords << 8)); + + for (i = 0; i < nwords; i++) + { + if (txbuffer) + { + if (priv->nbits == 8) + { + w_wd = ((uint8_t *)txbuffer)[i]; + } + else + { + w_wd = ((uint16_t *)txbuffer)[i]; + } + } + + r_wd = mpfs_spi_poll_send(priv, w_wd); + + if (rxbuffer) + { + if (priv->nbits == 8) + { + ((uint8_t *)rxbuffer)[i] = r_wd; + } + else + { + ((uint16_t *)rxbuffer)[i] = r_wd; + } + } + } +} + +/**************************************************************************** + * Name: mpfs_spi_exchange + * + * Description: + * Exchange a block of data from SPI. + * + * Input Parameters: + * dev - Device-specific state data + * txbuffer - A pointer to the buffer of data to be sent + * rxbuffer - A pointer to the buffer in which to receive data + * nwords - the length of data that to be exchanged in units of words. + * The wordsize is determined by the number of bits-per-word + * selected for the SPI interface. If nbits <= 8, the data is + * packed into uint8_t's; if nbits >8, the data is packed into + * uint16_t's + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_spi_exchange(struct spi_dev_s *dev, + const void *txbuffer, void *rxbuffer, + size_t nwords) +{ + struct mpfs_spi_priv_s *priv = (struct mpfs_spi_priv_s *)dev; + + if (priv->config->use_irq) + { + mpfs_spi_irq_exchange(priv, txbuffer, rxbuffer, nwords); + } + else + { + mpfs_spi_poll_exchange(priv, txbuffer, rxbuffer, nwords); + } +} + +#ifndef CONFIG_SPI_EXCHANGE + +/**************************************************************************** + * Name: mpfs_spi_sndblock + * + * Description: + * Send a block of data on SPI. + * + * Input Parameters: + * dev - Device-specific state data + * buffer - A pointer to the buffer of data to be sent + * nwords - the length of data to send from the buffer in number of words. + * The wordsize is determined by the number of bits-per-word + * selected for the SPI interface. If nbits <= 8, the data is + * packed into uint8_t's; if nbits >8, the data is packed into + * uint16_t's + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_spi_sndblock(struct spi_dev_s *dev, + const void *txbuffer, size_t nwords) +{ + spiinfo("txbuffer=%p nwords=%lu\n", txbuffer, nwords); + + mpfs_spi_exchange(dev, txbuffer, NULL, nwords); +} + +/**************************************************************************** + * Name: mpfs_spi_recvblock + * + * Description: + * Receive a block of data from SPI. + * + * Input Parameters: + * dev - Device-specific state data + * 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. The wordsize is determined by the number of bits- + * per-word selected for the SPI interface. If nbits <= 8, the + * data is packed into uint8_t's; if nbits >8, the data is packed + * into uint16_t's + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_spi_recvblock(struct spi_dev_s *dev, + void *rxbuffer, size_t nwords) +{ + spiinfo("rxbuffer=%p nwords=%lu\n", rxbuffer, nwords); + + mpfs_spi_exchange(dev, NULL, rxbuffer, nwords); +} +#endif + +/**************************************************************************** + * Name: mpfs_spi_trigger + * + * Description: + * Trigger a previously configured DMA transfer. + * + * Input Parameters: + * dev - Device-specific state data + * + * Returned Value: + * OK - Trigger was fired + * -ENOSYS - Trigger not fired due to lack of DMA or low level support + * -EIO - Trigger not fired because not previously primed + * + ****************************************************************************/ + +#ifdef CONFIG_SPI_TRIGGER +static int mpfs_spi_trigger(struct spi_dev_s *dev) +{ + spierr("SPI trigger not supported\n"); + DEBUGPANIC(); + + return -ENOSYS; +} +#endif + +/**************************************************************************** + * Name: mpfs_spi_set_master_mode + * + * Description: + * set spi mode + * + * Input Parameters: + * master - master or slave + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_spi_set_master_mode(struct mpfs_spi_priv_s *priv, + uint8_t master) +{ + if (priv->enabled) + { + modifyreg32(MPFS_SPI_CONTROL, MPFS_SPI_ENABLE, 0); + } + + if (master) + { + modifyreg32(MPFS_SPI_CONTROL, 0, MPFS_SPI_MODE); + } + else + { + modifyreg32(MPFS_SPI_CONTROL, MPFS_SPI_MODE, 0); + } + + modifyreg32(MPFS_SPI_CONTROL, 0, MPFS_SPI_BIGFIFO | MPFS_SPI_CLKMODE); + + if (priv->enabled) + { + modifyreg32(MPFS_SPI_CONTROL, MPFS_SPI_ENABLE, MPFS_SPI_ENABLE); + } +} + +/**************************************************************************** + * Name: mpfs_spi_irq + * + * Description: + * This is the common SPI interrupt handler. It will be invoked when an + * interrupt is received on the device. + * + * Parameters: + * cpuint - CPU interrupt index + * context - Context data from the ISR + * arg - Opaque pointer to the internal driver state structure. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mpfs_spi_irq(int cpuint, void *context, void *arg) +{ + struct mpfs_spi_priv_s *priv = (struct mpfs_spi_priv_s *)arg; + uint32_t remaining; + uint32_t status; + + status = getreg32(MPFS_SPI_INTMASK); + + spiinfo("irq status=%x\n", status); + + if (status & MPFS_SPI_RXRDYMSKINT) + { + remaining = priv->rxwords - priv->rx_pos; + + if (remaining <= priv->fifosize) + { + mpfs_spi_unload_rx_fifo(priv, priv->rxbuf, remaining); + } + else + { + mpfs_spi_unload_rx_fifo(priv, priv->rxbuf, priv->fifolevel); + } + + putreg32(MPFS_SPI_RXRDONECLR, MPFS_SPI_INT_CLEAR); + } + + if (status & MPFS_SPI_TXDONEMSKINT) + { + remaining = priv->txwords - priv->tx_pos; + + if (remaining == 0) + { + /* Done sending - finish up */ + + nxsem_post(&priv->sem_isr); + } + else + { + if (remaining <= priv->fifosize) + { + modifyreg32(MPFS_SPI_CONTROL, MPFS_SPI_FRAMECNT, + MPFS_SPI_FRAMECNT & (remaining << 8)); + mpfs_spi_load_tx_fifo(priv, priv->txbuf, remaining); + } + else + { + modifyreg32(MPFS_SPI_CONTROL, MPFS_SPI_FRAMECNT, + MPFS_SPI_FRAMECNT & (priv->fifolevel << 8)); + mpfs_spi_load_tx_fifo(priv, priv->txbuf, priv->fifolevel); + } + } + + putreg32(MPFS_SPI_TXDONECLR, MPFS_SPI_INT_CLEAR); + } + + if (status & MPFS_SPI_RXCHOVRFMSKINT) + { + /* Handle receive overflow */ + + modifyreg32(MPFS_SPI_COMMAND, 0, MPFS_SPI_RXFIFORST); + mpfs_spi_rxoverflow_recover(priv); + + putreg32(MPFS_SPI_RXCHOVRFLW, MPFS_SPI_INT_CLEAR); + } + + if (status & MPFS_SPI_TXCHUNDDMSKINT) + { + /* Handle transmit underrun */ + + modifyreg32(MPFS_SPI_COMMAND, 0, MPFS_SPI_TXFIFORST); + putreg32(MPFS_SPI_TXCHUNDRUN, MPFS_SPI_INT_CLEAR); + } + + if (status & MPFS_SPI_CMDMSKINT) + { + /* Disable command interrupt to avoid retriggering */ + + modifyreg32(MPFS_SPI_CONTROL2, MPFS_SPI_INTEN_CMD, 0); + putreg32(MPFS_SPI_CMDINT, MPFS_SPI_INT_CLEAR); + } + + if (status & MPFS_SPI_SSENDMSKINT) + { + modifyreg32(MPFS_SPI_COMMAND, 0, MPFS_SPI_TXFIFORST | + MPFS_SPI_RXFIFORST); + putreg32(MPFS_SPI_SSEND, MPFS_SPI_INT_CLEAR); + } + + return OK; +} + +/**************************************************************************** + * Name: mpfs_spi_enable + * + * Description: + * Enable or disable the SPI bus + * + * Input Parameters: + * enable - enable or disable + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_spi_enable(struct mpfs_spi_priv_s *priv, uint8_t enable) +{ + if (enable) + { + modifyreg32(MPFS_SPI_CONTROL, MPFS_SPI_ENABLE, MPFS_SPI_ENABLE); + priv->enabled = 1; + } + else + { + modifyreg32(MPFS_SPI_CONTROL, MPFS_SPI_ENABLE, 0); + priv->enabled = 0; + } +} + +/**************************************************************************** + * Name: mpfs_spi_init + * + * Description: + * Initialize mpfs SPI hardware interface + * + * Input Parameters: + * dev - Device-specific state data + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_spi_init(struct spi_dev_s *dev) +{ + struct mpfs_spi_priv_s *priv = (struct mpfs_spi_priv_s *)dev; + const struct mpfs_spi_config_s *config = priv->config; + + /* Initialize the SPI semaphore for mutually exclusive access */ + + nxsem_init(&priv->sem_excl, 0, 1); + + nxsem_init(&priv->sem_isr, 0, 0); + nxsem_set_protocol(&priv->sem_isr, SEM_PRIO_NONE); + + up_disable_irq(priv->plic_irq); + + /* Perform reset and enable clocks */ + + if (priv->id == 0) + { + modifyreg32(MPFS_SYSREG_SOFT_RESET_CR, 0, + (1 << SYSREG_SOFT_RESET_CR_SPI0)); + + modifyreg32(MPFS_SYSREG_SOFT_RESET_CR, + (1 << SYSREG_SOFT_RESET_CR_SPI0), 0); + + modifyreg32(MPFS_SYSREG_SUBBLK_CLOCK_CR, 0, + (1 << SYSREG_SUBBLK_CLOCK_CR_SPI0)); + } + else + { + modifyreg32(MPFS_SYSREG_SOFT_RESET_CR, 0, + (1 << SYSREG_SOFT_RESET_CR_SPI1)); + + modifyreg32(MPFS_SYSREG_SOFT_RESET_CR, + (1 << SYSREG_SOFT_RESET_CR_SPI1), 0); + + modifyreg32(MPFS_SYSREG_SUBBLK_CLOCK_CR, 0, + (1 << SYSREG_SUBBLK_CLOCK_CR_SPI1)); + } + + /* Reset the device */ + + modifyreg32(MPFS_SPI_CONTROL, 0, MPFS_SPI_RESET); + modifyreg32(MPFS_SPI_CONTROL, MPFS_SPI_RESET, 0); + + /* Install some default values */ + + mpfs_spi_setfrequency(dev, config->clk_freq); + mpfs_spi_setbits(dev, 8); + mpfs_spi_setmode(dev, config->mode); + + /* Set master mode */ + + mpfs_spi_set_master_mode(priv, 1); + mpfs_spi_enable(priv, 1); + + up_enable_irq(priv->plic_irq); +} + +/**************************************************************************** + * Name: mpfs_spi_deinit + * + * Description: + * Deinitialize mpfs SPI hardware interface + * + * Input Parameters: + * dev - Device-specific state data + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_spi_deinit(struct spi_dev_s *dev) +{ + struct mpfs_spi_priv_s *priv = (struct mpfs_spi_priv_s *)dev; + + up_disable_irq(priv->plic_irq); + mpfs_spi_enable(priv, 0); + irq_detach(priv->plic_irq); + + /* Disable clock */ + + if (priv->id == 0) + { + modifyreg32(MPFS_SYSREG_SUBBLK_CLOCK_CR, + (1 << SYSREG_SUBBLK_CLOCK_CR_SPI0), + 0); + } + else + { + modifyreg32(MPFS_SYSREG_SUBBLK_CLOCK_CR, + (1 << SYSREG_SUBBLK_CLOCK_CR_SPI1), + 0); + } + + priv->frequency = 0; + priv->actual = 0; + priv->mode = SPIDEV_MODE0; + priv->nbits = 0; +} + +/**************************************************************************** + * Name: mpfs_spibus_initialize + * + * Description: + * Initialize the selected SPI bus + * + * Input Parameters: + * Port number (for hardware that has multiple SPI interfaces) + * + * Returned Value: + * Valid SPI device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +struct spi_dev_s *mpfs_spibus_initialize(int port) +{ + struct spi_dev_s *spi_dev; + struct mpfs_spi_priv_s *priv; + irqstate_t flags; + int ret; + + switch (port) + { +#ifdef CONFIG_MPFS_SPI0 + case 0: + priv = &g_mpfs_spi0_priv; + break; +#endif +#ifdef CONFIG_MPFS_SPI1 + case 1: + priv = &g_mpfs_spi1_priv; + break; +#endif + default: + return NULL; + } + + spi_dev = (struct spi_dev_s *)priv; + + flags = enter_critical_section(); + + if (priv->refs != 0) + { + leave_critical_section(flags); + + return spi_dev; + } + + ret = irq_attach(priv->plic_irq, mpfs_spi_irq, priv); + if (ret != OK) + { + leave_critical_section(flags); + return NULL; + } + + mpfs_spi_init(spi_dev); + + priv->refs++; + + leave_critical_section(flags); + + return spi_dev; +} + +/**************************************************************************** + * Name: mpfs_spibus_uninitialize + * + * Description: + * Uninitialize an SPI bus + * + ****************************************************************************/ + +int mpfs_spibus_uninitialize(struct spi_dev_s *dev) +{ + struct mpfs_spi_priv_s *priv = (struct mpfs_spi_priv_s *)dev; + irqstate_t flags; + + DEBUGASSERT(dev); + + if (priv->refs == 0) + { + return ERROR; + } + + flags = enter_critical_section(); + + if (--priv->refs) + { + leave_critical_section(flags); + return OK; + } + + leave_critical_section(flags); + + mpfs_spi_deinit(dev); + + nxsem_destroy(&priv->sem_excl); + nxsem_destroy(&priv->sem_isr); + + return OK; +} + diff --git a/arch/risc-v/src/mpfs/mpfs_spi.h b/arch/risc-v/src/mpfs/mpfs_spi.h new file mode 100644 index 0000000000..46ad70d7c8 --- /dev/null +++ b/arch/risc-v/src/mpfs/mpfs_spi.h @@ -0,0 +1,84 @@ +/**************************************************************************** + * arch/risc-v/src/mpfs/mpfs_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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#ifndef __ARCH_RISCV_SRC_MPFS_MPFS_SPI_H +#define __ARCH_RISCV_SRC_MPFS_MPFS_SPI_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +#include +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: mpfs_spibus_initialize + * + * Description: + * Initialize the selected SPI bus + * + * Input Parameters: + * Port number (for hardware that has multiple SPI interfaces) + * + * Returned Value: + * Valid SPI device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +struct spi_dev_s *mpfs_spibus_initialize(int port); + +/**************************************************************************** + * Name: mpfs_spibus_uninitialize + * + * Description: + * Uninitialize an SPI bus + * + ****************************************************************************/ + +int mpfs_spibus_uninitialize(FAR struct spi_dev_s *dev); + +#ifdef __cplusplus +} +#endif +#undef EXTERN + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_RISCV_SRC_MPFS_MPFS_SPI_H */ diff --git a/boards/risc-v/mpfs/icicle/src/Makefile b/boards/risc-v/mpfs/icicle/src/Makefile index 3fcb0f558f..462e28cb6f 100755 --- a/boards/risc-v/mpfs/icicle/src/Makefile +++ b/boards/risc-v/mpfs/icicle/src/Makefile @@ -34,4 +34,8 @@ ifeq ($(CONFIG_ARCH_FPU),y) CSRCS += mpfs_ostest.c endif +ifeq ($(CONFIG_SPI),y) +CSRCS += mpfs_board_spi.c +endif + include $(TOPDIR)/boards/Board.mk diff --git a/boards/risc-v/mpfs/icicle/src/mpfs_board_spi.c b/boards/risc-v/mpfs/icicle/src/mpfs_board_spi.c new file mode 100644 index 0000000000..85f955214e --- /dev/null +++ b/boards/risc-v/mpfs/icicle/src/mpfs_board_spi.c @@ -0,0 +1,99 @@ +/**************************************************************************** + * boards/risc-v/mpfs/icicle/src/mpfs_board_spi.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 "mpfs_spi.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: board_spi_initialize + * + * Description: + * Initialize and register SPI driver for the defined SPI ports. + * + ****************************************************************************/ + +int mpfs_board_spi_init(void) +{ + int ret = OK; +#if defined(CONFIG_MPFS_SPI0) || defined(CONFIG_MPFS_SPI1) + struct spi_dev_s *spi; +#ifdef CONFIG_SPI_DRIVER + int port = 0; +#endif /* CONFIG_SPI_DRIVER */ +#endif + + /* Initialize SPI device */ + +#ifdef CONFIG_MPFS_SPI0 + spi = mpfs_spibus_initialize(0); + if (spi == NULL) + { + spierr("Failed to initialize SPI0\n"); + return -ENODEV; + } + +#ifdef CONFIG_SPI_DRIVER + ret = spi_register(spi, port++); + if (ret < 0) + { + syslog(LOG_ERR, "Failed to register /dev/spi0: %d\n", ret); + + mpfs_spibus_uninitialize(spi); + } +#endif /* CONFIG_SPI_DRIVER */ +#endif /* CONFIG_MPFS_SPI0 */ + +#ifdef CONFIG_MPFS_SPI1 + spi = mpfs_spibus_initialize(1); + if (spi == NULL) + { + spierr("Failed to initialize SPI1\n"); + return -ENODEV; + } + +#ifdef CONFIG_SPI_DRIVER + ret = spi_register(spi, port); + if (ret < 0) + { + spierr("Failed to register /dev/spi%d: %d\n", port, ret); + + mpfs_spibus_uninitialize(spi); + } + +#endif /* CONFIG_SPI_DRIVER */ +#endif /* CONFIG_MPFS_SPI1 */ + return ret; +} diff --git a/boards/risc-v/mpfs/icicle/src/mpfs_bringup.c b/boards/risc-v/mpfs/icicle/src/mpfs_bringup.c index 3907e10624..0563248ef6 100755 --- a/boards/risc-v/mpfs/icicle/src/mpfs_bringup.c +++ b/boards/risc-v/mpfs/icicle/src/mpfs_bringup.c @@ -33,6 +33,7 @@ #include #include +#include "mpfsicicle.h" #include "mpfs.h" /**************************************************************************** @@ -57,5 +58,16 @@ int mpfs_bringup(void) } #endif +#if defined(CONFIG_MPFS_SPI0) || defined(CONFIG_MPFS_SPI1) + /* Configure SPI peripheral interfaces */ + + ret = mpfs_board_spi_init(); + + if (ret < 0) + { + syslog(LOG_ERR, "Failed to initialize SPI driver: %d\n", ret); + } +#endif + return ret; } diff --git a/boards/risc-v/mpfs/icicle/src/mpfsicicle.h b/boards/risc-v/mpfs/icicle/src/mpfsicicle.h index 4bfbc5b815..6666cb89b1 100755 --- a/boards/risc-v/mpfs/icicle/src/mpfsicicle.h +++ b/boards/risc-v/mpfs/icicle/src/mpfsicicle.h @@ -42,5 +42,6 @@ #define ICICLE_GPIO_BUTTON3_ALT (GPIO_BANK2 | GPIO_PIN27 | GPIO_OUTPUT) int mpfs_bringup(void); +int mpfs_board_spi_init(void); #endif /* __BOARDS_RISCV_ICICLE_MPFS_SRC_MPFSICICLE_H */