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