diff options
author | Cástor Muñoz <cmvidal@gmail.com> | 2016-02-04 22:49:01 +0100 |
---|---|---|
committer | Cástor Muñoz <cmvidal@gmail.com> | 2016-05-25 10:59:31 +0200 |
commit | 1aefd9ea4146ebb7eee606be4efb5cf22654b082 (patch) | |
tree | ffbe9f88c2e0624faf93419c5bc9bd63963766ec /firmware/target | |
parent | c31fcddd985a9855ece85f4209a4bdae77f3f9c8 (diff) |
iPod Classic: HW preliminary initialization for bootloader
When the bootloader starts, most of HW never has been initialized.
This patch includes all code needed to perform the preliminary
initialization on SYSCON, GPIO, i2c, and MIU.
The code is based on emCORE and OF reverse engineering, ported to
C for readability.
Change-Id: I9ecf2c3e8b1b636241a211dbba8735137accd05c
Diffstat (limited to 'firmware/target')
-rw-r--r-- | firmware/target/arm/s5l8702/clocking-s5l8702.c | 121 | ||||
-rw-r--r-- | firmware/target/arm/s5l8702/clocking-s5l8702.h | 17 | ||||
-rw-r--r-- | firmware/target/arm/s5l8702/gpio-s5l8702.c | 19 | ||||
-rw-r--r-- | firmware/target/arm/s5l8702/gpio-s5l8702.h | 4 | ||||
-rw-r--r-- | firmware/target/arm/s5l8702/i2c-s5l8702.c | 28 | ||||
-rw-r--r-- | firmware/target/arm/s5l8702/ipod6g/pmu-ipod6g.c | 109 | ||||
-rw-r--r-- | firmware/target/arm/s5l8702/ipod6g/pmu-target.h | 9 | ||||
-rw-r--r-- | firmware/target/arm/s5l8702/system-s5l8702.c | 121 | ||||
-rw-r--r-- | firmware/target/arm/s5l8702/system-target.h | 4 |
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 */ |