-//$Id$
-
-/* "Copyright (c) 2000-2003 The Regents of the University of California.
- * All rights reserved.
- *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation for any purpose, without fee, and without written agreement
- * is hereby granted, provided that the above copyright notice, the following
- * two paragraphs and the author appear in all copies of this software.
- *
- * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
- * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY
- * OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
- */
-
-/**
- * @author Cory Sharp <cssharp@eecs.berkeley.edu>
- * @author Vlado Handziski <handzisk@tkn.tu-berlind.de>
- */
-
-#include "Msp430Timer.h"
-
-generic module Msp430ClockP(uint16_t TARGET_DCO_KHZ, uint16_t ACLK_KHZ) @safe()
-{
- provides interface Init;
- provides interface Msp430ClockInit;
-}
-implementation
-{
- MSP430REG_NORACE(IE1);
- MSP430REG_NORACE(TACTL);
- MSP430REG_NORACE(TAIV);
- MSP430REG_NORACE(TBCTL);
- MSP430REG_NORACE(TBIV);
-
- #if defined(__MSP430_HAS_BC2__) /* basic clock module+ */
- #define FIRST_STEP 0x1000
- #else /* orig basic clock module */
- #define RSEL3 0
- #define FIRST_STEP 0x800
- #endif
-
- enum
- {
- DCOX = DCO2 + DCO1 + DCO0,
- MODX = MOD4 + MOD3 + MOD2 + MOD1 + MOD0,
- RSELX = RSEL3 + RSEL2 + RSEL1 + RSEL0,
- ACLK_CALIB_PERIOD = 8,
- TARGET_DCO_DELTA = (TARGET_DCO_KHZ / ACLK_KHZ) * ACLK_CALIB_PERIOD,
- };
-
-
- command void Msp430ClockInit.defaultSetupDcoCalibrate()
- {
-
- // --- setup ---
-
- TACTL = TASSEL1 | MC1; // source SMCLK, continuous mode, everything else 0
- TBCTL = TBSSEL0 | MC1;
- BCSCTL1 = XT2OFF | RSEL2;
- BCSCTL2 = 0;
- TBCCTL0 = CM0;
- }
-
- command void Msp430ClockInit.defaultInitClocks()
- {
- const unsigned int divider = TARGET_DCO_KHZ / 1000;
-
- // BCSCTL1
- // .XT2OFF = 1; disable the external oscillator for SCLK and MCLK
- // .XTS = 0; set low frequency mode for LXFT1
- // .DIVA = 0; set the divisor on ACLK to 1
- // .RSEL, do not modify
- BCSCTL1 = XT2OFF | (BCSCTL1 & RSELX);
-
- // BCSCTL2
- // .SELM = 0; select DCOCLK as source for MCLK
- // .DIVM = 0; set the divisor of MCLK to 1
- // .SELS = 0; select DCOCLK as source for SCLK
- // .DIVS = see below
- // .DCOR = 0; select internal resistor for DCO
- //
- // TinyOS upper layers assume SMCLK runs at 1 binary MHz, or 1,048,576HZ.
- // If DCOCLK has been set to 1, 2, 4 or 8 binary MHz, we can correctly set
- // SMCLK to the expected value. Platforms using different clocks should
- // set the divider by overriding Msp430ClockInit.initClocks(), calling
- // Msp430ClockInit.defaultInitClocks(), then massaging the DIVS bits as
- // required.
- if (divider >= 8)
- BCSCTL2 = DIVS_3;
- else if (divider >= 4)
- BCSCTL2 = DIVS_2;
- else if (divider >= 2)
- BCSCTL2 = DIVS_1;
- else
- BCSCTL2 = DIVS_0;
-
-
- // IE1.OFIE = 0; no interrupt for oscillator fault
- CLR_FLAG( IE1, OFIE );
- }
-
- command void Msp430ClockInit.defaultInitTimerA()
- {
- TAR = 0;
-
- // TACTL
- // .TACLGRP = 0; each TACL group latched independently
- // .CNTL = 0; 16-bit counter
- // .TASSEL = 2; source SMCLK = DCO/4
- // .ID = 0; input divisor of 1
- // .MC = 0; initially disabled
- // .TACLR = 0; reset timer A
- // .TAIE = 1; enable timer A interrupts
- TACTL = TASSEL1 | TAIE;
- }
-
- command void Msp430ClockInit.defaultInitTimerB()
- {
- TBR = 0;
-
- // TBCTL
- // .TBCLGRP = 0; each TBCL group latched independently
- // .CNTL = 0; 16-bit counter
- // .TBSSEL = 1; source ACLK
- // .ID = 0; input divisor of 1
- // .MC = 0; initially disabled
- // .TBCLR = 0; reset timer B
- // .TBIE = 1; enable timer B interrupts
- TBCTL = TBSSEL0 | TBIE;
- }
-
- default event void Msp430ClockInit.setupDcoCalibrate()
- {
- call Msp430ClockInit.defaultSetupDcoCalibrate();
- }
-
- default event void Msp430ClockInit.initClocks()
- {
- call Msp430ClockInit.defaultInitClocks();
- }
-
- default event void Msp430ClockInit.initTimerA()
- {
- call Msp430ClockInit.defaultInitTimerA();
- }
-
- default event void Msp430ClockInit.initTimerB()
- {
- call Msp430ClockInit.defaultInitTimerB();
- }
-
-
- void startTimerA()
- {
- // TACTL.MC = 2; continuous mode
- TACTL = MC1 | (TACTL & ~(MC1|MC0));
- }
-
- void stopTimerA()
- {
- //TACTL.MC = 0; stop timer B
- TACTL = TACTL & ~(MC1|MC0);
- }
-
- void startTimerB()
- {
- // TBCTL.MC = 2; continuous mode
- TBCTL = MC1 | (TBCTL & ~(MC1|MC0));
- }
-
- void stopTimerB()
- {
- //TBCTL.MC = 0; stop timer B
- TBCTL = TBCTL & ~(MC1|MC0);
- }
-
- void set_dco_calib( int calib )
- {
- BCSCTL1 = (BCSCTL1 & ~RSELX) | ((calib >> 8) & RSELX);
- DCOCTL = calib & 0xff;
- }
-
- uint16_t test_calib_busywait_delta( int calib )
- {
- int8_t aclk_count = 2;
- uint16_t dco_prev = 0;
- uint16_t dco_curr = 0;
-
- set_dco_calib( calib );
-
- while( aclk_count-- > 0 )
- {
- TBCCR0 = TBR + ACLK_CALIB_PERIOD; // set next interrupt
- TBCCTL0 &= ~CCIFG; // clear pending interrupt
- while( (TBCCTL0 & CCIFG) == 0 ); // busy wait
- dco_prev = dco_curr;
- dco_curr = TAR;
- }
-
- return dco_curr - dco_prev;
- }
-
- // busyCalibrateDCO
- // Should take about 9ms if ACLK_CALIB_PERIOD=8.
- // DCOCTL and BCSCTL1 are calibrated when done.
- void busyCalibrateDco()
- {
- // --- variables ---
- int calib;
- int step;
-
- // --- calibrate ---
-
- // Binary search for RSEL,DCO,DCOMOD.
- // It's okay that RSEL isn't monotonic.
-
- for( calib=0,step=FIRST_STEP; step!=0; step>>=1 )
- {
- // if the step is not past the target, commit it
- if( test_calib_busywait_delta(calib|step) <= TARGET_DCO_DELTA )
- calib |= step;
- }
-
- // if DCOx is all 1s in calib, then MODx is not useable, set it to 0
- if( (calib & DCOX) == DCOX )
- calib &= ~MODX;
-
- set_dco_calib( calib );
- }
-
- command error_t Init.init()
- {
- // Reset timers and clear interrupt vectors
- TACTL = TACLR;
- TAIV = 0;
- TBCTL = TBCLR;
- TBIV = 0;
-
- atomic
- {
- signal Msp430ClockInit.setupDcoCalibrate();
- busyCalibrateDco();
- signal Msp430ClockInit.initClocks();
- signal Msp430ClockInit.initTimerA();
- signal Msp430ClockInit.initTimerB();
- startTimerA();
- startTimerB();
- }
-
- return SUCCESS;
- }
-}
-