Contiki-NG
Loading...
Searching...
No Matches
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 /* This code taken from the FU Berlin sources and reformatted. */
75#define DELTA ((MSP430_CPU_SPEED) / (32768 / 8))
76
77 unsigned int compare, oldcapture = 0;
78 unsigned int i;
79
80 BCSCTL1 = 0xa4; /* ACLK is devided by 4. RSEL=6 no division for MCLK
81 and SSMCLK. XT2 is off. */
82
83 BCSCTL2 = 0x00; /* Init FLL to desired frequency using the 32762Hz
84 crystal DCO frquenzy = 2,4576 MHz */
85
86 BCSCTL1 |= DIVA1 + DIVA0; /* ACLK = LFXT1CLK/8 */
87 for(i = 0xffff; i > 0; i--) { /* Delay for XTAL to settle */
88 asm("nop");
89 }
90
91 CCTL2 = CCIS0 + CM0 + CAP; /* Define CCR2, CAP, ACLK */
92 TACTL = TASSEL1 + TACLR + MC1; /* SMCLK, continous mode */
93
94
95 while(1) {
96
97 while((CCTL2 & CCIFG) != CCIFG); /* Wait until capture occured! */
98 CCTL2 &= ~CCIFG; /* Capture occured, clear flag */
99 compare = CCR2; /* Get current captured SMCLK */
100 compare = compare - oldcapture; /* SMCLK difference */
101 oldcapture = CCR2; /* Save current captured SMCLK */
102
103 if(DELTA == compare) {
104 break; /* if equal, leave "while(1)" */
105 } else if(DELTA < compare) { /* DCO is too fast, slow it down */
106 DCOCTL--;
107 if(DCOCTL == 0xFF) { /* Did DCO role under? */
108 BCSCTL1--;
109 }
110 } else { /* -> Select next lower RSEL */
111 DCOCTL++;
112 if(DCOCTL == 0x00) { /* Did DCO role over? */
113 BCSCTL1++;
114 }
115 /* -> Select next higher RSEL */
116 }
117 }
118
119 CCTL2 = 0; /* Stop CCR2 function */
120 TACTL = 0; /* Stop Timer_A */
121
122 BCSCTL1 &= ~(DIVA1 + DIVA0); /* remove /8 divisor from ACLK again */
123}
124/*---------------------------------------------------------------------------*/
125static void
126init_ports(void)
127{
128 /* Turn everything off, device drivers enable what is needed. */
129
130 /* All configured for digital I/O */
131#ifdef P1SEL
132 P1SEL = 0;
133#endif
134#ifdef P2SEL
135 P2SEL = 0;
136#endif
137#ifdef P3SEL
138 P3SEL = 0;
139#endif
140#ifdef P4SEL
141 P4SEL = 0;
142#endif
143#ifdef P5SEL
144 P5SEL = 0;
145#endif
146#ifdef P6SEL
147 P6SEL = 0;
148#endif
149
150 /* All available inputs */
151#ifdef P1DIR
152 P1DIR = 0;
153 P1OUT = 0;
154#endif
155#ifdef P2DIR
156 P2DIR = 0;
157 P2OUT = 0;
158#endif
159#ifdef P3DIR
160 P3DIR = 0;
161 P3OUT = 0;
162#endif
163#ifdef P4DIR
164 P4DIR = 0;
165 P4OUT = 0;
166#endif
167
168#ifdef P5DIR
169 P5DIR = 0;
170 P5OUT = 0;
171#endif
172
173#ifdef P6DIR
174 P6DIR = 0;
175 P6OUT = 0;
176#endif
177
178#ifdef P7DIR
179 P7DIR = 0;
180 P7OUT = 0;
181#endif
182
183#ifdef P8DIR
184 P8DIR = 0;
185 P8OUT = 0;
186#endif
187
188 P1IE = 0;
189 P2IE = 0;
190}
191/*---------------------------------------------------------------------------*/
192/* msp430-ld may align _end incorrectly. Workaround in cpu_init. */
193#if defined(__MSP430__) && defined(__GNUC__)
194extern int _end; /* Not in sys/unistd.h */
195static char *cur_break = (char *)&_end;
196#endif
197
198/*---------------------------------------------------------------------------*/
199/* add/remove_lpm_req - for requiring a specific LPM mode. currently Contiki */
200/* jumps to LPM3 to save power, but DMA will not work if DCO is not clocked */
201/* so some modules might need to enter their LPM requirements */
202/* NOTE: currently only works with LPM1 (e.g. DCO) requirements. */
203/*---------------------------------------------------------------------------*/
204void
205msp430_add_lpm_req(int req)
206{
207 if(req <= MSP430_REQUIRE_LPM1) {
208 msp430_dco_required++;
209 }
210}
211
212void
213msp430_remove_lpm_req(int req)
214{
215 if(req <= MSP430_REQUIRE_LPM1) {
216 msp430_dco_required--;
217 }
218}
219
220void
221msp430_cpu_init(void)
222{
223 dint();
225 init_ports();
226 msp430_init_dco();
227 eint();
228#if defined(__MSP430__) && defined(__GNUC__)
229 if((uintptr_t)cur_break & 1) { /* Workaround for msp430-ld bug! */
230 cur_break++;
231 }
232#endif
233
234 msp430_dco_required = 0;
235}
236/*---------------------------------------------------------------------------*/
237
238#define STACK_EXTRA 32
239
240/*
241 * Allocate memory from the heap. Check that we don't collide with the
242 * stack right now (some other routine might later). A watchdog might
243 * be used to check if cur_break and the stack pointer meet during
244 * runtime.
245 */
246#if defined(__MSP430__) && defined(__GNUC__)
247void *
248sbrk(int incr)
249{
250 char *stack_pointer;
251
252 asmv("mov r1, %0" : "=r" (stack_pointer));
253 stack_pointer -= STACK_EXTRA;
254 if(incr > (stack_pointer - cur_break))
255 return (void *)-1; /* ENOMEM */
256
257 void *old_break = cur_break;
258 cur_break += incr;
259 /*
260 * If the stack was never here then [old_break .. cur_break] should
261 * be filled with zeros.
262 */
263 return old_break;
264}
265#endif
266/*---------------------------------------------------------------------------*/
267/*
268 * Mask all interrupts that can be masked.
269 */
270int
271splhigh_(void)
272{
273 int sr;
274 /* Clear the GIE (General Interrupt Enable) flag. */
275#ifdef __IAR_SYSTEMS_ICC__
276 sr = __get_SR_register();
277 __bic_SR_register(GIE);
278#else
279 asmv("mov r2, %0" : "=r" (sr));
280 asmv("bic %0, r2" : : "i" (GIE));
281 /* GCC 9 warns about risk of incorrect execution without nop after
282 interrupt state changes. */
283 asmv("nop");
284#endif
285 return sr & GIE; /* Ignore other sr bits. */
286}
287/*---------------------------------------------------------------------------*/
288/*
289 * Restore previous interrupt mask.
290 */
291/* void */
292/* splx_(int sr) */
293/* { */
294/* #ifdef __IAR_SYSTEMS_ICC__ */
295/* __bis_SR_register(sr); */
296/* #else */
297/* /\* If GIE was set, restore it. *\/ */
298/* asmv("bis %0, r2" : : "r" (sr)); */
299/* #endif */
300/* } */
301/*---------------------------------------------------------------------------*/
302#ifdef __IAR_SYSTEMS_ICC__
303int __low_level_init(void)
304{
305 /* turn off watchdog so that C-init will run */
306 WDTCTL = WDTPW + WDTHOLD;
307 /*
308 * Return value:
309 *
310 * 1 - Perform data segment initialization.
311 * 0 - Skip data segment initialization.
312 */
313 return 1;
314}
315#endif
316/*---------------------------------------------------------------------------*/
317#if DCOSYNCH_CONF_ENABLED
318/* this code will always start the TimerB if not already started */
319void
320msp430_sync_dco(void) {
321 uint16_t last;
322 uint16_t diff;
323/* uint32_t speed; */
324 /* DELTA_2 assumes an ACLK of 32768 Hz */
325#define DELTA_2 ((MSP430_CPU_SPEED) / 32768)
326
327 /* Select SMCLK clock, and capture on ACLK for TBCCR6 */
328 TBCTL = TBSSEL1 | TBCLR;
329 TBCCTL6 = CCIS0 + CM0 + CAP;
330 /* start the timer */
331 TBCTL |= MC1;
332
333 /* wait for next Capture */
334 TBCCTL6 &= ~CCIFG;
335 while(!(TBCCTL6 & CCIFG));
336 last = TBCCR6;
337
338 TBCCTL6 &= ~CCIFG;
339 /* wait for next Capture - and calculate difference */
340 while(!(TBCCTL6 & CCIFG));
341 diff = TBCCR6 - last;
342
343 /* Stop timer - conserves energy according to user guide */
344 TBCTL = 0;
345
346/* speed = diff; */
347/* speed = speed * 32768; */
348/* printf("Last TAR diff:%d target: %ld ", diff, DELTA_2); */
349/* printf("CPU Speed: %lu DCOCTL: %d\n", speed, DCOCTL); */
350
351 /* resynchronize the DCO speed if not at target */
352 if(DELTA_2 < diff) { /* DCO is too fast, slow it down */
353 DCOCTL--;
354 if(DCOCTL == 0xFF) { /* Did DCO role under? */
355 BCSCTL1--;
356 }
357 } else if(DELTA_2 > diff) {
358 DCOCTL++;
359 if(DCOCTL == 0x00) { /* Did DCO role over? */
360 BCSCTL1++;
361 }
362 }
363}
364#endif /* DCOSYNCH_CONF_ENABLED */
365/*---------------------------------------------------------------------------*/
void watchdog_init(void)
Initialisation function for the WDT.
Definition watchdog.c:63