Newer
Older
mbed-os / targets / TARGET_ARM_SSG / TARGET_CM3DS_MPS2 / device / drivers / spi_pl022_drv.c
@Harrison Mutai Harrison Mutai on 15 Oct 2020 30 KB Add SPDX license identifier to Arm files
/*
 * Copyright (c) 2018 ARM Limited
 * 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.
 */

#include "spi_pl022_drv.h"

/******************************************************************************
 * PL022 device specific definitions based on DDI0194H_ssp_pl022_trm.pdf which
 * is available from infocenter.arm.com.
 *
 * This version of driver aims at necessary functionality for MPS2 board only
 *****************************************************************************/

/* Structure for the SSP Primary Cell device registers */
struct spi_pl022_dev_reg_map_t {
    volatile uint32_t sspcr0;        /* Control register 0 */
    volatile uint32_t sspcr1;        /* Control register 1 */
    volatile uint32_t sspdr;         /* Data register */
    volatile uint32_t sspsr;         /* Status register */
    volatile uint32_t sspcpsr;       /* Clock prescale register */
    volatile uint32_t sspimsc;       /* Interrupt mask set or clear register */
    volatile uint32_t sspris;        /* Raw interrupt status register */
    volatile uint32_t sspmis;        /* Masked interrupt status register */
    volatile uint32_t sspicr;        /* Interrupt clear register */
    volatile uint32_t sspdmacr;      /* DMA control register */
    volatile uint32_t reserved[1006];/* Reserved from Base+0x28-0xFE0 */
    volatile uint32_t sspperiphid0;  /* Peripheral id register 0 */
    volatile uint32_t sspperiphid1;  /* Peripheral id register 1 */
    volatile uint32_t sspperiphid2;  /* Peripheral id register 2 */
    volatile uint32_t sspperiphid3;  /* Peripheral id register 3 */
    volatile uint32_t ssppcellid0;   /* Primary cell id register 0 */
    volatile uint32_t ssppcellid1;   /* Primary cell id register 1 */
    volatile uint32_t ssppcellid2;   /* Primary cell id register 2 */
    volatile uint32_t ssppcellid3;   /* Primary cell id register 3 */
};

/*--- SSP Control Register 0 ---*/

/* Data Size Select {0x3=4 ... 0xF=16} */
#define SPI_PL022_SSPCR0_DSS_POS         0
#define SPI_PL022_SSPCR0_DSS_MSK         0xF

/* Frame format */
#define SPI_PL022_SSPCR0_FRF_POS         4
#define SPI_PL022_SSPCR0_FRF_MSK         (0x3ul<<SPI_PL022_SSPCR0_FRF_POS)
#define SPI_PL022_SSPCR0_FRF_MOT_SPI     0x0
#define SPI_PL022_SSPCR0_FRF_TI_SS       0x1
#define SPI_PL022_SSPCR0_FRF_MICROWIRE   0x2
#define SPI_PL022_SSPCR0_FRF_RES         0x3

/* Clock polarity applicable to Motorola SPI format only */
#define SPI_PL022_SSPCR0_SPO_POS         6
#define SPI_PL022_SSPCR0_SPO_MSK         (0x1ul<<SPI_PL022_SSPCR0_SPO_POS)

/* Clock phase applicable to Motorola SPI format only */
#define SPI_PL022_SSPCR0_SPH_POS         7
#define SPI_PL022_SSPCR0_SPH_MSK         (0x1ul<<SPI_PL022_SSPCR0_SPH_POS)

/* Serial clock rate */
#define SPI_PL022_SSPCR0_SCR_POS         8
#define SPI_PL022_SSPCR0_SCR_MSK         (0xFFul<<SPI_PL022_SSPCR0_SCR_POS)

/*--- SSP Control Register 1 ---*/

/* Loopback mode*/
#define SPI_PL022_SSPCR1_LBM_POS         0
#define SPI_PL022_SSPCR1_LBM_MSK         (0x1ul<<SPI_PL022_SSPCR1_LBM_POS)

/* Syncrhonous serial port enable*/
#define SPI_PL022_SSPCR1_SSE_POS         1
#define SPI_PL022_SSPCR1_SSE_MSK         (0x1ul<<SPI_PL022_SSPCR1_SSE_POS)

/* Master or Slave select */
#define SPI_PL022_SSPCR1_MS_POS          2
#define SPI_PL022_SSPCR1_MS_MSK          (0x1ul<<SPI_PL022_SSPCR1_MS_POS)

/* Slave mode output disable */
#define SPI_PL022_SSPCR1_SOD_POS         3
#define SPI_PL022_SSPCR1_SOD_MSK         (0x1ul<<SPI_PL022_SSPCR1_SOD_POS)

/*--- Clock PreScale Register ---*/

/* Divisor */
#define SPI_PL022_SSPCPSR_CPSDVSR_POS   0
#define SPI_PL022_SSPCPSR_CPSDVSR_MSK   (0xFFul<<SPI_PL022_SSPCPSR_CPSDVSR_POS)
#define SPI_PL022_INVALID_SSPCPSR_VALUE 0
#define SPI_PL022_MIN_SSPCPSR_VALUE     2
#define SPI_PL022_MAX_SSPCPSR_VALUE     254
#define SPI_PL022_MAX_SCR_VALUE         255


/*--- Interrupt Mask Set or Clear Register --- */

/* Receive Overrun Interrupt Mask */
#define SPI_PL022_SSPIMSC_RORIM_POS     0
#define SPI_PL022_SSPIMSC_RORIM_MSK     (0x1ul<<SPI_PL022_SSPIMSC_RORIM_POS)

/* Receive Timeout Interrupt Mask */
#define SPI_PL022_SSPIMSC_RTIM_POS      1
#define SPI_PL022_SSPIMSC_RTIM_MSK      (0x1ul<<SPI_PL022_SSPIMSC_RTIM_POS)

/* Receive FIFO Interrupt Mask */
#define SPI_PL022_SSPIMSC_RXIM_POS      2
#define SPI_PL022_SSPIMSC_RXIM_MSK      (0x1ul<<SPI_PL022_SSPIMSC_RXIM_POS)

/* Transmit FIFO Interrupt Mask */
#define SPI_PL022_SSPIMSC_TXIM_POS      3
#define SPI_PL022_SSPIMSC_TXIM_MSK      (0x1ul<<SPI_PL022_SSPIMSC_TXIM_POS)

/*--- Interrupt Mask Set or Clear Register ---*/

/* Receive Overrun Interrupt Mask */
#define SPI_PL022_SSPIMSC_RORIM_POS     0
#define SPI_PL022_SSPIMSC_RORIM_MSK     (0x1ul<<SPI_PL022_SSPIMSC_RORIM_POS)

/* Receive Timeout Interrupt Mask */
#define SPI_PL022_SSPIMSC_RTIM_POS      1
#define SPI_PL022_SSPIMSC_RTIM_MSK      (0x1ul<<SPI_PL022_SSPIMSC_RTIM_POS)

/* Receive FIFO Interrupt Mask */
#define SPI_PL022_SSPIMSC_RXIM_POS      2
#define SPI_PL022_SSPIMSC_RXIM_MSK      (0x1ul<<SPI_PL022_SSPIMSC_RXIM_POS)

/* Transmit FIFO Interrupt Mask */
#define SPI_PL022_SSPIMSC_TXIM_POS      3
#define SPI_PL022_SSPIMSC_TXIM_MSK      (0x1ul<<SPI_PL022_SSPIMSC_TXIM_POS)

#define SPI_PL022_SSPIMSC_VALID_MSK     \
                               ((0x1ul<<(SPI_PL022_SSPIMSC_TXIM_POS+1))-1)

/*--- Raw Interrupt Status Register ---*/

/* SSPRORINTR */
#define SPI_PL022_SSPRIS_RORRIS_POS     0
#define SPI_PL022_SSPRIS_RORRIS_MSK     (0x1ul<<SPI_PL022_SSPRIS_RORRIS_POS)

/* SSPRTINTR */
#define SPI_PL022_SSPRIS_RTRIS_POS      1
#define SPI_PL022_SSPRIS_RTRIS_MSK      (0x1ul<<SPI_PL022_SSPRIS_RTRIS_POS)

/* SSPRXINTR */
#define SPI_PL022_SSPRIS_RXRIS_POS      2
#define SPI_PL022_SSPRIS_RXRIS_MSK      (0x1ul<<SPI_PL022_SSPRIS_RXRIS_POS)

/* SSPTXINTR */
#define SPI_PL022_SSPRIS_TXRIS_POS      3
#define SPI_PL022_SSPRIS_TXRIS_MSK      (0x1ul<<SPI_PL022_SSPRIS_TXRIS_POS)

#define SPI_PL022_SSPRIS_VALID_MSK      \
                               ((0x1ul<<(SPI_PL022_SSPRIS_TXRIS_POS+1))-1)

/*--- Masked Interrupt Status Register ---*/

/* SSPRORINTR */
#define SPI_PL022_SSPMIS_RORMIS_POS     0
#define SPI_PL022_SSPMIS_RORMIS_MSK     (0x1ul<<SPI_PL022_SSPMIS_RORMIS_POS)

/* SSPRTINTR */
#define SPI_PL022_SSPMIS_RTMIS_POS      1
#define SPI_PL022_SSPMIS_RTMIS_MSK      (0x1ul<<SPI_PL022_SSPMIS_RTMIS_POS)

/* SSPRXINTR */
#define SPI_PL022_SSPMIS_RXMIS_POS      2
#define SPI_PL022_SSPMIS_RXMIS_MSK      (0x1ul<<SPI_PL022_SSPMIS_RXMIS_POS)

/* SSPTXINTR */
#define SPI_PL022_SSPMIS_TXMIS_POS      3
#define SPI_PL022_SSPMIS_TXMIS_MSK      (0x1ul<<SPI_PL022_SSPMIS_TXMIS_POS)

#define SPI_PL022_SSPMIS_VALID_MSK      \
                               ((0x1ul<<(SPI_PL022_SSPMIS_TXMIS_POS+1))-1)

/*--- Interrupt Clear Register --- */

/* SSPRORINTR Clear */
#define SPI_PL022_SSPICR_RORIC_POS      0
#define SPI_PL022_SSPICR_RORIC_MSK      (0x1ul<<SPI_PL022_SSPICR_RORIC_POS)

/* SSPRTINTR Clear */
#define SPI_PL022_SSPICR_RTIC_POS       1
#define SPI_PL022_SSPICR_RTIC_MSK       (0x1ul<<SPI_PL022_SSPICR_RTIC_POS)

#define SPI_PL022_SSPICR_VALID_MSK      ((0x1ul<<(SPI_PL022_SSPICR_RTIC_POS+1))-1)

/*--- DMA Control Register --- */

/* Receive DMA Enable */
#define SPI_PL022_SSPDMACR_RXDMAE_POS   0
#define SPI_PL022_SSPDMACR_RXDMAE_MSK   (0x1ul<<SPI_PL022_SSPDMACR_RXDMAE_POS)

/* Transmit DMA Enable */
#define SPI_PL022_SSPDMACR_TXDMAE_POS   1
#define SPI_PL022_SSPDMACR_TXDMAE_MSK   (0x1ul<<SPI_PL022_SSPDMACR_TXDMAE_POS)

#define SPI_PL022_SSPDMACR_VALID_MSK    ((0x1ul<<(SPI_PL022_SSPDMACR_TXDMAE_POS+1))-1)

/*--- Peripheral Identification Registers ---*/

#define SPI_PL022_SSPPERIPH_ID_OFFSET (0xFE0ul)

/* Part Number 0 */
#define SPI_PL022_SSPPERIPH_ID0_PARTNO_0_POS   0
#define SPI_PL022_SSPPERIPH_ID0_PARTNO_0_SIZE  8
#define SPI_PL022_SSPPERIPH_ID0_PARTNO_0_MSK   \
                                 (0xFFul<<SPI_PL022_SSPPERIPH_ID0_PARTNO_0_POS)

/* Part Number 1 */
#define SPI_PL022_SSPPERIPH_ID1_PARTNO_1_POS  0
#define SPI_PL022_SSPPERIPH_ID1_PARTNO_1_SIZE 0
#define SPI_PL022_SSPPERIPH_ID1_PARTNO_1_MSK \
                                 (0xFul<<SPI_PL022_SSPPERIPH_ID1_PARTNO_1_POS)

/* Designer 0 */
#define SPI_PL022_SSPPERIPH_ID1_DESIGNER_0_POS  4
#define SPI_PL022_SSPPERIPH_ID1_DESIGNER_0_SIZE 4
#define SPI_PL022_SSPPERIPH_ID1_DESIGNER_0_MSK \
                                 (0xFul<<SPI_PL022_SSPPERIPH_ID1_DESIGNER_0_POS)

/* Designer 1 */
#define SPI_PL022_SSPPERIPH_ID2_DESIGNER_1_POS  0
#define SPI_PL022_SSPPERIPH_ID2_DESIGNER_1_SIZE 4
#define SPI_PL022_SSPPERIPH_ID2_DESIGNER_1_MSK \
                                 (0xFul<<SPI_PL022_SSPPERIPH_ID2_DESIGNER_1_POS)

/* Revision */
#define SPI_PL022_SSPPERIPH_ID2_REVISION_POS  4
#define SPI_PL022_SSPPERIPH_ID2_REVISION_SIZE 4
#define SPI_PL022_SSPPERIPH_ID2_REVIISON_MSK \
                                 (0xFul<<SPI_PL022_SSPPERIPH_ID2_REVISION_POS)

/* Config */
#define SPI_PL022_SSPPERIPH_ID3_CONFIG_POS   0
#define SPI_PL022_SSPPERIPH_ID3_CONFIG_MSK   \
                                 (0xFFul<<SPI_PL022_SSPPERIPH_ID3_CONFIG_POS)

/*--- PrimeCell Identification Registers ---*/

#define SPI_PL022_SSPPCELL_ID_OFFSET   (0xFF0ul)

#define SPI_PL022_SSPPCELL_ID0_POS     0
#define SPI_PL022_SSPPCELL_ID0_MSK     (0xFFul<<SPI_PL022_SSPPCELL_ID0_POS)

#define SPI_PL022_SSPPCELL_ID1_POS     0
#define SPI_PL022_SSPPCELL_ID1_MSK     (0xFFul<<SPI_PL022_SSPPCELL_ID1_POS)

#define SPI_PL022_SSPPCELL_ID2_POS     0
#define SPI_PL022_SSPPCELL_ID2_MSK     (0xFFul<<SPI_PL022_SSPPCELL_ID2_POS)

#define SPI_PL022_SSPPCELL_ID3_POS     0
#define SPI_PL022_SSPPCELL_ID3_MSK     (0xFFul<<SPI_PL022_SSPPCELL_ID3_POS)

/* ARM SPI PL022 state definitions */
#define SPI_PL022_INITIALIZED          (1 << 0)

#define WORD_1BYTE_MASK                (0xFFul)
#define WORD_2BYTES_MASK               (0xFFFFul)


/************************* PL022 TEST Definitions ******************************/
#define SPI_PL022_TEST_REG_BASE        (0x80ul)

struct spi_pl022_dev_test_reg_map_t {
    volatile uint32_t ssptcr;        /* Test Control register */
    volatile uint32_t sspitip;       /* Integration test input register */
    volatile uint32_t sspitop;       /* Integration test output register */
    volatile uint32_t ssptdr;        /* Test data register */
};

/* Test control register */
#define SPI_PL022_SSPTCR_ITEN_POS           0
#define SPI_PL022_SSPTCR_ITEN_MSK          (0x1ul<<SPI_PL022_SSPTCR_ITEN_POS)

#define SPI_PL022_SSPTCR_TESTFIFO_POS       1
#define SPI_PL022_SSPTCR_TESTFIFO_MSK       (0x1ul<<SPI_PL022_SSPTCR_TESTFIFO_POS)


/* Integration test input register */
#define SPI_PL022_SSPITIP_RXD_POS           0
#define SPI_PL022_SSPITIP_RXD_MSK           (0x1ul<<SPI_PL022_SSPITIP_RXD_POS)

#define SPI_PL022_SSPITIP_FSSIN_POS         1
#define SPI_PL022_SSPITIP_FSSIN_MSK         (0x1ul<<SPI_PL022_SSPITIP_FSSIN_POS)

#define SPI_PL022_SSPITIP_CLKIN_POS         2
#define SPI_PL022_SSPITIP_CLKIN_MSK         (0x1ul<<SPI_PL022_SSPITIP_CLKIN_POS)

#define SPI_PL022_SSPITIP_RXDMACLR_POS      3
#define SPI_PL022_SSPITIP_RXDMACLR_MSK      (0x1ul<<SPI_PL022_SSPITIP_RXDMACLR_POS)

#define SPI_PL022_SSPITIP_TXDMACLR_POS      4
#define SPI_PL022_SSPITIP_TXDMACLR_MSK      (0x1ul<<SPI_PL022_SSPITIP_TXDMACLR_POS)

/* Integration test output register */
#define SPI_PL022_SSPITOP_RXDMABREQ_POS     10
#define SPI_PL022_SSPITOP_RXDMABREQ_MSK     (0x1ul<<SPI_PL022_SSPITOP_RXDMABREQ_POS)

#define SPI_PL022_SSPITOP_RXDMASREQ_POS     11
#define SPI_PL022_SSPITOP_RXDMASREQ_MSK     (0x1ul<<SPI_PL022_SSPITOP_RXDMASREQ_POS)

#define SPI_PL022_SSPITOP_TXDMABREQ_POS     12
#define SPI_PL022_SSPITOP_TXDMABREQ_MSK     (0x1ul<<SPI_PL022_SSPITOP_TXDMABREQ_POS)

#define SPI_PL022_SSPITOP_TXDMASREQ_POS     13
#define SPI_PL022_SSPITOP_TXDMASREQ_MSK     (0x1ul<<SPI_PL022_SSPITOP_TXDMASREQ_POS)

/************************* PL022 Definitions End ******************************/


/*
 * \brief Calculates clock prescale divisor and sets serial clock rate
 *        for the SPI PL022 device.
 *
 * \param[in] ctrl_cfg  SPI control configuration \ref spi_pl022_ctrl_cfg_t
 * \param[in] sys_clk   System clock.
 * \param[in/out] cr0   Pointer to PL022 control register 0
 *                                             \ref spi_pl022_dev_reg_map_t
 *
 * \return Value of the SSPCPSR register \ref spi_pl022_dev_reg_map_t
 *
 * \note This function doesn't check if sys_clk or ctrl_cfg->bit_rate is 0
 */
static uint32_t spi_calc_clock_rate(
                            const struct spi_pl022_ctrl_cfg_t* ctrl_cfg,
                            uint32_t sys_clk, volatile uint32_t *cr0)
{
    uint32_t clkps_dvsr;  /* clock prescale divisor */
    uint32_t scr;         /* serial clock rate */

    for(clkps_dvsr = SPI_PL022_MIN_SSPCPSR_VALUE;
        clkps_dvsr <= SPI_PL022_MAX_SSPCPSR_VALUE; clkps_dvsr += 2) {

        /* Calculate clock rate based on the new clock prescale divisor */
        scr = (sys_clk / (clkps_dvsr * ctrl_cfg->bit_rate)) - 1;

        /* Checks if it can be supported by the divider */
        if (scr <= SPI_PL022_MAX_SCR_VALUE) {
            *cr0 &= ~SPI_PL022_SSPCR0_SCR_MSK;
            *cr0 |= (scr << SPI_PL022_SSPCR0_SCR_POS);
            return clkps_dvsr;
        }
    }

    /* no good value was found */
    *cr0 &= ~SPI_PL022_SSPCR0_SCR_MSK;
    return SPI_PL022_INVALID_SSPCPSR_VALUE;
}

void spi_pl022_dev_enable(struct spi_pl022_dev_t* dev)
{
    struct spi_pl022_dev_reg_map_t* p_spi =
                              (struct spi_pl022_dev_reg_map_t*) dev->cfg->base;

    p_spi->sspcr1 |= SPI_PL022_SSPCR1_SSE_MSK;
}

void spi_pl022_dev_disable(struct spi_pl022_dev_t* dev)
{
    struct spi_pl022_dev_reg_map_t* p_spi =
                              (struct spi_pl022_dev_reg_map_t*) dev->cfg->base;

    p_spi->sspcr1 &= ~SPI_PL022_SSPCR1_SSE_MSK;
}

uint32_t spi_pl022_get_status(struct spi_pl022_dev_t* dev)
{
    struct spi_pl022_dev_reg_map_t* p_spi =
                               (struct spi_pl022_dev_reg_map_t*) dev->cfg->base;

    return p_spi->sspsr;
}

/*
 * \brief Configures the SPI PL022 device.
 *
 * \param[in] spi_dev   Pointer to SPI memory map \ref spi_pl022_dev_reg_map_t
 * \param[in] ctrl_cfg  SPI control configuration \ref spi_pl022_ctrl_cfg_t
 * \param[in] sys_clk   System clock.
 *
 * \return Error code from \ref spi_pl022_error_t
 */
static enum spi_pl022_error_t spi_set_configuration(
                                    struct spi_pl022_dev_reg_map_t* spi_dev,
                                    const struct spi_pl022_ctrl_cfg_t* ctrl_cfg,
                                    uint32_t sys_clk)
{
    uint32_t tmp_cr0, tmp_cr1;
    uint32_t clk_dvsr;

    if(!sys_clk || !ctrl_cfg->bit_rate) {
        return SPI_PL022_ERR_INVALID_ARGS;
    }

    /* Word size */
    tmp_cr0 = ((ctrl_cfg->word_size -1) << SPI_PL022_SSPCR0_DSS_POS)
              & SPI_PL022_SSPCR0_DSS_MSK;

    /* Frame format is stored in the least 2 bits*/
    switch(ctrl_cfg->frame_format & 0x3ul)
    {
    case SPI_PL022_CFG_FRF_MOT:
        tmp_cr0 |= (SPI_PL022_SSPCR0_FRF_MOT_SPI << SPI_PL022_SSPCR0_FRF_POS)
                   & SPI_PL022_SSPCR0_FRF_MSK;
        /* Add motorola phase & polarity */
        tmp_cr0 |= (SPI_PL022_SSPCR0_SPO_MSK & SPI_PL022_SSPCR0_SPH_MSK);
        break;
    case SPI_PL022_CFG_FRF_TI:
        tmp_cr0 |= (SPI_PL022_SSPCR0_FRF_TI_SS << SPI_PL022_SSPCR0_FRF_POS)
                   & SPI_PL022_SSPCR0_FRF_MSK;
        break;
    case SPI_PL022_CFG_FRF_MICROWIRE:
        tmp_cr0 |= (SPI_PL022_SSPCR0_FRF_MICROWIRE << SPI_PL022_SSPCR0_FRF_POS)
                   & SPI_PL022_SSPCR0_FRF_MSK;
        break;
    default:
        return SPI_PL022_ERR_BAD_CONFIG;
        /* break; */
    }

    /* Clock logic */
    clk_dvsr = spi_calc_clock_rate(ctrl_cfg, sys_clk, &tmp_cr0);

    if (SPI_PL022_INVALID_SSPCPSR_VALUE == clk_dvsr) {
        return SPI_PL022_ERR_BAD_CONFIG;
    }

    /* Enable device and set configured mode */
    tmp_cr1 = (0x1 << SPI_PL022_SSPCR1_SSE_POS) & SPI_PL022_SSPCR1_SSE_MSK;
    tmp_cr1 |= ((ctrl_cfg->spi_mode << SPI_PL022_SSPCR1_MS_POS)
                                                    & SPI_PL022_SSPCR1_MS_MSK);


    /* Start initialization by disabling the device */
    spi_dev->sspcr1 = 0;

    /* Set the value received for the configuration */
    spi_dev->sspcpsr = clk_dvsr;
    spi_dev->sspcr0  = tmp_cr0;

    /* Default setup hard coded */
    spi_dev->sspimsc  = 0;
    spi_dev->sspdmacr = 0;
    spi_dev->sspicr = (SPI_PL022_SSPICR_RORIC_MSK | SPI_PL022_SSPICR_RTIC_MSK);

    spi_dev->sspcr1 = tmp_cr1;

    return SPI_PL022_ERR_NONE;
}

enum spi_pl022_error_t spi_pl022_init(struct spi_pl022_dev_t* dev,
                                      uint32_t sys_clk)
{
    enum spi_pl022_error_t ret;
    struct spi_pl022_dev_reg_map_t* p_spi =
                               (struct spi_pl022_dev_reg_map_t*) dev->cfg->base;

    ret = spi_set_configuration(p_spi, &dev->cfg->default_ctrl_cfg, sys_clk);

    if(ret != SPI_PL022_ERR_NONE) {
        return ret;
    }

    dev->data->sys_clk = sys_clk;

    /* Initilizes current SPI control configuration */
    memcpy(&dev->data->ctrl_cfg, &dev->cfg->default_ctrl_cfg,
           sizeof(struct spi_pl022_ctrl_cfg_t));

    dev->data->state = SPI_PL022_INITIALIZED;

    return ret;
}

enum spi_pl022_error_t spi_pl022_set_ctrl_cfg(struct spi_pl022_dev_t* dev,
                                    const struct spi_pl022_ctrl_cfg_t* ctrl_cfg)
{
    enum spi_pl022_error_t ret;
    struct spi_pl022_dev_reg_map_t* p_spi =
                               (struct spi_pl022_dev_reg_map_t*) dev->cfg->base;

    if(!(dev->data->state & SPI_PL022_INITIALIZED)) {
        return SPI_PL022_ERR_NOT_INIT;
    }

    if(ctrl_cfg == NULL) {
        return SPI_PL022_ERR_INVALID_ARGS;
    }

    ret = spi_set_configuration(p_spi, ctrl_cfg, dev->data->sys_clk);

    if(ret != SPI_PL022_ERR_NONE) {
        return ret;
    }

    /* Updates current SPI control configuration */
    memcpy(&dev->data->ctrl_cfg, ctrl_cfg,
           sizeof(struct spi_pl022_ctrl_cfg_t));

    return SPI_PL022_ERR_NONE;
}

enum spi_pl022_error_t spi_pl022_get_ctrl_cfg(struct spi_pl022_dev_t* dev,
                                          struct spi_pl022_ctrl_cfg_t* ctrl_cfg)
{
    if(!(dev->data->state & SPI_PL022_INITIALIZED)) {
        return SPI_PL022_ERR_NOT_INIT;
    }

    if(ctrl_cfg == NULL) {
        return SPI_PL022_ERR_INVALID_ARGS;
    }

    /* Copy current SPI control configuration */
    memcpy(ctrl_cfg, &dev->data->ctrl_cfg,
           sizeof(struct spi_pl022_ctrl_cfg_t));

    return SPI_PL022_ERR_NONE;
}

void spi_pl022_select_mode(struct spi_pl022_dev_t* dev,
                           enum spi_pl022_mode_select_t mode)
{
    struct spi_pl022_dev_reg_map_t* p_spi =
                             (struct spi_pl022_dev_reg_map_t*) dev->cfg->base;


    /* Disable device */
    p_spi->sspcr1 &= ~SPI_PL022_SSPCR1_SSE_MSK;
    /* Set mode */
    p_spi->sspcr1 = (p_spi->sspcr1 & ~SPI_PL022_SSPCR1_MS_MSK)
                                           | (mode << SPI_PL022_SSPCR1_MS_POS);
    dev->data->ctrl_cfg.spi_mode = mode;
    /* Re-enable device */
    p_spi->sspcr1 |= SPI_PL022_SSPCR1_SSE_MSK;
}

void spi_pl022_set_slave_output(struct spi_pl022_dev_t* dev,
                                enum spi_pl022_slave_output_mode_t mode)
{
    struct spi_pl022_dev_reg_map_t* p_spi =
                               (struct spi_pl022_dev_reg_map_t*) dev->cfg->base;

    p_spi->sspcr1 = (p_spi->sspcr1 & ~SPI_PL022_SSPCR1_SOD_MSK)
                                          | (mode << SPI_PL022_SSPCR1_SOD_POS);
}

void spi_pl022_set_loopback_mode(struct spi_pl022_dev_t* dev,
                                 enum spi_pl022_loopback_select_t mode)
{
    struct spi_pl022_dev_reg_map_t* p_spi =
                               (struct spi_pl022_dev_reg_map_t*) dev->cfg->base;

    p_spi->sspcr1 = (p_spi->sspcr1 & ~SPI_PL022_SSPCR1_LBM_MSK)
                                          | (mode << SPI_PL022_SSPCR1_LBM_POS);
}

void spi_pl022_enable_interrupt(struct spi_pl022_dev_t* dev,
                                uint32_t irq_mask)
{
    struct spi_pl022_dev_reg_map_t* p_spi =
                              (struct spi_pl022_dev_reg_map_t*) dev->cfg->base;

    p_spi->sspimsc |= (irq_mask & SPI_PL022_SSPIMSC_VALID_MSK);
}

void spi_pl022_disable_interrupt(struct spi_pl022_dev_t* dev,
                                 uint32_t irq_mask)
{
    struct spi_pl022_dev_reg_map_t* p_spi =
                              (struct spi_pl022_dev_reg_map_t*) dev->cfg->base;

    p_spi->sspimsc &= ~(irq_mask & SPI_PL022_SSPIMSC_VALID_MSK);
}

uint32_t spi_pl022_get_raw_irq_status(struct spi_pl022_dev_t* dev)
{
    struct spi_pl022_dev_reg_map_t* p_spi =
                              (struct spi_pl022_dev_reg_map_t*) dev->cfg->base;

    return (p_spi->sspris & SPI_PL022_SSPRIS_VALID_MSK);
}

uint32_t spi_pl022_get_masked_irq_status(struct spi_pl022_dev_t* dev)
{
    struct spi_pl022_dev_reg_map_t* p_spi =
                              (struct spi_pl022_dev_reg_map_t*) dev->cfg->base;

    return (p_spi->sspmis & SPI_PL022_SSPMIS_VALID_MSK);
}

void spi_pl022_clear_interrupt(struct spi_pl022_dev_t* dev,
                               uint32_t irq_mask)
{
    struct spi_pl022_dev_reg_map_t* p_spi =
                              (struct spi_pl022_dev_reg_map_t*) dev->cfg->base;

    p_spi->sspicr = (irq_mask & SPI_PL022_SSPICR_VALID_MSK);
}

void spi_pl022_dma_mode_enable(struct spi_pl022_dev_t* dev,
                               uint32_t dma)
{
    struct spi_pl022_dev_reg_map_t* p_spi =
                               (struct spi_pl022_dev_reg_map_t*) dev->cfg->base;

    p_spi->sspdmacr |= (dma & SPI_PL022_SSPDMACR_VALID_MSK);
}

void spi_pl022_dma_mode_disable(struct spi_pl022_dev_t* dev,
                                uint32_t dma)
{
    struct spi_pl022_dev_reg_map_t* p_spi =
                               (struct spi_pl022_dev_reg_map_t*) dev->cfg->base;

    p_spi->sspdmacr &= ~(dma & SPI_PL022_SSPDMACR_VALID_MSK);
}

void spi_pl022_get_periphID(struct spi_pl022_dev_t* dev,
                            struct spi_pl022_periphid_t* periphid)
{
    uint32_t tempid, tempid2;
    struct spi_pl022_dev_reg_map_t* p_spi =
                                   (struct spi_pl022_dev_reg_map_t*) dev->cfg->base;

    tempid = p_spi->sspperiphid0 & SPI_PL022_SSPPERIPH_ID0_PARTNO_0_MSK;
    tempid2 = (p_spi->sspperiphid1 & SPI_PL022_SSPPERIPH_ID1_PARTNO_1_MSK)
                                    << SPI_PL022_SSPPERIPH_ID0_PARTNO_0_SIZE;
    periphid->partNumber = tempid | tempid2;

    tempid =  (p_spi->sspperiphid1 & SPI_PL022_SSPPERIPH_ID1_DESIGNER_0_MSK)
                                    >> SPI_PL022_SSPPERIPH_ID1_DESIGNER_0_POS;
    tempid2 = (p_spi->sspperiphid2 & SPI_PL022_SSPPERIPH_ID2_DESIGNER_1_MSK)
                                    << SPI_PL022_SSPPERIPH_ID1_DESIGNER_0_SIZE;
    periphid->designerID = tempid | tempid2;

    tempid = (p_spi->sspperiphid2 & SPI_PL022_SSPPERIPH_ID2_REVIISON_MSK)
                                    >> SPI_PL022_SSPPERIPH_ID2_REVISION_SIZE;
    periphid->revision = tempid;

    tempid = p_spi->sspperiphid3 & SPI_PL022_SSPPERIPH_ID3_CONFIG_MSK;
    periphid->configuration = tempid;
}

void spi_pl022_get_PrimeCell_ID(struct spi_pl022_dev_t* dev,
                          struct spi_pl022_primecell_id_t* cellid)
{
    struct spi_pl022_dev_reg_map_t* p_spi =
                                   (struct spi_pl022_dev_reg_map_t*) dev->cfg->base;

    cellid->cellid0 = (uint8_t) p_spi->ssppcellid0;
    cellid->cellid1 = (uint8_t) p_spi->ssppcellid1;
    cellid->cellid2 = (uint8_t) p_spi->ssppcellid2;
    cellid->cellid3 = (uint8_t) p_spi->ssppcellid3;
}

enum spi_pl022_error_t spi_pl022_set_sys_clk(struct spi_pl022_dev_t* dev,
                                             uint32_t sys_clk)
{
    uint32_t clk_dvsr;
    struct spi_pl022_dev_reg_map_t* p_spi =
                               (struct spi_pl022_dev_reg_map_t*) dev->cfg->base;

    if(!(dev->data->state & SPI_PL022_INITIALIZED)) {
        return SPI_PL022_ERR_NOT_INIT;
    }

    if(!sys_clk) {
        return SPI_PL022_ERR_INVALID_ARGS;
    }

    clk_dvsr = spi_calc_clock_rate(&dev->data->ctrl_cfg, sys_clk, &p_spi->sspcr0);

    if(SPI_PL022_INVALID_SSPCPSR_VALUE == clk_dvsr) {
        return SPI_PL022_ERR_BAD_CONFIG;
    }

    p_spi->sspcpsr = clk_dvsr;
    dev->data->sys_clk = sys_clk;

    return SPI_PL022_ERR_NONE;
}

enum spi_pl022_error_t spi_pl022_read(struct spi_pl022_dev_t* dev,
                                      void *rx_ptr)
{
    struct spi_pl022_dev_reg_map_t* p_spi =
                               (struct spi_pl022_dev_reg_map_t*) dev->cfg->base;

    if(p_spi->sspsr & SPI_PL022_SSPSR_RNE_MSK) {
        if(dev->data->ctrl_cfg.word_size <= 8) {
            *(uint8_t*)rx_ptr = p_spi->sspdr & WORD_1BYTE_MASK;
        } else {
            *(uint16_t*)rx_ptr = p_spi->sspdr & WORD_2BYTES_MASK;
        }
        return SPI_PL022_ERR_NONE;
    }
    return SPI_PL022_ERR_NO_RX;
}

uint32_t spi_pl022_slave_read(struct spi_pl022_dev_t* dev)
{
    struct spi_pl022_dev_reg_map_t* p_spi =
                               (struct spi_pl022_dev_reg_map_t*) dev->cfg->base;
    uint32_t data;

    if(dev->data->ctrl_cfg.word_size <= 8) {
        data = p_spi->sspdr & WORD_1BYTE_MASK;
    } else {
        data = p_spi->sspdr & WORD_2BYTES_MASK;
    }
    return data;
}

enum spi_pl022_error_t spi_pl022_write(struct spi_pl022_dev_t* dev,
                                       const enum spi_pl022_mode_select_t mode,
                                       const void *tx_ptr)
{
   struct spi_pl022_dev_reg_map_t* p_spi =
                               (struct spi_pl022_dev_reg_map_t*) dev->cfg->base;

    if(p_spi->sspsr & SPI_PL022_SSPSR_TNF_MSK){
        if(dev->data->ctrl_cfg.word_size <= 8) {
            p_spi->sspdr = *(const uint8_t*)tx_ptr;
        } else {
            p_spi->sspdr = *(const uint16_t*)tx_ptr;
        }
        /* Wait for write to go through */
        if (mode == SPI_PL022_MASTER_SELECT) {
            while(p_spi->sspsr & SPI_PL022_SSPSR_BSY_MSK) {};
        }
        return SPI_PL022_ERR_NONE;
    }
    return SPI_PL022_ERR_NO_TX;
}

enum spi_pl022_error_t spi_pl022_txrx_blocking(struct spi_pl022_dev_t* dev,
                                               const void *tx_ptr,
                                               uint32_t *tx_len_ptr,
                                               void *rx_ptr,
                                               uint32_t *rx_len_ptr)
{
    uint32_t i;
    enum spi_pl022_error_t retval = SPI_PL022_ERR_NONE;
    uint8_t word_size = 1;
    uint32_t rx_data, tx_data, total_len;

    if(dev->data->ctrl_cfg.word_size > 8) {
        word_size = 2;
        /* return error if sizes are not word_size aligned */
        if ((*tx_len_ptr & 0x1) || (*rx_len_ptr & 0x1)) {
            return SPI_PL022_ERR_INVALID_ARGS;
        }
    }

    total_len = (*tx_len_ptr > *rx_len_ptr) ? *tx_len_ptr : *rx_len_ptr;

    for(i=0;i<total_len;i+=word_size){
        if (i<*tx_len_ptr) {
            tx_data = *(const uint16_t*)tx_ptr;
        } else {
            /* send FF if there is no more valid data to send */
            tx_data = 0xFFFF;
        }
        retval = spi_pl022_write(dev, dev->data->ctrl_cfg.spi_mode, &tx_data);
        if(retval != SPI_PL022_ERR_NONE) {
            *tx_len_ptr = i;
            *rx_len_ptr = i;
            break;
        }
        if(i < *tx_len_ptr) {
            tx_ptr = (const uint8_t*)tx_ptr + word_size;
        }
        retval = spi_pl022_read(dev, &rx_data);
        if(retval != SPI_PL022_ERR_NONE) {
            /* send went through, align tx_len to the updated tx_ptr */
            *tx_len_ptr = i + word_size;
            /* don't update rx_len if there is an overflow */
            if (i < *rx_len_ptr) {
                *rx_len_ptr = i;
            }
            break;
        }
        /* do not overflow rx buffer */
        if(i<*rx_len_ptr) {
            if (word_size == 1) {
                *(uint8_t*)rx_ptr = (uint8_t) rx_data;
            } else {
                *(uint16_t*)rx_ptr = (uint16_t) rx_data;
            }
            rx_ptr = (uint8_t*)rx_ptr + word_size;
        }
    }

    return retval;
}

/*
 * TEST APIs
 */
void spi_pl022_test_fifo_enable(struct spi_pl022_dev_t* dev)
{
    struct spi_pl022_dev_test_reg_map_t* p_spi =
                 (struct spi_pl022_dev_test_reg_map_t*)
                                    (dev->cfg->base + SPI_PL022_TEST_REG_BASE);

    p_spi->ssptcr |= SPI_PL022_SSPTCR_TESTFIFO_MSK;
}

void spi_pl022_test_fifo_disable(struct spi_pl022_dev_t* dev)
{
    struct spi_pl022_dev_test_reg_map_t* p_spi =
                 (struct spi_pl022_dev_test_reg_map_t*)
                                    (dev->cfg->base + SPI_PL022_TEST_REG_BASE);

    p_spi->ssptcr &= ~SPI_PL022_SSPTCR_TESTFIFO_MSK;
}

void spi_pl022_integration_test_enable(struct spi_pl022_dev_t* dev)
{
    struct spi_pl022_dev_test_reg_map_t* p_spi =
                 (struct spi_pl022_dev_test_reg_map_t*)
                                    (dev->cfg->base + SPI_PL022_TEST_REG_BASE);

    p_spi->ssptcr |= SPI_PL022_SSPTCR_ITEN_MSK;
}

void spi_pl022_integration_test_disable(struct spi_pl022_dev_t* dev)
{
    struct spi_pl022_dev_test_reg_map_t* p_spi =
                 (struct spi_pl022_dev_test_reg_map_t*)
                                    (dev->cfg->base + SPI_PL022_TEST_REG_BASE);

    p_spi->ssptcr &= ~SPI_PL022_SSPTCR_ITEN_MSK;
}


void spi_pl022_write_test_data(struct spi_pl022_dev_t* dev,
                                                    void *tx_ptr)
{
    struct spi_pl022_dev_test_reg_map_t* p_spi =
                 (struct spi_pl022_dev_test_reg_map_t*)
                                    (dev->cfg->base + SPI_PL022_TEST_REG_BASE);

    if(dev->data->ctrl_cfg.word_size <= 8) {
        p_spi->ssptdr = *(const uint8_t*)tx_ptr;
    } else {
        p_spi->ssptdr = *(const uint16_t*)tx_ptr;
    }
}

uint32_t spi_pl022_read_test_output_reg(struct spi_pl022_dev_t* dev)
{
    struct spi_pl022_dev_test_reg_map_t* p_spi =
                 (struct spi_pl022_dev_test_reg_map_t*)
                                    (dev->cfg->base + SPI_PL022_TEST_REG_BASE);

    return p_spi->sspitop;
}