Newer
Older
mbed-os / targets / TARGET_Samsung / TARGET_SIDK_S1SBP6A / spi_api.c
@Heuisam Kwag Heuisam Kwag on 11 Aug 2020 9 KB introduce S1SBP6A
/****************************************************************************
 *
 * 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 "cmsis.h"
#include "spi_api.h"
#include "gpio_api.h"
#include "pinmap.h"
#include "mbed_error.h"
#include "PeripheralPins.h"

static void ssp_cs_manual(spi_t *obj)
{
    modifyreg32(&(obj->spi->CR1), SSP_CR1_MFSS_MASK, SSP_CR1_MFSS(1));
    gpio_dir(&obj->cs, PIN_OUTPUT);
}

static void ssp_exchange(spi_t *obj, const void *txbuffer, void *rxbuffer, size_t nwords)
{
    size_t sent = 0;
    size_t received = 0;
    int word_length;

    BP_SPI_TypeDef *base_reg;
    base_reg = (BP_SPI_TypeDef *)obj->spi;

    word_length = (base_reg->CR0 & SSP_CR0_DSS_MASK) >> SSP_CR0_DSS_SHIFT;

    if ((rxbuffer == NULL) && (txbuffer == NULL)) {
        while (received < nwords) {
            if (sent < nwords)
                if (!isTXFIFOFull(base_reg->SR)) {
                    base_reg->DR = 0;
                    sent++;
                }

            if (isRXFIFOEmpty(base_reg->SR)) {
                received++;
            }
        }
        return;
    }

    if (rxbuffer == NULL) {
        while (received < nwords) {
            if (sent < nwords)
                if (!isTXFIFOFull(base_reg->SR)) {
                    if (word_length > SSP_CR0_DSS_8BIT) {
                        base_reg->DR = ((uint16_t *) txbuffer)[sent++];
                    } else {
                        base_reg->DR = ((uint8_t *) txbuffer)[sent++];
                    }
                }

            if (isRXFIFOEmpty(base_reg->SR)) {
                received++;
            }
        }
        return;
    }

    if (txbuffer == NULL) {
        while (received < nwords) {
            if (sent < nwords)
                if (!isTXFIFOFull(base_reg->SR)) {
                    if (word_length > SSP_CR0_DSS_8BIT) {
                        base_reg->DR = ((uint16_t *) rxbuffer)[sent++];
                    } else {
                        base_reg->DR = ((uint8_t *) rxbuffer)[sent++];
                    }
                }

            if (isRXFIFOEmpty(base_reg->SR)) {
                if (word_length > SSP_CR0_DSS_8BIT) {
                    ((uint16_t *) rxbuffer)[received++] = base_reg->DR;
                } else {
                    ((uint8_t *) rxbuffer)[received++] = base_reg->DR;
                }
            }
        }
        return;
    }

    while (received < nwords) {
        if (sent < nwords)
            if (isTXFIFOEmpty(base_reg->SR)) {
                if (word_length > SSP_CR0_DSS_8BIT) {
                    base_reg->DR = ((uint16_t *) txbuffer)[sent++];
                } else {
                    base_reg->DR = ((uint8_t *) txbuffer)[sent++];
                }
            }

        if (isRXFIFOEmpty(base_reg->SR)) {
            if (word_length > SSP_CR0_DSS_8BIT) {
                ((uint16_t *) rxbuffer)[received++] = base_reg->DR;
            } else {
                ((uint8_t *) rxbuffer)[received++] = base_reg->DR;
            }
        }
    }
}

static inline int ssp_disable(spi_t *obj)
{
    return obj->spi->CR1 &= ~(1 << 1);
}

static inline int ssp_enable(spi_t *obj)
{
    return obj->spi->CR1 |= SSP_CR1_SSE_MASK;
}

static inline int ssp_readable(spi_t *obj)
{
    return obj->spi->SR & (1 << 2);
}

static inline int ssp_writeable(spi_t *obj)
{
    return obj->spi->SR & SSP_SR_BSY_MASK;
}

static inline void ssp_write(spi_t *obj, int value)
{
    obj->spi->DR = value;
    while (ssp_writeable(obj));
}

static inline int ssp_read(spi_t *obj)
{
    int read_DR = obj->spi->DR;
    return read_DR;
}

static inline int ssp_busy(spi_t *obj)
{
    return (obj->spi->SR & (1 << 4)) ? (1) : (0);
}


static void spi_select(spi_t *obj, int selected)
{
    gpio_write(&obj->cs, selected);
}


int spi_master_write(spi_t *obj, int value)
{
    unsigned char txbyte;
    unsigned char rxbyte;

    txbyte = (unsigned char)value;
    rxbyte = (unsigned char)0;

    spi_select(obj, 0);
    ssp_exchange(obj, &txbyte, &rxbyte, 1);
    spi_select(obj, 1);

    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, 0);
    ssp_exchange(obj, tx_buffer, rx_buffer, total);
    spi_select(obj, 1);

    return total;
}

SPIName spi_get_peripheral_name(PinName mosi, PinName miso, PinName sclk)
{
    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_per;

    // If 3 wire SPI is used, the miso is not connected.
    if (miso == NC) {
        spi_per = (SPIName)pinmap_merge(spi_mosi, spi_sclk);
    } else {
        SPIName spi_data = (SPIName)pinmap_merge(spi_mosi, spi_miso);
        spi_per = (SPIName)pinmap_merge(spi_data, spi_sclk);
    }

    return spi_per;
}
static int get_spi_channel(uint32_t spi)
{
    int i;

    for (i = 0; i < 4; i++) {
        if (PinMap_SPI_SCLK[i].peripheral == (int)spi) {
            return i;
        }
    }

    return -1;
}

void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel)
{

    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 = (BP_SPI_TypeDef *)pinmap_merge(spi_data, spi_cntl);

    if ((int)obj->spi == NC) {
        error("SPI pinout mapping failed");
    }

    pinmap_pinout(mosi, PinMap_SPI_MOSI);
    pinmap_pinout(miso, PinMap_SPI_MISO);
    pinmap_pinout(sclk, PinMap_SPI_SCLK);
    pinmap_pinout(ssel, PinMap_SPI_SSEL);

    gpio_init(&obj->cs, spi_ssel);
    modifyreg32(&(obj->spi->CR1), SSP_CR1_RXIFLSEL_MASK |
                SSP_CR1_TXIFLSEL_MASK,
                SSP_CR1_RXIFLSEL(4) |
                SSP_CR1_TXIFLSEL(4));
    int spi_idx = get_spi_channel((uint32_t)(obj->spi));
    bp6a_cmu_enable_clock(spi_idx + CMU_SPI0_CLK, true);
    ssp_cs_manual(obj);
    spi_select(obj, 1);
    ssp_enable(obj);
}

void spi_free(spi_t *obj)
{
    int spi_idx = get_spi_channel((uint32_t)obj->spi);

    bp6a_cmu_enable_clock(spi_idx + CMU_SPI0_CLK, false);
}

void spi_format(spi_t *obj, int bits, int mode, int slave)
{
    if (!(bits >= 4 && bits <= 16) || !(mode >= 0 && mode <= 3)) {
        error("SPI format error");
    }

    modifyreg32(&(obj->spi->CR1), SSP_CR1_MS_MASK, SSP_CR1_MS(slave));

    modifyreg32(&(obj->spi->CR0),
                SSP_CR0_DSS_MASK |
                SSP_CR0_FRF_MASK |
                SSP_CR0_SPO_MASK |
                SSP_CR0_SPH_MASK,
                SSP_CR0_DSS(bits - 1) |
                SSP_CR0_FRF(0) |
                SSP_CR0_SPO(!!(mode & 0x01)) |
                SSP_CR0_SPH(!!(mode & 0x02)));
}

void spi_frequency(spi_t *obj, int hz)
{
    uint32_t cpsdvsr;
    uint32_t scr;
    uint32_t freq = bp6a_get_clock_src_freq(CMU_SPI0_CLK);
    if (!obj && hz > (int)(freq / 2)) {
        error("SPI Clock error");
    }


    for (scr = 1; scr <= 256; scr++) {
        cpsdvsr = (freq / scr) / hz;
        if (cpsdvsr < 255) {
            break;
        }
    }

    if (scr > 256 || cpsdvsr > 255) {
        error("Couldn't setup requested SPI frequency");
    }

    if (cpsdvsr < 2) {
        cpsdvsr = 2;
    } else if (cpsdvsr > 254) {
        cpsdvsr = 254;
    }

    cpsdvsr = (cpsdvsr + 1) & ~1;

    putreg32(&(obj->spi->CPSR), cpsdvsr);
    modifyreg32(&(obj->spi->CR0), SSP_CR0_SCR_MASK, SSP_CR0_SCR(scr));
    obj->freq = hz;
    obj->freq_r = (freq / cpsdvsr) / scr;

}

int spi_slave_receive(spi_t *obj)
{
    return (ssp_readable(obj) && !ssp_busy(obj)) ? (1) : (0);
}

int spi_slave_read(spi_t *obj)
{
    return obj->spi->DR;
}

void spi_slave_write(spi_t *obj, int value)
{
    while (ssp_writeable(obj) == 0) ;
    obj->spi->DR = value;
}

int spi_busy(spi_t *obj)
{
    return isBusy(obj->spi->SR);
}

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