/*
* 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 .
*/
#include
#include
#include
#include
#include "def.h"
#include "hardware.h"
static void InitPorts(void);
static void GlobalInit(void);
volatile u16 Timer1;
volatile u16 SW1State;
volatile u16 SW2State;
enum state {
STOP = 10,
WAIT_VREF = 15,
WAIT_START = 20,
WAIT_ATX_OK = 30,
WAIT_V1P0 = 40,
WAIT_V1P2 = 50,
WAIT_V1P8 = 60,
WAIT_RSMRST = 70,
CK410_VTT_GD = 80,
CPU_RUN = 90,
WAIT_STOP = 100,
};
static u16 state;
#ifdef CAN_WAIT_TENSION
static void InitADC10(void);
static void start_conversion(void);
static void get_conversion_result(void);
# define TENSION_EXPIRED (Timer1 == 0)
# define TENSION_WAIT(t) (tension_ok[t])
enum tensions {
VCC3,
V1P8_PGOOD,
V1P0,
V1P2,
V1P8_DDR,
V2P5,
TEMP,
TENSIONS_NUM
};
static const u16 inch[] = {
[VCC3] = INCH_5,
[V1P8_PGOOD] = INCH_6,
[V1P0] = INCH_12,
[V1P2] = INCH_13,
[V1P8_DDR] = INCH_14,
[V2P5] = INCH_15,
[TEMP] = INCH_10,
};
static unsigned cidx;
static bool tension_ok[TENSIONS_NUM];
# ifdef MONITOR_TENSIONS
static bool monitoring[TENSIONS_NUM];
# define init_monitoring() memset(monitoring, 0, sizeof(monitoring))
# define start_monitoring(t) (monitoring[t] = true)
# define check_tension(t, min, max) \
case t: \
tension_ok[t] = ADC10MEM < min || ADC10MEM > max; \
if (monitoring[t] && !tension_ok[t]) \
state = STOP; \
break;
# else
# define init_monitoring()
# define start_monitoring(t)
# define check_tension(t, min, max) \
case t: \
tension_ok[t] = ADC10MEM < min || ADC10MEM > max; \
break;
# endif
static void InitADC10(void)
{
// Ports are already initiated with correct values
// ADC10SHT <- what kind of conversion time do we need?
ADC10CTL0 = SREF_1 | ADC10SHT_2 | REFON | REF2_5V | ADC10ON | ADC10IE;
// wait 30ms here
ADC10AE0 = BIT5 | BIT6;
ADC10AE1 = BIT4 | BIT5 | BIT6 | BIT7; // A12 A13 A14 A15
}
static void start_conversion(void)
{
ADC10CTL1 = ADC10CTL1 & ~INCH_15 | inch[cidx];
ADC10CTL0 |= ENC | ADC10SC;
}
static void get_conversion_result(void)
{
switch (cidx) {
/* See the ADC10 notes and InfosPowerSeq.ods for these values */
#ifdef BRIDGE_5_PERCENT
check_tension(V1P0 , 180, 233)
check_tension(V1P2 , 209, 288)
check_tension(V1P8_DDR, 314, 432)
check_tension(V2P5 , 435, 600)
check_tension(VCC3 , 575, 792)
#else
check_tension(V1P0 , 187, 224)
check_tension(V1P2 , 218, 277)
check_tension(V1P8_DDR, 327, 415)
check_tension(V2P5 , 454, 577)
check_tension(VCC3 , 599, 762)
#endif
case V1P8_PGOOD:
tension_ok[V1P8_PGOOD] = ADC10MEM > 368;
break;
case TEMP:
break;
}
cidx = (cidx + 1) % ARRAY_SIZE(inch);
}
#else
# define InitADC10()
# define start_conversion()
# define get_conversion_result()
# define init_monitoring()
# define start_monitoring(t)
# define TENSION_EXPIRED (0)
# define TENSION_WAIT(t) (Timer1 == 0)
#endif
int main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer to prevent time out reset
__disable_interrupt();
InitPorts();
GlobalInit();
InitADC10();
__enable_interrupt();
Timer1 = 30; // to allow Vref to settle
state = WAIT_VREF;
while (1) {
switch (state) {
case WAIT_VREF:
if (Timer1 == 0) {
start_conversion();
state = WAIT_START;
}
break;
case WAIT_START:
if (SW1State > 30)
state = WAIT_START + 1;
break;
case WAIT_START + 1:
if (SW1State == 0) {
SetBit(P4OUT, CMDPWR); // Start Atx Power Supply
Timer1 = 2000;
state = WAIT_ATX_OK;
}
break;
case WAIT_ATX_OK:
if (SW1State || TENSION_EXPIRED)
state = STOP;
if ((P4IN & ATX_PWROK) && TENSION_WAIT(V2P5) && TENSION_WAIT(VCC3)) {
start_monitoring(V2P5);
start_monitoring(VCC3);
ClrBit(P1OUT, V1P2_CORE_EN_N);
Timer1 = 30;
state = WAIT_V1P2;
}
break;
case WAIT_V1P2:
if (SW1State || TENSION_EXPIRED)
state = STOP;
if (TENSION_WAIT(V1P2)) {
start_monitoring(V1P2);
Timer1 = 30;
SetBit(P4OUT, V1P8_CMD);
state = WAIT_RSMRST;
}
break;
case WAIT_RSMRST:
if (SW1State)
state = STOP;
if (Timer1 < 20) {
SetBit(P2DIR, IMCH_RSMRST_N);
ClrBit(P2OUT, IMCH_RSMRST_N);
state = WAIT_V1P8;
}
break;
case WAIT_V1P8:
if (SW1State || TENSION_EXPIRED)
state = STOP;
if (TENSION_WAIT(V1P8_DDR) && TENSION_WAIT(V1P8_PGOOD)) {
start_monitoring(V1P8_DDR);
start_monitoring(V1P8_PGOOD);
ClrBit(P2OUT, CPU_VCCP_EN_N);
Timer1 = 30;
state = WAIT_V1P0;
}
break;
case WAIT_V1P0:
if (SW1State || TENSION_EXPIRED)
state = STOP;
if (TENSION_WAIT(V1P0)) {
start_monitoring(V1P0);
SetBit(P3OUT, VRMPWRGD);
ClrBit(P2OUT, GREEN_LED_N);
Timer1 = 3;
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 = 300;
state = CPU_RUN;
}
break;
case CPU_RUN:
if (SW1State)
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;
state = CPU_RUN + 1;
}
break;
case CPU_RUN + 1:
if (SW1State)
state = STOP;
if (Timer1 == 0) {
ClrBit(P2DIR, IMCH_PWRBTN_N);
state = WAIT_STOP;
}
break;
case WAIT_STOP:
if (SW1State >= 6000) // Sw1 button pressed for more than 6 secondes
state = STOP;
break;
case STOP:
InitPorts();
Timer1 = 10000; // Disable any other Power up for 10s.
// We are very careful at first but we know no reason why this should
// not work with an extremely lower timer. Under some conditions ATX
// imposes debouncing up to at least 100 ms. I guess a good compromise
// would be somewhere between 0.5 to 1s.
ClrBit(P2OUT, RED_LED_N); // To show no restart is possible for now
state = STOP + 1;
break;
case STOP + 1:
InitPorts();
if (Timer1 == 0) {
SetBit(P2OUT, RED_LED_N); // To show restart is possible now
state = WAIT_START;
}
break;
}
}
}
#pragma vector = TIMERA1_VECTOR
__interrupt void Timer_A(void)
{
if (!(P1IN & START_SW1_N))
SW1State++;
else
SW1State = 0;
if (!(P1IN & RST_SW2_N))
SW2State++;
else
SW2State = 0;
if (Timer1)
Timer1--;
}
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;
}
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR(void)
{
get_conversion_result();
start_conversion();
}
static void GlobalInit(void)
{
DCOCTL = CALDCO_12MHZ;
BCSCTL1 = CALBC1_12MHZ;
BCSCTL2 |= DIVS_3; // DIVS_3 => 1/8; SMCLK = 12/8 = 1.5Mhz
// Set timer A so that Timer_A is called every 1.00266 ms (nominal)
TACTL = TASSEL_2 + ID_3 + TACLR + TAIE + MC_1; // SMCLK + divise 8 + reset +
// enable intterupt + UP
// 188/187500.0 = 0.001002666666666666
TACCR0 = 188;
}
// vim: et:sw=2:sts=2