]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/at91/uart_dev_at91usart.c
This commit was generated by cvs2svn to compensate for changes in r156701,
[FreeBSD/FreeBSD.git] / sys / arm / at91 / uart_dev_at91usart.c
1 /*-
2  * Copyright (c) 2005 M. Warner Losh
3  * Copyright (c) 2005 cognet
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/bus.h>
34 #include <sys/conf.h>
35 #include <sys/cons.h>
36 #include <sys/tty.h>
37 #include <machine/bus.h>
38
39 #include <dev/uart/uart.h>
40 #include <dev/uart/uart_cpu.h>
41 #include <dev/uart/uart_bus.h>
42 #include <arm/at91/at91rm92reg.h>
43 #include <arm/at91/at91_usartreg.h>
44
45 #include "uart_if.h"
46
47 #define      DEFAULT_RCLK    AT91C_MASTER_CLOCK
48
49 #define SIGCHG(c, i, s, d)                              \
50         do {                                            \
51                 if (c) {                                \
52                         i |= (i & s) ? s : s | d;       \
53                 } else {                                \
54                         i = (i & s) ? (i & ~s) | d : i; \
55                 }                                       \
56         } while (0);
57
58 /*
59  * Low-level UART interface.
60  */
61 static int at91_usart_probe(struct uart_bas *bas);
62 static void at91_usart_init(struct uart_bas *bas, int, int, int, int);
63 static void at91_usart_term(struct uart_bas *bas);
64 static void at91_usart_putc(struct uart_bas *bas, int);
65 static int at91_usart_poll(struct uart_bas *bas);
66 static int at91_usart_getc(struct uart_bas *bas);
67
68 extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs;
69
70 static int
71 at91_usart_param(struct uart_bas *bas, int baudrate, int databits,
72     int stopbits, int parity)
73 {
74         uint32_t mr;
75
76         /*
77          * Assume 3-write RS-232 configuration.
78          * XXX Not sure how uart will present the other modes to us, so
79          * XXX they are unimplemented.  maybe ioctl?
80          */
81         mr = USART_MR_MODE_NORMAL;
82         mr |= USART_MR_USCLKS_MCK;      /* Assume MCK */
83
84         /*
85          * Or in the databits requested
86          */
87         if (databits < 9)
88                 mr &= ~USART_MR_MODE9;
89         switch (databits) {
90         case 5:
91                 mr |= USART_MR_CHRL_5BITS;
92                 break;
93         case 6:
94                 mr |= USART_MR_CHRL_6BITS;
95                 break;
96         case 7:
97                 mr |= USART_MR_CHRL_7BITS;
98                 break;
99         case 8:
100                 mr |= USART_MR_CHRL_8BITS;
101                 break;
102         case 9:
103                 mr |= USART_MR_CHRL_8BITS | USART_MR_MODE9;
104                 break;
105         default:
106                 return (EINVAL);
107         }
108
109         /*
110          * Or in the parity
111          */
112         switch (parity) {
113         case UART_PARITY_NONE:
114                 mr |= USART_MR_PAR_NONE;
115                 break;
116         case UART_PARITY_ODD:
117                 mr |= USART_MR_PAR_ODD;
118                 break;
119         case UART_PARITY_EVEN:
120                 mr |= USART_MR_PAR_EVEN;
121                 break;
122         case UART_PARITY_MARK:
123                 mr |= USART_MR_PAR_MARK;
124                 break;
125         case UART_PARITY_SPACE:
126                 mr |= USART_MR_PAR_SPACE;
127                 break;
128         default:
129                 return (EINVAL);
130         }
131
132         /*
133          * Or in the stop bits.  Note: The hardware supports
134          * 1.5 stop bits in async mode, but there's no way to
135          * specify that AFAICT.
136          */
137         if (stopbits > 1)
138                 mr |= USART_MR_NBSTOP_2;
139         else
140                 mr |= USART_MR_NBSTOP_2;
141         /* else if (stopbits == 1.5)
142                 mr |= USART_MR_NBSTOP_1_5; */
143
144         /*
145          * We want normal plumbing mode too, none of this fancy
146          * loopback or echo mode.
147          */
148         mr |= USART_MR_CHMODE_NORMAL;
149
150         mr &= ~USART_MR_MSBF;   /* lsb first */
151         mr &= ~USART_MR_CKLO_SCK;       /* Don't drive SCK */
152
153         /* XXX Need to take possible synchronous mode into account */
154         return (0);
155 }
156
157 struct uart_ops at91_usart_ops = {
158         .probe = at91_usart_probe,
159         .init = at91_usart_init,
160         .term = at91_usart_term,
161         .putc = at91_usart_putc,
162         .poll = at91_usart_poll,
163         .getc = at91_usart_getc,
164 };
165
166 static int
167 at91_usart_probe(struct uart_bas *bas)
168 {
169         /* We know that this is always here */
170         return (0);
171 }
172
173 /*
174  * Initialize this device (I think as the console)
175  */
176 static void
177 at91_usart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits,
178     int parity)
179 {
180         at91_usart_param(bas, baudrate, databits, stopbits, parity);
181
182         /* Turn on rx and tx */
183         uart_setreg(bas, USART_CR, USART_CR_RSTRX | USART_CR_RSTTX);
184         uart_setreg(bas, USART_CR, USART_CR_RXEN | USART_CR_TXEN);
185         uart_setreg(bas, USART_IER, USART_CSR_TXRDY | USART_CSR_RXRDY);
186 }
187
188 /*
189  * Free resources now that we're no longer the console.  This appears to
190  * be never called, and I'm unsure quite what to do if I am called.
191  */
192 static void
193 at91_usart_term(struct uart_bas *bas)
194 {
195         /* XXX */
196 }
197
198 /*
199  * Put a character of console output (so we do it here polling rather than
200  * interrutp driven).
201  */
202 static void
203 at91_usart_putc(struct uart_bas *bas, int c)
204 {
205
206         while (!(uart_getreg(bas, USART_CSR) & 
207             USART_CSR_TXRDY));
208         uart_setreg(bas, USART_THR, c);
209 }
210
211 /*
212  * Poll for a character available
213  */
214 static int
215 at91_usart_poll(struct uart_bas *bas)
216 {
217
218         if (!(uart_getreg(bas, USART_CSR) & USART_CSR_RXRDY))
219                 return (-1);
220         return (uart_getreg(bas, USART_RHR) & 0xff);
221 }
222
223 /*
224  * Block waiting for a character.
225  */
226 static int
227 at91_usart_getc(struct uart_bas *bas)
228 {
229         int c;
230
231         while (!(uart_getreg(bas, USART_CSR) & USART_CSR_RXRDY)) 
232                 ;
233         c = uart_getreg(bas, USART_RHR);
234         c &= 0xff;
235         return (c);
236 }
237
238 static int at91_usart_bus_probe(struct uart_softc *sc);
239 static int at91_usart_bus_attach(struct uart_softc *sc);
240 static int at91_usart_bus_flush(struct uart_softc *, int);
241 static int at91_usart_bus_getsig(struct uart_softc *);
242 static int at91_usart_bus_ioctl(struct uart_softc *, int, intptr_t);
243 static int at91_usart_bus_ipend(struct uart_softc *);
244 static int at91_usart_bus_param(struct uart_softc *, int, int, int, int);
245 static int at91_usart_bus_receive(struct uart_softc *);
246 static int at91_usart_bus_setsig(struct uart_softc *, int);
247 static int at91_usart_bus_transmit(struct uart_softc *);
248
249 static kobj_method_t at91_usart_methods[] = {
250         KOBJMETHOD(uart_probe,          at91_usart_bus_probe),
251         KOBJMETHOD(uart_attach,         at91_usart_bus_attach),
252         KOBJMETHOD(uart_flush,          at91_usart_bus_flush),
253         KOBJMETHOD(uart_getsig,         at91_usart_bus_getsig),
254         KOBJMETHOD(uart_ioctl,          at91_usart_bus_ioctl),
255         KOBJMETHOD(uart_ipend,          at91_usart_bus_ipend),
256         KOBJMETHOD(uart_param,          at91_usart_bus_param),
257         KOBJMETHOD(uart_receive,        at91_usart_bus_receive),
258         KOBJMETHOD(uart_setsig,         at91_usart_bus_setsig),
259         KOBJMETHOD(uart_transmit,       at91_usart_bus_transmit),
260         
261         { 0, 0 }
262 };
263
264 int
265 at91_usart_bus_probe(struct uart_softc *sc)
266 {
267         return (0);
268 }
269
270 static int
271 at91_usart_bus_attach(struct uart_softc *sc)
272 {
273         sc->sc_txfifosz = 1;
274         sc->sc_rxfifosz = 1;
275         sc->sc_hwiflow = 0;
276         return (0);
277 }
278 static int
279 at91_usart_bus_transmit(struct uart_softc *sc)
280 {
281         int i;
282
283         /* XXX VERY sub-optimial */
284         mtx_lock_spin(&sc->sc_hwmtx);
285         sc->sc_txbusy = 1;
286         for (i = 0; i < sc->sc_txdatasz; i++)
287                 at91_usart_putc(&sc->sc_bas, sc->sc_txbuf[i]);
288         mtx_unlock_spin(&sc->sc_hwmtx);
289 #ifdef USART0_CONSOLE
290         /*
291          * XXX: Gross hack : Skyeye doesn't raise an interrupt once the
292          * transfer is done, so simulate it.
293          */
294         uart_setreg(&sc->sc_bas, USART_IER, USART_CSR_TXRDY);
295 #endif
296         return (0);
297 }
298 static int
299 at91_usart_bus_setsig(struct uart_softc *sc, int sig)
300 {
301         uint32_t new, old, cr;
302         struct uart_bas *bas;
303
304         do {
305                 old = sc->sc_hwsig;
306                 new = old;
307                 if (sig & SER_DDTR)
308                         SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR);
309                 if (sig & SER_DRTS)
310                         SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS);
311         } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
312         bas = &sc->sc_bas;
313         mtx_lock_spin(&sc->sc_hwmtx);
314         cr = uart_getreg(bas, USART_CR);
315         cr &= ~(USART_CR_DTREN | USART_CR_DTRDIS | USART_CR_RTSEN |
316             USART_CR_RTSDIS);
317         if (new & SER_DTR)
318                 cr |= USART_CR_DTREN;
319         else
320                 cr |= USART_CR_DTRDIS;
321         if (new & SER_RTS)
322                 cr |= USART_CR_RTSEN;
323         else
324                 cr |= USART_CR_RTSDIS;
325         uart_setreg(bas, USART_CR, cr);
326         mtx_unlock_spin(&sc->sc_hwmtx);
327         return (0);
328 }
329 static int
330 at91_usart_bus_receive(struct uart_softc *sc)
331 {
332         
333         mtx_lock_spin(&sc->sc_hwmtx);
334         uart_rx_put(sc, at91_usart_getc(&sc->sc_bas));
335         mtx_unlock_spin(&sc->sc_hwmtx);
336         return (0);
337 }
338 static int
339 at91_usart_bus_param(struct uart_softc *sc, int baudrate, int databits,
340     int stopbits, int parity)
341 {
342         return (at91_usart_param(&sc->sc_bas, baudrate, databits, stopbits,
343             parity));
344 }
345 static int
346 at91_usart_bus_ipend(struct uart_softc *sc)
347 {
348         int csr = uart_getreg(&sc->sc_bas, USART_CSR);
349         int ipend = 0;
350         
351 #ifdef USART0_CONSOLE
352         /* 
353          * XXX: We have to cheat for skyeye, as it will return 0xff for all
354          * the devices it doesn't emulate.
355          */
356         if (sc->sc_bas.chan != 1)
357                 return (0);
358 #endif
359            
360         mtx_lock_spin(&sc->sc_hwmtx);
361         if (csr & USART_CSR_TXRDY && sc->sc_txbusy)
362                 ipend |= SER_INT_TXIDLE;
363         if (csr & USART_CSR_RXRDY)
364                 ipend |= SER_INT_RXREADY;
365         mtx_unlock_spin(&sc->sc_hwmtx);
366         return (ipend);
367 }
368 static int
369 at91_usart_bus_flush(struct uart_softc *sc, int what)
370 {
371         return (0);
372 }
373
374 static int
375 at91_usart_bus_getsig(struct uart_softc *sc)
376 {
377         uint32_t new, sig;
378         uint8_t csr;
379
380         mtx_lock_spin(&sc->sc_hwmtx);
381         csr = uart_getreg(&sc->sc_bas, USART_CSR);
382         sig = 0;
383         if (csr & USART_CSR_CTS)
384                 sig |= SER_CTS;
385         if (csr & USART_CSR_DCD)
386                 sig |= SER_DCD;
387         if (csr & USART_CSR_DSR)
388                 sig |= SER_DSR;
389         if (csr & USART_CSR_RI)
390                 sig |= SER_RI;
391         new = sig & ~SER_MASK_DELTA;
392         sc->sc_hwsig = new;
393         mtx_unlock_spin(&sc->sc_hwmtx);
394         return (sig);
395 }
396
397 static int
398 at91_usart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
399 {
400         return (EINVAL);
401 }
402 struct uart_class at91_usart_class = {
403         "at91_usart class",
404         at91_usart_methods,
405         1,
406         .uc_range = 8,
407         .uc_rclk = DEFAULT_RCLK
408 };