comparison packages/io/serial/current/src/powerpc/cogent_serial_with_ints.c @ 2:443894e2e912 ecos-v1_2_1-release

Block commit of eCos version 1.2.1
author jlarmour
date Tue, 11 May 1999 12:24:34 +0000
parents
children d376b777e2ce
comparison
equal deleted inserted replaced
1:72f549f0d891 2:443894e2e912
1 //==========================================================================
2 //
3 // io/serial/powerpc/cogent_serial_with_ints.c
4 //
5 // PowerPC Cogent Serial I/O Interface Module (interrupt driven)
6 //
7 //==========================================================================
8 //####COPYRIGHTBEGIN####
9 //
10 // -------------------------------------------
11 // The contents of this file are subject to the Cygnus eCos Public License
12 // Version 1.0 (the "License"); you may not use this file except in
13 // compliance with the License. You may obtain a copy of the License at
14 // http://sourceware.cygnus.com/ecos
15 //
16 // Software distributed under the License is distributed on an "AS IS"
17 // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
18 // License for the specific language governing rights and limitations under
19 // the License.
20 //
21 // The Original Code is eCos - Embedded Cygnus Operating System, released
22 // September 30, 1998.
23 //
24 // The Initial Developer of the Original Code is Cygnus. Portions created
25 // by Cygnus are Copyright (C) 1998, 1999 Cygnus Solutions.
26 // All Rights Reserved.
27 // -------------------------------------------
28 //
29 //####COPYRIGHTEND####
30 //==========================================================================
31 //#####DESCRIPTIONBEGIN####
32 //
33 // Author(s): jskov, based on ARM driver by gthomas
34 // Contributors:gthomas, jskov
35 // Date: 1999-03-02
36 // Purpose: Cogent Serial I/O module (interrupt driven version)
37 // Description:
38 //
39 // To Do:
40 // Put in magic to effectively use the FIFOs. Transmitter FIFO fill is a
41 // problem, and setting receiver FIFO interrupts to happen only after
42 // n chars may conflict with hal diag.
43 //
44 //####DESCRIPTIONEND####
45 //==========================================================================
46
47 #include <pkgconf/io_serial.h>
48 #include <pkgconf/io.h>
49
50 #include <cyg/io/io.h>
51 #include <cyg/hal/hal_intr.h>
52 #include <cyg/io/devtab.h>
53 #include <cyg/infra/diag.h>
54 #include <cyg/io/serial.h>
55
56 #ifdef CYGPKG_IO_SERIAL_POWERPC_COGENT
57
58 #include "cogent_serial.h"
59
60 // Make sure the configuration is sane.
61 #if defined(CYGPKG_IO_SERIAL_POWERPC_COGENT_SERIAL_A) && \
62 defined(CYGPKG_IO_SERIAL_POWERPC_COGENT_SERIAL_B) && \
63 !defined(CYGIMP_KERNEL_INTERRUPTS_CHAIN)
64 #error "Need CYGIMP_KERNEL_INTERRUPTS_CHAIN to support both ports"
65 #endif
66
67
68 #define BUFSIZE 128
69
70 typedef struct cogent_serial_info {
71 CYG_ADDRWORD base;
72 CYG_WORD int_num;
73 cyg_interrupt serial_interrupt;
74 cyg_handle_t serial_interrupt_handle;
75 } cogent_serial_info;
76
77 static bool cogent_serial_init(struct cyg_devtab_entry *tab);
78 static bool cogent_serial_putc(serial_channel *chan, unsigned char c);
79 static Cyg_ErrNo cogent_serial_lookup(struct cyg_devtab_entry **tab,
80 struct cyg_devtab_entry *sub_tab,
81 const char *name);
82 static unsigned char cogent_serial_getc(serial_channel *chan);
83 static bool cogent_serial_set_config(serial_channel *chan, cyg_serial_info_t *config);
84 static void cogent_serial_start_xmit(serial_channel *chan);
85 static void cogent_serial_stop_xmit(serial_channel *chan);
86
87 static cyg_uint32 cogent_serial_ISR(cyg_vector_t vector, cyg_addrword_t data);
88 static void cogent_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
89
90 static SERIAL_FUNS(cogent_serial_funs,
91 cogent_serial_putc,
92 cogent_serial_getc,
93 cogent_serial_set_config,
94 cogent_serial_start_xmit,
95 cogent_serial_stop_xmit
96 );
97
98 #ifdef CYGPKG_IO_SERIAL_POWERPC_COGENT_SERIAL_A
99 static cogent_serial_info cogent_serial_info0 ={CMA_SER_16550_BASE_A,
100 CYGNUM_HAL_INTERRUPT_SIU_IRQ1};
101 #if CYGNUM_IO_SERIAL_POWERPC_COGENT_SERIAL_A_BUFSIZE > 0
102 static unsigned char cogent_serial_out_buf0[CYGNUM_IO_SERIAL_POWERPC_COGENT_SERIAL_A_BUFSIZE];
103 static unsigned char cogent_serial_in_buf0[CYGNUM_IO_SERIAL_POWERPC_COGENT_SERIAL_A_BUFSIZE];
104
105 static SERIAL_CHANNEL_USING_INTERRUPTS(cogent_serial_channel0,
106 cogent_serial_funs,
107 cogent_serial_info0,
108 CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_COGENT_SERIAL_A_BAUD),
109 CYG_SERIAL_STOP_DEFAULT,
110 CYG_SERIAL_PARITY_DEFAULT,
111 CYG_SERIAL_WORD_LENGTH_DEFAULT,
112 CYG_SERIAL_FLAGS_DEFAULT,
113 &cogent_serial_out_buf0[0],
114 sizeof(cogent_serial_out_buf0),
115 &cogent_serial_in_buf0[0],
116 sizeof(cogent_serial_in_buf0)
117 );
118 #else
119 static SERIAL_CHANNEL(cogent_serial_channel0,
120 cogent_serial_funs,
121 cogent_serial_info0,
122 CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_COGENT_SERIAL_A_BAUD),
123 CYG_SERIAL_STOP_DEFAULT,
124 CYG_SERIAL_PARITY_DEFAULT,
125 CYG_SERIAL_WORD_LENGTH_DEFAULT,
126 CYG_SERIAL_FLAGS_DEFAULT
127 );
128 #endif
129
130 DEVTAB_ENTRY(cogent_serial_io0,
131 CYGDAT_IO_SERIAL_POWERPC_COGENT_SERIAL_A_NAME,
132 0, // Does not depend on a lower level interface
133 &serial_devio,
134 cogent_serial_init,
135 cogent_serial_lookup, // Serial driver may need initializing
136 &cogent_serial_channel0
137 );
138 #endif
139
140 #ifdef CYGPKG_IO_SERIAL_POWERPC_COGENT_SERIAL_B
141 static cogent_serial_info cogent_serial_info1 ={CMA_SER_16550_BASE_B,
142 CYGNUM_HAL_INTERRUPT_SIU_IRQ1};
143 #if CYGNUM_IO_SERIAL_POWERPC_COGENT_SERIAL_B_BUFSIZE > 0
144 static unsigned char cogent_serial_out_buf1[CYGNUM_IO_SERIAL_POWERPC_COGENT_SERIAL_B_BUFSIZE];
145 static unsigned char cogent_serial_in_buf1[CYGNUM_IO_SERIAL_POWERPC_COGENT_SERIAL_B_BUFSIZE];
146
147 static SERIAL_CHANNEL_USING_INTERRUPTS(cogent_serial_channel1,
148 cogent_serial_funs,
149 cogent_serial_info1,
150 CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_COGENT_SERIAL_B_BAUD),
151 CYG_SERIAL_STOP_DEFAULT,
152 CYG_SERIAL_PARITY_DEFAULT,
153 CYG_SERIAL_WORD_LENGTH_DEFAULT,
154 CYG_SERIAL_FLAGS_DEFAULT,
155 &cogent_serial_out_buf1[0],
156 sizeof(cogent_serial_out_buf1),
157 &cogent_serial_in_buf1[0],
158 sizeof(cogent_serial_in_buf1)
159 );
160 #else
161 static SERIAL_CHANNEL(cogent_serial_channel1,
162 cogent_serial_funs,
163 cogent_serial_info1,
164 CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_COGENT_SERIAL_B_BAUD),
165 CYG_SERIAL_STOP_DEFAULT,
166 CYG_SERIAL_PARITY_DEFAULT,
167 CYG_SERIAL_WORD_LENGTH_DEFAULT,
168 CYG_SERIAL_FLAGS_DEFAULT
169 );
170 #endif
171
172 DEVTAB_ENTRY(cogent_serial_io1,
173 CYGDAT_IO_SERIAL_POWERPC_COGENT_SERIAL_B_NAME,
174 0, // Does not depend on a lower level interface
175 &serial_devio,
176 cogent_serial_init,
177 cogent_serial_lookup, // Serial driver may need initializing
178 &cogent_serial_channel1
179 );
180 #endif
181
182
183
184 // Internal function to actually configure the hardware to desired baud rate, etc.
185 static bool
186 cogent_serial_config_port(serial_channel *chan, cyg_serial_info_t *new_config, bool init)
187 {
188 cogent_serial_info *cogent_chan = (cogent_serial_info *)chan->dev_priv;
189 cyg_addrword_t port = cogent_chan->base;
190 cyg_uint16 baud_divisor = select_baud[new_config->baud];
191 cyg_uint8 _lcr, _ier;
192
193 if (baud_divisor == 0)
194 return false; // Invalid baud rate selected
195
196 // Disable port interrupts while changing hardware
197 HAL_READ_UINT8(port+SER_16550_IER, _ier);
198 HAL_WRITE_UINT8(port+SER_16550_IER, 0);
199
200 // Set databits, stopbits and parity.
201 _lcr = select_word_length[(new_config->word_length -
202 CYGNUM_SERIAL_WORD_LENGTH_5)] |
203 select_stop_bits[new_config->stop] |
204 select_parity[new_config->parity];
205 HAL_WRITE_UINT8(port+SER_16550_LCR, _lcr);
206
207 // Set baud rate.
208 _lcr |= LCR_DL;
209 HAL_WRITE_UINT8(port+SER_16550_LCR, _lcr);
210 HAL_WRITE_UINT8(port+SER_16550_DLM, baud_divisor >> 8);
211 HAL_WRITE_UINT8(port+SER_16550_DLL, baud_divisor & 0xff);
212 _lcr &= ~LCR_DL;
213 HAL_WRITE_UINT8(port+SER_16550_LCR, _lcr);
214
215 if (init) {
216 // Enable and clear FIFO
217 HAL_WRITE_UINT8(port+SER_16550_FCR,
218 (FCR_ENABLE | FCR_CLEAR_RCVR | FCR_CLEAR_XMIT));
219
220 if (chan->out_cbuf.len != 0) {
221 HAL_WRITE_UINT8(port+SER_16550_IER, SIO_IER_ERDAI);
222 } else {
223 HAL_WRITE_UINT8(port+SER_16550_IER, 0);
224 }
225
226 {
227 // Special initialization for ST16C552 on CMA102
228 cyg_uint8 mcr;
229
230 HAL_READ_UINT8(CMA_SER_16550_BASE_A+SER_16550_MCR, mcr);
231 mcr |= 8;
232 HAL_WRITE_UINT8(CMA_SER_16550_BASE_A+SER_16550_MCR, mcr);
233
234 HAL_READ_UINT8(CMA_SER_16550_BASE_B+SER_16550_MCR, mcr);
235 mcr |= 8;
236 HAL_WRITE_UINT8(CMA_SER_16550_BASE_B+SER_16550_MCR, mcr);
237 }
238 } else {
239 HAL_WRITE_UINT8(port+SER_16550_IER, _ier);
240 }
241 if (new_config != &chan->config) {
242 chan->config = *new_config;
243 }
244 return true;
245 }
246
247 // Function to initialize the device. Called at bootstrap time.
248 static bool
249 cogent_serial_init(struct cyg_devtab_entry *tab)
250 {
251 serial_channel *chan = (serial_channel *)tab->priv;
252 cogent_serial_info *cogent_chan = (cogent_serial_info *)chan->dev_priv;
253 #ifdef CYGDBG_IO_INIT
254 diag_printf("COGENT SERIAL init - dev: %x.%d\n", cogent_chan->base, cogent_chan->int_num);
255 #endif
256 (chan->callbacks->serial_init)(chan); // Really only required for interrupt driven devices
257 if (chan->out_cbuf.len != 0) {
258 cyg_drv_interrupt_create(cogent_chan->int_num,
259 0, // can change IRQ0 priority
260 (cyg_addrword_t)chan, // Data item passed to interrupt handler
261 cogent_serial_ISR,
262 cogent_serial_DSR,
263 &cogent_chan->serial_interrupt_handle,
264 &cogent_chan->serial_interrupt);
265 cyg_drv_interrupt_attach(cogent_chan->serial_interrupt_handle);
266 cyg_drv_interrupt_unmask(cogent_chan->int_num);
267 }
268 cogent_serial_config_port(chan, &chan->config, true);
269 return true;
270 }
271
272 // This routine is called when the device is "looked" up (i.e. attached)
273 static Cyg_ErrNo
274 cogent_serial_lookup(struct cyg_devtab_entry **tab,
275 struct cyg_devtab_entry *sub_tab,
276 const char *name)
277 {
278 serial_channel *chan = (serial_channel *)(*tab)->priv;
279 (chan->callbacks->serial_init)(chan); // Really only required for interrupt driven devices
280 return ENOERR;
281 }
282
283 // Send a character to the device output buffer.
284 // Return 'true' if character is sent to device
285 static bool
286 cogent_serial_putc(serial_channel *chan, unsigned char c)
287 {
288 cogent_serial_info *cogent_chan = (cogent_serial_info *)chan->dev_priv;
289 cyg_addrword_t port = cogent_chan->base;
290 cyg_uint8 _lsr;
291
292 HAL_READ_UINT8(port+SER_16550_LSR, _lsr);
293 if (_lsr & SIO_LSR_THRE) {
294 // Transmit buffer is empty
295 HAL_WRITE_UINT8(port+SER_16550_THR, c);
296 return true;
297 } else {
298 // No space
299 return false;
300 }
301 }
302
303 // Fetch a character from the device input buffer, waiting if necessary
304 static unsigned char
305 cogent_serial_getc(serial_channel *chan)
306 {
307 unsigned char c;
308 cogent_serial_info *cogent_chan = (cogent_serial_info *)chan->dev_priv;
309 cyg_addrword_t port = cogent_chan->base;
310 cyg_uint8 _lsr;
311
312 do {
313 HAL_READ_UINT8(port+SER_16550_LSR, _lsr);
314 } while ((_lsr & SIO_LSR_DR) == 0);
315
316 HAL_READ_UINT8(port+SER_16550_RBR, c);
317 return c;
318 }
319
320 // Set up the device characteristics; baud rate, etc.
321 static bool
322 cogent_serial_set_config(serial_channel *chan, cyg_serial_info_t *config)
323 {
324 return cogent_serial_config_port(chan, config, false);
325 }
326
327 // Enable the transmitter on the device
328 static void
329 cogent_serial_start_xmit(serial_channel *chan)
330 {
331 cogent_serial_info *cogent_chan = (cogent_serial_info *)chan->dev_priv;
332 cyg_addrword_t port = cogent_chan->base;
333 cyg_uint8 _ier;
334
335 HAL_READ_UINT8(port+SER_16550_IER, _ier);
336 _ier |= IER_XMT; // Enable xmit interrupt
337 HAL_WRITE_UINT8(port+SER_16550_IER, _ier);
338
339 (chan->callbacks->xmt_char)(chan);
340 }
341
342 // Disable the transmitter on the device
343 static void
344 cogent_serial_stop_xmit(serial_channel *chan)
345 {
346 cogent_serial_info *cogent_chan = (cogent_serial_info *)chan->dev_priv;
347 cyg_addrword_t port = cogent_chan->base;
348 cyg_uint8 _ier;
349
350 HAL_READ_UINT8(port+SER_16550_IER, _ier);
351 _ier &= ~IER_XMT; // Disable xmit interrupt
352 HAL_WRITE_UINT8(port+SER_16550_IER, _ier);
353 }
354
355 // Serial I/O - low level interrupt handler (ISR)
356 static cyg_uint32
357 cogent_serial_ISR(cyg_vector_t vector, cyg_addrword_t data)
358 {
359 serial_channel *chan = (serial_channel *)data;
360 cogent_serial_info *cogent_chan = (cogent_serial_info *)chan->dev_priv;
361 cyg_drv_interrupt_mask(cogent_chan->int_num);
362 cyg_drv_interrupt_acknowledge(cogent_chan->int_num);
363 return CYG_ISR_CALL_DSR; // Cause DSR to be run
364 }
365
366 // Serial I/O - high level interrupt handler (DSR)
367 static void
368 cogent_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
369 {
370 serial_channel *chan = (serial_channel *)data;
371 cogent_serial_info *cogent_chan = (cogent_serial_info *)chan->dev_priv;
372 cyg_addrword_t port = cogent_chan->base;
373 cyg_uint8 _iir;
374
375 HAL_READ_UINT8(port+SER_16550_IIR, _iir);
376 if ((_iir & SIO_IIR_ID_MASK) == ISR_Tx) {
377 (chan->callbacks->xmt_char)(chan);
378 } else {
379 // Assume receive interrupt
380 cyg_uint8 _c;
381 HAL_READ_UINT8(port+SER_16550_RBR, _c);
382 (chan->callbacks->rcv_char)(chan, _c);
383 }
384 cyg_drv_interrupt_unmask(cogent_chan->int_num);
385 }
386 #endif