/**
 * \file   reset_inval.c
 * \date   December 14, 2014
 * \author Cesar Fuguet
 */

#include <reset_inval.h>
#include <io.h>
#include <defs.h>

#ifndef SEG_MMC_BASE
#   error "SEG_MMC_BASE constant must be defined in the hard_config.h file"
#endif

static int* const mcc_address = (int* const)SEG_MMC_BASE;

enum memc_registers
{
    MCC_ADDR_LO   = 0,
    MCC_ADDR_HI   = 1,
    MCC_LENGTH    = 2,
    MCC_CMD       = 3
};

enum memc_operations
{
    MCC_CMD_NOP   = 0,
    MCC_CMD_INVAL = 1,
    MCC_CMD_SYNC  = 2
};

/**
 * \brief Invalidate all data cache lines corresponding to a memory buffer
 *        (identified by an address and a size) in L1 cache and L2 cache.
 */
void reset_buf_invalidate (void* const buffer, size_t size, int inval_memc)
{
    unsigned int i;

    // iterate on L1 cache lines containing target buffer
    for (i = 0; i <= size; i += CACHE_LINE_SIZE)
    {
        asm volatile(
            " cache %0, %1"
            : /* no outputs */
            : "i" (0x11), "R" (*((char*)buffer + i))
            : "memory"
            );
    }

    if (inval_memc)
    {
        // this preloader uses only the cluster 0
        // It does not use the ADDR_HI bits, and does not take
        // any lock for exclusive access to MCC 
        iowrite32(&mcc_address[MCC_ADDR_LO], (unsigned int) buffer);
        iowrite32(&mcc_address[MCC_ADDR_HI], (unsigned int) 0);
        iowrite32(&mcc_address[MCC_LENGTH] , (unsigned int) size);
        iowrite32(&mcc_address[MCC_CMD]    , (unsigned int) MCC_CMD_INVAL);
    }
}

/*
 * vim: tabstop=4 : softtabstop=4 : shiftwidth=4 : expandtab
 */
