Mercurial > ecos
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 |
