| // 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, |
| }; |