]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/devs/serial/mips/idt79s334a/v2_0/src/mipsidt_serial.c
Initial revision
[karo-tx-redboot.git] / packages / devs / serial / mips / idt79s334a / v2_0 / src / mipsidt_serial.c
1 //==========================================================================
2 //
3 //      mipsidt_serial.c
4 //
5 //      Serial device driver for MIPS IDT79s334a reference platform on-chip serial devices
6 //
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.
12 //
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.
16 //
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
20 // for more details.
21 //
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.
25 //
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.
32 //
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.
35 //
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####
42 //
43 // Author(s):    tmichals based on driver by dmoseley, based on POWERPC driver by jskov
44 // Contributors: gthomas, jskov, dmoseley, tmichals
45 // Date:         2003-02-13
46 // Purpose:      MIPS IDT79s334a reference platform serial device driver
47 // Description:  IDT MIPS serial device driver
48 //
49 // To Do:
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.
53 //
54 //####DESCRIPTIONEND####
55 //
56 //==========================================================================
57
58 #include <pkgconf/io_serial.h>
59 #include <pkgconf/io.h>
60
61 #include <cyg/io/io.h>
62 #include <cyg/hal/hal_intr.h>
63 #include <cyg/io/devtab.h>
64 #include <cyg/infra/diag.h>
65 #include <cyg/io/serial.h>
66
67 #ifdef CYGPKG_IO_SERIAL_MIPS_IDT79S334A
68
69 #include "mipsidt_serial.h"
70
71 typedef struct mipsidt_serial_info {
72     CYG_ADDRWORD   base;
73     CYG_WORD       int_num;
74     cyg_interrupt  serial_interrupt;
75     cyg_handle_t   serial_interrupt_handle;
76     cyg_uint8      iir;
77 } mipsidt_serial_info;
78
79 static bool mipsidt_serial_init(struct cyg_devtab_entry *tab);
80 static bool mipsidt_serial_putc(serial_channel *chan, unsigned char c);
81 static Cyg_ErrNo mipsidt_serial_lookup(struct cyg_devtab_entry **tab, 
82                                    struct cyg_devtab_entry *sub_tab,
83                                    const char *name);
84 static unsigned char mipsidt_serial_getc(serial_channel *chan);
85 static Cyg_ErrNo mipsidt_serial_set_config(serial_channel *chan, cyg_uint32 key,
86                                          const void *xbuf, cyg_uint32 *len);
87 static void mipsidt_serial_start_xmit(serial_channel *chan);
88 static void mipsidt_serial_stop_xmit(serial_channel *chan);
89
90 static cyg_uint32 mipsidt_serial_ISR(cyg_vector_t vector, cyg_addrword_t data);
91 static void       mipsidt_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
92
93 static SERIAL_FUNS(mipsidt_serial_funs, 
94                    mipsidt_serial_putc, 
95                    mipsidt_serial_getc,
96                    mipsidt_serial_set_config,
97                    mipsidt_serial_start_xmit,
98                    mipsidt_serial_stop_xmit
99     );
100
101
102 #ifdef CYGPKG_IO_SERIAL_MIPS_IDT79S334A_SERIAL_A
103 static mipsidt_serial_info mipsidt_serial_info0 ={IDTMIPS_SER_16550_BASE_A,  
104                                                   CYGNUM_HAL_INTERRUPT_SIO_0};
105
106 #if CYGNUM_IO_SERIAL_MIPS_IDT79S334A_SERIAL_A_BUFSIZE > 0
107 static unsigned char mipsidt_serial_out_buf0[CYGNUM_IO_SERIAL_MIPS_IDT79S334A_SERIAL_A_BUFSIZE];
108 static unsigned char mipsidt_serial_in_buf0[CYGNUM_IO_SERIAL_MIPS_IDT79S334A_SERIAL_A_BUFSIZE];
109
110 static SERIAL_CHANNEL_USING_INTERRUPTS(mipsidt_serial_channel0,
111                                        mipsidt_serial_funs, 
112                                        mipsidt_serial_info0,
113                                        CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_MIPS_IDT79S334A_SERIAL_A_BAUD),
114                                        CYG_SERIAL_STOP_DEFAULT,
115                                        CYG_SERIAL_PARITY_DEFAULT,
116                                        CYG_SERIAL_WORD_LENGTH_DEFAULT,
117                                        CYG_SERIAL_FLAGS_DEFAULT,
118                                        &mipsidt_serial_out_buf0[0], 
119                                        sizeof(mipsidt_serial_out_buf0),
120                                        &mipsidt_serial_in_buf0[0], 
121                                        sizeof(mipsidt_serial_in_buf0)
122     );
123 #else
124 static SERIAL_CHANNEL(mipsidt_serial_channel0,
125                       mipsidt_serial_funs, 
126                       mipsidt_serial_info0,
127                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_MIPS_IDT79S334A_SERIAL_A_BAUD),
128                       CYG_SERIAL_STOP_DEFAULT,
129                       CYG_SERIAL_PARITY_DEFAULT,
130                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
131                       CYG_SERIAL_FLAGS_DEFAULT
132     );
133 #endif
134
135 DEVTAB_ENTRY(mipsidt_serial_io0, 
136              CYGDAT_IO_SERIAL_MIPS_IDT79S334A_SERIAL_A_NAME,
137              0,                 // Does not depend on a lower level interface
138              &cyg_io_serial_devio, 
139              mipsidt_serial_init, 
140              mipsidt_serial_lookup,     // Serial driver may need initializing
141              &mipsidt_serial_channel0
142     );
143
144 #endif
145
146 #ifdef CYGPKG_IO_SERIAL_MIPS_IDT79S334A_SERIAL_B
147 static mipsidt_serial_info mipsidt_serial_info1 ={IDTMIPS_SER_16550_BASE_B,
148                                                   CYGNUM_HAL_INTERRUPT_SIO_1};
149
150 #if CYGNUM_IO_SERIAL_MIPS_IDT79S334A_SERIAL_B_BUFSIZE > 0
151 static unsigned char mipsidt_serial_out_buf1[CYGNUM_IO_SERIAL_MIPS_IDT79S334A_SERIAL_B_BUFSIZE];
152 static unsigned char mipsidt_serial_in_buf1[CYGNUM_IO_SERIAL_MIPS_IDT79S334A_SERIAL_B_BUFSIZE];
153
154 static SERIAL_CHANNEL_USING_INTERRUPTS(mipsidt_serial_channel1,
155                                        mipsidt_serial_funs,
156                                        mipsidt_serial_info1,
157                                        CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_MIPS_IDT79S334A_SERIAL_B_BAUD),
158                                        CYG_SERIAL_STOP_DEFAULT,
159                                        CYG_SERIAL_PARITY_DEFAULT,
160                                        CYG_SERIAL_WORD_LENGTH_DEFAULT,
161                                        CYG_SERIAL_FLAGS_DEFAULT,
162                                        &mipsidt_serial_out_buf1[0],
163                                        sizeof(mipsidt_serial_out_buf1),
164                                        &mipsidt_serial_in_buf1[0],
165                                        sizeof(mipsidt_serial_in_buf1)
166     );
167 #else
168 static SERIAL_CHANNEL(mipsidt_serial_channel1,
169                       mipsidt_serial_funs,
170                       mipsidt_serial_info1,
171                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_MIPS_IDT79S334A_SERIAL_B_BAUD),
172                       CYG_SERIAL_STOP_DEFAULT,
173                       CYG_SERIAL_PARITY_DEFAULT,
174                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
175                       CYG_SERIAL_FLAGS_DEFAULT
176     );
177 #endif
178
179 DEVTAB_ENTRY(mipsidt_serial_io1,
180              CYGDAT_IO_SERIAL_MIPS_IDT79S334A_SERIAL_B_NAME,
181              0,                 // Does not depend on a lower level interface
182              &cyg_io_serial_devio,
183              mipsidt_serial_init,
184              mipsidt_serial_lookup,     // Serial driver may need initializing
185              &mipsidt_serial_channel1
186     );
187
188 #endif
189
190 // Internal function to actually configure the hardware to desired baud rate, etc.
191 static bool
192 mipsidt_serial_config_port(serial_channel *chan, cyg_serial_info_t *new_config, bool init)
193 {
194     mipsidt_serial_info *mipsidt_chan = (mipsidt_serial_info *)chan->dev_priv;
195     cyg_addrword_t port = mipsidt_chan->base;
196     cyg_uint32 baud_divisor = select_baud[new_config->baud]; ;
197     cyg_uint8 _lcr, _ier;
198
199     if (baud_divisor == 0)
200         return false;    // Invalid baud rate selected
201
202         baud_divisor = (CYGHWR_HAL_MIPS_CPU_FREQ_ACTUAL * 10) / (16 * select_baud[new_config->baud]);
203
204         baud_divisor +=5;
205         baud_divisor =  ((cyg_int32)baud_divisor) / 10;
206
207
208     // Disable port interrupts while changing hardware
209     HAL_READ_UINT8(port+SER_16550_IER, _ier);
210     HAL_WRITE_UINT8(port+SER_16550_IER, 0);
211
212     // Set databits, stopbits and parity.
213     _lcr = select_word_length[(new_config->word_length -
214                                CYGNUM_SERIAL_WORD_LENGTH_5)] | 
215         select_stop_bits[new_config->stop] |
216         select_parity[new_config->parity];
217     HAL_WRITE_UINT8(port+SER_16550_LCR, _lcr);
218
219     // Set baud rate.
220     _lcr |= LCR_DL;
221     HAL_WRITE_UINT8(port+SER_16550_LCR, _lcr);
222     HAL_WRITE_UINT8(port+SER_16550_DLM, baud_divisor >> 8);
223     HAL_WRITE_UINT8(port+SER_16550_DLL, baud_divisor & 0xff);
224     _lcr &= ~LCR_DL;
225     HAL_WRITE_UINT8(port+SER_16550_LCR, _lcr);
226
227     if (init) {
228         // Enable and clear FIFO
229         HAL_WRITE_UINT8(port+SER_16550_FCR,
230                         (FCR_ENABLE | FCR_CLEAR_RCVR | FCR_CLEAR_XMIT));
231
232         if (chan->out_cbuf.len != 0) {
233             HAL_WRITE_UINT8(port+SER_16550_IER, SIO_IER_ERDAI);
234         } else {
235             HAL_WRITE_UINT8(port+SER_16550_IER, 0);
236         }
237     } else {
238         HAL_WRITE_UINT8(port+SER_16550_IER, _ier);
239     }
240     if (new_config != &chan->config) {
241         chan->config = *new_config;
242     }
243     return true;
244 }
245
246 // Function to initialize the device.  Called at bootstrap time.
247 static bool 
248 mipsidt_serial_init(struct cyg_devtab_entry *tab)
249 {
250     serial_channel *chan = (serial_channel *)tab->priv;
251     mipsidt_serial_info *mipsidt_chan = (mipsidt_serial_info *)chan->dev_priv;
252 #ifdef CYGDBG_IO_INIT
253     diag_printf("IDT SERIAL init - dev: %x.%d\n", mipsidt_chan->base, mipsidt_chan->int_num);
254 #endif
255     (chan->callbacks->serial_init)(chan);  // Really only required for interrupt driven devices
256     if (chan->out_cbuf.len != 0) {
257         cyg_drv_interrupt_create(mipsidt_chan->int_num,
258                                  0,         // can change IRQ0 priority
259                                  (cyg_addrword_t)chan,   //  Data item passed to interrupt handler
260                                  mipsidt_serial_ISR,
261                                  mipsidt_serial_DSR,
262                                  &mipsidt_chan->serial_interrupt_handle,
263                                  &mipsidt_chan->serial_interrupt);
264         cyg_drv_interrupt_attach(mipsidt_chan->serial_interrupt_handle);
265         cyg_drv_interrupt_unmask(mipsidt_chan->int_num);
266     }
267     mipsidt_serial_config_port(chan, &chan->config, true);
268     return true;
269 }
270
271 // This routine is called when the device is "looked" up (i.e. attached)
272 static Cyg_ErrNo 
273 mipsidt_serial_lookup(struct cyg_devtab_entry **tab, 
274                   struct cyg_devtab_entry *sub_tab,
275                   const char *name)
276 {
277     serial_channel *chan = (serial_channel *)(*tab)->priv;
278     (chan->callbacks->serial_init)(chan);  // Really only required for interrupt driven devices
279     return ENOERR;
280 }
281
282 // Send a character to the device output buffer.
283 // Return 'true' if character is sent to device
284 static bool
285 mipsidt_serial_putc(serial_channel *chan, unsigned char c)
286 {
287     mipsidt_serial_info *mipsidt_chan = (mipsidt_serial_info *)chan->dev_priv;
288     cyg_addrword_t port = mipsidt_chan->base;
289     cyg_uint8 _lsr;
290
291     HAL_READ_UINT8(port+SER_16550_LSR, _lsr);
292     if (((_lsr & (SIO_LSR_THRE | SIO_LSR_TEMT)) == 0x60)) {
293         // Transmit buffer is empty
294         HAL_WRITE_UINT8(port+SER_16550_THR, c);
295         return true;
296     } else {
297         // No space
298         return false;
299     }
300 }
301
302 // Fetch a character from the device input buffer, waiting if necessary
303 static unsigned char 
304 mipsidt_serial_getc(serial_channel *chan)
305 {
306     unsigned char c;
307     mipsidt_serial_info *mipsidt_chan = (mipsidt_serial_info *)chan->dev_priv;
308     cyg_addrword_t port = mipsidt_chan->base;
309     cyg_uint8 _lsr;
310
311     do {
312         HAL_READ_UINT8(port+SER_16550_LSR, _lsr);
313     } while ((_lsr & SIO_LSR_DR) == 0);
314     HAL_READ_UINT8(port+SER_16550_RBR, c);
315     return c;
316 }
317
318 // Set up the device characteristics; baud rate, etc.
319 static Cyg_ErrNo
320 mipsidt_serial_set_config(serial_channel *chan, cyg_uint32 key,
321                         const void *xbuf, cyg_uint32 *len)
322 {
323     switch (key) {
324     case CYG_IO_SET_CONFIG_SERIAL_INFO:
325       {
326         cyg_serial_info_t *config = (cyg_serial_info_t *)xbuf;
327         if ( *len < sizeof(cyg_serial_info_t) ) {
328             return -EINVAL;
329         }
330         *len = sizeof(cyg_serial_info_t);
331         if ( true != mipsidt_serial_config_port(chan, config, false) )
332             return -EINVAL;
333       }
334       break;
335     default:
336         return -EINVAL;
337     }
338     return ENOERR;
339 }
340
341 // Enable the transmitter on the device
342 static void
343 mipsidt_serial_start_xmit(serial_channel *chan)
344 {
345     mipsidt_serial_info *mipsidt_chan = (mipsidt_serial_info *)chan->dev_priv;
346     cyg_addrword_t port = mipsidt_chan->base;
347     cyg_uint8 _ier;
348
349     HAL_READ_UINT8(port+SER_16550_IER, _ier);
350     _ier |= IER_XMT;                    // Enable xmit interrupt
351     HAL_WRITE_UINT8(port+SER_16550_IER, _ier);
352
353     // We should not need to call this here.  THRE Interrupts are enabled, and the DSR
354     // below calls this function.  However, sometimes we get called with Master Interrupts
355     // disabled, and thus the DSR never runs.  This is unfortunate because it means we
356     // will be doing multiple processing steps for the same thing.
357     (chan->callbacks->xmt_char)(chan);
358 }
359
360 // Disable the transmitter on the device
361 static void 
362 mipsidt_serial_stop_xmit(serial_channel *chan)
363 {
364     mipsidt_serial_info *mipsidt_chan = (mipsidt_serial_info *)chan->dev_priv;
365     cyg_addrword_t port = mipsidt_chan->base;
366     cyg_uint8 _ier;
367
368     HAL_READ_UINT8(port+SER_16550_IER, _ier);
369     _ier &= ~IER_XMT;                   // Disable xmit interrupt
370     HAL_WRITE_UINT8(port+SER_16550_IER, _ier);
371 }
372
373 // Serial I/O - low level interrupt handler (ISR)
374 static cyg_uint32 
375 mipsidt_serial_ISR(cyg_vector_t vector, cyg_addrword_t data)
376 {
377     serial_channel *chan = (serial_channel *)data;
378     mipsidt_serial_info *mipsidt_chan = (mipsidt_serial_info *)chan->dev_priv;
379
380     cyg_drv_interrupt_mask(mipsidt_chan->int_num);
381     cyg_drv_interrupt_acknowledge(mipsidt_chan->int_num);
382     return CYG_ISR_CALL_DSR;  // Cause DSR to be run
383 }
384
385 // Serial I/O - high level interrupt handler (DSR)
386 static void       
387 mipsidt_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
388 {
389     serial_channel *chan = (serial_channel *)data;
390     mipsidt_serial_info *mipsidt_chan = (mipsidt_serial_info *)chan->dev_priv;
391     cyg_addrword_t port = mipsidt_chan->base;
392     cyg_uint8 _iir;
393
394     HAL_READ_UINT8(port+SER_16550_IIR, _iir);
395     _iir &= SIO_IIR_ID_MASK;
396     if ( ISR_Tx_Empty == _iir ) {
397         (chan->callbacks->xmt_char)(chan);
398     } else if (( ISR_Rx_Avail == _iir ) || ( ISR_Rx_Char_Timeout == _iir )) {
399         cyg_uint8 _c;
400         HAL_READ_UINT8(port+SER_16550_RBR, _c);
401         (chan->callbacks->rcv_char)(chan, _c);
402     }
403
404     cyg_drv_interrupt_unmask(mipsidt_chan->int_num);
405 }
406
407 #endif
408
409 //-------------------------------------------------------------------------
410 // EOF mipsidt_serial.c