
Add a serial driver that supports driver model. So far it only works with the v1 serial port.
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/blackfin/include/asm/serial1.h | 2 + drivers/serial/Kconfig | 8 ++ drivers/serial/serial_bfin.c | 192 ++++++++++++++++++++++++++++++++++++ 3 files changed, 202 insertions(+)
diff --git a/arch/blackfin/include/asm/serial1.h b/arch/blackfin/include/asm/serial1.h index 467d381..be5a7a9 100644 --- a/arch/blackfin/include/asm/serial1.h +++ b/arch/blackfin/include/asm/serial1.h @@ -231,6 +231,7 @@ static inline void serial_early_do_portmux(void) SSYNC(); }
+#ifndef CONFIG_DM_SERIAL __attribute__((always_inline)) static inline int uart_init(uint32_t uart_base) { @@ -262,6 +263,7 @@ static inline int serial_early_uninit(uint32_t uart_base)
return 0; } +#endif /* !CONFIG_DM_SERIAL */
__attribute__((always_inline)) static inline void serial_set_divisor(uint32_t uart_base, uint16_t divisor) diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 620dd82..3b16c40 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -161,6 +161,14 @@ config DEBUG_MVEBU_A3700_UART will need to provide parameters to make this work. The driver will be available until the real driver-model serial is running.
+config DEBUG_UART_BLACKFIN + bool "Blackfin" + help + Select this to enable a debug UART using the serial_bfin driver. You + will need to provide parameters to make this work. The driver will + be available until the real driver-model serial is running. This + only supports the v1 UART at present. + config DEBUG_UART_ZYNQ bool "Xilinx Zynq" help diff --git a/drivers/serial/serial_bfin.c b/drivers/serial/serial_bfin.c index 1d5be2a..8d80b05 100644 --- a/drivers/serial/serial_bfin.c +++ b/drivers/serial/serial_bfin.c @@ -38,6 +38,8 @@ */
#include <common.h> +#include <debug_uart.h> +#include <dm.h> #include <post.h> #include <watchdog.h> #include <serial.h> @@ -47,6 +49,8 @@
DECLARE_GLOBAL_DATA_PTR;
+#ifndef CONFIG_DM_SERIAL + #ifdef CONFIG_UART_CONSOLE
#ifdef CONFIG_DEBUG_SERIAL @@ -409,3 +413,191 @@ void bfin_serial_initialize(void) serial_register(&bfin_serial_mem_device); } #endif /* CONFIG_UART_MEM */ + +#endif /* CONFIG_DM_SERIAL */ + + +#ifdef CONFIG_DM_SERIAL +struct bfin_serial_priv { + struct bfin_mmr_serial *regs; +}; + +static void dm_serial_set_divisor(struct bfin_mmr_serial *regs, + uint16_t divisor) +{ + /* Set DLAB in LCR to Access DLL and DLH */ + bfin_write_or(®s->lcr, DLAB); + SSYNC(); + + /* Program the divisor to get the baud rate we want */ + bfin_write(®s->dll, LOB(divisor)); + bfin_write(®s->dlh, HIB(divisor)); + SSYNC(); + + /* Clear DLAB in LCR to Access THR RBR IER */ + bfin_write_and(®s->lcr, ~DLAB); + SSYNC(); +} + +static void __serial_set_baud(struct bfin_mmr_serial *regs, unsigned baud) +{ + uint16_t divisor = (get_uart_clk() + (baud * 8)) / (baud * 16) + - ANOMALY_05000230; + + dm_serial_set_divisor(regs, divisor); +} + +static inline int uart_init(struct bfin_mmr_serial *regs) +{ + /* always enable UART -- avoids anomalies 05000309 and 05000350 */ + bfin_write(®s->gctl, UCEN); + + /* Set LCR to Word Lengh 8-bit word select */ + bfin_write(®s->lcr, WLS_8); + + SSYNC(); + + return 0; +} + +static int uart_tstc(struct bfin_mmr_serial *regs) +{ + WATCHDOG_RESET(); + return (_lsr_read(regs) & DR) ? 1 : 0; +} + +#if defined(CONFIG_DEBUG_UART_BLACKFIN) + +#include <debug_uart.h> + +static inline int serial_early_init(struct bfin_mmr_serial *regs) +{ + /* handle portmux crap on different Blackfins */ + serial_early_do_portmux(); + + uart_init(regs); + serial_early_set_baud((ulong)regs, CONFIG_BAUDRATE); + + return 0; +} + +static void _debug_uart_init(void) +{ + struct bfin_mmr_serial *regs = + (struct bfin_mmr_serial *)CONFIG_DEBUG_UART_BASE; + + serial_early_init(regs); +} + +static inline void _debug_uart_putc(int ch) +{ + struct bfin_mmr_serial *regs = + (struct bfin_mmr_serial *)CONFIG_DEBUG_UART_BASE; + + /* wait for the hardware fifo to clear up */ + while (!(_lsr_read(regs) & THRE)) + continue; + + /* queue the character for transmission */ + bfin_write(®s->thr, ch); + SSYNC(); +} + +DEBUG_UART_FUNCS + +#endif /* CONFIG_DEBUG_UART_BLACKFIN */ + +static int bfin_serial_probe(struct udevice *dev) +{ + struct bfin_serial_priv *priv = dev_get_priv(dev); + const unsigned short pins[] = { _P_UART(0, RX), _P_UART(0, TX), 0, }; + struct bfin_mmr_serial *regs; + + peripheral_request_list(pins, "bfin-uart"); + regs = (struct bfin_mmr_serial *)MMR_UART(0); + priv->regs = regs; + + return 0; +} + +static int bfin_serial_putc(struct udevice *dev, const char ch) +{ + struct bfin_serial_priv *priv = dev_get_priv(dev); + struct bfin_mmr_serial *regs = priv->regs; + + /* wait for the hardware fifo to clear up */ + if (!(_lsr_read(regs) & THRE)) + return -EAGAIN; + + /* queue the character for transmission */ + bfin_write(®s->thr, ch); + SSYNC(); + + return 0; +} + +static int bfin_serial_getc(struct udevice *dev) +{ + struct bfin_serial_priv *priv = dev_get_priv(dev); + struct bfin_mmr_serial *regs = priv->regs; + uint16_t uart_rbr_val; + + /* wait for data ! */ + if (!uart_tstc(regs)) + return -EAGAIN; + + /* grab the new byte */ + uart_rbr_val = bfin_read(®s->rbr); + _lsr_write(regs, -1); + + return uart_rbr_val; +} + +int bfin_serial_setbrg(struct udevice *dev, int baudrate) +{ + struct bfin_serial_priv *priv = dev_get_priv(dev); + struct bfin_mmr_serial *regs = priv->regs; + + uart_init(regs); + __serial_set_baud(regs, baudrate); + _lsr_write(regs, -1); + + return 0; +} + +static int bfin_serial_pending(struct udevice *dev, bool input) +{ + struct bfin_serial_priv *priv = dev_get_priv(dev); + struct bfin_mmr_serial *regs = priv->regs; + + if (input) + return (_lsr_read(regs) & DR) ? 1 : 0; + else + return (_lsr_read(regs) & TEMT) ? 0 : 1; + + return 0; +} + +static const struct udevice_id bfin_uart_of_match[] = { + { .compatible = "adi,uart-v1" }, + { /* sentinel */ } +}; + +static const struct dm_serial_ops bfin_serial_ops = { + .putc = bfin_serial_putc, + .pending = bfin_serial_pending, + .getc = bfin_serial_getc, + .setbrg = bfin_serial_setbrg, +}; + +U_BOOT_DRIVER(serial_bfin) = { + .name = "serial_bfin", + .id = UCLASS_SERIAL, + .of_match = bfin_uart_of_match, + .probe = bfin_serial_probe, + .ops = &bfin_serial_ops, + .priv_auto_alloc_size = sizeof(struct bfin_serial_priv), + .flags = DM_FLAG_PRE_RELOC, +}; + +#endif /* !CONFIG_DM_SERIAL */