1 //==========================================================================
3 // io/serial/arm/s3c4510_serial.c
5 // ARM S3C4510 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####
43 // Author(s): Lars.Lindqvist@combitechsystems.com
44 // Contributors: jlarmour
46 // Purpose: ARM S3C4510 Serial I/O Interface Module (interrupt driven)
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/io/serialio.h>
61 #include <cyg/infra/diag.h>
63 #if defined(CYGPKG_IO_SERIAL_ARM_S3C4510) && defined(CYGDAT_IO_SERIAL_ARM_S3C4510_INL)
65 typedef struct s3c4510_serial_info {
69 cyg_interrupt serial_tx_interrupt;
70 cyg_interrupt serial_rx_interrupt;
71 cyg_handle_t serial_tx_interrupt_handle;
72 cyg_handle_t serial_rx_interrupt_handle;
74 } s3c4510_serial_info;
76 static bool s3c4510_serial_init(struct cyg_devtab_entry *tab);
77 static bool s3c4510_serial_putc(serial_channel *chan, unsigned char c);
78 static Cyg_ErrNo s3c4510_serial_lookup(struct cyg_devtab_entry **tab,
79 struct cyg_devtab_entry *sub_tab,
81 static unsigned char s3c4510_serial_getc(serial_channel *chan);
82 static Cyg_ErrNo s3c4510_serial_set_config(serial_channel *chan,
84 const void *xbuf, cyg_uint32 *len);
85 static void s3c4510_serial_start_xmit(serial_channel *chan);
86 static void s3c4510_serial_stop_xmit(serial_channel *chan);
88 static cyg_uint32 s3c4510_serial_tx_ISR(cyg_vector_t vector, cyg_addrword_t data);
89 static void s3c4510_serial_tx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
90 static cyg_uint32 s3c4510_serial_rx_ISR(cyg_vector_t vector, cyg_addrword_t data);
91 static void s3c4510_serial_rx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
93 static SERIAL_FUNS(s3c4510_serial_funs,
96 s3c4510_serial_set_config,
97 s3c4510_serial_start_xmit,
98 s3c4510_serial_stop_xmit
101 #include CYGDAT_IO_SERIAL_ARM_S3C4510_INL
103 #include "s3c4510_serial.h"
105 // Internal function to actually configure the hardware to desired baud rate, etc.
107 s3c4510_serial_config_port(serial_channel *chan, cyg_serial_info_t *new_config, bool init)
109 s3c4510_serial_info *s3c4510_chan = (s3c4510_serial_info *)chan->dev_priv;
110 volatile struct serial_port *port = (volatile struct serial_port *)s3c4510_chan->base;
111 cyg_uint32 word_length = select_word_length[(new_config->word_length)-CYGNUM_SERIAL_WORD_LENGTH_5];
112 cyg_uint32 stop_bits = select_stop_bits[(new_config->stop)-CYGNUM_SERIAL_STOP_1];
113 cyg_uint32 parity_mode = select_parity[(new_config->parity)-CYGNUM_SERIAL_PARITY_NONE];
114 cyg_uint32 baud_divisor = select_baud[(new_config->baud)-CYGNUM_SERIAL_BAUD_50];
115 cyg_uint32 res = word_length | stop_bits | parity_mode | ULCON_SCI | ULCON_IROFF;
116 if ((word_length|stop_bits|parity_mode|baud_divisor) == U_NOT_SUPP) {
119 port->REG_ULCON = res;
120 port->REG_UCON = UCON_RXMINT | UCON_RXSIOFF | UCON_TXMINT | UCON_DSROFF | UCON_SBKOFF | UCON_LPBOFF;
121 port->REG_UBRDIV = baud_divisor;
122 if (new_config != &chan->config) {
123 chan->config = *new_config;
128 // Function to initialize the device. Called at bootstrap time.
130 s3c4510_serial_init(struct cyg_devtab_entry *tab)
132 serial_channel *chan = (serial_channel *)tab->priv;
133 s3c4510_serial_info *s3c4510_chan = (s3c4510_serial_info *)chan->dev_priv;
134 #ifdef CYGDBG_IO_INIT
135 diag_printf("S3C4510 SERIAL init - dev: %x.%d.%d\n", s3c4510_chan->base, s3c4510_chan->tx_int_num, s3c4510_chan->rx_int_num);
137 (chan->callbacks->serial_init)(chan); // Really only required for interrupt driven devices
138 if (chan->out_cbuf.len != 0) { // If bufferlength > 0 then interrupts are used for tx
139 cyg_drv_interrupt_create(s3c4510_chan->tx_int_num,
140 99, // Priority - unused
141 (cyg_addrword_t)chan, // Data item passed to interrupt handler
142 s3c4510_serial_tx_ISR,
143 s3c4510_serial_tx_DSR,
144 &s3c4510_chan->serial_tx_interrupt_handle,
145 &s3c4510_chan->serial_tx_interrupt);
146 cyg_drv_interrupt_attach(s3c4510_chan->serial_tx_interrupt_handle);
147 cyg_drv_interrupt_mask(s3c4510_chan->tx_int_num);
148 s3c4510_chan->tx_enabled = false;
150 if (chan->in_cbuf.len != 0) { // If bufferlength > 0 then interrupts are used for rx
151 cyg_drv_interrupt_create(s3c4510_chan->rx_int_num,
152 99, // Priority - unused
153 (cyg_addrword_t)chan, // Data item passed to interrupt handler
154 s3c4510_serial_rx_ISR,
155 s3c4510_serial_rx_DSR,
156 &s3c4510_chan->serial_rx_interrupt_handle,
157 &s3c4510_chan->serial_rx_interrupt);
158 cyg_drv_interrupt_attach(s3c4510_chan->serial_rx_interrupt_handle);
159 cyg_drv_interrupt_unmask(s3c4510_chan->rx_int_num);
161 s3c4510_serial_config_port(chan, &chan->config, true);
165 // This routine is called when the device is "looked" up (i.e. attached)
167 s3c4510_serial_lookup(struct cyg_devtab_entry **tab,
168 struct cyg_devtab_entry *sub_tab,
171 serial_channel *chan = (serial_channel *)(*tab)->priv;
172 (chan->callbacks->serial_init)(chan); // Really only required for interrupt driven devices
176 // Send a character to the device output buffer.
177 // Return 'true' if character is sent to device
179 s3c4510_serial_putc(serial_channel *chan, unsigned char c)
181 s3c4510_serial_info *s3c4510_chan = (s3c4510_serial_info *)chan->dev_priv;
182 volatile struct serial_port *port = (volatile struct serial_port *)s3c4510_chan->base;
184 if (port->REG_USTAT & USTAT_TBE) {
185 // Transmit buffer is empty
186 port->REG_UTXBUF = c;
194 // Fetch a character from the device input buffer, waiting if necessary
196 s3c4510_serial_getc(serial_channel *chan)
199 s3c4510_serial_info *s3c4510_chan = (s3c4510_serial_info *)chan->dev_priv;
200 volatile struct serial_port *port = (volatile struct serial_port *)s3c4510_chan->base;
201 while ((port->REG_USTAT & USTAT_RDR) == 0) ; // Wait for char
202 c = port->REG_URXBUF;
206 // Set up the device characteristics; baud rate, etc.
208 s3c4510_serial_set_config(serial_channel *chan, cyg_uint32 key,
209 const void *xbuf, cyg_uint32 *len)
212 case CYG_IO_SET_CONFIG_SERIAL_INFO:
214 cyg_serial_info_t *config = (cyg_serial_info_t *)xbuf;
215 if ( *len < sizeof(cyg_serial_info_t) ) {
218 *len = sizeof(cyg_serial_info_t);
219 if ( true != s3c4510_serial_config_port(chan, config, false) )
229 // Enable the transmitter on the device
231 s3c4510_serial_start_xmit(serial_channel *chan)
233 s3c4510_serial_info *s3c4510_chan = (s3c4510_serial_info *)chan->dev_priv;
234 s3c4510_chan->tx_enabled = true;
235 (chan->callbacks->xmt_char)(chan);
236 if (s3c4510_chan->tx_enabled) {
237 cyg_drv_interrupt_unmask(s3c4510_chan->tx_int_num);
241 // Disable the transmitter on the device
243 s3c4510_serial_stop_xmit(serial_channel *chan)
245 s3c4510_serial_info *s3c4510_chan = (s3c4510_serial_info *)chan->dev_priv;
246 cyg_drv_interrupt_mask(s3c4510_chan->tx_int_num);
247 s3c4510_chan->tx_enabled = false;
250 // Serial I/O - low level tx interrupt handler (ISR)
252 s3c4510_serial_tx_ISR(cyg_vector_t vector, cyg_addrword_t data)
254 serial_channel *chan = (serial_channel *)data;
255 s3c4510_serial_info *s3c4510_chan = (s3c4510_serial_info *)chan->dev_priv;
256 cyg_drv_interrupt_mask(s3c4510_chan->tx_int_num);
257 cyg_drv_interrupt_acknowledge(s3c4510_chan->tx_int_num);
258 return CYG_ISR_CALL_DSR; // Cause DSR to be run
261 // Serial I/O - high level tx interrupt handler (DSR)
263 s3c4510_serial_tx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
265 serial_channel *chan = (serial_channel *)data;
266 s3c4510_serial_info *s3c4510_chan = (s3c4510_serial_info *)chan->dev_priv;
267 (chan->callbacks->xmt_char)(chan);
268 if (s3c4510_chan->tx_enabled) {
269 cyg_drv_interrupt_unmask(s3c4510_chan->tx_int_num);
273 // Serial I/O - low level rx interrupt handler (ISR)
275 s3c4510_serial_rx_ISR(cyg_vector_t vector, cyg_addrword_t data)
277 serial_channel *chan = (serial_channel *)data;
278 s3c4510_serial_info *s3c4510_chan = (s3c4510_serial_info *)chan->dev_priv;
279 cyg_drv_interrupt_mask(s3c4510_chan->rx_int_num);
280 cyg_drv_interrupt_acknowledge(s3c4510_chan->rx_int_num);
281 return CYG_ISR_CALL_DSR; // Cause DSR to be run
284 // Serial I/O - high level rx interrupt handler (DSR)
286 s3c4510_serial_rx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
288 serial_channel *chan = (serial_channel *)data;
289 s3c4510_serial_info *s3c4510_chan = (s3c4510_serial_info *)chan->dev_priv;
290 volatile struct serial_port *port = (volatile struct serial_port *)s3c4510_chan->base;
291 (chan->callbacks->rcv_char)(chan, port->REG_URXBUF);
292 cyg_drv_interrupt_unmask(s3c4510_chan->rx_int_num);
295 #endif // CYGPKG_IO_SERIAL_ARM_S3C4510 && CYGDAT_IO_SERIAL_ARM_S3C4510_INL
297 // EOF s3c4510_serial.c