1 //==========================================================================
3 // io/serial/arm/gps4020_serial.c
5 // GPS4020 Serial I/O Interface Module
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
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.
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
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.
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.
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.
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####
45 // Contributors: gthomas
47 // Purpose: GPS4020 Serial I/O module
50 //####DESCRIPTIONEND####
52 //==========================================================================
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>
64 #include <cyg/hal/gps4020.h> // Hardware definitions
66 static short select_word_length[] = {
67 -1, // 5 bits / word (char)
69 SMR_LENGTH_7, // 7 bits/word
70 SMR_LENGTH_8 // 8 bits/word
73 static short select_stop_bits[] = {
75 SMR_STOP_1, // 1 stop bit
77 SMR_STOP_2 // 2 stop bits
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
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[] = {
94 BAUD_DIVISOR(50), // 50
95 BAUD_DIVISOR(75), // 75
96 BAUD_DIVISOR(110), // 110
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
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,
125 cyg_handle_t serial_tx_interrupt_handle,
126 serial_rx_interrupt_handle,
127 serial_ms_interrupt_handle;
129 } gps4020_serial_info;
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,
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);
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);
151 static SERIAL_FUNS(gps4020_serial_funs,
154 gps4020_serial_set_config,
155 gps4020_serial_start_xmit,
156 gps4020_serial_stop_xmit
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
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];
169 static SERIAL_CHANNEL_USING_INTERRUPTS(gps4020_serial_channel1,
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)
181 static SERIAL_CHANNEL(gps4020_serial_channel1,
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
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,
197 gps4020_serial_lookup, // Serial driver may need initializing
198 &gps4020_serial_channel1
200 #endif // CYGPKG_IO_SERIAL_ARM_GPS4020_SERIAL2
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
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];
212 static SERIAL_CHANNEL_USING_INTERRUPTS(gps4020_serial_channel2,
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)
224 static SERIAL_CHANNEL(gps4020_serial_channel2,
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
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,
240 gps4020_serial_lookup, // Serial driver may need initializing
241 &gps4020_serial_channel2
243 #endif // CYGPKG_IO_SERIAL_ARM_GPS4020_SERIAL2
245 // Internal function to actually configure the hardware to desired baud rate, etc.
247 gps4020_serial_config_port(serial_channel *chan, cyg_serial_info_t *new_config, bool init)
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;
259 while (baud_divisor > 0xFF) {
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);
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;
280 // Function to initialize the device. Called at bootstrap time.
282 gps4020_serial_init(struct cyg_devtab_entry *tab)
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);
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;
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);
313 gps4020_serial_config_port(chan, &chan->config, true);
317 // This routine is called when the device is "looked" up (i.e. attached)
319 gps4020_serial_lookup(struct cyg_devtab_entry **tab,
320 struct cyg_devtab_entry *sub_tab,
323 serial_channel *chan = (serial_channel *)(*tab)->priv;
324 (chan->callbacks->serial_init)(chan); // Really only required for interrupt driven devices
328 // Send a character to the device output buffer.
329 // Return 'true' if character is sent to device
331 gps4020_serial_putc(serial_channel *chan, unsigned char c)
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;
336 if ((regs->status & SSR_TxEmpty) != 0) {
337 // Transmit buffer/FIFO is not full
346 // Fetch a character from the device input buffer, waiting if necessary
348 gps4020_serial_getc(serial_channel *chan)
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;
354 while ((regs->status & SSR_RxFull) == 0) ; // Wait for character
359 // Set up the device characteristics; baud rate, etc.
361 gps4020_serial_set_config(serial_channel *chan, cyg_uint32 key,
362 const void *xbuf, cyg_uint32 *len)
365 case CYG_IO_SET_CONFIG_SERIAL_INFO:
367 cyg_serial_info_t *config = (cyg_serial_info_t *)xbuf;
368 if ( *len < sizeof(cyg_serial_info_t) ) {
371 *len = sizeof(cyg_serial_info_t);
372 if ( true != gps4020_serial_config_port(chan, config, false) )
382 // Enable the transmitter (interrupt) on the device
384 gps4020_serial_start_xmit(serial_channel *chan)
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);
391 // Disable the transmitter on the device
393 gps4020_serial_stop_xmit(serial_channel *chan)
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;
400 // Serial I/O - low level Tx interrupt handler (ISR)
402 gps4020_serial_tx_ISR(cyg_vector_t vector, cyg_addrword_t data)
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
411 // Serial I/O - high level Tx interrupt handler (DSR)
413 gps4020_serial_tx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
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);
423 // Serial I/O - low level Rx interrupt handler (ISR)
425 gps4020_serial_rx_ISR(cyg_vector_t vector, cyg_addrword_t data)
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
434 // Serial I/O - high level Rx interrupt handler (DSR)
436 gps4020_serial_rx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
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;
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);
447 #if 0 // FIXME - handle modem & errors
448 // Serial I/O - low level Ms interrupt handler (ISR)
450 gps4020_serial_ms_ISR(cyg_vector_t vector, cyg_addrword_t data)
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
459 // Serial I/O - high level Ms interrupt handler (DSR)
461 gps4020_serial_ms_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)