summaryrefslogtreecommitdiff
path: root/firmware/target/arm
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm')
-rw-r--r--firmware/target/arm/s5l8702/clocking-s5l8702.c121
-rw-r--r--firmware/target/arm/s5l8702/clocking-s5l8702.h17
-rw-r--r--firmware/target/arm/s5l8702/gpio-s5l8702.c19
-rw-r--r--firmware/target/arm/s5l8702/gpio-s5l8702.h4
-rw-r--r--firmware/target/arm/s5l8702/i2c-s5l8702.c28
-rw-r--r--firmware/target/arm/s5l8702/ipod6g/pmu-ipod6g.c109
-rw-r--r--firmware/target/arm/s5l8702/ipod6g/pmu-target.h9
-rw-r--r--firmware/target/arm/s5l8702/system-s5l8702.c121
-rw-r--r--firmware/target/arm/s5l8702/system-target.h4
9 files changed, 428 insertions, 4 deletions
diff --git a/firmware/target/arm/s5l8702/clocking-s5l8702.c b/firmware/target/arm/s5l8702/clocking-s5l8702.c
index 5293385453..3ef70ba1de 100644
--- a/firmware/target/arm/s5l8702/clocking-s5l8702.c
+++ b/firmware/target/arm/s5l8702/clocking-s5l8702.c
@@ -213,6 +213,125 @@ void set_clocking_level(int level)
udelay(50); /* TBC: probably not needed */
}
+#ifdef BOOTLOADER
+int pll_config(int pll, int op_mode, int p, int m, int s, int lock_time)
+{
+ PLLPMS(pll) = ((p & PLLPMS_PDIV_MSK) << PLLPMS_PDIV_POS)
+ | ((m & PLLPMS_MDIV_MSK) << PLLPMS_MDIV_POS)
+ | ((s & PLLPMS_SDIV_MSK) << PLLPMS_SDIV_POS);
+
+ /* lock_time are PClk ticks */
+ PLLCNT(pll) = lock_time & PLLCNT_MSK;
+
+ if (pll < 2)
+ {
+ if (op_mode == PLLOP_MM) {
+ PLLMODE &= ~PLLMODE_PMSMOD_BIT(pll); /* MM */
+ return 0;
+ }
+
+ PLLMODE |= PLLMODE_PMSMOD_BIT(pll);
+
+ if (op_mode == PLLOP_DM) {
+ PLLMOD2 &= ~PLLMOD2_DMOSC_BIT(pll); /* DM */
+ return 0;
+ }
+
+ PLLMOD2 |= PLLMOD2_DMOSC_BIT(pll);
+ }
+ else
+ {
+ if (op_mode == PLLOP_MM)
+ return -1; /* PLL2 does not support MM */
+
+ if (op_mode == PLLOP_DM) {
+ PLLMODE &= ~PLLMODE_PLL2DMOSC_BIT; /* DM */
+ return 0;
+ }
+
+ PLLMODE |= PLLMODE_PLL2DMOSC_BIT;
+ }
+
+ /* ALTOSCx */
+ PLLMOD2 = (PLLMOD2 & ~PLLMOD2_ALTOSC_BIT(pll)) |
+ ((op_mode == PLLOP_ALT0) ? 0 : PLLMOD2_ALTOSC_BIT(pll));
+
+ return 0;
+}
+
+int pll_onoff(int pll, bool onoff)
+{
+ if (onoff)
+ {
+ PLLMODE |= PLLMODE_EN_BIT(pll); /* start PLL */
+ while (!(PLLLOCK & PLLLOCK_LCK_BIT(pll))); /* locking... */
+ PLLMODE |= PLLMODE_PLLOUT_BIT(pll); /* slow mode OFF */
+
+ /* returns DMLCK status, only meaningful for Divisor Mode (DM) */
+ return (PLLLOCK & PLLLOCK_DMLCK_BIT(pll)) ? 1 : 0;
+ }
+ else
+ {
+ PLLMODE &= ~PLLMODE_PLLOUT_BIT(pll); /* slow mode ON */
+ udelay(50); /* TBC: needed when current F_in is 0 Hz */
+ PLLMODE &= ~PLLMODE_EN_BIT(pll); /* stop PLL */
+
+ return 0;
+ }
+}
+
+/* configure and enable/disable 16-bit clockgate */
+void cg16_config(volatile uint16_t* cg16,
+ bool onoff, int clksel, int div1, int div2)
+{
+ uint16_t val16 = ((clksel & CG16_SEL_MSK) << CG16_SEL_POS)
+ | (((div1 - 1) & CG16_DIV1_MSK) << CG16_DIV1_POS)
+ | (((div2 - 1) & CG16_DIV2_MSK) << CG16_DIV2_POS)
+ | (onoff ? 0 : CG16_DISABLE_BIT);
+
+ volatile uint32_t* reg32 = (uint32_t *)((int)cg16 & ~3);
+ int shift = ((int)cg16 & 2) << 3;
+
+ *reg32 = (*reg32 & (0xffff0000 >> shift)) | (val16 << shift);
+
+ /*udelay(100);*/ /* probably not needed */
+
+ while (*cg16 != val16);
+}
+
+void clockgate_enable(int gate, bool enable)
+{
+ int i = (gate >> 5) & 1;
+ uint32_t bit = 1 << (gate & 0x1f);
+ if (enable) PWRCON(i) &= ~bit;
+ else PWRCON(i) |= bit;
+}
+
+/* Configures EClk for USEC_TIMER. DRAM refresh also depends on EClk,
+ * this clock should be initialized by the bootloader, so USEC_TIMER
+ * is ready to use for RB.
+ */
+void usec_timer_init(void)
+{
+ /* select OSC0 for CG16 SEL_OSC */
+ PLLMODE &= ~PLLMODE_OSCSEL_BIT;
+
+ /* configure and enable ECLK */
+ cg16_config(&CG16_RTIME, true, CG16_SEL_OSC, 1, 1);
+
+ /* unmask timer controller clock gate */
+ clockgate_enable(CLOCKGATE_TIMER, true);
+
+ /* configure and start timer E */
+ TECON = (4 << 8) | /* TE_CS = ECLK / 1 */
+ (1 << 6) | /* select ECLK (12 MHz on Classic) */
+ (0 << 4); /* TE_MODE_SEL = interval mode */
+ TEPRE = (S5L8702_OSC0_HZ / 1000000) - 1; /* prescaler */
+ TEDATA0 = ~0;
+ TECMD = (1 << 1) | /* TE_CLR = initialize timer */
+ (1 << 0); /* TE_EN = enable */
+}
+
#if 0
/* - This function is mainly to documment how s5l8702 ROMBOOT and iPod
* Classic diagnostic OF detects primary external clock.
@@ -228,3 +347,5 @@ unsigned soc_get_osc0(void)
return (PDAT3 & 0x20) ? 24000000 : 12000000;
}
#endif
+
+#endif /* BOOTLOADER */
diff --git a/firmware/target/arm/s5l8702/clocking-s5l8702.h b/firmware/target/arm/s5l8702/clocking-s5l8702.h
index e7e4fab3bd..f21c25929b 100644
--- a/firmware/target/arm/s5l8702/clocking-s5l8702.h
+++ b/firmware/target/arm/s5l8702/clocking-s5l8702.h
@@ -433,4 +433,21 @@ unsigned pll_get_out_freq(int pll);
unsigned soc_get_oscsel_freq(void);
int soc_get_hsdiv(void);
+#ifdef BOOTLOADER
+#include <stdbool.h>
+
+void usec_timer_init(void);
+void clockgate_enable(int gate, bool enable);
+
+void soc_set_system_divs(unsigned cdiv, unsigned hdiv, unsigned hprat);
+unsigned soc_get_system_divs(unsigned *cdiv, unsigned *hdiv, unsigned *pdiv);
+void soc_set_hsdiv(int hsdiv);
+
+void cg16_config(volatile uint16_t* cg16,
+ bool onoff, int clksel, int div1, int div2);
+
+int pll_config(int pll, int op_mode, int p, int m, int s, int lock_time);
+int pll_onoff(int pll, bool onoff);
+#endif
+
#endif /* __CLOCKING_S5L8702_H */
diff --git a/firmware/target/arm/s5l8702/gpio-s5l8702.c b/firmware/target/arm/s5l8702/gpio-s5l8702.c
index bd87005ad8..85033de78c 100644
--- a/firmware/target/arm/s5l8702/gpio-s5l8702.c
+++ b/firmware/target/arm/s5l8702/gpio-s5l8702.c
@@ -214,3 +214,22 @@ void ICODE_ATTR INT_EXT6(void)
gpio_handler(0);
}
#endif
+
+#ifdef BOOTLOADER
+static uint32_t gpio_data[16] =
+{
+ 0x5322222F, 0xEEEEEE00, 0x2332EEEE, 0x3333E222,
+ 0x33333333, 0x33333333, 0x3F000E33, 0xEEEEEEEE,
+ 0xEEEEEEEE, 0xEEEEEEEE, 0xE0EEEEEE, 0xEE00EE0E,
+ 0xEEEE0EEE, 0xEEEEEEEE, 0xEE2222EE, 0xEEEE0EEE
+};
+
+void gpio_preinit(void)
+{
+ for (int i = 0; i < 16; i++) {
+ PCON(i) = gpio_data[i];
+ PUNB(i) = 0;
+ PUNC(i) = 0;
+ }
+}
+#endif
diff --git a/firmware/target/arm/s5l8702/gpio-s5l8702.h b/firmware/target/arm/s5l8702/gpio-s5l8702.h
index 00f5ba18f3..108d8feb0d 100644
--- a/firmware/target/arm/s5l8702/gpio-s5l8702.h
+++ b/firmware/target/arm/s5l8702/gpio-s5l8702.h
@@ -142,4 +142,8 @@ void gpio_int_disable(int gpio_n);
uint32_t gpio_group_get(int group);
void gpio_group_set(int group, uint32_t mask, uint32_t cfg);
+#ifdef BOOTLOADER
+void gpio_preinit(void);
+#endif
+
#endif /* __GPIO_S5L8702_H__ */
diff --git a/firmware/target/arm/s5l8702/i2c-s5l8702.c b/firmware/target/arm/s5l8702/i2c-s5l8702.c
index 619768410a..13787d7357 100644
--- a/firmware/target/arm/s5l8702/i2c-s5l8702.c
+++ b/firmware/target/arm/s5l8702/i2c-s5l8702.c
@@ -179,3 +179,31 @@ int i2c_read(int bus, unsigned char slave, int address, int len, unsigned char *
mutex_unlock(&i2c_mtx[bus]);
return ret;
}
+
+#ifdef BOOTLOADER
+#include "clocking-s5l8702.h"
+
+static void wait_rdy(int bus)
+{
+ while (IICUNK10(bus));
+}
+
+void i2c_preinit(int bus)
+{
+ clockgate_enable(I2CCLKGATE(bus), true);
+ wait_rdy(bus);
+ IICADD(bus) = 0x40; /* own slave address */
+ wait_rdy(bus);
+ IICUNK14(bus) = 0;
+ wait_rdy(bus);
+ IICUNK18(bus) = 0;
+ wait_rdy(bus);
+ IICSTAT(bus) = 0x80; /* master Rx mode, Tx/Rx off */
+ wait_rdy(bus);
+ IICCON(bus) = 0;
+ wait_rdy(bus);
+ IICSTAT(bus) = 0; /* slave Rx mode, Tx/Rx off */
+ wait_rdy(bus);
+ clockgate_enable(I2CCLKGATE(bus), false);
+}
+#endif
diff --git a/firmware/target/arm/s5l8702/ipod6g/pmu-ipod6g.c b/firmware/target/arm/s5l8702/ipod6g/pmu-ipod6g.c
index 2a9236046c..9c3ec8e711 100644
--- a/firmware/target/arm/s5l8702/ipod6g/pmu-ipod6g.c
+++ b/firmware/target/arm/s5l8702/ipod6g/pmu-ipod6g.c
@@ -165,4 +165,113 @@ int pmu_wr(int address, unsigned char val)
{
return pmu_wr_multiple(address, 1, &val);
}
+
+void pmu_preinit(void)
+{
+ static const char init_data[] =
+ {
+ /* reset OOC shutdown register */
+ PCF5063X_REG_OOCSHDWN, 0x0,
+
+ /* LDO_UNK1: 3000 mV, enabled */
+ PCF5063X_REG_LDO1OUT, 0x15,
+ PCF5063X_REG_LDO1ENA, 0x1,
+
+ /* LDO_UNK2: 3000 mV, enabled */
+ PCF5063X_REG_LDO2OUT, 0x15,
+ PCF5063X_REG_LDO2ENA, 0x1,
+
+ /* LDO_LCD: 3000 mV, enabled */
+ PCF5063X_REG_LDO3OUT, 0x15,
+ PCF5063X_REG_LDO3ENA, 0x1,
+
+ /* LDO_CODEC: 1800 mV, enabled */
+ PCF5063X_REG_LDO4OUT, 0x9,
+ PCF5063X_REG_LDO4ENA, 0x1,
+
+ /* LDO_UNK5: 3000 mV, disabled */
+ PCF5063X_REG_LDO5OUT, 0x15,
+ PCF5063X_REG_LDO5ENA, 0x0,
+
+ /* LDO_CWHEEL: 3000 mV, ON when GPIO2 High */
+ PCF5063X_REG_LDO6OUT, 0x15,
+ PCF5063X_REG_LDO6ENA, 0x4,
+
+ /* LDO_ACCY: 3300 mV, disabled */
+ PCF5063X_REG_HCLDOOUT, 0x18,
+ PCF5063X_REG_HCLDOENA, 0x0,
+
+ /* LDO_CWHEEL is ON in STANDBY state,
+ LDO_CWHEEL and MEMLDO are ON in UNKNOWN state (TBC) */
+ PCF5063X_REG_STBYCTL1, 0x0,
+ PCF5063X_REG_STBYCTL2, 0x8c,
+
+ /* GPIO1,2 = input, GPIO3 = output */
+ PCF5063X_REG_GPIOCTL, 0x3,
+ PCF5063X_REG_GPIO1CFG, 0x0,
+ PCF5063X_REG_GPIO2CFG, 0x0,
+
+ /* DOWN2 converter (SDRAM): 1800 mV, enabled,
+ startup current limit = 15mA*0x10 (TBC) */
+ PCF5063X_REG_DOWN2OUT, 0x2f,
+ PCF5063X_REG_DOWN2ENA, 0x1,
+ PCF5063X_REG_DOWN2CTL, 0x0,
+ PCF5063X_REG_DOWN2MXC, 0x10,
+
+ /* MEMLDO: 1800 mV, enabled */
+ PCF5063X_REG_MEMLDOOUT, 0x9,
+ PCF5063X_REG_MEMLDOENA, 0x1,
+
+ /* AUTOLDO (HDD): 3400 mV, disabled,
+ limit = 1000 mA (40mA*0x19), limit always active */
+ PCF5063X_REG_AUTOOUT, 0x6f,
+ PCF5063X_REG_AUTOENA, 0x0,
+ PCF5063X_REG_AUTOCTL, 0x0,
+ PCF5063X_REG_AUTOMXC, 0x59,
+
+ /* Vsysok = 3100 mV */
+ PCF5063X_REG_SVMCTL, 0x8,
+
+ /* Reserved */
+ 0x58, 0x0,
+
+ /* Mask all PMU interrupts */
+ PCF5063X_REG_INT1M, 0xff,
+ PCF5063X_REG_INT2M, 0xff,
+ PCF5063X_REG_INT3M, 0xff,
+ PCF5063X_REG_INT4M, 0xff,
+ PCF5063X_REG_INT5M, 0xff,
+ PCF50635_REG_INT6M, 0xff,
+
+ /* Wakeup on rising edge for EXTON1 and EXTON2,
+ wakeup on falling edge for EXTON3 and !ONKEY,
+ wakeup on RTC alarm, wakeup on adapter insert,
+ Vbat status has no effect in state machine */
+ PCF5063X_REG_OOCWAKE, 0xdf,
+ PCF5063X_REG_OOCTIM1, 0xaa,
+ PCF5063X_REG_OOCTIM2, 0x4a,
+ PCF5063X_REG_OOCMODE, 0x5,
+ PCF5063X_REG_OOCCTL, 0x27,
+
+ /* GPO selection = LED external NFET drive signal */
+ PCF5063X_REG_GPOCFG, 0x1,
+ /* LED converter OFF, overvoltage protection enabled,
+ OCP limit is 500 mA, led_dimstep = 16*0x6/32768 */
+ PCF5063X_REG_LEDENA, 0x0,
+ PCF5063X_REG_LEDCTL, 0x5,
+ PCF5063X_REG_LEDDIM, 0x6,
+
+ /* end marker */
+ 0
+ };
+
+ const char* ptr;
+ for (ptr = init_data; *ptr != 0; ptr += 2)
+ pmu_wr(ptr[0], ptr[1]);
+
+ /* clear PMU interrupts */
+ unsigned char rd_buf[5];
+ pmu_rd_multiple(PCF5063X_REG_INT1, 5, rd_buf);
+ pmu_rd(PCF50635_REG_INT6);
+}
#endif /* BOOTLOADER */
diff --git a/firmware/target/arm/s5l8702/ipod6g/pmu-target.h b/firmware/target/arm/s5l8702/ipod6g/pmu-target.h
index 3a90ad8789..e4bef6f47c 100644
--- a/firmware/target/arm/s5l8702/ipod6g/pmu-target.h
+++ b/firmware/target/arm/s5l8702/ipod6g/pmu-target.h
@@ -40,8 +40,8 @@
#define LDO_UNK5 5 /* TBC: nano3g NAND */
#define LDO_CWHEEL 6
#define LDO_ACCY 7 /* HCLDO */
-
-/* Other LDOs:
+/*
+ * Other LDOs:
* AUTOLDO: Hard Disk
* DOWN1: CPU
* DOWN2: SDRAM
@@ -50,9 +50,9 @@
* EXTON inputs:
* EXTON1: button/holdswitch related (TBC)
* EXTON2: USB Vbus (High when present)
- * EXTON3: ACCESORY (Low when present)
+ * EXTON3: ACCESSORY (Low when present)
*
- * GPIO:
+ * PMU GPIO:
* GPIO1: input, Mikey (jack remote ctrl) interrupt (TBC)
* GPIO2: input, hold switch (TBC)
* GPIO3: output, unknown
@@ -82,6 +82,7 @@ unsigned char pmu_rd(int address);
int pmu_wr(int address, unsigned char val);
int pmu_rd_multiple(int address, int count, unsigned char* buffer);
int pmu_wr_multiple(int address, int count, unsigned char* buffer);
+void pmu_preinit(void);
#endif
#endif /* __PMU_TARGET_H__ */
diff --git a/firmware/target/arm/s5l8702/system-s5l8702.c b/firmware/target/arm/s5l8702/system-s5l8702.c
index fd3a464e2f..d285efde78 100644
--- a/firmware/target/arm/s5l8702/system-s5l8702.c
+++ b/firmware/target/arm/s5l8702/system-s5l8702.c
@@ -277,3 +277,124 @@ void memory_init(void)
set_page_tables();
enable_mmu();
}
+
+#ifdef BOOTLOADER
+#include "i2c-s5l8702.h"
+
+static void syscon_preinit(void)
+{
+ /* after ROM boot, CG16_SYS is using PLL0 @108 MHz
+ CClk = 108 MHz, HClk = 54 MHz, PClk = 27 MHz */
+
+ CLKCON0 &= ~CLKCON0_SDR_DISABLE_BIT;
+
+ PLLMODE &= ~PLLMODE_OSCSEL_BIT; /* CG16_SEL_OSC = OSC0 */
+ cg16_config(&CG16_SYS, true, CG16_SEL_OSC, 1, 1);
+ soc_set_system_divs(1, 1, 1);
+
+ /* stop all PLLs */
+ for (int pll = 0; pll < 3; pll++)
+ pll_onoff(pll, false);
+
+ pll_config(2, PLLOP_DM, 1, 36, 1, 32400);
+ pll_onoff(2, true);
+ soc_set_system_divs(1, 2, 2 /*hprat*/);
+ cg16_config(&CG16_SYS, true, CG16_SEL_PLL2, 1, 1);
+ cg16_config(&CG16_2L, false, CG16_SEL_OSC, 1, 1);
+ cg16_config(&CG16_SVID, false, CG16_SEL_OSC, 1, 1);
+ cg16_config(&CG16_AUD0, false, CG16_SEL_OSC, 1, 1);
+ cg16_config(&CG16_AUD1, false, CG16_SEL_OSC, 1, 1);
+ cg16_config(&CG16_AUD2, false, CG16_SEL_OSC, 1, 1);
+ cg16_config(&CG16_RTIME, true, CG16_SEL_OSC, 1, 1);
+ cg16_config(&CG16_5L, false, CG16_SEL_OSC, 1, 1);
+
+ soc_set_hsdiv(1);
+
+ PWRCON_AHB = ~((1 << CLOCKGATE_SMx) |
+ (1 << CLOCKGATE_SM1));
+ PWRCON_APB = ~((1 << (CLOCKGATE_TIMER - 32)) |
+ (1 << (CLOCKGATE_GPIO - 32)));
+}
+
+static void miu_preinit(bool selfrefreshing)
+{
+ if (selfrefreshing)
+ MIUCON = 0x11; /* TBC: self-refresh -> IDLE */
+
+ MIUCON = 0x80D; /* remap = 1 (IRAM mapped to 0x0),
+ TBC: SDRAM bank and column configuration */
+ MIU_REG(0xF0) = 0x0;
+
+ MIUAREF = 0x6105D; /* Auto-Refresh enabled,
+ Row refresh interval = 0x5d/12MHz = 7.75 uS */
+ MIUSDPARA = 0x1FB621;
+
+ MIU_REG(0x200) = 0x1845;
+ MIU_REG(0x204) = 0x1845;
+ MIU_REG(0x210) = 0x1800;
+ MIU_REG(0x214) = 0x1800;
+ MIU_REG(0x220) = 0x1845;
+ MIU_REG(0x224) = 0x1845;
+ MIU_REG(0x230) = 0x1885;
+ MIU_REG(0x234) = 0x1885;
+ MIU_REG(0x14) = 0x19; /* 2^19 = 0x2000000 = SDRAMSIZE (32Mb) */
+ MIU_REG(0x18) = 0x19; /* 2^19 = 0x2000000 = SDRAMSIZE (32Mb) */
+ MIU_REG(0x1C) = 0x790682B;
+ MIU_REG(0x314) &= ~0x10;
+
+ for (int i = 0; i < 0x24; i++)
+ MIU_REG(0x2C + i*4) &= ~(1 << 24);
+
+ MIU_REG(0x1CC) = 0x540;
+ MIU_REG(0x1D4) |= 0x80;
+
+ MIUCOM = 0x33; /* No action CMD */
+ MIUCOM = 0x33;
+ MIUCOM = 0x233; /* Precharge all banks CMD */
+ MIUCOM = 0x33;
+ MIUCOM = 0x33;
+ MIUCOM = 0x33;
+ MIUCOM = 0x333; /* Auto-refresh CMD */
+ MIUCOM = 0x33;
+ MIUCOM = 0x33;
+ MIUCOM = 0x33;
+ MIUCOM = 0x333; /* Auto-refresh CMD */
+ MIUCOM = 0x33;
+ MIUCOM = 0x33;
+ MIUCOM = 0x33;
+
+ if (!selfrefreshing)
+ {
+ MIUMRS = 0x33; /* MRS: Bust Length = 8, CAS = 3 */
+ MIUCOM = 0x133; /* Mode Register Set CMD */
+ MIUCOM = 0x33;
+ MIUCOM = 0x33;
+ MIUCOM = 0x33;
+ MIUMRS = 0x8040; /* EMRS: Strength = 1/4, Self refresh area = Full */
+ MIUCOM = 0x133; /* Mode Register Set CMD */
+ MIUCOM = 0x33;
+ MIUCOM = 0x33;
+ MIUCOM = 0x33;
+ }
+
+ MIUAREF |= 0x61000; /* Auto-refresh enabled */
+}
+
+/* Preliminary HW initialization */
+void system_preinit(void)
+{
+ bool gpio3out, coldboot;
+
+ syscon_preinit();
+ gpio_preinit();
+ i2c_preinit(0);
+
+ /* get (previously) configured output selection for GPIO3 */
+ gpio3out = (pmu_rd(PCF5063X_REG_GPIO3CFG) & 7);
+ /* coldboot: when set, device has been in NoPower state */
+ coldboot = (pmu_rd(PCF5063X_REG_OOCSHDWN) & PCF5063X_OOCSHDWN_COLDBOOT);
+ pmu_preinit();
+
+ miu_preinit(!coldboot && !gpio3out);
+}
+#endif
diff --git a/firmware/target/arm/s5l8702/system-target.h b/firmware/target/arm/s5l8702/system-target.h
index 235e68e8ca..b40d563e46 100644
--- a/firmware/target/arm/s5l8702/system-target.h
+++ b/firmware/target/arm/s5l8702/system-target.h
@@ -47,4 +47,8 @@ static inline void udelay(unsigned usecs)
while (TIME_BEFORE(USEC_TIMER, stop));
}
+#ifdef BOOTLOADER
+void system_preinit(void);
+#endif
+
#endif /* SYSTEM_TARGET_H */