forked from Imagelibrary/rtems
aarch64/raspberrypi: Add SPI support
- Standard SPI master mode. - Polling mode. - Interrupt mode. Close #5056
This commit is contained in:
@@ -47,6 +47,9 @@
|
||||
#define BCM2835_IRQ_ID_GPU_TIMER_M2 (BCM2711_IRQ_VC_PERIPHERAL_BASE + 2)
|
||||
#define BCM2835_IRQ_ID_GPU_TIMER_M3 (BCM2711_IRQ_VC_PERIPHERAL_BASE + 3)
|
||||
|
||||
/* Interrupt Vectors: SPI */
|
||||
#define BCM2711_IRQ_SPI (BCM2711_IRQ_VC_PERIPHERAL_BASE + 54)
|
||||
|
||||
#define BCM2835_IRQ_ID_USB 9
|
||||
#define BCM2835_IRQ_ID_AUX 29
|
||||
#define BCM2835_IRQ_ID_SPI_SLAVE 43
|
||||
|
||||
122
bsps/aarch64/raspberrypi/include/bsp/raspberrypi-spi.h
Normal file
122
bsps/aarch64/raspberrypi/include/bsp/raspberrypi-spi.h
Normal file
@@ -0,0 +1,122 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @ingroup raspberrypi_4_spi
|
||||
*
|
||||
* @brief Raspberry Pi specific SPI definitions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2024 Ning Yang
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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 OWNER 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 LIBBSP_AARCH64_RASPBERRYPI_4_SPI_H
|
||||
#define LIBBSP_AARCH64_RASPBERRYPI_4_SPI_H
|
||||
|
||||
#include <bsp/utility.h>
|
||||
#include <bsp/rpi-gpio.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t spics;
|
||||
#define RPI_SPICS_LEN_LONG BSP_BIT32(25)
|
||||
#define RPI_SPICS_DMA_LEN BSP_BIT32(24)
|
||||
#define RPI_SPICS_CSPOL2 BSP_BIT32(23)
|
||||
#define RPI_SPICS_CSPOL1 BSP_BIT32(22)
|
||||
#define RPI_SPICS_CSPOL0 BSP_BIT32(21)
|
||||
#define RPI_SPICS_RXF BSP_BIT32(20)
|
||||
#define RPI_SPICS_RXR BSP_BIT32(19)
|
||||
#define RPI_SPICS_TXD BSP_BIT32(18)
|
||||
#define RPI_SPICS_RXD BSP_BIT32(17)
|
||||
#define RPI_SPICS_DONE BSP_BIT32(16)
|
||||
#define RPI_SPICS_LEN BSP_BIT32(13)
|
||||
#define RPI_SPICS_REN BSP_BIT32(12)
|
||||
#define RPI_SPICS_ADCS BSP_BIT32(11)
|
||||
#define RPI_SPICS_INTR BSP_BIT32(10)
|
||||
#define RPI_SPICS_INTD BSP_BIT32(9)
|
||||
#define RPI_SPICS_DMAEN BSP_BIT32(8)
|
||||
#define RPI_SPICS_TA BSP_BIT32(7)
|
||||
#define RPI_SPICS_CSPOL BSP_BIT32(6)
|
||||
#define RPI_SPICS_CLEAR_TX BSP_BIT32(5)
|
||||
#define RPI_SPICS_CLEAR_RX BSP_BIT32(4)
|
||||
#define RPI_SPICS_CPOL BSP_BIT32(3)
|
||||
#define RPI_SPICS_CPHA BSP_BIT32(2)
|
||||
#define RPI_SPICS_CS(val) BSP_FLD32(val, 0, 1)
|
||||
#define RPI_SPICS_CS_SET(reg,val) BSP_FLD32SET(reg, val, 0, 1)
|
||||
uint32_t spififo;
|
||||
#define RPI_SPIFIFO_DATA(val) BSP_FLD32(val, 0, 31)
|
||||
#define RPI_SPIFIFO_DATA_GET(reg) BSP_FLD32GET(reg, 0, 31)
|
||||
#define RPI_SPIFIFO_DATA_SET(reg, val) BSP_FLD32SET(reg, val, 0, 31)
|
||||
uint32_t spiclk;
|
||||
#define RPI_SPICLK_CDIV(val) BSP_FLD32(val, 0, 15)
|
||||
#define RPI_SPICLK_CDIV_GET(reg) BSP_FLD32GET(reg, 0, 15)
|
||||
#define RPI_SPICLK_CDIV_SET(reg, val) BSP_FLD32SET(reg, val, 0, 15)
|
||||
uint32_t spidlen;
|
||||
#define RPI_SPIDLEN_LEN(val) BSP_FLD32(val, 0, 15)
|
||||
#define RPI_SPIDLEN_LEN_GET(reg) BSP_FLD32GET(reg, 0, 15)
|
||||
#define RPI_SPIDLEN_LEN_SET(reg, val) BSP_FLD32SET(reg, val, 0, 15)
|
||||
uint32_t spiltoh;
|
||||
#define RPI_SPILTOH_TOH(val) BSP_FLD32(val, 0, 3)
|
||||
#define RPI_SPILTOH_TOH_GET(reg) BSP_FLD32GET(reg, 0, 3)
|
||||
#define RPI_SPILTOH_TOH_SET(reg, val) BSP_FLD32SET(reg, val, 0, 3)
|
||||
uint32_t spidc;
|
||||
#define RPI_SPIDC_RPANIC(val) BSP_FLD32(val, 24, 31)
|
||||
#define RPI_SPIDC_RPANIC_GET(reg) BSP_FLD32GET(reg, 24, 31)
|
||||
#define RPI_SPIDC_RPANIC_SET(reg, val) BSP_FLD32SET(reg, val, 24, 31)
|
||||
#define RPI_SPIDC_RDREQ(val) BSP_FLD32(val, 16, 23)
|
||||
#define RPI_SPIDC_RDREQ_GET(reg) BSP_FLD32GET(reg, 16, 23)
|
||||
#define RPI_SPIDC_RDREQ_SET(reg, val) BSP_FLD32SET(reg, val, 16, 23)
|
||||
#define RPI_SPIDC_TPANIC(val) BSP_FLD32(val, 8, 15)
|
||||
#define RPI_SPIDC_TPANIC_GET(reg) BSP_FLD32GET(reg, 8, 15)
|
||||
#define RPI_SPIDC_TPANIC_SET(reg, val) BSP_FLD32SET(reg, val, 8, 15)
|
||||
#define RPI_SPIDC_TDREQ(val) BSP_FLD32(val, 0, 7)
|
||||
#define RPI_SPIDC_TDREQ_GET(reg) BSP_FLD32GET(reg, 0, 7)
|
||||
#define RPI_SPIDC_TDREQ_SET(reg, val) BSP_FLD32SET(reg, val, 0, 7)
|
||||
} raspberrypi_spi;
|
||||
|
||||
typedef enum {
|
||||
raspberrypi_SPI0,
|
||||
raspberrypi_SPI3,
|
||||
raspberrypi_SPI4,
|
||||
raspberrypi_SPI5,
|
||||
raspberrypi_SPI6
|
||||
} raspberrypi_spi_device;
|
||||
|
||||
/**
|
||||
* @brief Register a spi device.
|
||||
*
|
||||
* @param device The optional devices are raspberrypi_SPI0, raspberrypi_SPI3,
|
||||
* raspberrypi_SPI4, raspberrypi_SPI5, raspberrypi_SPI6.
|
||||
*
|
||||
* @retval RTEMS_SUCCESSFUL Successfully registered SPI device.
|
||||
* @retval RTEMS_INVALID_NUMBER This status code indicates that a specified
|
||||
* number was invalid.
|
||||
* @retval RTEMS_UNSATISFIED This status code indicates that the request was
|
||||
* not satisfied.
|
||||
*/
|
||||
rtems_status_code raspberrypi_spi_init(raspberrypi_spi_device device);
|
||||
|
||||
#endif /* LIBBSP_AARCH64_RASPBERRYPI_4_SPI_H */
|
||||
@@ -237,6 +237,20 @@
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name SPI Registers
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
#define BCM2711_SPI0_BASE (RPI_PERIPHERAL_BASE + 0x204000)
|
||||
#define BCM2711_SPI3_BASE (RPI_PERIPHERAL_BASE + 0x204600)
|
||||
#define BCM2711_SPI4_BASE (RPI_PERIPHERAL_BASE + 0x204800)
|
||||
#define BCM2711_SPI5_BASE (RPI_PERIPHERAL_BASE + 0x204A00)
|
||||
#define BCM2711_SPI6_BASE (RPI_PERIPHERAL_BASE + 0x204C00)
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Mailbox Registers
|
||||
*
|
||||
|
||||
582
bsps/aarch64/raspberrypi/spi/raspberrypi-spi.c
Normal file
582
bsps/aarch64/raspberrypi/spi/raspberrypi-spi.c
Normal file
@@ -0,0 +1,582 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @ingroup RTEMSBSPsAArch64Raspberrypi4
|
||||
*
|
||||
* @brief SPI Driver
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2024 Ning Yang
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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 OWNER 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.
|
||||
*/
|
||||
|
||||
#include <bsp/irq.h>
|
||||
#include <bsp/raspberrypi.h>
|
||||
#include <bsp/raspberrypi-spi.h>
|
||||
#include <bsp/rpi-gpio.h>
|
||||
|
||||
#include <dev/spi/spi.h>
|
||||
#include <bspopts.h>
|
||||
|
||||
typedef struct {
|
||||
spi_bus base;
|
||||
volatile raspberrypi_spi *regs;
|
||||
const spi_ioc_transfer *msg;
|
||||
uint32_t msg_todo;
|
||||
uint8_t *rx_buf;
|
||||
const uint8_t *tx_buf;
|
||||
uint32_t todo;
|
||||
uint8_t num_cs;
|
||||
uint32_t in_transfer;
|
||||
rtems_id task_id;
|
||||
rtems_vector_number irq;
|
||||
}raspberrypi_spi_bus;
|
||||
|
||||
static int raspberrypi_spi_check_msg(
|
||||
raspberrypi_spi_bus *bus,
|
||||
const spi_ioc_transfer *msg,
|
||||
uint32_t n
|
||||
)
|
||||
{
|
||||
while (n > 0) {
|
||||
if (msg->bits_per_word != 8) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((msg->mode &
|
||||
~(SPI_CPHA | SPI_CPOL | SPI_NO_CS)) != 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (msg->cs >= bus->num_cs) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
++msg;
|
||||
--n;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Calculates a clock divider to be used with the GPU core clock rate
|
||||
* to set a SPI clock rate the closest (<=) to a desired frequency. */
|
||||
static rtems_status_code rpi_spi_calculate_clock_divider(
|
||||
uint32_t clock_hz,
|
||||
uint16_t *clock_divider
|
||||
)
|
||||
{
|
||||
uint16_t divider;
|
||||
uint32_t clock_rate;
|
||||
|
||||
/* Calculates an initial clock divider. */
|
||||
divider = GPU_CORE_CLOCK_RATE / clock_hz;
|
||||
|
||||
/* Because the divider must be a power of two (as per the BCM2835 datasheet),
|
||||
* calculate the next greater power of two. */
|
||||
--divider;
|
||||
|
||||
divider |= (divider >> 1);
|
||||
divider |= (divider >> 2);
|
||||
divider |= (divider >> 4);
|
||||
divider |= (divider >> 8);
|
||||
|
||||
++divider;
|
||||
|
||||
clock_rate = GPU_CORE_CLOCK_RATE / divider;
|
||||
|
||||
/* If the resulting clock rate is greater than the desired frequency,
|
||||
* try the next greater power of two divider. */
|
||||
while (clock_rate > clock_hz) {
|
||||
divider = (divider << 1);
|
||||
|
||||
clock_rate = GPU_CORE_CLOCK_RATE / divider;
|
||||
}
|
||||
|
||||
*clock_divider = divider;
|
||||
|
||||
return RTEMS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static int raspberrypi_spi_config(
|
||||
raspberrypi_spi_bus *bus,
|
||||
volatile raspberrypi_spi *regs,
|
||||
uint32_t speed_hz,
|
||||
uint32_t mode,
|
||||
uint8_t cs
|
||||
)
|
||||
{
|
||||
spi_bus *base = &bus->base;
|
||||
uint32_t spics = regs->spics;
|
||||
rtems_status_code sc;
|
||||
uint16_t clock_divider;
|
||||
|
||||
/* Calculate the most appropriate clock divider. */
|
||||
sc = rpi_spi_calculate_clock_divider(speed_hz, &clock_divider);
|
||||
if (sc != RTEMS_SUCCESSFUL) {
|
||||
return sc;
|
||||
}
|
||||
/* Set the bus clock divider. */
|
||||
regs->spiclk = RPI_SPICLK_CDIV_SET(regs->spiclk, clock_divider);
|
||||
|
||||
if ((mode & SPI_CPHA) != 0) {
|
||||
spics |= RPI_SPICS_CPHA;
|
||||
} else {
|
||||
spics &= ~RPI_SPICS_CPHA;
|
||||
}
|
||||
|
||||
if ((mode & SPI_CPOL) != 0) {
|
||||
spics |= RPI_SPICS_CPOL;
|
||||
} else {
|
||||
spics &= ~RPI_SPICS_CPOL;
|
||||
}
|
||||
|
||||
if ((mode & SPI_CS_HIGH) != 0) {
|
||||
spics |= RPI_SPICS_CSPOL;
|
||||
} else {
|
||||
spics &= ~RPI_SPICS_CSPOL;
|
||||
}
|
||||
|
||||
spics = RPI_SPICS_CS_SET(spics, cs);
|
||||
|
||||
regs->spics = spics;
|
||||
|
||||
base->speed_hz = speed_hz;
|
||||
base->mode = mode;
|
||||
base->cs = cs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef BSP_SPI_USE_INTERRUPTS
|
||||
static void raspberrypi_spi_done(raspberrypi_spi_bus *bus)
|
||||
{
|
||||
volatile raspberrypi_spi *regs;
|
||||
regs = bus->regs;
|
||||
regs->spics = regs->spics & ~RPI_SPICS_TA;
|
||||
rtems_event_transient_send(bus->task_id);
|
||||
}
|
||||
|
||||
static bool raspberrpi_spi_TX_FULL(volatile raspberrypi_spi *regs)
|
||||
{
|
||||
return !(regs->spics & RPI_SPICS_TXD);
|
||||
}
|
||||
|
||||
static void raspberrypi_spi_push(
|
||||
raspberrypi_spi_bus *bus,
|
||||
volatile raspberrypi_spi *regs
|
||||
)
|
||||
{
|
||||
uint8_t val;
|
||||
while (bus->todo > 0 && !raspberrpi_spi_TX_FULL(regs)) {
|
||||
val = 0;
|
||||
if (bus->tx_buf != NULL) {
|
||||
val = *bus->tx_buf;
|
||||
++bus->tx_buf;
|
||||
}
|
||||
|
||||
--bus->todo;
|
||||
regs->spififo = val;
|
||||
++bus->in_transfer;
|
||||
}
|
||||
}
|
||||
|
||||
static void raspberrypi_spi_next_msg(raspberrypi_spi_bus *bus)
|
||||
{
|
||||
const spi_ioc_transfer *msg;
|
||||
spi_bus *base;
|
||||
volatile raspberrypi_spi *regs;
|
||||
regs=bus->regs;
|
||||
|
||||
if (bus->msg_todo > 0) {
|
||||
base = &bus->base;
|
||||
msg = bus->msg;
|
||||
|
||||
if (
|
||||
msg->speed_hz != base->speed_hz
|
||||
|| msg->mode != base->mode
|
||||
|| msg->cs != base->cs
|
||||
) {
|
||||
raspberrypi_spi_config(
|
||||
bus,
|
||||
regs,
|
||||
msg->speed_hz,
|
||||
msg->mode,
|
||||
msg->cs
|
||||
);
|
||||
}
|
||||
|
||||
bus->todo = msg->len;
|
||||
bus->rx_buf = msg->rx_buf;
|
||||
bus->tx_buf = msg->tx_buf;
|
||||
raspberrypi_spi_push(bus, regs);
|
||||
} else {
|
||||
raspberrypi_spi_done(bus);
|
||||
}
|
||||
}
|
||||
|
||||
static void raspberrypi_spi_start(raspberrypi_spi_bus *bus)
|
||||
{
|
||||
volatile raspberrypi_spi *regs;
|
||||
regs = bus->regs;
|
||||
|
||||
regs->spics = regs->spics | RPI_SPICS_INTR | RPI_SPICS_INTD;
|
||||
/*
|
||||
* Set TA = 1. This will immediately trigger a first interrupt with
|
||||
* DONE = 1.
|
||||
*/
|
||||
regs->spics = regs->spics | RPI_SPICS_TA;
|
||||
}
|
||||
|
||||
static bool raspberrypi_spi_irq(volatile raspberrypi_spi *regs)
|
||||
{
|
||||
/* Check whether the interrupt is generated by this SPI device */
|
||||
if(regs->spics & RPI_SPICS_INTD && regs->spics & RPI_SPICS_DONE) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(regs->spics & RPI_SPICS_INTR && regs->spics & RPI_SPICS_RXR) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void raspberrypi_spi_interrupt(void *arg)
|
||||
{
|
||||
raspberrypi_spi_bus *bus;
|
||||
volatile raspberrypi_spi *regs;
|
||||
uint32_t val;
|
||||
|
||||
bus = arg;
|
||||
regs = bus->regs;
|
||||
|
||||
if (raspberrypi_spi_irq(regs)) {
|
||||
|
||||
if (bus->todo > 0) {
|
||||
raspberrypi_spi_push(bus, regs);
|
||||
} else {
|
||||
--bus->msg_todo;
|
||||
++bus->msg;
|
||||
raspberrypi_spi_next_msg(bus);
|
||||
}
|
||||
|
||||
while (regs->spics & RPI_SPICS_RXD && bus->in_transfer > 0) {
|
||||
/* RX FIFO contains at least 1 byte. */
|
||||
val = regs->spififo;
|
||||
if (bus->rx_buf != NULL) {
|
||||
*bus->rx_buf = (uint8_t)val;
|
||||
++bus->rx_buf;
|
||||
}
|
||||
--bus->in_transfer;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void raspberrypi_spi_polling_tx_rx(raspberrypi_spi_bus *bus)
|
||||
{
|
||||
volatile raspberrypi_spi *regs = bus->regs;
|
||||
|
||||
const unsigned char *sbuffer = bus->tx_buf;
|
||||
unsigned char *rbuffer;
|
||||
unsigned int size;
|
||||
unsigned int read_count, write_count;
|
||||
unsigned int data;
|
||||
|
||||
while (bus->msg_todo) {
|
||||
rbuffer = bus->rx_buf;
|
||||
size = bus->todo;
|
||||
|
||||
regs->spics = regs->spics | RPI_SPICS_CLEAR_RX | RPI_SPICS_CLEAR_TX
|
||||
| RPI_SPICS_TA;
|
||||
|
||||
read_count = 0;
|
||||
write_count = 0;
|
||||
|
||||
while (read_count < size || write_count < size) {
|
||||
if (write_count < size && regs->spics & RPI_SPICS_TXD) {
|
||||
if (sbuffer) {
|
||||
regs->spififo = *sbuffer++;
|
||||
} else {
|
||||
regs->spififo = 0;
|
||||
}
|
||||
|
||||
write_count++;
|
||||
}
|
||||
|
||||
if (read_count < size && regs->spics & RPI_SPICS_RXD) {
|
||||
data = regs->spififo;
|
||||
|
||||
if (rbuffer) {
|
||||
*rbuffer++ = data;
|
||||
}
|
||||
|
||||
read_count++;
|
||||
}
|
||||
}
|
||||
|
||||
while (!(regs->spics & RPI_SPICS_DONE)) {
|
||||
/*wait*/
|
||||
}
|
||||
regs->spics = (regs->spics & ~RPI_SPICS_TA);
|
||||
|
||||
bus->msg_todo--;
|
||||
|
||||
bus->msg++;
|
||||
bus->rx_buf = bus->msg->rx_buf;
|
||||
bus->tx_buf = bus->msg->tx_buf;
|
||||
bus->todo = bus->msg->len;
|
||||
}
|
||||
}
|
||||
|
||||
static void raspberrypi_spi_transfer_msg(
|
||||
raspberrypi_spi_bus *bus
|
||||
)
|
||||
{
|
||||
volatile raspberrypi_spi *regs = bus->regs;
|
||||
uint32_t msg_todo = bus->msg_todo;
|
||||
const spi_ioc_transfer *msg = bus->msg;
|
||||
|
||||
if (msg_todo > 0) {
|
||||
if (
|
||||
msg->speed_hz != bus->base.speed_hz
|
||||
|| msg->mode != bus->base.mode
|
||||
|| msg->cs != bus->base.cs
|
||||
) {
|
||||
raspberrypi_spi_config(
|
||||
bus,
|
||||
regs,
|
||||
msg->speed_hz,
|
||||
msg->mode,
|
||||
msg->cs
|
||||
);
|
||||
}
|
||||
|
||||
bus->todo = msg->len;
|
||||
bus->rx_buf = msg->rx_buf;
|
||||
bus->tx_buf = msg->tx_buf;
|
||||
raspberrypi_spi_polling_tx_rx(bus);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static int raspberrypi_spi_transfer(
|
||||
spi_bus *base,
|
||||
const spi_ioc_transfer *msgs,
|
||||
uint32_t msg_count
|
||||
)
|
||||
{
|
||||
int rv = 0;
|
||||
raspberrypi_spi_bus *bus;
|
||||
bus = (raspberrypi_spi_bus *) base;
|
||||
|
||||
rv = raspberrypi_spi_check_msg(bus, msgs, msg_count);
|
||||
if (rv == 0) {
|
||||
bus->msg_todo = msg_count;
|
||||
bus->msg = msgs;
|
||||
#ifdef BSP_SPI_USE_INTERRUPTS
|
||||
bus->task_id = rtems_task_self();
|
||||
|
||||
raspberrypi_spi_start(bus);
|
||||
rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
|
||||
#else
|
||||
raspberrypi_spi_transfer_msg(bus);
|
||||
#endif
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void raspberrypi_spi_destroy(spi_bus *base)
|
||||
{
|
||||
raspberrypi_spi_bus *bus;
|
||||
bus = (raspberrypi_spi_bus *) base;
|
||||
|
||||
#ifdef BSP_SPI_USE_INTERRUPTS
|
||||
rtems_interrupt_handler_remove(
|
||||
bus->irq,
|
||||
raspberrypi_spi_interrupt,
|
||||
bus
|
||||
);
|
||||
#endif
|
||||
|
||||
spi_bus_destroy_and_free(&bus->base);
|
||||
}
|
||||
|
||||
static int raspberrypi_spi_setup(spi_bus *base)
|
||||
{
|
||||
raspberrypi_spi_bus *bus;
|
||||
uint32_t mode = base->mode;
|
||||
|
||||
bus = (raspberrypi_spi_bus *) base;
|
||||
|
||||
if (mode & SPI_LOOP) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return raspberrypi_spi_config(
|
||||
bus,
|
||||
bus->regs,
|
||||
bus->base.speed_hz,
|
||||
bus->base.mode,
|
||||
bus->base.cs
|
||||
);
|
||||
}
|
||||
|
||||
static rtems_status_code raspberrypi_spi_init_gpio(
|
||||
raspberrypi_spi_device device
|
||||
)
|
||||
{
|
||||
switch (device) {
|
||||
case raspberrypi_SPI0:
|
||||
raspberrypi_gpio_set_function(7, GPIO_AF0); /* CS1 */
|
||||
raspberrypi_gpio_set_pull(7, GPIO_PULL_NONE);
|
||||
raspberrypi_gpio_set_function(8, GPIO_AF0); /* CS0 */
|
||||
raspberrypi_gpio_set_pull(8, GPIO_PULL_NONE);
|
||||
raspberrypi_gpio_set_function(9, GPIO_AF0); /* MISO */
|
||||
raspberrypi_gpio_set_function(10, GPIO_AF0); /* MOSI */
|
||||
raspberrypi_gpio_set_function(11, GPIO_AF0); /* SCLK */
|
||||
break;
|
||||
case raspberrypi_SPI3:
|
||||
raspberrypi_gpio_set_function(24, GPIO_AF5);
|
||||
raspberrypi_gpio_set_pull(24, GPIO_PULL_NONE);
|
||||
raspberrypi_gpio_set_function(0, GPIO_AF3);
|
||||
raspberrypi_gpio_set_pull(0, GPIO_PULL_NONE);
|
||||
raspberrypi_gpio_set_function(1, GPIO_AF3);
|
||||
raspberrypi_gpio_set_function(2, GPIO_AF3);
|
||||
raspberrypi_gpio_set_function(3, GPIO_AF3);
|
||||
break;
|
||||
case raspberrypi_SPI4:
|
||||
raspberrypi_gpio_set_function(25, GPIO_AF5);
|
||||
raspberrypi_gpio_set_pull(25, GPIO_PULL_NONE);
|
||||
raspberrypi_gpio_set_function(4, GPIO_AF3);
|
||||
raspberrypi_gpio_set_pull(4, GPIO_PULL_NONE);
|
||||
raspberrypi_gpio_set_function(5, GPIO_AF3);
|
||||
raspberrypi_gpio_set_function(6, GPIO_AF3);
|
||||
raspberrypi_gpio_set_function(7, GPIO_AF3);
|
||||
break;
|
||||
case raspberrypi_SPI5:
|
||||
raspberrypi_gpio_set_function(26, GPIO_AF5);
|
||||
raspberrypi_gpio_set_pull(26, GPIO_PULL_NONE);
|
||||
raspberrypi_gpio_set_function(12, GPIO_AF3);
|
||||
raspberrypi_gpio_set_pull(12, GPIO_PULL_NONE);
|
||||
raspberrypi_gpio_set_function(13, GPIO_AF3);
|
||||
raspberrypi_gpio_set_function(14, GPIO_AF3);
|
||||
raspberrypi_gpio_set_function(15, GPIO_AF3);
|
||||
break;
|
||||
case raspberrypi_SPI6:
|
||||
raspberrypi_gpio_set_function(27, GPIO_AF5);
|
||||
raspberrypi_gpio_set_pull(27, GPIO_PULL_NONE);
|
||||
raspberrypi_gpio_set_function(18, GPIO_AF3);
|
||||
raspberrypi_gpio_set_pull(18, GPIO_PULL_NONE);
|
||||
raspberrypi_gpio_set_function(19, GPIO_AF3);
|
||||
raspberrypi_gpio_set_function(20, GPIO_AF3);
|
||||
raspberrypi_gpio_set_function(21, GPIO_AF3);
|
||||
break;
|
||||
default:
|
||||
return RTEMS_INVALID_NUMBER;
|
||||
break;
|
||||
}
|
||||
return RTEMS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
rtems_status_code raspberrypi_spi_init(raspberrypi_spi_device device)
|
||||
{
|
||||
raspberrypi_spi_bus *bus;
|
||||
int eno;
|
||||
volatile raspberrypi_spi *regs;
|
||||
const char *bus_path;
|
||||
|
||||
bus = (raspberrypi_spi_bus *) spi_bus_alloc_and_init(sizeof(*bus));
|
||||
if (bus == NULL) {
|
||||
return RTEMS_UNSATISFIED;
|
||||
}
|
||||
|
||||
switch (device) {
|
||||
case raspberrypi_SPI0:
|
||||
regs = (volatile raspberrypi_spi *) BCM2711_SPI0_BASE;
|
||||
bus_path = "/dev/spidev0";
|
||||
break;
|
||||
case raspberrypi_SPI3:
|
||||
regs = (volatile raspberrypi_spi *) BCM2711_SPI3_BASE;
|
||||
bus_path = "/dev/spidev3";
|
||||
break;
|
||||
case raspberrypi_SPI4:
|
||||
regs = (volatile raspberrypi_spi *) BCM2711_SPI4_BASE;
|
||||
bus_path = "/dev/spidev4";
|
||||
break;
|
||||
case raspberrypi_SPI5:
|
||||
regs = (volatile raspberrypi_spi *) BCM2711_SPI5_BASE;
|
||||
bus_path = "/dev/spidev5";
|
||||
break;
|
||||
case raspberrypi_SPI6:
|
||||
regs = (volatile raspberrypi_spi *) BCM2711_SPI6_BASE;
|
||||
bus_path = "/dev/spidev6";
|
||||
break;
|
||||
default:
|
||||
spi_bus_destroy_and_free(&bus->base);
|
||||
return RTEMS_INVALID_NUMBER;
|
||||
break;
|
||||
}
|
||||
|
||||
bus->regs = regs;
|
||||
bus->num_cs = 2;
|
||||
|
||||
bus->base.transfer = raspberrypi_spi_transfer;
|
||||
bus->base.destroy = raspberrypi_spi_destroy;
|
||||
bus->base.setup = raspberrypi_spi_setup;
|
||||
bus->base.bits_per_word = 8;
|
||||
bus->base.max_speed_hz = 250000000;
|
||||
bus->base.cs = 0;
|
||||
#ifdef BSP_SPI_USE_INTERRUPTS
|
||||
bus->irq = BCM2711_IRQ_SPI;
|
||||
|
||||
eno = rtems_interrupt_handler_install(
|
||||
bus->irq,
|
||||
"SPI",
|
||||
RTEMS_INTERRUPT_SHARED,
|
||||
raspberrypi_spi_interrupt,
|
||||
bus
|
||||
);
|
||||
if (eno != RTEMS_SUCCESSFUL) {
|
||||
return EAGAIN;
|
||||
}
|
||||
#endif
|
||||
|
||||
eno = spi_bus_register(&bus->base, bus_path);
|
||||
if (eno != 0) {
|
||||
spi_bus_destroy_and_free(&bus->base);
|
||||
return RTEMS_UNSATISFIED;
|
||||
}
|
||||
|
||||
eno = raspberrypi_spi_init_gpio(device);
|
||||
if (eno != 0) {
|
||||
spi_bus_destroy_and_free(&bus->base);
|
||||
return RTEMS_INVALID_NUMBER;
|
||||
}
|
||||
|
||||
return RTEMS_SUCCESSFUL;
|
||||
}
|
||||
Reference in New Issue
Block a user