Blackfin: dynamically update UART speed when initializing
authorMike Frysinger <vapier@gentoo.org>
Wed, 10 Dec 2008 17:33:54 +0000 (12:33 -0500)
committerMike Frysinger <vapier@gentoo.org>
Fri, 6 Feb 2009 02:25:35 +0000 (21:25 -0500)
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 <vapier@gentoo.org>
Signed-off-by: Robin Getz <rgetz@blackfin.uclinux.org>
cpu/blackfin/initcode.c
cpu/blackfin/serial.h

index 704b791bcbfafcd6ad28f4ed349f603065f7190b..ae0016de18f9c67f6799ee2f73321ccb3fe50296 100644 (file)
@@ -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');
 
index f671096768b6b0bffce7ff5984335bb2b85da4c0..ce39148f83f68446bb4a10f419c322cc301fe641 100644 (file)
@@ -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