/*
 *
 * Copyright (C) 2012-2021, Samsung Electronics Co., Ltd.
 *
 * GPIO interface, local implementation
 */

#include <errno.h>
#include "dbg.h"
#include "device.h"
#include "secmap.h"
#include "touch_gpio.h"

#ifdef USE_TOUCH_INTERRUPT
static uint32_t gpioa_stored_data[3];

static void gpio_write_mask(uint32_t reg_id, uint32_t val, uint32_t mask)
{
    uint32_t old = GPA_REG(reg_id);

    val = (val & mask) | (old & ~mask);
    GPA_REG(reg_id) = val;
}

/* Get pending touch attention interrupt */
static bool gpio_get_pend_irq(void)
{
    return (GPA_REG(WEINT_GPA1_PEND_OFFSET) & WEINT_GPA1_2_PEND);
}

static void gpio_save_state(void)
{
    /* Save GPA1_2 settings */
    gpioa_stored_data[0] = GPA_REG(GPA1_CON_OFFSET) & GPA1_2_CONF_MASK;
    gpioa_stored_data[1] = GPA_REG(WEINT_GPA1_CON_OFFSET) & GPA1_EINT2_MASK;
    gpioa_stored_data[2] = GPA_REG(WEINT_GPA1_MASK_OFFSET) & WEINT_GPA1_2_PEND;
}

static uint32_t gpio_is_irq_disabled(void)
{
    return GPA_REG(WEINT_GPA1_MASK_OFFSET) & WEINT_GPA1_2_PEND;
}

int map_gpio_sfr(void)
{
    return sec_map(DEV_GPA, GPIO_PA_BASE, SIZE_4KB);
}

void gpio_restore_state(void)
{
    /* Restore GPA1_2 settings */
    gpio_write_mask(GPA1_CON_OFFSET, gpioa_stored_data[0], GPA1_2_CONF_MASK);
    gpio_write_mask(WEINT_GPA1_CON_OFFSET, gpioa_stored_data[1], GPA1_EINT2_MASK);
    gpio_write_mask(WEINT_GPA1_MASK_OFFSET, gpioa_stored_data[2], WEINT_GPA1_2_PEND);
}

void gpio_config(void)
{
    gpio_save_state();

    /* Configure GPA1_2 for EINT */
    gpio_write_mask(GPA1_CON_OFFSET, GPA1_2_CONF_EINT, GPA1_2_CONF_MASK);

    /* Configure GPA1_2 trigger type */
    gpio_write_mask(WEINT_GPA1_CON_OFFSET, GPA1_EINT2_LOW, GPA1_EINT2_MASK);
}

/* Enable/disable touch attention interrupt from gpio */
void gpio_enable_touch_irq(bool enable)
{
    int count = 0;

    if (map_gpio_sfr()) {
        TEE_Panic(0xDEADBEEF);
        return;
    }

    if (enable) {
        do {
            gpio_write_mask(WEINT_GPA1_MASK_OFFSET, ~WEINT_GPA1_2_PEND, WEINT_GPA1_2_PEND);
        } while (gpio_is_irq_disabled() && count++ < 10);
    } else {
        gpio_write_mask(WEINT_GPA1_MASK_OFFSET, WEINT_GPA1_2_PEND, WEINT_GPA1_2_PEND);
    }

    if (count >= 10) {
        errPrintf("failed to enable touch interrupt\n");
        TEE_Panic(0xDEADBEEF);
    }
}

/* Clear pending touch attention interrupt */
void gpio_clear_pend_irq(void)
{
    if (map_gpio_sfr()) {
        TEE_Panic(0xDEADBEEF);
        return;
    }

    if (gpio_get_pend_irq()) {
        GPA_REG(WEINT_GPA1_PEND_OFFSET) |= WEINT_GPA1_2_PEND;
    }
}
#endif /* USE_TOUCH_INTERRUPT */