| /**************************************************************************** |
| * 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 <nuttx/config.h> |
| |
| #include <assert.h> |
| #include <debug.h> |
| #include <errno.h> |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <time.h> |
| |
| #include <nuttx/arch.h> |
| #include <nuttx/irq.h> |
| #include <nuttx/mutex.h> |
| #include <nuttx/spi/spi.h> |
| |
| #include <arch/board/board.h> |
| |
| #include "mpfs_gpio.h" |
| #include "mpfs_spi.h" |
| #include "hardware/mpfs_spi.h" |
| #include "hardware/mpfs_sysreg.h" |
| #include "riscv_internal.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 */ |
| |
| mutex_t lock; /* Bus usage mutex */ |
| 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); |
| |
| #ifdef CONFIG_SPI_CS_CONTROL |
| static void mpfs_spi_select(struct spi_dev_s *dev, uint32_t devid, |
| bool selected); |
| #endif |
| |
| 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, |
| }; |
| |
| #ifdef CONFIG_MPFS_SPI0 |
| static const struct spi_ops_s mpfs_spi0_ops = |
| { |
| .lock = mpfs_spi_lock, |
| .select = mpfs_spi0_select, |
| .setfrequency = mpfs_spi_setfrequency, |
| #ifdef CONFIG_SPI_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, |
| }; |
| |
| static struct mpfs_spi_priv_s g_mpfs_spi0_priv = |
| { |
| .spi_dev = |
| { |
| .ops = &mpfs_spi0_ops |
| }, |
| .config = &mpfs_spi_config, |
| .hw_base = MPFS_SPI0_LO_BASE, |
| .plic_irq = MPFS_IRQ_SPI0, |
| .id = 0, |
| .devid = 0, |
| .lock = NXMUTEX_INITIALIZER, |
| .sem_isr = SEM_INITIALIZER(0), |
| }; |
| #endif /* CONFIG_MPFS_SPI0 */ |
| |
| #ifdef CONFIG_MPFS_SPI1 |
| static const struct spi_ops_s mpfs_spi1_ops = |
| { |
| .lock = mpfs_spi_lock, |
| .select = mpfs_spi1_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, |
| }; |
| static struct mpfs_spi_priv_s g_mpfs_spi1_priv = |
| { |
| .spi_dev = |
| { |
| .ops = &mpfs_spi1_ops |
| }, |
| .config = &mpfs_spi_config, |
| .hw_base = MPFS_SPI1_LO_BASE, |
| .plic_irq = MPFS_IRQ_SPI1, |
| .id = 1, |
| .devid = 1, |
| .lock = NXMUTEX_INITIALIZER, |
| .sem_isr = SEM_INITIALIZER(0), |
| }; |
| |
| #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_FSIZE); |
| 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_FSIZE); |
| |
| 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 = nxmutex_lock(&priv->lock); |
| } |
| else |
| { |
| ret = nxmutex_unlock(&priv->lock); |
| } |
| |
| 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 |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_SPI_CS_CONTROL |
| 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"); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: mpfs_spi0/1_select |
| * |
| * Description: |
| * The external function, mpfs_spi0/1_select. |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * devid - The SPI CS or device number |
| * selected - true: assert CS, false de-assert CS |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_MPFS_SPI0 |
| void weak_function mpfs_spi0_select(struct spi_dev_s *dev, |
| uint32_t devid, bool selected) |
| { |
| #ifdef CONFIG_SPI_CS_CONTROL |
| mpfs_spi_select(dev, devid, selected); |
| #else |
| # warning "Missing logic" |
| #endif |
| } |
| #endif /* CONFIG_MPFS_SPI0 */ |
| |
| #ifdef CONFIG_MPFS_SPI1 |
| void weak_function mpfs_spi1_select(struct spi_dev_s *dev, |
| uint32_t devid, bool selected) |
| { |
| #ifdef CONFIG_SPI_CS_CONTROL |
| mpfs_spi_select(dev, devid, selected); |
| #else |
| # warning "Missing logic" |
| #endif |
| } |
| #endif /* CONFIG_MPFS_SPI1 */ |
| |
| /**************************************************************************** |
| * 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) |
| { |
| return nxsem_tickwait_uninterruptible(&priv->sem_isr, SEC2TICK(10)); |
| } |
| |
| /**************************************************************************** |
| * 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); |
| |
| /* Clear the interrupts before writing FIFO */ |
| |
| putreg32(MPFS_SPI_TXCHUNDRUN | |
| MPFS_SPI_RXCHOVRFLW | |
| MPFS_SPI_RXRDONECLR | |
| MPFS_SPI_TXDONECLR, MPFS_SPI_INT_CLEAR); |
| |
| 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 */ |
| |
| 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_TXCHUNDRUN | |
| MPFS_SPI_RXCHOVRFLW | |
| MPFS_SPI_RXRDONECLR | |
| MPFS_SPI_TXDONECLR, MPFS_SPI_INT_CLEAR); |
| } |
| |
| /**************************************************************************** |
| * 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; |
| putreg32(MPFS_SPI_TXDONECLR, MPFS_SPI_INT_CLEAR); |
| |
| 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); |
| } |
| } |
| } |
| |
| 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; |
| |
| up_disable_irq(priv->plic_irq); |
| |
| /* Perform reset and enable clocks */ |
| |
| if (priv->id == 0) |
| { |
| modifyreg32(MPFS_SYSREG_SOFT_RESET_CR, 0, SYSREG_SOFT_RESET_CR_SPI0); |
| |
| modifyreg32(MPFS_SYSREG_SOFT_RESET_CR, SYSREG_SOFT_RESET_CR_SPI0, 0); |
| |
| modifyreg32(MPFS_SYSREG_SUBBLK_CLOCK_CR, 0, |
| SYSREG_SUBBLK_CLOCK_CR_SPI0); |
| } |
| else |
| { |
| modifyreg32(MPFS_SYSREG_SOFT_RESET_CR, 0, SYSREG_SOFT_RESET_CR_SPI1); |
| |
| modifyreg32(MPFS_SYSREG_SOFT_RESET_CR, SYSREG_SOFT_RESET_CR_SPI1, 0); |
| |
| modifyreg32(MPFS_SYSREG_SUBBLK_CLOCK_CR, 0, |
| 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, |
| SYSREG_SUBBLK_CLOCK_CR_SPI0, 0); |
| } |
| else |
| { |
| modifyreg32(MPFS_SYSREG_SUBBLK_CLOCK_CR, |
| 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; |
| 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; |
| |
| nxmutex_lock(&priv->lock); |
| if (priv->refs != 0) |
| { |
| priv->refs++; |
| nxmutex_unlock(&priv->lock); |
| |
| return spi_dev; |
| } |
| |
| ret = irq_attach(priv->plic_irq, mpfs_spi_irq, priv); |
| if (ret != OK) |
| { |
| nxmutex_unlock(&priv->lock); |
| return NULL; |
| } |
| |
| mpfs_spi_init(spi_dev); |
| priv->refs++; |
| |
| nxmutex_unlock(&priv->lock); |
| 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; |
| |
| DEBUGASSERT(dev); |
| |
| if (priv->refs == 0) |
| { |
| return ERROR; |
| } |
| |
| nxmutex_lock(&priv->lock); |
| if (--priv->refs) |
| { |
| nxmutex_unlock(&priv->lock); |
| return OK; |
| } |
| |
| mpfs_spi_deinit(dev); |
| nxmutex_unlock(&priv->lock); |
| |
| return OK; |
| } |
| |