]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/devs/serial/arm/gps4020/v2_0/src/gps4020_serial.c
Initial revision
[karo-tx-redboot.git] / packages / devs / serial / arm / gps4020 / v2_0 / src / gps4020_serial.c
1 //==========================================================================
2 //
3 //      io/serial/arm/gps4020_serial.c
4 //
5 //      GPS4020 Serial I/O Interface Module
6 //
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12 // Copyright (C) 2003 Gary Thomas
13 //
14 // eCos is free software; you can redistribute it and/or modify it under
15 // the terms of the GNU General Public License as published by the Free
16 // Software Foundation; either version 2 or (at your option) any later version.
17 //
18 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
19 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
21 // for more details.
22 //
23 // You should have received a copy of the GNU General Public License along
24 // with eCos; if not, write to the Free Software Foundation, Inc.,
25 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 //
27 // As a special exception, if other files instantiate templates or use macros
28 // or inline functions from this file, or you compile this file and link it
29 // with other works to produce a work based on this file, this file does not
30 // by itself cause the resulting work to be covered by the GNU General Public
31 // License. However the source code for this file must still be made available
32 // in accordance with section (3) of the GNU General Public License.
33 //
34 // This exception does not invalidate any other reasons why a work based on
35 // this file might be covered by the GNU General Public License.
36 //
37 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
38 // at http://sources.redhat.com/ecos/ecos-license/
39 // -------------------------------------------
40 //####ECOSGPLCOPYRIGHTEND####
41 //==========================================================================
42 //#####DESCRIPTIONBEGIN####
43 //
44 // Author(s):   gthomas
45 // Contributors:  gthomas
46 // Date:        1999-02-04
47 // Purpose:     GPS4020 Serial I/O module
48 // Description: 
49 //
50 //####DESCRIPTIONEND####
51 //
52 //==========================================================================
53
54 #include <pkgconf/system.h>
55 #include <pkgconf/io_serial.h>
56 #include <pkgconf/io.h>
57 #include <cyg/io/io.h>
58 #include <cyg/hal/hal_intr.h>
59 #include <cyg/io/devtab.h>
60 #include <cyg/io/serial.h>
61 #include <cyg/infra/diag.h>
62 #include <cyg/hal/hal_if.h>
63
64 #include <cyg/hal/gps4020.h>  // Hardware definitions
65
66 static short select_word_length[] = {
67     -1,            // 5 bits / word (char)
68     -1,            // 6 bits / word
69     SMR_LENGTH_7,  // 7 bits/word
70     SMR_LENGTH_8   // 8 bits/word
71 };
72
73 static short select_stop_bits[] = {
74     -1,
75     SMR_STOP_1,     // 1 stop bit
76     -1,             // 1.5 stop bit
77     SMR_STOP_2      // 2 stop bits
78 };
79
80 static short select_parity[] = {
81     SMR_PARITY_OFF,                // No parity
82     SMR_PARITY_ON|SMR_PARITY_EVEN, // Even parity
83     SMR_PARITY_ON|SMR_PARITY_ODD,  // Odd parity
84     -1,                            // Mark parity
85     -1,                            // Space parity
86 };
87
88 // Baud rate values, based on internal system (20MHz) clock
89 // Note: the extra *10 stuff is for rounding.  Since these values
90 // are so small, a little error here can make/break the calculation
91 #define BAUD_DIVISOR(baud) (((((20000000*10)/(16*baud))+5)/10)-1)
92 static cyg_int32 select_baud[] = {
93     0,                    // Unused
94     BAUD_DIVISOR(50),     // 50
95     BAUD_DIVISOR(75),     // 75
96     BAUD_DIVISOR(110),    // 110
97     0,                    // 134.5
98     BAUD_DIVISOR(150),    // 150
99     BAUD_DIVISOR(200),    // 200
100     BAUD_DIVISOR(300),    // 300
101     BAUD_DIVISOR(600),    // 600
102     BAUD_DIVISOR(1200),   // 1200
103     BAUD_DIVISOR(1800),   // 1800
104     BAUD_DIVISOR(2400),   // 2400
105     BAUD_DIVISOR(3600),   // 3600
106     BAUD_DIVISOR(4800),   // 4800
107     BAUD_DIVISOR(7200),   // 7200
108     BAUD_DIVISOR(9600),   // 9600
109     BAUD_DIVISOR(14400),  // 14400
110     BAUD_DIVISOR(19200),  // 19200
111     BAUD_DIVISOR(38400),  // 38400
112     BAUD_DIVISOR(57600),  // 57600
113     BAUD_DIVISOR(115200), // 115200
114     BAUD_DIVISOR(230400), // 230400
115 };
116
117 typedef struct gps4020_serial_info {
118     CYG_ADDRWORD   regs;                      // Pointer to UART registers
119     CYG_WORD       tx_int_num,                // Transmit interrupt number
120                    rx_int_num,                // Receive interrupt number
121                    ms_int_num;                // Modem Status Change interrupt number
122     cyg_interrupt  serial_tx_interrupt, 
123                    serial_rx_interrupt, 
124                    serial_ms_interrupt;
125     cyg_handle_t   serial_tx_interrupt_handle, 
126                    serial_rx_interrupt_handle, 
127                    serial_ms_interrupt_handle;
128     bool           tx_enabled;
129 } gps4020_serial_info;
130
131 static bool gps4020_serial_init(struct cyg_devtab_entry *tab);
132 static bool gps4020_serial_putc(serial_channel *chan, unsigned char c);
133 static Cyg_ErrNo gps4020_serial_lookup(struct cyg_devtab_entry **tab, 
134                                    struct cyg_devtab_entry *sub_tab,
135                                    const char *name);
136 static unsigned char gps4020_serial_getc(serial_channel *chan);
137 static Cyg_ErrNo gps4020_serial_set_config(serial_channel *chan, cyg_uint32 key,
138                                            const void *xbuf, cyg_uint32 *len);
139 static void gps4020_serial_start_xmit(serial_channel *chan);
140 static void gps4020_serial_stop_xmit(serial_channel *chan);
141
142 static cyg_uint32 gps4020_serial_tx_ISR(cyg_vector_t vector, cyg_addrword_t data);
143 static void       gps4020_serial_tx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
144 static cyg_uint32 gps4020_serial_rx_ISR(cyg_vector_t vector, cyg_addrword_t data);
145 static void       gps4020_serial_rx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
146 #if 0 // FIXME - handle modem & errors
147 static cyg_uint32 gps4020_serial_ms_ISR(cyg_vector_t vector, cyg_addrword_t data);
148 static void       gps4020_serial_ms_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
149 #endif
150
151 static SERIAL_FUNS(gps4020_serial_funs, 
152                    gps4020_serial_putc, 
153                    gps4020_serial_getc,
154                    gps4020_serial_set_config,
155                    gps4020_serial_start_xmit,
156                    gps4020_serial_stop_xmit
157     );
158
159 #ifdef CYGPKG_IO_SERIAL_ARM_GPS4020_SERIAL1
160 static gps4020_serial_info gps4020_serial_info1 = {GPS4020_UART1, // Data register
161                                                    CYGNUM_HAL_INTERRUPT_UART1_TX,  // Tx interrupt
162                                                    CYGNUM_HAL_INTERRUPT_UART1_RX,  // Rx interrupt
163                                                    CYGNUM_HAL_INTERRUPT_UART1_ERR, // Modem & Error interrupt
164                                                   };
165 #if CYGNUM_IO_SERIAL_ARM_GPS4020_SERIAL1_BUFSIZE > 0
166 static unsigned char gps4020_serial_out_buf1[CYGNUM_IO_SERIAL_ARM_GPS4020_SERIAL1_BUFSIZE];
167 static unsigned char gps4020_serial_in_buf1[CYGNUM_IO_SERIAL_ARM_GPS4020_SERIAL1_BUFSIZE];
168
169 static SERIAL_CHANNEL_USING_INTERRUPTS(gps4020_serial_channel1,
170                                        gps4020_serial_funs, 
171                                        gps4020_serial_info1,
172                                        CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_GPS4020_SERIAL1_BAUD),
173                                        CYG_SERIAL_STOP_DEFAULT,
174                                        CYG_SERIAL_PARITY_DEFAULT,
175                                        CYG_SERIAL_WORD_LENGTH_DEFAULT,
176                                        CYG_SERIAL_FLAGS_DEFAULT,
177                                        &gps4020_serial_out_buf1[0], sizeof(gps4020_serial_out_buf1),
178                                        &gps4020_serial_in_buf1[0], sizeof(gps4020_serial_in_buf1)
179     );
180 #else
181 static SERIAL_CHANNEL(gps4020_serial_channel1,
182                       gps4020_serial_funs, 
183                       gps4020_serial_info1,
184                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_GPS4020_SERIAL1_BAUD),
185                       CYG_SERIAL_STOP_DEFAULT,
186                       CYG_SERIAL_PARITY_DEFAULT,
187                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
188                       CYG_SERIAL_FLAGS_DEFAULT
189     );
190 #endif
191
192 DEVTAB_ENTRY(gps4020_serial_io1, 
193              CYGDAT_IO_SERIAL_ARM_GPS4020_SERIAL1_NAME,
194              0,                     // Does not depend on a lower level interface
195              &cyg_io_serial_devio, 
196              gps4020_serial_init, 
197              gps4020_serial_lookup,     // Serial driver may need initializing
198              &gps4020_serial_channel1
199     );
200 #endif //  CYGPKG_IO_SERIAL_ARM_GPS4020_SERIAL2
201
202 #ifdef CYGPKG_IO_SERIAL_ARM_GPS4020_SERIAL2
203 static gps4020_serial_info gps4020_serial_info2 = {GPS4020_UART2, // Data register
204                                                    CYGNUM_HAL_INTERRUPT_UART2_TX,  // Tx interrupt
205                                                    CYGNUM_HAL_INTERRUPT_UART2_RX,  // Rx interrupt
206                                                    CYGNUM_HAL_INTERRUPT_UART2_ERR, // Modem & Error interrupt
207                                                   };
208 #if CYGNUM_IO_SERIAL_ARM_GPS4020_SERIAL2_BUFSIZE > 0
209 static unsigned char gps4020_serial_out_buf2[CYGNUM_IO_SERIAL_ARM_GPS4020_SERIAL2_BUFSIZE];
210 static unsigned char gps4020_serial_in_buf2[CYGNUM_IO_SERIAL_ARM_GPS4020_SERIAL2_BUFSIZE];
211
212 static SERIAL_CHANNEL_USING_INTERRUPTS(gps4020_serial_channel2,
213                                        gps4020_serial_funs, 
214                                        gps4020_serial_info2,
215                                        CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_GPS4020_SERIAL2_BAUD),
216                                        CYG_SERIAL_STOP_DEFAULT,
217                                        CYG_SERIAL_PARITY_DEFAULT,
218                                        CYG_SERIAL_WORD_LENGTH_DEFAULT,
219                                        CYG_SERIAL_FLAGS_DEFAULT,
220                                        &gps4020_serial_out_buf2[0], sizeof(gps4020_serial_out_buf2),
221                                        &gps4020_serial_in_buf2[0], sizeof(gps4020_serial_in_buf2)
222     );
223 #else
224 static SERIAL_CHANNEL(gps4020_serial_channel2,
225                       gps4020_serial_funs, 
226                       gps4020_serial_info2,
227                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_GPS4020_SERIAL2_BAUD),
228                       CYG_SERIAL_STOP_DEFAULT,
229                       CYG_SERIAL_PARITY_DEFAULT,
230                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
231                       CYG_SERIAL_FLAGS_DEFAULT
232     );
233 #endif
234
235 DEVTAB_ENTRY(gps4020_serial_io2, 
236              CYGDAT_IO_SERIAL_ARM_GPS4020_SERIAL2_NAME,
237              0,                     // Does not depend on a lower level interface
238              &cyg_io_serial_devio, 
239              gps4020_serial_init, 
240              gps4020_serial_lookup,     // Serial driver may need initializing
241              &gps4020_serial_channel2
242     );
243 #endif //  CYGPKG_IO_SERIAL_ARM_GPS4020_SERIAL2
244
245 // Internal function to actually configure the hardware to desired baud rate, etc.
246 static bool
247 gps4020_serial_config_port(serial_channel *chan, cyg_serial_info_t *new_config, bool init)
248 {
249     gps4020_serial_info *gps4020_chan = (gps4020_serial_info *)chan->dev_priv;
250     volatile struct _gps4020_uart *regs = (volatile struct _gps4020_uart *)gps4020_chan->regs;
251     unsigned int baud_divisor = select_baud[new_config->baud];
252     short word_len = select_word_length[new_config->word_length - CYGNUM_SERIAL_WORD_LENGTH_5];
253     short stop_bits = select_stop_bits[new_config->stop];
254     short parity =   select_parity[new_config->parity];
255     short mode = word_len | stop_bits | parity;
256     int prescale = 0;
257
258     if (mode >= 0) {
259         while (baud_divisor > 0xFF) {
260             prescale++;
261             baud_divisor >>= 1;
262         }
263 #ifdef CYGDBG_IO_INIT
264         diag_printf("I/O MODE: %x, BAUD: %x\n", mode, baud_divisor);
265         CYGACC_CALL_IF_DELAY_US((cyg_int32)2*100000);
266 #endif
267         regs->mode = mode | SMR_DIV(prescale);
268         regs->baud = baud_divisor;
269         regs->modem_control = SMR_DTR | SMR_RTS;
270         regs->control = SCR_TEN | SCR_REN | SCR_TIE | SCR_RIE;
271         if (new_config != &chan->config) {
272             chan->config = *new_config;
273         }
274         return true;
275     } else {
276         return false;
277     }
278 }
279
280 // Function to initialize the device.  Called at bootstrap time.
281 static bool 
282 gps4020_serial_init(struct cyg_devtab_entry *tab)
283 {
284     serial_channel *chan = (serial_channel *)tab->priv;
285     gps4020_serial_info *gps4020_chan = (gps4020_serial_info *)chan->dev_priv;
286 #ifdef CYGDBG_IO_INIT
287     diag_printf("GPS4020 SERIAL init - dev: %x.%d\n", gps4020_chan->regs, gps4020_chan->tx_int_num);
288 #endif
289     (chan->callbacks->serial_init)(chan);  // Really only required for interrupt driven devices
290     if (chan->out_cbuf.len != 0) {
291         cyg_drv_interrupt_create(gps4020_chan->tx_int_num,
292                                  99,                     // Priority - unused
293                                  (cyg_addrword_t)chan,   //  Data item passed to interrupt handler
294                                  gps4020_serial_tx_ISR,
295                                  gps4020_serial_tx_DSR,
296                                  &gps4020_chan->serial_tx_interrupt_handle,
297                                  &gps4020_chan->serial_tx_interrupt);
298         cyg_drv_interrupt_attach(gps4020_chan->serial_tx_interrupt_handle);
299         cyg_drv_interrupt_mask(gps4020_chan->tx_int_num);
300         gps4020_chan->tx_enabled = false;
301     }
302     if (chan->in_cbuf.len != 0) {
303         cyg_drv_interrupt_create(gps4020_chan->rx_int_num,
304                                  99,                     // Priority - unused
305                                  (cyg_addrword_t)chan,   //  Data item passed to interrupt handler
306                                  gps4020_serial_rx_ISR,
307                                  gps4020_serial_rx_DSR,
308                                  &gps4020_chan->serial_rx_interrupt_handle,
309                                  &gps4020_chan->serial_rx_interrupt);
310         cyg_drv_interrupt_attach(gps4020_chan->serial_rx_interrupt_handle);
311         cyg_drv_interrupt_unmask(gps4020_chan->rx_int_num);
312     }
313     gps4020_serial_config_port(chan, &chan->config, true);
314     return true;
315 }
316
317 // This routine is called when the device is "looked" up (i.e. attached)
318 static Cyg_ErrNo 
319 gps4020_serial_lookup(struct cyg_devtab_entry **tab, 
320                   struct cyg_devtab_entry *sub_tab,
321                   const char *name)
322 {
323     serial_channel *chan = (serial_channel *)(*tab)->priv;
324     (chan->callbacks->serial_init)(chan);  // Really only required for interrupt driven devices
325     return ENOERR;
326 }
327
328 // Send a character to the device output buffer.
329 // Return 'true' if character is sent to device
330 static bool
331 gps4020_serial_putc(serial_channel *chan, unsigned char c)
332 {
333     gps4020_serial_info *gps4020_chan = (gps4020_serial_info *)chan->dev_priv;
334     volatile struct _gps4020_uart *regs = (volatile struct _gps4020_uart *)gps4020_chan->regs;
335
336     if ((regs->status & SSR_TxEmpty) != 0) {
337         // Transmit buffer/FIFO is not full
338         regs->TxRx = c;
339         return true;
340     } else {
341         // No space
342         return false;
343     }
344 }
345
346 // Fetch a character from the device input buffer, waiting if necessary
347 static unsigned char 
348 gps4020_serial_getc(serial_channel *chan)
349 {
350     unsigned char c;
351     gps4020_serial_info *gps4020_chan = (gps4020_serial_info *)chan->dev_priv;
352     volatile struct _gps4020_uart *regs = (volatile struct _gps4020_uart *)gps4020_chan->regs;
353
354     while ((regs->status & SSR_RxFull) == 0) ; // Wait for character
355     c = regs->TxRx;
356     return c;
357 }
358
359 // Set up the device characteristics; baud rate, etc.
360 static Cyg_ErrNo
361 gps4020_serial_set_config(serial_channel *chan, cyg_uint32 key,
362                           const void *xbuf, cyg_uint32 *len)
363 {
364     switch (key) {
365     case CYG_IO_SET_CONFIG_SERIAL_INFO:
366       {
367         cyg_serial_info_t *config = (cyg_serial_info_t *)xbuf;
368         if ( *len < sizeof(cyg_serial_info_t) ) {
369             return -EINVAL;
370         }
371         *len = sizeof(cyg_serial_info_t);
372         if ( true != gps4020_serial_config_port(chan, config, false) )
373             return -EINVAL;
374       }
375       break;
376     default:
377         return -EINVAL;
378     }
379     return ENOERR;
380 }
381
382 // Enable the transmitter (interrupt) on the device
383 static void
384 gps4020_serial_start_xmit(serial_channel *chan)
385 {
386     gps4020_serial_info *gps4020_chan = (gps4020_serial_info *)chan->dev_priv;
387     gps4020_chan->tx_enabled = true;
388     cyg_drv_interrupt_unmask(gps4020_chan->tx_int_num);
389 }
390
391 // Disable the transmitter on the device
392 static void 
393 gps4020_serial_stop_xmit(serial_channel *chan)
394 {
395     gps4020_serial_info *gps4020_chan = (gps4020_serial_info *)chan->dev_priv;
396     cyg_drv_interrupt_mask(gps4020_chan->tx_int_num);
397     gps4020_chan->tx_enabled = false;
398 }
399
400 // Serial I/O - low level Tx interrupt handler (ISR)
401 static cyg_uint32 
402 gps4020_serial_tx_ISR(cyg_vector_t vector, cyg_addrword_t data)
403 {
404     serial_channel *chan = (serial_channel *)data;
405     gps4020_serial_info *gps4020_chan = (gps4020_serial_info *)chan->dev_priv;
406     cyg_drv_interrupt_mask(gps4020_chan->tx_int_num);
407     cyg_drv_interrupt_acknowledge(gps4020_chan->tx_int_num);
408     return CYG_ISR_CALL_DSR;  // Cause DSR to be run
409 }
410
411 // Serial I/O - high level Tx interrupt handler (DSR)
412 static void       
413 gps4020_serial_tx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
414 {
415     serial_channel *chan = (serial_channel *)data;
416     gps4020_serial_info *gps4020_chan = (gps4020_serial_info *)chan->dev_priv;
417     (chan->callbacks->xmt_char)(chan);
418     if (gps4020_chan->tx_enabled) {
419         cyg_drv_interrupt_unmask(gps4020_chan->tx_int_num);
420     }
421 }
422
423 // Serial I/O - low level Rx interrupt handler (ISR)
424 static cyg_uint32 
425 gps4020_serial_rx_ISR(cyg_vector_t vector, cyg_addrword_t data)
426 {
427     serial_channel *chan = (serial_channel *)data;
428     gps4020_serial_info *gps4020_chan = (gps4020_serial_info *)chan->dev_priv;
429     cyg_drv_interrupt_mask(gps4020_chan->rx_int_num);
430     cyg_drv_interrupt_acknowledge(gps4020_chan->rx_int_num);
431     return CYG_ISR_CALL_DSR;  // Cause DSR to be run
432 }
433
434 // Serial I/O - high level Rx interrupt handler (DSR)
435 static void       
436 gps4020_serial_rx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
437 {
438     serial_channel *chan = (serial_channel *)data;
439     gps4020_serial_info *gps4020_chan = (gps4020_serial_info *)chan->dev_priv;
440     volatile struct _gps4020_uart *regs = (volatile struct _gps4020_uart *)gps4020_chan->regs;
441
442     while ((regs->status & SSR_RxFull) != 0) 
443         (chan->callbacks->rcv_char)(chan, regs->TxRx);
444     cyg_drv_interrupt_unmask(gps4020_chan->rx_int_num);
445 }
446
447 #if 0 // FIXME - handle modem & errors
448 // Serial I/O - low level Ms interrupt handler (ISR)
449 static cyg_uint32 
450 gps4020_serial_ms_ISR(cyg_vector_t vector, cyg_addrword_t data)
451 {
452     serial_channel *chan = (serial_channel *)data;
453     gps4020_serial_info *gps4020_chan = (gps4020_serial_info *)chan->dev_priv;
454     cyg_drv_interrupt_mask(gps4020_chan->ms_int_num);
455     cyg_drv_interrupt_acknowledge(gps4020_chan->ms_int_num);
456     return CYG_ISR_CALL_DSR;  // Cause DSR to be run
457 }
458
459 // Serial I/O - high level Ms interrupt handler (DSR)
460 static void       
461 gps4020_serial_ms_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
462 {
463 }
464 #endif
465