1 //==========================================================================
3 // io/serial/powerpc/cogent_serial_with_ints.c
5 // PowerPC Cogent 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): jskov, based on ARM driver by gthomas
44 // Contributors:gthomas, jskov
46 // Purpose: Cogent Serial I/O module (interrupt driven version)
50 // Put in magic to effectively use the FIFOs. Transmitter FIFO fill is a
51 // problem, and setting receiver FIFO interrupts to happen only after
52 // n chars may conflict with hal diag.
54 //####DESCRIPTIONEND####
55 //==========================================================================
57 #include <pkgconf/io_serial.h>
58 #include <pkgconf/io.h>
60 #include <cyg/io/io.h>
61 #include <cyg/hal/hal_intr.h>
62 #include <cyg/io/devtab.h>
63 #include <cyg/infra/diag.h>
64 #include <cyg/io/serial.h>
66 #ifdef CYGPKG_IO_SERIAL_POWERPC_COGENT
68 #include "cogent_serial.h"
70 // Make sure the configuration is sane.
71 #if defined(CYGPKG_IO_SERIAL_POWERPC_COGENT_SERIAL_A) && \
72 defined(CYGPKG_IO_SERIAL_POWERPC_COGENT_SERIAL_B) && \
73 !defined(CYGIMP_KERNEL_INTERRUPTS_CHAIN)
74 #error "Need CYGIMP_KERNEL_INTERRUPTS_CHAIN to support both ports"
80 typedef struct cogent_serial_info {
83 cyg_interrupt serial_interrupt;
84 cyg_handle_t serial_interrupt_handle;
87 static bool cogent_serial_init(struct cyg_devtab_entry *tab);
88 static bool cogent_serial_putc(serial_channel *chan, unsigned char c);
89 static Cyg_ErrNo cogent_serial_lookup(struct cyg_devtab_entry **tab,
90 struct cyg_devtab_entry *sub_tab,
92 static unsigned char cogent_serial_getc(serial_channel *chan);
93 static Cyg_ErrNo cogent_serial_set_config(serial_channel *chan, cyg_uint32 key,
94 const void *xbuf, cyg_uint32 *len);
95 static void cogent_serial_start_xmit(serial_channel *chan);
96 static void cogent_serial_stop_xmit(serial_channel *chan);
98 static cyg_uint32 cogent_serial_ISR(cyg_vector_t vector, cyg_addrword_t data);
99 static void cogent_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
101 static SERIAL_FUNS(cogent_serial_funs,
104 cogent_serial_set_config,
105 cogent_serial_start_xmit,
106 cogent_serial_stop_xmit
109 #ifdef CYGPKG_IO_SERIAL_POWERPC_COGENT_SERIAL_A
110 static cogent_serial_info cogent_serial_info0 ={CMA_SER_16550_BASE_A,
111 CYGNUM_HAL_INTERRUPT_SIU_IRQ1};
112 #if CYGNUM_IO_SERIAL_POWERPC_COGENT_SERIAL_A_BUFSIZE > 0
113 static unsigned char cogent_serial_out_buf0[CYGNUM_IO_SERIAL_POWERPC_COGENT_SERIAL_A_BUFSIZE];
114 static unsigned char cogent_serial_in_buf0[CYGNUM_IO_SERIAL_POWERPC_COGENT_SERIAL_A_BUFSIZE];
116 static SERIAL_CHANNEL_USING_INTERRUPTS(cogent_serial_channel0,
119 CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_COGENT_SERIAL_A_BAUD),
120 CYG_SERIAL_STOP_DEFAULT,
121 CYG_SERIAL_PARITY_DEFAULT,
122 CYG_SERIAL_WORD_LENGTH_DEFAULT,
123 CYG_SERIAL_FLAGS_DEFAULT,
124 &cogent_serial_out_buf0[0],
125 sizeof(cogent_serial_out_buf0),
126 &cogent_serial_in_buf0[0],
127 sizeof(cogent_serial_in_buf0)
130 static SERIAL_CHANNEL(cogent_serial_channel0,
133 CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_COGENT_SERIAL_A_BAUD),
134 CYG_SERIAL_STOP_DEFAULT,
135 CYG_SERIAL_PARITY_DEFAULT,
136 CYG_SERIAL_WORD_LENGTH_DEFAULT,
137 CYG_SERIAL_FLAGS_DEFAULT
141 DEVTAB_ENTRY(cogent_serial_io0,
142 CYGDAT_IO_SERIAL_POWERPC_COGENT_SERIAL_A_NAME,
143 0, // Does not depend on a lower level interface
144 &cyg_io_serial_devio,
146 cogent_serial_lookup, // Serial driver may need initializing
147 &cogent_serial_channel0
151 #ifdef CYGPKG_IO_SERIAL_POWERPC_COGENT_SERIAL_B
152 static cogent_serial_info cogent_serial_info1 ={CMA_SER_16550_BASE_B,
153 CYGNUM_HAL_INTERRUPT_SIU_IRQ1};
154 #if CYGNUM_IO_SERIAL_POWERPC_COGENT_SERIAL_B_BUFSIZE > 0
155 static unsigned char cogent_serial_out_buf1[CYGNUM_IO_SERIAL_POWERPC_COGENT_SERIAL_B_BUFSIZE];
156 static unsigned char cogent_serial_in_buf1[CYGNUM_IO_SERIAL_POWERPC_COGENT_SERIAL_B_BUFSIZE];
158 static SERIAL_CHANNEL_USING_INTERRUPTS(cogent_serial_channel1,
161 CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_COGENT_SERIAL_B_BAUD),
162 CYG_SERIAL_STOP_DEFAULT,
163 CYG_SERIAL_PARITY_DEFAULT,
164 CYG_SERIAL_WORD_LENGTH_DEFAULT,
165 CYG_SERIAL_FLAGS_DEFAULT,
166 &cogent_serial_out_buf1[0],
167 sizeof(cogent_serial_out_buf1),
168 &cogent_serial_in_buf1[0],
169 sizeof(cogent_serial_in_buf1)
172 static SERIAL_CHANNEL(cogent_serial_channel1,
175 CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_COGENT_SERIAL_B_BAUD),
176 CYG_SERIAL_STOP_DEFAULT,
177 CYG_SERIAL_PARITY_DEFAULT,
178 CYG_SERIAL_WORD_LENGTH_DEFAULT,
179 CYG_SERIAL_FLAGS_DEFAULT
183 DEVTAB_ENTRY(cogent_serial_io1,
184 CYGDAT_IO_SERIAL_POWERPC_COGENT_SERIAL_B_NAME,
185 0, // Does not depend on a lower level interface
186 &cyg_io_serial_devio,
188 cogent_serial_lookup, // Serial driver may need initializing
189 &cogent_serial_channel1
195 // Internal function to actually configure the hardware to desired baud rate, etc.
197 cogent_serial_config_port(serial_channel *chan, cyg_serial_info_t *new_config, bool init)
199 cogent_serial_info *cogent_chan = (cogent_serial_info *)chan->dev_priv;
200 cyg_addrword_t port = cogent_chan->base;
201 cyg_uint16 baud_divisor = select_baud[new_config->baud];
202 cyg_uint8 _lcr, _ier;
204 if (baud_divisor == 0)
205 return false; // Invalid baud rate selected
207 // Disable port interrupts while changing hardware
208 HAL_READ_UINT8(port+SER_16550_IER, _ier);
209 HAL_WRITE_UINT8(port+SER_16550_IER, 0);
211 // Set databits, stopbits and parity.
212 _lcr = select_word_length[(new_config->word_length -
213 CYGNUM_SERIAL_WORD_LENGTH_5)] |
214 select_stop_bits[new_config->stop] |
215 select_parity[new_config->parity];
216 HAL_WRITE_UINT8(port+SER_16550_LCR, _lcr);
220 HAL_WRITE_UINT8(port+SER_16550_LCR, _lcr);
221 HAL_WRITE_UINT8(port+SER_16550_DLM, baud_divisor >> 8);
222 HAL_WRITE_UINT8(port+SER_16550_DLL, baud_divisor & 0xff);
224 HAL_WRITE_UINT8(port+SER_16550_LCR, _lcr);
227 // Enable and clear FIFO
228 HAL_WRITE_UINT8(port+SER_16550_FCR,
229 (FCR_ENABLE | FCR_CLEAR_RCVR | FCR_CLEAR_XMIT));
231 if (chan->out_cbuf.len != 0) {
232 HAL_WRITE_UINT8(port+SER_16550_IER, SIO_IER_ERDAI);
234 HAL_WRITE_UINT8(port+SER_16550_IER, 0);
238 // Special initialization for ST16C552 on CMA102
241 HAL_READ_UINT8(CMA_SER_16550_BASE_A+SER_16550_MCR, mcr);
243 HAL_WRITE_UINT8(CMA_SER_16550_BASE_A+SER_16550_MCR, mcr);
245 HAL_READ_UINT8(CMA_SER_16550_BASE_B+SER_16550_MCR, mcr);
247 HAL_WRITE_UINT8(CMA_SER_16550_BASE_B+SER_16550_MCR, mcr);
250 HAL_WRITE_UINT8(port+SER_16550_IER, _ier);
252 if (new_config != &chan->config) {
253 chan->config = *new_config;
258 // Function to initialize the device. Called at bootstrap time.
260 cogent_serial_init(struct cyg_devtab_entry *tab)
262 serial_channel *chan = (serial_channel *)tab->priv;
263 cogent_serial_info *cogent_chan = (cogent_serial_info *)chan->dev_priv;
264 #ifdef CYGDBG_IO_INIT
265 diag_printf("COGENT SERIAL init - dev: %x.%d\n", cogent_chan->base, cogent_chan->int_num);
267 (chan->callbacks->serial_init)(chan); // Really only required for interrupt driven devices
268 if (chan->out_cbuf.len != 0) {
269 cyg_drv_interrupt_create(cogent_chan->int_num,
270 0, // can change IRQ0 priority
271 (cyg_addrword_t)chan, // Data item passed to interrupt handler
274 &cogent_chan->serial_interrupt_handle,
275 &cogent_chan->serial_interrupt);
276 cyg_drv_interrupt_attach(cogent_chan->serial_interrupt_handle);
277 cyg_drv_interrupt_unmask(cogent_chan->int_num);
279 cogent_serial_config_port(chan, &chan->config, true);
283 // This routine is called when the device is "looked" up (i.e. attached)
285 cogent_serial_lookup(struct cyg_devtab_entry **tab,
286 struct cyg_devtab_entry *sub_tab,
289 serial_channel *chan = (serial_channel *)(*tab)->priv;
290 (chan->callbacks->serial_init)(chan); // Really only required for interrupt driven devices
294 // Send a character to the device output buffer.
295 // Return 'true' if character is sent to device
297 cogent_serial_putc(serial_channel *chan, unsigned char c)
299 cogent_serial_info *cogent_chan = (cogent_serial_info *)chan->dev_priv;
300 cyg_addrword_t port = cogent_chan->base;
303 HAL_READ_UINT8(port+SER_16550_LSR, _lsr);
304 if (_lsr & SIO_LSR_THRE) {
305 // Transmit buffer is empty
306 HAL_WRITE_UINT8(port+SER_16550_THR, c);
314 // Fetch a character from the device input buffer, waiting if necessary
316 cogent_serial_getc(serial_channel *chan)
319 cogent_serial_info *cogent_chan = (cogent_serial_info *)chan->dev_priv;
320 cyg_addrword_t port = cogent_chan->base;
324 HAL_READ_UINT8(port+SER_16550_LSR, _lsr);
325 } while ((_lsr & SIO_LSR_DR) == 0);
327 HAL_READ_UINT8(port+SER_16550_RBR, c);
331 // Set up the device characteristics; baud rate, etc.
333 cogent_serial_set_config(serial_channel *chan, cyg_uint32 key,
334 const void *xbuf, cyg_uint32 *len)
337 case CYG_IO_SET_CONFIG_SERIAL_INFO:
339 cyg_serial_info_t *config = (cyg_serial_info_t *)xbuf;
340 if ( *len < sizeof(cyg_serial_info_t) ) {
343 *len = sizeof(cyg_serial_info_t);
344 if ( true != cogent_serial_config_port(chan, config, false) )
354 // Enable the transmitter on the device
356 cogent_serial_start_xmit(serial_channel *chan)
358 cogent_serial_info *cogent_chan = (cogent_serial_info *)chan->dev_priv;
359 cyg_addrword_t port = cogent_chan->base;
362 HAL_READ_UINT8(port+SER_16550_IER, _ier);
363 _ier |= IER_XMT; // Enable xmit interrupt
364 HAL_WRITE_UINT8(port+SER_16550_IER, _ier);
366 (chan->callbacks->xmt_char)(chan);
369 // Disable the transmitter on the device
371 cogent_serial_stop_xmit(serial_channel *chan)
373 cogent_serial_info *cogent_chan = (cogent_serial_info *)chan->dev_priv;
374 cyg_addrword_t port = cogent_chan->base;
377 HAL_READ_UINT8(port+SER_16550_IER, _ier);
378 _ier &= ~IER_XMT; // Disable xmit interrupt
379 HAL_WRITE_UINT8(port+SER_16550_IER, _ier);
382 // Serial I/O - low level interrupt handler (ISR)
384 cogent_serial_ISR(cyg_vector_t vector, cyg_addrword_t data)
386 serial_channel *chan = (serial_channel *)data;
387 cogent_serial_info *cogent_chan = (cogent_serial_info *)chan->dev_priv;
388 cyg_drv_interrupt_mask(cogent_chan->int_num);
389 cyg_drv_interrupt_acknowledge(cogent_chan->int_num);
390 return CYG_ISR_CALL_DSR; // Cause DSR to be run
393 // Serial I/O - high level interrupt handler (DSR)
395 cogent_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
397 serial_channel *chan = (serial_channel *)data;
398 cogent_serial_info *cogent_chan = (cogent_serial_info *)chan->dev_priv;
399 cyg_addrword_t port = cogent_chan->base;
402 HAL_READ_UINT8(port+SER_16550_IIR, _iir);
403 _iir &= SIO_IIR_ID_MASK;
404 if ( ISR_Tx == _iir ) {
405 (chan->callbacks->xmt_char)(chan);
406 } else if ( ISR_Rx == _iir ) {
408 HAL_READ_UINT8(port+SER_16550_RBR, _c);
409 (chan->callbacks->rcv_char)(chan, _c);
411 cyg_drv_interrupt_unmask(cogent_chan->int_num);