mbed-os/targets/TARGET_Ambiq_Micro/TARGET_Apollo3/device/spi_api.c

257 lines
6.7 KiB
C

/*
* Copyright (c) 2020 SparkFun Electronics
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#if DEVICE_SPI
#include "spi_api.h"
#include "iom_api.h"
#include "PeripheralPins.h"
#include "mbed_assert.h"
#include <string.h>
#define DEFAULT_CLK_FREQ (4000000)
#define DEFAULT_SPI_MODE (AM_HAL_IOM_SPI_MODE_0)
static am_hal_iom_transfer_t xfer = {0};
SPIName spi_get_peripheral_name(PinName mosi, PinName miso, PinName sclk)
{
uint32_t iom_mosi = pinmap_peripheral(mosi, spi_master_mosi_pinmap());
uint32_t iom_miso = pinmap_peripheral(miso, spi_master_miso_pinmap());
uint32_t iom_sclk = pinmap_peripheral(sclk, spi_master_clk_pinmap());
uint32_t iom;
if (miso == NC) {
iom = pinmap_merge(iom_mosi, iom_sclk);
} else if (mosi == NC) {
iom = pinmap_merge(iom_miso, iom_sclk);
} else {
uint32_t iom_data = pinmap_merge(iom_mosi, iom_miso);
iom = pinmap_merge(iom_data, iom_sclk);
}
if ((int)iom == NC) {
return IOM_NUM;
}
return (SPIName)iom;
}
void spi_get_capabilities(PinName ssel, bool slave, spi_capabilities_t *cap)
{
MBED_ASSERT(cap);
SPIName iom_ssel = (SPIName)pinmap_peripheral(ssel, spi_master_cs_pinmap());
cap->minimum_frequency = 0;
cap->maximum_frequency = AM_HAL_IOM_MAX_FREQ;
cap->word_length = 0x00000080;
cap->slave_delay_between_symbols_ns = 0;
cap->clk_modes = 0x0F;
cap->support_slave_mode = (iom_ssel == IOM_ANY) ? true : false;
cap->hw_cs_handle = false;
cap->async_mode = false;
cap->tx_rx_buffers_equal_length = false;
}
void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel)
{
MBED_ASSERT(obj);
MBED_ASSERT((int)ssel == NC);
// iom determination
SPIName iom = spi_get_peripheral_name(mosi, miso, sclk);
MBED_ASSERT((int)iom != IOM_NUM);
MBED_ASSERT((int)iom != IOM_ANY);
// iom configuration
obj->spi.iom_obj.iom.inst = (uint32_t)iom;
obj->spi.iom_obj.iom.cfg.eInterfaceMode = AM_HAL_IOM_SPI_MODE;
obj->spi.iom_obj.iom.cfg.ui32ClockFreq = DEFAULT_CLK_FREQ;
obj->spi.iom_obj.iom.cfg.eSpiMode = DEFAULT_SPI_MODE;
obj->spi.iom_obj.iom.cfg.pNBTxnBuf = NULL;
obj->spi.iom_obj.iom.cfg.ui32NBTxnBufLength = 0;
// invariant xfer settings
xfer.ui32InstrLen = 0;
xfer.ui32Instr = 0;
xfer.bContinue = false;
xfer.ui8RepeatCount = 0;
xfer.ui8Priority = 1;
xfer.ui32PauseCondition = 0;
xfer.ui32StatusSetClr = 0;
// pin configuration
pinmap_config(sclk, spi_master_clk_pinmap());
if ((int)mosi != NC) {
pinmap_config(mosi, spi_master_mosi_pinmap());
}
if ((int)miso != NC) {
pinmap_config(miso, spi_master_miso_pinmap());
}
if ((int)ssel != NC) {
pinmap_config(ssel, spi_master_cs_pinmap());
}
// initialization
iom_init(&obj->spi.iom_obj);
}
void spi_free(spi_t *obj)
{
iom_deinit(&obj->spi.iom_obj);
}
void spi_format(spi_t *obj, int bits, int mode, int slave)
{
MBED_ASSERT(obj);
obj->spi.iom_obj.iom.cfg.eSpiMode = (am_hal_iom_spi_mode_e)mode;
iom_init(&obj->spi.iom_obj);
}
void spi_frequency(spi_t *obj, int hz)
{
MBED_ASSERT(obj);
obj->spi.iom_obj.iom.cfg.ui32ClockFreq = (uint32_t)hz;
iom_init(&obj->spi.iom_obj);
}
int spi_master_write(spi_t *obj, int value)
{
uint32_t rxval = 0;
spi_master_block_write(obj, (const char *)&value, 1, (char *)&rxval, 1, SPI_FILL_CHAR);
return rxval;
}
int spi_master_block_write(spi_t *obj, const char *tx_buffer, int tx_length, char *rx_buffer, int rx_length, char write_fill)
{
MBED_ASSERT(obj);
int chars_handled = 0;
uint32_t status = AM_HAL_STATUS_SUCCESS;
// always perform a duplex xfer
xfer.eDirection = AM_HAL_IOM_FULLDUPLEX;
if (tx_length == rx_length) {
xfer.pui32RxBuffer = (uint32_t *)rx_buffer;
xfer.pui32TxBuffer = (uint32_t *)tx_buffer;
xfer.ui32NumBytes = tx_length;
status = am_hal_iom_spi_blocking_fullduplex(obj->spi.iom_obj.iom.handle, &xfer);
}
// handle difference between buffers
else if (tx_length < rx_length) {
xfer.pui32RxBuffer = (uint32_t *)rx_buffer;
xfer.ui32NumBytes = rx_length - tx_length;
uint8_t fill[xfer.ui32NumBytes];
memset(fill, write_fill, xfer.ui32NumBytes);
xfer.pui32TxBuffer = (uint32_t *)&fill;
status = am_hal_iom_spi_blocking_fullduplex(obj->spi.iom_obj.iom.handle, &xfer);
}
else {
xfer.pui32TxBuffer = (uint32_t *)tx_buffer;
xfer.ui32NumBytes = tx_length - rx_length;
uint8_t fill[xfer.ui32NumBytes];
memset(fill, write_fill, xfer.ui32NumBytes);
xfer.pui32RxBuffer = (uint32_t *)&fill;
status = am_hal_iom_spi_blocking_fullduplex(obj->spi.iom_obj.iom.handle, &xfer);
}
if (AM_HAL_STATUS_SUCCESS != status) {
return 0;
}
chars_handled += xfer.ui32NumBytes;
return chars_handled;
}
int spi_slave_receive(spi_t *obj)
{
MBED_ASSERT(0);
return 0;
}
int spi_slave_read(spi_t *obj)
{
MBED_ASSERT(0);
return 0;
}
void spi_slave_write(spi_t *obj, int value)
{
MBED_ASSERT(0);
}
int spi_busy(spi_t *obj)
{
MBED_ASSERT(0);
return 0;
}
const PinMap *spi_master_mosi_pinmap()
{
return PinMap_SPI_MOSI;
}
const PinMap *spi_master_miso_pinmap()
{
return PinMap_SPI_MISO;
}
const PinMap *spi_master_clk_pinmap()
{
return PinMap_SPI_SCLK;
}
const PinMap *spi_master_cs_pinmap()
{
return PinMap_SPI_SSEL;
}
const PinMap *spi_slave_mosi_pinmap()
{
return PinMap_SPI_MOSI;
}
const PinMap *spi_slave_miso_pinmap()
{
return PinMap_SPI_MISO;
}
const PinMap *spi_slave_clk_pinmap()
{
return PinMap_SPI_SCLK;
}
const PinMap *spi_slave_cs_pinmap()
{
return PinMap_SPI_SSEL;
}
#endif // DEVICE_SPI