/*
 * Copyright (c) 2006-2020, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2020-06-08     hqfang       first implementation.
 */

#include "drv_rtc.h"

#ifdef BSP_USING_RTC

static time_t get_timestamp(void)
{
    return (time_t)rtc_counter_get();
}

static int set_timestamp(time_t timestamp)
{
    /* wait until last write operation on RTC registers has finished */
    rtc_lwoff_wait();
    /* change the current time */
    rtc_counter_set((uint32_t)timestamp);
    /* wait until last write operation on RTC registers has finished */
    rtc_lwoff_wait();
    return RT_EOK;
}

static void rtc_configuration(void)
{
    /* enable PMU and BKPI clocks */
    rcu_periph_clock_enable(RCU_BKPI);
    rcu_periph_clock_enable(RCU_PMU);
    /* allow access to BKP domain */
    pmu_backup_write_enable();

    /* reset backup domain */
    bkp_deinit();

    /* enable LXTAL */
    rcu_osci_on(RCU_LXTAL);
    /* wait till LXTAL is ready */
    rcu_osci_stab_wait(RCU_LXTAL);

    /* select RCU_LXTAL as RTC clock source */
    rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);

    /* enable RTC Clock */
    rcu_periph_clock_enable(RCU_RTC);

    /* wait for RTC registers synchronization */
    rtc_register_sync_wait();

    /* wait until last write operation on RTC registers has finished */
    rtc_lwoff_wait();

    /* wait until last write operation on RTC registers has finished */
    rtc_lwoff_wait();

    /* set RTC prescaler: set RTC period to 1s */
    rtc_prescaler_set(32767);

    /* wait until last write operation on RTC registers has finished */
    rtc_lwoff_wait();
}

static rt_err_t gd32_rtc_init(rt_device_t dev)
{
    if (bkp_data_read(BKP_DATA_0) != 0xA5A5)
    {
        rtc_configuration();
        bkp_data_write(BKP_DATA_0, 0xA5A5);
    }
    else
    {
        /* allow access to BKP domain */
        rcu_periph_clock_enable(RCU_PMU);
        pmu_backup_write_enable();

        /* wait for RTC registers synchronization */
        rtc_register_sync_wait();
        /* wait until last write operation on RTC registers has finished */
        rtc_lwoff_wait();
    }

    return RT_EOK;
}

static rt_err_t gd32_rtc_open(rt_device_t dev, rt_uint16_t oflag)
{
    return RT_EOK;
}

static rt_err_t gd32_rtc_close(rt_device_t dev)
{
    return RT_EOK;
}

static rt_size_t gd32_rtc_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
    return RT_EOK;
}

static rt_size_t gd32_rtc_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
    return RT_EOK;
}

static rt_err_t gd32_rtc_control(rt_device_t dev, int cmd, void *args)
{
    RT_ASSERT(dev != RT_NULL);

    switch (cmd)
    {
    case RT_DEVICE_CTRL_RTC_GET_TIME:
    {
        *(uint32_t *)args = get_timestamp();
    }
    break;

    case RT_DEVICE_CTRL_RTC_SET_TIME:
    {
        set_timestamp(*(time_t *)args);
    }
    break;

    default:
        return RT_EINVAL;
    }

    return RT_EOK;
}

static struct rt_device rtc_device =
{
    .type    = RT_Device_Class_RTC,
    .init    = gd32_rtc_init,
    .open    = gd32_rtc_open,
    .close   = gd32_rtc_close,
    .read    = gd32_rtc_read,
    .write   = gd32_rtc_write,
    .control = gd32_rtc_control,
};

int rt_hw_rtc_init(void)
{
    rt_err_t ret = RT_EOK;

    ret = rt_device_register(&rtc_device, "rtc", RT_DEVICE_FLAG_RDWR);

    rt_device_open(&rtc_device, RT_DEVICE_OFLAG_RDWR);

    return RT_EOK;
}

INIT_DEVICE_EXPORT(rt_hw_rtc_init);

#endif /* BSP_USING_RTC */