pebble/src/fw/drivers/spi.h
2025-01-27 11:38:16 -08:00

188 lines
8 KiB
C

/*
* Copyright 2024 Google LLC
*
* Licensed 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.
*/
#pragma once
#include "board/board.h"
#include <stdint.h>
//
//! High level slave port interface
//! This part of the API can be used for fairly straightforward SPI interactions
//! The assertion and deassertion of the SCS line is automatic
//
//! Scatter Gather tx / rx information
typedef struct SPIScatterGather {
size_t sg_len; // number of bytes to tx and/or rx
const void *sg_out; //! may be NULL (0 padding sent)
void *sg_in; //! may be NULL (nothing saved)
} SPIScatterGather;
typedef bool (*SPIDMACompleteHandler)(const SPISlavePort *slave, void *context);
//! Write byte to slave port and return the corresponding received byte
uint8_t spi_slave_read_write(const SPISlavePort *slave, uint8_t out);
//! Write single data byte to the given slave port
void spi_slave_write(const SPISlavePort *slave, uint8_t out);
//! Read a burst of bytes from given slave port (asserts SCS)
//! 0 bytes are sent to the slave port to prompt the incoming bytes
void spi_slave_burst_read(const SPISlavePort *slave, void *in, size_t len);
//! Write a burst of bytes to the given slave port (asserts SCS)
//! No data is received or waited for
void spi_slave_burst_write(const SPISlavePort *slave, const void *out, size_t len);
//! Transmit and Receive data bytes to and from the SPI Slave Port
//! If out is NULL then 0s are transmitted, if in is NULL incoming data is not saved
void spi_slave_burst_read_write(const SPISlavePort *slave, const void *out, void *in, size_t len);
//! Transmit and Receive data bytes to and from the SPI Slave Port (scatter gather)
void spi_slave_burst_read_write_scatter(const SPISlavePort *slave,
const SPIScatterGather *sc_info,
size_t num_sg);
//! Set (or change) the clock frequency for the given SPI Slave Port (Hz)
void spi_slave_set_frequency(const SPISlavePort *slave, uint32_t frequency_hz);
//! Wait until the SPI Slave is idle
void spi_slave_wait_until_idle_blocking(const SPISlavePort *slave);
//! Gets the peripheral data register address for setting up DMA
uint32_t spi_get_dma_base_address(const SPISlavePort *slave);
//
//! Low level slave port interface
//! This part of the API can be used for slightly more complex SPI operations
//! (such as piecemeal reads or writes). Assertion and deassertion of SCS
//! is up to the caller. Asserts in the code will help to ensure that the
//! API is used correctly.
//
//! The general use case for the _ll_ functions:
//!
//! acquire the Slave Port (start peripheral clock and enable SPI)
//! spi_ll_slave_acquire(SLAVE);
//! spi_ll_slave_scs_assert(SLAVE);
//! perform (multiple) reads and writes as required
//! spi_ll_XXXX(SLAVE, ...);
//! spi_ll_XXXX(SLAVE, ...);
//! spi_ll_XXXX(SLAVE, ...);
//! release the Slave Port (stop peripheral clock and disable SPI)
//! spi_ll_slave_scs_deassert(SLAVE);
//! spi_ll_slave_release(SLAVE);
//!
//! Using the _ll_ routines it is also possible to perform slightly
//! odd SPI transactions such as transmitting while SCS is not asserted
//! or starting/stopping the clock while the SCS is asserted.
//! These routines can also be used to adjust for timing requirements
//! that some devices have.
//! Acquire the SPI device for use by spi_ll_XXXX functions
//! All spi_ll_XXXX have asserts to check for acquisition
//! Note: does not guarantee exclusivity for right now
//! but could easily do one day if we ever share a SPI
//! bus between multiple Slave Ports
void spi_ll_slave_acquire(const SPISlavePort *slave);
//! Release the SPI Slave Port
void spi_ll_slave_release(const SPISlavePort *slave);
//! Assert the SCS for the given SPI Slave Port
void spi_ll_slave_scs_assert(const SPISlavePort *slave);
//! Deassert the SCS for the given SPI slave port
void spi_ll_slave_scs_deassert(const SPISlavePort *slave);
//! Write byte to slave port and return the corresponding received byte
//! It is up to the caller to ensure SCS is asserted correctly
uint8_t spi_ll_slave_read_write(const SPISlavePort *slave, uint8_t out);
//! Write single data byte to the given slave port
//! It is up to the caller to ensure SCS is asserted correctly
void spi_ll_slave_write(const SPISlavePort *slave, uint8_t out);
//! Read a burst of bytes from given slave port
//! 0 bytes are sent to the slave port to prompt the incoming bytes
//! It is up to the caller to ensure SCS is asserted correctly
void spi_ll_slave_burst_read(const SPISlavePort *slave, void *in, size_t len);
//! Write a burst of bytes to the given slave port
//! No data is received or waited for
//! It is up to the caller to ensure SCS is asserted correctly
void spi_ll_slave_burst_write(const SPISlavePort *slave, const void *out, size_t len);
//! Transmit and Receive data bytes to and from the SPI Slave Port
//! If out is NULL then 0s are transmitted, if in is NULL incoming data is not saved
//! It is up to the caller to ensure SCS is asserted correctly
void spi_ll_slave_burst_read_write(const SPISlavePort *slave,
const void *out,
void *in,
size_t len);
//! Transmit and Receive data bytes to and from the SPI Slave Port (scatter gather)
//! It is up to the caller to ensure SCS is asserted correctly
void spi_ll_slave_burst_read_write_scatter(const SPISlavePort *slave,
const SPIScatterGather *sc_info,
size_t num_sg);
//! Reads data from the given slave port via DMA
void spi_ll_slave_read_dma_start(const SPISlavePort *slave, void *in, size_t len,
SPIDMACompleteHandler handler, void *context);
//! Stops the read DMA on the given slave port
void spi_ll_slave_read_dma_stop(const SPISlavePort *slave);
//! Write data to the given slave port via DMA
void spi_ll_slave_write_dma_start(const SPISlavePort *slave, const void *out, size_t len,
SPIDMACompleteHandler handler, void *context);
//! Stops the write DMA on the given slave port
void spi_ll_slave_write_dma_stop(const SPISlavePort *slave);
//! Sends and receives data via DMA on the given slave port. If out is NULL, 0s are transmitted. If
//! in is NULL, incoming data is not saved.
void spi_ll_slave_read_write_dma_start(const SPISlavePort *slave, const void *out, void *in,
size_t len, SPIDMACompleteHandler handler, void *context);
//! Stops the read + write DMA on the given slave port
void spi_ll_slave_read_write_dma_stop(const SPISlavePort *slave);
//! Checks whether a DMA operation is in progress on the given slave port
bool spi_ll_slave_dma_in_progress(const SPISlavePort *slave);
//! The state of the SPI clock lines between transactions is unspecified. Some devices
//! expect the clock to be in a certain state. This routine will drive the line low when
//! enable is true and reconfigure it as a SPI CLK line when false
void spi_ll_slave_drive_clock(const SPISlavePort *slave, bool enable);
//! Clears any errors which may be set
void spi_ll_slave_clear_errors(const SPISlavePort *slave);
//! Initialize a single SPI device instance. Must be called before first use
void spi_slave_port_init(SPISlavePort *device);
//! Deinitializes the SPI device
void spi_slave_port_deinit(const SPISlavePort *slave);
// REVISIT:
// This prototype is used by the roll-your-own SPI drivers.
// It (and the definition) can go away once the new driver
// API is adopted universally.
uint16_t spi_find_prescaler(uint32_t bus_frequency, SpiPeriphClock periph_clock);