1 /*
2 * xen/drivers/char/mvebu3700-uart.c
3 *
4 * Driver for Marvell MVEBU UART.
5 *
6 * Copyright (c) 2018, Amit Singh Tomar <amittomer25@gmail.com>.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms and conditions of the GNU General Public
10 * License, version 2, as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public
18 * License along with this program; If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <xen/irq.h>
22 #include <xen/serial.h>
23 #include <xen/vmap.h>
24 #include <asm/io.h>
25
26 /* Register offsets */
27 #define UART_RX_REG 0x00
28
29 #define UART_TX_REG 0x04
30
31 #define UART_CTRL_REG 0x08
32 #define CTRL_TXFIFO_RST BIT(15, UL)
33 #define CTRL_RXFIFO_RST BIT(14, UL)
34 #define CTRL_TX_RDY_INT BIT(5, UL)
35 #define CTRL_RX_RDY_INT BIT(4, UL)
36 #define CTRL_BRK_DET_INT BIT(3, UL)
37 #define CTRL_FRM_ERR_INT BIT(2, UL)
38 #define CTRL_PAR_ERR_INT BIT(1, UL)
39 #define CTRL_OVR_ERR_INT BIT(0, UL)
40 #define CTRL_ERR_INT (CTRL_BRK_DET_INT | CTRL_FRM_ERR_INT | \
41 CTRL_PAR_ERR_INT | CTRL_OVR_ERR_INT)
42
43 #define UART_STATUS_REG 0x0c
44 #define STATUS_TXFIFO_EMP BIT(13, UL)
45 #define STATUS_TXFIFO_FUL BIT(11, UL)
46 #define STATUS_TXFIFO_HFL BIT(10, UL)
47 #define STATUS_TX_RDY BIT(5, UL)
48 #define STATUS_RX_RDY BIT(4, UL)
49 #define STATUS_BRK_DET BIT(3, UL)
50 #define STATUS_FRM_ERR BIT(2, UL)
51 #define STATUS_PAR_ERR BIT(1, UL)
52 #define STATUS_OVR_ERR BIT(0, UL)
53 #define STATUS_BRK_ERR (STATUS_BRK_DET | STATUS_FRM_ERR | \
54 STATUS_PAR_ERR | STATUS_OVR_ERR)
55
56 #define TX_FIFO_SIZE 32
57
58 static struct mvebu3700_uart {
59 unsigned int irq;
60 void __iomem *regs;
61 struct irqaction irqaction;
62 struct vuart_info vuart;
63 } mvebu3700_com = {0};
64
65 #define mvebu3700_read(uart, off) readl((uart)->regs + off)
66 #define mvebu3700_write(uart, off, val) writel(val, (uart->regs) + off)
67
mvebu3700_uart_interrupt(int irq,void * data,struct cpu_user_regs * regs)68 static void mvebu3700_uart_interrupt(int irq, void *data,
69 struct cpu_user_regs *regs)
70 {
71 struct serial_port *port = data;
72 struct mvebu3700_uart *uart = port->uart;
73 uint32_t st = mvebu3700_read(uart, UART_STATUS_REG);
74
75 if ( st & (STATUS_RX_RDY | STATUS_OVR_ERR | STATUS_FRM_ERR |
76 STATUS_BRK_DET) )
77 serial_rx_interrupt(port, regs);
78
79 if ( st & STATUS_TX_RDY )
80 serial_tx_interrupt(port, regs);
81 }
82
mvebu3700_uart_init_preirq(struct serial_port * port)83 static void __init mvebu3700_uart_init_preirq(struct serial_port *port)
84 {
85 struct mvebu3700_uart *uart = port->uart;
86 uint32_t reg;
87
88 reg = mvebu3700_read(uart, UART_CTRL_REG);
89 reg |= (CTRL_TXFIFO_RST | CTRL_RXFIFO_RST);
90 mvebu3700_write(uart, UART_CTRL_REG, reg);
91
92 /* Before we make IRQ request, clear the error bits of state register. */
93 reg = mvebu3700_read(uart, UART_STATUS_REG);
94 reg |= STATUS_BRK_ERR;
95 mvebu3700_write(uart, UART_STATUS_REG, reg);
96
97 /* Clear error interrupts. */
98 mvebu3700_write(uart, UART_CTRL_REG, CTRL_ERR_INT);
99
100 /* Disable Rx/Tx interrupts. */
101 reg = mvebu3700_read(uart, UART_CTRL_REG);
102 reg &= ~(CTRL_RX_RDY_INT | CTRL_TX_RDY_INT);
103 mvebu3700_write(uart, UART_CTRL_REG, reg);
104 }
105
mvebu3700_uart_init_postirq(struct serial_port * port)106 static void __init mvebu3700_uart_init_postirq(struct serial_port *port)
107 {
108 struct mvebu3700_uart *uart = port->uart;
109 uint32_t reg;
110
111 uart->irqaction.handler = mvebu3700_uart_interrupt;
112 uart->irqaction.name = "mvebu3700_uart";
113 uart->irqaction.dev_id = port;
114
115 if ( setup_irq(uart->irq, 0, &uart->irqaction) != 0 )
116 {
117 printk("Failed to allocated mvebu3700_uart IRQ %d\n", uart->irq);
118 return;
119 }
120
121 /* Make sure Rx/Tx interrupts are enabled now */
122 reg = mvebu3700_read(uart, UART_CTRL_REG);
123 reg |= (CTRL_RX_RDY_INT | CTRL_TX_RDY_INT);
124 mvebu3700_write(uart, UART_CTRL_REG, reg);
125 }
126
mvebu3700_uart_suspend(struct serial_port * port)127 static void mvebu3700_uart_suspend(struct serial_port *port)
128 {
129 BUG();
130 }
131
mvebu3700_uart_resume(struct serial_port * port)132 static void mvebu3700_uart_resume(struct serial_port *port)
133 {
134 BUG();
135 }
136
mvebu3700_uart_putc(struct serial_port * port,char c)137 static void mvebu3700_uart_putc(struct serial_port *port, char c)
138 {
139 struct mvebu3700_uart *uart = port->uart;
140
141 mvebu3700_write(uart, UART_TX_REG, c);
142 }
143
mvebu3700_uart_getc(struct serial_port * port,char * c)144 static int mvebu3700_uart_getc(struct serial_port *port, char *c)
145 {
146 struct mvebu3700_uart *uart = port->uart;
147
148 if ( !(mvebu3700_read(uart, UART_STATUS_REG) & STATUS_RX_RDY) )
149 return 0;
150
151 *c = mvebu3700_read(uart, UART_RX_REG) & 0xff;
152
153 return 1;
154 }
155
mvebu3700_irq(struct serial_port * port)156 static int __init mvebu3700_irq(struct serial_port *port)
157 {
158 struct mvebu3700_uart *uart = port->uart;
159
160 return uart->irq;
161 }
162
mvebu3700_vuart_info(struct serial_port * port)163 static const struct vuart_info *mvebu3700_vuart_info(struct serial_port *port)
164 {
165 struct mvebu3700_uart *uart = port->uart;
166
167 return &uart->vuart;
168 }
169
mvebu3700_uart_stop_tx(struct serial_port * port)170 static void mvebu3700_uart_stop_tx(struct serial_port *port)
171 {
172 struct mvebu3700_uart *uart = port->uart;
173 uint32_t reg;
174
175 reg = mvebu3700_read(uart, UART_CTRL_REG);
176 reg &= ~CTRL_TX_RDY_INT;
177 mvebu3700_write(uart, UART_CTRL_REG, reg);
178 }
179
mvebu3700_uart_start_tx(struct serial_port * port)180 static void mvebu3700_uart_start_tx(struct serial_port *port)
181 {
182 struct mvebu3700_uart *uart = port->uart;
183 uint32_t reg;
184
185 reg = mvebu3700_read(uart, UART_CTRL_REG);
186 reg |= CTRL_TX_RDY_INT;
187 mvebu3700_write(uart, UART_CTRL_REG, reg);
188 }
189
mvebu3700_uart_tx_ready(struct serial_port * port)190 static int mvebu3700_uart_tx_ready(struct serial_port *port)
191 {
192 struct mvebu3700_uart *uart = port->uart;
193 uint32_t reg;
194
195 reg = mvebu3700_read(uart, UART_STATUS_REG);
196
197 if ( reg & STATUS_TXFIFO_EMP )
198 return TX_FIFO_SIZE;
199 if ( reg & STATUS_TXFIFO_FUL )
200 return 0;
201 if ( reg & STATUS_TXFIFO_HFL )
202 return TX_FIFO_SIZE / 2;
203
204 /*
205 * If we reach here, we don't know the number of free char in FIFO
206 * but we are sure that neither the FIFO is full nor empty.
207 * So, let's just return at least 1.
208 */
209 return 1;
210 }
211
212 static struct uart_driver __read_mostly mvebu3700_uart_driver = {
213 .init_preirq = mvebu3700_uart_init_preirq,
214 .init_postirq = mvebu3700_uart_init_postirq,
215 .endboot = NULL,
216 .suspend = mvebu3700_uart_suspend,
217 .resume = mvebu3700_uart_resume,
218 .putc = mvebu3700_uart_putc,
219 .getc = mvebu3700_uart_getc,
220 .tx_ready = mvebu3700_uart_tx_ready,
221 .stop_tx = mvebu3700_uart_stop_tx,
222 .start_tx = mvebu3700_uart_start_tx,
223 .irq = mvebu3700_irq,
224 .vuart_info = mvebu3700_vuart_info,
225 };
226
mvebu_uart_init(struct dt_device_node * dev,const void * data)227 static int __init mvebu_uart_init(struct dt_device_node *dev, const void *data)
228 {
229 const char *config = data;
230 struct mvebu3700_uart *uart;
231 int res;
232 u64 addr, size;
233
234 if ( strcmp(config, "") )
235 printk("WARNING: UART configuration is not supported\n");
236
237 uart = &mvebu3700_com;
238
239 res = dt_device_get_address(dev, 0, &addr, &size);
240 if ( res )
241 {
242 printk("mvebu3700: Unable to retrieve the base address of the UART\n");
243 return res;
244 }
245
246 res = platform_get_irq(dev, 0);
247 if ( res < 0 )
248 {
249 printk("mvebu3700: Unable to retrieve the IRQ\n");
250 return -EINVAL;
251 }
252
253 uart->irq = res;
254
255 uart->regs = ioremap_nocache(addr, size);
256 if ( !uart->regs )
257 {
258 printk("mvebu3700: Unable to map the UART memory\n");
259 return -ENOMEM;
260 }
261
262 uart->vuart.base_addr = addr;
263 uart->vuart.size = size;
264 uart->vuart.data_off = UART_CTRL_REG;
265 uart->vuart.status_off = UART_STATUS_REG;
266 uart->vuart.status = STATUS_TX_RDY | STATUS_RX_RDY;
267
268 /* Register with generic serial driver. */
269 serial_register_uart(SERHND_DTUART, &mvebu3700_uart_driver, uart);
270
271 dt_device_set_used_by(dev, DOMID_XEN);
272
273 return 0;
274 }
275
276 static const struct dt_device_match mvebu_dt_match[] __initconst =
277 {
278 DT_MATCH_COMPATIBLE("marvell,armada-3700-uart"),
279 { /* sentinel */ },
280 };
281
282 DT_DEVICE_START(mvebu, "Marvell Armada-3700 UART", DEVICE_SERIAL)
283 .dt_match = mvebu_dt_match,
284 .init = mvebu_uart_init,
285 DT_DEVICE_END
286
287 /*
288 * Local variables:
289 * mode: C
290 * c-file-style: "BSD"
291 * c-basic-offset: 4
292 * indent-tabs-mode: nil
293 * End:
294 */
295