mbed-os/targets/TARGET_Samsung/TARGET_SIDK_S5JS100/spi_api.c

537 lines
14 KiB
C

/****************************************************************************
*
* Copyright 2020 Samsung Electronics All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*
****************************************************************************/
#if DEVICE_SPI
#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;
int s5js100_configgpio(uint32_t cfgset);
static const PinMap PinMap_SPI_SCLK[] = {
{SPI0_CLK, SPI_0, 0},
{SPI1_CLK, SPI_1, 0},
{NC, NC, 0}
};
static const PinMap PinMap_SPI_MOSI[] = {
{SPI0_MOSI, SPI_0, 0},
{SPI1_MOSI, SPI_1, 0},
{NC, NC, 0}
};
static const PinMap PinMap_SPI_MISO[] = {
{SPI0_MISO, SPI_0, 0},
{SPI1_MISO, SPI_1, 0},
{NC, NC, 0}
};
static const PinMap PinMap_SPI_SSEL[] = {
{SPI0_CSN, SPI_0, 0},
{SPI1_CSN, SPI_1, 0},
{NC, NC, 0}
};
/*
* Retrieve the private data of the instance related to a given IP
*/
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;
}
}
#ifndef CONFIG_EXAMPLES_SPI_BUS_NUM
#define CONFIG_EXAMPLES_SPI_BUS_NUM 0
#endif
static inline uint32_t ssp_getreg(spi_t *priv, uint8_t offset)
{
return getreg32(priv->base + (uint32_t) offset);
}
static inline void ssp_putreg(spi_t *priv, uint8_t offset, uint32_t value)
{
putreg32(value, priv->base + (uint32_t) offset);
}
void ssp_setmode(spi_t *priv, enum spi_mode_e mode)
{
uint32_t regval;
/* Has the mode changed? */
if (mode != priv->mode) {
/* Yes... Set CR0 appropriately */
regval = ssp_getreg(priv, S5JS100_SSP_CR0_OFFSET);
regval &= ~(SSP_CR0_CPOL | SSP_CR0_CPHA);
switch (mode) {
case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */
break;
case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */
regval |= SSP_CR0_CPHA;
break;
case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */
regval |= SSP_CR0_CPOL;
break;
case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */
regval |= (SSP_CR0_CPOL | SSP_CR0_CPHA);
break;
default:
error("ERROR: Bad mode: %d\n", mode);
return;
}
ssp_putreg(priv, S5JS100_SSP_CR0_OFFSET, regval);
/* Save the mode so that subsequent re-configurations will be faster */
priv->mode = mode;
}
}
static void ssp_setbits(spi_t *priv, int nbits)
{
uint32_t regval;
/* Has the number of bits changed? */
if (priv == NULL || nbits <= 3 || nbits >= 17) {
error("SPI setbit error");
}
if ((uint32_t)nbits != priv->nbits) {
/* Yes... Set CR1 appropriately */
regval = ssp_getreg(priv, S5JS100_SSP_CR0_OFFSET);
regval &= ~SSP_CR0_DSS_MASK;
regval |= ((nbits - 1) << SSP_CR0_DSS_SHIFT);
ssp_putreg(priv, S5JS100_SSP_CR0_OFFSET, regval);
/* Save the selection so the subsequence re-configurations will be faster */
priv->nbits = nbits;
}
}
static uint32_t ssp_setfrequency(spi_t *priv, uint32_t frequency)
{
uint32_t cpsdvsr;
uint32_t scr;
uint32_t regval;
uint32_t actual;
/* Check if the requested frequency is the same as the frequency selection */
if (!(priv && frequency <= SSP_CLOCK / 2)) {
error("SSP Frequency error");
}
if (priv->frequency == frequency) {
/* We are already at this frequency. Return the actual. */
return priv->actual;
}
/* Set clock source value */
cal_clk_setrate(priv->freqid, (unsigned long)SSP_CLOCK);
/* The SSP bit frequency is given by:
*
* frequency = SSP_CLOCK / (CPSDVSR * (SCR+1)).
*
* Let's try for a solution with the smallest value of SCR. NOTES:
* (1) In the calculations below, the value of the variable 'scr' is
* (SCR+1) in the above equation. (2) On slower s5jxx parts, SCR
* will probably always be zero.
*/
for (scr = 1; scr <= 256; scr++) {
/* CPSDVSR = SSP_CLOCK / (SCR + 1) / frequency */
cpsdvsr = (SSP_CLOCK / scr) / frequency;
/* Break out on the first solution we find with the smallest value
* of SCR and with CPSDVSR within the maximum range or 254.
*/
if (cpsdvsr < 255) {
break;
}
}
if (!(scr <= 256 && cpsdvsr <= 255)) {
error("SSP Frequency error");
}
/* "In master mode, CPSDVSRmin = 2 or larger (even numbers only)" */
if (cpsdvsr < 2) {
/* Clip to the minimum value. */
cpsdvsr = 2;
} else if (cpsdvsr > 254) {
/* This should never happen */
cpsdvsr = 254;
}
/* Force even */
cpsdvsr = (cpsdvsr + 1) & ~1;
/* Save the new CPSDVSR and SCR values */
ssp_putreg(priv, S5JS100_SSP_CPSR_OFFSET, cpsdvsr);
regval = ssp_getreg(priv, S5JS100_SSP_CR0_OFFSET);
regval &= ~SSP_CR0_SCR_MASK;
regval |= ((scr - 1) << SSP_CR0_SCR_SHIFT);
ssp_putreg(priv, S5JS100_SSP_CR0_OFFSET, regval);
/* Calculate the new actual */
actual = (SSP_CLOCK / cpsdvsr) / scr;
/* Save the frequency setting */
priv->frequency = frequency;
priv->actual = actual;
return actual;
}
static void ssp_exchange(spi_t *priv, const void *txbuffer, void *rxbuffer, size_t nwords)
{
size_t sent = 0;
size_t received = 0;
int word_length;
SSP_Typedef *pSSPRegs;
pSSPRegs = (SSP_Typedef *) priv->base;
word_length = pSSPRegs->S5JS100_SSP_CR0_reg & SSP_CR0_DSS_MASK;
/* TX/RX */
if ((rxbuffer == NULL) && (txbuffer == NULL)) {
while (received < nwords) {
if (sent < nwords)
if (SSP_SR_TNF_CHECK(pSSPRegs->S5JS100_SSP_SR_reg)) {
pSSPRegs->S5JS100_SSP_DR_reg = 0;
sent++;
}
if (SSP_SR_RNE_CHECK(pSSPRegs->S5JS100_SSP_SR_reg)) {
received++;
}
}
return;
}
if (rxbuffer == NULL) {
while (received < nwords) {
if (sent < nwords)
if (SSP_SR_TNF_CHECK(pSSPRegs->S5JS100_SSP_SR_reg)) {
if (word_length > SSP_CR0_DSS_8BIT) {
pSSPRegs->S5JS100_SSP_DR_reg = ((uint16_t *) txbuffer)[sent++];
} else {
pSSPRegs->S5JS100_SSP_DR_reg = ((uint8_t *) txbuffer)[sent++];
}
}
if (SSP_SR_RNE_CHECK(pSSPRegs->S5JS100_SSP_SR_reg)) {
received++;
}
}
return;
}
if (txbuffer == NULL) {
while (received < nwords) {
if (sent < nwords)
if (SSP_SR_TNF_CHECK(pSSPRegs->S5JS100_SSP_SR_reg)) {
if (word_length > SSP_CR0_DSS_8BIT) {
pSSPRegs->S5JS100_SSP_DR_reg = ((uint16_t *) rxbuffer)[sent++];
} else {
pSSPRegs->S5JS100_SSP_DR_reg = ((uint8_t *) rxbuffer)[sent++];
}
}
if (SSP_SR_RNE_CHECK(pSSPRegs->S5JS100_SSP_SR_reg)) {
if (word_length > SSP_CR0_DSS_8BIT) {
((uint16_t *) rxbuffer)[received++] = pSSPRegs->S5JS100_SSP_DR_reg;
} else {
((uint8_t *) rxbuffer)[received++] = pSSPRegs->S5JS100_SSP_DR_reg;
}
}
}
return;
}
while (received < nwords) {
if (sent < nwords)
if (SSP_SR_TFE_CHECK(pSSPRegs->S5JS100_SSP_SR_reg)) {
if (word_length > SSP_CR0_DSS_8BIT) {
pSSPRegs->S5JS100_SSP_DR_reg = ((uint16_t *) txbuffer)[sent++];
} else {
pSSPRegs->S5JS100_SSP_DR_reg = ((uint8_t *) txbuffer)[sent++];
}
}
if (SSP_SR_RNE_CHECK(pSSPRegs->S5JS100_SSP_SR_reg)) {
if (word_length > SSP_CR0_DSS_8BIT) {
((uint16_t *) rxbuffer)[received++] = pSSPRegs->S5JS100_SSP_DR_reg;
} else {
((uint8_t *) rxbuffer)[received++] = pSSPRegs->S5JS100_SSP_DR_reg;
}
}
}
}
void ssp_select(spi_t *priv, int selected)
{
SSP_Typedef *pSSPRegs;
pSSPRegs = (SSP_Typedef *) priv->base;
if (selected) {
pSSPRegs->S5JS100_SSP_CR1_reg &= ~SSP_CR1_CS;
} else {
pSSPRegs->S5JS100_SSP_CR1_reg |= SSP_CR1_CS;
}
}
void spi_free(spi_t *obj)
{
}
void spi_select(spi_t *obj, int selected)
{
#if 0
SPI_TypeDef *pSPIRegs;
pSPIRegs = obj->spi;
unsigned int cs_reg;
cs_reg = getreg32(&pSPIRegs->CS_REG);
cs_reg |= CS_REG_nSS_INACTIVE;
if (selected == 1) {
cs_reg &= ~CS_REG_nSS_INACTIVE;
}
putreg32(cs_reg, &pSPIRegs->CS_REG);
#endif
ssp_select(obj, selected);
}
void spi_setmode(spi_t *obj, int mode)
{
ssp_setmode(obj, mode);
}
void spi_setbits(spi_t *obj, int nbits)
{
ssp_setbits(obj, nbits);
}
void spi_format(spi_t *obj, int bits, int mode, int slave)
{
spi_setmode(obj, mode);
spi_setbits(obj, bits);
}
void spi_frequency(spi_t *obj, int hz)
{
//cal_clk_setrate(obj->freqid, (unsigned long)hz);
ssp_setfrequency(obj, (uint32_t)hz);
}
static void spi_exchange(spi_t *obj, const void *txbuffer, void *rxbuffer, unsigned int nwords)
{
ssp_exchange(obj, txbuffer, rxbuffer, nwords);
}
int spi_master_write(spi_t *obj, int value)
{
//// private_spi_t *private_spi = get_spi_private(obj);
unsigned char txbyte;
unsigned char rxbyte;
txbyte = (unsigned char)value;
rxbyte = (unsigned char)0;
spi_select(obj, 1);
spi_exchange(obj, &txbyte, &rxbyte, 1);
spi_select(obj, 0);
return (unsigned int)rxbyte;
}
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;
spi_select(obj, 1);
spi_exchange(obj, tx_buffer, rx_buffer, tx_length);
spi_select(obj, 0);
return total;
}
uint8_t spi_get_module(spi_t *obj)
{
return 0;
}
int spi_busy(spi_t *obj)
{
return 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");
}
obj->base = SPI0_BASE;
uint32_t regval;
uint32_t i;
s5js100_configgpio(sclk);
s5js100_configgpio(ssel);
s5js100_configgpio(miso);
s5js100_configgpio(mosi);
switch (CONFIG_EXAMPLES_SPI_BUS_NUM) {
case 0 :
obj->freqid = d1_spi0;
break;
default :
error("SPI Bus select error");
break;
}
/* Configure 8-bit SPI mode */
ssp_putreg(obj, S5JS100_SSP_CR0_OFFSET, SSP_CR0_DSS_8BIT | SSP_CR0_FRF_SPI);
/* Disable the SSP and all interrupts (we'll poll for all data) */
ssp_putreg(obj, S5JS100_SSP_CR1_OFFSET, 0);
ssp_putreg(obj, S5JS100_SSP_IMSC_OFFSET, 0);
obj->frequency = 0;
obj->nbits = 8;
obj->mode = SPIDEV_MODE0;
/* Set the initial SSP configuration */
spi_setmode(obj, SPIDEV_MODE0);
spi_setbits(obj, 8);
spi_frequency(obj, 1000000);
regval = ssp_getreg(obj, S5JS100_SSP_CR1_OFFSET);
ssp_putreg(obj, S5JS100_SSP_CR1_OFFSET, regval | SSP_CR1_SSE | SSP_CR1_CSE);
for (i = 0; i < S5JS100_SSP_FIFOSZ; i++) {
(void)ssp_getreg(obj, S5JS100_SSP_DR_OFFSET);
}
}
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