1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2004, Psyent Corporation <www.psyent.com>
4  * Scott McNutt <smcnutt@psyent.com>
5  */
6 
7 #include <dm.h>
8 #include <errno.h>
9 #include <serial.h>
10 #include <asm/io.h>
11 #include <linux/bitops.h>
12 
13 /* status register */
14 #define ALTERA_UART_TMT		BIT(5)	/* tx empty */
15 #define ALTERA_UART_TRDY	BIT(6)	/* tx ready */
16 #define ALTERA_UART_RRDY	BIT(7)	/* rx ready */
17 
18 struct altera_uart_regs {
19 	u32	rxdata;		/* Rx data reg */
20 	u32	txdata;		/* Tx data reg */
21 	u32	status;		/* Status reg */
22 	u32	control;	/* Control reg */
23 	u32	divisor;	/* Baud rate divisor reg */
24 	u32	endofpacket;	/* End-of-packet reg */
25 };
26 
27 struct altera_uart_plat {
28 	struct altera_uart_regs *regs;
29 	unsigned int uartclk;
30 };
31 
altera_uart_setbrg(struct udevice * dev,int baudrate)32 static int altera_uart_setbrg(struct udevice *dev, int baudrate)
33 {
34 	struct altera_uart_plat *plat = dev_get_plat(dev);
35 	struct altera_uart_regs *const regs = plat->regs;
36 	u32 div;
37 
38 	div = (plat->uartclk / baudrate) - 1;
39 	writel(div, &regs->divisor);
40 
41 	return 0;
42 }
43 
altera_uart_putc(struct udevice * dev,const char ch)44 static int altera_uart_putc(struct udevice *dev, const char ch)
45 {
46 	struct altera_uart_plat *plat = dev_get_plat(dev);
47 	struct altera_uart_regs *const regs = plat->regs;
48 
49 	if (!(readl(&regs->status) & ALTERA_UART_TRDY))
50 		return -EAGAIN;
51 
52 	writel(ch, &regs->txdata);
53 
54 	return 0;
55 }
56 
altera_uart_pending(struct udevice * dev,bool input)57 static int altera_uart_pending(struct udevice *dev, bool input)
58 {
59 	struct altera_uart_plat *plat = dev_get_plat(dev);
60 	struct altera_uart_regs *const regs = plat->regs;
61 	u32 st = readl(&regs->status);
62 
63 	if (input)
64 		return st & ALTERA_UART_RRDY ? 1 : 0;
65 	else
66 		return !(st & ALTERA_UART_TMT);
67 }
68 
altera_uart_getc(struct udevice * dev)69 static int altera_uart_getc(struct udevice *dev)
70 {
71 	struct altera_uart_plat *plat = dev_get_plat(dev);
72 	struct altera_uart_regs *const regs = plat->regs;
73 
74 	if (!(readl(&regs->status) & ALTERA_UART_RRDY))
75 		return -EAGAIN;
76 
77 	return readl(&regs->rxdata) & 0xff;
78 }
79 
altera_uart_probe(struct udevice * dev)80 static int altera_uart_probe(struct udevice *dev)
81 {
82 	return 0;
83 }
84 
altera_uart_of_to_plat(struct udevice * dev)85 static int altera_uart_of_to_plat(struct udevice *dev)
86 {
87 	struct altera_uart_plat *plat = dev_get_plat(dev);
88 
89 	plat->regs = map_physmem(dev_read_addr(dev),
90 				 sizeof(struct altera_uart_regs),
91 				 MAP_NOCACHE);
92 	plat->uartclk = dev_read_u32_default(dev, "clock-frequency", 0);
93 
94 	return 0;
95 }
96 
97 static const struct dm_serial_ops altera_uart_ops = {
98 	.putc = altera_uart_putc,
99 	.pending = altera_uart_pending,
100 	.getc = altera_uart_getc,
101 	.setbrg = altera_uart_setbrg,
102 };
103 
104 static const struct udevice_id altera_uart_ids[] = {
105 	{ .compatible = "altr,uart-1.0" },
106 	{}
107 };
108 
109 U_BOOT_DRIVER(altera_uart) = {
110 	.name	= "altera_uart",
111 	.id	= UCLASS_SERIAL,
112 	.of_match = altera_uart_ids,
113 	.of_to_plat = altera_uart_of_to_plat,
114 	.plat_auto	= sizeof(struct altera_uart_plat),
115 	.probe = altera_uart_probe,
116 	.ops	= &altera_uart_ops,
117 };
118 
119 #ifdef CONFIG_DEBUG_UART_ALTERA_UART
120 
121 #include <debug_uart.h>
122 
_debug_uart_init(void)123 static inline void _debug_uart_init(void)
124 {
125 	struct altera_uart_regs *regs = (void *)CONFIG_VAL(DEBUG_UART_BASE);
126 	u32 div;
127 
128 	div = (CONFIG_DEBUG_UART_CLOCK / CONFIG_BAUDRATE) - 1;
129 	writel(div, &regs->divisor);
130 }
131 
_debug_uart_putc(int ch)132 static inline void _debug_uart_putc(int ch)
133 {
134 	struct altera_uart_regs *regs = (void *)CONFIG_VAL(DEBUG_UART_BASE);
135 
136 	while (1) {
137 		u32 st = readl(&regs->status);
138 
139 		if (st & ALTERA_UART_TRDY)
140 			break;
141 	}
142 
143 	writel(ch, &regs->txdata);
144 }
145 
146 DEBUG_UART_FUNCS
147 
148 #endif
149