/**************************************************************************
 * Copyright (C) 2012-2021  Unisound
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 **************************************************************************
 *
 * Description : uni_pmu_osc.h
 * Author      : yzs.unisound.com
 * Date        : 2021.03.01
 *
 **************************************************************************/
#ifndef __UNI_PMU_OSC_H__
#define __UNI_PMU_OSC_H__

#include <driver/uni_irq.h>

#define UNI_OSC_32K_VALUE_MAX 0x1FF ///< 32k trim 最大值
#define UNI_OSC_1M_VALUE_MAX  0x1FF ///< 1m trim 最大值
#define UNI_OSC_24M_VALUE_MAX 0x7F  ///< 24m trim 最大值

#define UNI_OSC_CLOCK_32K  32000    ///< 32k 时钟
#define UNI_OSC_CLOCK_1M   1024000  ///< 1m 时钟
#define UNI_OSC_CLOCK_24M  24576000 ///< 24m 时钟

// trim值下溢测试
// #define UNI_OSC_CLOCK_32K  1280000
// #define UNI_OSC_CLOCK_1M   50000000
// #define UNI_OSC_CLOCK_24M  100000000

// trim值上溢测试
// #define UNI_OSC_CLOCK_32K  3200
// #define UNI_OSC_CLOCK_1M   100000
// #define UNI_OSC_CLOCK_24M  1000000

// 中断MASK
#define UNI_OSC_INT_32K_DONE_MASK   0x01 ///< 32k trim 结束中断掩码
#define UNI_OSC_INT_1M_DONE_MASK    0x02 ///< 1m trim 结束中断掩码
#define UNI_OSC_INT_24M_DONE_MASK   0x04 ///< 24m trim 结束中断掩码
#define UNI_OSC_INT_32K_ERROR_MASK  0x08 ///< 32k trim 出错中断掩码
#define UNI_OSC_INT_1M_ERROR_MASK   0x10 ///< 1m trim 出错中断掩码
#define UNI_OSC_INT_24M_ERROR_MASK  0x20 ///< 24m trim 出错中断掩码

#define UNI_OSC_INT_32K_MASK   ((UNI_OSC_INT_32K_DONE_MASK) | (UNI_OSC_INT_32K_ERROR_MASK))
#define UNI_OSC_INT_1M_MASK    ((UNI_OSC_INT_1M_DONE_MASK) | (UNI_OSC_INT_1M_ERROR_MASK))
#define UNI_OSC_INT_24M_MASK   ((UNI_OSC_INT_24M_DONE_MASK) | (UNI_OSC_INT_24M_ERROR_MASK))

/**< 计算参考时钟cycle数 */
#define UNI_OSC_REF_CYCLE(ref_freq, target_freq, target_cycles) \
	(((unsigned long long)(ref_freq) * (target_cycles)) / (target_freq))

/**
 * @brief OSC 配置命令
 */
typedef enum {
	UNI_OSC_CMD_TRIM_CONFIG,   ///< OSC TRIM 配置
	UNI_OSC_CMD_CURRENT_CYCLE, ///< 实际时钟数获取
	UNI_OSC_CMD_TRIM_VALUE,    ///< TRIM值获取
	UNI_OSC_CMD_TRIM_DELAY,    ///< TRIM 后等待OSC稳定延时
	UNI_OSC_CMD_MAX            ///< OSC 命令最大值
} UNI_OSC_CMD;

/**
 * @brief 时钟类型
 */
typedef enum {
	UNI_OSC_CLK_TYPE_32K, ///< 32K 目标时钟
	UNI_OSC_CLK_TYPE_1M,  ///< 1M 目标时钟
	UNI_OSC_CLK_TYPE_24M, ///< 24M 目标时钟
	UNI_OSC_CLK_TYPE_MAX  ///< 时钟类型枚举最大值
} UNI_OSC_CLK_TYPE;

/**
 * @brief TRIM 模式
 */
typedef enum {
	UNI_OSC_TRIM_MODE_AUTO     = 0x01, ///< 手动校正模式
	UNI_OSC_TRIM_MODE_MANUAL   = 0x02, ///< 自动校正模式
} UNI_OSC_TRIM_MODE;

/**
 * @brief 目标时钟配置
 */
typedef struct osc_target_cycle {
	UNI_OSC_CLK_TYPE type;      ///< 时钟类型
	uint16_t target_cycles;    ///< 目标时钟 cycle 数
	uint16_t ref_cycles;       ///< 参考时钟 cycle 数
} UNI_OSC_TARGET_CYCLE;

/**
 * @brief 当前参考时钟 cycle
 */
typedef struct osc_current_cycle {
	UNI_OSC_CLK_TYPE type;   ///< 时钟类型
	uint32_t cycles;        ///< 参考时钟 cycle 数
} UNI_OSC_CURRENT_CYCLE;

/**
 * @brief TRIM 值
 */
typedef struct osc_trim {
	UNI_OSC_CLK_TYPE type; ///< 时钟类型
	uint32_t trim_value;  ///< trim 值
} UNI_OSC_TRIM;

/**
 * @brief osc 配置结构体
 */
typedef struct osc_trim_config {
	UNI_OSC_CLK_TYPE type;   ///< TRIM 时钟类型
	UNI_OSC_TRIM_MODE mode;  ///< TRIM 模式
	uint16_t target_cycles; ///< 目标时钟 cycle 数
	uint16_t ref_cycles;    ///< 参考时钟 cycle 数

	irq_handler_t callback; ///< TRIM 中断回调函数
	void *priv;             ///< TRIM 回调参数
	uint32_t int_status;    ///< 中断状态
} UNI_OSC_TRIM_CONFIG;

/**
 * @brief 配置 OSC 参数
 *
 * @param cmd 配置命令
 * @param data 要配置的参数
 *
 * @return int 配置是否成功
 * @retval 0 成功
 * @retval -1 失败
 */
int uni_pmu_osc_set(UNI_OSC_CMD cmd, void *data);

/**
 * @brief 获取 OSC 参数
 *
 * @param cmd 获取命令
 * @param data 获取到的参数数据
 *
 * @return int 获取是否成功
 * @retval 0 成功
 * @retval -1 失败
 */
int uni_pmu_osc_get(UNI_OSC_CMD cmd, void *data);

/**
 * @brief 对比目标 cycles 和当前 cycles 大小
 *
 * @param expected_cycles 目标 cycles
 * @param current_cycles 但其cycles
 * @param tolerance 容差值
 *
 * @return int 对比结果
 * @retval 0 : abs(expected_cycles - current_cycles) <= tolerance
 * @retval 1 : expected_cycles > current_cycles + tolerance
 * @retval -1 : current_cycles > expected_cycles + tolerance
 */
int uni_osc_cycles_compare(uint16_t expected_cycles, uint16_t current_cycles, uint16_t tolerance);

/**
 * @brief 使能 OSC 模块
 *
 * @param type 要使能的时钟类型
 * @return int 使能是否成功
 *
 * @retval 0 成功
 * @retval -1 失败
 */
int uni_pmu_osc_enable(UNI_OSC_CLK_TYPE type);

/**
 * @brief 关闭 OSC 模块
 *
 * @param type 要关闭的时钟类型
 * @return int 关闭是否成功
 *
 * @retval 0 成功
 * @retval -1 失败
 */
int uni_pmu_osc_disable(UNI_OSC_CLK_TYPE type);

/**
 * @brief OSC 初始化
 *
 * @return int 初始化是否成功
 * @retval 0 成功
 * @retval -1 失败
 */
int uni_pmu_osc_init(void);

/**
 * @brief 使能 OSC TRIM 功能
 *
 * @param type 要使能的时钟类型
 *
 * @return int 使能是否成功
 * @retval 0 成功
 * @retval -1 失败
 */
int uni_pmu_osc_trim_enable(UNI_OSC_CLK_TYPE type);

/**
 * @brief 初始化 OSC TRIM
 *
 * @return int 初始化是否成功
 * @retval 0 成功
 * @retval -1 失败
 */
int uni_pmu_osc_trim_init(void);

#endif
