5dd3b7427a
* bsp: cvitek: kconfig: add wdt for cv18xx_riscv Add Watchdog timer in Kconfig. Signed-off-by: Chen Wang <unicorn_wang@outlook.com> * drv: cvitek: remove using macro from source file Building of source file should be controlled by SConscript, but not in source file itself. Signed-off-by: Chen Wang <unicorn_wang@outlook.com> * bsp: cvitek: kconfig: add i2c for cv18xx_riscv Add I2C in Kconfig for c906B. Note, the IRQ# is different from that of c906L. Signed-off-by: Chen Wang <unicorn_wang@outlook.com> * bsp: cvitek: kconfig: add rtc for cv18xx_riscv Add RTC in Kconfig for c906B. Signed-off-by: Chen Wang <unicorn_wang@outlook.com> * bsp: cvitek: fix channel issue for pwm driver The original code confuses the concepts of controllers and channels. Fixed it and do some code cleanup. Signed-off-by: Chen Wang <unicorn_wang@outlook.com> * bsp:cvitek: add i2c pinmux config for cv18xx_riscv Pinmux in driver code is controlled by SOC type, bcos driver code should be general and support all pins defined by SoC. Pinmux configuration in Kconfig is controlled by BOARD type, bcos when we operate on board, it does not expose all chip-level pin signals and we can only use part of them. Following is I2C signals exported by duo family. Details see https://milkv.io/docs/duo/overview. Note: we have not added support for duo-S. Duo === NAME I2C CV1800B/GPIO <PINNAME>__<FUNCNAME> ---- --- ------------ --------------------- GP0 I2C0_SCL XGPIOA[28] IIC0_SCL__IIC0_SCL GP1 I2C0_SDA XGPIOA[29] IIC0_SDA__IIC0_SDA GP4 I2C1_SCL PWR_GPIO[19] SD1_D2__IIC1_SCL GP9 I2C1_SCL PWR_GPIO[18] SD1_D3__IIC1_SCL GP11 I2C1_SCL XGPIOC[10] PAD_MIPIRX0N__IIC1_SCL GP5 I2C1_SDA PWR_GPIO[20] SD1_D1__IIC1_SDA GP8 I2C1_SDA PWR_GPIO[21] SD1_D0__IIC1_SDA GP10 I2C1_SDA XGPIOC[9] PAD_MIPIRX1P__IIC1_SDA GP7 I2C3_SCL PWR_GPIO[22] SD1_CMD__IIC3_SCL GP6 I2C3_SDA PWR_GPIO[23] SD1_CLK__IIC3_SDA Duo 256m ======== NAME I2C CV1800B/GPIO <PINNAME>__<FUNCNAME> ---- --- ------------ --------------------- GP4 I2C1_SCL PWR_GPIO[19] SD1_D2__IIC1_SCL GP9 I2C1_SCL PWR_GPIO[18] SD1_D3__IIC1_SCL GP5 I2C1_SDA PWR_GPIO[20] SD1_D1__IIC1_SDA GP8 I2C1_SDA PWR_GPIO[21] SD1_D0__IIC1_SDA GP11 I2C2_SCL XGPIOC[15] PAD_MIPI_TXP1__IIC2_SCL GP10 I2C2_SDA XGPIOC[14] PAD_MIPI_TXM1__IIC2_SDA GP7 I2C3_SCL PWR_GPIO[22] SD1_CMD__IIC3_SCL GP6 I2C3_SDA PWR_GPIO[23] SD1_CLK__IIC3_SDA Duo S ===== NAME I2C CV1800B/GPIO <PINNAME>__<FUNCNAME> ---- --- ------------ --------------------- J3-B18 I2C1_SCL XGPIOB[18] VIVO_D3__IIC1_SCL J3-B12 I2C1_SCL XGPIOB[12] VIVO_D9__IIC1_SCL J3-B11 I2C1_SDA XGPIOB[11] VIVO_D10__IIC1_SDA J3-B13 I2C2_SCL XGPIOB[13] VIVO_D8__IIC2_SCL J4-E1 I2C2_SCL PWR_GPIO[1] PWR_GPIO1__IIC2_SCL J3-B14 I2C2_SDA XGPIOB[14] VIVO_D7__IIC2_SDA J4-E2 I2C2_SDA PWR_GPIO[2] PWR_GPIO2__IIC2_SDA J3-B20 I2C4_SCL XGPIOB[20] VIVO_D1__IIC4_SCL J4-B1 I2C4_SCL XGPIOB[1] ADC3__IIC4_SCL J3-B21 I2C4_SDA XGPIOB[21] VIVO_D0__IIC4_SDA J4-B2 I2C4_SDA XGPIOB[2] ADC2__IIC4_SDA Signed-off-by: Chen Wang <unicorn_wang@outlook.com> Signed-off-by: flyingcys <flyingcys@163.com> * bsp:cvitek: remove using macro from source file for i2c Signed-off-by: Chen Wang <unicorn_wang@outlook.com> * bsp:cvitek: unify menu message text for i2c as other drivers Other dirvers has no extra word "HW". Signed-off-by: Chen Wang <unicorn_wang@outlook.com> * bsp:cvitek: add i2c pinmux config for c906_little Porting what we have done in commit "bsp:cvitek: add i2c pinmux config for cv18xx_riscv" to c906_little. Signed-off-by: Chen Wang <unicorn_wang@outlook.com> --------- Signed-off-by: Chen Wang <unicorn_wang@outlook.com> Signed-off-by: flyingcys <flyingcys@163.com> Co-authored-by: flyingcys <flyingcys@163.com>
406 lines
10 KiB
C
406 lines
10 KiB
C
/*
|
|
* Copyright (c) 2006-2024, RT-Thread Development Team
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2024-03-04 ShichengChu the first version
|
|
*/
|
|
|
|
#include <rtthread.h>
|
|
#include <rtdevice.h>
|
|
|
|
#define DBG_TAG "DRV.RTC"
|
|
#define DBG_LVL DBG_WARNING
|
|
#include <rtdbg.h>
|
|
|
|
#include "pinctrl.h"
|
|
#include "mmio.h"
|
|
|
|
#define CVI_RTC_BASE 0x05026000U
|
|
#define RTC_ALARM_O 17
|
|
#define CVI_RTC_CTRL_BASE 0x05025000U
|
|
#define CLK_EN_0 0x03002000U
|
|
#define CLK_RTC_25M_BIT (1 << 8)
|
|
|
|
/* CVITEK RTC registers */
|
|
#define CVI_RTC_ANA_CALIB 0x0
|
|
#define CVI_RTC_SEC_PULSE_GEN 0x4
|
|
#define CVI_RTC_ALARM_TIME 0x8
|
|
#define CVI_RTC_ALARM_ENABLE 0xC
|
|
#define CVI_RTC_SET_SEC_CNTR_VALUE 0x10
|
|
#define CVI_RTC_SET_SEC_CNTR_TRIG 0x14
|
|
#define CVI_RTC_SEC_CNTR_VALUE 0x18
|
|
#define CVI_RTC_APB_RDATA_SEL 0x3C
|
|
#define CVI_RTC_POR_DB_MAGIC_KEY 0x68
|
|
#define CVI_RTC_EN_PWR_WAKEUP 0xBC
|
|
#define CVI_RTC_PWR_DET_SEL 0x140
|
|
|
|
/* CVITEK RTC MACRO registers */
|
|
#define RTC_MACRO_DA_CLEAR_ALL 0x480
|
|
#define RTC_MACRO_DA_SOC_READY 0x48C
|
|
#define RTC_MACRO_RO_T 0x4A8
|
|
#define RTC_MACRO_RG_SET_T 0x498
|
|
|
|
/* CVITEK RTC CTRL registers */
|
|
#define CVI_RTC_FC_COARSE_EN 0x40
|
|
#define CVI_RTC_FC_COARSE_CAL 0x44
|
|
#define CVI_RTC_FC_FINE_EN 0x48
|
|
#define CVI_RTC_FC_FINE_CAL 0x50
|
|
|
|
#define RTC_SEC_MAX_VAL 0xFFFFFFFF
|
|
|
|
#define RTC_OFFSET_SN 0x5201800
|
|
#define RTC_ALARM_IRQ_NUM 0x11
|
|
|
|
struct rtc_device_object
|
|
{
|
|
rt_rtc_dev_t rtc_dev;
|
|
};
|
|
|
|
static struct rtc_device_object rtc_device;
|
|
|
|
#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400)
|
|
|
|
typedef struct {
|
|
int tm_sec; ///< Second. [0-59]
|
|
int tm_min; ///< Minute. [0-59]
|
|
int tm_hour; ///< Hour. [0-23]
|
|
int tm_mday; ///< Day. [1-31]
|
|
int tm_mon; ///< Month. [0-11]
|
|
int tm_year; ///< Year-1900. [70- ] !NOTE:Set 100 mean 2000
|
|
int tm_wday; ///< Day of week. [0-6 ] !NOTE:Set 0 mean Sunday
|
|
int tm_yday; ///< Days in year.[0-365] !NOTE:Set 0 mean January 1st
|
|
} cvi_rtc_time_t;
|
|
|
|
static const unsigned char cvi_rtc_days_in_month[] = {
|
|
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
|
};
|
|
|
|
static inline int is_leap_year(unsigned int year)
|
|
{
|
|
return (!(year % 4) && (year % 100)) || !(year % 400);
|
|
}
|
|
|
|
static int rtc_month_days(unsigned int month, unsigned int year)
|
|
{
|
|
return cvi_rtc_days_in_month[month] + (is_leap_year(year) && month == 1);
|
|
}
|
|
|
|
static void hal_cvi_rtc_clk_set(int enable)
|
|
{
|
|
uint32_t clk_state;
|
|
|
|
clk_state = mmio_read_32((long unsigned int)CLK_EN_0);
|
|
|
|
if(enable)
|
|
clk_state |= CLK_RTC_25M_BIT;
|
|
else
|
|
clk_state &= ~(CLK_RTC_25M_BIT);
|
|
|
|
mmio_write_32((long unsigned int)CLK_EN_0, clk_state);
|
|
}
|
|
|
|
static void hal_cvi_rtc_enable_sec_counter(uintptr_t rtc_base)
|
|
{
|
|
uint32_t value = 0;
|
|
|
|
value = mmio_read_32(rtc_base + CVI_RTC_SEC_PULSE_GEN) & ~(1 << 31);
|
|
mmio_write_32(rtc_base + CVI_RTC_SEC_PULSE_GEN, value);
|
|
|
|
value = mmio_read_32(rtc_base + CVI_RTC_ANA_CALIB) & ~(1 << 31);
|
|
mmio_write_32(rtc_base + CVI_RTC_ANA_CALIB, value);
|
|
|
|
mmio_read_32(rtc_base + CVI_RTC_SEC_CNTR_VALUE);
|
|
mmio_write_32(rtc_base + CVI_RTC_ALARM_ENABLE, 0x0);
|
|
}
|
|
|
|
static void hal_cvi_rtc_set_time(uintptr_t rtc_base, unsigned long sec)
|
|
{
|
|
mmio_write_32(rtc_base + CVI_RTC_SET_SEC_CNTR_VALUE, sec);
|
|
mmio_write_32(rtc_base + CVI_RTC_SET_SEC_CNTR_TRIG, 1);
|
|
mmio_write_32(rtc_base + RTC_MACRO_RG_SET_T, sec);
|
|
mmio_write_32(rtc_base + RTC_MACRO_DA_CLEAR_ALL, 1);
|
|
mmio_write_32(rtc_base + RTC_MACRO_DA_SOC_READY, 1);
|
|
mmio_write_32(rtc_base + RTC_MACRO_DA_CLEAR_ALL, 0);
|
|
mmio_write_32(rtc_base + RTC_MACRO_RG_SET_T, 0);
|
|
mmio_write_32(rtc_base + RTC_MACRO_DA_SOC_READY, 0);
|
|
}
|
|
|
|
static int hal_cvi_rtc_get_time_sec(uintptr_t rtc_base,unsigned long *ret_sec)
|
|
{
|
|
int ret = 0;
|
|
unsigned long sec;
|
|
unsigned long sec_ro_t;
|
|
|
|
sec = mmio_read_32(rtc_base + CVI_RTC_SEC_CNTR_VALUE);
|
|
sec_ro_t = mmio_read_32(rtc_base + RTC_MACRO_RO_T);
|
|
|
|
LOG_D("sec=%lx, sec_ro_t=%lx\n", sec, sec_ro_t);
|
|
|
|
if (sec_ro_t > 0x30000000) {
|
|
sec = sec_ro_t;
|
|
// Writeback to SEC CVI_RTC_SEC_CNTR_VALUE
|
|
mmio_write_32(rtc_base + CVI_RTC_SET_SEC_CNTR_VALUE, sec);
|
|
mmio_write_32(rtc_base + CVI_RTC_SET_SEC_CNTR_TRIG, 1);
|
|
} else if (sec < 0x30000000) {
|
|
LOG_D("RTC invalid time\n");
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
*ret_sec = sec;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static inline int64_t div_u64_rem(uint64_t dividend, uint32_t divisor, uint32_t *remainder)
|
|
{
|
|
*remainder = dividend % divisor;
|
|
return dividend / divisor;
|
|
}
|
|
|
|
/*
|
|
* rtc_time_to_tm64 - Converts time64_t to rtc_time.
|
|
* Convert seconds since 01-01-1970 00:00:00 to Gregorian date.
|
|
*/
|
|
static void rtc_time64_to_tm(int64_t time, cvi_rtc_time_t *cvi_tm)
|
|
{
|
|
unsigned int month, year, secs;
|
|
int days;
|
|
|
|
/* time must be positive */
|
|
days = div_u64_rem(time, 86400, &secs);
|
|
|
|
/* day of the week, 1970-01-01 was a Thursday */
|
|
cvi_tm->tm_wday = (days + 4) % 7;
|
|
|
|
year = 1970 + days / 365;
|
|
days -= (year - 1970) * 365
|
|
+ LEAPS_THRU_END_OF(year - 1)
|
|
- LEAPS_THRU_END_OF(1970 - 1);
|
|
while (days < 0) {
|
|
year -= 1;
|
|
days += 365 + is_leap_year(year);
|
|
}
|
|
cvi_tm->tm_year = year - 1900;
|
|
cvi_tm->tm_yday = days + 1;
|
|
|
|
for (month = 0; month < 11; month++) {
|
|
int newdays;
|
|
|
|
newdays = days - rtc_month_days(month, year);
|
|
if (newdays < 0)
|
|
break;
|
|
days = newdays;
|
|
}
|
|
cvi_tm->tm_mon = month;
|
|
cvi_tm->tm_mday = days + 1;
|
|
|
|
cvi_tm->tm_hour = secs / 3600;
|
|
secs -= cvi_tm->tm_hour * 3600;
|
|
cvi_tm->tm_min = secs / 60;
|
|
cvi_tm->tm_sec = secs - cvi_tm->tm_min * 60;
|
|
}
|
|
|
|
static int64_t mktime64(const unsigned int year0, const unsigned int mon0,
|
|
const unsigned int day, const unsigned int hour,
|
|
const unsigned int min, const unsigned int sec)
|
|
{
|
|
unsigned int mon = mon0, year = year0;
|
|
|
|
/* 1..12 -> 11,12,1..10 */
|
|
if (0 >= (int) (mon -= 2)) {
|
|
mon += 12; /* Puts Feb last since it has leap day */
|
|
year -= 1;
|
|
}
|
|
|
|
return ((((int64_t)
|
|
(year/4 - year/100 + year/400 + 367*mon/12 + day) +
|
|
year*365 - 719499
|
|
)*24 + hour /* now have hours - midnight tomorrow handled here */
|
|
)*60 + min /* now have minutes */
|
|
)*60 + sec; /* finally seconds */
|
|
}
|
|
|
|
/*
|
|
* rtc_tm_to_time64 - Converts rtc_time to time64_t.
|
|
* Convert Gregorian date to seconds since 01-01-1970 00:00:00.
|
|
*/
|
|
static int64_t rtc_tm_to_time64(const cvi_rtc_time_t *cvi_tm)
|
|
{
|
|
return mktime64(cvi_tm->tm_year + 1900, cvi_tm->tm_mon + 1, cvi_tm->tm_mday,
|
|
cvi_tm->tm_hour, cvi_tm->tm_min, cvi_tm->tm_sec);
|
|
}
|
|
|
|
static rt_err_t _rtc_get_timeval(struct timeval *tv)
|
|
{
|
|
unsigned long sec;
|
|
cvi_rtc_time_t t = {0};
|
|
struct tm tm_new = {0};
|
|
|
|
hal_cvi_rtc_get_time_sec(CVI_RTC_BASE, &sec);
|
|
rtc_time64_to_tm(sec, &t);
|
|
|
|
tm_new.tm_sec = t.tm_sec;
|
|
tm_new.tm_min = t.tm_min;
|
|
tm_new.tm_hour = t.tm_hour;
|
|
tm_new.tm_wday = t.tm_wday;
|
|
tm_new.tm_mday = t.tm_mday;
|
|
tm_new.tm_mon = t.tm_mon;
|
|
tm_new.tm_year = t.tm_year;
|
|
|
|
tv->tv_sec = timegm(&tm_new);
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t _rtc_init(void)
|
|
{
|
|
hal_cvi_rtc_clk_set(1);
|
|
hal_cvi_rtc_enable_sec_counter(CVI_RTC_BASE);
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t _rtc_get_secs(time_t *sec)
|
|
{
|
|
struct timeval tv;
|
|
|
|
_rtc_get_timeval(&tv);
|
|
*(time_t *) sec = tv.tv_sec;
|
|
LOG_D("RTC: get rtc_time %d", *sec);
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t _rtc_set_secs(time_t *sec)
|
|
{
|
|
rt_err_t result = RT_EOK;
|
|
cvi_rtc_time_t t = {0};
|
|
struct tm tm = {0};
|
|
unsigned long set_sec;
|
|
|
|
gmtime_r(sec, &tm);
|
|
|
|
t.tm_sec = tm.tm_sec;
|
|
t.tm_min = tm.tm_min;
|
|
t.tm_hour = tm.tm_hour;
|
|
t.tm_mday = tm.tm_mday;
|
|
t.tm_mon = tm.tm_mon;
|
|
t.tm_year = tm.tm_year;
|
|
t.tm_wday = tm.tm_wday;
|
|
|
|
set_sec = rtc_tm_to_time64(&t);
|
|
hal_cvi_rtc_set_time(CVI_RTC_BASE, set_sec);
|
|
|
|
return result;
|
|
}
|
|
|
|
#ifdef RT_USING_ALARM
|
|
static void rtc_alarm_enable(rt_bool_t enable)
|
|
{
|
|
mmio_write_32(CVI_RTC_BASE + CVI_RTC_ALARM_ENABLE, enable);
|
|
}
|
|
|
|
static void rt_hw_rtc_isr(int irqno, void *param)
|
|
{
|
|
rt_interrupt_enter();
|
|
|
|
/* send event to alarm */
|
|
rt_alarm_update(&rtc_device.rtc_dev.parent, 1);
|
|
/* clear alarm */
|
|
rtc_alarm_enable(0);
|
|
|
|
rt_interrupt_leave();
|
|
}
|
|
#endif
|
|
|
|
static rt_err_t _rtc_get_alarm(struct rt_rtc_wkalarm *alarm)
|
|
{
|
|
if (alarm == RT_NULL)
|
|
return -RT_ERROR;
|
|
|
|
unsigned long int sec;
|
|
cvi_rtc_time_t t = {0};
|
|
|
|
sec = mmio_read_32(CVI_RTC_BASE + CVI_RTC_ALARM_TIME);
|
|
rtc_time64_to_tm(sec, &t);
|
|
|
|
alarm->tm_sec = t.tm_sec;
|
|
alarm->tm_min = t.tm_min;
|
|
alarm->tm_hour = t.tm_hour;
|
|
alarm->tm_mday = t.tm_mday;
|
|
alarm->tm_mon = t.tm_mon;
|
|
alarm->tm_year = t.tm_year;
|
|
LOG_D("GET_ALARM %d:%d:%d", alarm->tm_hour, alarm->tm_min, alarm->tm_sec);
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t _rtc_set_alarm(struct rt_rtc_wkalarm *alarm)
|
|
{
|
|
if (alarm == RT_NULL)
|
|
return -RT_ERROR;
|
|
|
|
cvi_rtc_time_t t = {0};
|
|
unsigned long int set_sec;
|
|
|
|
if (alarm->enable){
|
|
t.tm_sec = alarm->tm_sec;
|
|
t.tm_min = alarm->tm_min;
|
|
t.tm_hour = alarm->tm_hour;
|
|
t.tm_mday = alarm->tm_mday;
|
|
t.tm_mon = alarm->tm_mon;
|
|
t.tm_year = alarm->tm_year;
|
|
|
|
set_sec = rtc_tm_to_time64(&t);
|
|
mmio_write_32(CVI_RTC_BASE + CVI_RTC_ALARM_TIME, set_sec);
|
|
|
|
LOG_D("GET_ALARM %d:%d:%d", alarm->tm_hour, alarm->tm_min, alarm->tm_sec);
|
|
}
|
|
|
|
rtc_alarm_enable(alarm->enable);
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static const struct rt_rtc_ops _rtc_ops =
|
|
{
|
|
_rtc_init,
|
|
_rtc_get_secs,
|
|
_rtc_set_secs,
|
|
#ifdef RT_USING_ALARM
|
|
_rtc_get_alarm,
|
|
_rtc_set_alarm,
|
|
#else
|
|
RT_NULL,
|
|
RT_NULL,
|
|
#endif
|
|
_rtc_get_timeval,
|
|
RT_NULL,
|
|
};
|
|
|
|
static int rt_hw_rtc_init(void)
|
|
{
|
|
rt_err_t result;
|
|
|
|
rtc_device.rtc_dev.ops = &_rtc_ops;
|
|
result = rt_hw_rtc_register(&rtc_device.rtc_dev, "rtc", RT_DEVICE_FLAG_RDWR, RT_NULL);
|
|
if (result != RT_EOK)
|
|
{
|
|
LOG_E("rtc register err code: %d", result);
|
|
return result;
|
|
}
|
|
|
|
#ifdef RT_USING_ALARM
|
|
rt_hw_interrupt_install(RTC_ALARM_IRQ_NUM, rt_hw_rtc_isr, RT_NULL, "rtc");
|
|
rt_hw_interrupt_umask(RTC_ALARM_IRQ_NUM);
|
|
#endif
|
|
|
|
LOG_D("rtc init success");
|
|
|
|
return RT_EOK;
|
|
}
|
|
INIT_DEVICE_EXPORT(rt_hw_rtc_init);
|