vos/ambiq-hal-sys/ambiq-sparkfun-sdk/bootloader/am_ios_boot_handlers.c

652 lines
21 KiB
C
Raw Normal View History

2022-10-24 06:45:43 +00:00
//*****************************************************************************
//
//! @file am_ios_boot_handlers.c
//!
//! @brief Boot related functions for the IOS interface.
//!
//! This file contains the main state machine for handling boot commands via
//! the I2C interface.
//
//*****************************************************************************
//*****************************************************************************
//
// Copyright (c) 2020, Ambiq Micro
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. 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.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// Third party software included in this distribution is subject to the
// additional license terms as defined in the /docs/licenses directory.
//
// 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 HOLDER 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.
//
// This is part of revision 2.4.2 of the AmbiqSuite Development Package.
//
//*****************************************************************************
#include "am_mcu_apollo.h"
#include "am_bsp.h"
#include "am_util.h"
#include "am_multi_boot_private.h"
//*****************************************************************************
//
// SPI Slave Configuration
//
//*****************************************************************************
static am_hal_ios_config_t g_sIOSConfig =
{
// Configure the IOS in SPI mode.
.ui32InterfaceSelect = AM_HAL_IOS_USE_SPI,
// Eliminate the "read-only" section, so an external host can use the
// entire "direct write" section.
.ui32ROBase = 0x78,
// Set the FIFO base to the maximum value, making the "direct write"
// section as big as possible.
.ui32FIFOBase = 0x78,
// We don't need any RAM space, so extend the FIFO all the way to the end
// of the LRAM.
.ui32RAMBase = 0x100,
};
static uint32_t g_iosIntPin;
static volatile bool g_bIosImageValid = false;
#if defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P)
void *g_IOSHandle;
#define TEST_ACC_INT 1
//*****************************************************************************
//
// GPIO Configuration
//
//*****************************************************************************
const am_hal_gpio_pincfg_t g_AM_BOOT_GPIO_IOS_SCL =
{
.uFuncSel = AM_HAL_PIN_0_SLSCL,
.eDriveStrength = AM_HAL_GPIO_PIN_DRIVESTRENGTH_12MA,
.eGPInput = AM_HAL_GPIO_PIN_INPUT_ENABLE
};
const am_hal_gpio_pincfg_t g_AM_BOOT_GPIO_IOS_SDA =
{
.uFuncSel = AM_HAL_PIN_1_SLSDAWIR3,
.ePullup = AM_HAL_GPIO_PIN_PULLUP_1_5K,
.eDriveStrength = AM_HAL_GPIO_PIN_DRIVESTRENGTH_12MA,
.eGPOutcfg = AM_HAL_GPIO_PIN_OUTCFG_OPENDRAIN
};
const am_hal_gpio_pincfg_t g_AM_BOOT_GPIO_IOS_SCK =
{
.uFuncSel = AM_HAL_PIN_0_SLSCK,
.eDriveStrength = AM_HAL_GPIO_PIN_DRIVESTRENGTH_12MA,
.eGPInput = AM_HAL_GPIO_PIN_INPUT_ENABLE
};
const am_hal_gpio_pincfg_t g_AM_BOOT_GPIO_IOS_CE =
{
.uFuncSel = AM_HAL_PIN_3_SLnCE,
.eDriveStrength = AM_HAL_GPIO_PIN_DRIVESTRENGTH_12MA,
.eGPInput = AM_HAL_GPIO_PIN_INPUT_ENABLE,
.uNCE = 0,
.eCEpol = AM_HAL_GPIO_PIN_CEPOL_ACTIVELOW
};
const am_hal_gpio_pincfg_t g_AM_BOOT_GPIO_IOS_MISO =
{
.uFuncSel = AM_HAL_PIN_2_SLMISO
};
const am_hal_gpio_pincfg_t g_AM_BOOT_GPIO_IOS_MOSI =
{
.uFuncSel = AM_HAL_PIN_1_SLMOSI,
.eDriveStrength = AM_HAL_GPIO_PIN_DRIVESTRENGTH_12MA,
.eGPInput = AM_HAL_GPIO_PIN_INPUT_ENABLE
};
const am_hal_gpio_pincfg_t g_AM_HAL_GPIO_INPUT_ENABLE =
{
.uFuncSel = AM_HAL_PIN_0_GPIO,
.eGPInput = AM_HAL_GPIO_PIN_INPUT_ENABLE,
};
const am_hal_gpio_pincfg_t g_AM_BOOT_GPIO_ENABLE =
{
.uFuncSel = 3,
.eGPOutcfg = AM_HAL_GPIO_PIN_OUTCFG_PUSHPULL
};
const am_hal_gpio_pincfg_t g_AM_BOOT_GPIO_DISABLE =
{
.uFuncSel = 3,
.eGPOutcfg = AM_HAL_GPIO_PIN_OUTCFG_DISABLE,
};
#endif
//*****************************************************************************
//
//! @brief Multiboot protocol handler for IOS implemented in IOS-ACC ISR
//!
//! This function should be invoked from respective IOS-ACC ISR
//!
//! @return none.
//
//*****************************************************************************
void
am_multiboot_ios_acc_isr_handler(void)
{
uint32_t ui32Status;
uint32_t *pui32Packet;
//
// Check to see what caused this interrupt, then clear the bit from the
// interrupt register.
//
#if defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P)
am_hal_ios_control(g_IOSHandle, AM_HAL_IOS_REQ_ACC_INTGET, &ui32Status);
am_hal_ios_control(g_IOSHandle, AM_HAL_IOS_REQ_ACC_INTCLR, &ui32Status);
#else
ui32Status = am_hal_ios_access_int_status_get(false);
am_hal_ios_access_int_clear(ui32Status);
#endif
//
// Set up a pointer for writing 32-bit aligned packets through the IO slave
// interface.
//
pui32Packet = (uint32_t *) am_hal_ios_pui8LRAM;
if ( ui32Status & AM_HAL_IOS_ACCESS_INT_03 )
{
#if defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P)
am_hal_gpio_state_write(g_iosIntPin, AM_HAL_GPIO_OUTPUT_SET);
#else
am_hal_gpio_out_bit_set(g_iosIntPin);
#endif
//
// Figure out what to do next based on the packet header.
//
switch(pui32Packet[0])
{
case AM_BOOTLOADER_NEW_IMAGE:
//
// Parse the image packet, and store the result to the global
// image structure.
//
g_bIosImageValid = image_start_packet_read(&g_sImage,
(uint32_t *) am_hal_ios_pui8LRAM);
//
// Make sure the image packet had reasonable contents. If it
// didn't, we need to let the host know.
//
if ( g_bIosImageValid )
{
//
// Good image; Send back a "READY" packet.
//
pui32Packet[0] = AM_BOOTLOADER_READY;
#if defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P)
am_hal_gpio_state_write(g_iosIntPin, AM_HAL_GPIO_OUTPUT_CLEAR);
#else
am_hal_gpio_out_bit_clear(g_iosIntPin);
#endif
}
else
{
//
// Bad image; Send back an error.
//
pui32Packet[0] = AM_BOOTLOADER_ERROR;
#if defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P)
am_hal_gpio_state_write(g_iosIntPin, AM_HAL_GPIO_OUTPUT_CLEAR);
#else
am_hal_gpio_out_bit_clear(g_iosIntPin);
#endif
}
break;
case AM_BOOTLOADER_SET_OVERRIDE_CMD:
//
// Set the override GPIO settings based on the packet
// information.
//
g_sImage.ui32OverrideGPIO = pui32Packet[1];
g_sImage.ui32OverridePolarity = pui32Packet[2];
//
// Send back a "READY" packet.
//
pui32Packet[0] = AM_BOOTLOADER_READY;
#if defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P)
am_hal_gpio_state_write(g_iosIntPin, AM_HAL_GPIO_OUTPUT_CLEAR);
#else
am_hal_gpio_out_bit_clear(g_iosIntPin);
#endif
break;
case AM_BOOTLOADER_NEW_PACKET:
//
// Only take new packets if our image structure is valid.
//
if ( !g_bIosImageValid )
{
pui32Packet[0] = AM_BOOTLOADER_ERROR;
#if defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P)
am_hal_gpio_state_write(g_iosIntPin, AM_HAL_GPIO_OUTPUT_CLEAR);
#else
am_hal_gpio_out_bit_clear(g_iosIntPin);
#endif
break;
}
//
// Get the size information from the packet header, and set the src pointer
// to the beginning of the actual data.
//
//
// Parse the reset of the packet sitting in the IOS LRAM.
//
image_data_packet_read((uint8_t *)(am_hal_ios_pui8LRAM + 8),
*((uint32_t *) (am_hal_ios_pui8LRAM + 4)));
//
// If this packet completed the image...
//
if ( g_ui32BytesReceived == g_sImage.ui32NumBytes )
{
#ifdef MULTIBOOT_SECURE
if ( (g_ui32CRC != g_sImage.ui32CRC) || multiboot_secure_verify(&g_sImage.ui32CRC) )
{
pui32Packet[0] = AM_BOOTLOADER_BAD_CRC;
}
#else
if ( g_ui32CRC != g_sImage.ui32CRC )
{
pui32Packet[0] = AM_BOOTLOADER_BAD_CRC;
}
#endif
else
{
// Protect (and optionally write if stored in SRAM)
// image in flash now as it has been validated now
program_image(g_sImage.bEncrypted);
// Validate the flash contents of a boot image to make
// sure it's safe to run
if ( am_bootloader_flash_check(&g_sImage) )
{
pui32Packet[0] = AM_BOOTLOADER_IMAGE_COMPLETE;
}
else
{
pui32Packet[0] = AM_BOOTLOADER_ERROR;
}
}
}
else
{
//
// If this wasn't the end of the image, just send back a
// "READY" packet.
//
pui32Packet[0] = AM_BOOTLOADER_READY;
}
//
// Assert the interrupt line so the host knows we have a
// message.
//
#if defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P)
am_hal_gpio_state_write(g_iosIntPin, AM_HAL_GPIO_OUTPUT_CLEAR);
#else
am_hal_gpio_out_bit_clear(g_iosIntPin);
#endif
break;
case AM_BOOTLOADER_RESET:
if ( USE_FLAG_PAGE )
{
//
// Write the flag page.
//
am_bootloader_flag_page_update(&g_sImage, (uint32_t *)FLAG_PAGE_LOCATION);
}
#ifdef MULTIBOOT_SECURE
wipe_sram();
#endif
case AM_BOOTLOADER_RESTART:
//
// Perform a software reset.
//
#if AM_APOLLO3_RESET
am_hal_reset_control(AM_HAL_RESET_CONTROL_SWPOI, 0);
#else // AM_APOLLO3_RESET
am_hal_reset_poi();
#endif // AM_APOLLO3_RESET
//
// Wait for the reset to take effect.
//
while (1);
case AM_BOOTLOADER_BL_VERSION_CMD:
//
// Respond with the version number.
//
pui32Packet[0] = AM_BOOTLOADER_BL_VERSION;
pui32Packet[1] = AM_BOOTLOADER_VERSION_NUM;
#if defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P)
am_hal_gpio_state_write(g_iosIntPin, AM_HAL_GPIO_OUTPUT_CLEAR);
#else
am_hal_gpio_out_bit_clear(g_iosIntPin);
#endif
break;
case AM_BOOTLOADER_ACK_CMD:
case AM_BOOTLOADER_NAK_CMD:
break;
default:
// Error
pui32Packet[0] = AM_BOOTLOADER_ERROR;
#if defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P)
am_hal_gpio_state_write(g_iosIntPin, AM_HAL_GPIO_OUTPUT_CLEAR);
#else
am_hal_gpio_out_bit_clear(g_iosIntPin);
#endif
break;
}
}
}
//*****************************************************************************
//
//! @brief Configure the IOS for IOS boot
//!
//! @param interruptPin is the handshake pin to use for interrupting host.
//!
//! This function configures the IOS for bootloader
//!
//! @return none
//
//*****************************************************************************
void
am_multiboot_setup_ios_interface(uint32_t interruptPin)
{
#if defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P)
uint32_t ui32Arg = 0;
//
// Check pin 0 to see if we should be using SPI or I2C
//
am_hal_gpio_pinconfig(0, g_AM_HAL_GPIO_INPUT_ENABLE);
am_hal_gpio_state_read(0, AM_HAL_GPIO_INPUT_READ, &ui32Arg);
if ( ui32Arg )
{
//
// If the clock line is high, we'll assume I2C.
//
am_hal_gpio_pinconfig(0, g_AM_BOOT_GPIO_IOS_SCL);
am_hal_gpio_pinconfig(1, g_AM_BOOT_GPIO_IOS_SDA);
g_sIOSConfig.ui32InterfaceSelect = (AM_HAL_IOS_USE_I2C |
AM_HAL_IOS_I2C_ADDRESS(I2C_SLAVE_ADDR << 1));
}
else
{
//
// If the clock line is low, we'll assume SPI.
//
am_hal_gpio_pinconfig(0, g_AM_BOOT_GPIO_IOS_SCK);
am_hal_gpio_pinconfig(1, g_AM_BOOT_GPIO_IOS_MISO);
am_hal_gpio_pinconfig(2, g_AM_BOOT_GPIO_IOS_MOSI);
am_hal_gpio_pinconfig(3, g_AM_BOOT_GPIO_IOS_CE);
g_sIOSConfig.ui32InterfaceSelect = AM_HAL_IOS_USE_SPI;
}
//
// Configure the IOS interface and LRAM structure.
//
am_hal_ios_initialize(0, &g_IOSHandle);
am_hal_ios_power_ctrl(g_IOSHandle, AM_HAL_SYSCTRL_WAKE, false);
am_hal_ios_configure(g_IOSHandle, &g_sIOSConfig);
g_iosIntPin = interruptPin;
//
// Make sure the interrupt pin is set up correctly
//
am_hal_gpio_state_write(g_iosIntPin, AM_HAL_GPIO_OUTPUT_SET);
am_hal_gpio_pinconfig(g_iosIntPin, g_AM_BOOT_GPIO_ENABLE);
//
// Clear out any IOS register-access interrupts that may be active, and
// enable interrupts for the registers we're interested in.
//
#ifdef TEST_ACC_INT
ui32Arg = AM_HAL_IOS_ACCESS_INT_ALL;
am_hal_ios_control(g_IOSHandle, AM_HAL_IOS_REQ_ACC_INTCLR, &ui32Arg);
ui32Arg = AM_HAL_IOS_ACCESS_INT_03;
am_hal_ios_control(g_IOSHandle, AM_HAL_IOS_REQ_ACC_INTEN, &ui32Arg);
#endif
am_hal_ios_interrupt_clear(g_IOSHandle, AM_HAL_IOS_INT_ALL);
am_hal_ios_interrupt_enable(g_IOSHandle, AM_HAL_IOS_INT_FSIZE);
//
// Set the bit in the NVIC to accept access interrupts from the IO Slave.
//
#if AM_CMSIS_REGS
NVIC_EnableIRQ(IOSLAVEACC_IRQn);
// NVIC_EnableIRQ(IOSLAVE_IRQn);
#else // AM_CMSIS_REGS
am_hal_interrupt_enable(AM_HAL_INTERRUPT_IOSACC);
// am_hal_interrupt_enable(AM_HAL_INTERRUPT_IOSLAVE);
#endif // AM_CMSIS_REGS
//
// Notify the host that we're ready to receive data.
//
*((uint32_t *) am_hal_ios_pui8LRAM) = AM_BOOTLOADER_READY;
am_hal_gpio_state_write(g_iosIntPin, AM_HAL_GPIO_OUTPUT_CLEAR);
#else
//
// Check pin 0 to see if we should be using SPI or I2C
//
am_hal_gpio_pin_config(0, AM_HAL_GPIO_INPUT);
if ( am_hal_gpio_input_bit_read(0) )
{
//
// If the clock line is high, we'll assume I2C.
//
am_hal_gpio_pin_config(0, AM_HAL_PIN_0_SLSCL);
am_hal_gpio_pin_config(1, AM_HAL_PIN_1_SLSDA);
g_sIOSConfig.ui32InterfaceSelect = (AM_HAL_IOS_USE_I2C |
AM_HAL_IOS_I2C_ADDRESS(I2C_SLAVE_ADDR << 1));
}
else
{
//
// If the clock line is low, we'll assume SPI.
//
am_hal_gpio_pin_config(0, AM_HAL_PIN_0_SLSCK);
am_hal_gpio_pin_config(1, AM_HAL_PIN_1_SLMISO);
am_hal_gpio_pin_config(2, AM_HAL_PIN_2_SLMOSI);
am_hal_gpio_pin_config(3, AM_HAL_PIN_3_SLnCE);
g_sIOSConfig.ui32InterfaceSelect = AM_HAL_IOS_USE_SPI;
}
//
// Configure the IOS interface and LRAM structure.
//
am_hal_ios_config(&g_sIOSConfig);
g_iosIntPin = interruptPin;
//
// Make sure the interrupt pin is set up correctly
//
am_hal_gpio_out_bit_set(g_iosIntPin);
am_hal_gpio_pin_config(g_iosIntPin, AM_HAL_PIN_OUTPUT);
//
// Clear out any IOS register-access interrupts that may be active, and
// enable interrupts for the registers we're interested in.
//
am_hal_ios_access_int_clear(AM_HAL_IOS_ACCESS_INT_ALL);
am_hal_ios_access_int_enable(AM_HAL_IOS_ACCESS_INT_03);
am_hal_ios_int_clear(AM_HAL_IOS_INT_ALL);
am_hal_ios_int_enable(AM_HAL_IOS_INT_FSIZE);
//
// Set the bit in the NVIC to accept access interrupts from the IO Slave.
//
am_hal_interrupt_enable(AM_HAL_INTERRUPT_IOSACC);
// am_hal_interrupt_enable(AM_HAL_INTERRUPT_IOSLAVE);
//
// Notify the host that we're ready to receive data.
//
*((uint32_t *) am_hal_ios_pui8LRAM) = AM_BOOTLOADER_READY;
am_hal_gpio_out_bit_clear(g_iosIntPin);
#endif
}
//*****************************************************************************
//
//! @brief Cleanup the IOS
//!
//! This function un-configures the IOS
//!
//! @return none
//
//*****************************************************************************
void
am_multiboot_cleanup_ios_interface(void)
{
#if defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P)
am_hal_gpio_pinconfig(0, g_AM_BOOT_GPIO_DISABLE);
am_hal_gpio_pinconfig(1, g_AM_BOOT_GPIO_DISABLE);
am_hal_gpio_pinconfig(2, g_AM_BOOT_GPIO_DISABLE);
am_hal_gpio_pinconfig(3, g_AM_BOOT_GPIO_DISABLE);
#else
am_hal_gpio_pin_config(0, AM_HAL_PIN_DISABLE);
am_hal_gpio_pin_config(1, AM_HAL_PIN_DISABLE);
am_hal_gpio_pin_config(2, AM_HAL_PIN_DISABLE);
am_hal_gpio_pin_config(3, AM_HAL_PIN_DISABLE);
#endif
}
#if 0
//*****************************************************************************
//
// IO Slave Main ISR.
//
//*****************************************************************************
void
am_ioslave_ios_isr(void)
{
uint32_t ui32Status;
#if defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P)
//
// Check to see what caused this interrupt, then clear the bit from the
// interrupt register.
//
am_hal_ios_interrupt_status_get(g_IOSHandle, false, &ui32Status);
am_hal_ios_interrupt_clear(g_IOSHandle, ui32Status);
//
// Service the I2C slave FIFO if necessary.
//
am_hal_ios_interrupt_service(g_IOSHandle, ui32Status);
#else
//
// Check to see what caused this interrupt, then clear the bit from the
// interrupt register.
//
ui32Status = am_hal_ios_int_status_get(false);
am_hal_ios_int_clear(ui32Status);
//
// Service the I2C slave FIFO if necessary.
//
am_hal_ios_fifo_service(ui32Status);
#endif
}
//*****************************************************************************
//
// IO Slave handshake
//
//*****************************************************************************
bool
am_ioslave_handshake(uint32_t ui32Timeout)
{
uint32_t i;
uint32_t *pui32Packet;
bool bReturnVal = false;
//
// Set up a pointer for writing 32-bit aligned packets through the IO slave
// interface.
//
pui32Packet = (uint32_t *) am_hal_ios_pui8LRAM;
#if defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P)
am_hal_gpio_state_write(g_iosIntPin, AM_HAL_GPIO_OUTPUT_SET);
#else
am_hal_gpio_out_bit_set(g_iosIntPin);
#endif
for ( i = 0; i < ui32Timeout; i++ )
{
if ( pui32Packet[0] == 0x5A )
{
bReturnVal = true;
//
// Notify the host that we're ready to receive data.
//
*((uint32_t *) am_hal_ios_pui8LRAM) = AM_BOOTLOADER_READY;
#if defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P)
am_hal_gpio_state_write(g_iosIntPin, AM_HAL_GPIO_OUTPUT_CLEAR);
#else
am_hal_gpio_out_bit_clear(g_iosIntPin);
#endif
break;
}
am_util_delay_us(1);
}
return bReturnVal;
}
#endif