mirror of
https://github.com/apache/nuttx.git
synced 2026-08-08 22:17:18 +00:00
arm/rp2040: Add RP2040 SPI device support
This commit is contained in:
parent
a8d269df98
commit
2d7aabf13b
5 changed files with 1261 additions and 0 deletions
|
|
@ -73,6 +73,29 @@ config RP2040_UART1_2STOP
|
|||
|
||||
endif
|
||||
|
||||
config RP2040_SPI
|
||||
bool "SPI"
|
||||
select SPI
|
||||
|
||||
if RP2040_SPI
|
||||
|
||||
config RP2040_SPI0
|
||||
bool "SPI0"
|
||||
|
||||
config RP2040_SPI1
|
||||
bool "SPI1"
|
||||
|
||||
config RP2040_SPI_DRIVER
|
||||
bool "SPI character driver"
|
||||
default y
|
||||
select SPI_DRIVER
|
||||
---help---
|
||||
Build in support for a character driver at /dev/spi[N] that may be
|
||||
used to perform SPI bus transfers from applications. The intent of
|
||||
this driver is to support SPI testing.
|
||||
|
||||
endif
|
||||
|
||||
config RP2040_I2C
|
||||
bool "I2C"
|
||||
select I2C
|
||||
|
|
@ -96,3 +119,25 @@ config RP2040_I2C_DRIVER
|
|||
in any real driver application.
|
||||
|
||||
endif
|
||||
|
||||
menuconfig RP2040_SPISD
|
||||
bool "SPI SD Card"
|
||||
default n
|
||||
select MMCSD_SPI
|
||||
|
||||
if RP2040_SPISD
|
||||
|
||||
config RP2040_SPISD_SLOT_NO
|
||||
int "SPI SD Card Slot Number"
|
||||
default 0
|
||||
---help---
|
||||
Select spi sd card slot number.
|
||||
|
||||
config RP2040_SPISD_SPI_CH
|
||||
int "SPI channel number"
|
||||
default 0
|
||||
range 0 1
|
||||
---help---
|
||||
Select spi channel number to use spi sd card.
|
||||
|
||||
endif # SPISD Configuration
|
||||
|
|
|
|||
|
|
@ -67,6 +67,10 @@ CHIP_CSRCS += rp2040_cpuidlestack.c
|
|||
CHIP_CSRCS += rp2040_testset.c
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_RP2040_SPI),y)
|
||||
CHIP_CSRCS += rp2040_spi.c
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_RP2040_I2C),y)
|
||||
CHIP_CSRCS += rp2040_i2c.c
|
||||
endif
|
||||
|
|
|
|||
160
arch/arm/src/rp2040/hardware/rp2040_spi.h
Normal file
160
arch/arm/src/rp2040/hardware/rp2040_spi.h
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
/****************************************************************************
|
||||
* arch/arm/src/rp2040/hardware/rp2040_spi.h
|
||||
*
|
||||
* Generated from rp2040.svd originally provided by
|
||||
* Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __ARCH_ARM_SRC_RP2040_HARDWARE_RP2040_SPI_H
|
||||
#define __ARCH_ARM_SRC_RP2040_HARDWARE_RP2040_SPI_H
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include "hardware/rp2040_memorymap.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/* Register offsets *********************************************************/
|
||||
|
||||
#define RP2040_SPI_SSPCR0_OFFSET 0x000000 /* Control register 0 */
|
||||
#define RP2040_SPI_SSPCR1_OFFSET 0x000004 /* Control register 1 */
|
||||
#define RP2040_SPI_SSPDR_OFFSET 0x000008 /* Data register */
|
||||
#define RP2040_SPI_SSPSR_OFFSET 0x00000c /* Status register */
|
||||
#define RP2040_SPI_SSPCPSR_OFFSET 0x000010 /* Clock prescale register */
|
||||
#define RP2040_SPI_SSPIMSC_OFFSET 0x000014 /* Interrupt mask set or clear register */
|
||||
#define RP2040_SPI_SSPRIS_OFFSET 0x000018 /* Raw interrupt status register */
|
||||
#define RP2040_SPI_SSPMIS_OFFSET 0x00001c /* Masked interrupt status register */
|
||||
#define RP2040_SPI_SSPICR_OFFSET 0x000020 /* Interrupt clear register */
|
||||
#define RP2040_SPI_SSPDMACR_OFFSET 0x000024 /* DMA control register */
|
||||
#define RP2040_SPI_SSPPERIPHID0_OFFSET 0x000fe0 /* Peripheral identification registers */
|
||||
#define RP2040_SPI_SSPPERIPHID1_OFFSET 0x000fe4 /* Peripheral identification registers */
|
||||
#define RP2040_SPI_SSPPERIPHID2_OFFSET 0x000fe8 /* Peripheral identification registers */
|
||||
#define RP2040_SPI_SSPPERIPHID3_OFFSET 0x000fec /* Peripheral identification registers */
|
||||
#define RP2040_SPI_SSPPCELLID0_OFFSET 0x000ff0 /* PrimeCell identification registers */
|
||||
#define RP2040_SPI_SSPPCELLID1_OFFSET 0x000ff4 /* PrimeCell identification registers */
|
||||
#define RP2040_SPI_SSPPCELLID2_OFFSET 0x000ff8 /* PrimeCell identification registers */
|
||||
#define RP2040_SPI_SSPPCELLID3_OFFSET 0x000ffc /* PrimeCell identification registers */
|
||||
|
||||
/* Register definitions *****************************************************/
|
||||
|
||||
#define RP2040_SPI_SSPCR0(n) (RP2040_SPI_BASE(n) + RP2040_SPI_SSPCR0_OFFSET)
|
||||
#define RP2040_SPI_SSPCR1(n) (RP2040_SPI_BASE(n) + RP2040_SPI_SSPCR1_OFFSET)
|
||||
#define RP2040_SPI_SSPDR(n) (RP2040_SPI_BASE(n) + RP2040_SPI_SSPDR_OFFSET)
|
||||
#define RP2040_SPI_SSPSR(n) (RP2040_SPI_BASE(n) + RP2040_SPI_SSPSR_OFFSET)
|
||||
#define RP2040_SPI_SSPCPSR(n) (RP2040_SPI_BASE(n) + RP2040_SPI_SSPCPSR_OFFSET)
|
||||
#define RP2040_SPI_SSPIMSC(n) (RP2040_SPI_BASE(n) + RP2040_SPI_SSPIMSC_OFFSET)
|
||||
#define RP2040_SPI_SSPRIS(n) (RP2040_SPI_BASE(n) + RP2040_SPI_SSPRIS_OFFSET)
|
||||
#define RP2040_SPI_SSPMIS(n) (RP2040_SPI_BASE(n) + RP2040_SPI_SSPMIS_OFFSET)
|
||||
#define RP2040_SPI_SSPICR(n) (RP2040_SPI_BASE(n) + RP2040_SPI_SSPICR_OFFSET)
|
||||
#define RP2040_SPI_SSPDMACR(n) (RP2040_SPI_BASE(n) + RP2040_SPI_SSPDMACR_OFFSET)
|
||||
#define RP2040_SPI_SSPPERIPHID0(n) (RP2040_SPI_BASE(n) + RP2040_SPI_SSPPERIPHID0_OFFSET)
|
||||
#define RP2040_SPI_SSPPERIPHID1(n) (RP2040_SPI_BASE(n) + RP2040_SPI_SSPPERIPHID1_OFFSET)
|
||||
#define RP2040_SPI_SSPPERIPHID2(n) (RP2040_SPI_BASE(n) + RP2040_SPI_SSPPERIPHID2_OFFSET)
|
||||
#define RP2040_SPI_SSPPERIPHID3(n) (RP2040_SPI_BASE(n) + RP2040_SPI_SSPPERIPHID3_OFFSET)
|
||||
#define RP2040_SPI_SSPPCELLID0(n) (RP2040_SPI_BASE(n) + RP2040_SPI_SSPPCELLID0_OFFSET)
|
||||
#define RP2040_SPI_SSPPCELLID1(n) (RP2040_SPI_BASE(n) + RP2040_SPI_SSPPCELLID1_OFFSET)
|
||||
#define RP2040_SPI_SSPPCELLID2(n) (RP2040_SPI_BASE(n) + RP2040_SPI_SSPPCELLID2_OFFSET)
|
||||
#define RP2040_SPI_SSPPCELLID3(n) (RP2040_SPI_BASE(n) + RP2040_SPI_SSPPCELLID3_OFFSET)
|
||||
|
||||
/* Register bit definitions *************************************************/
|
||||
|
||||
#define RP2040_SPI_SSPCR0_SCR_SHIFT (8) /* Serial clock rate */
|
||||
#define RP2040_SPI_SSPCR0_SCR_MASK (0xff << RP2040_SPI_SSPCR0_SCR_SHIFT)
|
||||
#define RP2040_SPI_SSPCR0_SPH (1 << 7) /* SSPCLKOUT phase */
|
||||
#define RP2040_SPI_SSPCR0_SPO (1 << 6) /* SSPCLKOUT polarity */
|
||||
#define RP2040_SPI_SSPCR0_FRF_SHIFT (4) /* Frame format */
|
||||
#define RP2040_SPI_SSPCR0_FRF_MASK (0x03 << RP2040_SPI_SSPCR0_FRF_SHIFT)
|
||||
#define RP2040_SPI_SSPCR0_DSS_MASK (0x0f) /* Data Size Select */
|
||||
#define RP2040_SPI_SSPCR0_DSS_SHIFT (0)
|
||||
|
||||
#define RP2040_SPI_SSPCR1_SOD (1 << 3) /* Slave-mode output disable */
|
||||
#define RP2040_SPI_SSPCR1_MS (1 << 2) /* Master or slave mode select */
|
||||
#define RP2040_SPI_SSPCR1_SSE (1 << 1) /* Synchronous serial port enable: 0 SSP operation disabled. 1 SSP operation enabled. */
|
||||
#define RP2040_SPI_SSPCR1_LBM (1 << 0) /* Loop back mode */
|
||||
|
||||
#define RP2040_SPI_SSPDR_DATA_MASK (0xffff) /* Transmit/Receive FIFO */
|
||||
|
||||
#define RP2040_SPI_SSPSR_BSY (1 << 4) /* PrimeCell SSP busy flag */
|
||||
#define RP2040_SPI_SSPSR_RFF (1 << 3) /* Receive FIFO full */
|
||||
#define RP2040_SPI_SSPSR_RNE (1 << 2) /* Receive FIFO not empty */
|
||||
#define RP2040_SPI_SSPSR_TNF (1 << 1) /* Transmit FIFO not full */
|
||||
#define RP2040_SPI_SSPSR_TFE (1 << 0) /* Transmit FIFO empty */
|
||||
|
||||
#define RP2040_SPI_SSPCPSR_CPSDVSR_MASK (0xff) /* Clock prescale divisor. Must be an even number from 2-254 */
|
||||
|
||||
#define RP2040_SPI_SSPIMSC_TXIM (1 << 3) /* Transmit FIFO interrupt mask */
|
||||
#define RP2040_SPI_SSPIMSC_RXIM (1 << 2) /* Receive FIFO interrupt mask */
|
||||
#define RP2040_SPI_SSPIMSC_RTIM (1 << 1) /* Receive timeout interrupt mask */
|
||||
#define RP2040_SPI_SSPIMSC_RORIM (1 << 0) /* Receive overrun interrupt mask */
|
||||
|
||||
#define RP2040_SPI_SSPRIS_TXRIS (1 << 3) /* Gives the raw interrupt state, prior to masking, of the SSPTXINTR interrupt */
|
||||
#define RP2040_SPI_SSPRIS_RXRIS (1 << 2) /* Gives the raw interrupt state, prior to masking, of the SSPRXINTR interrupt */
|
||||
#define RP2040_SPI_SSPRIS_RTRIS (1 << 1) /* Gives the raw interrupt state, prior to masking, of the SSPRTINTR interrupt */
|
||||
#define RP2040_SPI_SSPRIS_RORRIS (1 << 0) /* Gives the raw interrupt state, prior to masking, of the SSPRORINTR interrupt */
|
||||
|
||||
#define RP2040_SPI_SSPMIS_TXMIS (1 << 3) /* Gives the transmit FIFO masked interrupt state, after masking, of the SSPTXINTR interrupt */
|
||||
#define RP2040_SPI_SSPMIS_RXMIS (1 << 2) /* Gives the receive FIFO masked interrupt state, after masking, of the SSPRXINTR interrupt */
|
||||
#define RP2040_SPI_SSPMIS_RTMIS (1 << 1) /* Gives the receive timeout masked interrupt state, after masking, of the SSPRTINTR interrupt */
|
||||
#define RP2040_SPI_SSPMIS_RORMIS (1 << 0) /* Gives the receive over run masked interrupt status, after masking, of the SSPRORINTR interrupt */
|
||||
|
||||
#define RP2040_SPI_SSPICR_RTIC (1 << 1) /* Clears the SSPRTINTR interrupt */
|
||||
#define RP2040_SPI_SSPICR_RORIC (1 << 0) /* Clears the SSPRORINTR interrupt */
|
||||
|
||||
#define RP2040_SPI_SSPDMACR_TXDMAE (1 << 1) /* Transmit DMA Enable. If this bit is set to 1, DMA for the transmit FIFO is enabled. */
|
||||
#define RP2040_SPI_SSPDMACR_RXDMAE (1 << 0) /* Receive DMA Enable. If this bit is set to 1, DMA for the receive FIFO is enabled. */
|
||||
|
||||
#define RP2040_SPI_SSPPERIPHID0_PARTNUMBER0_MASK (0xff) /* These bits read back as 0x22 */
|
||||
|
||||
#define RP2040_SPI_SSPPERIPHID1_DESIGNER0_SHIFT (4) /* These bits read back as 0x1 */
|
||||
#define RP2040_SPI_SSPPERIPHID1_DESIGNER0_MASK (0x0f << RP2040_SPI_SSPPERIPHID1_DESIGNER0_SHIFT)
|
||||
#define RP2040_SPI_SSPPERIPHID1_PARTNUMBER1_MASK (0x0f) /* These bits read back as 0x0 */
|
||||
|
||||
#define RP2040_SPI_SSPPERIPHID2_REVISION_SHIFT (4) /* These bits return the peripheral revision */
|
||||
#define RP2040_SPI_SSPPERIPHID2_REVISION_MASK (0x0f << RP2040_SPI_SSPPERIPHID2_REVISION_SHIFT)
|
||||
#define RP2040_SPI_SSPPERIPHID2_DESIGNER1_MASK (0x0f) /* These bits read back as 0x4 */
|
||||
|
||||
#define RP2040_SPI_SSPPERIPHID3_CONFIGURATION_MASK (0xff) /* These bits read back as 0x00 */
|
||||
|
||||
#define RP2040_SPI_SSPPCELLID0_MASK (0xff) /* These bits read back as 0x0D */
|
||||
|
||||
#define RP2040_SPI_SSPPCELLID1_MASK (0xff) /* These bits read back as 0xF0 */
|
||||
|
||||
#define RP2040_SPI_SSPPCELLID2_MASK (0xff) /* These bits read back as 0x05 */
|
||||
|
||||
#define RP2040_SPI_SSPPCELLID3_MASK (0xff) /* These bits read back as 0xB1 */
|
||||
|
||||
#endif /* __ARCH_ARM_SRC_RP2040_HARDWARE_RP2040_SPI_H */
|
||||
828
arch/arm/src/rp2040/rp2040_spi.c
Normal file
828
arch/arm/src/rp2040/rp2040_spi.c
Normal file
|
|
@ -0,0 +1,828 @@
|
|||
/****************************************************************************
|
||||
* arch/arm/src/rp2040/rp2040_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 <sys/types.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
#include <debug.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <arch/board/board.h>
|
||||
#include <nuttx/irq.h>
|
||||
#include <nuttx/arch.h>
|
||||
#include <nuttx/semaphore.h>
|
||||
#include <nuttx/spi/spi.h>
|
||||
|
||||
#include "arm_internal.h"
|
||||
#include "arm_arch.h"
|
||||
|
||||
#include "chip.h"
|
||||
|
||||
#include "rp2040_spi.h"
|
||||
#include "hardware/rp2040_spi.h"
|
||||
|
||||
#ifdef CONFIG_RP2040_SPI
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __unused
|
||||
#define __unused __attribute__((unused))
|
||||
#endif
|
||||
|
||||
/* 8 frame FIFOs for both transmit and receive */
|
||||
|
||||
#define RP2040_SPI_FIFOSZ 8
|
||||
|
||||
/****************************************************************************
|
||||
* Private Types
|
||||
****************************************************************************/
|
||||
|
||||
/* This structure describes the state of the SPI driver */
|
||||
|
||||
struct rp2040_spidev_s
|
||||
{
|
||||
struct spi_dev_s spidev; /* Externally visible part of the SPI interface */
|
||||
uint32_t spibase; /* SPIn base address */
|
||||
uint32_t spibasefreq;
|
||||
#ifdef CONFIG_RP2040_SPI_INTERRUPTS
|
||||
uint8_t spiirq; /* SPI IRQ number */
|
||||
#endif
|
||||
sem_t exclsem; /* Held while chip is selected for mutual exclusion */
|
||||
uint32_t frequency; /* Requested clock frequency */
|
||||
uint32_t actual; /* Actual clock frequency */
|
||||
uint8_t nbits; /* Width of word in bits (4 to 16) */
|
||||
uint8_t mode; /* Mode 0,1,2,3 */
|
||||
uint8_t port; /* Port number */
|
||||
int initialized; /* Initialized flag */
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
/* Helpers */
|
||||
|
||||
static inline uint32_t spi_getreg(FAR struct rp2040_spidev_s *priv,
|
||||
uint8_t offset);
|
||||
static inline void spi_putreg(FAR struct rp2040_spidev_s *priv,
|
||||
uint8_t offset, uint32_t value);
|
||||
|
||||
/* SPI methods */
|
||||
|
||||
static int spi_lock(FAR struct spi_dev_s *dev, bool lock);
|
||||
static uint32_t spi_setfrequency(FAR struct spi_dev_s *dev,
|
||||
uint32_t frequency);
|
||||
static void spi_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode);
|
||||
static void spi_setbits(FAR struct spi_dev_s *dev, int nbits);
|
||||
static uint32_t spi_send(FAR struct spi_dev_s *dev, uint32_t wd);
|
||||
static void __unused spi_exchange(FAR struct spi_dev_s *dev,
|
||||
FAR const void *txbuffer,
|
||||
FAR void *rxbuffer,
|
||||
size_t nwords);
|
||||
#ifndef CONFIG_SPI_EXCHANGE
|
||||
static void spi_sndblock(FAR struct spi_dev_s *dev, FAR const void *buffer,
|
||||
size_t nwords);
|
||||
static void spi_recvblock(FAR struct spi_dev_s *dev, FAR void *buffer,
|
||||
size_t nwords);
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_RP2040_SPI0
|
||||
static const struct spi_ops_s g_spi0ops =
|
||||
{
|
||||
.lock = spi_lock,
|
||||
.select = rp2040_spi0select, /* Provided externally */
|
||||
.setfrequency = spi_setfrequency,
|
||||
.setmode = spi_setmode,
|
||||
.setbits = spi_setbits,
|
||||
#ifdef CONFIG_SPI_HWFEATURES
|
||||
.hwfeatures = 0, /* Not supported */
|
||||
#endif
|
||||
.status = rp2040_spi0status, /* Provided externally */
|
||||
#ifdef CONFIG_SPI_CMDDATA
|
||||
.cmddata = rp2040_spi0cmddata, /* Provided externally */
|
||||
#endif
|
||||
.send = spi_send,
|
||||
#ifdef CONFIG_SPI_EXCHANGE
|
||||
.exchange = spi_exchange,
|
||||
#else
|
||||
.sndblock = spi_sndblock,
|
||||
.recvblock = spi_recvblock,
|
||||
#endif
|
||||
#ifdef CONFIG_SPI_CALLBACK
|
||||
.registercallback = rp2040_spi0register, /* Provided externally */
|
||||
#else
|
||||
.registercallback = 0, /* Not implemented */
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct rp2040_spidev_s g_spi0dev =
|
||||
{
|
||||
.spidev =
|
||||
{
|
||||
&g_spi0ops
|
||||
},
|
||||
.spibase = RP2040_SPI0_BASE,
|
||||
.spibasefreq = 0,
|
||||
.port = 0,
|
||||
.initialized = 0,
|
||||
#ifdef CONFIG_RP2040_SPI_INTERRUPTS
|
||||
.spiirq = RP2040_SPI0_IRQ,
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RP2040_SPI1
|
||||
static const struct spi_ops_s g_spi1ops =
|
||||
{
|
||||
.lock = spi_lock,
|
||||
.select = rp2040_spi1select, /* Provided externally */
|
||||
.setfrequency = spi_setfrequency,
|
||||
.setmode = spi_setmode,
|
||||
.setbits = spi_setbits,
|
||||
#ifdef CONFIG_SPI_HWFEATURES
|
||||
.hwfeatures = 0, /* Not supported */
|
||||
#endif
|
||||
.status = rp2040_spi1status, /* Provided externally */
|
||||
#ifdef CONFIG_SPI_CMDDATA
|
||||
.cmddata = rp2040_spi1cmddata, /* Provided externally */
|
||||
#endif
|
||||
.send = spi_send,
|
||||
#ifdef CONFIG_SPI_EXCHANGE
|
||||
.exchange = spi_exchange,
|
||||
#else
|
||||
.sndblock = spi_sndblock,
|
||||
.recvblock = spi_recvblock,
|
||||
#endif
|
||||
#ifdef CONFIG_SPI_CALLBACK
|
||||
.registercallback = rp2040_spi1register, /* Provided externally */
|
||||
#else
|
||||
.registercallback = 0, /* Not implemented */
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct rp2040_spidev_s g_spi1dev =
|
||||
{
|
||||
.spidev =
|
||||
{
|
||||
&g_spi1ops
|
||||
},
|
||||
.spibase = RP2040_SPI1_BASE,
|
||||
.spibasefreq = 0,
|
||||
.port = 1,
|
||||
.initialized = 0,
|
||||
#ifdef CONFIG_RP2040_SPI_INTERRUPTS
|
||||
.spiirq = RP2040_SPI1_IRQ,
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Public Data
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: spi_getreg
|
||||
*
|
||||
* Description:
|
||||
* Get the contents of the SPI register at offset
|
||||
*
|
||||
* Input Parameters:
|
||||
* priv - private SPI device structure
|
||||
* offset - offset to the register of interest
|
||||
*
|
||||
* Returned Value:
|
||||
* The contents of the 32-bit register
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static inline uint32_t spi_getreg(FAR struct rp2040_spidev_s *priv,
|
||||
uint8_t offset)
|
||||
{
|
||||
return getreg32(priv->spibase + (uint32_t)offset);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: spi_putreg
|
||||
*
|
||||
* Description:
|
||||
* Write a 32-bit value to the SPI register at offset
|
||||
*
|
||||
* Input Parameters:
|
||||
* priv - private SPI device structure
|
||||
* offset - offset to the register of interest
|
||||
* value - the 16-bit value to be written
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static inline void spi_putreg(FAR struct rp2040_spidev_s *priv,
|
||||
uint8_t offset, uint32_t value)
|
||||
{
|
||||
putreg32(value, priv->spibase + (uint32_t)offset);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: spi_lock
|
||||
*
|
||||
* Description:
|
||||
* On SPI buses where there are multiple devices, it will be necessary to
|
||||
* lock SPI to have exclusive access to the buses for a sequence of
|
||||
* transfers. The bus should be locked before the chip is selected. After
|
||||
* locking the SPI bus, the caller should then also call the setfrequency,
|
||||
* setbits, and setmode methods to make sure that the SPI is properly
|
||||
* configured for the device. If the SPI bus is being shared, then it
|
||||
* may have been left in an incompatible state.
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - Device-specific state data
|
||||
* lock - true: Lock spi bus, false: unlock SPI bus
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int spi_lock(FAR struct spi_dev_s *dev, bool lock)
|
||||
{
|
||||
FAR struct rp2040_spidev_s *priv = (FAR struct rp2040_spidev_s *)dev;
|
||||
|
||||
if (lock)
|
||||
{
|
||||
/* Take the semaphore (perhaps waiting) */
|
||||
|
||||
return nxsem_wait_uninterruptible(&priv->exclsem);
|
||||
}
|
||||
else
|
||||
{
|
||||
return nxsem_post(&priv->exclsem);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: 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 spi_setfrequency(FAR struct spi_dev_s *dev,
|
||||
uint32_t frequency)
|
||||
{
|
||||
FAR struct rp2040_spidev_s *priv = (FAR struct rp2040_spidev_s *)dev;
|
||||
uint32_t divisor;
|
||||
uint32_t actual;
|
||||
|
||||
/* Set SPI_CLOCK */
|
||||
|
||||
/* frequency = SPI_CLOCK / divisor, or divisor = SPI_CLOCK / frequency */
|
||||
|
||||
priv->spibasefreq = BOARD_PERI_FREQ;
|
||||
divisor = priv->spibasefreq / frequency;
|
||||
|
||||
/* "In master mode, CPSDVSRmin = 2 or larger (even numbers only)" */
|
||||
|
||||
if (divisor < 2)
|
||||
{
|
||||
divisor = 2;
|
||||
}
|
||||
else if (divisor > 254)
|
||||
{
|
||||
divisor = 254;
|
||||
}
|
||||
|
||||
divisor = (divisor + 1) & ~1;
|
||||
|
||||
/* Save the new divisor value */
|
||||
|
||||
spi_putreg(priv, RP2040_SPI_SSPCPSR_OFFSET, divisor);
|
||||
|
||||
/* Calculate the new actual */
|
||||
|
||||
actual = priv->spibasefreq / divisor;
|
||||
|
||||
/* Save the frequency setting */
|
||||
|
||||
priv->frequency = frequency;
|
||||
priv->actual = actual;
|
||||
|
||||
spiinfo("Frequency %" PRId32 "->%" PRId32 "\n", frequency, actual);
|
||||
return actual;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: spi_setmode
|
||||
*
|
||||
* Description:
|
||||
* Set the SPI mode. Optional. See enum spi_mode_e for mode definitions
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - Device-specific state data
|
||||
* mode - The SPI mode requested
|
||||
*
|
||||
* Returned Value:
|
||||
* none
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void spi_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode)
|
||||
{
|
||||
FAR struct rp2040_spidev_s *priv = (FAR struct rp2040_spidev_s *)dev;
|
||||
uint32_t regval;
|
||||
|
||||
/* Has the mode changed? */
|
||||
|
||||
if (mode != priv->mode)
|
||||
{
|
||||
/* Yes... Set CR0 appropriately */
|
||||
|
||||
regval = spi_getreg(priv, RP2040_SPI_SSPCR0_OFFSET);
|
||||
regval &= ~(RP2040_SPI_SSPCR0_SPO | RP2040_SPI_SSPCR0_SPH);
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */
|
||||
break;
|
||||
|
||||
case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */
|
||||
regval |= RP2040_SPI_SSPCR0_SPH;
|
||||
break;
|
||||
|
||||
case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */
|
||||
regval |= RP2040_SPI_SSPCR0_SPO;
|
||||
break;
|
||||
|
||||
case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */
|
||||
regval |= (RP2040_SPI_SSPCR0_SPO | RP2040_SPI_SSPCR0_SPH);
|
||||
break;
|
||||
|
||||
default:
|
||||
spierr("Bad mode: %d\n", mode);
|
||||
DEBUGASSERT(FALSE);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
spi_putreg(priv, RP2040_SPI_SSPCR0_OFFSET, regval);
|
||||
|
||||
/* Save the mode so that subsequent re-configurations will be faster */
|
||||
|
||||
priv->mode = mode;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: spi_setbits
|
||||
*
|
||||
* Description:
|
||||
* Set the number if bits per word.
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - Device-specific state data
|
||||
* nbits - The number of bits requests
|
||||
*
|
||||
* Returned Value:
|
||||
* none
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void spi_setbits(FAR struct spi_dev_s *dev, int nbits)
|
||||
{
|
||||
FAR struct rp2040_spidev_s *priv = (FAR struct rp2040_spidev_s *)dev;
|
||||
uint32_t regval;
|
||||
|
||||
/* Has the number of bits changed? */
|
||||
|
||||
DEBUGASSERT(priv && nbits > 3 && nbits < 17);
|
||||
|
||||
if (nbits != priv->nbits)
|
||||
{
|
||||
/* Yes... Set CR0 appropriately */
|
||||
|
||||
regval = spi_getreg(priv, RP2040_SPI_SSPCR0_OFFSET);
|
||||
regval &= ~RP2040_SPI_SSPCR0_DSS_MASK;
|
||||
regval |= ((nbits - 1) << RP2040_SPI_SSPCR0_DSS_SHIFT);
|
||||
spi_putreg(priv, RP2040_SPI_SSPCR0_OFFSET, regval);
|
||||
|
||||
/* Save the selection so that re-configurations will be faster
|
||||
*/
|
||||
|
||||
priv->nbits = nbits;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: 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:
|
||||
* response
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static uint32_t spi_send(FAR struct spi_dev_s *dev, uint32_t wd)
|
||||
{
|
||||
FAR struct rp2040_spidev_s *priv = (FAR struct rp2040_spidev_s *)dev;
|
||||
register uint32_t regval;
|
||||
|
||||
/* Wait while the TX FIFO is full */
|
||||
|
||||
while (!(spi_getreg(priv, RP2040_SPI_SSPSR_OFFSET) & RP2040_SPI_SSPSR_TNF))
|
||||
;
|
||||
|
||||
/* Write the byte to the TX FIFO */
|
||||
|
||||
spi_putreg(priv, RP2040_SPI_SSPDR_OFFSET, wd);
|
||||
|
||||
/* Wait for the RX FIFO not empty */
|
||||
|
||||
while (!(spi_getreg(priv, RP2040_SPI_SSPSR_OFFSET) & RP2040_SPI_SSPSR_RNE))
|
||||
;
|
||||
|
||||
/* Get the value from the RX FIFO and return it */
|
||||
|
||||
regval = spi_getreg(priv, RP2040_SPI_SSPDR_OFFSET);
|
||||
spiinfo("%04" PRIx32 "->%04" PRIx32 "\n", wd, regval);
|
||||
|
||||
return regval;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: spi_do_exchange
|
||||
*
|
||||
* Description:
|
||||
* Exchange a block of data from SPI. Required.
|
||||
*
|
||||
* 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 spi_do_exchange(FAR struct spi_dev_s *dev,
|
||||
FAR const void *txbuffer, FAR void *rxbuffer,
|
||||
size_t nwords)
|
||||
{
|
||||
FAR struct rp2040_spidev_s *priv = (FAR struct rp2040_spidev_s *)dev;
|
||||
|
||||
union
|
||||
{
|
||||
FAR const uint8_t *p8;
|
||||
FAR const uint16_t *p16;
|
||||
FAR const void *pv;
|
||||
} tx;
|
||||
|
||||
union
|
||||
{
|
||||
FAR uint8_t *p8;
|
||||
FAR uint16_t *p16;
|
||||
FAR void *pv;
|
||||
} rx;
|
||||
|
||||
uint32_t data;
|
||||
uint32_t datadummy = (priv->nbits > 8) ? 0xffff : 0xff;
|
||||
uint32_t rxpending = 0;
|
||||
|
||||
/* Remaining data to be sent (and no synchronization error has occurred) */
|
||||
|
||||
tx.pv = txbuffer;
|
||||
rx.pv = rxbuffer;
|
||||
|
||||
while (nwords || rxpending)
|
||||
{
|
||||
/* Write data to the data register while (1) the TX FIFO is
|
||||
* not full, (2) we have not exceeded the depth of the TX FIFO,
|
||||
* and (3) there are more bytes to be sent.
|
||||
*/
|
||||
|
||||
spiinfo("TX: rxpending: %" PRId32 " nwords: %d\n", rxpending, nwords);
|
||||
while ((spi_getreg(priv, RP2040_SPI_SSPSR_OFFSET) &
|
||||
RP2040_SPI_SSPSR_TNF) &&
|
||||
(rxpending < RP2040_SPI_FIFOSZ) && nwords)
|
||||
{
|
||||
if (txbuffer)
|
||||
{
|
||||
if (priv->nbits > 8)
|
||||
{
|
||||
data = (uint32_t)*tx.p16++;
|
||||
}
|
||||
else
|
||||
{
|
||||
data = (uint32_t)*tx.p8++;
|
||||
}
|
||||
}
|
||||
|
||||
spi_putreg(priv, RP2040_SPI_SSPDR_OFFSET,
|
||||
txbuffer ? data : datadummy);
|
||||
nwords--;
|
||||
rxpending++;
|
||||
}
|
||||
|
||||
/* Now, read the RX data from the RX FIFO
|
||||
* while the RX FIFO is not empty
|
||||
*/
|
||||
|
||||
spiinfo("RX: rxpending: %" PRId32 "\n", rxpending);
|
||||
while (spi_getreg(priv, RP2040_SPI_SSPSR_OFFSET) &
|
||||
RP2040_SPI_SSPSR_RNE)
|
||||
{
|
||||
data = spi_getreg(priv, RP2040_SPI_SSPDR_OFFSET);
|
||||
if (rxbuffer)
|
||||
{
|
||||
if (priv->nbits > 8)
|
||||
{
|
||||
*rx.p16++ = (uint16_t)data;
|
||||
}
|
||||
else
|
||||
{
|
||||
*rx.p8++ = (uint8_t)data;
|
||||
}
|
||||
}
|
||||
|
||||
rxpending--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: spi_exchange
|
||||
*
|
||||
* Description:
|
||||
* Wrapper function of 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 spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer,
|
||||
FAR void *rxbuffer, size_t nwords)
|
||||
{
|
||||
spi_do_exchange(dev, txbuffer, rxbuffer, nwords);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: 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
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CONFIG_SPI_EXCHANGE
|
||||
static void spi_sndblock(FAR struct spi_dev_s *dev, FAR const void *buffer,
|
||||
size_t nwords)
|
||||
{
|
||||
return spi_exchange(dev, buffer, NULL, nwords);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: spi_recvblock
|
||||
*
|
||||
* Description:
|
||||
* Revice 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 spi_recvblock(FAR struct spi_dev_s *dev, FAR void *buffer,
|
||||
size_t nwords)
|
||||
{
|
||||
return spi_exchange(dev, NULL, buffer, nwords);
|
||||
}
|
||||
#endif /* !CONFIG_SPI_EXCHANGE */
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: rp2040_spibus_initialize
|
||||
*
|
||||
* Description:
|
||||
* Initialize the selected SPI port
|
||||
*
|
||||
* Input Parameter:
|
||||
* port - Port number
|
||||
*
|
||||
* Returned Value:
|
||||
* Valid SPI device structure reference on success; a NULL on failure
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
FAR struct spi_dev_s *rp2040_spibus_initialize(int port)
|
||||
{
|
||||
FAR struct rp2040_spidev_s *priv;
|
||||
uint32_t regval;
|
||||
int i;
|
||||
|
||||
switch (port)
|
||||
{
|
||||
#ifdef CONFIG_RP2040_SPI0
|
||||
case 0:
|
||||
priv = &g_spi0dev;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RP2040_SPI1
|
||||
case 1:
|
||||
priv = &g_spi1dev;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If already initialized */
|
||||
|
||||
if (priv->initialized)
|
||||
{
|
||||
return &priv->spidev;
|
||||
}
|
||||
|
||||
/* Configure clocking */
|
||||
|
||||
priv->spibasefreq = BOARD_PERI_FREQ;
|
||||
|
||||
/* Configure 8-bit SPI mode */
|
||||
|
||||
spi_putreg(priv, RP2040_SPI_SSPCR0_OFFSET,
|
||||
((8 - 1) << RP2040_SPI_SSPCR0_DSS_SHIFT) |
|
||||
(0 << RP2040_SPI_SSPCR0_FRF_SHIFT));
|
||||
|
||||
/* Disable SPI and all interrupts (we'll poll for all data) */
|
||||
|
||||
spi_putreg(priv, RP2040_SPI_SSPCR1_OFFSET, 0);
|
||||
spi_putreg(priv, RP2040_SPI_SSPIMSC_OFFSET, 0);
|
||||
|
||||
/* Clear interrupts */
|
||||
|
||||
spi_putreg(priv, RP2040_SPI_SSPICR_OFFSET, 0x3);
|
||||
|
||||
/* Set the initial SPI configuration */
|
||||
|
||||
priv->frequency = 0;
|
||||
priv->nbits = 8;
|
||||
priv->mode = SPIDEV_MODE0;
|
||||
|
||||
/* Select a default frequency of approx. 400KHz */
|
||||
|
||||
spi_setfrequency((FAR struct spi_dev_s *)priv, 400000);
|
||||
|
||||
/* Initialize the SPI semaphore that enforces mutually exclusive access */
|
||||
|
||||
nxsem_init(&priv->exclsem, 0, 1);
|
||||
|
||||
regval = spi_getreg(priv, RP2040_SPI_SSPCR1_OFFSET);
|
||||
spi_putreg(priv, RP2040_SPI_SSPCR1_OFFSET, regval | RP2040_SPI_SSPCR1_SSE);
|
||||
|
||||
for (i = 0; i < RP2040_SPI_FIFOSZ; i++)
|
||||
{
|
||||
spi_getreg(priv, RP2040_SPI_SSPDR_OFFSET);
|
||||
}
|
||||
|
||||
/* Set a initialized flag */
|
||||
|
||||
priv->initialized = 1;
|
||||
|
||||
return &priv->spidev;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: spi_flush
|
||||
*
|
||||
* Description:
|
||||
* Flush and discard any words left in the RX fifo. This can be done
|
||||
* after a device is deselected if you worry about such things.
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - Device-specific state data
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void spi_flush(FAR struct spi_dev_s *dev)
|
||||
{
|
||||
FAR struct rp2040_spidev_s *priv = (FAR struct rp2040_spidev_s *)dev;
|
||||
|
||||
/* Wait for the TX FIFO not full indication */
|
||||
|
||||
while (!(spi_getreg(priv, RP2040_SPI_SSPSR_OFFSET) & RP2040_SPI_SSPSR_TNF))
|
||||
;
|
||||
spi_putreg(priv, RP2040_SPI_SSPDR_OFFSET, 0xff);
|
||||
|
||||
/* Wait until TX FIFO and TX shift buffer are empty */
|
||||
|
||||
while (spi_getreg(priv, RP2040_SPI_SSPSR_OFFSET) & RP2040_SPI_SSPSR_BSY);
|
||||
|
||||
/* Wait until RX FIFO is not empty */
|
||||
|
||||
while (!(spi_getreg(priv, RP2040_SPI_SSPSR_OFFSET) & RP2040_SPI_SSPSR_RNE))
|
||||
;
|
||||
|
||||
/* Then read and discard bytes until the RX FIFO is empty */
|
||||
|
||||
do
|
||||
{
|
||||
spi_getreg(priv, RP2040_SPI_SSPDR_OFFSET);
|
||||
}
|
||||
while (spi_getreg(priv, RP2040_SPI_SSPSR_OFFSET) & RP2040_SPI_SSPSR_RNE);
|
||||
}
|
||||
|
||||
#endif
|
||||
224
arch/arm/src/rp2040/rp2040_spi.h
Normal file
224
arch/arm/src/rp2040/rp2040_spi.h
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
/****************************************************************************
|
||||
* arch/arm/src/rp2040/rp2040_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_ARM_SRC_RP2040_RP2040_SPI_H
|
||||
#define __ARCH_ARM_SRC_RP2040_RP2040_SPI_H
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
#include <nuttx/spi/spi.h>
|
||||
#include "hardware/rp2040_spi.h"
|
||||
#ifdef CONFIG_RP2040_DMAC
|
||||
#include "rp2040_dmac.h"
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_RP2040_SPI0) || defined(CONFIG_RP2040_SPI1)
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/* This header file defines interfaces to common SPI logic.
|
||||
* To use this common SPI logic on your board:
|
||||
*
|
||||
* 1. Provide logic in rp2040_boardinitialize() to configure SPI chip select
|
||||
* pins.
|
||||
* 2. Provide rp2040_spi0/1select() and rp2040_spi0/1status() functions in
|
||||
* your board-specific logic. These functions will perform chip selection
|
||||
* and status operations using GPIOs in the way your board is configured.
|
||||
* 3. If CONFIG_SPI_CMDDATA is defined in the NuttX configuration, provide
|
||||
* rp2040_spi0/1cmddata() functions in your board-specific logic. These
|
||||
* functions will perform cmd/data selection operations using GPIOs in the
|
||||
* way your board is configured.
|
||||
* 4. Your low level board initialization logic should call
|
||||
* rp2040_spibus_initialize.
|
||||
* 5. The handle returned by rp2040_spibus_initialize() may then be used to
|
||||
* bind the SPI driver to higher level logic
|
||||
* (e.g., calling mmcsd_spislotinitialize(), for example, will bind the
|
||||
* SPI driver to the SPI MMC/SD driver).
|
||||
*/
|
||||
|
||||
#define RP2040_SPI_DMAC_CHTYPE_TX (0)
|
||||
#define RP2040_SPI_DMAC_CHTYPE_RX (1)
|
||||
|
||||
/****************************************************************************
|
||||
* Public Types
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Public Data
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#undef EXTERN
|
||||
#if defined(__cplusplus)
|
||||
#define EXTERN extern "C"
|
||||
extern "C"
|
||||
{
|
||||
#else
|
||||
#define EXTERN extern
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: rp2040_spibus_initialize
|
||||
*
|
||||
* Description:
|
||||
* Initialize the selected SPI port
|
||||
*
|
||||
* Input Parameter:
|
||||
* port - Port number
|
||||
*
|
||||
* Returned Value:
|
||||
* Valid SPI device structure reference on success; a NULL on failure
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
FAR struct spi_dev_s *rp2040_spibus_initialize(int port);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: rp2040_spi_dmaconfig
|
||||
*
|
||||
* Description:
|
||||
* Enable DMA configuration.
|
||||
*
|
||||
* Input Parameter:
|
||||
* port - Port number
|
||||
* chtype - Channel type(TX or RX)
|
||||
* handle - DMA channel handle
|
||||
* conf - DMA configuration
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_RP2040_DMAC
|
||||
void rp2040_spi_dmaconfig(int port, int chtype, DMA_HANDLE handle,
|
||||
FAR dma_config_t *conf);
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: rp2040_spiXselect, rp2040_spiXstatus, and rp2040_spiXcmddata
|
||||
*
|
||||
* Description:
|
||||
* These functions must be provided in your board-specific logic.
|
||||
* The rp2040_spi0/1select functions will perform chip selection and the
|
||||
* rp2040_spi0/1status will perform status operations using GPIOs in
|
||||
* the way your board is configured.
|
||||
*
|
||||
* If CONFIG_SPI_CMDDATA is defined in the NuttX configuration, then
|
||||
* rp2040_spi0/1cmddata must also be provided.
|
||||
* This functions performs cmd/data selection operations using GPIOs in
|
||||
* the way your board is configured.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_RP2040_SPI0
|
||||
void rp2040_spi0select(FAR struct spi_dev_s *dev,
|
||||
uint32_t devid,
|
||||
bool selected);
|
||||
uint8_t rp2040_spi0status(FAR struct spi_dev_s *dev,
|
||||
uint32_t devid);
|
||||
#ifdef CONFIG_SPI_CMDDATA
|
||||
int rp2040_spi0cmddata(FAR struct spi_dev_s *dev,
|
||||
uint32_t devid,
|
||||
bool cmd);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RP2040_SPI1
|
||||
void rp2040_spi1select(FAR struct spi_dev_s *dev,
|
||||
uint32_t devid,
|
||||
bool selected);
|
||||
uint8_t rp2040_spi1status(FAR struct spi_dev_s *dev,
|
||||
uint32_t devid);
|
||||
#ifdef CONFIG_SPI_CMDDATA
|
||||
int rp2040_spi1cmddata(FAR struct spi_dev_s *dev,
|
||||
uint32_t devid,
|
||||
bool cmd);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: spi_flush
|
||||
*
|
||||
* Description:
|
||||
* Flush and discard any words left in the RX fifo. This can be called
|
||||
* from spi0/1select after a device is deselected (if you worry about such
|
||||
* things).
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - Device-specific state data
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void spi_flush(FAR struct spi_dev_s *dev);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: rp2040_spiXregister
|
||||
*
|
||||
* Description:
|
||||
* If the board supports a card detect callback to inform the SPI-based
|
||||
* MMC/SD driver when an SD card is inserted or removed, then
|
||||
* CONFIG_SPI_CALLBACK should be defined and the following function(s) must
|
||||
* must be implemented. These functions implements the registercallback
|
||||
* method of the SPI interface (see include/nuttx/spi/spi.h for details)
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - Device-specific state data
|
||||
* callback - The function to call on the media change
|
||||
* arg - A caller provided value to return with the callback
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 on success; negated errno on failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_SPI_CALLBACK
|
||||
#ifdef CONFIG_RP2040_SPI0
|
||||
int rp2040_spi0register(FAR struct spi_dev_s *dev,
|
||||
spi_mediachange_t callback, FAR void *arg);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RP2040_SPI1
|
||||
int rp2040_spi1register(FAR struct spi_dev_s *dev,
|
||||
spi_mediachange_t callback, FAR void *arg);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#undef EXTERN
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
#endif /* CONFIG_RP2040_SPI0/1 */
|
||||
#endif /* __ARCH_ARM_SRC_RP2040_RP2040_SPI_H */
|
||||
Loading…
Add table
Add a link
Reference in a new issue