From f790ef6ff12381cb0e43de54fb2b0f1204ad8ed6 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 10 Dec 2008 12:33:54 -0500 Subject: [PATCH] Blackfin: dynamically update UART speed when initializing Previously, booting over the UART required the baud rate to be known ahead of time. Using a bit of tricky simple math, we can calculate the new board rate based on the old divisors. Signed-off-by: Mike Frysinger Signed-off-by: Robin Getz --- cpu/blackfin/initcode.c | 62 ++++++++++++++--------------------- cpu/blackfin/serial.h | 72 +++++++++++------------------------------ 2 files changed, 43 insertions(+), 91 deletions(-) diff --git a/cpu/blackfin/initcode.c b/cpu/blackfin/initcode.c index 704b791bcb..ae0016de18 100644 --- a/cpu/blackfin/initcode.c +++ b/cpu/blackfin/initcode.c @@ -20,7 +20,7 @@ #include "serial.h" __attribute__((always_inline)) -static inline uint32_t serial_init(void) +static inline void serial_init(void) { #ifdef __ADSPBF54x__ # ifdef BFIN_BOOT_UART_USE_RTS @@ -61,25 +61,16 @@ static inline uint32_t serial_init(void) } #endif - uint32_t old_baud; - if (BFIN_DEBUG_EARLY_SERIAL || CONFIG_BFIN_BOOT_MODE == BFIN_BOOT_UART) - old_baud = serial_early_get_baud(); - else - old_baud = CONFIG_BAUDRATE; - if (BFIN_DEBUG_EARLY_SERIAL) { + int ucen = *pUART_GCTL & UCEN; serial_early_init(); /* If the UART is off, that means we need to program * the baud rate ourselves initially. */ - if (!old_baud) { - old_baud = CONFIG_BAUDRATE; + if (ucen != UCEN) serial_early_set_baud(CONFIG_BAUDRATE); - } } - - return old_baud; } __attribute__((always_inline)) @@ -93,30 +84,6 @@ static inline void serial_deinit(void) #endif } -/* We need to reset the baud rate when we have early debug turned on - * or when we are booting over the UART. - * XXX: we should fix this to calc the old baud and restore it rather - * than hardcoding it via CONFIG_LDR_LOAD_BAUD ... but we have - * to figure out how to avoid the division in the baud calc ... - */ -__attribute__((always_inline)) -static inline void serial_reset_baud(uint32_t baud) -{ - if (!BFIN_DEBUG_EARLY_SERIAL && CONFIG_BFIN_BOOT_MODE != BFIN_BOOT_UART) - return; - -#ifndef CONFIG_LDR_LOAD_BAUD -# define CONFIG_LDR_LOAD_BAUD 115200 -#endif - - if (CONFIG_BFIN_BOOT_MODE == BFIN_BOOT_BYPASS) - serial_early_set_baud(baud); - else if (CONFIG_BFIN_BOOT_MODE == BFIN_BOOT_UART) - serial_early_set_baud(CONFIG_LDR_LOAD_BAUD); - else - serial_early_set_baud(CONFIG_BAUDRATE); -} - __attribute__((always_inline)) static inline void serial_putc(char c) { @@ -239,7 +206,14 @@ static inline void serial_putc(char c) BOOTROM_CALLED_FUNC_ATTR void initcode(ADI_BOOT_DATA *bootstruct) { - uint32_t old_baud = serial_init(); + /* Save the clock pieces that are used in baud rate calculation */ + unsigned int sdivB, divB, vcoB; + serial_init(); + if (BFIN_DEBUG_EARLY_SERIAL || CONFIG_BFIN_BOOT_MODE == BFIN_BOOT_UART) { + sdivB = bfin_read_PLL_DIV() & 0xf; + vcoB = (bfin_read_PLL_CTL() >> 9) & 0x3f; + divB = serial_early_get_div(); + } #ifdef CONFIG_HW_WATCHDOG # ifndef CONFIG_HW_WATCHDOG_TIMEOUT_INITCODE @@ -334,8 +308,20 @@ void initcode(ADI_BOOT_DATA *bootstruct) /* Since we've changed the SCLK above, we may need to update * the UART divisors (UART baud rates are based on SCLK). + * Do the division by hand as there are no native instructions + * for dividing which means we'd generate a libgcc reference. */ - serial_reset_baud(old_baud); + if (CONFIG_BFIN_BOOT_MODE == BFIN_BOOT_UART) { + unsigned int sdivR, vcoR; + sdivR = bfin_read_PLL_DIV() & 0xf; + vcoR = (bfin_read_PLL_CTL() >> 9) & 0x3f; + int dividend = sdivB * divB * vcoR; + int divisor = vcoB * sdivR; + unsigned int quotient; + for (quotient = 0; dividend > 0; ++quotient) + dividend -= divisor; + serial_early_put_div(quotient - ANOMALY_05000230); + } serial_putc('F'); diff --git a/cpu/blackfin/serial.h b/cpu/blackfin/serial.h index f671096768..ce39148f83 100644 --- a/cpu/blackfin/serial.h +++ b/cpu/blackfin/serial.h @@ -156,16 +156,25 @@ static inline void serial_early_init(void) } __attribute__((always_inline)) -static inline uint32_t serial_early_get_baud(void) +static inline void serial_early_put_div(uint16_t divisor) { - /* If the UART isnt enabled, then we are booting an LDR - * from a non-UART source (so like flash) which means - * the baud rate here is meaningless. - */ - if ((*pUART_GCTL & UCEN) != UCEN) - return 0; + /* Set DLAB in LCR to Access DLL and DLH */ + ACCESS_LATCH(); + SSYNC(); -#if (0) /* See comment for serial_reset_baud() in initcode.c */ + /* Program the divisor to get the baud rate we want */ + *pUART_DLL = LOB(divisor); + *pUART_DLH = HIB(divisor); + SSYNC(); + + /* Clear DLAB in LCR to Access THR RBR IER */ + ACCESS_PORT_IER(); + SSYNC(); +} + +__attribute__((always_inline)) +static inline uint16_t serial_early_get_div(void) +{ /* Set DLAB in LCR to Access DLL and DLH */ ACCESS_LATCH(); SSYNC(); @@ -173,16 +182,12 @@ static inline uint32_t serial_early_get_baud(void) uint8_t dll = *pUART_DLL; uint8_t dlh = *pUART_DLH; uint16_t divisor = (dlh << 8) | dll; - uint32_t baud = get_sclk() / (divisor * 16); /* Clear DLAB in LCR to Access THR RBR IER */ ACCESS_PORT_IER(); SSYNC(); - return baud; -#else - return CONFIG_BAUDRATE; -#endif + return divisor; } __attribute__((always_inline)) @@ -192,20 +197,7 @@ static inline void serial_early_set_baud(uint32_t baud) * weird multiplication is to make sure we over sample just * a little rather than under sample the incoming signals. */ - uint16_t divisor = (get_sclk() + (baud * 8)) / (baud * 16) - ANOMALY_05000230; - - /* Set DLAB in LCR to Access DLL and DLH */ - ACCESS_LATCH(); - SSYNC(); - - /* Program the divisor to get the baud rate we want */ - *pUART_DLL = LOB(divisor); - *pUART_DLH = HIB(divisor); - SSYNC(); - - /* Clear DLAB in LCR to Access THR RBR IER */ - ACCESS_PORT_IER(); - SSYNC(); + serial_early_put_div((get_sclk() + (baud * 8)) / (baud * 16) - ANOMALY_05000230); } #ifndef BFIN_IN_INITCODE @@ -235,32 +227,6 @@ static inline void serial_early_puts(const char *s) #endif .endm -/* Recursively expand calls to _serial_putc for every byte - * passed to us. Append a newline when we're all done. - */ -.macro _serial_early_putc byte:req morebytes:vararg -#ifdef CONFIG_DEBUG_EARLY_SERIAL - R0 = \byte; - call _serial_putc; -.ifnb \morebytes - _serial_early_putc \morebytes -.else -.if (\byte != '\n') - _serial_early_putc '\n' -.endif -.endif -#endif -.endm - -/* Wrapper around recurisve _serial_early_putc macro which - * simply prepends the string "Early: " - */ -.macro serial_early_putc byte:req morebytes:vararg -#ifdef CONFIG_DEBUG_EARLY_SERIAL - _serial_early_putc 'E', 'a', 'r', 'l', 'y', ':', ' ', \byte, \morebytes -#endif -.endm - /* Since we embed the string right into our .text section, we need * to find its address. We do this by getting our PC and adding 2 * bytes (which is the length of the jump instruction). Then we -- 2.30.2