mbed-os/targets/TARGET_ARM_SSG/TARGET_BEETLE/spi_api.c

287 lines
8.1 KiB
C

/* mbed Microcontroller Library
* Copyright (c) 2015 ARM Limited
*
* 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.
*/
#include <math.h>
#include "spi_api.h"
#include "spi_def.h"
#include "cmsis.h"
#include "pinmap.h"
#include "mbed_error.h"
#include "mbed_wait_api.h"
/*
* Driver private data structure that should not be shared by multiple
* instances of the driver (same driver for multiple instances of the IP)
*/
typedef struct {
uint32_t size; /* size of an SPI frame in bits: can be 8 or 16 */
} private_spi_t;
static const PinMap PinMap_SPI_SCLK[] = {
{SHIELD_SPI_SCK , SPI_0, 0},
{ADC_SPI_SCK , SPI_1, 0},
{NC, NC, 0}
};
static const PinMap PinMap_SPI_MOSI[] = {
{SHIELD_SPI_MOSI, SPI_0, 0},
{ADC_SPI_MOSI, SPI_1, 0},
{NC, NC, 0}
};
static const PinMap PinMap_SPI_MISO[] = {
{SHIELD_SPI_MISO, SPI_0, 0},
{ADC_SPI_MISO, SPI_1, 0},
{NC, NC, 0}
};
static const PinMap PinMap_SPI_SSEL[] = {
{SHIELD_SPI_nCS, SPI_0, 0},
{ADC_SPI_nCS, SPI_1, 0},
{NC, NC, 0}
};
/*
* Retrieve the private data of the instance related to a given IP
*/
static private_spi_t* get_spi_private(spi_t *obj) {
static private_spi_t data0, data1;
/*
* Select which instance to give using the base
* address of registers
*/
switch ((intptr_t)obj->spi) {
case SPI0_BASE:
return &data0;
case SPI1_BASE:
return &data1;
default:
error("SPI driver private data structure not found for this registers base address");
return (void*)0;
}
}
void spi_init(spi_t *obj, PinName mosi,
PinName miso, PinName sclk, PinName ssel) {
// determine the SPI to use
SPIName spi_mosi = (SPIName)pinmap_peripheral(mosi, PinMap_SPI_MOSI);
SPIName spi_miso = (SPIName)pinmap_peripheral(miso, PinMap_SPI_MISO);
SPIName spi_sclk = (SPIName)pinmap_peripheral(sclk, PinMap_SPI_SCLK);
SPIName spi_ssel = (SPIName)pinmap_peripheral(ssel, PinMap_SPI_SSEL);
SPIName spi_data = (SPIName)pinmap_merge(spi_mosi, spi_miso);
SPIName spi_cntl = (SPIName)pinmap_merge(spi_sclk, spi_ssel);
obj->spi = (SPI_TypeDef*)pinmap_merge(spi_data, spi_cntl);
if ((int)obj->spi == NC) {
error("SPI pinout mapping failed");
}
/* Set default format and frequency */
if (ssel == NC) {
spi_format(obj, 8, 0, 0); // 8 bits, mode SPI_MSB, master
} else {
spi_format(obj, 8, 0, 1); // 8 bits, mode SPI_LSB, slave
}
spi_frequency(obj, 1562500);
/* Pin out the spi pins */
pinmap_pinout(mosi, PinMap_SPI_MOSI);
pinmap_pinout(miso, PinMap_SPI_MISO);
pinmap_pinout(sclk, PinMap_SPI_SCLK);
if (ssel != NC) {
pinmap_pinout(ssel, PinMap_SPI_SSEL);
}
/*
* Set desired enabled IRQs:
* MF: Mode Fail
* TF: TX FIFO Full
* TNF: TX FIFO Not Full
* RNE: RX FIFO Not Empty
*/
uint32_t irqs = (IRQ_ENABLE_MFE | IRQ_ENABLE_TFE
| IRQ_ENABLE_TNFE | IRQ_ENABLE_RNEE);
/*
* Enable:
* - Master mode
* - Manual start mode
* - Manual chip select
* - Peripheral select decode
*/
obj->spi->CONFIG |= (CONFIG_MSEL | CONFIG_MSE
/*| CONFIG_MCSE | CONFIG_PSD*/);
/* Set all peripheral select lines high - these should be unused */
obj->spi->CONFIG |= 0x00000; //CONFIG_PCSL;
obj->spi->IRQ_ENABLE = irqs;
obj->spi->IRQ_DISABLE = ~irqs;
obj->spi->SPI_ENABLE |= SPI_ENABLE_SPIE;
}
void spi_free(spi_t *obj) {
}
void spi_format(spi_t *obj, int bits, int mode, int slave) {
private_spi_t *private_spi = get_spi_private(obj);
obj->spi->SPI_ENABLE &= ~SPI_ENABLE_SPIE;
/*
* The mbed API specifies 'bits' as being 4-16 per frame. This
* controller supports only 8 or 16 bit frames. Therefore we will
* assume 8 bits and, if anything larger is specified, we will use
* 16 bits.
*/
obj->spi->CONFIG &= ~CONFIG_TWS; /* 00 = 8 bit frame */
private_spi->size = 8;
if (bits > 8) {
switch (bits) {
case 16:
private_spi->size = 16;
break;
default:
obj->spi->CONFIG |= CONFIG_TWS_1; /* 01 = 16 bit frame */
break;
}
}
switch (mode) {
default:
case 0:
obj->spi->CONFIG &= ~CONFIG_CPOL;
obj->spi->CONFIG &= ~CONFIG_CPHA;
break;
case 1:
obj->spi->CONFIG &= ~CONFIG_CPOL;
obj->spi->CONFIG |= CONFIG_CPHA;
break;
case 2:
obj->spi->CONFIG |= CONFIG_CPOL;
obj->spi->CONFIG &= ~CONFIG_CPHA;
break;
case 3:
obj->spi->CONFIG |= CONFIG_CPOL;
obj->spi->CONFIG |= CONFIG_CPHA;
break;
}
obj->spi->SPI_ENABLE |= SPI_ENABLE_SPIE;
}
void spi_frequency(spi_t *obj, int hz) {
/*
* Valid frequencies are derived from a 25MHz peripheral clock.
* Frequency | Divisor | MBRD Value | Hz
* 12.0 MHz 2 000 12000000
* 6.0 MHz 4 001 6000000
* 3.0 MHz 8 010 3000000
* 1.5 MHz 16 011 1500000
* 750.0 KHz 32 100 750000
* 375.0 KHz 64 101 375000
* 187.500 KHz 128 110 187500
* 93.750 KHz 256 111 93750
*/
int valid_frequencies[] = {12000000, 6000000, 3000000, 1500000,
750000, 375000, 187500, 93750};
uint16_t mbrd_value = 0;
uint32_t config = (obj->spi->CONFIG & ~CONFIG_MBRD);
/* Store the index of the minimum supported frequency */
uint32_t index = 7;
for (int i = 0; i < 8; i++) {
if (hz >= valid_frequencies[i]) {
/*
* Store the index of the closest lower or equal supported
* frequency.
*/
index = i;
break;
}
mbrd_value++;
}
/*
* Set the selected frequency. If the frequency is below the minimum
* supported the driver sets the minumum.
*/
config |= index << CONFIG_MBRD_SHIFT;
/*
* If the specified frequency didn't match any of the valid frequencies
* then leave CONFIG_MBRD to the closest lower frequency supported.
*/
obj->spi->CONFIG = config;
}
int spi_master_write(spi_t *obj, int value) {
private_spi_t *private_spi = get_spi_private(obj);
int data = 0;
if(private_spi->size == 16) {
obj->spi->TX_DATA = (uint8_t)((value >> 8) & TX_DATA_TDATA);
obj->spi->TX_DATA = (uint8_t)(value & TX_DATA_TDATA);
/* Manually trigger start */
obj->spi->CONFIG |= CONFIG_MSC;
while(!(obj->spi->IRQ_STATUS & IRQ_STATUS_TNF))
continue;
data = (obj->spi->RX_DATA & RX_DATA_RDATA) << 8;
data = data | (obj->spi->RX_DATA & RX_DATA_RDATA);
} else {
obj->spi->TX_DATA = (uint16_t)(value & TX_DATA_TDATA);
/* Manually trigger start */
obj->spi->CONFIG |= CONFIG_MSC;
while(!(obj->spi->IRQ_STATUS & IRQ_STATUS_TNF))
continue;
data = obj->spi->RX_DATA & RX_DATA_RDATA;
}
return data;
}
int spi_master_block_write(spi_t *obj, const char *tx_buffer, int tx_length,
char *rx_buffer, int rx_length, char write_fill) {
int total = (tx_length > rx_length) ? tx_length : rx_length;
for (int i = 0; i < total; i++) {
char out = (i < tx_length) ? tx_buffer[i] : write_fill;
char in = spi_master_write(obj, out);
if (i < rx_length) {
rx_buffer[i] = in;
}
}
return total;
}
uint8_t spi_get_module(spi_t *obj) {
return obj->spi->MID;
}
int spi_busy(spi_t *obj) {
return 0;
}