Contiki-NG
msp430.c
1/*
2 * Copyright (c) 2005, Swedish Institute of Computer Science
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the Institute nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * This file is part of the Contiki operating system.
30 */
31
32#include "contiki.h"
33#include "dev/watchdog.h"
34
35/* dco_required set to 1 will cause the CPU not to go into
36 * sleep modes where the DCO clock stopped */
37int msp430_dco_required;
38
39#if defined(__MSP430__) && defined(__GNUC__)
40#define asmv(arg) __asm__ __volatile__ (arg)
41#endif
42
43/*---------------------------------------------------------------------------*/
44#if defined(__MSP430__) && defined(__GNUC__) && MSP430_MEMCPY_WORKAROUND
45void *
46w_memcpy(void *out, const void *in, size_t n)
47{
48 uint8_t *src, *dest;
49 src = (uint8_t *)in;
50 dest = (uint8_t *)out;
51 while(n-- > 0) {
52 *dest++ = *src++;
53 }
54 return out;
55}
56#endif /* __GNUC__ && __MSP430__ && MSP430_MEMCPY_WORKAROUND */
57/*---------------------------------------------------------------------------*/
58#if defined(__MSP430__) && defined(__GNUC__) && MSP430_MEMCPY_WORKAROUND
59void *
60w_memset(void *out, int value, size_t n)
61{
62 uint8_t *dest;
63 dest = (uint8_t *)out;
64 while(n-- > 0) {
65 *dest++ = value & 0xff;
66 }
67 return out;
68}
69#endif /* __GNUC__ && __MSP430__ && MSP430_MEMCPY_WORKAROUND */
70/*---------------------------------------------------------------------------*/
71void
72msp430_init_dco(void)
73{
74 if(CALBC1_8MHZ != 0xFF) {
75 DCOCTL = 0x00;
76 BCSCTL1 = CALBC1_8MHZ; /*Set DCO to 8MHz */
77 DCOCTL = CALDCO_8MHZ;
78 } else { /*start using reasonable values at 8 Mhz */
79 DCOCTL = 0x00;
80 BCSCTL1 = 0x8D;
81 DCOCTL = 0x88;
82 }
83
84 /*BCSCTL1 |= XT2OFF; // Make sure XT2 is off */
85 /* BCSCTL2 = 0x00; // MCLK = DCOCLK/1 */
86 /* SMCLK = DCOCLK/1 */
87 /* DCO Internal Resistor */
88}
89/*---------------------------------------------------------------------------*/
90static void
91init_ports(void)
92{
93 /* Turn everything off, device drivers enable what is needed. */
94
95 /* All configured for digital I/O */
96#ifdef P1SEL
97 P1SEL = 0;
98#endif
99#ifdef P2SEL
100 P2SEL = 0;
101#endif
102#ifdef P3SEL
103 P3SEL = 0;
104#endif
105#ifdef P4SEL
106 P4SEL = 0;
107#endif
108#ifdef P5SEL
109 P5SEL = 0;
110#endif
111#ifdef P6SEL
112 P6SEL = 0;
113#endif
114
115 /* All available inputs */
116#ifdef P1DIR
117 P1DIR = 0;
118 P1OUT = 0;
119#endif
120#ifdef P2DIR
121 P2DIR = 0;
122 P2OUT = 0;
123#endif
124#ifdef P3DIR
125 P3DIR = 0;
126 P3OUT = 0;
127#endif
128#ifdef P4DIR
129 P4DIR = 0;
130 P4OUT = 0;
131#endif
132
133#ifdef P5DIR
134 P5DIR = 0;
135 P5OUT = 0;
136#endif
137
138#ifdef P6DIR
139 P6DIR = 0;
140 P6OUT = 0;
141#endif
142
143#ifdef P7DIR
144 P7DIR = 0;
145 P7OUT = 0;
146#endif
147
148#ifdef P8DIR
149 P8DIR = 0;
150 P8OUT = 0;
151#endif
152
153 P1IE = 0;
154 P2IE = 0;
155}
156/*---------------------------------------------------------------------------*/
157/* msp430-ld may align _end incorrectly. Workaround in cpu_init. */
158#if defined(__MSP430__) && defined(__GNUC__)
159extern int _end; /* Not in sys/unistd.h */
160static char *cur_break = (char *)&_end;
161#endif
162
163/*---------------------------------------------------------------------------*/
164/* add/remove_lpm_req - for requiring a specific LPM mode. currently Contiki */
165/* jumps to LPM3 to save power, but DMA will not work if DCO is not clocked */
166/* so some modules might need to enter their LPM requirements */
167/* NOTE: currently only works with LPM1 (e.g. DCO) requirements. */
168/*---------------------------------------------------------------------------*/
169void
170msp430_add_lpm_req(int req)
171{
172 if(req <= MSP430_REQUIRE_LPM1) {
173 msp430_dco_required++;
174 }
175}
176void
177msp430_remove_lpm_req(int req)
178{
179 if(req <= MSP430_REQUIRE_LPM1) {
180 msp430_dco_required--;
181 }
182}
183void
184msp430_cpu_init(void)
185{
186 dint();
188 init_ports();
189 /* set DCO to a reasonable default value (8MHz) */
190 msp430_init_dco();
191 /* calibrate the DCO step-by-step */
192 msp430_sync_dco();
193 eint();
194#if defined(__MSP430__) && defined(__GNUC__)
195 if((uintptr_t)cur_break & 1) { /* Workaround for msp430-ld bug! */
196 cur_break++;
197 }
198#endif
199 msp430_dco_required = 0;
200}
201/*---------------------------------------------------------------------------*/
202
203/*---------------------------------------------------------------------------*/
204/*
205 * Mask all interrupts that can be masked.
206 */
207int
208splhigh_(void)
209{
210 /* Clear the GIE (General Interrupt Enable) flag. */
211 int sr;
212#ifdef __IAR_SYSTEMS_ICC__
213 sr = __get_SR_register();
214 __bic_SR_register(GIE);
215#else
216 asmv("mov r2, %0" : "=r" (sr));
217 asmv("bic %0, r2" : : "i" (GIE));
218 /* GCC 9 warns about risk of incorrect execution without nop after
219 interrupt state changes. */
220 asmv("nop");
221#endif
222 return sr & GIE; /* Ignore other sr bits. */
223}
224/*---------------------------------------------------------------------------*/
225/*
226 * Restore previous interrupt mask.
227 */
228/* void */
229/* splx_(int sr) */
230/* { */
231/* #ifdef __IAR_SYSTEMS_ICC__ */
232/* __bis_SR_register(sr); */
233/* #else */
234/* /\* If GIE was set, restore it. *\/ */
235/* asmv("bis %0, r2" : : "r" (sr)); */
236/* #endif */
237/* } */
238/*---------------------------------------------------------------------------*/
239#ifdef __IAR_SYSTEMS_ICC__
240int
241__low_level_init(void)
242{
243 /* turn off watchdog so that C-init will run */
244 WDTCTL = WDTPW + WDTHOLD;
245 /*
246 * Return value:
247 *
248 * 1 - Perform data segment initialization.
249 * 0 - Skip data segment initialization.
250 */
251 return 1;
252}
253#endif
254/*---------------------------------------------------------------------------*/
255void
256msp430_sync_dco(void)
257{
258 uint16_t oldcapture;
259 int16_t diff;
260 /* DELTA_2 assumes an ACLK of 32768 Hz */
261#define DELTA_2 ((MSP430_CPU_SPEED) / 32768)
262
263 /* Select SMCLK clock, and capture on ACLK for TBCCR6 */
264 TBCTL = TBSSEL1 | TBCLR;
265 TBCCTL6 = CCIS0 + CM0 + CAP;
266 /* start the timer */
267 TBCTL |= MC1;
268
269 while(1) {
270 /* wait for the next capture */
271 TBCCTL6 &= ~CCIFG;
272 while(!(TBCCTL6 & CCIFG));
273 oldcapture = TBCCR6;
274
275 /* wait for the next capture - and calculate difference */
276 TBCCTL6 &= ~CCIFG;
277 while(!(TBCCTL6 & CCIFG));
278 diff = TBCCR6 - oldcapture;
279
280 /* resynchronize the DCO speed if not at target */
281 if(DELTA_2 == diff) {
282 break; /* if equal, leave "while(1)" */
283 } else if(DELTA_2 < diff) { /* DCO is too fast, slow it down */
284 DCOCTL--;
285 if(DCOCTL == 0xFF) { /* Did DCO roll under? */
286 BCSCTL1--;
287 }
288 } else { /* -> Select next lower RSEL */
289 DCOCTL++;
290 if(DCOCTL == 0x00) { /* Did DCO roll over? */
291 BCSCTL1++;
292 }
293 /* -> Select next higher RSEL */
294 }
295 }
296
297 /* Stop the timer - conserves energy according to user guide */
298 TBCTL = 0;
299}
300/*---------------------------------------------------------------------------*/
void watchdog_init(void)
Initialisation function for the WDT.
Definition: watchdog.c:63