forked from zephyrproject-rtos/zephyr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrtc_qmsi.c
204 lines (161 loc) · 4.65 KB
/
rtc_qmsi.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/*
* Copyright (c) 2015 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <errno.h>
#include <device.h>
#include <drivers/ioapic.h>
#include <init.h>
#include <kernel.h>
#include <rtc.h>
#include <power.h>
#include <soc.h>
#include <misc/util.h>
#include "qm_isr.h"
#include "qm_rtc.h"
struct rtc_data {
#ifdef CONFIG_RTC_QMSI_API_REENTRANCY
struct k_sem sem;
#endif
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
u32_t device_power_state;
#endif
};
#define RTC_HAS_CONTEXT_DATA \
(CONFIG_RTC_QMSI_API_REENTRANCY || CONFIG_DEVICE_POWER_MANAGEMENT)
#if RTC_HAS_CONTEXT_DATA
static struct rtc_data rtc_context;
#define RTC_CONTEXT (&rtc_context)
#else
#define RTC_CONTEXT (NULL)
#endif /* RTC_HAS_CONTEXT_DATA */
#ifdef CONFIG_RTC_QMSI_API_REENTRANCY
#define RP_GET(dev) (&((struct rtc_data *)(dev->driver_data))->sem)
#else
#define RP_GET(dev) (NULL)
#endif
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
static void rtc_qmsi_set_power_state(struct device *dev, u32_t power_state)
{
struct rtc_data *context = dev->driver_data;
context->device_power_state = power_state;
}
static u32_t rtc_qmsi_get_power_state(struct device *dev)
{
struct rtc_data *context = dev->driver_data;
return context->device_power_state;
}
#else
#define rtc_qmsi_set_power_state(...)
#endif
static void rtc_qmsi_enable(struct device *dev)
{
clk_periph_enable(CLK_PERIPH_RTC_REGISTER | CLK_PERIPH_CLK);
}
static void rtc_qmsi_disable(struct device *dev)
{
clk_periph_disable(CLK_PERIPH_RTC_REGISTER);
}
static int rtc_qmsi_set_config(struct device *dev, struct rtc_config *cfg)
{
qm_rtc_config_t qm_cfg;
int result = 0;
qm_cfg.init_val = cfg->init_val;
qm_cfg.alarm_en = cfg->alarm_enable;
qm_cfg.alarm_val = cfg->alarm_val;
/* Casting callback type due different input parameter from QMSI
* compared aganst the Zephyr callback from void cb(struct device *dev)
* to void cb(void *)
*/
qm_cfg.callback = (void *) cfg->cb_fn;
qm_cfg.callback_data = dev;
/* Set prescaler value. Ideally, the divider should come from struct
* rtc_config instead. It's safe to use RTC_DIVIDER here for now since
* values defined by clk_rtc_div and by QMSI's clk_rtc_div_t match for
* both D2000 and SE.
*/
qm_cfg.prescaler = (clk_rtc_div_t)CONFIG_RTC_PRESCALER - 1;
if (IS_ENABLED(CONFIG_RTC_QMSI_API_REENTRANCY)) {
k_sem_take(RP_GET(dev), K_FOREVER);
}
if (qm_rtc_set_config(QM_RTC_0, &qm_cfg)) {
result = -EIO;
}
if (IS_ENABLED(CONFIG_RTC_QMSI_API_REENTRANCY)) {
k_sem_give(RP_GET(dev));
}
k_busy_wait(60);
return result;
}
static int rtc_qmsi_set_alarm(struct device *dev, const u32_t alarm_val)
{
return qm_rtc_set_alarm(QM_RTC_0, alarm_val);
}
static u32_t rtc_qmsi_read(struct device *dev)
{
return QM_RTC[QM_RTC_0]->rtc_ccvr;
}
static u32_t rtc_qmsi_get_pending_int(struct device *dev)
{
return QM_RTC[QM_RTC_0]->rtc_stat;
}
static const struct rtc_driver_api api = {
.enable = rtc_qmsi_enable,
.disable = rtc_qmsi_disable,
.read = rtc_qmsi_read,
.set_config = rtc_qmsi_set_config,
.set_alarm = rtc_qmsi_set_alarm,
.get_pending_int = rtc_qmsi_get_pending_int,
};
static int rtc_qmsi_init(struct device *dev)
{
if (IS_ENABLED(CONFIG_RTC_QMSI_API_REENTRANCY)) {
k_sem_init(RP_GET(dev), 1, UINT_MAX);
}
IRQ_CONNECT(DT_RTC_0_IRQ, CONFIG_RTC_0_IRQ_PRI,
qm_rtc_0_isr, NULL, DT_RTC_0_IRQ_FLAGS);
/* Unmask RTC interrupt */
irq_enable(DT_RTC_0_IRQ);
/* Route RTC interrupt to the current core */
QM_IR_UNMASK_INTERRUPTS(QM_INTERRUPT_ROUTER->rtc_0_int_mask);
rtc_qmsi_set_power_state(dev, DEVICE_PM_ACTIVE_STATE);
return 0;
}
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
static qm_rtc_context_t rtc_ctx;
static int rtc_suspend_device(struct device *dev)
{
qm_rtc_save_context(QM_RTC_0, &rtc_ctx);
rtc_qmsi_set_power_state(dev, DEVICE_PM_SUSPEND_STATE);
return 0;
}
static int rtc_resume_device(struct device *dev)
{
qm_rtc_restore_context(QM_RTC_0, &rtc_ctx);
rtc_qmsi_set_power_state(dev, DEVICE_PM_ACTIVE_STATE);
return 0;
}
/*
* Implements the driver control management functionality
* the *context may include IN data or/and OUT data
*/
static int rtc_qmsi_device_ctrl(struct device *dev, u32_t ctrl_command,
void *context)
{
if (ctrl_command == DEVICE_PM_SET_POWER_STATE) {
if (*((u32_t *)context) == DEVICE_PM_SUSPEND_STATE) {
return rtc_suspend_device(dev);
} else if (*((u32_t *)context) == DEVICE_PM_ACTIVE_STATE) {
return rtc_resume_device(dev);
}
} else if (ctrl_command == DEVICE_PM_GET_POWER_STATE) {
*((u32_t *)context) = rtc_qmsi_get_power_state(dev);
return 0;
}
return 0;
}
#endif
DEVICE_DEFINE(rtc, CONFIG_RTC_0_NAME, &rtc_qmsi_init, rtc_qmsi_device_ctrl,
RTC_CONTEXT, NULL, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&api);