// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
/*
 * Copyright (c) 2023 MediaTek Inc.
 */

/*****************************************************************************/
#include <dm/ofnode.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <dm.h>
#include <adc.h>

#include "common.h"
#include "command.h"
#include "debug_impl.h"
#include "mtk_sar_keypad.h"

#define LOW_WAKEUP_ADC(a, b) ((a > b)? (a-b) : 0)

// bit 0 : upgrade
// bit 1 : recovery
// bit 2 : OLED ERROR DETECT
// ex :
// adc-mode :0 Disable upgrade, Disable recovery, Disable OLED_ERROR_DETECT
// adc-mode :7 Enable upgrade , Enable  recovery, Enable OLED_ERROR_det
#define CHECK_SUPPORT_MODE(X, Y) ((X >> (Y - 1)) & 0x1)

u32 gupgrade_ch = CH_DEFAULT;
u16 gadc[KEY_NUMS] = {0};
u32 grecovery_adc = ADC_DEFAULT;
u32 oled_det_adc = ADC_DEFAULT;
u32 gadc_mode;
u32 gadc_offset;
u8 is_first = true;

static int mtk_sar_get_dts_info(void)
{
    ofnode ini_node;
    ofnode node = {0};
    u32 adc[KEY_NUMS] = {0};
    int ret = false;

    if (is_first == true)
    {
        /* Check device. */
        ini_node = ofnode_path("/pmu_ini");
        if (!ofnode_valid(ini_node))
        {
            UBOOT_ERROR("ofnode_path(/pmu_ini) not found.\n");
            return -ENODEV;
        }

        node = ofnode_find_subnode(ini_node, "sar");
        if (!ofnode_valid(node))
        {
            UBOOT_ERROR("[SAR] ofnode_path(SAR) not found.\n");
            return -ENODEV;
        }

        /*upgrade channel*/
        ret = ofnode_read_u32(node, "upgrade-ch", &gupgrade_ch);
        if (ret)
        {
            UBOOT_INFO("[SAR] read (upgrade-ch) failed.(%d)\n", ret);
            return -EINVAL;
        }

        /*upgrade adc*/
        ret = ofnode_read_u32_array(node, "upgrade-adc", adc, KEY_NUMS);
        if (ret)
        {
            UBOOT_INFO("[SAR] read (upgrade-adc) failed.(%d)\n", ret);
            return -EINVAL;
        }

        UBOOT_TRACE("g_upgrade ch [%d]\n", gupgrade_ch);

        for (int idx = 0; idx < KEY_NUMS; idx++)
        {
            gadc[idx] = (adc[idx] & CH_MASK);
            UBOOT_TRACE("g_upgrade adc[%d]=0x%X \n", idx, gadc[idx]);
        }

        /*recovery adc*/
        ret = ofnode_read_u32(node, "recovery-adc", &grecovery_adc);
        if (ret)
        {
            UBOOT_INFO("[SAR] read (recovery-adc) failed.(%d)\n", ret);
            grecovery_adc = ADC_DEFAULT;
        }
        UBOOT_TRACE("grecovery_adc [%d]\n", grecovery_adc);

        /*oled error clear adc*/
        ret = ofnode_read_u32(node, "oled_det-adc", &oled_det_adc);
        if (ret)
        {
            UBOOT_INFO("[SAR] read (oled_det-adc) failed.(%d)\n", ret);
            oled_det_adc = ADC_DEFAULT;
        }
        UBOOT_TRACE("oled_det_adc [%d]\n", oled_det_adc);

        /*adc mode*/
        ret = ofnode_read_u32(node, "adc-mode", &gadc_mode);
        if (ret)
        {
            UBOOT_INFO("[SAR] read (adc-mode) failed.(%d)\n", ret);
            gadc_mode = ADC_MODE_DEFAULT;
        }
        UBOOT_TRACE("gadc_mode [%d]\n", gadc_mode);

        /*adc offset*/
        ret = ofnode_read_u32(node, "adc-offset", &gadc_offset);
        if (ret)
        {
            UBOOT_INFO("[SAR] read (adc-offset) failed.(%d)\n", ret);
            gadc_offset = ADC_OFFSET_DEFAULT;
        }
        UBOOT_TRACE("gadc_offset [%d]\n", gadc_offset);
        is_first = false;
        ret = true;
    }
    else
    {
        UBOOT_TRACE("Get dts already run\n");
        ret = true;
    }

    return ret;
}

static int mtk_sar_check_upgrade(int adc_mode, unsigned int adc_val)
{
    int idx = 0;
    int ret = EN_ADC_MODE_RET_UNKNOWN;

    UBOOT_TRACE("(adc_mode, adc_val, grecovery_adc , gadc_offset) = (%d, %d, %d, %d)\n",
                adc_mode, adc_val, grecovery_adc , gadc_offset);

    switch (adc_mode)
    {
        case EN_ADC_MODE_UPGRADE:
        for (idx = 0; idx < KEY_NUMS; idx++)
        {
            if (gadc[idx] != ADC_DEFAULT)
            {
                if ((adc_val <= (gadc[idx] + gadc_offset)) &&
                     (adc_val >= LOW_WAKEUP_ADC(gadc[idx], gadc_offset)))
                {
                    ret = EN_ADC_MODE_RET_UPGRADE;
                }
            }
        }
        break;
        case EN_ADC_MODE_RECOVERY:
        if (grecovery_adc != ADC_DEFAULT)
        {
            if ((adc_val <= (grecovery_adc + gadc_offset)) &&
                 (adc_val >= LOW_WAKEUP_ADC(grecovery_adc, gadc_offset)))
            {
                ret = EN_ADC_MODE_RET_RECOVERY;
            }
        }
        break;
        case EN_ADC_MODE_OLED_ERROR_CLEAR:
        if (oled_det_adc != ADC_DEFAULT)
        {
            if ((adc_val <= (oled_det_adc + gadc_offset)) &&
                 (adc_val >= LOW_WAKEUP_ADC(oled_det_adc, gadc_offset)))
            {
                ret = EN_ADC_MODE_RET_OLED_ERROR_CLEAR;
            }
        }
        break;
        default:
        ret = EN_ADC_MODE_RET_UNKNOWN;
        UBOOT_TRACE("Error mode NOT support mode!!!\n");
        break;
    }

    UBOOT_INFO("%s: (adc_mode,ret) = (%d, %d)\n", __func__, adc_mode, ret);
    return ret;
}
static int mtk_sar_check_bootmode_status(int adc_mode)
{
    struct udevice *dev;
    unsigned int data = 0;
    int uV;
    int ret = EN_ADC_MODE_RET_UNKNOWN;

    mtk_sar_get_dts_info();

    if (gupgrade_ch != CH_DEFAULT)
    {
        ret = adc_channel_single_shot("sar", gupgrade_ch, &data);

        if (ret)
        {
            UBOOT_ERROR("Error getting single shot for device sar channel %d\n",
                    gupgrade_ch);
            ret = EN_ADC_MODE_RET_UNKNOWN;
            goto exit;
        }

        ret = uclass_get_device_by_name(UCLASS_ADC, "sar", &dev);
        if (!ret && !adc_raw_to_uV(dev, data, &uV))
        {
            UBOOT_INFO("get sar_adc = %u, %d uV\n", data, uV);
        }
        else
        {
            UBOOT_INFO("get sar_adc = %u\n", data);
        }


        if (CHECK_SUPPORT_MODE(gadc_mode, adc_mode))
        {
            UBOOT_TRACE("adc_mode %d\n", adc_mode);
            ret = mtk_sar_check_upgrade(adc_mode, data);
        }
        /* not support, need enable "adc-mode" on dts */
        else
        {
            ret = EN_ADC_MODE_RET_UNKNOWN;
            UBOOT_TRACE("Error not support adc_mode %d\n", adc_mode);
        }

    }
    else
    {
        ret = EN_ADC_MODE_RET_UNKNOWN;
        UBOOT_TRACE("Error getting sar channel %d\n", gupgrade_ch);
    }

exit:
    return ret;
}
#define CHECK_BOOTMODE_TIME  5
#define CHECK_BOOTMODE_CYCLE 500
// check boot mode status in 3s, if keypad not released, return boot mode
int mtk_sar_check_status(int adc_mode)
{
    int pre_bootmode = EN_ADC_MODE_RET_UNKNOWN;
    int cur_bootmode = EN_ADC_MODE_RET_UNKNOWN;
    int i = 0;

    cur_bootmode = mtk_sar_check_bootmode_status(adc_mode);
    if (cur_bootmode == EN_ADC_MODE_RET_UNKNOWN)
    {
        return EN_ADC_MODE_RET_UNKNOWN;
    }
    pre_bootmode = cur_bootmode;
    mdelay(CHECK_BOOTMODE_CYCLE);
    for (i = 0; i < CHECK_BOOTMODE_TIME; i++)
    {
        cur_bootmode = mtk_sar_check_bootmode_status(adc_mode);
        // if current boot mode is not equal to previous boot mode, return EN_ADC_MODE_RET_UNKNOWN
        if (cur_bootmode != pre_bootmode)
        {
            return EN_ADC_MODE_RET_UNKNOWN;
        }
        pre_bootmode = cur_bootmode;
        mdelay(CHECK_BOOTMODE_CYCLE);
    }
    return cur_bootmode;
}

