| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * (C) Copyright 2004, Psyent Corporation <www.psyent.com> |
| * Scott McNutt <smcnutt@psyent.com> |
| */ |
| |
| #include <dm.h> |
| #include <errno.h> |
| #include <serial.h> |
| #include <asm/io.h> |
| #include <linux/bitops.h> |
| |
| /* status register */ |
| #define ALTERA_UART_TMT BIT(5) /* tx empty */ |
| #define ALTERA_UART_TRDY BIT(6) /* tx ready */ |
| #define ALTERA_UART_RRDY BIT(7) /* rx ready */ |
| |
| struct altera_uart_regs { |
| u32 rxdata; /* Rx data reg */ |
| u32 txdata; /* Tx data reg */ |
| u32 status; /* Status reg */ |
| u32 control; /* Control reg */ |
| u32 divisor; /* Baud rate divisor reg */ |
| u32 endofpacket; /* End-of-packet reg */ |
| }; |
| |
| struct altera_uart_plat { |
| struct altera_uart_regs *regs; |
| unsigned int uartclk; |
| }; |
| |
| static int altera_uart_setbrg(struct udevice *dev, int baudrate) |
| { |
| struct altera_uart_plat *plat = dev_get_plat(dev); |
| struct altera_uart_regs *const regs = plat->regs; |
| u32 div; |
| |
| div = (plat->uartclk / baudrate) - 1; |
| writel(div, ®s->divisor); |
| |
| return 0; |
| } |
| |
| static int altera_uart_putc(struct udevice *dev, const char ch) |
| { |
| struct altera_uart_plat *plat = dev_get_plat(dev); |
| struct altera_uart_regs *const regs = plat->regs; |
| |
| if (!(readl(®s->status) & ALTERA_UART_TRDY)) |
| return -EAGAIN; |
| |
| writel(ch, ®s->txdata); |
| |
| return 0; |
| } |
| |
| static int altera_uart_pending(struct udevice *dev, bool input) |
| { |
| struct altera_uart_plat *plat = dev_get_plat(dev); |
| struct altera_uart_regs *const regs = plat->regs; |
| u32 st = readl(®s->status); |
| |
| if (input) |
| return st & ALTERA_UART_RRDY ? 1 : 0; |
| else |
| return !(st & ALTERA_UART_TMT); |
| } |
| |
| static int altera_uart_getc(struct udevice *dev) |
| { |
| struct altera_uart_plat *plat = dev_get_plat(dev); |
| struct altera_uart_regs *const regs = plat->regs; |
| |
| if (!(readl(®s->status) & ALTERA_UART_RRDY)) |
| return -EAGAIN; |
| |
| return readl(®s->rxdata) & 0xff; |
| } |
| |
| static int altera_uart_probe(struct udevice *dev) |
| { |
| return 0; |
| } |
| |
| static int altera_uart_of_to_plat(struct udevice *dev) |
| { |
| struct altera_uart_plat *plat = dev_get_plat(dev); |
| |
| plat->regs = map_physmem(dev_read_addr(dev), |
| sizeof(struct altera_uart_regs), |
| MAP_NOCACHE); |
| plat->uartclk = dev_read_u32_default(dev, "clock-frequency", 0); |
| |
| return 0; |
| } |
| |
| static const struct dm_serial_ops altera_uart_ops = { |
| .putc = altera_uart_putc, |
| .pending = altera_uart_pending, |
| .getc = altera_uart_getc, |
| .setbrg = altera_uart_setbrg, |
| }; |
| |
| static const struct udevice_id altera_uart_ids[] = { |
| { .compatible = "altr,uart-1.0" }, |
| {} |
| }; |
| |
| U_BOOT_DRIVER(altera_uart) = { |
| .name = "altera_uart", |
| .id = UCLASS_SERIAL, |
| .of_match = altera_uart_ids, |
| .of_to_plat = altera_uart_of_to_plat, |
| .plat_auto = sizeof(struct altera_uart_plat), |
| .probe = altera_uart_probe, |
| .ops = &altera_uart_ops, |
| }; |
| |
| #ifdef CONFIG_DEBUG_UART_ALTERA_UART |
| |
| #include <debug_uart.h> |
| |
| static inline void _debug_uart_init(void) |
| { |
| struct altera_uart_regs *regs = (void *)CONFIG_VAL(DEBUG_UART_BASE); |
| u32 div; |
| |
| div = (CONFIG_DEBUG_UART_CLOCK / CONFIG_BAUDRATE) - 1; |
| writel(div, ®s->divisor); |
| } |
| |
| static inline void _debug_uart_putc(int ch) |
| { |
| struct altera_uart_regs *regs = (void *)CONFIG_VAL(DEBUG_UART_BASE); |
| |
| while (1) { |
| u32 st = readl(®s->status); |
| |
| if (st & ALTERA_UART_TRDY) |
| break; |
| } |
| |
| writel(ch, ®s->txdata); |
| } |
| |
| DEBUG_UART_FUNCS |
| |
| #endif |