#include <stddef.h>
#include <assert.h>
#include "oc_spi16.h"

/*
 * SPI stuff, used by later code
 */

#define SPI_REG_RX0	0
#define SPI_REG_TX0	0
#define SPI_REG_RX1	4
#define SPI_REG_TX1	4
#define SPI_REG_RX2	8
#define SPI_REG_TX2	8
#define SPI_REG_RX3	12
#define SPI_REG_TX3	12

#define SPI_REG_CTRL	16
#define SPI_REG_DIVIDER	20
#define SPI_REG_SS	24

#define SPI_CTRL_ASS		(1<<13)
#define SPI_CTRL_IE		(1<<12)
#define SPI_CTRL_LSB		(1<<11)
#define SPI_CTRL_TXNEG		(1<<10)
#define SPI_CTRL_RXNEG		(1<<9)
#define SPI_CTRL_GO_BSY		(1<<8)
#define SPI_CTRL_CHAR_LEN(x)	((x) & 0x7f)

void libwr2rf_oc_spi_init(struct libwr2rf_dev *dev, unsigned base, unsigned div)
{
        /* Set data and  */
        for (int i = 0; i < 8; i++)
                libwr2rf_write16(dev, base + i * 2, 0);

	libwr2rf_write16(dev, base + SPI_REG_DIVIDER, div);
}

void libwr2rf_oc_spi_txrx128(struct libwr2rf_dev *dev,
                             unsigned base, unsigned cs,
                             int nbits, uint16_t *vals)
{
        unsigned i;
        unsigned nwords = (nbits + 15) / 16;

        /* Automatic CS control, drive MOSI line on the falling edge.   */
	libwr2rf_write16(dev, base + SPI_REG_CTRL,
                         SPI_CTRL_ASS | SPI_CTRL_CHAR_LEN(nbits)
                         | SPI_CTRL_TXNEG);

        /* Set data and CS.  */
        for (i = 0; i < nwords; i++)
                libwr2rf_write16(dev, base + i * 2, vals[i]);
        libwr2rf_write16(dev, base + SPI_REG_SS, (1 << cs));

        /* Send data.
           ASS: automatic chip select,
           /LSB: MSB is sent first,
           TXNEG: mosi is changed on the falling edge of clk,
           RXNEG: miso is latched on the falling edge of clk.  */
	libwr2rf_write16(dev, base + SPI_REG_CTRL,
                         SPI_CTRL_ASS | SPI_CTRL_CHAR_LEN(nbits)
                         | SPI_CTRL_TXNEG | SPI_CTRL_RXNEG | SPI_CTRL_GO_BSY);

        /* Wait until data sent.  */
	while(libwr2rf_read16(dev, base + SPI_REG_CTRL) & SPI_CTRL_GO_BSY)
		;

        for (i = 0; i < nwords; i++)
                vals[i] = libwr2rf_read16(dev, base + i * 2);
}

void libwr2rf_oc_spi_txrx16(struct libwr2rf_dev *dev,
                            unsigned base, unsigned cs,
                            int nbits, uint32_t in, uint32_t *out)
{
        uint16_t v;

        assert(nbits <= 16);
        v = in;
        libwr2rf_oc_spi_txrx128(dev, base, cs, nbits, &v);
        if (out)
                *out = libwr2rf_read16(dev, base + SPI_REG_RX0);
}
