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