]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/devs/serial/arm/ebsa285/v2_0/src/ebsa285_serial.c
Initial revision
[karo-tx-redboot.git] / packages / devs / serial / arm / ebsa285 / v2_0 / src / ebsa285_serial.c
1 //==========================================================================
2 //
3 //      devs/serial/arm/ebsa285/current/src/ebsa285_serial.c
4 //
5 //      ARM EBSA285 Serial I/O Interface Module (interrupt driven)
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):   hmt
44 // Contributors:  hmt
45 // Date:        1999-07-26
46 // Purpose:     EBSA285 Serial I/O module (interrupt driven version)
47 // Description: 
48 //
49 //####DESCRIPTIONEND####
50 //
51 //==========================================================================
52
53 #include <pkgconf/system.h>
54 #include <pkgconf/io_serial.h>
55 #include <pkgconf/io.h>
56
57 #ifdef CYGPKG_IO_SERIAL_ARM_EBSA285
58
59 #include <cyg/io/io.h>
60 #include <cyg/hal/hal_intr.h>
61 #include <cyg/io/devtab.h>
62 #include <cyg/io/serial.h>
63 #include <cyg/infra/diag.h>
64
65 #include <cyg/hal/hal_ebsa285.h>         // Hardware definitions
66
67 // ------------------------------------------------------------------------
68 // Baud rates and the like, table-driven setup
69 #define FCLK_MHZ 50
70
71 struct _baud {
72     unsigned char divisor_high, divisor_low;
73 };
74
75 // The indexing of this table must match the enum in serialio.h
76 // The arithmetic is (clock/4)/(baud * 16) - 1
77
78 #define NONE {0,0}
79 const static struct _baud bauds[] = {
80 #if (FCLK_MHZ == 50)
81     NONE,                  // unused
82     NONE,                  // 50
83     NONE,                  // 75
84     NONE,                  // 110
85     NONE,                  // 134.5
86     NONE,                  // 150
87     NONE,                  // 200
88     { 0xA, 0x2B },         // 300   2603  = 0x0A2B
89     { 0x5, 0x15 },         // 600   1301  = 0x0515
90     { 0x2, 0x8A },         // 1200  650   = 0x028A
91     { 0x1, 0xB1 },         // 1800  433   = 0x01B1
92     { 0x1, 0x45 },         // 2400  325   = 0x0145
93     { 0x0, 0xD8 },         // 3600  216   = 0x00D8
94     { 0x0, 0xA2 },         // 4800  162   = 0x00A2
95     { 0x0, 0x6B },         // 7200  107   = 0x006B
96     { 0x0, 0x50 },         // 9600  80    = 0x0050
97     { 0x0, 0x35 },         // 14400 53    = 0x0035
98     { 0x0, 0x28 },         // 19200 40    = 0x0028
99     { 0x0, 0x13 },         // 38400 19    = 0x0013
100     NONE,                  // 57600 
101     NONE,                  // 115200
102     NONE                   // 230400
103 #elif (FCLK_MHZ == 60)
104 #error NOT SUPPORTED - these figures are more for documentation
105     { /*   300, */ 0xC, 0x34},                  /* 2603  = 0x0A2B */
106     { /*   600, */ 0x6, 0x19},                  /* 1301  = 0x0515 */
107     { /*  1200, */ 0x3, 0x0C},                  /* 650   = 0x028A */
108     { /*  2400, */ 0x1, 0x86},                  /* 325   = 0x0145 */
109     { /*  4800, */ 0x0, 0xC2},                  /* 162   = 0x00A2 */
110     { /*  9600, */ 0x0, 0x61},                  /* 80    = 0x0050 */
111     { /* 19200, */ 0x0, 0x30},                  /* 40    = 0x0028 */
112     { /* 38400, */ 0x0, 0x17},                  /* 19    = 0x0013 */
113 #endif
114 };
115
116 static int select_word_length[] = {
117     SA110_UART_DATA_LENGTH_5_BITS,      // 5 bits
118     SA110_UART_DATA_LENGTH_6_BITS,      // 6 bits
119     SA110_UART_DATA_LENGTH_7_BITS,      // 7 bits
120     SA110_UART_DATA_LENGTH_8_BITS       // 8 bits
121 };
122
123 static int select_stop_bits[] = {
124     -1,                          // unused
125     SA110_UART_STOP_BITS_ONE,    // 1 stop bit
126     -1,                          // 1.5 stop bit
127     SA110_UART_STOP_BITS_TWO     // 2 stop bits
128 };
129
130 static int select_parity[] = {
131     SA110_UART_PARITY_DISABLED,                           // No parity
132     SA110_UART_PARITY_ENABLED | SA110_UART_PARITY_EVEN,   // Even parity
133     SA110_UART_PARITY_ENABLED | SA110_UART_PARITY_ODD,    // Odd parity
134     -1,                                                   // Mark parity
135     -1                                                    // Space parity
136 };
137
138 // ------------------------------------------------------------------------
139 // some forward references
140
141 struct ebsa285_serial_interrupt {
142     CYG_WORD       int_num;
143     cyg_interrupt  serial_interrupt;
144     cyg_handle_t   serial_interrupt_handle;
145 };
146
147 typedef struct ebsa285_serial_info {
148     struct ebsa285_serial_interrupt rx;
149     struct ebsa285_serial_interrupt tx;
150     int tx_active;
151 } ebsa285_serial_info;
152
153 static bool ebsa285_serial_init(struct cyg_devtab_entry *tab);
154 static bool ebsa285_serial_putc(serial_channel *chan, unsigned char c);
155 static Cyg_ErrNo ebsa285_serial_lookup(struct cyg_devtab_entry **tab, 
156                                    struct cyg_devtab_entry *sub_tab,
157                                    const char *name);
158 static unsigned char ebsa285_serial_getc(serial_channel *chan);
159 static Cyg_ErrNo ebsa285_serial_set_config(serial_channel *chan, cyg_uint32 key,
160                                            const void *xbuf, cyg_uint32 *len);
161                                            
162 static void ebsa285_serial_start_xmit(serial_channel *chan);
163 static void ebsa285_serial_stop_xmit(serial_channel *chan);
164
165 static cyg_uint32 ebsa285_serial_rx_ISR(cyg_vector_t vector, cyg_addrword_t data);
166 static void       ebsa285_serial_rx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
167 static cyg_uint32 ebsa285_serial_tx_ISR(cyg_vector_t vector, cyg_addrword_t data);
168 static void       ebsa285_serial_tx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
169
170 static SERIAL_FUNS(ebsa285_serial_funs, 
171                    ebsa285_serial_putc, 
172                    ebsa285_serial_getc,
173                    ebsa285_serial_set_config,
174                    ebsa285_serial_start_xmit,
175                    ebsa285_serial_stop_xmit
176     );
177
178
179 // ------------------------------------------------------------------------
180 // this is dummy in config: there is only one device on the EBSA285
181 #ifdef CYGPKG_IO_SERIAL_ARM_EBSA285_SERIAL
182
183 static ebsa285_serial_info ebsa285_serial_info1 = {
184     { CYGNUM_HAL_INTERRUPT_SERIAL_RX },
185     { CYGNUM_HAL_INTERRUPT_SERIAL_TX },
186     0
187 };
188
189 #if CYGNUM_IO_SERIAL_ARM_EBSA285_SERIAL_BUFSIZE > 0
190 static unsigned char ebsa285_serial_out_buf[CYGNUM_IO_SERIAL_ARM_EBSA285_SERIAL_BUFSIZE];
191 static unsigned char ebsa285_serial_in_buf[CYGNUM_IO_SERIAL_ARM_EBSA285_SERIAL_BUFSIZE];
192
193 static SERIAL_CHANNEL_USING_INTERRUPTS(ebsa285_serial_channel,
194                                        ebsa285_serial_funs, 
195                                        ebsa285_serial_info1,
196                                        CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_EBSA285_SERIAL_BAUD),
197                                        CYG_SERIAL_STOP_DEFAULT,
198                                        CYG_SERIAL_PARITY_DEFAULT,
199                                        CYG_SERIAL_WORD_LENGTH_DEFAULT,
200                                        CYG_SERIAL_FLAGS_DEFAULT,
201                                        &ebsa285_serial_out_buf[0], sizeof(ebsa285_serial_out_buf),
202                                        &ebsa285_serial_in_buf[0], sizeof(ebsa285_serial_in_buf)
203     );
204 #else
205 static SERIAL_CHANNEL(ebsa285_serial_channel,
206                       ebsa285_serial_funs, 
207                       ebsa285_serial_info1,
208                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_EBSA285_SERIAL_BAUD),
209                       CYG_SERIAL_STOP_DEFAULT,
210                       CYG_SERIAL_PARITY_DEFAULT,
211                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
212                       CYG_SERIAL_FLAGS_DEFAULT
213     );
214 #endif
215
216 DEVTAB_ENTRY(ebsa285_serial_io, 
217              CYGDAT_IO_SERIAL_ARM_EBSA285_SERIAL_NAME,
218              0,                     // Does not depend on a lower level interface
219              &cyg_io_serial_devio, 
220              ebsa285_serial_init, 
221              ebsa285_serial_lookup,     // Serial driver may need initializing
222              &ebsa285_serial_channel
223     );
224 #endif //  CYGPKG_IO_SERIAL_ARM_EBSA285_SERIAL
225
226 // ------------------------------------------------------------------------
227
228
229 // ------------------------------------------------------------------------
230 // Internal function to actually configure the hardware to desired baud rate, etc.
231 static bool
232 ebsa285_serial_config_port(serial_channel *chan, cyg_serial_info_t *new_config, bool init)
233 {
234     int dummy, h, m, l;
235     
236     // Make sure everything is off
237     *SA110_UART_CONTROL_REGISTER = SA110_UART_DISABLED | SA110_SIR_DISABLED;
238     
239     // Read the RXStat to drain the fifo
240     dummy = *SA110_UART_RXSTAT;
241
242     // Set the baud rate - this also turns the uart on.
243     // 
244     // Note that the ordering of these writes is critical,
245     // and the writes to the H_BAUD_CONTROL and CONTROL_REGISTER
246     // are necessary to force the UART to update its register
247     // contents.
248
249     l = bauds[new_config->baud].divisor_low;   // zeros in unused slots here
250     m = bauds[new_config->baud].divisor_high;  // and here
251     h = SA110_UART_BREAK_DISABLED    |      
252         select_stop_bits[new_config->stop] |   // -1s in unused slots for these
253         select_parity[new_config->parity]  |   // and these
254         SA110_UART_FIFO_ENABLED |              // and these below
255         select_word_length[new_config->word_length - CYGNUM_SERIAL_WORD_LENGTH_5];
256
257     if ( 0 != (l + m) && h >= 0 && h < 256 ) {
258         *SA110_UART_L_BAUD_CONTROL = l;
259         *SA110_UART_M_BAUD_CONTROL = m;
260         *SA110_UART_H_BAUD_CONTROL = h;
261         init = true; // AOK
262     }
263     else if ( init ) {
264         // put in some sensible defaults
265         *SA110_UART_L_BAUD_CONTROL   = 0x13; // bp->divisor_low;
266         *SA110_UART_M_BAUD_CONTROL   = 0x00; // bp->divisor_high;
267         *SA110_UART_H_BAUD_CONTROL = SA110_UART_BREAK_DISABLED    |
268             SA110_UART_PARITY_DISABLED   |
269             SA110_UART_STOP_BITS_ONE     |
270             SA110_UART_FIFO_ENABLED      |
271             SA110_UART_DATA_LENGTH_8_BITS;
272     }
273
274     // All set, re-enable the device:
275     *SA110_UART_CONTROL_REGISTER = SA110_UART_ENABLED | SA110_SIR_DISABLED;
276
277     if (init && new_config != &chan->config) {
278         // record the new setup
279         chan->config = *new_config;
280     }
281     // All done
282     return init;
283 }
284
285 // Function to initialize the device.  Called at bootstrap time.
286 static bool 
287 ebsa285_serial_init(struct cyg_devtab_entry *tab)
288 {
289     serial_channel *chan = (serial_channel *)tab->priv;
290     ebsa285_serial_info *ebsa285_chan = (ebsa285_serial_info *)chan->dev_priv;
291 #ifdef CYGDBG_IO_INIT
292     diag_printf("EBSA285 SERIAL init - dev: %x\n", ebsa285_chan);
293 #endif
294     (chan->callbacks->serial_init)(chan);  // Really only required for interrupt driven devices
295     if (chan->out_cbuf.len != 0) {
296
297         // first for rx
298         cyg_drv_interrupt_create(ebsa285_chan->rx.int_num,
299                                  99,                   // Priority - unused
300                                  (cyg_addrword_t)chan, //  Data item passed to interrupt handler
301                                  ebsa285_serial_rx_ISR,
302                                  ebsa285_serial_rx_DSR,
303                                  &ebsa285_chan->rx.serial_interrupt_handle,
304                                  &ebsa285_chan->rx.serial_interrupt);
305         cyg_drv_interrupt_attach(ebsa285_chan->rx.serial_interrupt_handle);
306         cyg_drv_interrupt_unmask(ebsa285_chan->rx.int_num);
307
308         // then for tx
309         cyg_drv_interrupt_create(ebsa285_chan->tx.int_num,
310                                  99,                   // Priority - unused
311                                  (cyg_addrword_t)chan, //  Data item passed to interrupt handler
312                                  ebsa285_serial_tx_ISR,
313                                  ebsa285_serial_tx_DSR,
314                                  &ebsa285_chan->tx.serial_interrupt_handle,
315                                  &ebsa285_chan->tx.serial_interrupt);
316         cyg_drv_interrupt_attach(ebsa285_chan->tx.serial_interrupt_handle);
317         // DO NOT cyg_drv_interrupt_unmask(ebsa285_chan->tx.int_num);
318         ebsa285_chan->tx_active = 0;
319     }
320     (void)ebsa285_serial_config_port(chan, &chan->config, true);
321     return true;
322 }
323
324 // This routine is called when the device is "looked" up (i.e. attached)
325 static Cyg_ErrNo 
326 ebsa285_serial_lookup(struct cyg_devtab_entry **tab, 
327                   struct cyg_devtab_entry *sub_tab,
328                   const char *name)
329 {
330     serial_channel *chan = (serial_channel *)(*tab)->priv;
331     (chan->callbacks->serial_init)(chan);  // Really only required for interrupt driven devices
332     return ENOERR;
333 }
334
335 // Send a character to the device output buffer.
336 // Return 'true' if character is sent to device
337 static bool
338 ebsa285_serial_putc(serial_channel *chan, unsigned char c)
339 {
340     if ((*SA110_UART_FLAG_REGISTER & SA110_TX_FIFO_STATUS_MASK) == SA110_TX_FIFO_BUSY)
341         return false; // No space
342     
343     *SA110_UART_DATA_REGISTER = c; // Transmit buffer is empty
344     return true;
345 }
346
347 // Fetch a character from the device input buffer, waiting if necessary
348 static unsigned char 
349 ebsa285_serial_getc(serial_channel *chan)
350 {
351     unsigned char c;
352     while ((*SA110_UART_FLAG_REGISTER & SA110_RX_FIFO_STATUS_MASK) == SA110_RX_FIFO_EMPTY)
353         ; // wait for char
354     c = (char)(*SA110_UART_DATA_REGISTER & 0xFF);
355     // no error checking... no way to return the info
356     return c;
357 }
358
359 // Set up the device characteristics; baud rate, etc.
360 static Cyg_ErrNo
361 ebsa285_serial_set_config(serial_channel *chan, cyg_uint32 key,
362                           const void *xbuf, cyg_uint32 *len)
363 {
364     switch (key) {
365     case CYG_IO_SET_CONFIG_SERIAL_INFO:
366       {
367         cyg_serial_info_t *config = (cyg_serial_info_t *)xbuf;
368         if ( *len < sizeof(cyg_serial_info_t) ) {
369             return -EINVAL;
370         }
371         *len = sizeof(cyg_serial_info_t);
372         if ( true != ebsa285_serial_config_port(chan, config, false) )
373             return -EINVAL;
374       }
375       break;
376     default:
377         return -EINVAL;
378     }
379     return ENOERR;
380 }
381
382 // Enable the transmitter on the device (nope, already in use by hal_diag)
383 static void
384 ebsa285_serial_start_xmit(serial_channel *chan)
385 {
386     ebsa285_serial_info *ebsa285_chan = (ebsa285_serial_info *)chan->dev_priv;
387     ebsa285_chan->tx_active = 1;
388     cyg_drv_interrupt_unmask(ebsa285_chan->tx.int_num);
389 }
390
391 // Disable the transmitter on the device (nope, remains in use by hal_diag)
392 static void 
393 ebsa285_serial_stop_xmit(serial_channel *chan)
394 {
395     ebsa285_serial_info *ebsa285_chan = (ebsa285_serial_info *)chan->dev_priv;
396     cyg_drv_interrupt_mask(ebsa285_chan->tx.int_num);
397     ebsa285_chan->tx_active = 0;
398 }
399
400 // Serial I/O - low level interrupt handlers (ISR)
401 static cyg_uint32 
402 ebsa285_serial_rx_ISR(cyg_vector_t vector, cyg_addrword_t data)
403 {
404     serial_channel *chan = (serial_channel *)data;
405     ebsa285_serial_info *ebsa285_chan = (ebsa285_serial_info *)chan->dev_priv;
406     cyg_drv_interrupt_mask(ebsa285_chan->rx.int_num);
407     cyg_drv_interrupt_acknowledge(ebsa285_chan->rx.int_num);
408     return CYG_ISR_CALL_DSR;  // Cause DSR to be run
409 }
410
411 static cyg_uint32 
412 ebsa285_serial_tx_ISR(cyg_vector_t vector, cyg_addrword_t data)
413 {
414     serial_channel *chan = (serial_channel *)data;
415     ebsa285_serial_info *ebsa285_chan = (ebsa285_serial_info *)chan->dev_priv;
416     cyg_drv_interrupt_mask(ebsa285_chan->tx.int_num);
417     cyg_drv_interrupt_acknowledge(ebsa285_chan->tx.int_num);
418     return CYG_ISR_CALL_DSR;  // Cause DSR to be run
419 }
420
421 // Serial I/O - high level interrupt handlers (DSR)
422 static void       
423 ebsa285_serial_rx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
424 {
425     serial_channel *chan = (serial_channel *)data;
426     ebsa285_serial_info *ebsa285_chan = (ebsa285_serial_info *)chan->dev_priv;
427     if ((*SA110_UART_FLAG_REGISTER & SA110_RX_FIFO_STATUS_MASK) != SA110_RX_FIFO_EMPTY) {
428         char c;
429         int status;
430         c = (char)(*SA110_UART_DATA_REGISTER & 0xFF);
431         status = *SA110_UART_RXSTAT;
432         if ( 0 == (status & (SA110_UART_FRAMING_ERROR_MASK |
433                              SA110_UART_PARITY_ERROR_MASK  |
434                              SA110_UART_OVERRUN_ERROR_MASK)) )
435             (chan->callbacks->rcv_char)(chan, c);
436     }
437     cyg_drv_interrupt_unmask(ebsa285_chan->rx.int_num);
438 }
439
440 static void       
441 ebsa285_serial_tx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
442 {
443     serial_channel *chan = (serial_channel *)data;
444     ebsa285_serial_info *ebsa285_chan = (ebsa285_serial_info *)chan->dev_priv;
445     if ((*SA110_UART_FLAG_REGISTER & SA110_TX_FIFO_STATUS_MASK) != SA110_TX_FIFO_BUSY) {
446         (chan->callbacks->xmt_char)(chan);
447     }
448     if ( ebsa285_chan->tx_active ) // it might be halted in callback above
449         cyg_drv_interrupt_unmask(ebsa285_chan->tx.int_num);
450 }
451 #endif // CYGPKG_IO_SERIAL_ARM_EBSA285
452
453 // ------------------------------------------------------------------------
454 // EOF ebsa285_serial.c