1
0
mirror of git://projects.qi-hardware.com/f32xbase.git synced 2024-11-18 01:44:03 +02:00
f32xbase/fw/boot/boot.c

246 lines
4.6 KiB
C
Raw Normal View History

/*
* boot/boot.c - Boot loader setup and main loop
*
* Written 2008, 2010 by Werner Almesberger
* Copyright 2008, 2010 Werner Almesberger
*
* 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 2 of the License, or
* (at your option) any later version.
*/
/*
* This code follows the register read/write sequences from the examples in
* SiLabs/MCU/Examples/C8051F326_7/USB_Interrupt/Firmware/F326_USB_Main.c and
* SiLabs/MCU/Examples/C8051F326_7/USB_Interrupt/Firmware/F326_USB_ISR.c
*/
#include <stdint.h>
#include "version.h"
#include "regs.h"
#include "uart.h"
#include "usb.h"
#include "dfu.h"
#include "config.h"
#if !defined(CONFIG_DEBUG) && !defined(CONFIG_ERROR) && !defined(CONFIG_PRINTK)
#define uart_init(x)
#endif
/*
* GTA example:
*
#define PLATFORM_SETUP \
GPIOCN |= WEAKPUD; \
I2C_SDA_PULL = 0; \
delay();
// Re-enable pull-ups
// Don't waste power in pull-down
#define PLATFORM_EXIT \
GPIOCN &= ~WEAKPUD; \
I2C_SDA_PULL = 1;
#define PLATFORM_TEST \
(!I2C_SDA || dfu.state != dfuIDLE)
*/
#ifndef PLATFORM_SETUP
#define PLATFORM_SETUP
#endif
#ifndef PLATFORM_EXIT
#define PLATFORM_EXIT
#endif
void run_payload(void)
{
PLATFORM_EXIT;
/* No interrupts while jumping between worlds */
EA = 0;
/* Restart USB */
USB0XCN = 0;
debug("launching payload\n");
__asm
ljmp PAYLOAD_START
__endasm;
}
/* ----- Interrupts -------------------------------------------------------- */
/*
* The boot loader doesn't use interrupts, so we forward all interrupts to the
* payload.
*
* What we'd really like to do here is to say something like
*
* void whatever_isr(void) __interrupt(n) __at(PAYLOAD+n*8+1);
*
* However, sdcc doesn't support such things yet. So we declare the ISR such
* that the vector entry gets created, and then we tell the assembler where to
* find it.
*
* Since __asm/__endasm isn't allowed outside a function body, we generate a
* dummy function for each assignment. The function is "naked", so that no
* actual code is generated for it.
*/
#define ISR(n) \
void isr_nr_##n(void) __interrupt(n); \
void isr_dummy_##n(void) __naked \
{ \
__asm \
_isr_nr_##n = PAYLOAD_START+n*8+3 \
__endasm; \
}
ISR(0)
ISR(1)
ISR(2)
ISR(3)
ISR(4)
ISR(8)
ISR(15)
/* ----- The actual boot loader -------------------------------------------- */
static void delay(void)
{
int x;
for (x = 0; x < 500; x)
x++;
}
static void boot_loader(void)
{
/*
* If we have VBUS, proceed as follows:
* - bring up USB
* - try to contact the PMU (in a loop)
* - possible transitions:
* - DFU gets selected -> enter DFU mode
* - PMU responds -> jump to payload
*
* In DFU mode, the following transitions are possible:
* - VBUS drops -> reset
* - USB bus reset -> reset
*
* @@@ this may be too complex - probably don't really need to talk to
* the PMU.
*/
/*
* Note: if we do anything that delays CPU bringup after nRESET goes
* high, we must still stay in the 100ms budget for raising KEEPACT.
*/
OSCICN |= IFCN0 | IFCN1;
#ifdef LOW_SPEED
CLKSEL = 0x10; /* USBCLK = int/2, SYS_INT_OSC = int */
#else /* LOW_SPEED */
/*
* Clock multiplier enable sequence, section 10.4
*
* - reset the multiplier
* - select the multiplier input source
* - enable the multiplier
* - delay for 5us
* - initialize the multiplier
* - poll for multiplier to be ready
*/
CLKMUL = 0;
CLKMUL |= MULEN;
delay();
CLKMUL |= MULINIT | MULEN;
while (!(CLKMUL & MULRDY));
CLKSEL = 0; /* USBCLK = 4*int, SYSCLK = int */
CLKSEL = 0x02; /* F326_USB_Main.c does this (sets 24MHz). Why ? */
uart_init(24);
#endif /* !LOW_SPEED */
printk("%s #%u\n", build_date, build_number);
PLATFORM_SETUP;
dfu_init();
usb_init();
#ifdef PLATFORM_TEST
while (PLATFORM_TEST)
usb_poll();
#else /* PLATFORM_TEST */
#define MS_TO_LOOPS(ms) ((uint32_t) (ms)*190)
{
uint32_t loop = 0;
while (loop != MS_TO_LOOPS(2000)) {
usb_poll();
if (dfu.state == dfuIDLE)
loop++;
else
loop = 0;
}
}
#endif /* !PLATFORM_TEST */
}
void main(void)
{
/*
* VDD monitor enable sequence, section 7.2
*
* - enable voltage monitor
* - wait for monitor to stabilize
* - enable VDD monitor reset
*/
VDM0CN = VDMEN;
while (!(VDM0CN & VDDSTAT));
RSTSRC = PORSF;
/*
* @@@ if we don't have VBUS, proceed as follows:
* - stay at 3MHz (current < 2mA, so we're fine with GPIO power)
* - jump directly to the payload
*/
OSCICN |= IFCN0;
uart_init(3);
if (REG0CN & VBSTAT)
boot_loader();
run_payload();
}