diff --git a/Makefile b/Makefile index 84d3bce..4e8bb37 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,7 @@ LIBS = SRC_C = \ bionic.c \ cmd-irq.c \ + emi.c \ irq.c \ main.c \ scriptic.c \ diff --git a/emi.c b/emi.c new file mode 100644 index 0000000..1fb4b2a --- /dev/null +++ b/emi.c @@ -0,0 +1,149 @@ +#include +#include "bionic.h" +#include "memio.h" + +#include "fernvale-emi.h" +#include "fernvale-pmic.h" + +#define PSRAM_TEST_SHOULD_INVERT (1 << 0) +#define PSRAM_TEST_SHOULD_RANDOMIZE (1 << 1) + +/* Use the built-in memory self test to check calibration */ +static int psram_test_run(uint32_t addr, uint32_t length, uint16_t pattern) +{ + int run; + uint32_t test_range; + uint32_t params; + + test_range = (addr << 8) & 0xffff0000; + test_range |= (length >> 8) - 1; + + for (run = 0; run < 4; run++) { + /* Params contains the "start key", plus some other flags */ + params = EMI_CTRL_MBISTB_START_KEY; + + /* On some runs, invert the pattern */ + if (run & PSRAM_TEST_SHOULD_INVERT) + params |= EMI_CTRL_MBISTB_INVERT; + + /* On some runs, randomize the data */ + if (run & PSRAM_TEST_SHOULD_RANDOMIZE) + params |= EMI_CTRL_MBISTB_RANDOMIZE; + + /* Reset MBIST engine */ + writel(0, EMI_CTRL_MBISTA); + + /* Set range (start and length) */ + writel(test_range, EMI_CTRL_MBISTB); + + /* Kick off the test */ + writel(params, EMI_CTRL_MBISTA); + + /* Wait for it to finish */ + while ( !(readl(EMI_CTRL_MBISTD) & EMI_CTRL_MBISTD_FINISHED) ) + ; + + /* If the test failed, return false */ + if ( readl(EMI_CTRL_MBISTD) & EMI_CTRL_MBISTD_FAILURE ) + return 0; + } + + return 1; +} + +static int psram_test(void) +{ + int i; + uint32_t test_start = 0x100000; + uint32_t test_length = 0x8000; + + uint16_t patterns[] = {0xffff, 0xa55a}; + + + for (i = 0; i < sizeof(patterns)/sizeof(*patterns); i++) + if (!psram_test_run(test_start, test_length, patterns[i])) + return 0; + + /* All tests passed */ + return 1; +} + +/* Set all 16 DQY delays to the same value */ +static void psram_set_ganged_dqy(uint8_t delay) +{ + uint32_t vals = (delay << 0) | + (delay << 8) | + (delay << 16) | + (delay << 24); + writel(vals, EMI_CTRL_IDLA); + writel(vals, EMI_CTRL_IDLB); + writel(vals, EMI_CTRL_IDLC); + writel(vals, EMI_CTRL_IDLD); +} + +static void psram_set_ganged_dqs(uint8_t delay) +{ + uint32_t vals = (delay << 24) | + (delay << 16); + writel(vals, EMI_CTRL_IDLE); +} + +int calibrate_psram(void) +{ + int dqy_delay; + int dqy_delay_upper = -1; + int dqy_delay_lower = -1; + + int dqs_delay; + int dqs_delay_upper = -1; + int dqs_delay_lower = -1; + + for (dqy_delay = 0; dqy_delay < 32; dqy_delay++) { + + /* + * Since this chip is probably routed properly, assume + * all trace lengths require the same delay. + */ + psram_set_ganged_dqy(dqy_delay); + if (psram_test()) { + if (dqy_delay_lower == -1) + dqy_delay_lower = dqy_delay; + else + dqy_delay_upper = dqy_delay; + } + } + + if (-1 == dqy_delay_upper) + dqy_delay_upper = dqy_delay_lower; + + if (dqy_delay_upper != -1) { + dqy_delay = (dqy_delay_lower + dqy_delay_upper) / 2; + psram_set_ganged_dqy(dqy_delay); + return 1; + } + + /* If DQY delays fail, try DQS instead */ + psram_set_ganged_dqy(0); + for (dqs_delay = 1; dqs_delay <= 31; dqs_delay++) { + psram_set_ganged_dqs(dqs_delay); + if (psram_test()) { + if (dqs_delay_lower == -1) + dqs_delay_lower = dqs_delay; + else + dqs_delay_upper = dqs_delay; + return 1; + } + } + + if (-1 == dqs_delay_upper) + dqs_delay_upper = dqs_delay_lower; + + if (dqs_delay_upper != -1) { + dqs_delay = (dqs_delay_upper + dqs_delay_lower) / 2; + psram_set_ganged_dqs(dqs_delay); + return 1; + } + + /* Failure */ + return 0; +} diff --git a/fernvale.ld b/fernvale.ld index 04b37d2..d22bd76 100644 --- a/fernvale.ld +++ b/fernvale.ld @@ -39,7 +39,7 @@ SECTIONS { /* The OS entry point is here */ - . = 0x70000000; /* bootloader will copy data to this address */ + . = 0x70006000; /* bootloader will copy data to this address */ .text : { _stext = ABSOLUTE(.); KEEP(*(vectors)) diff --git a/include/fernvale-emi.h b/include/fernvale-emi.h new file mode 100644 index 0000000..96340b6 --- /dev/null +++ b/include/fernvale-emi.h @@ -0,0 +1,96 @@ +#ifndef __FV_EMI_H__ +#define __FV_EMI_H__ + +/* Register names based off of: + * https://github.com/dragonpt/lenovo_A390_mt6577_uboot_kernel/blob + * /master/mediatek/custom + * /huaqin77_cu_a510_ics2/preloader/inc/mt6577_emi_reg.h +*/ + +/* Note: MT6260 has only bank 1 */ +#define EMI_CTRL_ADDR 0xa0050000 +#define EMI_CTRL_CONA (EMI_CTRL_ADDR + 0x00) /* Bank 0 control (low) */ +#define EMI_CTRL_CONB (EMI_CTRL_ADDR + 0x08) /* Bank 1 control (low) */ +#define EMI_CTRL_CONC (EMI_CTRL_ADDR + 0x10) /* Bank 2 control (low) */ +#define EMI_CTRL_COND (EMI_CTRL_ADDR + 0x18) /* Bank 3 control (low) */ +#define EMI_CTRL_CONE (EMI_CTRL_ADDR + 0x20) /* Bank 0 control (high) */ +#define EMI_CTRL_CONF (EMI_CTRL_ADDR + 0x28) /* Bank 1 control (high) */ +#define EMI_CTRL_CONG (EMI_CTRL_ADDR + 0x30) /* Bank 2 control (high) */ +#define EMI_CTRL_CONH (EMI_CTRL_ADDR + 0x38) /* Bank 3 control (high) */ +#define EMI_CTRL_CONI (EMI_CTRL_ADDR + 0x40) /* Bank 0 control for mDDR */ +#define EMI_CTRL_CONJ (EMI_CTRL_ADDR + 0x48) /* Bank 1 control for mDDR */ +#define EMI_CTRL_CONK (EMI_CTRL_ADDR + 0x50) /* Bank 2 control for mDDR */ +#define EMI_CTRL_CONL (EMI_CTRL_ADDR + 0x58) /* Bank 3 control for mDDR */ +#define EMI_CTRL_CONM (EMI_CTRL_ADDR + 0x60) /* */ +#define EMI_CTRL_GENA (EMI_CTRL_ADDR + 0x70) /* General control register 0 */ +#define EMI_CTRL_GENB (EMI_CTRL_ADDR + 0x78) /* General control register 1 */ +#define EMI_CTRL_ADMUX (EMI_CTRL_ADDR + 0x80) /* Something involving GPIO mux */ +#define EMI_CTRL_RDCT (EMI_CTRL_ADDR + 0x88) +#define EMI_CTRL_DLLV (EMI_CTRL_ADDR + 0x90) +#define EMI_CTRL_IDLA (EMI_CTRL_ADDR + 0xc0) +#define EMI_CTRL_IDLB (EMI_CTRL_ADDR + 0xc8) +#define EMI_CTRL_IDLC (EMI_CTRL_ADDR + 0xd0) +#define EMI_CTRL_IDLD (EMI_CTRL_ADDR + 0xd8) +#define EMI_CTRL_IDLE (EMI_CTRL_ADDR + 0xe0) +#define EMI_CTRL_ODLA (EMI_CTRL_ADDR + 0xe8) +#define EMI_CTRL_ODLB (EMI_CTRL_ADDR + 0xf0) +#define EMI_CTRL_ODLC (EMI_CTRL_ADDR + 0xf8) +#define EMI_CTRL_ODLD (EMI_CTRL_ADDR + 0x100) +#define EMI_CTRL_ODLE (EMI_CTRL_ADDR + 0x108) +#define EMI_CTRL_ODLF (EMI_CTRL_ADDR + 0x110) +#define EMI_CTRL_IOA (EMI_CTRL_ADDR + 0x130) +#define EMI_CTRL_IOB (EMI_CTRL_ADDR + 0x138) +#define EMI_CTRL_DSRAM (EMI_CTRL_ADDR + 0x150) +#define EMI_CTRL_ARBA (EMI_CTRL_ADDR + 0x170) +#define EMI_CTRL_ARBB (EMI_CTRL_ADDR + 0x178) +#define EMI_CTRL_ARBC (EMI_CTRL_ADDR + 0x180) +#define EMI_CTRL_SLCT (EMI_CTRL_ADDR + 0x198) +#define EMI_CTRL_ABCT (EMI_CTRL_ADDR + 0x1a0) +#define EMI_CTRL_BMEN (EMI_CTRL_ADDR + 0x200) +#define EMI_CTRL_BCNT (EMI_CTRL_ADDR + 0x208) +#define EMI_CTRL_TACT (EMI_CTRL_ADDR + 0x210) +#define EMI_CTRL_TSCT (EMI_CTRL_ADDR + 0x218) +#define EMI_CTRL_WACT (EMI_CTRL_ADDR + 0x220) +#define EMI_CTRL_WSCT (EMI_CTRL_ADDR + 0x228) +#define EMI_CTRL_BACT (EMI_CTRL_ADDR + 0x230) +#define EMI_CTRL_BSCT0 (EMI_CTRL_ADDR + 0x238) +#define EMI_CTRL_BSCT1 (EMI_CTRL_ADDR + 0x240) +#define EMI_CTRL_TTYPE1 (EMI_CTRL_ADDR + 0x280) +#define EMI_CTRL_TTYPE2 (EMI_CTRL_ADDR + 0x288) +#define EMI_CTRL_TTYPE3 (EMI_CTRL_ADDR + 0x290) +#define EMI_CTRL_TTYPE4 (EMI_CTRL_ADDR + 0x298) +#define EMI_CTRL_TTYPE5 (EMI_CTRL_ADDR + 0x2a0) +#define EMI_CTRL_TTYPE6 (EMI_CTRL_ADDR + 0x2a8) +#define EMI_CTRL_TTYPE7 (EMI_CTRL_ADDR + 0x2b0) +#define EMI_CTRL_TTYPE8 (EMI_CTRL_ADDR + 0x2b8) +#define EMI_CTRL_TTYPE9 (EMI_CTRL_ADDR + 0x2c0) +#define EMI_CTRL_TTYPE10 (EMI_CTRL_ADDR + 0x2c8) +#define EMI_CTRL_TTYPE11 (EMI_CTRL_ADDR + 0x2d0) +#define EMI_CTRL_TTYPE12 (EMI_CTRL_ADDR + 0x2d8) +#define EMI_CTRL_TTYPE13 (EMI_CTRL_ADDR + 0x2e0) +#define EMI_CTRL_TTYPE14 (EMI_CTRL_ADDR + 0x2e8) +#define EMI_CTRL_TTYPE15 (EMI_CTRL_ADDR + 0x2f0) +#define EMI_CTRL_TTYPE16 (EMI_CTRL_ADDR + 0x2f8) + +#define EMI_CTRL_MBISTA (EMI_CTRL_ADDR + 0x300) +#define EMI_CTRL_MBISTA_PATTERN_SHIFT 16 +#define EMI_CTRL_MBISTA_PATTERN_MASK 0xffff0000 + +#define EMI_CTRL_MBISTB (EMI_CTRL_ADDR + 0x308) +#define EMI_CTRL_MBISTB_START_KEY 0x325 +#define EMI_CTRL_MBISTB_RANDOMIZE (1 << 12) +#define EMI_CTRL_MBISTB_INVERT (1 << 13) + +#define EMI_CTRL_MBISTC (EMI_CTRL_ADDR + 0x310) + +#define EMI_CTRL_MBISTD (EMI_CTRL_ADDR + 0x318) +#define EMI_CTRL_MBISTD_FAILURE (1 << 0) +#define EMI_CTRL_MBISTD_FINISHED (1 << 1) + + + +#define EMI_CTRL_TEST (EMI_CTRL_ADDR + 0x330) + +#define EMI_CTRL_REMAP (0xA0510000) + +#endif /* __FV_EMI_H__ */ diff --git a/include/fernvale-pll.h b/include/fernvale-pll.h new file mode 100644 index 0000000..753ccd6 --- /dev/null +++ b/include/fernvale-pll.h @@ -0,0 +1,72 @@ +#ifndef __FV_PLL_H__ +#define __FV_PLL_H__ + +/* Register names and values adapted from: + * https://github.com/wiko-sources/cink-slim/blob + * /master/mediatek/platform/mt6577 + * /kernel/core/include/mach/mt6577_clock_manager.h + */ + +#define PLL_CTRL_ADDR 0xa0170000 +#define PLL_CLK_ADDR 0x80000100 + +#define PLL_CTRL_XOSC_CON0 (PLL_CTRL_ADDR + 0x00) +#define PLL_CTRL_XOSC_CON1 (PLL_CTRL_ADDR + 0x04) + +#define PLL_CTRL_CLKSQ_CON0 (PLL_CTRL_ADDR + 0x20) +#define PLL_CTRL_CLKSQ_CON1 (PLL_CTRL_ADDR + 0x24) +#define PLL_CTRL_CLKSQ_CON2 (PLL_CTRL_ADDR + 0x28) + +#define PLL_CTRL_CON0 (PLL_CTRL_ADDR + 0x40) +#define PLL_CTRL_CON1 (PLL_CTRL_ADDR + 0x44) +#define PLL_CTRL_CON2 (PLL_CTRL_ADDR + 0x48) +#define PLL_CTRL_CON3 (PLL_CTRL_ADDR + 0x4c) +#define PLL_CTRL_CON4 (PLL_CTRL_ADDR + 0x50) +#define PLL_CTRL_CON5 (PLL_CTRL_ADDR + 0x54) +#define PLL_CTRL_CON6 (PLL_CTRL_ADDR + 0x58) +#define PLL_CTRL_CON7 (PLL_CTRL_ADDR + 0x5c) +#define PLL_CTRL_CON8 (PLL_CTRL_ADDR + 0x5c) +#define PLL_CTRL_CON9 (PLL_CTRL_ADDR + 0x5c) +#define PLL_CTRL_CON10 (PLL_CTRL_ADDR + 0x5c) +#define PLL_CTRL_CON11 (PLL_CTRL_ADDR + 0x5c) + +#define PLL_CTRL_DPM_CON0 (PLL_CTRL_ADDR + 0x90) +#define PLL_CTRL_DPM_CON1 (PLL_CTRL_ADDR + 0x94) +#define PLL_CTRL_DPM_CON2 (PLL_CTRL_ADDR + 0x98) + +#define PLL_CTRL_MPLL_CON0 (PLL_CTRL_ADDR + 0x100) +#define PLL_CTRL_MPLL_CON1 (PLL_CTRL_ADDR + 0x104) +#define PLL_CTRL_MPLL_CON2 (PLL_CTRL_ADDR + 0x108) + +#define PLL_CTRL_UPLL_CON0 (PLL_CTRL_ADDR + 0x140) +#define PLL_CTRL_UPLL_CON1 (PLL_CTRL_ADDR + 0x144) +#define PLL_CTRL_UPLL_CON2 (PLL_CTRL_ADDR + 0x148) + +#define PLL_CTRL_EPLL_CON0 (PLL_CTRL_ADDR + 0x180) +#define PLL_CTRL_EPLL_CON1 (PLL_CTRL_ADDR + 0x184) +#define PLL_CTRL_EPLL_CON2 (PLL_CTRL_ADDR + 0x188) + +#define PLL_CTRL_FH_CON0 (PLL_CTRL_ADDR + 0x500) +#define PLL_CTRL_FH_CON1 (PLL_CTRL_ADDR + 0x504) +#define PLL_CTRL_FH_CON2 (PLL_CTRL_ADDR + 0x508) +#define PLL_CTRL_FH_CON3 (PLL_CTRL_ADDR + 0x50c) +#define PLL_CTRL_FH_CON4 (PLL_CTRL_ADDR + 0x510) + +#define PLL_CTRL_MDDS_CON0 (PLL_CTRL_ADDR + 0x640) +#define PLL_CTRL_MDDS_CON1 (PLL_CTRL_ADDR + 0x644) +#define PLL_CTRL_MDDS_CON2 (PLL_CTRL_ADDR + 0x648) + +#define PLL_CTRL_EDDS_CON0 (PLL_CTRL_ADDR + 0x680) +#define PLL_CTRL_EDDS_CON1 (PLL_CTRL_ADDR + 0x684) +#define PLL_CTRL_EDDS_CON2 (PLL_CTRL_ADDR + 0x688) + +#define PLL_CTRL_CLK_CONDA (PLL_CLK_ADDR + 0x00) +#define PLL_CTRL_CLK_CONDB (PLL_CLK_ADDR + 0x04) +#define PLL_CTRL_CLK_CONDC (PLL_CLK_ADDR + 0x08) +#define PLL_CTRL_CLK_CONDD (PLL_CLK_ADDR + 0x0c) +#define PLL_CTRL_CLK_CONDE (PLL_CLK_ADDR + 0x10) +#define PLL_CTRL_CLK_CONDF (PLL_CLK_ADDR + 0x14) +#define PLL_CTRL_CLK_CONDG (PLL_CLK_ADDR + 0x18) +#define PLL_CTRL_CLK_CONDH (PLL_CLK_ADDR + 0x1c) + +#endif /* __FV_PLL_H__ */ diff --git a/include/fernvale-pmic.h b/include/fernvale-pmic.h new file mode 100644 index 0000000..da62908 --- /dev/null +++ b/include/fernvale-pmic.h @@ -0,0 +1,17 @@ +#ifndef __FV_PMIC_H__ +#define __FV_PMIC_H__ + +#define PMIC_ADDR 0xa0700a00 +#define PMIC_CTRL0 (PMIC_ADDR + 0x00) +#define PMIC_CTRL1 (PMIC_ADDR + 0x04) +#define PMIC_CTRL2 (PMIC_ADDR + 0x08) +#define PMIC_CTRL3 (PMIC_ADDR + 0x0c) +#define PMIC_CTRL4 (PMIC_ADDR + 0x10) +#define PMIC_CTRL5 (PMIC_ADDR + 0x14) +#define PMIC_CTRL6 (PMIC_ADDR + 0x18) +#define PMIC_CTRL7 (PMIC_ADDR + 0x1c) +#define PMIC_CTRL8 (PMIC_ADDR + 0x20) +#define PMIC_CTRL9 (PMIC_ADDR + 0x24) +#define PMIC_CTRL10 (PMIC_ADDR + 0x28) + +#endif /* __FV_PMIC_H__ */ diff --git a/include/memio.h b/include/memio.h index 0b3c1fd..0460c83 100644 --- a/include/memio.h +++ b/include/memio.h @@ -1,35 +1,35 @@ -#ifndef __MEMIO_H__ -#define __MEMIO_H__ +#ifndef __FV_MEMIO_H__ +#define __FV_MEMIO_H__ #include -inline void writeb(uint8_t value, uint32_t addr) +static inline void writeb(uint8_t value, uint32_t addr) { *((volatile uint8_t *)addr) = value; } -inline uint8_t readb(uint32_t addr) +static inline uint8_t readb(uint32_t addr) { return *(volatile uint8_t *)addr; } -inline void writew(uint16_t value, uint32_t addr) +static inline void writew(uint16_t value, uint32_t addr) { *((volatile uint16_t *)addr) = value; } -inline uint16_t readw(uint32_t addr) +static inline uint16_t readw(uint32_t addr) { return *(volatile uint16_t *)addr; } -inline void writel(uint32_t value, uint32_t addr) +static inline void writel(uint32_t value, uint32_t addr) { *((volatile uint32_t *)addr) = value; } -inline uint32_t readl(uint32_t addr) +static inline uint32_t readl(uint32_t addr) { return *(volatile uint32_t *)addr; } -#endif /* __MEMIO_H__ */ +#endif /* __FV_MEMIO_H__ */ diff --git a/include/scriptic.h b/include/scriptic.h new file mode 100644 index 0000000..0a891da --- /dev/null +++ b/include/scriptic.h @@ -0,0 +1,170 @@ +#ifndef __SCRIPTIC_H__ +#define __SCRIPTIC_H__ + +#define sc_end_cmd 0 +#define sc_read32_cmd 1 +#define sc_write32_cmd 2 +#define sc_read16_cmd 3 +#define sc_write16_cmd 4 +#define sc_call_cmd 5 +#define sc_usleep_cmd 6 + +#define sc_header_size (4 * 2) +#define sc_end_size (sc_header_size + 0) +#define sc_read32_size (sc_header_size + 4 * 3) +#define sc_write32_size (sc_header_size + 4 * 3) +#define sc_read16_size (sc_header_size + 4 * 2 + 1 * 4) +#define sc_write16_size (sc_header_size + 4 * 2 + 1 * 4) +#define sc_call_size (sc_header_size + 4 * 2) +#define sc_usleep_size (sc_header_size + 4 * 1) + +#ifdef __ASSEMBLY__ + +.macro sc_header cmd + @cmdindex=@cmdindex+1 + .long \cmd + .long 0 @cmdindex +.endm + +.macro sc_end + sc_header sc_end_cmd +.endm + +.macro sc_read32 match, mask, addr + sc_header sc_read32_cmd + .long \match + .long \mask + .long \addr +.endm + +.macro sc_write32 value, mask, addr + sc_header sc_write32_cmd + .long \value + .long \mask + .long \addr +.endm + +.macro sc_read16 match, mask, addr + sc_header sc_read16_cmd + .short \match + .short \mask + .long \addr +.endm + +.macro sc_write16 value, mask, addr + sc_header sc_write16_cmd + .short \value + .short \mask + .long \addr +.endm + +.macro sc_call func, arg + sc_header sc_call_cmd + .long \func + .long \arg +.endm + +.macro sc_usleep microsecs + sc_header sc_usleep_cmd + .long \microsecs +.endm + +.macro sc_new name, major, minor, rev +.align 4 +.global \name + @cmdindex=0 // Reset command index counter variable +\name: +1: + .asciz "\name" +2: + .iflt 16 - (2b - 1b) + .error "Name too long (16-bytes max)" + .endif + + // Zero-pad + .ifgt 16 - (2b - 1b) + .zero 16 - (2b - 1b) + .endif + + .byte \major // ver_major + .byte \minor // ver_minor + .short \rev // ver_rev + .long 0 // command_count + // Commands will follow in RAM +.endm + +#else /* ! __ASSEMBLY__ */ + +#include + +/* Header prefixed in all commands */ +struct scriptic_header { + uint32_t command; + uint32_t index; +} __attribute__((__packed__)); + +/* Stop execution */ +struct scriptic_end { + struct scriptic_header header; +} __attribute__((__packed__)); + +/* Read 32-bit value (and optionally wait for a value) */ +struct scriptic_read32 { + struct scriptic_header header; + uint32_t match; /* Value to match */ + uint32_t mask; /* Mask for bits to match */ + uint32_t addr; /* Address to read from */ +} __attribute__((__packed__)); + +/* Write a 32-bit value to an address */ +struct scriptic_write32 { + struct scriptic_header header; + uint32_t value; /* Value to write */ + uint32_t mask; /* Mask for bits to write */ + uint32_t addr; /* Address to write to */ +} __attribute__((__packed__)); + +/* Read 16-bit value (and optionally wait for a value) */ +struct scriptic_read16 { + struct scriptic_header header; + uint16_t match; /* Value to match */ + uint16_t mask; /* Mask for bits to match */ + uint32_t addr; /* Address to read from */ +} __attribute__((__packed__)); + +/* Write a 16-bit value to an address */ +struct scriptic_write16 { + struct scriptic_header header; + uint16_t value; /* Value to write */ + uint16_t mask; /* Mask for bits to write */ + uint32_t addr; /* Address to write to */ +} __attribute__((__packed__)); + +/* Call a function repeatedly and wait for it to return true */ +struct scriptic_call { + struct scriptic_header header; + int (*func)(void *arg); /* Function to call */ + void *opaque; /* Arg to pass */ +} __attribute__((__packed__)); + +/* Sleep for a given number of microseconds */ +struct scriptic_usleep { + struct scriptic_header header; + uint32_t usecs; /* Number of microseconds */ +} __attribute__((__packed__)); + +struct scriptic { + const char name[16]; + uint8_t ver_major; + uint8_t ver_minor; + uint16_t ver_rev; + uint32_t command_count; +} __attribute__((__packed__)); + +int scriptic_execute(const struct scriptic *script); +const struct scriptic *scriptic_get(const char *name); + + +#endif /* __ASSEMBLY__ */ + +#endif /* __SCRIPTIC_H__ */ diff --git a/include/spi.h b/include/spi.h new file mode 100644 index 0000000..6d63d00 --- /dev/null +++ b/include/spi.h @@ -0,0 +1,7 @@ + +#ifndef __SPI_H__ +#define __SPI_H__ + +#define SPI_ADDR 0xa0140000 + +#endif /* __SPI_H__ */ diff --git a/include/utils.h b/include/utils.h index dd4a244..77bd7e4 100644 --- a/include/utils.h +++ b/include/utils.h @@ -2,6 +2,19 @@ #define __UTILS_H__ #include +# define do_div(n,base) ({ \ + uint32_t __base = (base); \ + uint32_t __rem; \ + (void)(((typeof((n)) *)0) == ((uint64_t *)0)); \ + if (((n) >> 32) == 0) { \ + __rem = (uint32_t)(n) % __base; \ + (n) = (uint32_t)(n) / __base; \ + } else \ + __rem = __div64_32(&(n), __base); \ + __rem; \ + }) + uint32_t _udiv64(uint64_t n, uint32_t d); +uint32_t __div64_32(uint64_t *n, uint32_t base); #endif /* __UTILS_H__ */ diff --git a/magic.mk b/magic.mk index 1e5d5cb..d069b5e 100644 --- a/magic.mk +++ b/magic.mk @@ -17,7 +17,7 @@ vpath %.S . $(TOP) $(BUILD)/%.o: %.S $(ECHO) "CC $<" - $(Q)$(CC) $(CFLAGS) -c -o $@ $< + $(Q)$(CC) $(CFLAGS) $(AFLAGS) -c -o $@ $< vpath %.s . $(TOP) $(BUILD)/%.o: %.s diff --git a/main.c b/main.c index 6ceeba1..dfe8354 100644 --- a/main.c +++ b/main.c @@ -4,6 +4,9 @@ #include "printf.h" #include "serial.h" #include "utils.h" +#include "scriptic.h" + +#include "fernvale-pmic.h" //#define AUTOMATED @@ -193,34 +196,27 @@ static int list_registers(void) return 0; } -#define PMIC_BASE 0xa0700a00 -#define PMIC_CTRL0 (PMIC_BASE + 0x04) -#define PMIC_CTRL1 (PMIC_BASE + 0x04) -#define PMIC_CTRL2 (PMIC_BASE + 0x08) -#define PMIC_CTRL3 (PMIC_BASE + 0x0c) -#define PMIC_CTRL4 (PMIC_BASE + 0x10) -#define PMIC_CTRL5 (PMIC_BASE + 0x14) -#define PMIC_CTRL6 (PMIC_BASE + 0x18) -#define PMIC_CTRL7 (PMIC_BASE + 0x1c) -#define PMIC_CTRL8 (PMIC_BASE + 0x20) -#define PMIC_CTRL9 (PMIC_BASE + 0x24) -#define PMIC_CTRL10 (PMIC_BASE + 0x28) - static int do_init(void) { + const struct scriptic *script; + list_registers(); /* Disable system watchdog */ writel(0x2200, 0xa0030000); - - /* Enable USB Download mode (required for no-battery operation) */ writew(0x8000, PMIC_CTRL10); /* Disable battery watchdog */ writew(0x2, PMIC_CTRL9); + script = scriptic_get("set_plls"); + scriptic_execute(script); + + script = scriptic_get("enable_psram"); + scriptic_execute(script); + serial_puts("\n\nFernly shell\n"); return 0; diff --git a/scriptic.c b/scriptic.c new file mode 100644 index 0000000..9b1e8bc --- /dev/null +++ b/scriptic.c @@ -0,0 +1,183 @@ +#include +#include "scriptic.h" +#include "bionic.h" +#include "memio.h" + +extern struct scriptic set_plls; +extern struct scriptic enable_psram; + +static int sc_header_command(struct scriptic_header *header) +{ + return header->command; +} + +static int sc_command_size(void *header) { + switch(sc_header_command(header)) { + case sc_end_cmd: return sizeof(struct scriptic_end); + case sc_read32_cmd: return sizeof(struct scriptic_read32); + case sc_write32_cmd: return sizeof(struct scriptic_write32); + case sc_read16_cmd: return sizeof(struct scriptic_read16); + case sc_write16_cmd: return sizeof(struct scriptic_write16); + case sc_call_cmd: return sizeof(struct scriptic_call); + case sc_usleep_cmd: return sizeof(struct scriptic_usleep); + default: return sizeof(struct scriptic_header); + } +} + +static struct scriptic_header *sc_next_command(void *header) +{ + return header + sc_command_size(header); +} + +static int sc_command_count(struct scriptic *script) +{ + uint32_t count = 1; + struct scriptic_header *header = (struct scriptic_header *)&script[1]; + + for (count = 0; sc_header_command(header) != sc_end_cmd; count++) { + header->index = count; + header = sc_next_command(header); + } + header->index = count; + + return count; +} + +/* Command functions */ + +static void sc_read32(struct scriptic_read32 *pkt) +{ + if ((pkt->mask == 0) || (pkt->mask == 0xffffffff)) + (void)readl(pkt->addr); + else { + while (1) { + uint32_t val = readl(pkt->addr); + if ((val & pkt->mask) == (pkt->match & pkt->mask) ) + break; + } + } +} + +static void sc_read16(struct scriptic_read16 *pkt) +{ + if ((pkt->mask == 0) || (pkt->mask == 0xffff)) + (void)readw(pkt->addr); + else + while (1) { + uint16_t val = readw(pkt->addr); + if ((val & pkt->mask) == (pkt->match & pkt->mask) ) + break; + } +} + +static void sc_write32(struct scriptic_write32 *pkt) +{ + if ((pkt->mask == 0) || (pkt->mask == 0xffffffff)) { + writel(pkt->value, pkt->addr); + } + else { + uint32_t tmp; + tmp = readl(pkt->addr); + tmp &= ~pkt->mask; + tmp |= (pkt->value & pkt->mask); + writel(tmp, pkt->addr); + } +} + +static void sc_write16(struct scriptic_write16 *pkt) +{ + if ((pkt->mask == 0) || (pkt->mask == 0xffff)) { + writew(pkt->value, pkt->addr); + } + else { + uint16_t tmp; + tmp = readw(pkt->addr); + tmp &= ~pkt->mask; + tmp |= (pkt->value & pkt->mask); + writew(tmp, pkt->addr); + } +} + +static void sc_call(struct scriptic_call *pkt) +{ + while (!pkt->func(pkt->opaque)); +} + +void sc_usleep(struct scriptic_usleep *pkt) +{ + uint32_t usecs; + int i, j; + + usecs = pkt->usecs; + + /* Outer loop is 11 cycles total, 6 cycles on its own */ + for (i = 0; i < usecs; i++) + /* Inner loop is 5 cycles */ + for (j = 0; j < 73; j++) + asm("nop"); +} + +/* Exported functions */ + +int scriptic_execute(const struct scriptic *script) +{ + void *header; + + if (!script) + return -1; + + header = (struct scriptic_header *)&script[1]; + + while (sc_header_command(header) != sc_end_cmd) { + switch(sc_header_command(header)) { + case sc_end_cmd: + break; + + case sc_read32_cmd: + sc_read32(header); + break; + + case sc_write32_cmd: + sc_write32(header); + break; + + case sc_read16_cmd: + sc_read16(header); + break; + + case sc_write16_cmd: + sc_write16(header); + break; + + case sc_call_cmd: + sc_call(header); + break; + + case sc_usleep_cmd: + sc_usleep(header); + break; + + default: + break; + } + + header = sc_next_command(header); + } + + return 0; +} + +const struct scriptic *scriptic_get(const char *name) +{ + struct scriptic *script = NULL; + + if (!_strcasecmp(name, "set_plls")) + script = &set_plls; + else if (!_strcasecmp(name, "enable_psram")) + script = &enable_psram; + + if (script && script->command_count == 0) + script->command_count = sc_command_count(script); + + return script; +} diff --git a/scriptic/enable-psram.S b/scriptic/enable-psram.S new file mode 100644 index 0000000..d0b55b8 --- /dev/null +++ b/scriptic/enable-psram.S @@ -0,0 +1,74 @@ +#include "scriptic.h" +#include "fernvale-emi.h" + + sc_new "enable_psram", 1, 0, 0 + + sc_write32 0x401, 0, EMI_CTRL_GENA + + /* Remap EMI to 0x10000000, and SPI to 0x00000000 */ + sc_write32 2, 0, EMI_CTRL_REMAP + + /* Memory configuration */ + sc_read16 0, 0, 0x1ffffffe + sc_usleep 50 + sc_read16 0, 0, 0x1ffffffe + sc_usleep 50 + + sc_write16 1, 0, 0x1ffffffe + sc_usleep 50 + sc_write16 0x2b13, 0, 0x1ffffffe + sc_usleep 50 + + sc_read16 0, 0, 0x1ffffffe + sc_usleep 50 + sc_read16 0, 0, 0x1ffffffe + sc_usleep 50 + + sc_write16 0, 0, 0x1ffffffe + sc_usleep 50 + sc_write16 0x10, 0, 0x1ffffffe + sc_usleep 50 + + sc_write32 0xa0000000, 0, EMI_CTRL_CONB + sc_write32 0xb2024419, 0, EMI_CTRL_CONF + sc_usleep 50 + + sc_write32 0x400, 0x400, EMI_CTRL_GENA + + /* Now map EMI back to 0x00000000, and SPI to 0x00000000 */ + sc_write32 3, 0, EMI_CTRL_REMAP + sc_usleep 50 + + sc_write32 0x20004001, 0, EMI_CTRL_RDCT + sc_write32 0x5111, 0, EMI_CTRL_DSRAM + + sc_write32 0, 0, EMI_CTRL_IDLA + sc_write32 0, 0, EMI_CTRL_IDLB + sc_write32 0, 0, EMI_CTRL_IDLC + sc_write32 0, 0, EMI_CTRL_IDLD + sc_write32 0, 0, EMI_CTRL_IDLE + sc_write32 0, 0, EMI_CTRL_ODLA + sc_write32 0, 0, EMI_CTRL_ODLB + sc_write32 0, 0, EMI_CTRL_ODLC + sc_write32 0, 0, EMI_CTRL_ODLD + sc_write32 0, 0, EMI_CTRL_ODLE + + sc_write32 0x00010001, 0, EMI_CTRL_IOA + sc_write32 0x00010001, 0, EMI_CTRL_IOB + + sc_usleep 50 + + /* Calibrate DQ in delay */ + sc_call calibrate_psram, 0 + + sc_write32 0x300f0000, 0, EMI_CTRL_DLLV + sc_read32 0x80, 0x80, EMI_CTRL_DLLV + sc_write32 0x700f0000, 0, EMI_CTRL_DLLV + sc_read32 0x80, 0x00, EMI_CTRL_DLLV + sc_write32 0x100f0000, 0, EMI_CTRL_DLLV + + sc_write32 0x5426, 0, EMI_CTRL_ARBA + sc_write32 0x5009, 0, EMI_CTRL_ARBB + sc_write32 0x5051, 0, EMI_CTRL_ARBC + + sc_end diff --git a/scriptic/set-plls.S b/scriptic/set-plls.S new file mode 100644 index 0000000..cfa869e --- /dev/null +++ b/scriptic/set-plls.S @@ -0,0 +1,29 @@ +#include "scriptic.h" +#include "fernvale-pll.h" + + sc_new "set_plls", 1, 0, 0 + + sc_write16 0, 0, PLL_CTRL_CON2 + sc_write16 0, 0, PLL_CTRL_CON3 + sc_write16 0, 0, PLL_CTRL_CON0 + sc_usleep 1 + + sc_write16 1, 1, PLL_CTRL_UPLL_CON0 + sc_write16 0x1840, 0, PLL_CTRL_EPLL_CON0 + sc_write16 0x100, 0x100, PLL_CTRL_EPLL_CON1 + sc_write16 1, 0, PLL_CTRL_MDDS_CON0 + sc_write16 1, 1, PLL_CTRL_MPLL_CON0 + sc_usleep 1 + + sc_write16 1, 0, PLL_CTRL_EDDS_CON0 + sc_write16 1, 1, PLL_CTRL_EPLL_CON0 + sc_usleep 1 + + sc_write16 0x4000, 0x4000, PLL_CTRL_CLK_CONDB + sc_usleep 1 + + sc_write32 0x8048, 0, PLL_CTRL_CLK_CONDC + /* Run the SPI clock at 104 MHz */ + sc_write32 0xd002, 0, PLL_CTRL_CLK_CONDH + sc_write32 0xb6a0, 0, PLL_CTRL_CLK_CONDC + sc_end diff --git a/usb-loader.S b/usb-loader.S index 2ec498d..fedfc90 100644 --- a/usb-loader.S +++ b/usb-loader.S @@ -8,6 +8,10 @@ disable_interrupts: orr r0, r0, r1 msr cpsr_cxsf, r0 +relocate_stack: + ldr r0, =0x7000bffc // stack_start + mov sp, r0 + print_welcome_banner: adr r0, welcome_banner bl uart_puts @@ -25,6 +29,7 @@ load_program: # r0 contains the current offset to write to. # Load bytes from the serial port into RAM. mov r0, #0x70000000 + orr r0, r0, #0x6000 mvn r2, #0 ldr r3, =0xfff03639 blx r3 @@ -33,6 +38,7 @@ jump_to_new_program: adr r0, launch_message bl uart_puts mov r0, #0x70000000 + orr r0, r0, #0x6000 mov pc, r0 .align 4 diff --git a/utils.c b/utils.c index e2c7b09..a58e298 100644 --- a/utils.c +++ b/utils.c @@ -1,5 +1,6 @@ #include #include "serial.h" +#include "printf.h" static inline int _isprint(char c) { @@ -62,9 +63,41 @@ int serial_print_hex(const void *block, int count) return serial_print_hex_offset(block, count, 0); } -extern uint32_t __udiv64(uint32_t a, uint32_t b, uint32_t c); -uint32_t _udiv64(uint64_t n, uint32_t d) +uint32_t __div64_32(uint64_t *n, uint32_t base) { - return __udiv64(n >> 32, n, d); + uint64_t rem = *n; + uint64_t b = base; + uint64_t res, d = 1; + uint32_t high = rem >> 32; + + /* Reduce the thing a bit first */ + res = 0; + if (high >= base) { + high /= base; + res = (uint64_t) high << 32; + rem -= (uint64_t) (high*base) << 32; + } + + while ((int64_t)b > 0 && b < rem) { + b = b+b; + d = d+d; + } + + do { + if (rem >= b) { + rem -= b; + res += d; + } + b >>= 1; + d >>= 1; + } while (d); + + *n = res; + return rem; } +void __div0 (void) +{ + printf("Division by 0. Hanging.\n"); + while(1); +}