/* --COPYRIGHT--,BSD
 * Copyright (c) 2012, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * --/COPYRIGHT--*/
#include <stdint.h>
#include <msp430.h>

#include "bsl.h"

#define TIMEOUT_COUNT   1000000
// Calculate this timeout around ~400ms-500ms due to mass erase
#define RETRY_COUNT     100000
#define NUM_RETRIES     50

#define CS_LOW()      {P1OUT &= ~BIT5;__delay_cycles(200);}
#define CS_HIGH()     {__delay_cycles(200);P1OUT |= BIT5;__delay_cycles(200);}
#define DUMMY_BYTE      0x90

static void BSL_writebyte(uint8_t data);
static int8_t BSL_transferbyte(uint8_t data, uint8_t *response);

void BSL_Comm_Init(void)
{
      /* Initializing SPI Module */
    P1SEL |= (BIT1 | BIT2 | BIT4);      // Enable P1[1:2,4] for USCI_A0 SPI mode
    P1SEL2 |= (BIT1 | BIT2 | BIT4);     // P1.1=MISO, P1.2=MOSI, P1.4=CLK
    
    P1OUT |= BIT5;                      // Enable P1.5 as CS
    P1DIR |= BIT5;
    
    
    UCA0CTL0 |= UCMSB + UCCKPL + UCMST + UCSYNC;   // 3-pin, 8-bit SPI master
    UCA0CTL1 |= UCSSEL_2;                           // SMCLK = 8Mhz
    UCA0BR0 = 32;                                  // /16 = 250Khz
    UCA0BR1 = 0;                                    //
    UCA0MCTL = 0;                                   // No modulation
    UCA0CTL1 &= ~UCSWRST;                           // **Initialize USCI state machine**
}

static int8_t BSL_transferbyte(uint8_t data, uint8_t *response)
{
    uint32_t timeout = TIMEOUT_COUNT;

    BSL_writebyte(data);

    // Get the response
    // SPI doesn't really need a timeout since the slave responds always
    while ( (!(IFG2 & UCA0RXIFG)) && (timeout-- > 0))
        ;
    
    *response = UCA0RXBUF;
    
    if (!timeout)
        return -1;
    else
        return 1;
}


static void BSL_writebyte(uint8_t data)
{
    while (!(IFG2 & UCA0TXIFG));    // USCI_A0 TX buffer ready?
    UCA0TXBUF = data;               // start transfer
}

static uint8_t BSL_getResponsefromSlave(uint8_t retries)
{
    uint8_t response; 
    int8_t ret;
    uint8_t count=0;
    
    // In SPI, the slave responds always, so the host needs to poll the slave
    // until it has finished processing the command
    do {
        BSL_flush();
        CS_LOW();                      
        // Send a dummy byte to get the response from target
        ret = BSL_transferbyte(DUMMY_BYTE, &response);
        // wait for packet transmission
        while (UCA0STAT & UCBUSY)
          ;
        CS_HIGH();
        
        if (ret < 0)
        {
            return 0xFF;
        }
        else
        {
            // A response of 0xC0 means that slave is busy receiving packet
            // 0xC1 means that slave is busy processing packet
            if ((response != 0xC0) && (response != 0xC1))
            {
                //if response if not 0xC0 or 0xC1, return immediately, 
                // otherwise, retry a few times
                return response;
            }
            
        }
        // delay for retry
        __delay_cycles(RETRY_COUNT);
    } while (count++ < retries);
    
    return response;
}

/* Reads one byte from BSL target */
uint8_t BSL_getResponse(void)
{
    // For BSL packets, retry 10 times (due to mass erase delay)
    return (BSL_getResponsefromSlave(NUM_RETRIES));
}

/* Checks if a slave is responding */
uint8_t BSL_slavePresent(void)
{
    // Only try to read once from slave
    if (BSL_getResponsefromSlave(0) == 0xFF)
        return 0;
    else
        return 1;
}

/* Sends single I2C Byte (start and stop included) */
void BSL_sendSingleByte(uint8_t ui8Byte)
{
    BSL_flush();
    CS_LOW();              
    BSL_writebyte(ui8Byte);
    // wait for packet transmission
    while (UCA0STAT & UCBUSY)
      ;
    CS_HIGH();
}

/* Sends application image to BSL target (start and stop included)
 * This is a polling function and is blocking. */
void BSL_sendPacket(tBSLPacket tPacket)
{
    uint16_t ii;
    volatile uint8_t crch, crcl;

    BSL_flush();
    CS_LOW();                      
    BSL_writebyte(0x80);
    BSL_writebyte(tPacket.ui8Length);
    BSL_writebyte(tPacket.tPayload.ui8Command);

    if(tPacket.ui8Length > 1)
    {
        BSL_writebyte(tPacket.tPayload.ui8Addr_L);
        BSL_writebyte(tPacket.tPayload.ui8Addr_H);
        for (ii=0; ii < (tPacket.ui8Length-3); ii++)
        {
            BSL_writebyte(tPacket.tPayload.ui8pData[ii]);
        }
    }

    crcl = (uint8_t)(tPacket.ui16Checksum & 0xFF);
    BSL_writebyte(crcl);

    crch = (uint8_t)(tPacket.ui16Checksum >> 8);
    BSL_writebyte(crch);
    
    // wait for packet transmission
    while (UCA0STAT & UCBUSY)
      ;

    CS_HIGH();
    
}

void BSL_flush(void)
{
  IFG2 &= ~(UCA0RXIFG);
}
