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