]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/devs/serial/arm/smdk2410/v2_0/src/smdk2410_serial.c
Initial revision
[karo-tx-redboot.git] / packages / devs / serial / arm / smdk2410 / v2_0 / src / smdk2410_serial.c
1 //==========================================================================
2 //
3 //      io/serial/arm/smdk2410_serial.c
4 //
5 //      Samsung SMDK2410 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):    michael anburaj <michaelanburaj@hotmail.com>
44 // Contributors: michael anburaj <michaelanburaj@hotmail.com>
45 // Date:         2003-08-01
46 // Purpose:      SMDK2410 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 #include <cyg/io/io.h>
57 #include <cyg/hal/hal_intr.h>           // interrupt
58 #include <cyg/hal/hal_io.h>             // register base
59 #include <cyg/io/devtab.h>
60 #include <cyg/io/serial.h>
61 #include <cyg/infra/diag.h>
62
63 #ifdef CYGPKG_IO_SERIAL_ARM_SMDK2410
64
65 #include "smdk2410_serial.h"
66
67 typedef struct smdk2410_serial_info {
68     CYG_ADDRWORD   base;
69     CYG_WORD       int_num;
70     cyg_uint32     bit_sub_rxd;
71     cyg_interrupt  serial_interrupt;
72     cyg_handle_t   serial_interrupt_handle;
73 } smdk2410_serial_info;
74
75 static bool smdk2410_serial_init(struct cyg_devtab_entry *tab);
76 static bool smdk2410_serial_putc(serial_channel *chan, unsigned char c);
77 static Cyg_ErrNo smdk2410_serial_lookup(struct cyg_devtab_entry **tab, 
78                                         struct cyg_devtab_entry *sub_tab,
79                                         const char *name);
80 static unsigned char smdk2410_serial_getc(serial_channel *chan);
81 static Cyg_ErrNo smdk2410_serial_set_config(serial_channel *chan, 
82                                             cyg_uint32 key,
83                                             const void *xbuf, cyg_uint32 *len);
84 static void smdk2410_serial_start_xmit(serial_channel *chan);
85 static void smdk2410_serial_stop_xmit(serial_channel *chan);
86
87 static cyg_uint32 smdk2410_serial_ISR(cyg_vector_t vector, cyg_addrword_t data);
88 static void       smdk2410_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
89
90 static SERIAL_FUNS(smdk2410_serial_funs, 
91                    smdk2410_serial_putc, 
92                    smdk2410_serial_getc,
93                    smdk2410_serial_set_config,
94                    smdk2410_serial_start_xmit,
95                    smdk2410_serial_stop_xmit
96     );
97
98 #ifdef CYGPKG_IO_SERIAL_ARM_SMDK2410_SERIAL0
99 static smdk2410_serial_info smdk2410_serial_info0 = {(cyg_uint32)ULCON0,
100                                                      CYGNUM_HAL_INTERRUPT_UART0,
101                                                      BIT_SUB_RXD0};
102 #if CYGNUM_IO_SERIAL_ARM_SMDK2410_SERIAL0_BUFSIZE > 0
103 static unsigned char smdk2410_serial_out_buf0[CYGNUM_IO_SERIAL_ARM_SMDK2410_SERIAL0_BUFSIZE];
104 static unsigned char smdk2410_serial_in_buf0[CYGNUM_IO_SERIAL_ARM_SMDK2410_SERIAL0_BUFSIZE];
105
106 static SERIAL_CHANNEL_USING_INTERRUPTS(smdk2410_serial_channel0,
107                                        smdk2410_serial_funs, 
108                                        smdk2410_serial_info0,
109                                        CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_SMDK2410_SERIAL0_BAUD),
110                                        CYG_SERIAL_STOP_DEFAULT,
111                                        CYG_SERIAL_PARITY_DEFAULT,
112                                        CYG_SERIAL_WORD_LENGTH_DEFAULT,
113                                        CYG_SERIAL_FLAGS_DEFAULT,
114                                        &smdk2410_serial_out_buf0[0], sizeof(smdk2410_serial_out_buf0),
115                                        &smdk2410_serial_in_buf0[0], sizeof(smdk2410_serial_in_buf0)
116     );
117 #else
118 static SERIAL_CHANNEL(smdk2410_serial_channel0,
119                       smdk2410_serial_funs, 
120                       smdk2410_serial_info0,
121                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_SMDK2410_SERIAL0_BAUD),
122                       CYG_SERIAL_STOP_DEFAULT,
123                       CYG_SERIAL_PARITY_DEFAULT,
124                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
125                       CYG_SERIAL_FLAGS_DEFAULT
126     );
127 #endif
128
129 DEVTAB_ENTRY(smdk2410_serial_io0, 
130              CYGDAT_IO_SERIAL_ARM_SMDK2410_SERIAL0_NAME,
131              0,                          // Does not depend on a lower level interface
132              &cyg_io_serial_devio, 
133              smdk2410_serial_init, 
134              smdk2410_serial_lookup,     // Serial driver may need initializing
135              &smdk2410_serial_channel0
136     );
137 #endif //  CYGPKG_IO_SERIAL_ARM_SMDK2410_SERIAL0
138
139 #ifdef CYGPKG_IO_SERIAL_ARM_SMDK2410_SERIAL1
140 static smdk2410_serial_info smdk2410_serial_info1 = {(cyg_uint32)ULCON1,
141                                                      CYGNUM_HAL_INTERRUPT_UART1,
142                                                      BIT_SUB_RXD1};
143 #if CYGNUM_IO_SERIAL_ARM_SMDK2410_SERIAL1_BUFSIZE > 0
144 static unsigned char smdk2410_serial_out_buf1[CYGNUM_IO_SERIAL_ARM_SMDK2410_SERIAL1_BUFSIZE];
145 static unsigned char smdk2410_serial_in_buf1[CYGNUM_IO_SERIAL_ARM_SMDK2410_SERIAL1_BUFSIZE];
146
147 static SERIAL_CHANNEL_USING_INTERRUPTS(smdk2410_serial_channel1,
148                                        smdk2410_serial_funs, 
149                                        smdk2410_serial_info1,
150                                        CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_SMDK2410_SERIAL1_BAUD),
151                                        CYG_SERIAL_STOP_DEFAULT,
152                                        CYG_SERIAL_PARITY_DEFAULT,
153                                        CYG_SERIAL_WORD_LENGTH_DEFAULT,
154                                        CYG_SERIAL_FLAGS_DEFAULT,
155                                        &smdk2410_serial_out_buf1[0], sizeof(smdk2410_serial_out_buf1),
156                                        &smdk2410_serial_in_buf1[0], sizeof(smdk2410_serial_in_buf1)
157     );
158 #else
159 static SERIAL_CHANNEL(smdk2410_serial_channel1,
160                       smdk2410_serial_funs, 
161                       smdk2410_serial_info1,
162                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_SMDK2410_SERIAL1_BAUD),
163                       CYG_SERIAL_STOP_DEFAULT,
164                       CYG_SERIAL_PARITY_DEFAULT,
165                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
166                       CYG_SERIAL_FLAGS_DEFAULT
167     );
168 #endif
169
170 DEVTAB_ENTRY(smdk2410_serial_io1, 
171              CYGDAT_IO_SERIAL_ARM_SMDK2410_SERIAL1_NAME,
172              0,                          // Does not depend on a lower level interface
173              &cyg_io_serial_devio, 
174              smdk2410_serial_init, 
175              smdk2410_serial_lookup,     // Serial driver may need initializing
176              &smdk2410_serial_channel1
177     );
178 #endif //  CYGPKG_IO_SERIAL_ARM_SMDK2410_SERIAL1
179
180 // Internal function to actually configure the hardware to desired baud rate, etc.
181 static bool
182 smdk2410_serial_config_port(serial_channel *chan, cyg_serial_info_t *new_config, bool init)
183 {
184     smdk2410_serial_info *smdk2410_chan = (smdk2410_serial_info *)chan->dev_priv;
185     CYG_ADDRWORD base = smdk2410_chan->base;
186     unsigned short baud_divisor = select_baud[new_config->baud];
187     cyg_uint32 _lcr;
188
189     if (baud_divisor == 0) return false;
190
191     if (init) {
192         //UART FIFO control register
193         HAL_WRITE_UINT32(base+OFS_UFCON, (3<<6) | (3<<4) | (1<<2) | (1<<1) | (1<<0));
194
195         //UART modem control register
196         HAL_WRITE_UINT32(base+OFS_UMCON, 0);
197     }
198
199     _lcr = select_word_length[new_config->word_length - CYGNUM_SERIAL_WORD_LENGTH_5] | 
200         select_stop_bits[new_config->stop] |
201         select_parity[new_config->parity];
202     HAL_WRITE_UINT32(base+OFS_ULCON, _lcr);
203
204     //UART control register, Enable Rx Timeout Int
205     HAL_WRITE_UINT32(base+OFS_UCON, 0x085);
206
207     if (new_config != &chan->config) {
208         chan->config = *new_config;
209     }
210     return true;
211 }
212
213 // Function to initialize the device.  Called at bootstrap time.
214 static bool 
215 smdk2410_serial_init(struct cyg_devtab_entry *tab)
216 {
217     serial_channel *chan = (serial_channel *)tab->priv;
218     smdk2410_serial_info *smdk2410_chan = (smdk2410_serial_info *)chan->dev_priv;
219     cyg_uint32 _intsubm;
220 #ifdef CYGDBG_IO_INIT
221     diag_printf("SMDK2410 SERIAL init - dev: 0x%08x.%d\n", 
222                 smdk2410_chan->base, smdk2410_chan->int_num);
223 #endif
224     (chan->callbacks->serial_init)(chan);  // Really only required for interrupt driven devices
225     if (chan->out_cbuf.len != 0) {
226         cyg_drv_interrupt_create(smdk2410_chan->int_num,
227                                  1,                    // Priority - unused
228                                  (cyg_addrword_t)chan, //  Data item passed to interrupt handler
229                                  smdk2410_serial_ISR,
230                                  smdk2410_serial_DSR,
231                                  &smdk2410_chan->serial_interrupt_handle,
232                                  &smdk2410_chan->serial_interrupt);
233         cyg_drv_interrupt_attach(smdk2410_chan->serial_interrupt_handle);
234         cyg_drv_interrupt_unmask(smdk2410_chan->int_num);
235
236         HAL_READ_UINT32(INTSUBMSK, _intsubm);
237         _intsubm &= ~(smdk2410_chan->bit_sub_rxd<<0);         // BIT_SUB_RXD
238         HAL_WRITE_UINT32(INTSUBMSK, _intsubm);
239     }
240     smdk2410_serial_config_port(chan, &chan->config, true);
241     return true;
242 }
243
244 // This routine is called when the device is "looked" up (i.e. attached)
245 static Cyg_ErrNo 
246 smdk2410_serial_lookup(struct cyg_devtab_entry **tab, 
247                   struct cyg_devtab_entry *sub_tab,
248                   const char *name)
249 {
250     serial_channel *chan = (serial_channel *)(*tab)->priv;
251     (chan->callbacks->serial_init)(chan);  // Really only required for interrupt driven devices
252     return ENOERR;
253 }
254
255 // Send a character to the device output buffer.
256 // Return 'true' if character is sent to device
257 static bool
258 smdk2410_serial_putc(serial_channel *chan, unsigned char c)
259 {
260     smdk2410_serial_info *smdk2410_chan = (smdk2410_serial_info *)chan->dev_priv;
261     CYG_ADDRWORD base = smdk2410_chan->base;
262     cyg_uint32 _status;
263
264     HAL_READ_UINT32(base+OFS_UFSTAT, _status);
265     if (_status & 0x200) {
266         // No space
267         return false;
268     } else {
269         // Transmit buffer is not full
270         HAL_WRITE_UINT8(base+OFS_UTXH, (cyg_uint32)c);
271         return true;
272     }
273 }
274
275 // Fetch a character from the device input buffer, waiting if necessary
276 static unsigned char 
277 smdk2410_serial_getc(serial_channel *chan)
278 {
279     smdk2410_serial_info *smdk2410_chan = (smdk2410_serial_info *)chan->dev_priv;
280     CYG_ADDRWORD base = smdk2410_chan->base;
281     cyg_uint32 _status;
282     cyg_uint8 _c;
283
284     do {
285         HAL_READ_UINT32(base+OFS_UFSTAT, _status);
286     } while ((_status & 0xf) == 0);
287
288     HAL_READ_UINT8(base+OFS_URXH, _c);
289     return (unsigned char)_c;
290 }
291
292 // Set up the device characteristics; baud rate, etc.
293 static Cyg_ErrNo
294 smdk2410_serial_set_config(serial_channel *chan, cyg_uint32 key,
295                       const void *xbuf, cyg_uint32 *len)
296 {
297     switch (key) {
298     case CYG_IO_SET_CONFIG_SERIAL_INFO:
299       {
300         cyg_serial_info_t *config = (cyg_serial_info_t *)xbuf;
301         if ( *len < sizeof(cyg_serial_info_t) ) {
302             return -EINVAL;
303         }
304         *len = sizeof(cyg_serial_info_t);
305         if ( true != smdk2410_serial_config_port(chan, config, false) )
306             return -EINVAL;
307       }
308       break;
309     default:
310         return -EINVAL;
311     }
312     return ENOERR;
313 }
314
315 // Enable the transmitter on the device
316 static void
317 smdk2410_serial_start_xmit(serial_channel *chan)
318 {
319     smdk2410_serial_info *smdk2410_chan = (smdk2410_serial_info *)chan->dev_priv;
320     cyg_uint32 _intsubm;
321
322     HAL_READ_UINT32(INTSUBMSK, _intsubm);
323     _intsubm &= ~(smdk2410_chan->bit_sub_rxd<<1);         // BIT_SUB_TXD
324     HAL_WRITE_UINT32(INTSUBMSK, _intsubm);
325 }
326
327 // Disable the transmitter on the device
328 static void 
329 smdk2410_serial_stop_xmit(serial_channel *chan)
330 {
331     smdk2410_serial_info *smdk2410_chan = (smdk2410_serial_info *)chan->dev_priv;
332     cyg_uint32 _intsubm;
333
334     HAL_READ_UINT32(INTSUBMSK, _intsubm);
335     _intsubm |= (smdk2410_chan->bit_sub_rxd<<1);          // BIT_SUB_TXD
336     HAL_WRITE_UINT32(INTSUBMSK, _intsubm);
337 }
338
339 // Serial I/O - low level interrupt handler (ISR)
340 static cyg_uint32 
341 smdk2410_serial_ISR(cyg_vector_t vector, cyg_addrword_t data)
342 {
343     serial_channel *chan = (serial_channel *)data;
344     smdk2410_serial_info *smdk2410_chan = (smdk2410_serial_info *)chan->dev_priv;
345     cyg_drv_interrupt_mask(smdk2410_chan->int_num);
346     cyg_drv_interrupt_acknowledge(smdk2410_chan->int_num);
347     return CYG_ISR_CALL_DSR;  // Cause DSR to be run
348 }
349
350 // Serial I/O - high level interrupt handler (DSR)
351 static void       
352 smdk2410_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
353 {
354     serial_channel *chan = (serial_channel *)data;
355     smdk2410_serial_info *smdk2410_chan = (smdk2410_serial_info *)chan->dev_priv;
356     CYG_ADDRWORD base = smdk2410_chan->base;
357     cyg_uint32 _intsubpnd, _status, _c;
358     cyg_uint32 _rxd_bit = (smdk2410_chan->bit_sub_rxd<<0), _txd_bit=(smdk2410_chan->bit_sub_rxd<<1);
359
360     HAL_READ_UINT32(SUBSRCPND, _intsubpnd);
361
362     // Empty Rx FIFO
363     if (_intsubpnd & _rxd_bit) {
364         HAL_READ_UINT32(base+OFS_UFSTAT, _status);
365         while((_status & 0x0f) != 0) {
366             HAL_READ_UINT8(base+OFS_URXH, _c);
367             (chan->callbacks->rcv_char)(chan, (unsigned char)_c);
368             HAL_READ_UINT32(base+OFS_UFSTAT, _status);
369         }
370         HAL_WRITE_UINT32(SUBSRCPND, _rxd_bit);
371     }
372
373     // Fill into Tx FIFO. xmt_char will mask the interrupt when it
374     // runs out of chars, so doing this in a loop is OK.
375     if (_intsubpnd & _txd_bit) {
376         (chan->callbacks->xmt_char)(chan);
377         HAL_WRITE_UINT32(SUBSRCPND, _txd_bit);
378     }
379
380     cyg_drv_interrupt_unmask(smdk2410_chan->int_num);
381 }
382
383 #endif // CYGPKG_IO_SERIAL_ARM_SMDK2410
384
385 // EOF smdk2410_serial.c