1 /*
2  * Copyright (c) 2019 Winner Microelectronics Co., Ltd.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2018-09-15     flyingcys    1st version
9  * 2019-03-01     fanwenl      add alarm
10  */
11 
12 #include <rtdevice.h>
13 #include <rtthread.h>
14 #include <sys/time.h>
15 #include "wm_regs.h"
16 #include "wm_irq.h"
17 #include "tls_common.h"
18 #include "wm_rtc.h"
19 #include "drv_rtc.h"
20 
21 #ifdef BSP_USING_RTC
22 
23 #if defined(RT_USING_SOFT_RTC)
24 #error "Please CANCEL the RT_USING_SOFT_RTC option. Make sure there is only one RTC device on the system."
25 #endif
26 
27 static struct rt_rtc_device rtc_device;
28 
wm_get_timestamp(void)29 static time_t wm_get_timestamp(void)
30 {
31     struct tm tm_new = {0};
32 
33     int ctrl1 = 0;
34     int ctrl2 = 0;
35 
36     ctrl1 = tls_reg_read32(HR_PMU_RTC_CTRL1);
37     ctrl2 = tls_reg_read32(HR_PMU_RTC_CTRL2);
38     tm_new.tm_year = ((int)((int)ctrl2 & 0x00007f00) >> 8);
39     tm_new.tm_mon  = (ctrl2 & 0x0000000f);
40     tm_new.tm_mday = (ctrl1 & 0x1f000000) >> 24;
41     tm_new.tm_hour = (ctrl1 & 0x001f0000) >> 16;
42     tm_new.tm_min  = (ctrl1 & 0x00003f00) >>  8;
43     tm_new.tm_sec  =  ctrl1 & 0x0000003f;
44 
45     return timegm(&tm_new);
46 }
47 
wm_set_timestamp(time_t timestamp)48 static int wm_set_timestamp(time_t timestamp)
49 {
50     int ctrl1 = 0;
51     int ctrl2 = 0;
52 
53     struct tm tblock;
54 
55     gmtime_r(&timestamp, &tblock);
56 
57     ctrl2  = tls_reg_read32(HR_PMU_RTC_CTRL2);  /* disable */
58     ctrl2 &= ~(1 << 16);
59     tls_reg_write32(HR_PMU_RTC_CTRL2, ctrl2);
60 
61     ctrl1 |= tblock.tm_sec;
62     ctrl1 |= tblock.tm_min  << 8;
63     ctrl1 |= tblock.tm_hour << 16;
64     ctrl1 |= tblock.tm_mday << 24;
65     tls_reg_write32(HR_PMU_RTC_CTRL1, ctrl1);
66 
67     ctrl2  = 0;
68     ctrl2 |= tblock.tm_mon;
69     ctrl2 |= tblock.tm_year << 8;
70     tls_reg_write32(HR_PMU_RTC_CTRL2, ctrl2);
71 
72     ctrl2  = tls_reg_read32(HR_PMU_RTC_CTRL2);/* enable */
73     ctrl2 |= (1 << 16);
74     tls_reg_write32(HR_PMU_RTC_CTRL2, ctrl2);
75 
76     return RT_EOK;
77 }
78 #ifdef BSP_USING_ALARM
wm_alarm_set_timestamp(struct rt_rtc_wkalarm * wkalarm)79 static int wm_alarm_set_timestamp(struct rt_rtc_wkalarm *wkalarm)
80 {
81     int ctrl1 = 0;
82     int ctrl2 = 0;
83     struct tm tblock;
84     time_t timestamp = 0;
85 
86     timestamp = wm_get_timestamp();
87     gmtime_r(&timestamp, &tblock);
88 
89     tls_irq_enable(PMU_RTC_INT);
90 
91     ctrl1 |= wkalarm->tm_sec;
92     ctrl1 |= wkalarm->tm_min  << 8;
93     ctrl1 |= wkalarm->tm_hour << 16;
94     ctrl1 |= tblock.tm_mday << 24;
95 
96     ctrl2 |= tblock.tm_mon;
97     ctrl2 |= tblock.tm_year << 8;
98 
99     tls_reg_write32(HR_PMU_RTC_CTRL2, ctrl2 | BIT(16));
100 
101     tls_reg_write32(HR_PMU_RTC_CTRL1, ctrl1 | BIT(31));/* must set the enable */
102 
103     return RT_EOK;
104 }
105 
wm_rtc_alarm_callback(void * arg)106 static void wm_rtc_alarm_callback(void *arg)
107 {
108     rt_alarm_update(0, 0);
109 }
110 #endif
wm_rtc_init(rt_device_t dev)111 static rt_err_t wm_rtc_init(rt_device_t dev)
112 {
113     wm_set_timestamp((time_t)0);
114 
115 #ifdef BSP_USING_ALARM
116     tls_rtc_isr_register(wm_rtc_alarm_callback, (void *)0);
117 #endif
118     return RT_EOK;
119 }
120 
wm_rtc_open(rt_device_t dev,rt_uint16_t oflag)121 static rt_err_t wm_rtc_open(rt_device_t dev, rt_uint16_t oflag)
122 {
123     return RT_EOK;
124 }
125 
wm_rtc_close(rt_device_t dev)126 static rt_err_t wm_rtc_close(rt_device_t dev)
127 {
128     return RT_EOK;
129 }
130 
wm_rtc_control(rt_device_t dev,int cmd,void * args)131 static rt_err_t wm_rtc_control(rt_device_t dev, int cmd, void *args)
132 {
133 #ifdef BSP_USING_ALARM
134     struct rt_rtc_device* rtc_device;
135     rtc_device = (struct rt_rtc_device* )dev;
136 #endif
137     RT_ASSERT(dev != RT_NULL);
138 
139     switch (cmd)
140     {
141     case RT_DEVICE_CTRL_RTC_GET_TIME:
142         *(rt_uint32_t *)args = wm_get_timestamp();
143         break;
144     case RT_DEVICE_CTRL_RTC_SET_TIME:
145         wm_set_timestamp(*(time_t *)args);
146         break;
147 #ifdef BSP_USING_ALARM
148     case RT_DEVICE_CTRL_RTC_GET_ALARM:
149         *(struct rt_rtc_wkalarm *)args = rtc_device->wkalarm;
150         return RT_EOK;
151     case RT_DEVICE_CTRL_RTC_SET_ALARM:
152         rtc_device->wkalarm = *(struct rt_rtc_wkalarm *)args;
153         wm_alarm_set_timestamp(&rtc_device->wkalarm);
154         break;
155 #endif
156     default:
157         return -RT_EINVAL;
158     }
159     return RT_EOK;
160 }
161 
wm_rtc_read(rt_device_t dev,rt_off_t pos,void * buffer,rt_size_t size)162 static rt_ssize_t wm_rtc_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
163 {
164     wm_rtc_control(dev, RT_DEVICE_CTRL_RTC_GET_TIME, buffer);
165     return size;
166 }
167 
wm_rtc_write(rt_device_t dev,rt_off_t pos,const void * buffer,rt_size_t size)168 static rt_ssize_t wm_rtc_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
169 {
170     wm_rtc_control(dev, RT_DEVICE_CTRL_RTC_SET_TIME, (void *)buffer);
171     return size;
172 }
173 
174 #ifdef RT_USING_DEVICE_OPS
175 const static struct rt_device_ops _ops =
176 {
177     .init = wm_rtc_init,
178     .open = wm_rtc_open,
179     .close = wm_rtc_close,
180     .read = wm_rtc_read,
181     .write = wm_rtc_write,
182     .control = wm_rtc_control
183 };
184 #endif
185 
wm_hw_rtc_init(void)186 int wm_hw_rtc_init(void)
187 {
188     rt_memset(&rtc_device, 0, sizeof(rtc_device));
189 
190     rtc_device.device.type        = RT_Device_Class_RTC;
191     rtc_device.device.rx_indicate = RT_NULL;
192     rtc_device.device.tx_complete = RT_NULL;
193 
194 #ifdef RT_USING_DEVICE_OPS
195     rtc_device.ops         = &_ops;
196 #else
197     rtc_device.device.init        = wm_rtc_init;
198     rtc_device.device.open        = wm_rtc_open;
199     rtc_device.device.close       = wm_rtc_close;
200     rtc_device.device.read        = wm_rtc_read;
201     rtc_device.device.write       = wm_rtc_write;
202     rtc_device.device.control     = wm_rtc_control;
203 #endif
204     rtc_device.device.user_data   = RT_NULL;
205 
206     /* register a rtc device */
207     rt_device_register(&rtc_device.device, "rtc", RT_DEVICE_FLAG_RDWR);
208 
209     return 0;
210 }
211 INIT_DEVICE_EXPORT(wm_hw_rtc_init);
212 #endif /* BSP_USING_RTC */
213