comparison packages/devs/serial/powerpc/cogent/current/src/cogent_serial_with_ints.c @ 82:6736c52df507 ecos-sw-2000-04-14

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