// SPDX-License-Identifier: GPL-2.0+ /* * Support for Serial I/O using STMicroelectronics' on-chip ASC. * * Copyright (C) 2017, STMicroelectronics - All Rights Reserved * Author(s): Patrice Chotard, <patrice.chotard@st.com> for STMicroelectronics. */ #include <common.h> #include <dm.h> #include <serial.h> #include <asm/io.h> DECLARE_GLOBAL_DATA_PTR; #define BAUDMODE 0x00001000 #define RXENABLE 0x00000100 #define RUN 0x00000080 #define MODE 0x00000001 #define MODE_8BIT 0x0001 #define STOP_1BIT 0x0008 #define PARITYODD 0x0020 #define STA_TF BIT(9) #define STA_RBF BIT(0) struct sti_asc_uart { u32 baudrate; u32 txbuf; u32 rxbuf; u32 control; u32 inten; u32 status; u32 guardtime; u32 timeout; u32 txreset; u32 rxreset; }; struct sti_asc_serial { /* address of registers in physical memory */ struct sti_asc_uart *regs; }; /* Values for the BAUDRATE Register */ #define PCLK (200ul * 1000000ul) #define BAUDRATE_VAL_M0(bps) (PCLK / (16 * (bps))) #define BAUDRATE_VAL_M1(bps) ((bps * (1 << 14)) + (1<<13)) / (PCLK/(1 << 6)) /* * MODE 0 * ICCLK * ASCBaudRate = ---------------- * baudrate * 16 * * MODE 1 * baudrate * 16 * 2^16 * ASCBaudRate = ------------------------ * ICCLK * * NOTE: * Mode 1 should be used for baudrates of 19200, and above, as it * has a lower deviation error than Mode 0 for higher frequencies. * Mode 0 should be used for all baudrates below 19200. */ static int sti_asc_pending(struct udevice *dev, bool input) { struct sti_asc_serial *priv = dev_get_priv(dev); struct sti_asc_uart *const uart = priv->regs; unsigned long status; status = readl(&uart->status); if (input) return status & STA_RBF; else return status & STA_TF; } static int _sti_asc_serial_setbrg(struct sti_asc_uart *uart, int baudrate) { unsigned long val; int t, mode = 1; switch (baudrate) { case 9600: t = BAUDRATE_VAL_M0(9600); mode = 0; break; case 19200: t = BAUDRATE_VAL_M1(19200); break; case 38400: t = BAUDRATE_VAL_M1(38400); break; case 57600: t = BAUDRATE_VAL_M1(57600); break; default: debug("ASC: unsupported baud rate: %d, using 115200 instead.\n", baudrate); case 115200: t = BAUDRATE_VAL_M1(115200); break; } /* disable the baudrate generator */ val = readl(&uart->control); writel(val & ~RUN, &uart->control); /* set baud generator reload value */ writel(t, &uart->baudrate); /* reset the RX & TX buffers */ writel(1, &uart->txreset); writel(1, &uart->rxreset); /* set baud generator mode */ if (mode) val |= BAUDMODE; /* finally, write value and enable ASC */ writel(val, &uart->control); return 0; } /* called to adjust baud-rate */ static int sti_asc_serial_setbrg(struct udevice *dev, int baudrate) { struct sti_asc_serial *priv = dev_get_priv(dev); struct sti_asc_uart *const uart = priv->regs; return _sti_asc_serial_setbrg(uart, baudrate); } /* blocking function, that returns next char */ static int sti_asc_serial_getc(struct udevice *dev) { struct sti_asc_serial *priv = dev_get_priv(dev); struct sti_asc_uart *const uart = priv->regs; /* polling wait: for a char to be read */ if (!sti_asc_pending(dev, true)) return -EAGAIN; return readl(&uart->rxbuf); } /* write write out a single char */ static int sti_asc_serial_putc(struct udevice *dev, const char c) { struct sti_asc_serial *priv = dev_get_priv(dev); struct sti_asc_uart *const uart = priv->regs; /* wait till safe to write next char */ if (sti_asc_pending(dev, false)) return -EAGAIN; /* finally, write next char */ writel(c, &uart->txbuf); return 0; } /* initialize the ASC */ static int sti_asc_serial_probe(struct udevice *dev) { struct sti_asc_serial *priv = dev_get_priv(dev); unsigned long val; fdt_addr_t base; base = devfdt_get_addr(dev); if (base == FDT_ADDR_T_NONE) return -EINVAL; priv->regs = (struct sti_asc_uart *)base; sti_asc_serial_setbrg(dev, gd->baudrate); /* * build up the value to be written to CONTROL * set character length, bit stop number, odd parity */ val = RXENABLE | RUN | MODE_8BIT | STOP_1BIT | PARITYODD; writel(val, &priv->regs->control); return 0; } static const struct dm_serial_ops sti_asc_serial_ops = { .putc = sti_asc_serial_putc, .pending = sti_asc_pending, .getc = sti_asc_serial_getc, .setbrg = sti_asc_serial_setbrg, }; static const struct udevice_id sti_serial_of_match[] = { { .compatible = "st,asc" }, { } }; U_BOOT_DRIVER(serial_sti_asc) = { .name = "serial_sti_asc", .id = UCLASS_SERIAL, .of_match = sti_serial_of_match, .ops = &sti_asc_serial_ops, .probe = sti_asc_serial_probe, .priv_auto_alloc_size = sizeof(struct sti_asc_serial), .flags = DM_FLAG_PRE_RELOC, };