1 //==========================================================================
3 // io/serial/arm/edb7xxx_serial.c
5 // Cirrus Logic EDB7XXX Serial I/O Interface Module (interrupt driven)
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.
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
36 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37 // at http://sources.redhat.com/ecos/ecos-license/
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //==========================================================================
41 //#####DESCRIPTIONBEGIN####
44 // Contributors: gthomas
46 // Purpose: EDB7XXX Serial I/O module (interrupt driven version)
49 //####DESCRIPTIONEND####
51 //==========================================================================
53 #include <pkgconf/system.h>
54 #include <pkgconf/io_serial.h>
55 #include <pkgconf/io.h>
56 #include <cyg/io/io.h>
57 #include <cyg/hal/hal_intr.h>
58 #include <cyg/io/devtab.h>
59 #include <cyg/io/serial.h>
60 #include <cyg/infra/diag.h>
62 #ifdef CYGPKG_IO_SERIAL_ARM_EDB7XXX
64 #include "edb7xxx_serial.h"
66 typedef struct edb7xxx_serial_info {
67 CYG_ADDRWORD data, // Pointer to data register
68 control, // Pointer to baud rate/line control register
69 stat, // Pointer to system flags for this port
70 syscon; // Pointer to system control for this port
71 CYG_WORD tx_int_num, // Transmit interrupt number
72 rx_int_num, // Receive interrupt number
73 ms_int_num; // Modem Status Change interrupt number
74 cyg_interrupt serial_tx_interrupt,
77 cyg_handle_t serial_tx_interrupt_handle,
78 serial_rx_interrupt_handle,
79 serial_ms_interrupt_handle;
81 } edb7xxx_serial_info;
83 static bool edb7xxx_serial_init(struct cyg_devtab_entry *tab);
84 static bool edb7xxx_serial_putc(serial_channel *chan, unsigned char c);
85 static Cyg_ErrNo edb7xxx_serial_lookup(struct cyg_devtab_entry **tab,
86 struct cyg_devtab_entry *sub_tab,
88 static unsigned char edb7xxx_serial_getc(serial_channel *chan);
89 static Cyg_ErrNo edb7xxx_serial_set_config(serial_channel *chan, cyg_uint32 key,
90 const void *xbuf, cyg_uint32 *len);
91 static void edb7xxx_serial_start_xmit(serial_channel *chan);
92 static void edb7xxx_serial_stop_xmit(serial_channel *chan);
94 static cyg_uint32 edb7xxx_serial_tx_ISR(cyg_vector_t vector, cyg_addrword_t data);
95 static void edb7xxx_serial_tx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
96 static cyg_uint32 edb7xxx_serial_rx_ISR(cyg_vector_t vector, cyg_addrword_t data);
97 static void edb7xxx_serial_rx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
98 static cyg_uint32 edb7xxx_serial_ms_ISR(cyg_vector_t vector, cyg_addrword_t data);
99 static void edb7xxx_serial_ms_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
101 static SERIAL_FUNS(edb7xxx_serial_funs,
104 edb7xxx_serial_set_config,
105 edb7xxx_serial_start_xmit,
106 edb7xxx_serial_stop_xmit
109 #ifdef CYGPKG_IO_SERIAL_ARM_EDB7XXX_SERIAL1
110 static edb7xxx_serial_info edb7xxx_serial_info1 = {UARTDR1, // Data register
111 UBLCR1, // Port control
113 SYSCON1, // System config
114 CYGNUM_HAL_INTERRUPT_UTXINT1, // Tx interrupt
115 CYGNUM_HAL_INTERRUPT_URXINT1, // Rx interrupt
116 0 /*CYGNUM_HAL_INTERRUPT_UMSINT*/}; // Modem control
117 #if CYGNUM_IO_SERIAL_ARM_EDB7XXX_SERIAL1_BUFSIZE > 0
118 static unsigned char edb7xxx_serial_out_buf1[CYGNUM_IO_SERIAL_ARM_EDB7XXX_SERIAL1_BUFSIZE];
119 static unsigned char edb7xxx_serial_in_buf1[CYGNUM_IO_SERIAL_ARM_EDB7XXX_SERIAL1_BUFSIZE];
121 static SERIAL_CHANNEL_USING_INTERRUPTS(edb7xxx_serial_channel1,
123 edb7xxx_serial_info1,
124 CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_EDB7XXX_SERIAL1_BAUD),
125 CYG_SERIAL_STOP_DEFAULT,
126 CYG_SERIAL_PARITY_DEFAULT,
127 CYG_SERIAL_WORD_LENGTH_DEFAULT,
128 CYG_SERIAL_FLAGS_DEFAULT,
129 &edb7xxx_serial_out_buf1[0], sizeof(edb7xxx_serial_out_buf1),
130 &edb7xxx_serial_in_buf1[0], sizeof(edb7xxx_serial_in_buf1)
133 static SERIAL_CHANNEL(edb7xxx_serial_channel1,
135 edb7xxx_serial_info1,
136 CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_EDB7XXX_SERIAL1_BAUD),
137 CYG_SERIAL_STOP_DEFAULT,
138 CYG_SERIAL_PARITY_DEFAULT,
139 CYG_SERIAL_WORD_LENGTH_DEFAULT,
140 CYG_SERIAL_FLAGS_DEFAULT
144 DEVTAB_ENTRY(edb7xxx_serial_io1,
145 CYGDAT_IO_SERIAL_ARM_EDB7XXX_SERIAL1_NAME,
146 0, // Does not depend on a lower level interface
147 &cyg_io_serial_devio,
149 edb7xxx_serial_lookup, // Serial driver may need initializing
150 &edb7xxx_serial_channel1
152 #endif // CYGPKG_IO_SERIAL_ARM_EDB7XXX_SERIAL2
154 #ifdef CYGPKG_IO_SERIAL_ARM_EDB7XXX_SERIAL2
155 static edb7xxx_serial_info edb7xxx_serial_info2 = {UARTDR2, // Data register
156 UBLCR2, // Port control
158 SYSCON2, // System config
159 CYGNUM_HAL_INTERRUPT_UTXINT2, // Tx interrupt
160 CYGNUM_HAL_INTERRUPT_URXINT2, // Rx interrupt
161 0}; // No modem control
162 #if CYGNUM_IO_SERIAL_ARM_EDB7XXX_SERIAL2_BUFSIZE > 0
163 static unsigned char edb7xxx_serial_out_buf2[CYGNUM_IO_SERIAL_ARM_EDB7XXX_SERIAL2_BUFSIZE];
164 static unsigned char edb7xxx_serial_in_buf2[CYGNUM_IO_SERIAL_ARM_EDB7XXX_SERIAL2_BUFSIZE];
166 static SERIAL_CHANNEL_USING_INTERRUPTS(edb7xxx_serial_channel2,
168 edb7xxx_serial_info2,
169 CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_EDB7XXX_SERIAL2_BAUD),
170 CYG_SERIAL_STOP_DEFAULT,
171 CYG_SERIAL_PARITY_DEFAULT,
172 CYG_SERIAL_WORD_LENGTH_DEFAULT,
173 CYG_SERIAL_FLAGS_DEFAULT,
174 &edb7xxx_serial_out_buf2[0], sizeof(edb7xxx_serial_out_buf2),
175 &edb7xxx_serial_in_buf2[0], sizeof(edb7xxx_serial_in_buf2)
178 static SERIAL_CHANNEL(edb7xxx_serial_channel2,
180 edb7xxx_serial_info2,
181 CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_EDB7XXX_SERIAL2_BAUD),
182 CYG_SERIAL_STOP_DEFAULT,
183 CYG_SERIAL_PARITY_DEFAULT,
184 CYG_SERIAL_WORD_LENGTH_DEFAULT,
185 CYG_SERIAL_FLAGS_DEFAULT
189 DEVTAB_ENTRY(edb7xxx_serial_io2,
190 CYGDAT_IO_SERIAL_ARM_EDB7XXX_SERIAL2_NAME,
191 0, // Does not depend on a lower level interface
192 &cyg_io_serial_devio,
194 edb7xxx_serial_lookup, // Serial driver may need initializing
195 &edb7xxx_serial_channel2
197 #endif // CYGPKG_IO_SERIAL_ARM_EDB7XXX_SERIAL2
199 // Internal function to actually configure the hardware to desired baud rate, etc.
201 edb7xxx_serial_config_port(serial_channel *chan, cyg_serial_info_t *new_config, bool init)
203 edb7xxx_serial_info *edb7xxx_chan = (edb7xxx_serial_info *)chan->dev_priv;
204 volatile cyg_uint32 *syscon = (volatile cyg_uint32 *)edb7xxx_chan->syscon;
205 volatile cyg_uint32 *blcfg = (volatile cyg_uint32 *)edb7xxx_chan->control;
206 unsigned int baud_divisor = select_baud[new_config->baud];
208 if (baud_divisor == 0) return false;
209 // Disable port interrupts while changing hardware
210 _lcr = select_word_length[new_config->word_length - CYGNUM_SERIAL_WORD_LENGTH_5] |
211 select_stop_bits[new_config->stop] |
212 select_parity[new_config->parity] |
213 UBLCR_FIFOEN | UART_BITRATE(baud_divisor);
214 #ifdef CYGDBG_IO_INIT
215 diag_printf("Set CTL: %x = %x\n", blcfg, _lcr);
218 *syscon |= SYSCON1_UART1EN;
219 if (new_config != &chan->config) {
220 chan->config = *new_config;
225 // Function to initialize the device. Called at bootstrap time.
227 edb7xxx_serial_init(struct cyg_devtab_entry *tab)
229 serial_channel *chan = (serial_channel *)tab->priv;
230 edb7xxx_serial_info *edb7xxx_chan = (edb7xxx_serial_info *)chan->dev_priv;
231 #ifdef CYGDBG_IO_INIT
232 diag_printf("EDB7XXX SERIAL init - dev: %x.%d\n", edb7xxx_chan->control, edb7xxx_chan->tx_int_num);
234 (chan->callbacks->serial_init)(chan); // Really only required for interrupt driven devices
235 if (chan->out_cbuf.len != 0) {
236 cyg_drv_interrupt_create(edb7xxx_chan->tx_int_num,
237 99, // Priority - unused
238 (cyg_addrword_t)chan, // Data item passed to interrupt handler
239 edb7xxx_serial_tx_ISR,
240 edb7xxx_serial_tx_DSR,
241 &edb7xxx_chan->serial_tx_interrupt_handle,
242 &edb7xxx_chan->serial_tx_interrupt);
243 cyg_drv_interrupt_attach(edb7xxx_chan->serial_tx_interrupt_handle);
244 cyg_drv_interrupt_mask(edb7xxx_chan->tx_int_num);
245 edb7xxx_chan->tx_enabled = false;
247 if (chan->in_cbuf.len != 0) {
248 cyg_drv_interrupt_create(edb7xxx_chan->rx_int_num,
249 99, // Priority - unused
250 (cyg_addrword_t)chan, // Data item passed to interrupt handler
251 edb7xxx_serial_rx_ISR,
252 edb7xxx_serial_rx_DSR,
253 &edb7xxx_chan->serial_rx_interrupt_handle,
254 &edb7xxx_chan->serial_rx_interrupt);
255 cyg_drv_interrupt_attach(edb7xxx_chan->serial_rx_interrupt_handle);
256 cyg_drv_interrupt_unmask(edb7xxx_chan->rx_int_num);
258 edb7xxx_serial_config_port(chan, &chan->config, true);
262 // This routine is called when the device is "looked" up (i.e. attached)
264 edb7xxx_serial_lookup(struct cyg_devtab_entry **tab,
265 struct cyg_devtab_entry *sub_tab,
268 serial_channel *chan = (serial_channel *)(*tab)->priv;
269 (chan->callbacks->serial_init)(chan); // Really only required for interrupt driven devices
273 // Send a character to the device output buffer.
274 // Return 'true' if character is sent to device
276 edb7xxx_serial_putc(serial_channel *chan, unsigned char c)
278 edb7xxx_serial_info *edb7xxx_chan = (edb7xxx_serial_info *)chan->dev_priv;
279 volatile cyg_uint8 *data = (volatile cyg_uint8 *)edb7xxx_chan->data;
280 volatile cyg_uint32 *stat = (volatile cyg_uint32 *)edb7xxx_chan->stat;
281 if ((*stat & SYSFLG1_UTXFF1) == 0) {
282 // Transmit buffer/FIFO is not full
291 // Fetch a character from the device input buffer, waiting if necessary
293 edb7xxx_serial_getc(serial_channel *chan)
296 edb7xxx_serial_info *edb7xxx_chan = (edb7xxx_serial_info *)chan->dev_priv;
297 volatile cyg_uint32 *data = (volatile cyg_uint32 *)edb7xxx_chan->data;
298 volatile cyg_uint32 *stat = (volatile cyg_uint32 *)edb7xxx_chan->stat;
299 while (*stat & SYSFLG1_URXFE1) ; // Wait for char
304 // Set up the device characteristics; baud rate, etc.
306 edb7xxx_serial_set_config(serial_channel *chan, cyg_uint32 key,
307 const void *xbuf, cyg_uint32 *len)
310 case CYG_IO_SET_CONFIG_SERIAL_INFO:
312 cyg_serial_info_t *config = (cyg_serial_info_t *)xbuf;
313 if ( *len < sizeof(cyg_serial_info_t) ) {
316 *len = sizeof(cyg_serial_info_t);
317 if ( true != edb7xxx_serial_config_port(chan, config, false) )
327 // Enable the transmitter (interrupt) on the device
329 edb7xxx_serial_start_xmit(serial_channel *chan)
331 edb7xxx_serial_info *edb7xxx_chan = (edb7xxx_serial_info *)chan->dev_priv;
332 edb7xxx_chan->tx_enabled = true;
333 cyg_drv_interrupt_unmask(edb7xxx_chan->tx_int_num);
336 // Disable the transmitter on the device
338 edb7xxx_serial_stop_xmit(serial_channel *chan)
340 edb7xxx_serial_info *edb7xxx_chan = (edb7xxx_serial_info *)chan->dev_priv;
341 cyg_drv_interrupt_mask(edb7xxx_chan->tx_int_num);
342 edb7xxx_chan->tx_enabled = false;
345 // Serial I/O - low level Tx interrupt handler (ISR)
347 edb7xxx_serial_tx_ISR(cyg_vector_t vector, cyg_addrword_t data)
349 serial_channel *chan = (serial_channel *)data;
350 edb7xxx_serial_info *edb7xxx_chan = (edb7xxx_serial_info *)chan->dev_priv;
351 cyg_drv_interrupt_mask(edb7xxx_chan->tx_int_num);
352 cyg_drv_interrupt_acknowledge(edb7xxx_chan->tx_int_num);
353 return CYG_ISR_CALL_DSR; // Cause DSR to be run
356 // Serial I/O - high level Tx interrupt handler (DSR)
358 edb7xxx_serial_tx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
360 serial_channel *chan = (serial_channel *)data;
361 edb7xxx_serial_info *edb7xxx_chan = (edb7xxx_serial_info *)chan->dev_priv;
362 (chan->callbacks->xmt_char)(chan);
363 if (edb7xxx_chan->tx_enabled) {
364 cyg_drv_interrupt_unmask(edb7xxx_chan->tx_int_num);
368 // Serial I/O - low level Rx interrupt handler (ISR)
370 edb7xxx_serial_rx_ISR(cyg_vector_t vector, cyg_addrword_t data)
372 serial_channel *chan = (serial_channel *)data;
373 edb7xxx_serial_info *edb7xxx_chan = (edb7xxx_serial_info *)chan->dev_priv;
374 cyg_drv_interrupt_mask(edb7xxx_chan->rx_int_num);
375 cyg_drv_interrupt_acknowledge(edb7xxx_chan->rx_int_num);
376 return CYG_ISR_CALL_DSR; // Cause DSR to be run
379 // Serial I/O - high level Rx interrupt handler (DSR)
381 edb7xxx_serial_rx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
383 serial_channel *chan = (serial_channel *)data;
384 edb7xxx_serial_info *edb7xxx_chan = (edb7xxx_serial_info *)chan->dev_priv;
385 volatile cyg_uint32 *datreg = (volatile cyg_uint32 *)edb7xxx_chan->data;
386 volatile cyg_uint32 *stat = (volatile cyg_uint32 *)edb7xxx_chan->stat;
387 while (!(*stat & SYSFLG1_URXFE1))
388 (chan->callbacks->rcv_char)(chan, *datreg);
389 cyg_drv_interrupt_unmask(edb7xxx_chan->rx_int_num);
392 // Serial I/O - low level Ms interrupt handler (ISR)
394 edb7xxx_serial_ms_ISR(cyg_vector_t vector, cyg_addrword_t data)
396 serial_channel *chan = (serial_channel *)data;
397 edb7xxx_serial_info *edb7xxx_chan = (edb7xxx_serial_info *)chan->dev_priv;
398 cyg_drv_interrupt_mask(edb7xxx_chan->ms_int_num);
399 cyg_drv_interrupt_acknowledge(edb7xxx_chan->ms_int_num);
400 return CYG_ISR_CALL_DSR; // Cause DSR to be run
403 // Serial I/O - high level Ms interrupt handler (DSR)
405 edb7xxx_serial_ms_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
408 #endif // CYGPKG_IO_SERIAL_ARM_EDB7XXX