mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-24 21:54:01 +00:00
STiH410: Add STi serial driver
This patch adds support to ASC (asynchronous serial controller) driver, which is basically a standard serial driver. This IP is common across other STMicroelectronics SoCs Signed-off-by: Patrice Chotard <patrice.chotard@st.com> Reviewed-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
parent
413788cef5
commit
214a17e61d
4 changed files with 222 additions and 0 deletions
|
@ -1096,6 +1096,8 @@ config STM32
|
|||
config ARCH_STI
|
||||
bool "Support STMicrolectronics SoCs"
|
||||
select CPU_V7
|
||||
select DM
|
||||
select DM_SERIAL
|
||||
help
|
||||
Support for STMicroelectronics STiH407/10 SoC family.
|
||||
This SoC is used on Linaro 96Board STiH410-B2260
|
||||
|
|
|
@ -413,4 +413,12 @@ config PXA_SERIAL
|
|||
If you have a machine based on a Marvell XScale PXA2xx CPU you
|
||||
can enable its onboard serial ports by enabling this option.
|
||||
|
||||
config STI_ASC_SERIAL
|
||||
bool "STMicroelectronics on-chip UART"
|
||||
depends on DM_SERIAL && ARCH_STI
|
||||
help
|
||||
Select this to enable Asynchronous Serial Controller available
|
||||
on STiH410 SoC. This is a basic implementation, it supports
|
||||
following baudrate 9600, 19200, 38400, 57600 and 115200.
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -41,6 +41,7 @@ obj-$(CONFIG_FSL_LINFLEXUART) += serial_linflexuart.o
|
|||
obj-$(CONFIG_ARC_SERIAL) += serial_arc.o
|
||||
obj-$(CONFIG_UNIPHIER_SERIAL) += serial_uniphier.o
|
||||
obj-$(CONFIG_STM32_SERIAL) += serial_stm32.o
|
||||
obj-$(CONFIG_STI_ASC_SERIAL) += serial_sti_asc.o
|
||||
obj-$(CONFIG_PIC32_SERIAL) += serial_pic32.o
|
||||
obj-$(CONFIG_STM32X7_SERIAL) += serial_stm32x7.o
|
||||
obj-$(CONFIG_BCM283X_MU_SERIAL) += serial_bcm283x_mu.o
|
||||
|
|
211
drivers/serial/serial_sti_asc.c
Normal file
211
drivers/serial/serial_sti_asc.c
Normal file
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* Support for Serial I/O using STMicroelectronics' on-chip ASC.
|
||||
*
|
||||
* Copyright (c) 2017
|
||||
* Patrice Chotard <patrice.chotard@st.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#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 = dev_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,
|
||||
};
|
||||
|
Loading…
Reference in a new issue