/*
* XIOHV5.12 power sequence
* Copyright (C) 2012 Avencall
* Authors:
* Jean-Marc Ouvrard
* Noe Rubinstein
* Guillaume Knispel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
/* Random notes:
*
* We have decided to remove the reset button and to keep the power button.
*
* We should make sure that the power button works more reliably especially
* considering the ACPI specification and interactions with the EP80579
* standard ACPI subsystem. We should also debounce the On -> Off transition
* of the power button.
*/
/* define WATCHDOG to get one :) */
#define WATCHDOG
//#undef WATCHDOG
/* define TRACE_SERIAL to get debugging traces capabilities on the
* MSP430 serial port
* WARNING: This might disable some functions too, check the code
* to see exactly what that does in this particular version */
// #define TRACE_SERIAL
#undef TRACE_SERIAL
/* define LOOP_REBOOT if you want that the board reboot forever
* define it to the number of ms to spend in the WAIT_STOP state.
*/
//#define LOOP_REBOOT 5000
#undef LOOP_REBOOT
#ifdef TRACE_SERIAL
#include
#endif /* TRACE_SERIAL */
#include
#include
#include
#include "def.h"
#include "hardware.h"
static void InitPorts(void);
static void GlobalInit(void);
volatile u16 TAVector;
volatile u16 Timer1;
volatile u16 Timer2;
volatile u16 SW1State;
volatile u16 SW2State;
#ifdef CAN_WAIT_TENSION
//has to be coded on real board
volatile u8 bV1P0 = true;
volatile u8 bV1P2 = true;
volatile u8 bV1P8_DDR = true;
volatile u8 bV2P5 = true;
volatile u8 bVCC3 = true;
#define TENSION_EXPIRED (Timer1 == 0)
#define TENSION_WAIT(t) (t)
#else
#define TENSION_EXPIRED (0)
#define TENSION_WAIT(t) (Timer1 == 0)
#endif
#define STOP 10
#define WAIT_START 20
#define WAIT_ATX_OK 30
#define WAIT_V1P0 40
#define WAIT_V1P2 50
#define WAIT_V1P8 60
#define WAIT_RSMRST 70
#define CK410_VTT_GD 80
#define STATE_SYS_PWR_OK 90
#define CPU_RUN 100
#define WAIT_STOP 110
#define RST_STATE 10
#define ON_STATE 20
#define RST_WAIT 30
#ifdef TRACE_SERIAL
// For UCOS16 = 1
// BRCLK Baud Rate UCBRx UCBRSx UCBRFx Tx Error Rx Error
// 12,000,000 115200 6 0 8 -1.8 0 -2.2 0.4
static void SerialInit(void)
{
UCA0CTL1_bit.UCSWRST = 1;
UCA0CTL0 = 0;
UCA0CTL1 = 0xC1; // UCSSEL=11b => SMCLK; (keep) UCSWRST = 1;
UCA0BR0 = 6;
UCA0BR1 = 0;
UCA0MCTL = 0x81; // UCOS16 = 1; UCBRSx = 0; UCBRFx = 8;
UCA0STAT = 0;
// Configure port for UART access
// Pin 25 as UCA0TXD
// Pin 26 as UCA0RXD
P3SEL |= MSP_BSL_TXD | MSP_BSL_RXD;
UCA0CTL1_bit.UCSWRST = 0;
}
static void ll_putchar(unsigned char c)
{
while (!IFG2_bit.UCA0TXIFG);
UCA0TXBUF = c;
}
/* __write, remove, __close, and __lseek need to be implemented by the
* application for the full DLib to work.
*/
size_t __write(int handle, const unsigned char * buffer, size_t size)
{
size_t nChars = 0;
if (buffer == 0)
return 0;
for (; size != 0; --size) {
ll_putchar(*buffer++);
++nChars;
}
return nChars;
}
int remove(const char *name)
{
return -1;
}
int __close(int handle)
{
return 0;
}
long __lseek(int handle, long offset, int whence)
{
return -1;
}
// TB_SIZE must be a power of 2
#define TB_SIZE 64
static u8 trace_buffer[TB_SIZE];
static u8 tb_beg;
static u8 tb_fill;
static inline u8 TB_I(u8 raw_idx)
{
return raw_idx & (TB_SIZE - 1);
}
static void trace(u8 val)
{
if (tb_fill == TB_SIZE) {
tb_beg = TB_I(tb_beg + 1);
tb_fill--;
}
trace_buffer[TB_I(tb_beg + (tb_fill++))] = val;
}
static inline char hex_nibble(u8 n)
{
return n + ((n > 9) ? 'a' - 10 : '0');
}
static inline void dump_byte(char b[3], u8 n)
{
b[0] = hex_nibble(n >> 4);
b[1] = hex_nibble(n & 0xf);
b[2] = ' ';
}
static void dump_trace(void)
{
static char buf[64];
int pos = 0;
#define flush() do { buf[pos] = 0; fputs(buf, stdout); pos = 0; } while (0)
int i;
fputs("TRACE:\r\n", stdout);
pos = 0;
for (i = 0; i < tb_fill; i++) {
dump_byte(buf + pos, trace_buffer[TB_I(tb_beg + i)]);
pos += 3;
if ((i & 15) == 15) {
buf[pos++] = '\r';
buf[pos++] = '\n';
} else if ((i & 7) == 7) {
buf[pos++] = ' ';
}
if (pos >= 56)
flush();
}
flush();
if (i & 15) puts("\r");
}
#define TRACE(v) trace(v)
#define change_state(ns) do { trace(ns); state = (ns); } while (0)
#else /* !TRACE_SERIAL */
#define TRACE(v) /* noop */
#define change_state(ns) do { state = (ns); } while (0)
#endif /* TRACE_SERIAL */
volatile u16 Timer_A_count;
int main(void)
{
u16 state, resetState;
#ifndef WATCHDOG
WDTCTL = WDTPW | WDTHOLD;
#endif /* WATCHDOG */
__disable_interrupt();
GlobalInit();
InitPorts();
#ifdef TRACE_SERIAL
SerialInit();
#endif /* TRACE_SERIAL */
__enable_interrupt();
state = WAIT_START;
resetState = ON_STATE;
while (1) {
P2OUT = (P2OUT & ~GP24) | ((Timer_A_count & 1) ? GP24 : 0);
////////////////////////////////////////////////////////////////////
#ifdef TRACE_SERIAL // debug behavior
switch (resetState) {
case ON_STATE:
if (SW2State > 100) {
dump_trace();
resetState = RST_WAIT;
}
break;
resetState = ON_STATE;
case RST_WAIT:
if (SW2State == 0)
break;
}
#else // normal behavior
switch (resetState) {
case ON_STATE:
if (SW2State > 300) {
resetState = RST_STATE;
}
break;
case RST_STATE:
ClrBit(P2REN, SYS_RESET_N);
SetBit(P2DIR, SYS_RESET_N);
ClrBit(P2OUT, SYS_RESET_N);
Timer2 = 150;
resetState = RST_WAIT;
break;
case RST_WAIT:
if (Timer2 == 0) {
SetBit(P2OUT, SYS_RESET_N);
if (SW2State == 0)
resetState = ON_STATE;
}
break;
}
#endif /* TRACE_SERIAL */
////////////////////////////////////////////////////////////////////
switch (state) {
case WAIT_START:
#ifdef LOOP_REBOOT
change_state(WAIT_START + 1);
#endif
if (SW1State > 30)
change_state(WAIT_START + 1);
break;
case WAIT_START + 1:
if (SW1State == 0) {
SetBit(P4OUT, CMDPWR); // Start Atx Power Supply
Timer1 = 2000;
change_state(WAIT_ATX_OK);
}
break;
case WAIT_ATX_OK:
if (SW1State || TENSION_EXPIRED) {
change_state(STOP);
}
if ((P4IN & ATX_PWROK) && TENSION_WAIT(bV2P5 && bVCC3)) {
ClrBit(P1OUT, V1P2_CORE_EN_N);
Timer1 = 30;
change_state(WAIT_V1P2);
}
break;
case WAIT_V1P2:
if (SW1State || TENSION_EXPIRED)
change_state(STOP);
if (TENSION_WAIT(bV1P2)) {
Timer1 = 30;
SetBit(P4OUT, V1P8_CMD);
change_state(WAIT_RSMRST);
}
break;
case WAIT_RSMRST:
if (SW1State)
change_state(STOP);
if (Timer1 < 19) {
SetBit(P2OUT, IMCH_RSMRST_N);
change_state(WAIT_V1P8);
}
break;
case WAIT_V1P8:
if (SW1State || TENSION_EXPIRED)
change_state(STOP);
if (TENSION_WAIT(bV1P8)) {
ClrBit(P2OUT, CPU_VCCP_EN_N);
Timer1 = 30;
change_state(WAIT_V1P0);
}
break;
case WAIT_V1P0:
if (SW1State || TENSION_EXPIRED)
change_state(STOP);
if (TENSION_WAIT(bV1P0)) {
SetBit(P3OUT, VRMPWRGD);
ClrBit(P2OUT, GREEN_LED_N);
Timer1 = 3;
change_state(CK410_VTT_GD);
}
break;
case CK410_VTT_GD:
if (Timer1 == 0) {
SetBit(P2DIR, CK410_PWR_GD_N);
ClrBit(P2OUT, CK410_PWR_GD_N);
Timer1 = 105;
change_state(STATE_SYS_PWR_OK);
}
break;
case STATE_SYS_PWR_OK:
if (Timer1 == 0) {
SetBit(P3OUT, SYS_PWR_OK);
Timer1 = 10;
change_state(CPU_RUN);
}
break;
case CPU_RUN:
if (SW1State)
change_state(STOP);
if (Timer1 == 0) {
// Start Tolapai: emulate the power button from its point of view
// we don't really know where is the best place to do that so
// we will try it here at first.
SetBit(P2DIR, IMCH_PWRBTN_N);
Timer1 = 200;
change_state(CPU_RUN + 1);
}
break;
case CPU_RUN + 1:
if (SW1State)
change_state(STOP);
if (Timer1 == 0) {
ClrBit(P2DIR, IMCH_PWRBTN_N);
#ifdef LOOP_REBOOT
Timer1 = LOOP_REBOOT;
#endif
change_state(WAIT_STOP);
}
break;
case WAIT_STOP:
#ifdef LOOP_REBOOT
if (Timer1 == 0)
change_state(STOP);
#endif
if (SW1State >= 4000) // Sw1 button pressed for more than 4 seconds
change_state(STOP);
break;
case STOP:
InitPorts();
Timer1 = 3000; // Disable any other Power up for 3 s.
ClrBit(P2OUT, RED_LED_N); // To show no restart is possible for now
change_state(STOP + 1);
break;
case STOP + 1:
if ((Timer1 == 0) && (SW1State == 0)) {
SetBit(P2OUT, RED_LED_N); // To show restart is possible now
change_state(WAIT_START);
}
break;
}
}
}
#pragma vector = TIMERA1_VECTOR
__interrupt void Timer_A(void)
{
#ifdef WATCHDOG
/* ACLK (VLO) /64 => T belongs to [0.016; 0.0032] s */
WDTCTL = WDTPW | WDTCNTCL | WDTSSEL | WDTIS1 | WDTIS0;
P3OUT = P3IN ^ MSP_BSL_TXD;
/* if (IFG2_bit.UCA0TXIFG)
UCA0TXBUF = 'a'; */
#endif /* WATCHDOG */
Timer_A_count++;
if (!(P1IN & START_SW1_N))
SW1State++;
else
SW1State = 0;
if (!(P1IN & RST_SW2_N))
SW2State++;
else
SW2State = 0;
if (Timer1)
Timer1--;
if (Timer2)
Timer2--;
TAVector = TAIV;
}
static void InitPorts(void)
{
/* DIR: direction: 0 input 1 output
* SEL: function: 0 gpio
* REN: resistor enabled
* OUT: output when REN=0 and DIR=1
* 0 pull-down 1 pull-up if REN=1
* IES: Interrupt Edge Select
* IE: Interrupt Enable
*/
P1DIR = P1DIR_INIT;
P1SEL = P1SEL_INIT;
P1REN = P1REN_INIT;
P1OUT = P1OUT_INIT;
P1IES = P1IES_INIT;
P1IE = P1IE_INIT;
P2OUT = P2OUT_INIT;
P2SEL = P2SEL_INIT;
P2REN = P2REN_INIT;
P2DIR = P2DIR_INIT;
P2IES = P2IES_INIT;
P2IE = P2IE_INIT;
P3OUT = P3OUT_INIT;
P3SEL = P3SEL_INIT;
P3DIR = P3DIR_INIT;
P4OUT = P4OUT_INIT;
P4SEL = P4SEL_INIT;
P4DIR = P4DIR_INIT;
P4REN = P4REN_INIT;
}
static void GlobalInit(void)
{
/******** MCLK SMCLK ACLK Configuration ********/
/* freq selection procedure compliant with errata BCL12
* (potentially switching from RSEL < 12 to > 13)
*/
DCOCTL = 0;
/* LFXT1 low freq (to allow for VLO), ACLK will be /1 */
BCSCTL1 = CALBC1_12MHZ & ~(XTS | DIVA0 | DIVA1);
DCOCTL = CALDCO_12MHZ;
BCSCTL3 = LFXT1S_2 /* VLO */;
/* Status here:
* MCLK and SMCLK from DCO, 12MHz nominal.
* ACLK from VLOCLK, 4 -> 20kHz.
*/
#ifdef WATCHDOG
/******** Watchdog early configuration ********/
/* ACLK /8192 */
WDTCTL = WDTPW + WDTCNTCL + WDTSSEL + WDTIS0;
/* This initial WDT will expire in 0.4 to 2.1 s
* Note that it will be reconfigured to expire with shorter intervals
* starting from the next clearing.
*/
#endif /* WATCHDOG */
/******** Timer A configuration ********/
TACTL = TASSEL_2 | ID_3 | TACLR | TAIE | MC_1; // SMCLK | div by 8 | reset |
// enable interrupt | UP
// Calibrated DCO Frequencies - Tolerance Over Temperature 0C to 85C
//
// Min Typ Max
// BCSCTL1 = CALBC1_12MHZ, 2.2 V 11.7 12 12.3
// fCAL(12MHz) DCOCTL = CALDCO_12MHZ, 3 V 11.7 12 12.3 MHz
// Gating time: 5 ms 3.6 V 11.7 12 12.3
// Timer_A called every:
//
// >>> 1539 / 1500000. * .975
// 0.00100035 1.000 ms min
// >>> 1539 / 1500000.
// 0.001026 1.026 ms nom
// >>> 1539 / 1500000. * 1.025
// 0.00105165 1.052 ms max
TACCR0 = 1539 + 1; /* TA16 errata => + 1 => so T belongs to [1.000 ; 1.053] */
}