diff options
author | 2023-10-10 14:33:42 +0000 | |
---|---|---|
committer | 2023-10-10 14:33:42 +0000 | |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/u-boot/drivers/serial/serial_zynq.c | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/u-boot/drivers/serial/serial_zynq.c')
-rw-r--r-- | roms/u-boot/drivers/serial/serial_zynq.c | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/serial/serial_zynq.c b/roms/u-boot/drivers/serial/serial_zynq.c new file mode 100644 index 000000000..799d52404 --- /dev/null +++ b/roms/u-boot/drivers/serial/serial_zynq.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2012 Michal Simek <monstr@monstr.eu> + * Copyright (C) 2011-2012 Xilinx, Inc. All rights reserved. + */ + +#include <clk.h> +#include <common.h> +#include <debug_uart.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <log.h> +#include <watchdog.h> +#include <asm/io.h> +#include <dm/device_compat.h> +#include <linux/bitops.h> +#include <linux/compiler.h> +#include <serial.h> +#include <linux/err.h> + +#define ZYNQ_UART_SR_TXACTIVE BIT(11) /* TX active */ +#define ZYNQ_UART_SR_TXFULL BIT(4) /* TX FIFO full */ +#define ZYNQ_UART_SR_RXEMPTY BIT(1) /* RX FIFO empty */ + +#define ZYNQ_UART_CR_TX_EN BIT(4) /* TX enabled */ +#define ZYNQ_UART_CR_RX_EN BIT(2) /* RX enabled */ +#define ZYNQ_UART_CR_TXRST BIT(1) /* TX logic reset */ +#define ZYNQ_UART_CR_RXRST BIT(0) /* RX logic reset */ + +#define ZYNQ_UART_MR_PARITY_NONE 0x00000020 /* No parity mode */ + +struct uart_zynq { + u32 control; /* 0x0 - Control Register [8:0] */ + u32 mode; /* 0x4 - Mode Register [10:0] */ + u32 reserved1[4]; + u32 baud_rate_gen; /* 0x18 - Baud Rate Generator [15:0] */ + u32 reserved2[4]; + u32 channel_sts; /* 0x2c - Channel Status [11:0] */ + u32 tx_rx_fifo; /* 0x30 - FIFO [15:0] or [7:0] */ + u32 baud_rate_divider; /* 0x34 - Baud Rate Divider [7:0] */ +}; + +struct zynq_uart_plat { + struct uart_zynq *regs; +}; + +/* Set up the baud rate */ +static void _uart_zynq_serial_setbrg(struct uart_zynq *regs, + unsigned long clock, unsigned long baud) +{ + /* Calculation results. */ + unsigned int calc_bauderror, bdiv, bgen; + unsigned long calc_baud = 0; + + /* Covering case where input clock is so slow */ + if (clock < 1000000 && baud > 4800) + baud = 4800; + + /* master clock + * Baud rate = ------------------ + * bgen * (bdiv + 1) + * + * Find acceptable values for baud generation. + */ + for (bdiv = 4; bdiv < 255; bdiv++) { + bgen = clock / (baud * (bdiv + 1)); + if (bgen < 2 || bgen > 65535) + continue; + + calc_baud = clock / (bgen * (bdiv + 1)); + + /* + * Use first calculated baudrate with + * an acceptable (<3%) error + */ + if (baud > calc_baud) + calc_bauderror = baud - calc_baud; + else + calc_bauderror = calc_baud - baud; + if (((calc_bauderror * 100) / baud) < 3) + break; + } + + writel(bdiv, ®s->baud_rate_divider); + writel(bgen, ®s->baud_rate_gen); +} + +/* Initialize the UART, with...some settings. */ +static void _uart_zynq_serial_init(struct uart_zynq *regs) +{ + /* RX/TX enabled & reset */ + writel(ZYNQ_UART_CR_TX_EN | ZYNQ_UART_CR_RX_EN | ZYNQ_UART_CR_TXRST | \ + ZYNQ_UART_CR_RXRST, ®s->control); + writel(ZYNQ_UART_MR_PARITY_NONE, ®s->mode); /* 8 bit, no parity */ +} + +static int _uart_zynq_serial_putc(struct uart_zynq *regs, const char c) +{ + if (readl(®s->channel_sts) & ZYNQ_UART_SR_TXFULL) + return -EAGAIN; + + writel(c, ®s->tx_rx_fifo); + + return 0; +} + +static int zynq_serial_setbrg(struct udevice *dev, int baudrate) +{ + struct zynq_uart_plat *plat = dev_get_plat(dev); + unsigned long clock; + + int ret; + struct clk clk; + + ret = clk_get_by_index(dev, 0, &clk); + if (ret < 0) { + dev_err(dev, "failed to get clock\n"); + return ret; + } + + clock = clk_get_rate(&clk); + if (IS_ERR_VALUE(clock)) { + dev_err(dev, "failed to get rate\n"); + return clock; + } + debug("%s: CLK %ld\n", __func__, clock); + + ret = clk_enable(&clk); + if (ret) { + dev_err(dev, "failed to enable clock\n"); + return ret; + } + + _uart_zynq_serial_setbrg(plat->regs, clock, baudrate); + + return 0; +} + +static int zynq_serial_probe(struct udevice *dev) +{ + struct zynq_uart_plat *plat = dev_get_plat(dev); + struct uart_zynq *regs = plat->regs; + u32 val; + + /* No need to reinitialize the UART if TX already enabled */ + val = readl(®s->control); + if (val & ZYNQ_UART_CR_TX_EN) + return 0; + + _uart_zynq_serial_init(plat->regs); + + return 0; +} + +static int zynq_serial_getc(struct udevice *dev) +{ + struct zynq_uart_plat *plat = dev_get_plat(dev); + struct uart_zynq *regs = plat->regs; + + if (readl(®s->channel_sts) & ZYNQ_UART_SR_RXEMPTY) + return -EAGAIN; + + return readl(®s->tx_rx_fifo); +} + +static int zynq_serial_putc(struct udevice *dev, const char ch) +{ + struct zynq_uart_plat *plat = dev_get_plat(dev); + + return _uart_zynq_serial_putc(plat->regs, ch); +} + +static int zynq_serial_pending(struct udevice *dev, bool input) +{ + struct zynq_uart_plat *plat = dev_get_plat(dev); + struct uart_zynq *regs = plat->regs; + + if (input) + return !(readl(®s->channel_sts) & ZYNQ_UART_SR_RXEMPTY); + else + return !!(readl(®s->channel_sts) & ZYNQ_UART_SR_TXACTIVE); +} + +static int zynq_serial_of_to_plat(struct udevice *dev) +{ + struct zynq_uart_plat *plat = dev_get_plat(dev); + + plat->regs = (struct uart_zynq *)dev_read_addr(dev); + if (IS_ERR(plat->regs)) + return PTR_ERR(plat->regs); + + return 0; +} + +static const struct dm_serial_ops zynq_serial_ops = { + .putc = zynq_serial_putc, + .pending = zynq_serial_pending, + .getc = zynq_serial_getc, + .setbrg = zynq_serial_setbrg, +}; + +static const struct udevice_id zynq_serial_ids[] = { + { .compatible = "xlnx,xuartps" }, + { .compatible = "cdns,uart-r1p8" }, + { .compatible = "cdns,uart-r1p12" }, + { } +}; + +U_BOOT_DRIVER(serial_zynq) = { + .name = "serial_zynq", + .id = UCLASS_SERIAL, + .of_match = zynq_serial_ids, + .of_to_plat = zynq_serial_of_to_plat, + .plat_auto = sizeof(struct zynq_uart_plat), + .probe = zynq_serial_probe, + .ops = &zynq_serial_ops, +}; + +#ifdef CONFIG_DEBUG_UART_ZYNQ +static inline void _debug_uart_init(void) +{ + struct uart_zynq *regs = (struct uart_zynq *)CONFIG_DEBUG_UART_BASE; + + _uart_zynq_serial_init(regs); + _uart_zynq_serial_setbrg(regs, CONFIG_DEBUG_UART_CLOCK, + CONFIG_BAUDRATE); +} + +static inline void _debug_uart_putc(int ch) +{ + struct uart_zynq *regs = (struct uart_zynq *)CONFIG_DEBUG_UART_BASE; + + while (_uart_zynq_serial_putc(regs, ch) == -EAGAIN) + WATCHDOG_RESET(); +} + +DEBUG_UART_FUNCS + +#endif |