#include <stdint.h>
#include <stdio.h>

#include "xmc.h"
#include "reg.h"
#include "dma.h"


#define MCBSP0_BASE 0x021B4000

#define DRR0  REG(MCBSP0_BASE + 0x00) // Data Receive Reg
#define DXR0  REG(MCBSP0_BASE + 0x04) // Data Transmit Reg
#define SPCR0 REG(MCBSP0_BASE + 0x08) // Serial Port Ctrl Reg
#define RCR0  REG(MCBSP0_BASE + 0x0C) // Receive Control Reg
#define XCR0  REG(MCBSP0_BASE + 0x10) // Transmit Control Reg
#define SRGR0 REG(MCBSP0_BASE + 0x14) // Sample Rate Generator Reg
#define PCR0  REG(MCBSP0_BASE + 0x24) // Pin Ctrl Reg

#define CPU_FREQ     0x3B9ACA00 // 1000 MHz
#define McBSP_FREQ0  0x007A1200 // 8 MHz

#define BITV(x) (1UL << x)

#define McBSP_SPCR_FRST  BITV(23)
#define McBSP_SPCR_GRST  BITV(22)
#define McBSP_SPCR_XRST  BITV(16)
#define McBSP_SPCR_DLB   BITV(15) // LoopBack-Mode
#define McBSP_SPCR_RFULL BITV(2)
#define McBSP_SPCR_RRDY  BITV(1)
#define McBSP_SPCR_RRST  BITV(0)

#define McBSP_SRGR_FSGM    BITV(28) // Frame Sync Generator = Yes
#define McBSP_SRGR_CLKSM   BITV(29) // Clock Sync Manager    = intern CPU/4
#define McBSP_SRGR_CLKSP   BITV(30) // falling edge triggers CLKS
#define McBSP_SRGR_GSYNC   BITV(31) // not "free runing"
#define McBSP_SRGR_SRG     McBSP_SRGR_CLKSM // internal CLCK (SCLKME not set)

#define McBSP_DATDLY_BITS  16
#define McBSP_WORDLEN      BITV(5)
#define McBSP_RFIG         BITV(18) // Frame Sync ignore
#define McBSP_REVERSED     (BITV(4) | BITV(19)) // REVERSED bit + COMPAND bit

#define McBSP_PCR_CLKRP    BITV(0) //  Clock Receive Polarity       = rising edge
#define McBSP_PCR_CLKXP    BITV(1) //  Clock Transmit Polarity      = falling edge
#define McBSP_PCR_FSRP     BITV(2) //  Frame Sync Receive Polarity  = Low active
#define McBSP_PCR_FSXP     BITV(3) //  Frame Sync Transmit Polarity = Low active
#define McBSP_PCR_POLARITY (McBSP_PCR_FSRP | McBSP_PCR_FSXP)
#define McBSP_PCR_SCLKME   BITV(7) //  set = external CLCK, 0 = internal CLCK
#define McBSP_PCR_CLKRM    BITV(8) //  Clock Receive Manager        = Output
#define McBSP_PCR_CLKXM    BITV(9) //  Clock Transmit Manager       = Output
#define McBSP_PCR_FSRM     BITV(10) //  Frame Sync Receive Manager   = Output
#define McBSP_PCR_FSXM     BITV(11) //  Frame Sync Transmit Manager  = Output
#define McBSP_PCR_PINIO    (McBSP_PCR_CLKXM | McBSP_PCR_FSXM)  // X-Pins are Output, R-Pins Input

void mcbsp_init(void)
{
    uint32_t McBSPClkDiv = CPU_FREQ / 6 / McBSP_FREQ0 - 1;
    SPCR0 = 0x00000000;                                 // resets everything

#define MCBSP_32BIT 5

    PCR0  = McBSP_PCR_POLARITY | McBSP_PCR_PINIO;       //  ~CLKRP, ~CLKXP, FSRP, FSXP,  ~CLKRM, CLKXM, ~FSRM, FSXM
    RCR0  = MCBSP_32BIT * McBSP_WORDLEN | ( 0 << McBSP_DATDLY_BITS ); //  RWDLEN1, RDATDLY  // | McBSP_RFIG not used
    XCR0  = MCBSP_32BIT * McBSP_WORDLEN | ( 0 << McBSP_DATDLY_BITS ); //  XWDLEN1, XDATDLY
    SRGR0 = McBSP_SRGR_SRG | McBSPClkDiv;               //  ~GSYNC, CLKSM, ~CLKSP, ~FSGM, FPER = FWID = 0, CLKGDV

    asm( " nop 9" );            //  nop 9 = 16 CPU cycles
    SPCR0 |= McBSP_SPCR_GRST;   // GRST Bit #22
    asm( " nop 9" );            //  nop 9 = 16 CPU cycles
    SPCR0 |= McBSP_SPCR_XRST;   // XRST Bit #16
    asm( " nop 9" );            //  nop 9 = 16 CPU cycles

    SPCR0 &= ~McBSP_SPCR_XRST;  // XRST Bit #16, reset again

    SPCR0 |= McBSP_SPCR_RRST | McBSP_SPCR_XRST;   // XRST Bit #16, RRST Bit #0

}

#pragma DATA_SECTION(mcbsp_buffer, ".shared_vars");
uint32_t volatile mcbsp_buffer;

#define DMA_CHAN_MCBSP0_RX   36
#define EDMA3_EVENT_MCBSP0_R 36

#define DRR0_ADDR (uint32_t)(MCBSP0_BASE + 0) // Data Receive Reg Addr



dma_params_t mcbsp_dma_config = {
 .channels = (uint32_t)EDMA_CHANNELS,
 .channel = DMA_CHAN_MCBSP0_RX,
 .link = EDMA3_EVENT_MCBSP0_R,
 .src            = DRR0_ADDR,
 .a_count        = sizeof(uint32_t),
 .b_count        = 1,
 .b_count_reload = 1,
 .c_count        = 1,
 .interrupt_on_complete = true,
 .completion_code = 36
};

dma_params_t mcbsp_dma_link_config = {
 .channels = (uint32_t)LINK_CHANNELS,
 .channel = DMA_CHAN_MCBSP0_RX,
 .link = EDMA3_EVENT_MCBSP0_R,
 .src            = DRR0_ADDR,
 .a_count        = sizeof(uint32_t),
 .b_count        = 1,
 .b_count_reload = 1,
 .c_count        = 1,
 .interrupt_on_complete = true,
 .completion_code = 36
};

void mcbsp0_send(uint32_t val)
{
    DXR0 = val;
}

void dma_init()
{
    mcbsp_dma_config.dst = MSMC_ANTI_ALIAS((uint32_t)&mcbsp_buffer);
    mcbsp_dma_link_config.dst = MSMC_ANTI_ALIAS((uint32_t)&mcbsp_buffer);

    EDMA_MAP(DMA_CHAN_MCBSP0_RX, DMA_CHAN_MCBSP0_RX);

    /* Event missed clear register */
    EMCRL = 0xFFFFFFFF;
    EMCRH = 0xFFFFFFFF;

    /* Secondary event clear register */
    SECRL = 0xFFFFFFFF;
    SECRH = 0xFFFFFFFF;

    dma_channel_init(&mcbsp_dma_config);
    dma_channel_init(&mcbsp_dma_link_config);

    EDMA_ENABLE(EDMA3_EVENT_MCBSP0_R);
    CIESRH |= (1UL << (DMA_CHAN_MCBSP0_RX & 0x1F));

}


void dma_channel_config_dump(int channel)
{
    pEDMA chan = &EDMA_CHANNELS[channel];

    printf("OPT: %08X\n", chan->OPT);
    printf("SRC: %08X\n", chan->SRC);
    printf("BACNT: %08X\n", chan->BACNT);
    printf("DST: %08X\n", chan->DST);
    printf("BIDX: %08X\n", chan->BIDX);
    printf("RDLNK: %08X\n", chan->RLDLNK);
    printf("CIDX: %08X\n", chan->CIDX);
    printf("CCNT: %08X\n", chan->CCNT);
}

static void mem_remap_uncached(void)
{
    /* MSMC SRAM is cached and this cache cannot be disabled. Every access to
     * the range 0x0C0000000-0x0CFFFFFFF is hence cached. This is also where
     * DMA and/or PCI operations take place. However, uncached access is
     * needed. The only possibility to accomplish this is by remapping this
     * address range using the XMC (eXtended Memory Controller)
     */

    XMC_REMAP(4, MSMC_ALIAS_ADDR, MSMC_START_ADDR, \
                 XMC_SEGSZ_1M, XMC_PERM_RW_RW_);

    XMC_MAR_Reg(XMC_LADDR_TO_MAR(MSMC_ALIAS_ADDR)) &=
        ~(XMC_MAR_PFX | XMC_MAR_PC);
}


void mcbsp_receiver_reset()
{
    SPCR0 &= ~1UL;
    SPCR0 |= 1UL;

}


int main(void)
{
    mem_remap_uncached();
    dma_reset();
    mcbsp_init();
    dma_init();

    /* now we should receive whatever we send, if we place hardware jumpers */
    uint32_t send_value;
    uint64_t x = 0x0000000055AA55AA;
    uint32_t recv_value;

    mcbsp_buffer = 0;
    send_value = x;

    EDMA_ACK(EDMA3_EVENT_MCBSP0_R);
    for (; x; x <<= 1) {
        send_value = (uint32_t)(x >> 32);
        printf("SPCR0 is: %08X\n", SPCR0);
//        printf("DMA channel params dump:");
//        dma_channel_config_dump(DMA_CHAN_MCBSP0_RX);
//        printf("Interrupt pending register before send: %08X:%08X\n", CIPRH, CIPRL);
        mcbsp0_send(send_value);
//        printf("Interrupt pending register before wait: %08X:%08X\n", CIPRH, CIPRL);
        EDMA_WAIT(EDMA3_EVENT_MCBSP0_R);
//        printf("Interrupt pending register after wait: %08X:%08X\n", CIPRH, CIPRL);

        //for (int i = 0; i < 10000; i++);
        if (mcbsp_buffer != send_value) {
            printf("Error, mcbsp received wrong data, got %08X instead of %08X\n", mcbsp_buffer, send_value);
        } else {
            printf("Sent: %08X, received %08X\n", send_value, mcbsp_buffer);
        }
        EDMA_ACK(EDMA3_EVENT_MCBSP0_R);
  //      printf("Interrupt pending register after EDMA_ACK: %08X:%08X\n", CIPRH, CIPRL);

        mcbsp_receiver_reset();
    }
    printf("Mcbsp test completed.\n");


    for (uint32_t i = 0; i < 5242880; i++) {};
    recv_value = DRR0;

    while(1) {};
    return 0;
}
