/*
 * touch_gpio.c
 *
 * Copyright (C) 2012-2020, Samsung Electronics Co., Ltd.
 *
 * common part of touch gpio implementation
 */

#include <errno.h>
#include "board.h"
#include "dbg.h"
#include "device.h"
#include "touch_gpio.h"

#ifdef USE_TOUCH_INTERRUPT
#include <tee_interrupt.h>

#define ATTN_INTERRUPT_MSEC_TIMEOUT 1000

static TEES_InterruptHandle handle;
static read_event_func_t read_event_func = NULL;

extern int map_gpio_sfr(void);
extern void gpio_restore_state(void);
extern void gpio_config(void);
extern void gpio_clear_pend_irq(void);
extern void gpio_enable_touch_irq(bool enable);

static void touch_handler(struct intrinfo *info)
{
    (void)info;

    gpio_clear_pend_irq();
    gpio_enable_touch_irq(false); // mask tsp_int irq

    if (read_event_func) {
        read_event_func();
    }

    TEES_CompleteInterrupt(handle);
    gpio_enable_touch_irq(true); // unmask tsp_int irq
}

/* Wait for touch attention interrupt */
int gpio_wait_touch_irq(void)
{
    TEE_Result ret;

    ret = TEES_WaitForInterrupt(handle, ATTN_INTERRUPT_MSEC_TIMEOUT);
    if (ret == TEE_SUCCESS || ret == TEE_ERROR_TIMEOUT) {
        return 0;
    } else {
        errPrintf("%s failed! ret = %d\n", __func__, ret);
        return -1;
    }
}

/* Stop waiting for touch attention interrupt */
int gpio_complete_touch_irq(void)
{
    return TEES_CompleteInterrupt(handle);
}

TEE_Result touch_gpio_init(deviceInfo_t *gpio_dev)
{
    dbgPrintf(">> %s\n", __func__);
    if (gpio_dev->state & DEV_SFR_CONFIGURED) {
        return E_TUI_HAL_BUSY;
    }
    if (map_gpio_sfr()) {
        return E_TUI_HAL_MAP;
    }

    /* Config GPIO pins */
    gpio_config();
    gpio_dev->state |= DEV_SFR_CONFIGURED;
    dbgPrintf("<< %s\n", __func__);
    return TEE_SUCCESS;
}

TEE_Result touch_gpio_release(deviceInfo_t *gpio_dev)
{
    TEE_Result ret = TEE_SUCCESS;

    dbgPrintf(">> %s\n", __func__);

    if (map_gpio_sfr()) {
        return TEE_ERROR_GENERIC;
    }
    if (gpio_dev->state & DEV_SFR_CONFIGURED) {
        gpio_restore_state();
        gpio_dev->state &= ~DEV_SFR_CONFIGURED;
    }
    dbgPrintf("<< %s\n", __func__);
    return ret;
}

TEE_Result touch_gpio_register_int(deviceInfo_t *gpio_dev, read_event_func_t func)
{
    if (gpio_dev->state & DEV_IRQ_CONFIGURED) {
        return TEE_SUCCESS;
    }
    if (!(gpio_dev->state & DEV_SFR_CONFIGURED)) {
        return E_TUI_HAL_BAD_PARAMETERS;
    }
    if (func == NULL) {
        return E_TUI_HAL_BAD_PARAMETERS;
    }

    read_event_func = func;

    if (TEES_AllocateInterrupt(gpio_dev->irq_num, touch_handler, &handle)) {
        errPrintf("ERROR=%d, TEES_AllocateInterrupt(%d) failed!\n", errno, gpio_dev->irq_num);
        return TEE_ERROR_GENERIC;
    }

    gpio_enable_touch_irq(true);

    gpio_dev->state |= DEV_IRQ_CONFIGURED;
    dbgPrintf("SUCCESS %s:%i TEES_AllocateInterrupt(%d)\n",
              __func__, __LINE__, gpio_dev->irq_num);
    return TEE_SUCCESS;
}

TEE_Result touch_gpio_unregister_int(deviceInfo_t *gpio_dev)
{
    if (gpio_dev->state & DEV_IRQ_CONFIGURED) {
        if (TEES_ReleaseInterrupt(handle) != 0) {
            errPrintf("ERROR=%d, TEES_ReleaseInterrupt() failed!\n", errno);
            return TEE_ERROR_GENERIC;
        }
        gpio_dev->state &= ~DEV_IRQ_CONFIGURED;
        dbgPrintf("SUCCESS %s:%i TEES_ReleaseInterrupt(%d)\n",
                  __func__, __LINE__, gpio_dev->irq_num);
    }
    return TEE_SUCCESS;
}
#else /* USE_TOUCH_INTERRUPT */
TEE_Result touch_gpio_init(deviceInfo_t *gpio_dev)
{
    (void)gpio_dev;
    return TEE_SUCCESS;
}

TEE_Result touch_gpio_release(deviceInfo_t *gpio_dev)
{
    (void)gpio_dev;
    return TEE_SUCCESS;
}

TEE_Result touch_gpio_register_int(deviceInfo_t *gpio_dev, read_event_func_t func)
{
    (void)gpio_dev;
    (void)func;
    return TEE_SUCCESS;
}

TEE_Result touch_gpio_unregister_int(deviceInfo_t *gpio_dev)
{
    (void)gpio_dev;
    return TEE_SUCCESS;
}
#endif /* USE_TOUCH_INTERRUPT */
