/*
 * Copyright (c) 2014 - 2016 MediaTek Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#define TAG "[devapc]"
#include "mtk_log.h"

#include "mtk_dapc.h"
#include "mtk_generic.h"

int has_started;
unsigned char *devapc_ao_vaddr;

static inline unsigned int readl(volatile unsigned int* addr)
{
    return *(volatile unsigned int*)addr;
}

static inline void writel(unsigned int val, volatile unsigned int* addr)
{
    *(volatile unsigned int*)addr = val;
}

int set_master_transaction(unsigned int master_index, unsigned int transaction_type)
{
    volatile unsigned int* base = 0;
    unsigned int set_bit = 0;
    unsigned int master_register_index = 0;
    unsigned int master_set_index = 0;

    if (master_index > MASTER_MAX_INDEX)
        return -1;

    master_register_index = master_index / 32;
    master_set_index = master_index % 32;

    base = (volatile unsigned int*) (DEVAPC_MAS_SEC + master_register_index * 4);

    if (transaction_type == 0) {
        set_bit = ~(1 << master_set_index);
        writel(readl(base) & set_bit, base);
    } else if (transaction_type == 1) {
        set_bit = 1 << master_set_index;
        writel(readl(base) | set_bit, base);
    } else {
        return -2;
    }

    return 0;
}

void set_module_apc(unsigned int module, E_MAS_DOM domain_num, APC_ATTR permission_control)
{

    volatile unsigned int *base = 0;
    unsigned int clr_bit = 0x3 << ((module % MOD_NO_IN_1_DEVAPC) * 2);
    unsigned int set_bit = permission_control << ((module % MOD_NO_IN_1_DEVAPC) * 2);

    if (module >= DEVAPC_DEVICE_NUMBER) {
        TUI_LOGE("set_module_apc : device number %d exceeds the max number!\n", module);
        return;
    }

    if (domain_num == E_DOMAIN_0) {
        base = (volatile unsigned int*) (DEVAPC_D0_APC_0 + (module/MOD_NO_IN_1_DEVAPC) * 4);
    } else if (domain_num == E_DOMAIN_1) {
        base = (volatile unsigned int*) (DEVAPC_D1_APC_0 + (module/MOD_NO_IN_1_DEVAPC) * 4);
    } else if (domain_num == E_DOMAIN_2) {
        base = (volatile unsigned int*) (DEVAPC_D2_APC_0 + (module/MOD_NO_IN_1_DEVAPC) * 4);
    } else if (domain_num == E_DOMAIN_3) {
        base = (volatile unsigned int*) (DEVAPC_D3_APC_0 + (module/MOD_NO_IN_1_DEVAPC) * 4);
    } else if (domain_num == E_DOMAIN_4) {
        base = (volatile unsigned int*) (DEVAPC_D4_APC_0 + (module/MOD_NO_IN_1_DEVAPC) * 4);
    } else if (domain_num == E_DOMAIN_5) {
        base = (volatile unsigned int*) (DEVAPC_D5_APC_0 + (module/MOD_NO_IN_1_DEVAPC) * 4);
    } else if (domain_num == E_DOMAIN_6) {
        base = (volatile unsigned int*) (DEVAPC_D6_APC_0 + (module/MOD_NO_IN_1_DEVAPC) * 4);
    } else if (domain_num == E_DOMAIN_7) {
        base = (volatile unsigned int*) (DEVAPC_D7_APC_0 + (module/MOD_NO_IN_1_DEVAPC) * 4);
    } else {
        TUI_LOGE("set_module_apc: domain_num %d is overflow!!\n", domain_num);
        return;
    }

    writel(readl(base) & ~clr_bit, base);
    writel(readl(base) | set_bit, base);

}

static int map_DAPC_Register()
{
    int ret=0;

    if((ret = buffer_map_to_va(DEVAPC_AO_BASE, SIZE_4KB, (void **)&DEVAPC_AO_VA_BUFFER)) != 0) {
        TUI_LOGE("map DEVAPC_AO_BASE failed! ERROR:%d, phy addr:0x%x\n", ret, DEVAPC_AO_BASE);
        return -1;
    }
    TUI_LOGD("dapc pa to va (0x%x->0x%p)\n", DEVAPC_AO_BASE, DEVAPC_AO_VA_BUFFER);

    return ret;
}

int start_devapc()
{
    int ret = 0;

    if (!has_started) {
        ret = -1;
        if (map_DAPC_Register() == 0) {
            /*Enable Devapc*/
            writel(readl(DEVAPC_APC_CON) &  (0xFFFFFFFF ^ (1<<2)), DEVAPC_APC_CON);
            has_started = 1;
            ret = 0;
        }
    }
    TUI_LOGD("devapc already started\n");
    return ret;
}
