/* * 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 #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_ENTER #define PLATFORM_ENTER #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; /* * The USB host must detect a disconnect (pull-ups absent) within 2 and * 2.5 us according to parameter Tddis in table 7-13 on page 186 of the * Universal Serial Bus Specification Revision 2.0. * * If our application calls usb_init really really quickly, we may need * an extra delay here. */ 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) { OSCICN |= IFCN0 | IFCN1; /* SYSCLK = IOSC/1 (12 MHz) */ #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_ENTER; 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) { /* * Any early platform setup, such as turning off the watch dog. */ PLATFORM_SETUP; /* * 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; /* SYSCLK = IOSC/4 (3 MHz) */ uart_init(3); if (REG0CN & VBSTAT) boot_loader(); run_payload(); }