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 */
37 int 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
45 void *
46 w_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
59 void *
60 w_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 /*---------------------------------------------------------------------------*/
71 void
72 msp430_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 /*---------------------------------------------------------------------------*/
125 static void
126 init_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__)
194 extern int _end; /* Not in sys/unistd.h */
195 static 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 /*---------------------------------------------------------------------------*/
204 void
205 msp430_add_lpm_req(int req)
206 {
207  if(req <= MSP430_REQUIRE_LPM1) {
208  msp430_dco_required++;
209  }
210 }
211 
212 void
213 msp430_remove_lpm_req(int req)
214 {
215  if(req <= MSP430_REQUIRE_LPM1) {
216  msp430_dco_required--;
217  }
218 }
219 
220 void
221 msp430_cpu_init(void)
222 {
223  dint();
224  watchdog_init();
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__)
247 void *
248 sbrk(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  */
270 int
271 splhigh_(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 #endif
282  return sr & GIE; /* Ignore other sr bits. */
283 }
284 /*---------------------------------------------------------------------------*/
285 /*
286  * Restore previous interrupt mask.
287  */
288 /* void */
289 /* splx_(int sr) */
290 /* { */
291 /* #ifdef __IAR_SYSTEMS_ICC__ */
292 /* __bis_SR_register(sr); */
293 /* #else */
294 /* /\* If GIE was set, restore it. *\/ */
295 /* asmv("bis %0, r2" : : "r" (sr)); */
296 /* #endif */
297 /* } */
298 /*---------------------------------------------------------------------------*/
299 #ifdef __IAR_SYSTEMS_ICC__
300 int __low_level_init(void)
301 {
302  /* turn off watchdog so that C-init will run */
303  WDTCTL = WDTPW + WDTHOLD;
304  /*
305  * Return value:
306  *
307  * 1 - Perform data segment initialization.
308  * 0 - Skip data segment initialization.
309  */
310  return 1;
311 }
312 #endif
313 /*---------------------------------------------------------------------------*/
314 #if DCOSYNCH_CONF_ENABLED
315 /* this code will always start the TimerB if not already started */
316 void
317 msp430_sync_dco(void) {
318  uint16_t last;
319  uint16_t diff;
320 /* uint32_t speed; */
321  /* DELTA_2 assumes an ACLK of 32768 Hz */
322 #define DELTA_2 ((MSP430_CPU_SPEED) / 32768)
323 
324  /* Select SMCLK clock, and capture on ACLK for TBCCR6 */
325  TBCTL = TBSSEL1 | TBCLR;
326  TBCCTL6 = CCIS0 + CM0 + CAP;
327  /* start the timer */
328  TBCTL |= MC1;
329 
330  /* wait for next Capture */
331  TBCCTL6 &= ~CCIFG;
332  while(!(TBCCTL6 & CCIFG));
333  last = TBCCR6;
334 
335  TBCCTL6 &= ~CCIFG;
336  /* wait for next Capture - and calculate difference */
337  while(!(TBCCTL6 & CCIFG));
338  diff = TBCCR6 - last;
339 
340  /* Stop timer - conserves energy according to user guide */
341  TBCTL = 0;
342 
343 /* speed = diff; */
344 /* speed = speed * 32768; */
345 /* printf("Last TAR diff:%d target: %ld ", diff, DELTA_2); */
346 /* printf("CPU Speed: %lu DCOCTL: %d\n", speed, DCOCTL); */
347 
348  /* resynchronize the DCO speed if not at target */
349  if(DELTA_2 < diff) { /* DCO is too fast, slow it down */
350  DCOCTL--;
351  if(DCOCTL == 0xFF) { /* Did DCO role under? */
352  BCSCTL1--;
353  }
354  } else if(DELTA_2 > diff) {
355  DCOCTL++;
356  if(DCOCTL == 0x00) { /* Did DCO role over? */
357  BCSCTL1++;
358  }
359  }
360 }
361 #endif /* DCOSYNCH_CONF_ENABLED */
362 /*---------------------------------------------------------------------------*/
void watchdog_init(void)
Initialisation function for the WDT.
Definition: watchdog.c:63