| // 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 |