1 /*
2  * Copyright (C) 2016 Stefan Roese <sr@denx.de>
3  *
4  * SPDX-License-Identifier:	GPL-2.0+
5  */
6 
7 #include <common.h>
8 #include <dm.h>
9 #include <serial.h>
10 #include <asm/io.h>
11 
12 struct mvebu_platdata {
13 	void __iomem *base;
14 };
15 
16 /*
17  * Register offset
18  */
19 #define UART_RX_REG		0x00
20 #define UART_TX_REG		0x04
21 #define UART_CTRL_REG		0x08
22 #define UART_STATUS_REG		0x0c
23 #define UART_BAUD_REG		0x10
24 #define UART_POSSR_REG		0x14
25 
26 #define UART_STATUS_RX_RDY	0x10
27 #define UART_STATUS_TXFIFO_FULL	0x800
28 
29 #define UART_CTRL_RXFIFO_RESET	0x4000
30 #define UART_CTRL_TXFIFO_RESET	0x8000
31 
32 #define CONFIG_UART_BASE_CLOCK	25804800
33 
34 static int mvebu_serial_putc(struct udevice *dev, const char ch)
35 {
36 	struct mvebu_platdata *plat = dev_get_platdata(dev);
37 	void __iomem *base = plat->base;
38 
39 	while (readl(base + UART_STATUS_REG) & UART_STATUS_TXFIFO_FULL)
40 		;
41 
42 	writel(ch, base + UART_TX_REG);
43 
44 	return 0;
45 }
46 
47 static int mvebu_serial_getc(struct udevice *dev)
48 {
49 	struct mvebu_platdata *plat = dev_get_platdata(dev);
50 	void __iomem *base = plat->base;
51 
52 	while (!(readl(base + UART_STATUS_REG) & UART_STATUS_RX_RDY))
53 		;
54 
55 	return readl(base + UART_RX_REG) & 0xff;
56 }
57 
58 static int mvebu_serial_pending(struct udevice *dev, bool input)
59 {
60 	struct mvebu_platdata *plat = dev_get_platdata(dev);
61 	void __iomem *base = plat->base;
62 
63 	if (readl(base + UART_STATUS_REG) & UART_STATUS_RX_RDY)
64 		return 1;
65 
66 	return 0;
67 }
68 
69 static int mvebu_serial_setbrg(struct udevice *dev, int baudrate)
70 {
71 	struct mvebu_platdata *plat = dev_get_platdata(dev);
72 	void __iomem *base = plat->base;
73 
74 	/*
75 	 * Calculate divider
76 	 * baudrate = clock / 16 / divider
77 	 */
78 	writel(CONFIG_UART_BASE_CLOCK / baudrate / 16, base + UART_BAUD_REG);
79 
80 	/*
81 	 * Set Programmable Oversampling Stack to 0,
82 	 * UART defaults to 16x scheme
83 	 */
84 	writel(0, base + UART_POSSR_REG);
85 
86 	return 0;
87 }
88 
89 static int mvebu_serial_probe(struct udevice *dev)
90 {
91 	struct mvebu_platdata *plat = dev_get_platdata(dev);
92 	void __iomem *base = plat->base;
93 
94 	/* reset FIFOs */
95 	writel(UART_CTRL_RXFIFO_RESET | UART_CTRL_TXFIFO_RESET,
96 	       base + UART_CTRL_REG);
97 
98 	/* No Parity, 1 Stop */
99 	writel(0, base + UART_CTRL_REG);
100 
101 	return 0;
102 }
103 
104 static int mvebu_serial_ofdata_to_platdata(struct udevice *dev)
105 {
106 	struct mvebu_platdata *plat = dev_get_platdata(dev);
107 
108 	plat->base = dev_get_addr_ptr(dev);
109 
110 	return 0;
111 }
112 
113 static const struct dm_serial_ops mvebu_serial_ops = {
114 	.putc = mvebu_serial_putc,
115 	.pending = mvebu_serial_pending,
116 	.getc = mvebu_serial_getc,
117 	.setbrg = mvebu_serial_setbrg,
118 };
119 
120 static const struct udevice_id mvebu_serial_ids[] = {
121 	{ .compatible = "marvell,armada-3700-uart" },
122 	{ }
123 };
124 
125 U_BOOT_DRIVER(serial_mvebu) = {
126 	.name	= "serial_mvebu",
127 	.id	= UCLASS_SERIAL,
128 	.of_match = mvebu_serial_ids,
129 	.ofdata_to_platdata = mvebu_serial_ofdata_to_platdata,
130 	.platdata_auto_alloc_size = sizeof(struct mvebu_platdata),
131 	.probe	= mvebu_serial_probe,
132 	.ops	= &mvebu_serial_ops,
133 	.flags	= DM_FLAG_PRE_RELOC,
134 };
135 
136 #ifdef CONFIG_DEBUG_MVEBU_A3700_UART
137 
138 #include <debug_uart.h>
139 
140 static inline void _debug_uart_init(void)
141 {
142 	void __iomem *base = (void __iomem *)CONFIG_DEBUG_UART_BASE;
143 
144 	/* reset FIFOs */
145 	writel(UART_CTRL_RXFIFO_RESET | UART_CTRL_TXFIFO_RESET,
146 	       base + UART_CTRL_REG);
147 
148 	/* No Parity, 1 Stop */
149 	writel(0, base + UART_CTRL_REG);
150 
151 	/*
152 	 * Calculate divider
153 	 * baudrate = clock / 16 / divider
154 	 */
155 	writel(CONFIG_UART_BASE_CLOCK / 115200 / 16, base + UART_BAUD_REG);
156 
157 	/*
158 	 * Set Programmable Oversampling Stack to 0,
159 	 * UART defaults to 16x scheme
160 	 */
161 	writel(0, base + UART_POSSR_REG);
162 }
163 
164 static inline void _debug_uart_putc(int ch)
165 {
166 	void __iomem *base = (void __iomem *)CONFIG_DEBUG_UART_BASE;
167 
168 	while (readl(base + UART_STATUS_REG) & UART_STATUS_TXFIFO_FULL)
169 		;
170 
171 	writel(ch, base + UART_TX_REG);
172 }
173 
174 DEBUG_UART_FUNCS
175 #endif
176