//*****************************************************************************
//
// bl_autobaud.c - Automatic baud rate detection code.
//
// Copyright (c) 2006-2020 Texas Instruments Incorporated.  All rights reserved.
// Software License Agreement
// 
// Texas Instruments (TI) is supplying this software for use solely and
// exclusively on TI's microcontroller products. The software is owned by
// TI and/or its suppliers, and is protected under applicable copyright
// laws. You may not combine this software with "viral" open-source
// software in order to form a larger program.
// 
// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES, FOR ANY REASON WHATSOEVER.
// 
// This is part of revision 2.2.0.295 of the Tiva Firmware Development Package.
//
//*****************************************************************************

#include <stdint.h>
#include "inc/hw_gpio.h"
#include "inc/hw_memmap.h"
#include "inc/hw_nvic.h"
#include "inc/hw_types.h"
#include "bl_config.h"
#include "boot_loader/bl_uart.h"

//*****************************************************************************
//
// If using auto-baud, make sure that the data buffer is large enough.
//
//*****************************************************************************
#if defined(UART_ENABLE_UPDATE) && defined(UART_AUTOBAUD) && (BUFFER_SIZE < 20)
#error ERROR: BUFFER_SIZE must be >= 20!
#endif

//*****************************************************************************
//
//! \addtogroup bl_autobaud_api
//! @{
//
//*****************************************************************************
#if defined(UART_ENABLE_UPDATE) && defined(UART_AUTOBAUD) || defined(DOXYGEN)

//*****************************************************************************
//
// This define holds the multiplier for the pulse detection algorithm.  The
// value is used to generate a fractional difference detection of
// 1 / PULSE_DETECTION_MULT.
//
//*****************************************************************************
#define PULSE_DETECTION_MULT    3

//*****************************************************************************
//
// This define holds the minimum number of edges to successfully sync to a
// pattern of 2 bytes.
//
//*****************************************************************************
#define MIN_EDGE_COUNT          18

//*****************************************************************************
//
// This global holds the number of edges that have been stored in the global
// buffer g_pui32DataBuffer.
//
//*****************************************************************************
static volatile uint32_t g_ui32TickIndex;

//*****************************************************************************
//
// The data buffer that is used for receiving packets is used to hold the edge
// times during auto-baud.  The buffer is not used for receiving packets while
// auto-baud is in progress, so this does not present problems.
//
//*****************************************************************************
extern uint32_t g_pui32DataBuffer[];

//*****************************************************************************
//
//! Handles the UART Rx GPIO interrupt.
//!
//! When an edge is detected on the UART Rx pin, this function is called to
//! save the time of the edge.  These times are later used to determine the
//! ratio of the UART baud rate to the processor clock rate.
//!
//! \return None.
//
//*****************************************************************************
void
GPIOIntHandler(void)
{
    uint32_t ui32Temp;

    //
    // Clear the GPIO interrupt source.
    //
    HWREG(GPIO_PORTA_BASE + GPIO_O_ICR) = UART_RX;

    //
    // While we still have space in our buffer, store the current system tick
    // count and return from interrupt.
    //
    if(g_ui32TickIndex < 20)
    {
        ui32Temp = HWREG(NVIC_ST_CURRENT);
        g_pui32DataBuffer[g_ui32TickIndex++] = ui32Temp;
    }
}

//*****************************************************************************
//
//! Performs auto-baud on the UART port.
//!
//! \param pui32Ratio is the ratio of the processor's crystal frequency to the
//! baud rate being used by the UART port for communications.
//!
//! This function attempts to synchronize to the updater program that is trying
//! to communicate with the boot loader.  The UART port is monitored for edges
//! using interrupts.  Once enough edges are detected, the boot loader
//! determines the ratio of baud rate and crystal frequency needed to program
//! the UART.  To accomplish this, two data packets of 0x55 should be
//! transmitted on the UART lines to generate a sufficient number of edges for
//! the function to detect the baud rate.
//!
//! \return Returns a value of 0 to indicate that this call successfully
//! synchronized with the other device communicating over the UART, and a
//! negative value to indicate that this function did not successfully
//! synchronize with the other UART device.
//
//*****************************************************************************
int
UARTAutoBaud(uint32_t *pui32Ratio)
{
    int32_t i32Pulse, i32ValidPulses, i32Temp, i32Total;
    volatile int32_t i32Delay;

    //
    // Configure and enable SysTick.  Set the reload value to the maximum;
    // there are only 24 bits in the register but loading 32 bits of ones is
    // more efficient.
    //
    HWREG(NVIC_ST_RELOAD) = 0xffffffff;
    HWREG(NVIC_ST_CTRL) = NVIC_ST_CTRL_CLK_SRC | NVIC_ST_CTRL_ENABLE;

    //
    // Reset the counters that control the pulse detection.
    //
    i32ValidPulses = 0;
    i32Total = 0;
    g_ui32TickIndex = 0;

    //
    // Set the pad(s) for standard push-pull operation.
    //
    HWREG(GPIO_PORTA_BASE + GPIO_O_PUR) |= UART_RX;
    HWREG(GPIO_PORTA_BASE + GPIO_O_DEN) |= UART_RX;

    //
    // Interrupt on both edges.
    //
    HWREG(GPIO_PORTA_BASE + GPIO_O_IBE) = UART_RX;

    //
    // Clear out all of the gpio interrupts in this register.
    //
    HWREG(GPIO_PORTA_BASE + GPIO_O_ICR) = UART_RX;

    //
    // Enable the GPIO pin corresponding to the UART RX pin.
    //
    HWREG(GPIO_PORTA_BASE + GPIO_O_IM) = UART_RX;

    //
    // Enable GPIOA Interrupt.
    //
    HWREG(NVIC_EN0) = 1;

    //
    // Wait for MIN_EDGE_COUNT to pass to collect enough edges.
    //
    while(g_ui32TickIndex < MIN_EDGE_COUNT)
    {
    }

    //
    // Disable GPIOA Interrupt.
    //
    HWREG(NVIC_DIS0) = 1;

    //
    // Calculate the pulse widths from the array of tick times.
    //
    for(i32Pulse = 0; i32Pulse < (MIN_EDGE_COUNT - 1); i32Pulse++)
    {
        i32Temp = (((int32_t)g_pui32DataBuffer[i32Pulse] -
                    (int32_t)g_pui32DataBuffer[i32Pulse + 1]) & 0x00ffffff);
        g_pui32DataBuffer[i32Pulse] = i32Temp;
    }

    //
    // This loops handles checking for consecutive pulses that have pulse
    // widths that are within an acceptable margin.
    //
    for(i32Pulse = 0; i32Pulse < (MIN_EDGE_COUNT - 1); i32Pulse++)
    {
        //
        // Calculate the absolute difference between two consecutive pulses.
        //
        i32Temp = (int32_t)g_pui32DataBuffer[i32Pulse];
        i32Temp -= (int32_t)g_pui32DataBuffer[i32Pulse + 1];
        if(i32Temp < 0)
        {
            i32Temp *= -1;
        }

        //
        // This pulse detection code uses the following algorithm:
        // If the following is true then we have consecutive acceptable pulses
        // abs(Pulse[n] - Pulse[n + 1]) < Pulse[n + 1] / PULSE_DETECTION_MULT
        // or
        // PULSE_DETECTION_MULT * abs(Pulse[n] - Pulse[n + 1]) < Pulse[n + 1]
        //
        if((i32Temp * PULSE_DETECTION_MULT) <
           (int32_t)g_pui32DataBuffer[i32Pulse + 1])
        {
            i32Total += (int32_t)g_pui32DataBuffer[i32Pulse];
            i32ValidPulses++;
        }
        else
        {
            i32ValidPulses = 0;
            i32Total = 0;
        }

        //
        // Once we have 7 pulses calculate the ratio needed to program the
        // UART.
        //
        if(i32ValidPulses == 7)
        {
            //
            // Add in the last pulse and calculate the ratio.
            //
            i32Total += (int32_t)g_pui32DataBuffer[i32Pulse];
            *pui32Ratio = i32Total >> 1;

            //
            // Wait for at least 2 UART clocks since we only wait for 18 of 20
            // that are coming from the host.  If we don't wait, we can turn
            // on the UART while the last two pulses come down.
            //
            for(i32Delay = i32Total; i32Delay; i32Delay--)
            {
            }

            //
            // Indicate a successful auto baud operation.
            //
            return(0);
        }
    }

    //
    // Automatic baud rate detection failed.
    //
    return(-1);
}

//*****************************************************************************
//
// Close the Doxygen group.
//! @}
//
//*****************************************************************************
#endif
