/* * 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