/**************************************************************************** * * 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