1 /* mbed Microcontroller Library
2 * Copyright (c) 2018-2019 ARM Limited
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "lp_ticker_api.h"
18
19 #if DEVICE_LPTICKER
20
21 /*******************************************************************************
22 * lp_ticker implementation on this target is mapped on top of the sleep clock counter
23 * that is running in the lowest energy modes. The sleep clock counter is 48b running
24 * at 32.768KHz. This gives 0.03ms resolution for the low power timer which requires
25 * millisecond accuracy.
26 *
27 ******************************************************************************/
28
29 #include "reg_timer.h"
30 #include "sysctrl_api.h"
31
32 static bool lp_ticker_inited = false;
33
34 /// 3-part macro to function/variable/enum string
35 #define LPT_M2STR_P3_I(p0,p1,p2) p0##p1##p2
36 #define LPT_M2STR_P3(p0,p1,p2) LPT_M2STR_P3_I(p0, p1, p2)
37
38 #define LP_TICKER_TIMER CS_TIM2
39 #define LP_TICKER_TIMER_MRi 0
40 #define LP_TICKER_TIMER_IRQn LPT_M2STR_P3(TIMER2, LP_TICKER_TIMER_MRi, _IRQn)
41 #define LP_TICKER_ISR_FUNC LPT_M2STR_P3(TIMER2, LP_TICKER_TIMER_MRi, _IRQHandler)
42
43 /// Match interrupt enable set
44 #define LPT_MINT_ENA_SET_BIT (0x01UL << LP_TICKER_TIMER_MRi)
45 /// Wrap interrupt enable set
46 #define LPT_WINT_ENA_SET_BIT (0x01UL << 7)
47 /// Match interrupt enable clear
48 #define LPT_MINT_ENA_CLR_BIT (0x01UL << (8 + LP_TICKER_TIMER_MRi))
49 /// Wrap interrupt enable clear
50 #define LPT_WINT_ENA_CLR_BIT (0x01UL << 15)
51 /// Match interrupt clear bit
52 #define LPT_MINT_CLR_BIT (0x01UL << (16 + LP_TICKER_TIMER_MRi))
53 /// Wrap interrupt clear bit
54 #define LPT_WINT_CLR_BIT (0x01UL << 23)
55 /// Match interrupt status masked bit
56 #define LPT_MINT_ST_MSK_BIT (0x01UL << (8 + LP_TICKER_TIMER_MRi))
57 /// Wrap interrupt status masked bit
58 #define LPT_WINT_ST_MSK_BIT (0x01UL << 15)
59 /// Match enable
60 #define LPT_MATCH_ENA_BIT (0x01UL << (12 + LP_TICKER_TIMER_MRi))
61
lp_ticker_get_info()62 const ticker_info_t *lp_ticker_get_info()
63 {
64 static const ticker_info_t info = {
65 1000000, // 1MHz
66 32 // 32 bit counter
67 };
68 return &info;
69 }
70
71 /** LP Ticker Interrupt Handler
72 *
73 * This function handles the lp ticker interrupt.
74 */
LP_TICKER_ISR_FUNC(void)75 void LP_TICKER_ISR_FUNC(void)
76 {
77 uint32_t status = LP_TICKER_TIMER->IS;
78 // wrap interrupt
79 if (status & LPT_WINT_ST_MSK_BIT) {
80 LP_TICKER_TIMER->IC = LPT_WINT_CLR_BIT; // clear interrupt
81 }
82 // match interrupt
83 if (status & LPT_MINT_ST_MSK_BIT) {
84 LP_TICKER_TIMER->IC = LPT_MINT_CLR_BIT; // clear interrupt
85 lp_ticker_irq_handler();
86 }
87 }
88
lp_ticker_init()89 void lp_ticker_init()
90 {
91 if (lp_ticker_inited) {
92 /* calling init again should cancel current interrupt */
93 lp_ticker_disable_interrupt();
94 return;
95 }
96 lp_ticker_inited = true;
97
98 if (pwrctrl_pwrmd_cpusys_sw_record_getf() == CPU_SYS_POWER_ON_RESET) {
99 uint32_t reg = LP_TICKER_TIMER->CTL;
100 if ((reg & 0x01UL) == 0x00UL) { // Init if disabled
101 const uint32_t tick_sel = 0x01UL; // div2(64KHz)
102 LP_TICKER_TIMER->TCL = 0x00UL; // time counter
103 LP_TICKER_TIMER->TCH = 0x00UL;
104 LP_TICKER_TIMER->TD = tick_sel | (0x01UL << 4);
105 reg &= ~(0x03UL << 8);
106 reg |= (0x01UL | (0x01UL << 5) | (tick_sel << 8)); // enable timer, ignore MR high-16bit, set tick freq
107 LP_TICKER_TIMER->CTL = reg;
108 }
109 if ((reg & LPT_MATCH_ENA_BIT) == 0x00UL) {
110 LP_TICKER_TIMER->CTL = reg | LPT_MATCH_ENA_BIT; // enable match
111 }
112 }
113
114 NVIC_SetPriority(LP_TICKER_TIMER_IRQn, __NVIC_PRIO_LOWEST);
115 NVIC_EnableIRQ(LP_TICKER_TIMER_IRQn);
116 }
117
lp_ticker_free()118 void lp_ticker_free()
119 {
120 LP_TICKER_TIMER->CTL &= ~LPT_MATCH_ENA_BIT; // disable match
121 LP_TICKER_TIMER->IC = LPT_MINT_ENA_CLR_BIT;
122 NVIC_DisableIRQ(LP_TICKER_TIMER_IRQn);
123 }
124
lp_ticker_set_interrupt(timestamp_t timestamp)125 void lp_ticker_set_interrupt(timestamp_t timestamp)
126 {
127 // set match value
128 LP_TICKER_TIMER->MR[LP_TICKER_TIMER_MRi].L = (uint32_t)timestamp;
129 // enable match interrupt
130 LP_TICKER_TIMER->IC = LPT_MINT_ENA_SET_BIT;
131 }
132
lp_ticker_fire_interrupt(void)133 void lp_ticker_fire_interrupt(void)
134 {
135 NVIC_SetPendingIRQ(LP_TICKER_TIMER_IRQn);
136 }
137
lp_ticker_disable_interrupt()138 void lp_ticker_disable_interrupt()
139 {
140 LP_TICKER_TIMER->IC = LPT_MINT_ENA_CLR_BIT;
141 }
142
lp_ticker_clear_interrupt()143 void lp_ticker_clear_interrupt()
144 {
145 LP_TICKER_TIMER->IC = LPT_MINT_CLR_BIT;
146 }
147
lp_ticker_read()148 timestamp_t lp_ticker_read()
149 {
150 // Read forever until reaching two of the same
151 volatile unsigned long long read_previous, read_current;
152 do {
153 read_previous = LP_TICKER_TIMER->TCL;
154 read_current = LP_TICKER_TIMER->TCL;
155 } while (read_previous != read_current);
156
157 return read_current;
158 }
159
lp_ticker_read_time(uint32_t * msb,uint32_t * lsb)160 void lp_ticker_read_time(uint32_t *msb, uint32_t *lsb)
161 {
162 uint32_t _msb, _msb_r, _lsb;
163 _msb = LP_TICKER_TIMER->TCH;
164 _lsb = LP_TICKER_TIMER->TCL;
165 _msb_r = LP_TICKER_TIMER->TCH;
166
167 if (_msb_r != _msb)
168 {
169 _lsb = LP_TICKER_TIMER->TCL;
170 }
171 *msb = _msb_r;
172 *lsb = _lsb;
173 }
174
lp_ticker_set_match_time(uint32_t msb,uint32_t lsb)175 void lp_ticker_set_match_time(uint32_t msb, uint32_t lsb)
176 {
177 // set match value
178 LP_TICKER_TIMER->MR[LP_TICKER_TIMER_MRi].H = msb;
179 LP_TICKER_TIMER->MR[LP_TICKER_TIMER_MRi].L = lsb;
180 // enable match interrupt
181 LP_TICKER_TIMER->IC = LPT_MINT_ENA_SET_BIT;
182 }
183
lp_ticker_set_match_time_lo(uint32_t time)184 void lp_ticker_set_match_time_lo(uint32_t time)
185 {
186 LP_TICKER_TIMER->MR[LP_TICKER_TIMER_MRi].L = time; // set match value
187 LP_TICKER_TIMER->IC = LPT_MINT_ENA_SET_BIT; // enable match interrupt
188 }
189
190
lp_ticker_set_ignore_tc_hi(int ignore)191 void lp_ticker_set_ignore_tc_hi(int ignore)
192 {
193 if (ignore) {
194 LP_TICKER_TIMER->CTL |= (0x01UL << 5);
195 } else {
196 LP_TICKER_TIMER->CTL &= ~(0x01UL << 5);
197 }
198 }
199
lp_ticker_time_cmp(uint32_t time1,uint32_t time2)200 bool lp_ticker_time_cmp(uint32_t time1, uint32_t time2)
201 {
202 uint32_t diff = time1 - time2;
203 return (((int32_t)diff) < 0);
204 }
205
lp_ticker_time_past(uint32_t time)206 bool lp_ticker_time_past(uint32_t time)
207 {
208 uint32_t _lsb = LP_TICKER_TIMER->TCL;
209 return (lp_ticker_time_cmp(time, _lsb));
210 }
211
212 #endif /* DEVICE_LPTICKER */
213