/*
 * dsk324sr.c
 *
 *  Created on: 8/05/2021
 *      Author: alexrayne <alexraynepe196@gmail.com>
 */

#include <project-conf.h>
#include <c_compat.h>
#include "dsk324sr.h"
#include "r_i2c_master_api.h"
#include "board.h"
#include <sys/pt.h>

// provide: TM_YEAR0
#include <OsTime.h>


#if 1
#include <assert.h>
#define ASSERT(...) assert( __VA_ARGS__ )
#else
#define ASSERT(...)
#endif



////////////////////////////////////////////////////////////////////////////////////
#include "sys/log.h"
LOG_MODULE_LOCAL("dsk324");
#ifdef LOG_LEVEL_DSK324
#define LOG_LEVEL LOG_LEVEL_DSK324
#else
#define LOG_LEVEL LOG_LEVEL_NONE
#endif

#include <trace_probes.h>
#ifndef trace_rtc_read
trace_need(rtc_valid);
trace_need(rtc_read);
trace_need(rtc_set);
trace_need(rtc_invalidate);
trace_need(rtc_i2c);
#endif
#ifndef trace_rtc_damage
trace_need(rtc_damage);
#endif



///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///

#ifndef RTC_DSK324_I2C
#error  "need DSK324SR rtc i2c-master"
#endif



/// @brief включает валидацияю считываемых регистров на предмет мусора. мусор может возникать если с РТС ненадежна
#define RTC_DSK324_STYLE_VALIDATE   1
/// @brief включает верификацию считываемых регистров повторным чтением.
#define RTC_DSK324_STYLE_VERIFY     2


#ifndef RTC_DSK324_STYLE
#define RTC_DSK324_STYLE    (RTC_DSK324_STYLE_VALIDATE)
#endif




enum DSK324RtcOperation{
    //< when initialised, and ready to comm
    DSK324RTC_IDLE,
    DSK324RTC_READY = DSK324RTC_IDLE,

    //< when powerup, but not valid
    DSK324RTC_NONE,
    DSK324RTC_READ,
    DSK324RTC_START,
    // setup rtc mode only
    DSK324RTC_REINIT,
    // cleanup rtc and mode
    DSK324RTC_RESET,
    DSK324RTC_SET,
};
typedef enum DSK324RtcOperation    DSK324RtcOperation;

struct  DSK324RTCtx {

    //< this false while I2C activity
    volatile  bool  valid_regs;

    DSK324RtcOperation     op;

    struct pt       pt;
};
typedef struct  DSK324RTCtx DSK324RTCtx;

typedef enum DSK324CtxState DSK324CtxState;
typedef enum DSK324TimeState DSK324TimeState;


DSK324Ctx       rtc_dsk324_ctx;
DSK324CtxState  rtc_dsk324_valid;
DSK324TimeState rtc_dsk324_time_status;
struct tm       rtc_dsk324_now;

DSK324Ctx   rtc_dsk324_last;
i2cHandle   rtc_dsk324_port;
DSK324RTCtx rtc_dsk324;

DSK324Ctx       rtc_dsk324_setup;
DSK324Ctx       rtc_dsk324_tmp;



///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///
#if 1

#define SELFARG
#define SELF        (rtc_dsk324)

#define API(method) (g_i2c_master_on_iic.method)

#else
// system use multiple rtc
#define SELFARG     DSK324RTCtx* self,
#define SELF        (*self)

#define API(method) (*api->method)

#endif



#ifndef CODE_ISR
#ifdef __HOTFUNC
#define CODE_ISR    __HOTFUNC
#else
#define CODE_ISR
#endif
#endif



///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///
#include "lib/bcdnum.h"

static
void rtc_dsk324_update_now(void){
    DSK324Ctx*  regs = &rtc_dsk324_ctx;
    struct tm*  x = &rtc_dsk324_now;

    x->tm_sec   = bcd2num(regs->sec);
    x->tm_min   = bcd2num(regs->min);
    x->tm_hour  = bcd2num(regs->hour);
    x->tm_mday  = bcd2num(regs->day)    +TM_MDAY0 - RTC_DSK324_MDAY0;
    x->tm_mon   = bcd2num(regs->month)  +TM_MON0  - RTC_DSK324_MON0;
    x->tm_year  = bcd2num(regs->year) + RTC_DSK324_YEAR0 - TM_YEAR0;
    x->tm_wday  = regs->week;
    x->tm_isdst = (regs->ctrl & DSK324CTRL_RAM) != 0;
    x->tm_yday  = 0; // mktime ignore it

    rtc_dsk324_last = *regs;
}

// после обновления содержимого RTC, надо пересчитать структуру rtc_dsk324_now
static
void rtc_dsk324_updated(void){
    rtc_dsk324_update_now();
}



void rtc_dsk324_clear(void){
    DSK324Ctx*  regs = &rtc_dsk324_ctx;
    memset(regs->reg, 0, sizeof(regs->reg) );
    rtc_dsk324_updated();
}

/// @brief assign context regs cfg : 1SEc IRQ, 32kHz output, fast TCS
void        rtc_dsk324_cfg_online_1sec_32k(void){
    DSK324Ctx*  regs = &rtc_dsk324_ctx;

    regs->sel   = DSK324SEL_CFS_32K | DSK324SEL_TCS_2HZ | DSK324SEL_UTS_SEC;
    // profide 1sec irq on RTC_IRQ
    regs->ctrl &= ~DSK324CTRL_ALLIE;
    regs->ctrl |= DSK324CTRL_UTIE;
}
/// @brief assign context regs cfg : no IRQ and outputs, sloowest TCS
void        rtc_dsk324_cfg_offline(void){
    DSK324Ctx*  regs = &rtc_dsk324_ctx;
    regs->sel   = DSK324SEL_CFS_1HZ | DSK324SEL_TCS_30SEC;
    regs->ctrl  &= ~DSK324CTRL_ALLIE;
}



void dsk324sr_i2c_callback_dummy(i2cHandle h, i2c_master_event_t ev, void* ctx);

/// @brief обработчик приложения событий от прерывания i2c на dsk324.
///         пользовательский код может сделать свою реализацию.
///         Необходимо чтобы на события i2c вызывался rtc_dsk324_process
CODE_ISR
void dsk324sr_i2c_callback(i2cHandle h, i2c_master_event_t ev, void* ctx)
    __WEAK_REF("dsk324sr_i2c_callback_dummy");



/// @brief это преамбула для захвата шины i2c.
///         пока что он только назначает свой обработчик событий порту.
void dsk324_aquire( void ){
    //DSK324Ctx* regs = &rtc_dsk324_ctx;
    //fsp_err_t ok;

    I2C_assign_handler( rtc_dsk324_port, &(dsk324sr_i2c_callback), NULL);
}


#if DSK324_API_SYNC == 0

//  всякое назначение состояния SELF.op требует работы. //уведомлю приложение что dsk324 хочет работать
static
void dsk324sr_poll(void) {
    rtc_dsk324_on_event(rtc_dsk324_port, DSK324EV_BUSY);
}



CODE_ISR
void dsk324sr_i2c_callback_dummy(i2cHandle h, i2c_master_event_t ev, void* ctx)
{
    (void)h;(void)ev;(void)ctx;
    trace_rtc_i2c_twist();

    //rtc_dsk324_process();
    dsk324sr_poll();
}


__WEAK
void dsk324_on_event(i2cHandle port, DSK324EventID ev){
    (void)port; (void)ev;
}

/// @brief обработчик приложения событий от dsk324. Вызывается из rtc_dsk324_process
///         пользовательский код может сделать свою реализацию
void rtc_dsk324_on_event(i2cHandle port, DSK324EventID ev)
    __WEAK_REF("dsk324_on_event");



static
void rtc_dsk324_fail_event(DSK324EventID ev){
    rtc_dsk324_valid        = DSK324CTX_FAIL;
    rtc_dsk324_time_status  = DSK324_TIME_UNCKNOWN;
    rtc_dsk324_on_event(rtc_dsk324_port, ev);
}

PT_THREAD ( rtc_dsk324_process(void) ){
    struct pt* pt = &(SELF.pt);
    DSK324Ctx*  regs = &rtc_dsk324_ctx;
    fsp_err_t   ok;
    I2C_Status  i2cok;

    PT_BEGIN( pt )

rtc_dsk324_process_again:

    if (SELF.op == DSK324RTC_IDLE){

        if (rtc_dsk324_valid > DSK324CTX_INVALID)
            PT_EXIT(pt);

        if (rtc_dsk324_valid == DSK324CTX_INVALID) {
            LOG_DBG("invalidated refresh\n");
            SELF.op = DSK324RTC_READ;
        }
        else {
            LOG_DBG("invalidated reinit\n");
            SELF.op = DSK324RTC_REINIT;
        }

    }

    if (SELF.op == DSK324RTC_READ){
        LOG_DBG("read\n");
rtc_dsk324_process_read:

        trace_rtc_read_on();
        rtc_dsk324_valid = DSK324CTX_UPDATE;
        trace_rtc_valid_off();

        // запуск команды чтения часов
        PT_WAIT_UNTIL(pt, ( ok = I2C_status(rtc_dsk324_port)) != I2C_BUSY );
        dsk324_aquire();

        // post adress for reading
        SELF.op = DSK324RTC_READ;

        SELF.valid_regs  = false;
        ok = I2C_read_regs(rtc_dsk324_port, DSK324ADDR, 0, regs, DSK324REG_TOTAL);

        PT_WAIT_UNTIL(pt, ( i2cok = I2C_wait(rtc_dsk324_port)) != I2C_BUSY );

        if ( rtc_dsk324_valid < DSK324CTX_UPDATE ){
            // invalidated via was restart diuring read
            //continue;
            //PT_YIELD(pt);
            LOG_DBG("read retry\n");
            goto rtc_dsk324_process_read;
        }

        if (i2cok == I2C_OK){

            SELF.valid_regs  = true;
            rtc_dsk324_valid        = DSK324CTX_VALID;
            trace_rtc_valid_on();

            if ( (regs->flag & DSK324FLAG_VDHF) == 0)
                rtc_dsk324_time_status   = DSK324_TIME_VALID;
            else
                rtc_dsk324_time_status   = DSK324_TIME_COLD;

            if ( (regs->flag & DSK324FLAG_VDLF) == 0){
                SELF.op = DSK324RTC_IDLE;

                rtc_dsk324_updated();
                rtc_dsk324_on_event(rtc_dsk324_port, DSK324EV_AFTER_REFRESH);

                trace_rtc_read_off();
                goto rtc_dsk324_process_again;

            }
            else {

                // rtc was power-up.
                LOG_WARN("rtc power-up, now default time\n");

                // provide default time from now
                rtc_dsk324_clear();
                SELF.op = DSK324RTC_RESET;

                rtc_dsk324_on_event(rtc_dsk324_port, DSK324EV_POWERUP);
            }
        }
        else {
            rtc_dsk324_fail_event(DSK324EV_AFTER_REFRESH);
            //?? try again later
            //SELF.op = DSK324RTC_IDLE;
            PT_EXIT(pt);
        }
    }
    trace_rtc_read_off();

rtc_dsk324_process_reinit:
    if (SELF.op == DSK324RTC_START)
    {
        LOG_DBG("start\n");
        rtc_dsk324_valid        = DSK324CTX_STARTUP;

        PT_WAIT_UNTIL(pt, ( ok = I2C_wait(rtc_dsk324_port)) != I2C_BUSY );
        dsk324_aquire();

        regs->sel   = DSK324SEL_CFS_1HZ | DSK324SEL_TCS_2HZ | DSK324SEL_UTS_SEC;
        rtc_dsk324_on_event(rtc_dsk324_port, DSK324EV_PREPARE_RESET);

        ok = I2C_write_regs(rtc_dsk324_port, DSK324ADDR, DSK324REG_SELECT, &regs->sel, 1);
        ASSERT(ok == FSP_SUCCESS);
        PT_WAIT_UNTIL(pt, ( i2cok = I2C_wait(rtc_dsk324_port)) != I2C_BUSY );

        if ( UNLIKELY(i2cok != I2C_OK) ) {
            //?? try again later
            LOG_ERR("start fails %d\n", i2cok);

            SELF.op = DSK324RTC_IDLE;
            rtc_dsk324_fail_event(DSK324EV_ERROR_INIT);

            PT_EXIT(pt);
        }

        // посмотрю статус холодного старта чипа.
        //  загружу и control - чтобы проверить DSK324CTRL_UTIE
        ok = I2C_read_next(rtc_dsk324_port, &regs->flag, 2);
        ASSERT(ok == FSP_SUCCESS);
        PT_WAIT_UNTIL(pt, ( i2cok = I2C_wait(rtc_dsk324_port)) != I2C_BUSY );

        if ( UNLIKELY(i2cok != I2C_OK) ) {
            //?? try again later
            LOG_ERR("start fails %d\n", i2cok);

            SELF.op = DSK324RTC_IDLE;
            rtc_dsk324_fail_event(DSK324EV_ERROR_INIT);

            PT_EXIT(pt);
        }

        if ( (regs->flag & DSK324FLAG_VDLF) == 0){
            rtc_dsk324_time_status   = DSK324_TIME_VALID;

            if ( rtc_dsk324_valid >= DSK324CTX_STARTUP)
            if ((regs->ctrl & DSK324CTRL_UTIE) != 0){
                // прерывание от часов уже включено, нормальноо работаем
                rtc_dsk324_on_event(rtc_dsk324_port, DSK324EV_AFTER_INIT);
                SELF.op = DSK324RTC_READ;
                goto rtc_dsk324_process_read;
            }

            // надо включить прерывание от часов, посему пойдем от полного ресета
            regs->sel   = DSK324SEL_CFS_1HZ | DSK324SEL_TCS_2HZ | DSK324SEL_UTS_SEC;
            // keep power up status
            regs->flag  = DSK324FLAG_VDLF | DSK324FLAG_VDHF;
            // profide 1sec irq on RTC_IRQ
            regs->ctrl  =  (regs->ctrl & DSK324CTRL_RAM) | DSK324CTRL_UTIE;
            SELF.op = DSK324RTC_REINIT;
        }
        else {
            SELF.op = DSK324RTC_RESET;
            LOG_WARN("start cold\n");
        }
    }

    if (SELF.op == DSK324RTC_REINIT)
    {
        LOG_DBG("reinit\n");
        rtc_dsk324_valid        = DSK324CTX_CONFIGURE;
        rtc_dsk324_on_event(rtc_dsk324_port, DSK324EV_PREPARE_RESET);


        ok = I2C_write_regs(rtc_dsk324_port, DSK324ADDR, DSK324REG_SELECT, &regs->sel, 3);
        ASSERT(ok == FSP_SUCCESS);
        PT_WAIT_UNTIL(pt, ( i2cok = I2C_wait(rtc_dsk324_port)) != I2C_BUSY );

        if ( rtc_dsk324_valid < DSK324CTX_CONFIGURE)
            // если в процессе инициализации затребована новая переинициализация, начну заново
            goto rtc_dsk324_process_reinit;

        if (i2cok == I2C_OK){
            SELF.op = DSK324RTC_READ;
            rtc_dsk324_on_event(rtc_dsk324_port, DSK324EV_AFTER_INIT);
            goto rtc_dsk324_process_read;
        }
        else {
            //?? try again later
            LOG_ERR("reinit fails %d\n", i2cok);

            SELF.op = DSK324RTC_IDLE;
            rtc_dsk324_fail_event(DSK324EV_ERROR_INIT);

            PT_EXIT(pt);
        }
    }

    if (SELF.op == DSK324RTC_RESET){
        // here after power-down RTC
        LOG_DBG("reset clear\n");

        rtc_dsk324_valid        = DSK324CTX_UPDATE;
        rtc_dsk324_clear();
        rtc_dsk324_time_status   = DSK324_TIME_COLD;
        regs->flag  = DSK324FLAG_VDHF;
        rtc_dsk324_on_event(rtc_dsk324_port, DSK324EV_POWERUP);

        regs->sel   = DSK324SEL_CFS_1HZ | DSK324SEL_TCS_2HZ | DSK324SEL_UTS_SEC;
        // profide 1sec irq on RTC_IRQ
        regs->ctrl  =  DSK324CTRL_UTIE;
        rtc_dsk324_on_event(rtc_dsk324_port, DSK324EV_PREPARE_RESET);

        PT_WAIT_UNTIL(pt, ( ok = I2C_wait(rtc_dsk324_port)) != I2C_BUSY );
        dsk324_aquire();

        ok = I2C_write_regs(rtc_dsk324_port, DSK324ADDR, 0, &regs->reg, DSK324REG_TOTAL);
        ASSERT(ok == FSP_SUCCESS);
        PT_WAIT_UNTIL(pt, ( i2cok = I2C_wait(rtc_dsk324_port)) != I2C_BUSY );

        if ( rtc_dsk324_valid < DSK324CTX_CONFIGURE)
            // если в процессе инициализации затребована новая переинициализация, начну заново
            goto rtc_dsk324_process_reinit;

        if (i2cok == I2C_OK){
            rtc_dsk324_valid        = DSK324CTX_VALID;
            rtc_dsk324_updated();
            rtc_dsk324_on_event(rtc_dsk324_port, DSK324EV_AFTER_INIT);
        }
        else {
            //?? try again later
            LOG_ERR("reset fails %d\n", i2cok);
            rtc_dsk324_fail_event(DSK324EV_ERROR_INIT);
        }
        SELF.op = DSK324RTC_IDLE;
        PT_EXIT(pt);
    }
    else
    if (SELF.op == DSK324RTC_SET){
        LOG_DBG("set:\n");

rtc_dsk324_process_reset:
        trace_rtc_set_on();

        PT_WAIT_UNTIL(pt, ( ok = I2C_wait(rtc_dsk324_port)) != I2C_BUSY );
        dsk324_aquire();

        rtc_dsk324_valid = DSK324CTX_UPDATE;
        regs->flag  &= ~DSK324FLAG_VDHF;
        ok = I2C_write_regs(rtc_dsk324_port, DSK324ADDR, 0, rtc_dsk324_setup.reg, DSK324REG_TOTAL);
        ASSERT(ok == FSP_SUCCESS);

        PT_WAIT_UNTIL(pt, ( i2cok = I2C_wait(rtc_dsk324_port)) != I2C_BUSY );
        if (i2cok == I2C_OK){
            if ( rtc_dsk324_valid < DSK324CTX_UPDATE ){
                LOG_WARN("set again\n");
                // invalidate during setup. resetup again
                //continue;
                goto rtc_dsk324_process_reset;
                //PT_EXIT(pt);
            }
            LOG_INFO("set ok\n");
            rtc_dsk324_time_status  = DSK324_TIME_VALID;
            rtc_dsk324_valid        = DSK324CTX_VALID;
            rtc_dsk324_updated();
            SELF.op = DSK324RTC_IDLE;
            rtc_dsk324_on_event(rtc_dsk324_port, DSK324EV_AFTER_STORE);
        }
        else {
            //TODO: retry ?? try again later
            LOG_ERR("set fails %d\n", i2cok);
            PT_EXIT(pt);

            SELF.op = DSK324RTC_IDLE;
            //rtc_dsk324_fail_event(DSK324EV_AFTER_STORE);
            rtc_dsk324_valid        = DSK324CTX_FAIL;
            rtc_dsk324_on_event(rtc_dsk324_port, DSK324EV_AFTER_STORE);
        }
    }
    else {
        LOG_ERR("uncknown op%d\n", SELF.op);
        SELF.op = DSK324RTC_IDLE;
    }
    trace_rtc_set_off();

    if (rtc_dsk324_valid <= DSK324CTX_INVALID){
        LOG_DBG("valid regs %d:\n", SELF.valid_regs);
        SELF.op = DSK324RTC_READ;
        goto rtc_dsk324_process_read;
    }

    PT_END( pt );
}



/// @brief reconfigure rtc_dsk324 from ctx, leading to refresh.
/// TODO: need avoid refresh after reconfigure, method should not have side-affects
void        rtc_dsk324_reconfigure(void){
    if (rtc_dsk324_valid >= DSK324CTX_RECONFIG) {

        rtc_dsk324_valid = DSK324CTX_RECONFIG;
        //уведомлю приложение что dsk324 требует работы
        dsk324sr_poll();

    }
    trace_rtc_invalidate_twist();
}



RTCStatus_t rtc_dsk324_wait(void){
    if ( PT_SCHEDULE(rtc_dsk324_process() ) )
        return RTC_BUSY;
    return (rtc_dsk324_valid > DSK324CTX_FAIL)? RTC_OK : rtc_dsk324_valid;
}



fsp_err_t rtc_dsk324_refresh( void ){
    DSK324Ctx* regs = &rtc_dsk324_ctx;
    fsp_err_t ok;

    if (SELF.op != DSK324RTC_IDLE)
        return RTC_BUSY;

    dsk324_aquire();

    // post adress for reading
    SELF.op = DSK324RTC_READ;
    ok = I2C_read_regs(rtc_dsk324_port, DSK324ADDR, 0, regs, DSK324REG_TOTAL);

    ASSERT( ok == FSP_SUCCESS );
    return ok;
}


static
RTCStatus_t rtc_dsk324_set( DSK324Ctx*  regs ){
    memcpy(&rtc_dsk324_setup, regs, sizeof(rtc_dsk324_ctx) );

    SELF.op = DSK324RTC_SET;
    rtc_dsk324_time_status = DSK324_TIME_SETUP;

    dsk324sr_poll();
    return RTC_OK;
}

#else // DSK324_API_SYNC


#define dsk324sr_poll()
#define dsk324sr_wait()


static
I2C_Status  i2c_wait_port(i2cHandle h){
    I2C_Status  i2cok;
    while ( (i2cok = I2C_wait(h)) == I2C_BUSY )
        {  dsk324sr_wait(); }
    return i2cok;
}


CODE_ISR
void dsk324sr_i2c_callback_dummy(i2cHandle h, i2c_master_event_t ev, void* ctx)
{
    (void)h;(void)ev;(void)ctx;
    trace_rtc_i2c_twist();
}



RTCStatus_t rtc_dsk324_startup( DSK324InitMode mode ){
    RTCStatus_t ok;
    DSK324Ctx*  regs = &rtc_dsk324_ctx;
    I2C_Status  i2cok;

    dsk324_aquire();

    LOG_DBG("start\n");
    rtc_dsk324_valid        = DSK324CTX_STARTUP;

    i2cok = i2c_wait_port(rtc_dsk324_port);
    dsk324_aquire();

    //regs->sel   = DSK324SEL_CFS_1HZ | DSK324SEL_TCS_2HZ | DSK324SEL_UTS_SEC;

    ok = I2C_write_regs(rtc_dsk324_port, DSK324ADDR, DSK324REG_SELECT, &regs->sel, 1);
    ASSERT(ok == FSP_SUCCESS);

    i2cok = i2c_wait_port(rtc_dsk324_port);
    if ( UNLIKELY(i2cok != I2C_OK) ) {
        //?? try again later
        LOG_ERR("start fails %d\n", i2cok);
        return i2cok;
    }

    // посмотрю статус холодного старта чипа.
    //  загружу и control - чтобы проверить DSK324CTRL_UTIE
    uint8_t flags[2];

    ok = I2C_read_next(rtc_dsk324_port, flags, 2);
    ASSERT(ok == FSP_SUCCESS);

    i2cok = i2c_wait_port(rtc_dsk324_port);
    if ( UNLIKELY(i2cok != I2C_OK) ) {
        //?? try again later
        LOG_ERR("start fails %d\n", i2cok);
        return i2cok;
    }

    if ((flags[0] & DSK324FLAG_VDLF) != 0) {
        LOG_WARN("start cold\n");

        if (mode == DSK324_INIT_CLEAN)  {
            // here after power-down RTC
            LOG_DBG("reset clear\n");

            rtc_dsk324_valid        = DSK324CTX_UPDATE;
            rtc_dsk324_time_status  = DSK324_TIME_COLD;

            // сохраню этот флаг как признак холодного времени, после ресета часов - следующий
            //  стартап по ресету, по нему определит что часы хоть инициированы, но время - холодное.
            //  только явная установка часов, сбрасывает этот признак
            regs->flag  = DSK324FLAG_VDHF;

            ok = I2C_write_regs(rtc_dsk324_port, DSK324ADDR, 0, &regs->reg, DSK324REG_TOTAL);
            ASSERT(ok == FSP_SUCCESS);

            i2cok = i2c_wait_port(rtc_dsk324_port);
            SELF.op = DSK324RTC_IDLE;

            if (i2cok == I2C_OK){
                rtc_dsk324_valid        = DSK324CTX_VALID;

                //rtc_dsk324_updated();
                rtc_dsk324_update_now();    //форсирую обновление rtc now

                //SELF.op = DSK324RTC_IDLE;
                return RTC_OK;
            }
            else {
                //?? try again later
                LOG_ERR("reset fails %d\n", i2cok);
                return i2cok;
            }
        }
    }
    else  {//if ( (regs->flag & DSK324FLAG_VDLF) == 0)
        LOG_WARN("start warm\n");
        regs->flag = flags[0];

        if ((flags[0] & DSK324FLAG_VDHF) == 0)
            rtc_dsk324_time_status   = DSK324_TIME_VALID;
        else
            rtc_dsk324_time_status   = DSK324_TIME_COLD;
    }

    if ( rtc_dsk324_valid >= DSK324CTX_STARTUP){
        if ((regs->ctrl & DSK324CTRL_ALLIE) != (flags[1] & DSK324CTRL_ALLIE) ){

            // прерывание от часов не совпадает, надо сконфигурировать часы

            rtc_dsk324_valid  = DSK324CTX_INVALID;

            // keep power up status
            regs->flag  &= DSK324FLAG_VDLF | DSK324FLAG_VDHF;

            ok = rtc_dsk324_reconfigure();
        }
        else
            regs->ctrl = flags[1];
    }
    else
        rtc_dsk324_valid        = DSK324CTX_INVALID;

    return rtc_dsk324_refresh();
}



static
RTCStatus_t rtc_dsk324_wr_cfg( DSK324Ctx*  regs ){
    I2C_Status ok;

    ok = I2C_write_regs(rtc_dsk324_port, DSK324ADDR, DSK324REG_SELECT, &regs->sel, 3);
    ASSERT(ok == FSP_SUCCESS);

    ok = i2c_wait_port(rtc_dsk324_port);
    return ok;
}

// verify
static
RTCStatus_t rtc_dsk324_ver_cfg( DSK324Ctx*  regs ){
    RTCStatus_t ok;

    // post adress for reading
    ok = I2C_read_regs(rtc_dsk324_port, DSK324ADDR, DSK324REG_SELECT, &regs->sel, 3);
    ASSERT( ok == FSP_SUCCESS );

    ok = i2c_wait_port(rtc_dsk324_port);
    if (ok != I2C_OK) return ok;

    bool same = (regs->sel == rtc_dsk324_ctx.sel)
              && (regs->ctrl == rtc_dsk324_ctx.ctrl)
              ;
    if ( !same ) return I2C_BADDATA;

    return I2C_OK;
}


/// @brief reconfigure rtc_dsk324 from ctx, leading to refresh.
/// TODO: need avoid refresh after reconfigure, method should not have side-affects
RTCStatus_t  rtc_dsk324_reconfigure(void){
    DSK324Ctx*  regs = &rtc_dsk324_ctx;
    I2C_Status  i2cok;

    dsk324_aquire();
    trace_rtc_invalidate_twist();

    LOG_INFO("reсonfig\n");
    rtc_dsk324_valid        = DSK324CTX_CONFIGURE;

    i2cok = rtc_dsk324_wr_cfg(regs);

#if ((RTC_DSK324_STYLE & RTC_DSK324_STYLE_VERIFY) != 0)
    if (i2cok == I2C_OK){

        i2cok = rtc_dsk324_ver_cfg(&rtc_dsk324_tmp);

        if (i2cok == I2C_BADDATA){

            i2cok = rtc_dsk324_wr_cfg(regs);

            if (i2cok == I2C_OK){
                i2cok = rtc_dsk324_ver_cfg(&rtc_dsk324_tmp);
            }
        }
    }
#endif

    if (i2cok == I2C_OK){
        rtc_dsk324_valid = DSK324CTX_INVALID;
        //SELF.op = DSK324RTC_IDLE;
        return RTC_OK;
    }
    else {
        //?? try again later
        LOG_ERR("reinit fails %d\n", i2cok);
        return i2cok;
    }
}


static
bool rtc_dsk324_validate_ctx(DSK324Ctx* x){

    static const uint8_t masks[6] = {
         [DSK324REG_SEC]   = 0x7f,
         [DSK324REG_MIMUTE]= 0x7f,
         [DSK324REG_HOUR]  = 0x3f,
         [DSK324REG_WEEK]  = 7,
         [DSK324REG_DAY]   = 0x3f,
         [DSK324REG_MONTH ]= 0x1f,
    };

    for (unsigned i= 0; i < ARRAY_COUNT(masks); ++i ){
        if ((x->reg[i] & ~masks[i]) != 0)
            return false;
    }

    if ( (x->flag & DSK324FLAG_DUMMY0) != 0)
        return false;

    DSK324Ctx* regs = &rtc_dsk324_ctx;  //&rtc_dsk324_last;
    if (x->ctrl != regs->ctrl)
        return false;

    return true;
}


static
I2C_Status rtc_dsk324_rd_ctx( DSK324Ctx*  regs ){
    I2C_Status ok;

    // post adress for reading
    ok = I2C_read_regs(rtc_dsk324_port, DSK324ADDR, 0, regs, DSK324REG_TOTAL);
    ASSERT( ok == FSP_SUCCESS );

    ok = i2c_wait_port(rtc_dsk324_port);
    if (ok != I2C_OK) return ok;

#if ((RTC_DSK324_STYLE & RTC_DSK324_STYLE_VALIDATE) != 0)
    if ( rtc_dsk324_validate_ctx(regs) ){
        return ok;
    }
    trace_rtc_damage_twist();
    return I2C_BADDATA;
#endif

    return ok;
}


I2C_Status rtc_dsk324_refresh( void ){
    DSK324Ctx* regs = &rtc_dsk324_ctx;
    I2C_Status ok;

    if (rtc_dsk324_valid >= DSK324CTX_INVALID){
        dsk324_aquire();
    }
    else {

        ok = rtc_dsk324_reconfigure();
        if (ok != RTC_OK)
            return ok;

        rtc_dsk324_valid = DSK324CTX_INVALID;
    }

    for (unsigned retry = 3; retry > 0; --retry){
        ok = rtc_dsk324_rd_ctx(&rtc_dsk324_tmp);
        ASSERT( ok == FSP_SUCCESS );
        if( ok == FSP_SUCCESS ) break;

# if ((RTC_DSK324_STYLE & RTC_DSK324_STYLE_VALIDATE) != 0)
        if( ok == I2C_BADDATA)
            // retry read
            continue;
#endif
        break;
    }

    if (ok == I2C_OK){
        rtc_dsk324_ctx      = rtc_dsk324_tmp;

        // верифицирую считанные регистры
        if ((RTC_DSK324_STYLE & RTC_DSK324_STYLE_VERIFY) != 0)
        for (unsigned retry = 3; retry > 0; --retry){

            ok = rtc_dsk324_rd_ctx(&rtc_dsk324_tmp);
# if ((RTC_DSK324_STYLE & RTC_DSK324_STYLE_VALIDATE) != 0)
        if( ok == I2C_BADDATA)
            // retry read
            continue;
#endif

            ASSERT( ok == FSP_SUCCESS );
            if (ok != I2C_OK) break;

            bool same = memcmp( rtc_dsk324_tmp.reg, rtc_dsk324_ctx.reg, sizeof(rtc_dsk324_ctx.reg) ) == 0;
            if ( same ) break;

            trace_rtc_damage_twist();
            ok = I2C_BADDATA;
            // retry read
        }

        SELF.op = DSK324RTC_IDLE;

        rtc_dsk324_valid    = DSK324CTX_VALID;

        if ((regs->flag & DSK324FLAG_VDHF) == 0)
            rtc_dsk324_time_status   = DSK324_TIME_VALID;
        else
            rtc_dsk324_time_status   = DSK324_TIME_COLD;

        rtc_dsk324_updated();

        //SELF.op = DSK324RTC_IDLE;
        return RTC_OK;
    }
    else {
        SELF.op = DSK324RTC_IDLE;

        //?? try again later
        LOG_ERR("reinit fails %d\n", ok);
        return ok;
    }
}


static
RTCStatus_t rtc_dsk324_wr_ctx( DSK324Ctx*  regs ){
    I2C_Status ok;

    ok = I2C_write_regs(rtc_dsk324_port, DSK324ADDR, 0, &regs->reg, DSK324REG_TOTAL);
    ASSERT(ok == FSP_SUCCESS);

    ok = i2c_wait_port(rtc_dsk324_port);
    return ok;
}

static
RTCStatus_t rtc_dsk324_ver_ctx( DSK324Ctx*  regs ){
    RTCStatus_t ok;

    // verify
    ok = rtc_dsk324_refresh();
    if (ok != RTC_OK) return I2C_BADDATA;

    bool same = memcmp( regs->reg, rtc_dsk324_ctx.reg, sizeof(rtc_dsk324_ctx.reg) ) == 0;
    if ( !same ) return I2C_BADDATA;

    return I2C_OK;
}

RTCStatus_t rtc_dsk324_set( DSK324Ctx*  regs ){
    I2C_Status ok;

    if (rtc_dsk324_valid <= DSK324CTX_STARTUP){
        ok = rtc_dsk324_startup( DSK324_INIT_SHORT );
        if (ok != RTC_OK)
            return ok;
    }

    LOG_DBG("set:\n");
    trace_rtc_set_on();

    ok = i2c_wait_port(rtc_dsk324_port);
    dsk324_aquire();

    rtc_dsk324_valid = DSK324CTX_UPDATE;
    regs->flag  &= ~DSK324FLAG_VDHF;

    ok = rtc_dsk324_wr_ctx(regs);

#if ((RTC_DSK324_STYLE & RTC_DSK324_STYLE_VERIFY) != 0)
    if (ok == RTC_OK)
    {
        ok = rtc_dsk324_ver_ctx(regs);
        if (ok == I2C_BADDATA)
        {
            // retry write
            ok = rtc_dsk324_wr_ctx(regs);
            if (ok == RTC_OK)
                ok = rtc_dsk324_ver_ctx(regs);
        }
    }
#endif


    if (ok == RTC_OK){

        LOG_INFO("set ok\n");

#if ((RTC_DSK324_STYLE & RTC_DSK324_STYLE_VERIFY) == 0)
        rtc_dsk324_time_status  = DSK324_TIME_VALID;
        rtc_dsk324_valid        = DSK324CTX_VALID;

        if ( &rtc_dsk324_ctx != regs)
            memcpy(&rtc_dsk324_ctx, regs, sizeof(rtc_dsk324_ctx) );

        rtc_dsk324_updated();

        SELF.op = DSK324RTC_IDLE;
#endif

        return RTC_OK;
    }
    else {
        //TODO: retry ?? try again later
        LOG_ERR("set fails %d\n", ok);

        //rtc_dsk324_fail_event(DSK324EV_AFTER_STORE);
        rtc_dsk324_valid        = DSK324CTX_FAIL;

        SELF.op = DSK324RTC_IDLE;
        return ok;
    }
}

#endif  // DSK324_API_SYNC


/// @brief invalidates rtc_dsk324_ctx, leading to refresh.
///         it may informed on RTC 1sec int
void        rtc_dsk324_invalidate(void){
    if (rtc_dsk324_valid >= DSK324CTX_INVALID) {

        rtc_dsk324_valid = DSK324CTX_INVALID;
        //уведомлю приложение что dsk324 требует работы
        dsk324sr_poll();

    }
    trace_rtc_invalidate_twist();
    trace_rtc_valid_off();

}



void rtc_dsk324_init(i2cHandle h) {
    (void)h;
    //DSK324Ctx* regs = &rtc_dsk324_ctx;
    //fsp_err_t ok;
    struct pt* pt = &(SELF.pt);

    rtc_dsk324_port = h;
    rtc_dsk324_invalidate();
    rtc_dsk324_time_status   = DSK324_TIME_UNCKNOWN;
    rtc_dsk324_update_now();

    SELF.op = DSK324RTC_START; //DSK324RTC_REINIT;

    PT_INIT(pt);
    dsk324sr_poll();
}


static bool rtc_valid_tm(const struct tm* p_time);

RTCStatus_t rtc_dsk324_CalendarTimeSet(const struct tm* p_time){
    if (SELF.op != DSK324RTC_IDLE)
        return RTC_BUSY;

    if (!rtc_valid_tm(p_time))
        return FSP_ERR_INVALID_ARGUMENT;

    DSK324Ctx*  regs = &rtc_dsk324_setup; //rtc_dsk324_ctx;
    memcpy(regs, &rtc_dsk324_ctx, sizeof(*regs));

    const struct tm*  x = p_time;

    regs->sec   = num2bcd(x->tm_sec);
    regs->min   = num2bcd(x->tm_min);
    regs->hour  = num2bcd(x->tm_hour);
    regs->day   = num2bcd(x->tm_mday-TM_MDAY0)  + RTC_DSK324_MDAY0;
    regs->month = num2bcd(x->tm_mon-TM_MON0)    + RTC_DSK324_MON0;
    regs->year  = num2bcd(x->tm_year + TM_YEAR0 - RTC_DSK324_YEAR0);
    regs->week  = x->tm_wday - TM_WDAY0;
    if (x->tm_isdst > 0)
        regs->ctrl |= DSK324CTRL_RAM;
    else
        regs->ctrl &= ~DSK324CTRL_RAM;

    // маркирую валидность времени в часах
    regs->flag &= ~DSK324FLAG_VDHF;

    return rtc_dsk324_set(regs);
}

static
bool rtc_valid_tm(const struct tm* x){
    return (x->tm_sec < 60)
          && (x->tm_min < 60)
          && (x->tm_hour < 24)
          && (x->tm_mday < (TM_MDAY0 + 31) ) && (x->tm_mday >= TM_MDAY0)
          && (x->tm_mon >= TM_MON0) && (x->tm_mon < (TM_MON0 + 12) )
          && (x->tm_year >= (RTC_DSK324_YEAR0-TM_YEAR0)) && (x->tm_year <= ( (RTC_DSK324_YEAR0+99)-TM_YEAR0))
          && (x->tm_wday < (TM_WDAY0+7) ) && (x->tm_wday >= TM_WDAY0)
          ;
}
