1 /*
2  * xen/drivers/char/exynos4210-uart.c
3  *
4  * Driver for Exynos 4210 UART.
5  *
6  * Anthony PERARD <anthony.perard@citrix.com>
7  * Copyright (c) 2012 Citrix Systems.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  */
19 
20 #include <xen/console.h>
21 #include <xen/errno.h>
22 #include <xen/serial.h>
23 #include <xen/init.h>
24 #include <xen/irq.h>
25 #include <xen/mm.h>
26 #include <asm/device.h>
27 #include <asm/exynos4210-uart.h>
28 #include <asm/io.h>
29 
30 static struct exynos4210_uart {
31     unsigned int baud, clock_hz, data_bits, parity, stop_bits;
32     unsigned int irq;
33     void *regs;
34     struct irqaction irqaction;
35     struct vuart_info vuart;
36 } exynos4210_com = {0};
37 
38 /* These parity settings can be ORed directly into the ULCON. */
39 #define PARITY_NONE  (0)
40 #define PARITY_ODD   (0x4)
41 #define PARITY_EVEN  (0x5)
42 #define FORCED_CHECKED_AS_ONE (0x6)
43 #define FORCED_CHECKED_AS_ZERO (0x7)
44 
45 #define exynos4210_read(uart, off)          readl((uart)->regs + off)
46 #define exynos4210_write(uart, off, val)    writel(val, (uart->regs) + off)
47 
exynos4210_uart_interrupt(int irq,void * data,struct cpu_user_regs * regs)48 static void exynos4210_uart_interrupt(int irq, void *data, struct cpu_user_regs *regs)
49 {
50     struct serial_port *port = data;
51     struct exynos4210_uart *uart = port->uart;
52     unsigned int status;
53 
54     status = exynos4210_read(uart, UINTP);
55 
56     while ( status != 0 )
57     {
58         /* Clear all pending interrupts
59          * but should take care of ERROR and MODEM
60          */
61 
62         if ( status & UINTM_ERROR )
63         {
64             uint32_t error_bit;
65 
66             error_bit = exynos4210_read(uart, UERSTAT);
67 
68             if ( error_bit & UERSTAT_OVERRUN )
69                 dprintk(XENLOG_ERR, "uart: overrun error\n");
70             if ( error_bit & UERSTAT_PARITY )
71                 dprintk(XENLOG_ERR, "uart: parity error\n");
72             if ( error_bit & UERSTAT_FRAME )
73                 dprintk(XENLOG_ERR, "uart: frame error\n");
74             if ( error_bit & UERSTAT_BREAK )
75                 dprintk(XENLOG_ERR, "uart: break detected\n");
76             /* Clear error pending interrupt */
77             exynos4210_write(uart, UINTP, UINTM_ERROR);
78         }
79 
80 
81         if ( status & (UINTM_RXD | UINTM_ERROR) )
82         {
83             /* uart->regs[UINTM] |= RXD|ERROR; */
84             serial_rx_interrupt(port, regs);
85             /* uart->regs[UINTM] &= ~(RXD|ERROR); */
86             exynos4210_write(uart, UINTP, UINTM_RXD | UINTM_ERROR);
87         }
88 
89         if ( status & (UINTM_TXD | UINTM_MODEM) )
90         {
91             /* uart->regs[UINTM] |= TXD|MODEM; */
92             serial_tx_interrupt(port, regs);
93             /* uart->regs[UINTM] &= ~(TXD|MODEM); */
94             exynos4210_write(uart, UINTP, UINTM_TXD | UINTM_MODEM);
95         }
96 
97         status = exynos4210_read(uart, UINTP);
98     }
99 }
100 
exynos4210_uart_init_preirq(struct serial_port * port)101 static void __init exynos4210_uart_init_preirq(struct serial_port *port)
102 {
103     struct exynos4210_uart *uart = port->uart;
104     unsigned int divisor;
105     uint32_t ulcon;
106 
107     /* reset, TX/RX disables */
108     exynos4210_write(uart, UCON, 0);
109 
110     /* No Interrupt, auto flow control */
111     exynos4210_write(uart, UMCON, 0);
112 
113     /* Line control and baud-rate generator. */
114     if ( uart->baud != BAUD_AUTO )
115     {
116         /* Baud rate specified: program it into the divisor latch. */
117         divisor = ((uart->clock_hz) / (uart->baud)) - 1;
118         /* FIXME: will use a hacked divisor, assuming the src clock and bauds */
119         exynos4210_write(uart, UFRACVAL, 53);
120         exynos4210_write(uart, UBRDIV, 4);
121     }
122     else
123     {
124         /*
125          * TODO: should be updated
126          * Baud rate already set: read it out from the divisor latch.
127          * divisor = (uart->regs[IBRD] << 6) | uart->regs[FBRD];
128          * uart->baud = (uart->clock_hz << 2) / divisor;
129          */
130     }
131 
132     /*
133      * Number of bits per character
134      * 0 => 5 bits
135      * 1 => 6 bits
136      * 2 => 7 bits
137      * 3 => 8 bits
138      */
139     ASSERT(uart->data_bits >= 5 && uart->data_bits <= 8);
140     ulcon = (uart->data_bits - 5);
141 
142     /*
143      * Stop bits
144      * 0 => 1 stop bit per frame
145      * 1 => 2 stop bit per frame
146      */
147     ASSERT(uart->stop_bits >= 1 && uart->stop_bits <= 2);
148     ulcon |= (uart->stop_bits - 1) << ULCON_STOPB_SHIFT;
149 
150 
151     /* Parity */
152     ulcon |= uart->parity << ULCON_PARITY_SHIFT;
153 
154     exynos4210_write(uart, ULCON, ulcon);
155 
156     /* Mask and clear the interrupts */
157     exynos4210_write(uart, UINTM, UINTM_ALLI);
158     exynos4210_write(uart, UINTP, UINTM_ALLI);
159 
160     /* reset FIFO */
161     exynos4210_write(uart, UFCON, UFCON_FIFO_RESET);
162 
163     /* TODO: Add timeout to avoid infinite loop */
164     while ( exynos4210_read(uart, UFCON) & UFCON_FIFO_RESET )
165         ;
166 
167     /*
168      * Enable FIFO and set the trigger level of Tx FIFO
169      * The trigger level is always set to b101, an interrupt will be
170      * generated when data count of Tx FIFO is less than or equal to the
171      * following value:
172      * UART0 => 160 bytes
173      * UART1 => 40 bytes
174      * UART2 => 10 bytes
175      * UART3 => 10 bytes
176      */
177     exynos4210_write(uart, UFCON, UFCON_FIFO_TX_TRIGGER | UFCON_FIFO_EN);
178 
179     /*
180      * Enable the UART for Rx and Tx
181      *   - Use only interrupt request
182      *   - Interrupts are level trigger
183      *   - Enable Rx timeout
184      */
185     exynos4210_write(uart, UCON,
186                      UCON_RX_IRQ_LEVEL | UCON_TX_IRQ_LEVEL | UCON_RX_IRQ |
187                      UCON_TX_IRQ | UCON_RX_TIMEOUT);
188 }
189 
exynos4210_uart_init_postirq(struct serial_port * port)190 static void __init exynos4210_uart_init_postirq(struct serial_port *port)
191 {
192     struct exynos4210_uart *uart = port->uart;
193     int rc;
194 
195     uart->irqaction.handler = exynos4210_uart_interrupt;
196     uart->irqaction.name    = "exynos4210_uart";
197     uart->irqaction.dev_id  = port;
198 
199     if ( (rc = setup_irq(uart->irq, 0, &uart->irqaction)) != 0 )
200         dprintk(XENLOG_ERR, "Failed to allocated exynos4210_uart IRQ %d\n",
201                 uart->irq);
202 
203     /* Unmask interrupts */
204     exynos4210_write(uart, UINTM, ~UINTM_ALLI);
205 
206     /* Clear pending interrupts */
207     exynos4210_write(uart, UINTP, UINTM_ALLI);
208 
209     /* Enable interrupts */
210     exynos4210_write(uart, UMCON, exynos4210_read(uart, UMCON) | UMCON_INT_EN);
211 }
212 
exynos4210_uart_suspend(struct serial_port * port)213 static void exynos4210_uart_suspend(struct serial_port *port)
214 {
215     BUG(); // XXX
216 }
217 
exynos4210_uart_resume(struct serial_port * port)218 static void exynos4210_uart_resume(struct serial_port *port)
219 {
220     BUG(); // XXX
221 }
222 
exynos4210_uart_tx_ready(struct serial_port * port)223 static int exynos4210_uart_tx_ready(struct serial_port *port)
224 {
225     struct exynos4210_uart *uart = port->uart;
226 
227     /* Tx fifo full ? */
228     if ( exynos4210_read(uart, UFSTAT) & UFSTAT_TX_FULL )
229         return 0;
230     else
231     {
232         uint32_t val = exynos4210_read(uart, UFSTAT);
233 
234         val = (val & UFSTAT_TX_COUNT_MASK) >> UFSTAT_TX_COUNT_SHIFT;
235 
236         /* XXX: Here we assume that we use UART 2/3, on the others
237          * UART the buffer is bigger
238          */
239         ASSERT(val >= 0 && val <= FIFO_MAX_SIZE);
240 
241         return (FIFO_MAX_SIZE - val);
242     }
243 }
244 
exynos4210_uart_putc(struct serial_port * port,char c)245 static void exynos4210_uart_putc(struct serial_port *port, char c)
246 {
247     struct exynos4210_uart *uart = port->uart;
248 
249     exynos4210_write(uart, UTXH, (uint32_t)(unsigned char)c);
250 }
251 
exynos4210_uart_getc(struct serial_port * port,char * pc)252 static int exynos4210_uart_getc(struct serial_port *port, char *pc)
253 {
254     struct exynos4210_uart *uart = port->uart;
255     uint32_t ufstat = exynos4210_read(uart, UFSTAT);
256     uint32_t count;
257 
258     count = (ufstat & UFSTAT_RX_COUNT_MASK) >> UFSTAT_RX_COUNT_SHIFT;
259 
260     /* Check if Rx fifo is full or if the is something in it */
261     if ( ufstat & UFSTAT_RX_FULL || count )
262     {
263         *pc = exynos4210_read(uart, URXH) & URXH_DATA_MASK;
264         return 1;
265     }
266     else
267         return 0;
268 }
269 
exynos4210_uart_irq(struct serial_port * port)270 static int __init exynos4210_uart_irq(struct serial_port *port)
271 {
272     struct exynos4210_uart *uart = port->uart;
273 
274     return uart->irq;
275 }
276 
exynos4210_vuart_info(struct serial_port * port)277 static const struct vuart_info *exynos4210_vuart_info(struct serial_port *port)
278 {
279     struct exynos4210_uart *uart = port->uart;
280 
281     return &uart->vuart;
282 }
283 
284 static struct uart_driver __read_mostly exynos4210_uart_driver = {
285     .init_preirq  = exynos4210_uart_init_preirq,
286     .init_postirq = exynos4210_uart_init_postirq,
287     .endboot      = NULL,
288     .suspend      = exynos4210_uart_suspend,
289     .resume       = exynos4210_uart_resume,
290     .tx_ready     = exynos4210_uart_tx_ready,
291     .putc         = exynos4210_uart_putc,
292     .getc         = exynos4210_uart_getc,
293     .irq          = exynos4210_uart_irq,
294     .vuart_info   = exynos4210_vuart_info,
295 };
296 
297 /* TODO: Parse UART config from the command line */
exynos4210_uart_init(struct dt_device_node * dev,const void * data)298 static int __init exynos4210_uart_init(struct dt_device_node *dev,
299                                        const void *data)
300 {
301     const char *config = data;
302     struct exynos4210_uart *uart;
303     int res;
304     u64 addr, size;
305 
306     if ( strcmp(config, "") )
307         printk("WARNING: UART configuration is not supported\n");
308 
309     uart = &exynos4210_com;
310 
311     /* uart->clock_hz  = 0x16e3600; */
312     uart->baud      = BAUD_AUTO;
313     uart->data_bits = 8;
314     uart->parity    = PARITY_NONE;
315     uart->stop_bits = 1;
316 
317     res = dt_device_get_address(dev, 0, &addr, &size);
318     if ( res )
319     {
320         printk("exynos4210: Unable to retrieve the base"
321                " address of the UART\n");
322         return res;
323     }
324 
325     res = platform_get_irq(dev, 0);
326     if ( res < 0 )
327     {
328         printk("exynos4210: Unable to retrieve the IRQ\n");
329         return -EINVAL;
330     }
331     uart->irq = res;
332 
333     uart->regs = ioremap_nocache(addr, size);
334     if ( !uart->regs )
335     {
336         printk("exynos4210: Unable to map the UART memory\n");
337         return -ENOMEM;
338     }
339 
340     uart->vuart.base_addr = addr;
341     uart->vuart.size = size;
342     uart->vuart.data_off = UTXH;
343     uart->vuart.status_off = UTRSTAT;
344     uart->vuart.status = UTRSTAT_TXE | UTRSTAT_TXFE;
345 
346     /* Register with generic serial driver. */
347     serial_register_uart(SERHND_DTUART, &exynos4210_uart_driver, uart);
348 
349     dt_device_set_used_by(dev, DOMID_XEN);
350 
351     return 0;
352 }
353 
354 static const struct dt_device_match exynos4210_dt_match[] __initconst =
355 {
356     DT_MATCH_COMPATIBLE("samsung,exynos4210-uart"),
357     { /* sentinel */ },
358 };
359 
360 DT_DEVICE_START(exynos4210, "Exynos 4210 UART", DEVICE_SERIAL)
361         .dt_match = exynos4210_dt_match,
362         .init = exynos4210_uart_init,
363 DT_DEVICE_END
364 
365 /*
366  * Local variables:
367  * mode: C
368  * c-file-style: "BSD"
369  * c-basic-offset: 4
370  * indent-tabs-mode: nil
371  * End:
372  */
373