/*
 * prussdrv.c
 *
 * User space driver for PRUSS
 *
 * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
 *
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *    Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *    Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the
 *    distribution.
 *
 *    Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
*/


/*
 * ============================================================================
 * Copyright (c) Texas Instruments Inc 2010-12
 *
 * Use of this software is controlled by the terms and conditions found in the
 * license agreement under which this software has been supplied or provided.
 * ============================================================================
 */


#include <prussdrv.h>
#include "__prussdrv.h"
#include <stdio.h>

#ifdef __DEBUG
#define DEBUG_PRINTF(FORMAT, ...) fprintf(stderr, FORMAT, ## __VA_ARGS__)
#else
#define DEBUG_PRINTF(FORMAT, ...)
#endif

#define PRUSS_UIO_PRAM_PATH_LEN 128
#define PRUSS_UIO_PARAM_VAL_LEN 20
#define HEXA_DECIMAL_BASE 16

static tprussdrv prussdrv;

int __prussdrv_memmap_init(void)
{
    int i, fd;
    char hexstring[PRUSS_UIO_PARAM_VAL_LEN];

    if (prussdrv.mmap_fd == 0) {
        for (i = 0; i < NUM_PRU_HOSTIRQS; i++) {
            if (prussdrv.fd[i])
                break;
        }
        if (i == NUM_PRU_HOSTIRQS)
            return -1;
        else
            prussdrv.mmap_fd = prussdrv.fd[i];
    }
    fd = open(PRUSS_UIO_DRV_PRUSS_BASE, O_RDONLY);
    if (fd >= 0) {
        read(fd, hexstring, PRUSS_UIO_PARAM_VAL_LEN);
        prussdrv.pruss_phys_base =
            strtoul(hexstring, NULL, HEXA_DECIMAL_BASE);
        close(fd);
    } else
        return -1;
    fd = open(PRUSS_UIO_DRV_PRUSS_SIZE, O_RDONLY);
    if (fd >= 0) {
        read(fd, hexstring, PRUSS_UIO_PARAM_VAL_LEN);
        prussdrv.pruss_map_size =
            strtoul(hexstring, NULL, HEXA_DECIMAL_BASE);
        close(fd);
    } else
        return -1;

    prussdrv.pru0_dataram_base =
        mmap(0, prussdrv.pruss_map_size, PROT_READ | PROT_WRITE,
             MAP_SHARED, prussdrv.mmap_fd, PRUSS_UIO_MAP_OFFSET_PRUSS);
    prussdrv.version =
        __pruss_detect_hw_version(prussdrv.pru0_dataram_base);

    switch (prussdrv.version) {
    case PRUSS_V1:
        {
            DEBUG_PRINTF(PRUSS_V1_STR "\n");
            prussdrv.pru0_dataram_phy_base = AM18XX_DATARAM0_PHYS_BASE;
            prussdrv.pru1_dataram_phy_base = AM18XX_DATARAM1_PHYS_BASE;
            prussdrv.intc_phy_base = AM18XX_INTC_PHYS_BASE;
            prussdrv.pru0_control_phy_base = AM18XX_PRU0CONTROL_PHYS_BASE;
            prussdrv.pru0_debug_phy_base = AM18XX_PRU0DEBUG_PHYS_BASE;
            prussdrv.pru1_control_phy_base = AM18XX_PRU1CONTROL_PHYS_BASE;
            prussdrv.pru1_debug_phy_base = AM18XX_PRU1DEBUG_PHYS_BASE;
            prussdrv.pru0_iram_phy_base = AM18XX_PRU0IRAM_PHYS_BASE;
            prussdrv.pru1_iram_phy_base = AM18XX_PRU1IRAM_PHYS_BASE;
        }
        break;
    case PRUSS_V2:
        {
            DEBUG_PRINTF(PRUSS_V2_STR "\n");
            prussdrv.pru0_dataram_phy_base = AM33XX_DATARAM0_PHYS_BASE;
            prussdrv.pru1_dataram_phy_base = AM33XX_DATARAM1_PHYS_BASE;
            prussdrv.intc_phy_base = AM33XX_INTC_PHYS_BASE;
            prussdrv.pru0_control_phy_base = AM33XX_PRU0CONTROL_PHYS_BASE;
            prussdrv.pru0_debug_phy_base = AM33XX_PRU0DEBUG_PHYS_BASE;
            prussdrv.pru1_control_phy_base = AM33XX_PRU1CONTROL_PHYS_BASE;
            prussdrv.pru1_debug_phy_base = AM33XX_PRU1DEBUG_PHYS_BASE;
            prussdrv.pru0_iram_phy_base = AM33XX_PRU0IRAM_PHYS_BASE;
            prussdrv.pru1_iram_phy_base = AM33XX_PRU1IRAM_PHYS_BASE;
            prussdrv.pruss_sharedram_phy_base =
                AM33XX_PRUSS_SHAREDRAM_BASE;
            prussdrv.pruss_cfg_phy_base = AM33XX_PRUSS_CFG_BASE;
            prussdrv.pruss_uart_phy_base = AM33XX_PRUSS_UART_BASE;
            prussdrv.pruss_iep_phy_base = AM33XX_PRUSS_IEP_BASE;
            prussdrv.pruss_ecap_phy_base = AM33XX_PRUSS_ECAP_BASE;
            prussdrv.pruss_miirt_phy_base = AM33XX_PRUSS_MIIRT_BASE;
            prussdrv.pruss_mdio_phy_base = AM33XX_PRUSS_MDIO_BASE;
        }
        break;
    default:
        DEBUG_PRINTF(PRUSS_UNKNOWN_STR "\n");
    }

    prussdrv.pru1_dataram_base =
        prussdrv.pru0_dataram_base + prussdrv.pru1_dataram_phy_base -
        prussdrv.pru0_dataram_phy_base;
    prussdrv.intc_base =
        prussdrv.pru0_dataram_base + prussdrv.intc_phy_base -
        prussdrv.pru0_dataram_phy_base;
    prussdrv.pru0_control_base =
        prussdrv.pru0_dataram_base + prussdrv.pru0_control_phy_base -
        prussdrv.pru0_dataram_phy_base;
    prussdrv.pru0_debug_base =
        prussdrv.pru0_dataram_base + prussdrv.pru0_debug_phy_base -
        prussdrv.pru0_dataram_phy_base;
    prussdrv.pru1_control_base =
        prussdrv.pru0_dataram_base + prussdrv.pru1_control_phy_base -
        prussdrv.pru0_dataram_phy_base;
    prussdrv.pru1_debug_base =
        prussdrv.pru0_dataram_base + prussdrv.pru1_debug_phy_base -
        prussdrv.pru0_dataram_phy_base;
    prussdrv.pru0_iram_base =
        prussdrv.pru0_dataram_base + prussdrv.pru0_iram_phy_base -
        prussdrv.pru0_dataram_phy_base;
    prussdrv.pru1_iram_base =
        prussdrv.pru0_dataram_base + prussdrv.pru1_iram_phy_base -
        prussdrv.pru0_dataram_phy_base;
    if (prussdrv.version == PRUSS_V2) {
        prussdrv.pruss_sharedram_base =
            prussdrv.pru0_dataram_base +
            prussdrv.pruss_sharedram_phy_base -
            prussdrv.pru0_dataram_phy_base;
        prussdrv.pruss_cfg_base =
            prussdrv.pru0_dataram_base + prussdrv.pruss_cfg_phy_base -
            prussdrv.pru0_dataram_phy_base;
        prussdrv.pruss_uart_base =
            prussdrv.pru0_dataram_base + prussdrv.pruss_uart_phy_base -
            prussdrv.pru0_dataram_phy_base;
        prussdrv.pruss_iep_base =
            prussdrv.pru0_dataram_base + prussdrv.pruss_iep_phy_base -
            prussdrv.pru0_dataram_phy_base;
        prussdrv.pruss_ecap_base =
            prussdrv.pru0_dataram_base + prussdrv.pruss_ecap_phy_base -
            prussdrv.pru0_dataram_phy_base;
        prussdrv.pruss_miirt_base =
            prussdrv.pru0_dataram_base + prussdrv.pruss_miirt_phy_base -
            prussdrv.pru0_dataram_phy_base;
        prussdrv.pruss_mdio_base =
            prussdrv.pru0_dataram_base + prussdrv.pruss_mdio_phy_base -
            prussdrv.pru0_dataram_phy_base;
    }
#ifndef DISABLE_L3RAM_SUPPORT
    fd = open(PRUSS_UIO_DRV_L3RAM_BASE, O_RDONLY);
    if (fd >= 0) {
        read(fd, hexstring, PRUSS_UIO_PARAM_VAL_LEN);
        prussdrv.l3ram_phys_base =
            strtoul(hexstring, NULL, HEXA_DECIMAL_BASE);
        close(fd);
    } else
        return -1;


    fd = open(PRUSS_UIO_DRV_L3RAM_SIZE, O_RDONLY);
    if (fd >= 0) {
        read(fd, hexstring, PRUSS_UIO_PARAM_VAL_LEN);
        prussdrv.l3ram_map_size =
            strtoul(hexstring, NULL, HEXA_DECIMAL_BASE);
        close(fd);
    } else
        return -1;

    prussdrv.l3ram_base =
        mmap(0, prussdrv.l3ram_map_size, PROT_READ | PROT_WRITE,
             MAP_SHARED, prussdrv.mmap_fd, PRUSS_UIO_MAP_OFFSET_L3RAM);
#endif

    fd = open(PRUSS_UIO_DRV_EXTRAM_BASE, O_RDONLY);
    if (fd >= 0) {
        read(fd, hexstring, PRUSS_UIO_PARAM_VAL_LEN);
        prussdrv.extram_phys_base =
            strtoul(hexstring, NULL, HEXA_DECIMAL_BASE);
        close(fd);
    } else
        return -1;

    fd = open(PRUSS_UIO_DRV_EXTRAM_SIZE, O_RDONLY);
    if (fd >= 0) {
        read(fd, hexstring, PRUSS_UIO_PARAM_VAL_LEN);
        prussdrv.extram_map_size =
            strtoul(hexstring, NULL, HEXA_DECIMAL_BASE);
        close(fd);
    } else
        return -1;


    prussdrv.extram_base =
        mmap(0, prussdrv.extram_map_size, PROT_READ | PROT_WRITE,
             MAP_SHARED, prussdrv.mmap_fd, PRUSS_UIO_MAP_OFFSET_EXTRAM);

    return 0;

}

int prussdrv_init(void)
{
    memset(&prussdrv, 0, sizeof(prussdrv));
    return 0;

}

int prussdrv_open(unsigned int host_interrupt)
{
    char name[PRUSS_UIO_PRAM_PATH_LEN];
    if (!prussdrv.fd[host_interrupt]) {
        sprintf(name, "/dev/uio%d", host_interrupt);
        prussdrv.fd[host_interrupt] = open(name, O_RDWR | O_SYNC);
        return __prussdrv_memmap_init();
    } else {
        return -1;

    }
}

int prussdrv_version() {
    return prussdrv.version;
}

const char * prussdrv_strversion(int version) {
    switch (version) {
        case PRUSS_V1:
            return PRUSS_V1_STR;
        case PRUSS_V2:
            return PRUSS_V2_STR;
        default:
            return PRUSS_UNKNOWN_STR;
    }
}

int prussdrv_pru_reset(unsigned int prunum)
{
    unsigned int *prucontrolregs;
    if (prunum == 0)
        prucontrolregs = (unsigned int *) prussdrv.pru0_control_base;
    else if (prunum == 1)
        prucontrolregs = (unsigned int *) prussdrv.pru1_control_base;
    else
        return -1;
    *prucontrolregs = 0;
    return 0;
}

int prussdrv_pru_enable(unsigned int prunum)
{
  return prussdrv_pru_enable_at(prunum, 0);
}

int prussdrv_pru_enable_at(unsigned int prunum, size_t addr)
{
    volatile uint32_t* prucontrolregs;
    if (prunum == 0)
        prucontrolregs = (volatile uint32_t *) prussdrv.pru0_control_base;
    else if (prunum == 1)
        prucontrolregs = (volatile uint32_t *) prussdrv.pru1_control_base;
    else
        return -1;

    /* address is in bytes and must be converted in 32 bits words */
    *prucontrolregs = ((uint32_t)(addr / sizeof(uint32_t)) << 16) | 2;

    return 0;

}

int prussdrv_pru_disable(unsigned int prunum)
{
    unsigned int *prucontrolregs;
    if (prunum == 0)
        prucontrolregs = (unsigned int *) prussdrv.pru0_control_base;
    else if (prunum == 1)
        prucontrolregs = (unsigned int *) prussdrv.pru1_control_base;
    else
        return -1;
    *prucontrolregs = 1;
    return 0;

}

int prussdrv_pru_write_memory(unsigned int pru_ram_id,
                              unsigned int wordoffset,
                              const unsigned int *memarea,
                              unsigned int bytelength)
{
    unsigned int *pruramarea, i, wordlength;
    switch (pru_ram_id) {
    case PRUSS0_PRU0_IRAM:
        pruramarea = (unsigned int *) prussdrv.pru0_iram_base;
        break;
    case PRUSS0_PRU1_IRAM:
        pruramarea = (unsigned int *) prussdrv.pru1_iram_base;
        break;
    case PRUSS0_PRU0_DATARAM:
        pruramarea = (unsigned int *) prussdrv.pru0_dataram_base;
        break;
    case PRUSS0_PRU1_DATARAM:
        pruramarea = (unsigned int *) prussdrv.pru1_dataram_base;
        break;
    case PRUSS0_SHARED_DATARAM:
        if (prussdrv.version != PRUSS_V2)
            return -1;
        pruramarea = (unsigned int *) prussdrv.pruss_sharedram_base;
        break;
    default:
        return -1;
    }


    wordlength = (bytelength + 3) >> 2; //Adjust length as multiple of 4 bytes
    for (i = 0; i < wordlength; i++) {
        *(pruramarea + i + wordoffset) = *(memarea + i);
    }
    return wordlength;

}


int prussdrv_pruintc_init(const tpruss_intc_initdata *prussintc_init_data)
{
    unsigned int *pruintc_io = (unsigned int *) prussdrv.intc_base;
    unsigned int i, mask1, mask2;

    pruintc_io[PRU_INTC_SIPR1_REG >> 2] = 0xFFFFFFFF;
    pruintc_io[PRU_INTC_SIPR2_REG >> 2] = 0xFFFFFFFF;

    for (i = 0; i < (NUM_PRU_SYS_EVTS + 3) >> 2; i++)
        pruintc_io[(PRU_INTC_CMR1_REG >> 2) + i] = 0;
    for (i = 0;
         ((prussintc_init_data->sysevt_to_channel_map[i].sysevt != -1)
          && (prussintc_init_data->sysevt_to_channel_map[i].channel !=
              -1)); i++) {
        __prussintc_set_cmr(pruintc_io,
                            prussintc_init_data->sysevt_to_channel_map[i].
                            sysevt,
                            prussintc_init_data->sysevt_to_channel_map[i].
                            channel);
    }
    for (i = 0; i < (NUM_PRU_HOSTS + 3) >> 2; i++)
        pruintc_io[(PRU_INTC_HMR1_REG >> 2) + i] = 0;
    for (i = 0;
         ((prussintc_init_data->channel_to_host_map[i].channel != -1)
          && (prussintc_init_data->channel_to_host_map[i].host != -1));
         i++) {

        __prussintc_set_hmr(pruintc_io,
                            prussintc_init_data->channel_to_host_map[i].
                            channel,
                            prussintc_init_data->channel_to_host_map[i].
                            host);
    }

    pruintc_io[PRU_INTC_SITR1_REG >> 2] = 0x0;
    pruintc_io[PRU_INTC_SITR2_REG >> 2] = 0x0;


    mask1 = mask2 = 0;
    for (i = 0; prussintc_init_data->sysevts_enabled[i] != 255; i++) {
        if (prussintc_init_data->sysevts_enabled[i] < 32) {
            mask1 =
                mask1 + (1 << (prussintc_init_data->sysevts_enabled[i]));
        } else if (prussintc_init_data->sysevts_enabled[i] < 64) {
            mask2 =
                mask2 +
                (1 << (prussintc_init_data->sysevts_enabled[i] - 32));
        } else {
            DEBUG_PRINTF("Error: SYS_EVT%d out of range\n",
			 prussintc_init_data->sysevts_enabled[i]);
            return -1;
        }
    }
    pruintc_io[PRU_INTC_ESR1_REG >> 2] = mask1;
    pruintc_io[PRU_INTC_SECR1_REG >> 2] = mask1;
    pruintc_io[PRU_INTC_ESR2_REG >> 2] = mask2;
    pruintc_io[PRU_INTC_SECR2_REG >> 2] = mask2;

    for (i = 0; i < MAX_HOSTS_SUPPORTED; i++)
        if (prussintc_init_data->host_enable_bitmask & (1 << i)) {
            pruintc_io[PRU_INTC_HIEISR_REG >> 2] = i;
        }

    pruintc_io[PRU_INTC_GER_REG >> 2] = 0x1;

    // Stash a copy of the intc settings
    memcpy( &prussdrv.intc_data, prussintc_init_data,
            sizeof(prussdrv.intc_data) );

    return 0;
}

short prussdrv_get_event_to_channel_map( unsigned int eventnum )
{
    unsigned int i;
    for (i = 0; i < NUM_PRU_SYS_EVTS &&
                prussdrv.intc_data.sysevt_to_channel_map[i].sysevt  !=-1 &&
                prussdrv.intc_data.sysevt_to_channel_map[i].channel !=-1; ++i) {
        if ( eventnum == prussdrv.intc_data.sysevt_to_channel_map[i].sysevt )
            return prussdrv.intc_data.sysevt_to_channel_map[i].channel;
    }
    return -1;
}

short prussdrv_get_channel_to_host_map( unsigned int channel )
{
    unsigned int i;
    for (i = 0; i < NUM_PRU_CHANNELS &&
                prussdrv.intc_data.channel_to_host_map[i].channel != -1 &&
                prussdrv.intc_data.channel_to_host_map[i].host    != -1; ++i) {
        if ( channel == prussdrv.intc_data.channel_to_host_map[i].channel )
            /** -2 is because first two host interrupts are reserved
             * for PRU0 and PRU1 */
            return prussdrv.intc_data.channel_to_host_map[i].host - 2;
    }
    return -1;
}

short prussdrv_get_event_to_host_map( unsigned int eventnum )
{
    short ans = prussdrv_get_event_to_channel_map( eventnum );
    if (ans < 0) return ans;
    return prussdrv_get_channel_to_host_map( ans );
}

int prussdrv_pru_send_event(unsigned int eventnum)
{
    unsigned int *pruintc_io = (unsigned int *) prussdrv.intc_base;
    if (eventnum < 32)
        pruintc_io[PRU_INTC_SRSR1_REG >> 2] = 1 << eventnum;
    else
        pruintc_io[PRU_INTC_SRSR2_REG >> 2] = 1 << (eventnum - 32);
    return 0;
}

unsigned int prussdrv_pru_wait_event(unsigned int host_interrupt)
{
    unsigned int event_count;
    read(prussdrv.fd[host_interrupt], &event_count, sizeof(int));
    return event_count;
}

int prussdrv_pru_event_fd(unsigned int host_interrupt)
{
    if (host_interrupt < NUM_PRU_HOSTIRQS)
        return prussdrv.fd[host_interrupt];
    else
        return -1;
}

int prussdrv_pru_clear_event(unsigned int host_interrupt, unsigned int sysevent)
{
    unsigned int *pruintc_io = (unsigned int *) prussdrv.intc_base;
    if (sysevent < 32)
        pruintc_io[PRU_INTC_SECR1_REG >> 2] = 1 << sysevent;
    else
        pruintc_io[PRU_INTC_SECR2_REG >> 2] = 1 << (sysevent - 32);

    // Re-enable the host interrupt.  Note that we must do this _after_ the
    // system event has been cleared so as to not re-tigger the interrupt line.
    // See Section 6.4.9 of Reference manual about HIEISR register.
    // The +2 is because the first two host interrupts are reserved for
    // PRU0 and PRU1.
    pruintc_io[PRU_INTC_HIEISR_REG >> 2] = host_interrupt+2;
    return 0;
}

int prussdrv_pru_send_wait_clear_event(unsigned int send_eventnum,
                                       unsigned int host_interrupt,
                                       unsigned int ack_eventnum)
{
    prussdrv_pru_send_event(send_eventnum);
    prussdrv_pru_wait_event(host_interrupt);
    prussdrv_pru_clear_event(host_interrupt, ack_eventnum);
    return 0;

}


int prussdrv_map_l3mem(void **address)
{
    *address = prussdrv.l3ram_base;
    return 0;
}



int prussdrv_map_extmem(void **address)
{

    *address = prussdrv.extram_base;
    return 0;

}

unsigned int prussdrv_extmem_size(void)
{
    return prussdrv.extram_map_size;
}

int prussdrv_map_prumem(unsigned int pru_ram_id, void **address)
{
    switch (pru_ram_id) {
    case PRUSS0_PRU0_DATARAM:
        *address = prussdrv.pru0_dataram_base;
        break;
    case PRUSS0_PRU1_DATARAM:
        *address = prussdrv.pru1_dataram_base;
        break;
    case PRUSS0_SHARED_DATARAM:
        if (prussdrv.version != PRUSS_V2)
            return -1;
        *address = prussdrv.pruss_sharedram_base;
        break;
    default:
        *address = 0;
        return -1;
    }
    return 0;
}

int prussdrv_map_peripheral_io(unsigned int per_id, void **address)
{
    if (prussdrv.version != PRUSS_V2)
        return -1;

    switch (per_id) {
    case PRUSS0_CFG:
        *address = prussdrv.pruss_cfg_base;
        break;
    case PRUSS0_UART:
        *address = prussdrv.pruss_uart_base;
        break;
    case PRUSS0_IEP:
        *address = prussdrv.pruss_iep_base;
        break;
    case PRUSS0_ECAP:
        *address = prussdrv.pruss_ecap_base;
        break;
    case PRUSS0_MII_RT:
        *address = prussdrv.pruss_miirt_base;
        break;
    case PRUSS0_MDIO:
        *address = prussdrv.pruss_mdio_base;
        break;
    default:
        *address = 0;
        return -1;
    }
    return 0;
}

unsigned int prussdrv_get_phys_addr(const void *address)
{
    unsigned int retaddr = 0;
    if ((address >= prussdrv.pru0_dataram_base)
        && (address <
            prussdrv.pru0_dataram_base + prussdrv.pruss_map_size)) {
        retaddr =
            ((unsigned int) (address - prussdrv.pru0_dataram_base) +
             prussdrv.pru0_dataram_phy_base);
    } else if ((address >= prussdrv.l3ram_base)
               && (address <
                   prussdrv.l3ram_base + prussdrv.l3ram_map_size)) {
        retaddr =
            ((unsigned int) (address - prussdrv.l3ram_base) +
             prussdrv.l3ram_phys_base);
    } else if ((address >= prussdrv.extram_base)
               && (address <
                   prussdrv.extram_base + prussdrv.extram_map_size)) {
        retaddr =
            ((unsigned int) (address - prussdrv.extram_base) +
             prussdrv.extram_phys_base);
    }
    return retaddr;

}

void *prussdrv_get_virt_addr(unsigned int phyaddr)
{
    void *address = 0;
    if ((phyaddr >= prussdrv.pru0_dataram_phy_base)
        && (phyaddr <
            prussdrv.pru0_dataram_phy_base + prussdrv.pruss_map_size)) {
        address =
            (void *) ((unsigned int) prussdrv.pru0_dataram_base +
                      (phyaddr - prussdrv.pru0_dataram_phy_base));
    } else if ((phyaddr >= prussdrv.l3ram_phys_base)
               && (phyaddr <
                   prussdrv.l3ram_phys_base + prussdrv.l3ram_map_size)) {
        address =
            (void *) ((unsigned int) prussdrv.l3ram_base +
                      (phyaddr - prussdrv.l3ram_phys_base));
    } else if ((phyaddr >= prussdrv.extram_phys_base)
               && (phyaddr <
                   prussdrv.extram_phys_base + prussdrv.extram_map_size)) {
        address =
            (void *) ((unsigned int) prussdrv.extram_base +
                      (phyaddr - prussdrv.extram_phys_base));
    }
    return address;

}


int prussdrv_exit()
{
    int i;
    munmap(prussdrv.pru0_dataram_base, prussdrv.pruss_map_size);
    munmap(prussdrv.l3ram_base, prussdrv.l3ram_map_size);
    munmap(prussdrv.extram_base, prussdrv.extram_map_size);
    for (i = 0; i < NUM_PRU_HOSTIRQS; i++) {
        if (prussdrv.fd[i])
            close(prussdrv.fd[i]);
    }
    return 0;
}

int prussdrv_exec_program(int prunum, const char *filename)
{
  return prussdrv_exec_program_at(prunum, filename, 0);
}

int prussdrv_exec_program_at(int prunum, const char *filename, size_t addr)
{
    FILE *fPtr;
    unsigned char fileDataArray[PRUSS_MAX_IRAM_SIZE];
    int fileSize = 0;

    // Open an File from the hard drive
    fPtr = fopen(filename, "rb");
    if (fPtr == NULL) {
        DEBUG_PRINTF("File %s open failed\n", filename);
	return -1;
    } else {
        DEBUG_PRINTF("File %s open passed\n", filename);
    }
    // Read file size
    fseek(fPtr, 0, SEEK_END);
    fileSize = ftell(fPtr);

    if (fileSize == 0) {
        DEBUG_PRINTF("File read failed.. Closing program\n");
        fclose(fPtr);
        return -1;
    }

    fseek(fPtr, 0, SEEK_SET);

    if (fileSize !=
        fread((unsigned char *) fileDataArray, 1, fileSize, fPtr)) {
        DEBUG_PRINTF("WARNING: File Size mismatch\n");
	fclose(fPtr);
	return -1;
    }

    fclose(fPtr);

    return prussdrv_exec_code_at(prunum, (const unsigned int *) fileDataArray, fileSize, addr);
}

int prussdrv_exec_code(int prunum, const unsigned int *code, int codelen)
{
  return prussdrv_exec_code_at(prunum, code, codelen, 0);
}

int prussdrv_exec_code_at(int prunum, const unsigned int *code, int codelen, size_t addr)
{
    unsigned int pru_ram_id;

    if (prunum == 0)
        pru_ram_id = PRUSS0_PRU0_IRAM;
    else if (prunum == 1)
        pru_ram_id = PRUSS0_PRU1_IRAM;
    else
        return -1;

    // Make sure PRU sub system is first disabled/reset
    prussdrv_pru_disable(prunum);
    prussdrv_pru_write_memory(pru_ram_id, 0, code, codelen);
    prussdrv_pru_enable_at(prunum, addr);

    return 0;
}

int prussdrv_load_datafile(int prunum, const char *filename)
{
    FILE *fPtr;
    unsigned char fileDataArray[PRUSS_MAX_IRAM_SIZE];
    int fileSize = 0;

    // Open an File from the hard drive
    fPtr = fopen(filename, "rb");
    if (fPtr == NULL) {
        DEBUG_PRINTF("File %s open failed\n", filename);
	return -1;
    } else {
        DEBUG_PRINTF("File %s open passed\n", filename);
    }
    // Read file size
    fseek(fPtr, 0, SEEK_END);
    fileSize = ftell(fPtr);

    if (fileSize == 0) {
        DEBUG_PRINTF("File read failed.. Closing program\n");
        fclose(fPtr);
        return -1;
    }

    fseek(fPtr, 0, SEEK_SET);

    if (fileSize !=
        fread((unsigned char *) fileDataArray, 1, fileSize, fPtr)) {
        DEBUG_PRINTF("WARNING: File Size mismatch\n");
	fclose(fPtr);
	return -1;
    }

    fclose(fPtr);

    return prussdrv_load_data(prunum, (const unsigned int *) fileDataArray, fileSize);
}

int prussdrv_load_data(int prunum, const unsigned int *code, int codelen)
{
    unsigned int pru_ram_id;

    if (prunum == 0)
        pru_ram_id = PRUSS0_PRU0_DATARAM;
    else if (prunum == 1)
        pru_ram_id = PRUSS0_PRU1_DATARAM;
    else
        return -1;

    // Make sure PRU sub system is first disabled/reset
    prussdrv_pru_disable(prunum);
    prussdrv_pru_write_memory(pru_ram_id, 0, code, codelen);
    //prussdrv_pru_enable(prunum);

    return 0;
}
