| // SPDX-License-Identifier: GPL-2.0+ | |
| /* | |
| * Actions Semi OWL SoCs UART driver | |
| * | |
| * Copyright (C) 2015 Actions Semi Co., Ltd. | |
| * Copyright (C) 2018 Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> | |
| */ | |
| #include <clk.h> | |
| #include <dm.h> | |
| #include <errno.h> | |
| #include <fdtdec.h> | |
| #include <serial.h> | |
| #include <asm/io.h> | |
| #include <asm/types.h> | |
| #include <linux/bitops.h> | |
| /* UART Registers */ | |
| #define OWL_UART_CTL (0x0000) | |
| #define OWL_UART_RXDAT (0x0004) | |
| #define OWL_UART_TXDAT (0x0008) | |
| #define OWL_UART_STAT (0x000C) | |
| /* UART_CTL Register Definitions */ | |
| #define OWL_UART_CTL_PRS_NONE GENMASK(6, 4) | |
| #define OWL_UART_CTL_STPS BIT(2) | |
| #define OWL_UART_CTL_DWLS 3 | |
| /* UART_STAT Register Definitions */ | |
| #define OWL_UART_STAT_TFES BIT(10) /* TX FIFO Empty Status */ | |
| #define OWL_UART_STAT_RFFS BIT(9) /* RX FIFO full Status */ | |
| #define OWL_UART_STAT_TFFU BIT(6) /* TX FIFO full Status */ | |
| #define OWL_UART_STAT_RFEM BIT(5) /* RX FIFO Empty Status */ | |
| struct owl_serial_priv { | |
| phys_addr_t base; | |
| }; | |
| int owl_serial_setbrg(struct udevice *dev, int baudrate) | |
| { | |
| /* Driver supports only fixed baudrate */ | |
| return 0; | |
| } | |
| static int owl_serial_getc(struct udevice *dev) | |
| { | |
| struct owl_serial_priv *priv = dev_get_priv(dev); | |
| if (readl(priv->base + OWL_UART_STAT) & OWL_UART_STAT_RFEM) | |
| return -EAGAIN; | |
| return (int)(readl(priv->base + OWL_UART_RXDAT)); | |
| } | |
| static int owl_serial_putc(struct udevice *dev, const char ch) | |
| { | |
| struct owl_serial_priv *priv = dev_get_priv(dev); | |
| if (readl(priv->base + OWL_UART_STAT) & OWL_UART_STAT_TFFU) | |
| return -EAGAIN; | |
| writel(ch, priv->base + OWL_UART_TXDAT); | |
| return 0; | |
| } | |
| static int owl_serial_pending(struct udevice *dev, bool input) | |
| { | |
| struct owl_serial_priv *priv = dev_get_priv(dev); | |
| unsigned int stat = readl(priv->base + OWL_UART_STAT); | |
| if (input) | |
| return !(stat & OWL_UART_STAT_RFEM); | |
| else | |
| return !(stat & OWL_UART_STAT_TFES); | |
| } | |
| static int owl_serial_probe(struct udevice *dev) | |
| { | |
| struct owl_serial_priv *priv = dev_get_priv(dev); | |
| struct clk clk; | |
| u32 uart_ctl; | |
| int ret; | |
| /* Set data, parity and stop bits */ | |
| uart_ctl = readl(priv->base + OWL_UART_CTL); | |
| uart_ctl &= ~(OWL_UART_CTL_PRS_NONE); | |
| uart_ctl &= ~(OWL_UART_CTL_STPS); | |
| uart_ctl |= OWL_UART_CTL_DWLS; | |
| writel(uart_ctl, priv->base + OWL_UART_CTL); | |
| /* Enable UART clock */ | |
| ret = clk_get_by_index(dev, 0, &clk); | |
| if (ret < 0) | |
| return ret; | |
| ret = clk_enable(&clk); | |
| if (ret < 0) | |
| return ret; | |
| return 0; | |
| } | |
| static int owl_serial_of_to_plat(struct udevice *dev) | |
| { | |
| struct owl_serial_priv *priv = dev_get_priv(dev); | |
| priv->base = dev_read_addr(dev); | |
| if (priv->base == FDT_ADDR_T_NONE) | |
| return -EINVAL; | |
| return 0; | |
| } | |
| static const struct dm_serial_ops owl_serial_ops = { | |
| .putc = owl_serial_putc, | |
| .pending = owl_serial_pending, | |
| .getc = owl_serial_getc, | |
| .setbrg = owl_serial_setbrg, | |
| }; | |
| static const struct udevice_id owl_serial_ids[] = { | |
| { .compatible = "actions,owl-uart" }, | |
| { } | |
| }; | |
| U_BOOT_DRIVER(serial_owl) = { | |
| .name = "serial_owl", | |
| .id = UCLASS_SERIAL, | |
| .of_match = owl_serial_ids, | |
| .of_to_plat = owl_serial_of_to_plat, | |
| .priv_auto = sizeof(struct owl_serial_priv), | |
| .probe = owl_serial_probe, | |
| .ops = &owl_serial_ops, | |
| }; |