Contiki 2.6

msp430.c

00001 /*
00002  * Copyright (c) 2005, Swedish Institute of Computer Science
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the Institute nor the names of its contributors
00014  *    may be used to endorse or promote products derived from this software
00015  *    without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
00018  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
00021  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00022  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00023  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00024  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00025  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00026  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00027  * SUCH DAMAGE.
00028  *
00029  * This file is part of the Contiki operating system.
00030  */
00031 
00032 #include "contiki.h"
00033 #include "dev/watchdog.h"
00034 
00035 /* dco_required set to 1 will cause the CPU not to go into
00036    sleep modes where the DCO clock stopped */
00037 int msp430_dco_required;
00038 
00039 #if defined(__MSP430__) && defined(__GNUC__)
00040 #define asmv(arg) __asm__ __volatile__(arg)
00041 #endif
00042 
00043 /*---------------------------------------------------------------------------*/
00044 #if defined(__MSP430__) && defined(__GNUC__) && MSP430_MEMCPY_WORKAROUND
00045 void *
00046 w_memcpy(void *out, const void *in, size_t n)
00047 {
00048   uint8_t *src, *dest;
00049   src = (uint8_t *) in;
00050   dest = (uint8_t *) out;
00051   while(n-- > 0) {
00052     *dest++ = *src++;
00053   }
00054   return out;
00055 }
00056 #endif /* __GNUC__ &&  __MSP430__ && MSP430_MEMCPY_WORKAROUND */
00057 /*---------------------------------------------------------------------------*/
00058 #if defined(__MSP430__) && defined(__GNUC__) && MSP430_MEMCPY_WORKAROUND
00059 void *
00060 w_memset(void *out, int value, size_t n)
00061 {
00062   uint8_t *dest;
00063   dest = (uint8_t *) out;
00064   while(n-- > 0) {
00065     *dest++ = value & 0xff;
00066   }
00067   return out;
00068 }
00069 #endif /* __GNUC__ &&  __MSP430__ && MSP430_MEMCPY_WORKAROUND */
00070 /*---------------------------------------------------------------------------*/
00071 void
00072 msp430_init_dco(void)
00073 {
00074   /* This code taken from the FU Berlin sources and reformatted. */
00075 #define DELTA    ((MSP430_CPU_SPEED) / (32768 / 8))
00076 
00077   unsigned int compare, oldcapture = 0;
00078   unsigned int i;
00079 
00080   BCSCTL1 = 0xa4; /* ACLK is devided by 4. RSEL=6 no division for MCLK
00081                      and SSMCLK. XT2 is off. */
00082 
00083   BCSCTL2 = 0x00; /* Init FLL to desired frequency using the 32762Hz
00084                      crystal DCO frquenzy = 2,4576 MHz  */
00085 
00086   BCSCTL1 |= DIVA1 + DIVA0;             /* ACLK = LFXT1CLK/8 */
00087   for(i = 0xffff; i > 0; i--) {         /* Delay for XTAL to settle */
00088     asm("nop");
00089   }
00090 
00091   CCTL2 = CCIS0 + CM0 + CAP;            /* Define CCR2, CAP, ACLK */
00092   TACTL = TASSEL1 + TACLR + MC1;        /* SMCLK, continous mode */
00093 
00094 
00095   while(1) {
00096 
00097     while((CCTL2 & CCIFG) != CCIFG);    /* Wait until capture occured! */
00098     CCTL2 &= ~CCIFG;                    /* Capture occured, clear flag */
00099     compare = CCR2;                     /* Get current captured SMCLK */
00100     compare = compare - oldcapture;     /* SMCLK difference */
00101     oldcapture = CCR2;                  /* Save current captured SMCLK */
00102 
00103     if(DELTA == compare) {
00104       break;                            /* if equal, leave "while(1)" */
00105     } else if(DELTA < compare) {        /* DCO is too fast, slow it down */
00106       DCOCTL--;
00107       if(DCOCTL == 0xFF) {              /* Did DCO role under? */
00108         BCSCTL1--;
00109       }
00110     } else {                            /* -> Select next lower RSEL */
00111       DCOCTL++;
00112       if(DCOCTL == 0x00) {              /* Did DCO role over? */
00113         BCSCTL1++;
00114       }
00115                                         /* -> Select next higher RSEL  */
00116     }
00117   }
00118 
00119   CCTL2 = 0;                            /* Stop CCR2 function */
00120   TACTL = 0;                            /* Stop Timer_A */
00121 
00122   BCSCTL1 &= ~(DIVA1 + DIVA0);          /* remove /8 divisor from ACLK again */
00123 }
00124 /*---------------------------------------------------------------------------*/
00125 static void
00126 init_ports(void)
00127 {
00128   /* Turn everything off, device drivers enable what is needed. */
00129 
00130   /* All configured for digital I/O */
00131 #ifdef P1SEL
00132   P1SEL = 0;
00133 #endif
00134 #ifdef P2SEL
00135   P2SEL = 0;
00136 #endif
00137 #ifdef P3SEL
00138   P3SEL = 0;
00139 #endif
00140 #ifdef P4SEL
00141   P4SEL = 0;
00142 #endif
00143 #ifdef P5SEL
00144   P5SEL = 0;
00145 #endif
00146 #ifdef P6SEL
00147   P6SEL = 0;
00148 #endif
00149 
00150   /* All available inputs */
00151 #ifdef P1DIR
00152   P1DIR = 0;
00153   P1OUT = 0;
00154 #endif
00155 #ifdef P2DIR
00156   P2DIR = 0;
00157   P2OUT = 0;
00158 #endif
00159 #ifdef P3DIR
00160   P3DIR = 0;
00161   P3OUT = 0;
00162 #endif
00163 #ifdef P4DIR
00164   P4DIR = 0;
00165   P4OUT = 0;
00166 #endif
00167 
00168 #ifdef P5DIR
00169   P5DIR = 0;
00170   P5OUT = 0;
00171 #endif
00172 
00173 #ifdef P6DIR
00174   P6DIR = 0;
00175   P6OUT = 0;
00176 #endif
00177 
00178 #ifdef P7DIR
00179   P7DIR = 0;
00180   P7OUT = 0;
00181 #endif
00182 
00183 #ifdef P8DIR
00184   P8DIR = 0;
00185   P8OUT = 0;
00186 #endif
00187 
00188   P1IE = 0;
00189   P2IE = 0;
00190 }
00191 /*---------------------------------------------------------------------------*/
00192 /* msp430-ld may align _end incorrectly. Workaround in cpu_init. */
00193 #if defined(__MSP430__) && defined(__GNUC__)
00194 extern int _end;                /* Not in sys/unistd.h */
00195 static char *cur_break = (char *)&_end;
00196 #endif
00197 
00198 /*---------------------------------------------------------------------------*/
00199 /* add/remove_lpm_req - for requiring a specific LPM mode. currently Contiki */
00200 /* jumps to LPM3 to save power, but DMA will not work if DCO is not clocked  */
00201 /* so some modules might need to enter their LPM requirements                */
00202 /* NOTE: currently only works with LPM1 (e.g. DCO) requirements.             */
00203 /*---------------------------------------------------------------------------*/
00204 void
00205 msp430_add_lpm_req(int req)
00206 {
00207   if(req <= MSP430_REQUIRE_LPM1) {
00208     msp430_dco_required++;
00209   }
00210 }
00211 
00212 void
00213 msp430_remove_lpm_req(int req)
00214 {
00215   if(req <= MSP430_REQUIRE_LPM1) {
00216     msp430_dco_required--;
00217   }
00218 }
00219 
00220 void
00221 msp430_cpu_init(void)
00222 {
00223   dint();
00224   watchdog_init();
00225   init_ports();
00226   msp430_init_dco();
00227   eint();
00228 #if defined(__MSP430__) && defined(__GNUC__)
00229   if((uintptr_t)cur_break & 1) { /* Workaround for msp430-ld bug! */
00230     cur_break++;
00231   }
00232 #endif
00233 
00234   msp430_dco_required = 0;
00235 }
00236 /*---------------------------------------------------------------------------*/
00237 
00238 #define STACK_EXTRA 32
00239 
00240 /*
00241  * Allocate memory from the heap. Check that we don't collide with the
00242  * stack right now (some other routine might later). A watchdog might
00243  * be used to check if cur_break and the stack pointer meet during
00244  * runtime.
00245  */
00246 #if defined(__MSP430__) && defined(__GNUC__)
00247 void *
00248 sbrk(int incr)
00249 {
00250   char *stack_pointer;
00251 
00252   asmv("mov r1, %0" : "=r" (stack_pointer));
00253   stack_pointer -= STACK_EXTRA;
00254   if(incr > (stack_pointer - cur_break))
00255     return (void *)-1;          /* ENOMEM */
00256 
00257   void *old_break = cur_break;
00258   cur_break += incr;
00259   /*
00260    * If the stack was never here then [old_break .. cur_break] should
00261    * be filled with zeros.
00262   */
00263   return old_break;
00264 }
00265 #endif
00266 /*---------------------------------------------------------------------------*/
00267 /*
00268  * Mask all interrupts that can be masked.
00269  */
00270 int
00271 splhigh_(void)
00272 {
00273   int sr;
00274   /* Clear the GIE (General Interrupt Enable) flag. */
00275 #ifdef __IAR_SYSTEMS_ICC__
00276   sr = __get_SR_register();
00277   __bic_SR_register(GIE);
00278 #else
00279   asmv("mov r2, %0" : "=r" (sr));
00280   asmv("bic %0, r2" : : "i" (GIE));
00281 #endif
00282   return sr & GIE;              /* Ignore other sr bits. */
00283 }
00284 /*---------------------------------------------------------------------------*/
00285 /*
00286  * Restore previous interrupt mask.
00287  */
00288 /* void */
00289 /* splx_(int sr) */
00290 /* { */
00291 /* #ifdef __IAR_SYSTEMS_ICC__ */
00292 /*   __bis_SR_register(sr); */
00293 /* #else */
00294 /*   /\* If GIE was set, restore it. *\/ */
00295 /*   asmv("bis %0, r2" : : "r" (sr)); */
00296 /* #endif */
00297 /* } */
00298 /*---------------------------------------------------------------------------*/
00299 #ifdef __IAR_SYSTEMS_ICC__
00300 int __low_level_init(void)
00301 {
00302   /* turn off watchdog so that C-init will run */
00303   WDTCTL = WDTPW + WDTHOLD;
00304   /*
00305    * Return value:
00306    *
00307    *  1 - Perform data segment initialization.
00308    *  0 - Skip data segment initialization.
00309    */
00310   return 1;
00311 }
00312 #endif
00313 /*---------------------------------------------------------------------------*/
00314 #if DCOSYNCH_CONF_ENABLED
00315 /* this code will always start the TimerB if not already started */
00316 void
00317 msp430_sync_dco(void) {
00318   uint16_t last;
00319   uint16_t diff;
00320 /*   uint32_t speed; */
00321   /* DELTA_2 assumes an ACLK of 32768 Hz */
00322 #define DELTA_2    ((MSP430_CPU_SPEED) / 32768)
00323 
00324   /* Select SMCLK clock, and capture on ACLK for TBCCR6 */
00325   TBCTL = TBSSEL1 | TBCLR;
00326   TBCCTL6 = CCIS0 + CM0 + CAP;
00327   /* start the timer */
00328   TBCTL |= MC1;
00329 
00330   /* wait for next Capture */
00331   TBCCTL6 &= ~CCIFG;
00332   while(!(TBCCTL6 & CCIFG));
00333   last = TBCCR6;
00334 
00335   TBCCTL6 &= ~CCIFG;
00336   /* wait for next Capture - and calculate difference */
00337   while(!(TBCCTL6 & CCIFG));
00338   diff = TBCCR6 - last;
00339 
00340   /* Stop timer - conserves energy according to user guide */
00341   TBCTL = 0;
00342 
00343 /*   speed = diff; */
00344 /*   speed = speed * 32768; */
00345 /*   printf("Last TAR diff:%d target: %ld ", diff, DELTA_2); */
00346 /*   printf("CPU Speed: %lu DCOCTL: %d\n", speed, DCOCTL); */
00347 
00348   /* resynchronize the DCO speed if not at target */
00349   if(DELTA_2 < diff) {        /* DCO is too fast, slow it down */
00350     DCOCTL--;
00351     if(DCOCTL == 0xFF) {              /* Did DCO role under? */
00352       BCSCTL1--;
00353     }
00354   } else if(DELTA_2 > diff) {
00355     DCOCTL++;
00356     if(DCOCTL == 0x00) {              /* Did DCO role over? */
00357       BCSCTL1++;
00358     }
00359   }
00360 }
00361 #endif /* DCOSYNCH_CONF_ENABLED */
00362 /*---------------------------------------------------------------------------*/