From 5914af84c74a18190ac1654067862e4388ed813e Mon Sep 17 00:00:00 2001 From: liang Date: Thu, 4 Feb 2021 10:18:28 +0800 Subject: [PATCH] arch/risc-v/bl602: spi_master support. --- arch/risc-v/src/bl602/Kconfig | 3 + arch/risc-v/src/bl602/Make.defs | 4 + arch/risc-v/src/bl602/bl602_spi.c | 1232 +++++++++++++++++ arch/risc-v/src/bl602/bl602_spi.h | 88 ++ .../bl602/bl602evb/configs/spi/defconfig | 79 ++ boards/risc-v/bl602/bl602evb/include/board.h | 7 + .../risc-v/bl602/bl602evb/src/bl602_bringup.c | 9 + 7 files changed, 1422 insertions(+) create mode 100644 arch/risc-v/src/bl602/bl602_spi.c create mode 100644 arch/risc-v/src/bl602/bl602_spi.h create mode 100644 boards/risc-v/bl602/bl602evb/configs/spi/defconfig diff --git a/arch/risc-v/src/bl602/Kconfig b/arch/risc-v/src/bl602/Kconfig index 122e92c563..79d2b2fd82 100644 --- a/arch/risc-v/src/bl602/Kconfig +++ b/arch/risc-v/src/bl602/Kconfig @@ -42,6 +42,9 @@ config BL602_PWM0 config BL602_I2C0 bool "I2C0" +config BL602_SPI0 + bool "SPI0" + config BL602_SPIFLASH bool "SPI Flash" default n diff --git a/arch/risc-v/src/bl602/Make.defs b/arch/risc-v/src/bl602/Make.defs index a1a1d69cd8..d6a88ea509 100644 --- a/arch/risc-v/src/bl602/Make.defs +++ b/arch/risc-v/src/bl602/Make.defs @@ -56,6 +56,10 @@ ifeq ($(CONFIG_I2C),y) CHIP_CSRCS += bl602_i2c.c endif +ifeq ($(CONFIG_SPI),y) +CHIP_CSRCS += bl602_spi.c +endif + ifeq ($(CONFIG_TIMER),y) CHIP_CSRCS += bl602_tim_lowerhalf.c endif diff --git a/arch/risc-v/src/bl602/bl602_spi.c b/arch/risc-v/src/bl602/bl602_spi.c new file mode 100644 index 0000000000..6f224eed0e --- /dev/null +++ b/arch/risc-v/src/bl602/bl602_spi.c @@ -0,0 +1,1232 @@ +/**************************************************************************** + * arch/risc-v/src/bl602/bl602_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 "bl602_glb.h" +#include "bl602_gpio.h" +#include "bl602_spi.h" +#include "hardware/bl602_glb.h" +#include "hardware/bl602_spi.h" +#include "riscv_arch.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define SPI_FREQ_DEFAULT 400000 +#define BASIC_CLK 4000000 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* SPI frequency cal configuration */ + +struct prescale_and_count_cal_ctx_s +{ + uint64_t desired_ticks; + uint64_t count_max; + uint64_t error; + uint64_t count; + uint32_t prescale; + uint8_t update; +}; + +/* SPI clock configuration */ + +struct spi_clock_cfg_s +{ + uint8_t start_len; /* Length of start condition */ + uint8_t stop_len; /* Length of stop condition */ + uint8_t data_phase0_len; /* Length of data phase 0,affecting clock */ + uint8_t data_phase1_len; /* Length of data phase 1,affecting clock */ + uint8_t interval_len; /* Length of interval between frame */ +}; + +/* SPI configuration */ + +struct spi_cfg_s +{ + uint8_t deglitch_enable; /* Enable or disable de-glitch function */ + uint8_t continuous_enable; /* Enable or disable master continuous transfer + * mode,enable:SS will stay asserted if next data + * is valid + */ + uint8_t byte_sequence; /* The byte is sent first in SPI transfer ,0 is send + * 0byte first; 1 is send 3byte first + */ + uint8_t bit_sequence; /* The bit is sent first in SPI transfer ,0 is each byte + * is sent out MSB first; 1 is each byte is sent out LSB + * first + */ +}; + +/* SPI Device hardware configuration */ + +struct bl602_spi_config_s +{ + uint32_t clk_freq; /* SPI clock frequency */ + enum spi_mode_e mode; /* SPI default mode */ + + bool use_dma; /* Use DMA */ +}; + +struct bl602_spi_priv_s +{ + /* Externally visible part of the SPI interface */ + + struct spi_dev_s spi_dev; + + /* Port configuration */ + + const struct bl602_spi_config_s *config; + + int refs; /* Referernce count */ + + /* Held while chip is selected for mutual exclusion */ + + sem_t exclsem; + + /* Interrupt wait semaphore */ + + sem_t sem_isr; + + uint32_t frequency; /* Requested clock frequency */ + uint32_t actual; /* Actual clock frequency */ + + enum spi_mode_e mode; /* Actual SPI hardware mode */ + + /* Actual SPI send/receive bits once transmission */ + + uint8_t nbits; + uint8_t dma_chan; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int bl602_spi_lock(FAR struct spi_dev_s *dev, bool lock); + +static void bl602_spi_select(FAR struct spi_dev_s *dev, uint32_t devid, + bool selected); + +static uint32_t bl602_spi_setfrequency(FAR struct spi_dev_s *dev, + uint32_t frequency); +static void bl602_spi_setmode(FAR struct spi_dev_s *dev, + enum spi_mode_e mode); +static void bl602_spi_setbits(FAR struct spi_dev_s *dev, int nbits); +#ifdef CONFIG_SPI_HWFEATURES +static int bl602_spi_hwfeatures(FAR struct spi_dev_s *dev, + spi_hwfeatures_t features); +#endif +static uint8_t bl602_spi_status(FAR struct spi_dev_s *dev, + uint32_t devid); +#ifdef CONFIG_SPI_CMDDATA +static int bl602_spi_cmddata(FAR struct spi_dev_s *dev, + uint32_t devid, bool cmd); +#endif +static uint32_t bl602_spi_send(FAR struct spi_dev_s *dev, uint32_t wd); +static void bl602_spi_exchange(FAR struct spi_dev_s *dev, + FAR const void *txbuffer, + FAR void *rxbuffer, size_t nwords); +#ifndef CONFIG_SPI_EXCHANGE +static void bl602_spi_sndblock(FAR struct spi_dev_s *dev, + FAR const void *txbuffer, size_t nwords); +static void bl602_spi_recvblock(FAR struct spi_dev_s *dev, + FAR void *rxbuffer, size_t nwords); +#endif +#ifdef CONFIG_SPI_TRIGGER +static int bl602_spi_trigger(FAR struct spi_dev_s *dev); +#endif +static void bl602_spi_init(FAR struct spi_dev_s *dev); +static void bl602_spi_deinit(FAR struct spi_dev_s *dev); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef CONFIG_BL602_SPI0 +static const struct bl602_spi_config_s bl602_spi_config = +{ + .clk_freq = SPI_FREQ_DEFAULT, + .mode = SPIDEV_MODE0, + .use_dma = false, +}; + +static const struct spi_ops_s bl602_spi_ops = +{ + .lock = bl602_spi_lock, + .select = bl602_spi_select, + .setfrequency = bl602_spi_setfrequency, +#ifdef CONFIG_SPI_CS_DELAY_CONTROL + .setdelay = bl602_spi_setdelay, +#endif + .setmode = bl602_spi_setmode, + .setbits = bl602_spi_setbits, +#ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = bl602_spi_hwfeatures, +#endif + .status = bl602_spi_status, +#ifdef CONFIG_SPI_CMDDATA + .cmddata = bl602_spi_cmddata, +#endif + .send = bl602_spi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = bl602_spi_exchange, +#else + .sndblock = bl602_spi_sndblock, + .recvblock = bl602_spi_recvblock, +#endif +#ifdef CONFIG_SPI_TRIGGER + .trigger = bl602_spi_trigger, +#endif + .registercallback = NULL, +}; + +static struct bl602_spi_priv_s bl602_spi_priv = +{ + .spi_dev = + { + .ops = &bl602_spi_ops + }, + .config = &bl602_spi_config +}; + +#endif /* CONFIG_BL602_SPI0 */ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: bl602_check_with_new_prescale + * + * Description: + * Check if the option prescale + * + ****************************************************************************/ + +static void +bl602_check_with_new_prescale(struct prescale_and_count_cal_ctx_s *p_ctx, + uint32_t prescale_new) +{ + uint64_t count = p_ctx->desired_ticks / prescale_new; + uint64_t error; + + if (count > p_ctx->count_max) + { + count = p_ctx->count_max; + } + + error = p_ctx->desired_ticks - count * prescale_new; + + if (p_ctx->error > error) + { + p_ctx->error = error; + p_ctx->count = count; + p_ctx->prescale = prescale_new; + p_ctx->update = 1; + } +} + +/**************************************************************************** + * Name: bl602_prescale_and_count_cal + * + * Description: + * prescale and count cal + * + ****************************************************************************/ + +static int +bl602_prescale_and_count_cal(uint32_t counter_width, + uint32_t prescale_max, size_t ticks, + uint32_t *p_prescale, size_t *p_count) +{ + struct prescale_and_count_cal_ctx_s ctx; + + uint32_t prescale_min; + uint32_t prescale; + + size_t count_max; + + count_max = ((uint64_t)1ull << counter_width) - 1; + + if (ticks <= count_max) + { + *p_prescale = 1; + *p_count = ticks; + + return 0; + } + + prescale_min = ticks / count_max; + + ctx.count_max = count_max; + ctx.desired_ticks = ticks; + ctx.error = ticks; + ctx.count = count_max; + ctx.prescale = 1; + ctx.update = 0; + + if (prescale_max < prescale_min) + { + return -1; + } + + for (prescale = prescale_min; prescale <= prescale_max; prescale++) + { + bl602_check_with_new_prescale(&ctx, prescale); + if (ctx.error == 0) + { + break; + } + } + + if (ctx.update) + { + *p_prescale = ctx.prescale; + *p_count = ctx.count; + + return 0; + } + + return -1; +} + +/**************************************************************************** + * Name: bl602_set_spi_clk + * + * Description: + * set SPI clock + * + ****************************************************************************/ + +static void bl602_set_spi_clk(uint8_t enable, uint8_t div) +{ + modifyreg32(BL602_CLK_CFG3, CLK_CFG3_SPI_CLK_DIV_MASK, div); + + if (enable) + { + modifyreg32(BL602_CLK_CFG3, 0, CLK_CFG3_SPI_CLK_EN); + } + else + { + modifyreg32(BL602_CLK_CFG3, CLK_CFG3_SPI_CLK_EN, 0); + } +} + +/**************************************************************************** + * Name: bl602_clockconfig + * + * Description: + * SPI clock config + * + ****************************************************************************/ + +static void bl602_clockconfig(struct spi_clock_cfg_s *clockcfg) +{ + /* Configure length of data phase1/0 and start/stop condition */ + + modifyreg32(BL602_SPI_PRD_0, SPI_PRD_0_CR_S_MASK, + (clockcfg->start_len - 1)); + modifyreg32(BL602_SPI_PRD_0, SPI_PRD_0_CR_P_MASK, + (clockcfg->stop_len - 1) << SPI_PRD_0_CR_P_SHIFT); + modifyreg32(BL602_SPI_PRD_0, SPI_PRD_0_CR_D_PH_0_MASK, + (clockcfg->data_phase0_len - 1) << SPI_PRD_0_CR_D_PH_0_SHIFT); + modifyreg32(BL602_SPI_PRD_0, SPI_PRD_0_CR_D_PH_1_MASK, + (clockcfg->data_phase1_len - 1) << SPI_PRD_0_CR_D_PH_1_SHIFT); + + /* Configure length of interval between frame */ + + modifyreg32(BL602_SPI_PRD_1, SPI_PRD_1_CR_I_MASK, + clockcfg->interval_len - 1); +} + +/**************************************************************************** + * Name: bl602_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 bl602_spi_lock(FAR struct spi_dev_s *dev, bool lock) +{ + int ret; + FAR struct bl602_spi_priv_s *priv = (FAR struct bl602_spi_priv_s *)dev; + + if (lock) + { + ret = nxsem_wait_uninterruptible(&priv->exclsem); + } + else + { + ret = nxsem_post(&priv->exclsem); + } + + return ret; +} + +/**************************************************************************** + * Name: bl602_spi_select + * + * Description: + * Enable/disable the SPI chip select. The implementation of this method + * must include handshaking: If a device is selected, it must hold off + * all other attempts to select the device until the device is deselected. + * + * If disable bl602_SPI_SWCS, driver will use hardware CS so that when + * once transmission is started, hardware select the device and when this + * transmission is done, hardware deselect the device automatically. And + * the function will do nothing. + * + * 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 bl602_spi_select(FAR struct spi_dev_s *dev, uint32_t devid, + bool selected) +{ + /* we used hardware CS */ + + spiinfo("devid: %lu, CS: %s\n", devid, selected ? "select" : "free"); +} + +/**************************************************************************** + * Name: bl602_spi_setfrequency + * + * Description: + * Set the SPI frequency. + * + * Input Parameters: + * dev - Device-specific state data + * frequency - The SPI frequency requested + * + * Returned Value: + * Returns the actual frequency selected + * + ****************************************************************************/ + +static uint32_t bl602_spi_setfrequency(FAR struct spi_dev_s *dev, + uint32_t frequency) +{ + FAR struct bl602_spi_priv_s *priv = (FAR struct bl602_spi_priv_s *)dev; + struct spi_clock_cfg_s clockcfg; + size_t count; + uint32_t clk_div; + uint8_t ticks; + + if (priv->frequency == frequency) + { + /* We are already at this frequency. Return the actual. */ + + return priv->actual; + } + + ticks = BASIC_CLK / frequency; + + if (bl602_prescale_and_count_cal(8, 0xff, ticks, &clk_div, &count) != 0) + { + spiinfo("--------spi div clk error!!!!--------\r\n"); + return -1; + } + + bl602_set_spi_clk(1, clk_div - 1); + + clockcfg.start_len = count; + clockcfg.stop_len = count; + clockcfg.data_phase0_len = count; + clockcfg.data_phase1_len = count; + clockcfg.interval_len = count; + + bl602_clockconfig(&clockcfg); + + priv->frequency = frequency; + + spiinfo("frequency=%lu, actual=%lu\r\n", priv->frequency, priv->actual); + + return priv->actual; +} + +/**************************************************************************** + * Name: bl602_spi_setdelay + * + * Description: + * Set the SPI Delays in nanoseconds. Optional. + * + * Input Parameters: + * dev - Device-specific state data + * startdelay - The delay between CS active and first CLK + * stopdelay - The delay between last CLK and CS inactive + * csdelay - The delay between CS inactive and CS active again + * + * Returned Value: + * Returns zero (OK) on success; a negated errno value is return on any + * failure. + * + ****************************************************************************/ + +#ifdef CONFIG_SPI_CS_DELAY_CONTROL +static int bl602_spi_setdelay(FAR struct spi_dev_s *dev, uint32_t startdelay, + uint32_t stopdelay, uint32_t csdelay) +{ + spiinfo("not support\r\n"); + return -1; +} +#endif + +/**************************************************************************** + * Name: bl602_spi_setmode + * + * Description: + * Set the SPI mode. + * + * Input Parameters: + * dev - Device-specific state data + * mode - The SPI mode requested + * + * Returned Value: + * none + * + ****************************************************************************/ + +static void +bl602_spi_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode) +{ + FAR struct bl602_spi_priv_s *priv = (FAR struct bl602_spi_priv_s *)dev; + + spiinfo("mode=%d\n", mode); + + /* Has the mode changed? */ + + if (mode != priv->mode) + { + switch (mode) + { + case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */ + modifyreg32(BL602_SPI_CFG, SPI_CFG_CR_SCLK_POL + | SPI_CFG_CR_SCLK_PH, 0); + break; + + case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */ + modifyreg32(BL602_SPI_CFG, SPI_CFG_CR_SCLK_POL, 0); + modifyreg32(BL602_SPI_CFG, 0, SPI_CFG_CR_SCLK_PH); + break; + + case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */ + modifyreg32(BL602_SPI_CFG, SPI_CFG_CR_SCLK_PH, 0); + modifyreg32(BL602_SPI_CFG, 0, SPI_CFG_CR_SCLK_POL); + break; + + case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */ + modifyreg32(BL602_SPI_CFG, 0, SPI_CFG_CR_SCLK_POL + | SPI_CFG_CR_SCLK_PH); + break; + + default: + return; + } + + priv->mode = mode; + } +} + +/**************************************************************************** + * Name: bl602_spi_setbits + * + * Description: + * Set the number if bits per word. + * + * Input Parameters: + * dev - Device-specific state data + * nbits - The number of bits in an SPI word. + * + * Returned Value: + * none + * + ****************************************************************************/ + +static void bl602_spi_setbits(FAR struct spi_dev_s *dev, int nbits) +{ + FAR struct bl602_spi_priv_s *priv = (FAR struct bl602_spi_priv_s *)dev; + + spiinfo("nbits=%d\n", nbits); + + /* Has the number of bits changed? */ + + if (nbits != priv->nbits) + { + /* Save the selection so that subsequent re-configurations + * will be faster. + */ + + switch (nbits) + { + case 8: + + /* set valid width for each fifo entry 8 bit */ + + modifyreg32(BL602_SPI_CFG, SPI_CFG_CR_FRAME_SIZE_MASK, 0); + break; + + case 16: + modifyreg32(BL602_SPI_CFG, SPI_CFG_CR_FRAME_SIZE_MASK, + 1 << SPI_CFG_CR_FRAME_SIZE_SHIFT); + break; + + default: + return; + } + + priv->nbits = nbits; + } +} + +/**************************************************************************** + * Name: bl602_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 bl602_spi_status(FAR struct spi_dev_s *dev, uint32_t devid) +{ + uint8_t status = 0; + + return status; +} + +/**************************************************************************** + * Name: bl602_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 is typical, for + * example, in "9-bit" displays where the 9th bit is the CMD/DATA bit. + * This function provides selection of command or data. + * + * This "latches" the CMD/DATA state. It does not have to be called before + * every word is transferred; only when the CMD/DATA state changes. This + * method is required if CONFIG_SPI_CMDDATA is selected in the NuttX + * configuration + * + * 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 bl602_spi_cmddata(FAR struct spi_dev_s *dev, + uint32_t devid, bool cmd) +{ + spiinfo("not support\r\n"); + return -1; +} +#endif + +/**************************************************************************** + * Name: bl602_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 bl602_spi_hwfeatures(FAR struct spi_dev_s *dev, + spi_hwfeatures_t features) +{ + /* Other H/W features are not supported */ + + spiinfo("not support\r\n"); + return -1; +} +#endif + +/**************************************************************************** + * Name: bl602_spi_dma_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 bl602_spi_dma_exchange(FAR struct bl602_spi_priv_s *priv, + FAR const void *txbuffer, + FAR void *rxbuffer, uint32_t nwords) +{ + spiinfo("dma is not support now\r\n"); +} + +/**************************************************************************** + * Name: bl602_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 bl602_spi_poll_send(FAR struct bl602_spi_priv_s *priv, + uint32_t wd) +{ + uint32_t val; + uint32_t tmp_val = 0; + + /* write data to tx fifo */ + + putreg32(wd, BL602_SPI_FIFO_WDATA); + + while (0 == tmp_val) + { + /* get data from rx fifo */ + + tmp_val = getreg32(BL602_SPI_FIFO_CFG_1); + tmp_val = (tmp_val & SPI_FIFO_CFG_1_RX_CNT_MASK) + >> SPI_FIFO_CFG_1_RX_CNT_SHIFT; + } + + val = getreg32(BL602_SPI_FIFO_RDATA); + + spiinfo("send=%lx and recv=%lx\n", wd, val); + + return val; +} + +/**************************************************************************** + * Name: bl602_spi_dma_send + * + * Description: + * Exchange one word on SPI by SPI DMA mode. + * + * 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 bl602_spi_dma_send(FAR struct bl602_spi_priv_s *priv, + uint32_t wd) +{ + uint32_t rd = 0; + + bl602_spi_dma_exchange(priv, &wd, &rd, 1); + + return rd; +} + +/**************************************************************************** + * Name: bl602_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 bl602_spi_send(FAR struct spi_dev_s *dev, uint32_t wd) +{ + FAR struct bl602_spi_priv_s *priv = (FAR struct bl602_spi_priv_s *)dev; + uint32_t rd; + + if (priv->dma_chan) + { + rd = bl602_spi_dma_send(priv, wd); + } + else + { + rd = bl602_spi_poll_send(priv, wd); + } + + return rd; +} + +/**************************************************************************** + * Name: bl602_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 bl602_spi_poll_exchange(FAR struct bl602_spi_priv_s *priv, + FAR const void *txbuffer, + FAR void *rxbuffer, size_t nwords) +{ + int i; + uint32_t w_wd = 0xffff; + uint32_t r_wd; + + /* spi enable master */ + + modifyreg32(BL602_SPI_CFG, SPI_CFG_CR_S_EN, 0); + modifyreg32(BL602_SPI_CFG, 0, SPI_CFG_CR_M_EN); + + /* spi fifo clear */ + + modifyreg32(BL602_SPI_FIFO_CFG_0, SPI_FIFO_CFG_0_RX_CLR + | SPI_FIFO_CFG_0_TX_CLR, 0); + + 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 = bl602_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: bl602_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 bl602_spi_exchange(FAR struct spi_dev_s *dev, + FAR const void *txbuffer, FAR void *rxbuffer, + size_t nwords) +{ + FAR struct bl602_spi_priv_s *priv = (FAR struct bl602_spi_priv_s *)dev; + + if (priv->dma_chan) + { + bl602_spi_dma_exchange(priv, txbuffer, rxbuffer, nwords); + } + else + { + bl602_spi_poll_exchange(priv, txbuffer, rxbuffer, nwords); + } +} + +#ifndef CONFIG_SPI_EXCHANGE + +/**************************************************************************** + * Name: bl602_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 bl602_spi_sndblock(FAR struct spi_dev_s *dev, + FAR const void *txbuffer, size_t nwords) +{ + spiinfo("txbuffer=%p nwords=%d\n", txbuffer, nwords); + + bl602_spi_exchange(dev, txbuffer, NULL, nwords); +} + +/**************************************************************************** + * Name: bl602_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 bl602_spi_recvblock(FAR struct spi_dev_s *dev, + FAR void *rxbuffer, size_t nwords) +{ + spiinfo("rxbuffer=%p nwords=%d\n", rxbuffer, nwords); + + bl602_spi_exchange(dev, NULL, rxbuffer, nwords); +} +#endif + +/**************************************************************************** + * Name: bl602_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 bl602_spi_trigger(FAR struct spi_dev_s *dev) +{ + spiinfo("not support\r\n"); + return -ENOSYS; +} +#endif + +/**************************************************************************** + * Name: bl602_set_spi_0_act_mode_sel + * + * Description: + * set spi act mode + * + * Input Parameters: + * mod - mode + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void bl602_set_spi_0_act_mode_sel(uint8_t mod) +{ + if (mod) + { + modifyreg32(BL602_GLB_GLB_PARM, 0, GLB_PARM_REG_SPI_0_MASTER_MODE); + } + else + { + modifyreg32(BL602_GLB_GLB_PARM, GLB_PARM_REG_SPI_0_MASTER_MODE, 0); + } +} + +/**************************************************************************** + * Name: bl602_spi_init + * + * Description: + * Initialize bl602 SPI hardware interface + * + * Input Parameters: + * dev - Device-specific state data + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void bl602_spi_init(FAR struct spi_dev_s *dev) +{ + FAR struct bl602_spi_priv_s *priv = (FAR struct bl602_spi_priv_s *)dev; + const struct bl602_spi_config_s *config = priv->config; + + /* Initialize the SPI semaphore that enforces mutually exclusive access */ + + nxsem_init(&priv->exclsem, 0, 1); + + bl602_configgpio(BOARD_SPI_CS); + bl602_configgpio(BOARD_SPI_MOSI); + bl602_configgpio(BOARD_SPI_MISO); + bl602_configgpio(BOARD_SPI_CLK); + + /* set master mode */ + + bl602_set_spi_0_act_mode_sel(1); + + /* spi cfg reg: + * cr_spi_deg_en 1 + * cr_spi_m_cont_en 0 + * cr_spi_byte_inv 0 + * cr_spi_bit_inv 0 + */ + + modifyreg32(BL602_SPI_CFG, 0, SPI_CFG_CR_DEG_EN); + modifyreg32(BL602_SPI_CFG, SPI_CFG_CR_M_CONT_EN, 0); + modifyreg32(BL602_SPI_CFG, SPI_CFG_CR_BYTE_INV, 0); + modifyreg32(BL602_SPI_CFG, SPI_CFG_CR_BIT_INV, 0); + + /* disable rx ignore */ + + modifyreg32(BL602_SPI_CFG, SPI_CFG_CR_RXD_IGNR_EN, 0); + + bl602_spi_setfrequency(dev, config->clk_freq); + bl602_spi_setbits(dev, 8); + bl602_spi_setmode(dev, config->mode); + + /* spi fifo clear */ + + modifyreg32(BL602_SPI_CFG, SPI_FIFO_CFG_0_RX_CLR + | SPI_FIFO_CFG_0_TX_CLR, 0); +} + +/**************************************************************************** + * Name: bl602_spi_deinit + * + * Description: + * Deinitialize bl602 SPI hardware interface + * + * Input Parameters: + * dev - Device-specific state data + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void bl602_spi_deinit(FAR struct spi_dev_s *dev) +{ + FAR struct bl602_spi_priv_s *priv = (FAR struct bl602_spi_priv_s *)dev; + + bl602_swrst_ahb_slave1(AHB_SLAVE1_SPI); + + priv->frequency = 0; + priv->actual = 0; + priv->mode = SPIDEV_MODE0; + priv->nbits = 0; + priv->dma_chan = 0; +} + +/**************************************************************************** + * Name: bl602_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 + * + ****************************************************************************/ + +FAR struct spi_dev_s *bl602_spibus_initialize(int port) +{ + FAR struct spi_dev_s *spi_dev; + FAR struct bl602_spi_priv_s *priv; + irqstate_t flags; + + switch (port) + { +#ifdef CONFIG_BL602_SPI0 + case 0: + priv = &bl602_spi_priv; + break; +#endif + default: + return NULL; + } + + spi_dev = (FAR struct spi_dev_s *)priv; + + flags = enter_critical_section(); + + if ((volatile int)priv->refs != 0) + { + leave_critical_section(flags); + + return spi_dev; + } + + bl602_spi_init(spi_dev); + + priv->refs++; + + leave_critical_section(flags); + + return spi_dev; +} + +/**************************************************************************** + * Name: bl602_spibus_uninitialize + * + * Description: + * Uninitialize an SPI bus + * + ****************************************************************************/ + +int bl602_spibus_uninitialize(FAR struct spi_dev_s *dev) +{ + irqstate_t flags; + FAR struct bl602_spi_priv_s *priv = (FAR struct bl602_spi_priv_s *)dev; + + 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); + + bl602_spi_deinit(dev); + + nxsem_destroy(&priv->exclsem); + + return OK; +} + diff --git a/arch/risc-v/src/bl602/bl602_spi.h b/arch/risc-v/src/bl602/bl602_spi.h new file mode 100644 index 0000000000..e2c340f3be --- /dev/null +++ b/arch/risc-v/src/bl602/bl602_spi.h @@ -0,0 +1,88 @@ +/**************************************************************************** + * arch/risc-v/src/bl602/bl602_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_BL602_BL602_SPI_H +#define __ARCH_RISCV_SRC_BL602_BL602_SPI_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +#ifdef CONFIG_BL602_SPI0 + +#include +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: bl602_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 + * + ****************************************************************************/ + +FAR struct spi_dev_s *bl602_spibus_initialize(int port); + +/**************************************************************************** + * Name: bl602_spibus_uninitialize + * + * Description: + * Uninitialize an SPI bus + * + ****************************************************************************/ + +int bl602_spibus_uninitialize(FAR struct spi_dev_s *dev); + +#endif /* CONFIG_BL602_SPI0 */ + +#ifdef __cplusplus +} +#endif +#undef EXTERN + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_RISCV_SRC_BL602_BL602_SPI_H */ diff --git a/boards/risc-v/bl602/bl602evb/configs/spi/defconfig b/boards/risc-v/bl602/bl602evb/configs/spi/defconfig new file mode 100644 index 0000000000..e7b198a923 --- /dev/null +++ b/boards/risc-v/bl602/bl602evb/configs/spi/defconfig @@ -0,0 +1,79 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_NSH_DISABLEBG is not set +# CONFIG_NSH_DISABLE_LOSMART is not set +# CONFIG_NSH_DISABLE_UNAME is not set +CONFIG_ARCH="risc-v" +CONFIG_ARCH_BOARD="bl602evb" +CONFIG_ARCH_BOARD_BL602EVB=y +CONFIG_ARCH_CHIP="bl602" +CONFIG_ARCH_CHIP_BL602=y +CONFIG_ARCH_INTERRUPTSTACK=8192 +CONFIG_ARCH_RISCV=y +CONFIG_ARCH_STACKDUMP=y +CONFIG_BINFMT_DISABLE=y +CONFIG_BL602_HAVE_UART0=y +CONFIG_BL602_SPI0=y +CONFIG_BL602_TIMER0=y +CONFIG_BOARD_LOOPSPERMSEC=10000 +CONFIG_BUILTIN=y +CONFIG_DEBUG_FEATURES=y +CONFIG_DEBUG_FULLOPT=y +CONFIG_DEBUG_SYMBOLS=y +CONFIG_DEFAULT_SMALL=y +CONFIG_DEV_ZERO=y +CONFIG_DISABLE_MQUEUE=y +CONFIG_EXAMPLES_HELLO=y +CONFIG_EXAMPLES_HELLO_STACKSIZE=8192 +CONFIG_EXAMPLES_TIMER=y +CONFIG_FS_PROCFS=y +CONFIG_IDLETHREAD_STACKSIZE=8192 +CONFIG_INTELHEX_BINARY=y +CONFIG_LIBC_PERROR_STDOUT=y +CONFIG_LIBC_STRERROR=y +CONFIG_MAX_TASKS=8 +CONFIG_NFILE_DESCRIPTORS=6 +CONFIG_NSH_ARCHINIT=y +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_DISABLE_CD=y +CONFIG_NSH_DISABLE_CP=y +CONFIG_NSH_DISABLE_IFUPDOWN=y +CONFIG_NSH_DISABLE_MKDIR=y +CONFIG_NSH_DISABLE_RM=y +CONFIG_NSH_DISABLE_RMDIR=y +CONFIG_NSH_DISABLE_UMOUNT=y +CONFIG_NSH_FILEIOSIZE=64 +CONFIG_NSH_MAXARGUMENTS=16 +CONFIG_NSH_STRERROR=y +CONFIG_PREALLOC_TIMERS=0 +CONFIG_PTHREAD_STACK_DEFAULT=8192 +CONFIG_RAM_SIZE=134217728 +CONFIG_RAM_START=0xc0800000 +CONFIG_RAW_BINARY=y +CONFIG_RR_INTERVAL=200 +CONFIG_RV32IM_CUSTOM_IRQ_SUPPORT=y +CONFIG_SCHED_WAITPID=y +CONFIG_SPI=y +CONFIG_STACK_COLORATION=y +CONFIG_START_DAY=20 +CONFIG_START_MONTH=3 +CONFIG_START_YEAR=2020 +CONFIG_STDIO_DISABLE_BUFFERING=y +CONFIG_SYSTEM_NSH=y +CONFIG_SYSTEM_SPITOOL=y +CONFIG_TASK_NAME_SIZE=12 +CONFIG_TASK_SPAWN_DEFAULT_STACKSIZE=8192 +CONFIG_TESTING_GETPRIME=y +CONFIG_TIMER=y +CONFIG_TIMER_ARCH=y +CONFIG_UART0_BAUD=2000000 +CONFIG_UART0_RXBUFSIZE=128 +CONFIG_UART0_SERIAL_CONSOLE=y +CONFIG_UART0_TXBUFSIZE=128 +CONFIG_USERMAIN_STACKSIZE=8192 +CONFIG_USER_ENTRYPOINT="nsh_main" diff --git a/boards/risc-v/bl602/bl602evb/include/board.h b/boards/risc-v/bl602/bl602evb/include/board.h index 9c7fed6363..538e8c3089 100644 --- a/boards/risc-v/bl602/bl602evb/include/board.h +++ b/boards/risc-v/bl602/bl602evb/include/board.h @@ -75,6 +75,13 @@ #define BOARD_I2C_SCL (GPIO_INPUT | GPIO_PULLUP | GPIO_FUNC_I2C | GPIO_PIN4) #define BOARD_I2C_SDA (GPIO_INPUT | GPIO_PULLUP | GPIO_FUNC_I2C | GPIO_PIN3) +/* SPI Configuration */ + +#define BOARD_SPI_CS (GPIO_INPUT | GPIO_PULLUP | GPIO_FUNC_SPI | GPIO_PIN2) +#define BOARD_SPI_MOSI (GPIO_INPUT | GPIO_PULLUP | GPIO_FUNC_SPI | GPIO_PIN1) +#define BOARD_SPI_MISO (GPIO_INPUT | GPIO_PULLUP | GPIO_FUNC_SPI | GPIO_PIN0) +#define BOARD_SPI_CLK (GPIO_INPUT | GPIO_PULLUP | GPIO_FUNC_SPI | GPIO_PIN3) + /**************************************************************************** * Public Types ****************************************************************************/ diff --git a/boards/risc-v/bl602/bl602evb/src/bl602_bringup.c b/boards/risc-v/bl602/bl602evb/src/bl602_bringup.c index 5194853725..0c11c5ee11 100644 --- a/boards/risc-v/bl602/bl602evb/src/bl602_bringup.c +++ b/boards/risc-v/bl602/bl602evb/src/bl602_bringup.c @@ -40,6 +40,7 @@ #include #include #include +#include #if defined(CONFIG_BL602_SPIFLASH) #include @@ -78,6 +79,9 @@ int bl602_bringup(void) #endif #ifdef CONFIG_I2C struct i2c_master_s *i2c_bus; +#endif +#ifdef CONFIG_SPI + struct spi_dev_s *spi_bus; #endif int ret = OK; @@ -179,6 +183,11 @@ int bl602_bringup(void) i2c_register(i2c_bus, 0); #endif +#ifdef CONFIG_SPI + spi_bus = bl602_spibus_initialize(0); + spi_register(spi_bus, 0); +#endif + #ifdef CONFIG_BL602_SPIFLASH mtd_part = bl602_spiflash_alloc_mtdpart();