[0737710] | 1 | /* |
---|
[aa0da6b] | 2 | * This file contains the TTY driver for the National Semiconductor NS16550. |
---|
| 3 | * |
---|
[a3d3d9a] | 4 | * This part is widely cloned and second sourced. It is found in a number |
---|
[aa0da6b] | 5 | * of "Super IO" controllers. |
---|
[0737710] | 6 | * |
---|
| 7 | * COPYRIGHT (c) 1998 by Radstone Technology |
---|
| 8 | * |
---|
| 9 | * |
---|
| 10 | * THIS FILE IS PROVIDED TO YOU, THE USER, "AS IS", WITHOUT WARRANTY OF ANY |
---|
| 11 | * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE |
---|
| 12 | * IMPLIED WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK |
---|
| 13 | * AS TO THE QUALITY AND PERFORMANCE OF ALL CODE IN THIS FILE IS WITH YOU. |
---|
| 14 | * |
---|
| 15 | * You are hereby granted permission to use, copy, modify, and distribute |
---|
| 16 | * this file, provided that this notice, plus the above copyright notice |
---|
| 17 | * and disclaimer, appears in all copies. Radstone Technology will provide |
---|
| 18 | * no support for this code. |
---|
| 19 | * |
---|
| 20 | * This driver uses the termios pseudo driver. |
---|
| 21 | */ |
---|
[3495c57] | 22 | |
---|
[69be08ee] | 23 | /* |
---|
| 24 | * $Id$ |
---|
| 25 | */ |
---|
[0737710] | 26 | |
---|
[e97e0e0] | 27 | #include <stdlib.h> |
---|
| 28 | |
---|
[0737710] | 29 | #include <rtems.h> |
---|
| 30 | #include <rtems/libio.h> |
---|
[4caeb10] | 31 | #include <rtems/ringbuf.h> |
---|
[e97e0e0] | 32 | #include <rtems/bspIo.h> |
---|
[ec5d4505] | 33 | #include <rtems/termiostypes.h> |
---|
[0737710] | 34 | |
---|
[ee3b242b] | 35 | #include <libchip/serial.h> |
---|
[7e05b53] | 36 | #include <libchip/sersupp.h> |
---|
[e97e0e0] | 37 | |
---|
[8c0cab0] | 38 | #include <bsp.h> |
---|
[0737710] | 39 | |
---|
[8c0cab0] | 40 | #include "ns16550_p.h" |
---|
[fc9cbcf3] | 41 | |
---|
[2e3f4398] | 42 | #if defined(BSP_FEATURE_IRQ_EXTENSION) |
---|
[8c0cab0] | 43 | #include <bsp/irq.h> |
---|
[2e3f4398] | 44 | #elif defined(BSP_FEATURE_IRQ_LEGACY) |
---|
[8c0cab0] | 45 | #include <bsp/irq.h> |
---|
[2e3f4398] | 46 | #elif defined(__PPC__) || defined(__i386__) |
---|
[8c0cab0] | 47 | #include <bsp/irq.h> |
---|
[e97e0e0] | 48 | #define BSP_FEATURE_IRQ_LEGACY |
---|
| 49 | #ifdef BSP_SHARED_HANDLER_SUPPORT |
---|
| 50 | #define BSP_FEATURE_IRQ_LEGACY_SHARED_HANDLER_SUPPORT |
---|
| 51 | #endif |
---|
| 52 | #endif |
---|
| 53 | |
---|
[0737710] | 54 | /* |
---|
| 55 | * Flow control is only supported when using interrupts |
---|
| 56 | */ |
---|
[cae0504] | 57 | |
---|
| 58 | console_flow ns16550_flow_RTSCTS = { |
---|
[0737710] | 59 | ns16550_negate_RTS, /* deviceStopRemoteTx */ |
---|
| 60 | ns16550_assert_RTS /* deviceStartRemoteTx */ |
---|
| 61 | }; |
---|
| 62 | |
---|
[cae0504] | 63 | console_flow ns16550_flow_DTRCTS = { |
---|
[0737710] | 64 | ns16550_negate_DTR, /* deviceStopRemoteTx */ |
---|
| 65 | ns16550_assert_DTR /* deviceStartRemoteTx */ |
---|
| 66 | }; |
---|
| 67 | |
---|
[cae0504] | 68 | console_fns ns16550_fns = { |
---|
[064b9be] | 69 | libchip_serial_default_probe, /* deviceProbe */ |
---|
[0737710] | 70 | ns16550_open, /* deviceFirstOpen */ |
---|
[29badca6] | 71 | ns16550_close, /* deviceLastClose */ |
---|
[0737710] | 72 | NULL, /* deviceRead */ |
---|
| 73 | ns16550_write_support_int, /* deviceWrite */ |
---|
[29badca6] | 74 | ns16550_init, /* deviceInitialize */ |
---|
[0737710] | 75 | ns16550_write_polled, /* deviceWritePolled */ |
---|
[8739322] | 76 | ns16550_set_attributes, /* deviceSetAttributes */ |
---|
[6640459d] | 77 | true /* deviceOutputUsesInterrupts */ |
---|
[0737710] | 78 | }; |
---|
| 79 | |
---|
[cae0504] | 80 | console_fns ns16550_fns_polled = { |
---|
[064b9be] | 81 | libchip_serial_default_probe, /* deviceProbe */ |
---|
[0737710] | 82 | ns16550_open, /* deviceFirstOpen */ |
---|
| 83 | ns16550_close, /* deviceLastClose */ |
---|
| 84 | ns16550_inbyte_nonblocking_polled, /* deviceRead */ |
---|
| 85 | ns16550_write_support_polled, /* deviceWrite */ |
---|
| 86 | ns16550_init, /* deviceInitialize */ |
---|
| 87 | ns16550_write_polled, /* deviceWritePolled */ |
---|
[8739322] | 88 | ns16550_set_attributes, /* deviceSetAttributes */ |
---|
[6640459d] | 89 | false /* deviceOutputUsesInterrupts */ |
---|
[0737710] | 90 | }; |
---|
| 91 | |
---|
[cae0504] | 92 | /* |
---|
| 93 | * ns16550_init |
---|
| 94 | */ |
---|
| 95 | |
---|
[27045a82] | 96 | NS16550_STATIC void ns16550_init(int minor) |
---|
[0737710] | 97 | { |
---|
[642c500] | 98 | uintptr_t pNS16550; |
---|
[ee4f57d] | 99 | uint8_t ucTrash; |
---|
| 100 | uint8_t ucDataByte; |
---|
| 101 | uint32_t ulBaudDivisor; |
---|
[0737710] | 102 | ns16550_context *pns16550Context; |
---|
| 103 | setRegister_f setReg; |
---|
| 104 | getRegister_f getReg; |
---|
| 105 | |
---|
| 106 | pns16550Context=(ns16550_context *)malloc(sizeof(ns16550_context)); |
---|
| 107 | |
---|
[e97e0e0] | 108 | if (pns16550Context == NULL) { |
---|
| 109 | printk( "%s: Error: Not enough memory\n", __func__); |
---|
| 110 | rtems_fatal_error_occurred( 0xdeadbeef); |
---|
| 111 | } |
---|
| 112 | |
---|
[0737710] | 113 | Console_Port_Data[minor].pDeviceContext=(void *)pns16550Context; |
---|
| 114 | pns16550Context->ucModemCtrl=SP_MODEM_IRQ; |
---|
| 115 | |
---|
[229bcca8] | 116 | pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1; |
---|
| 117 | setReg = Console_Port_Tbl[minor]->setRegister; |
---|
| 118 | getReg = Console_Port_Tbl[minor]->getRegister; |
---|
[0737710] | 119 | |
---|
| 120 | /* Clear the divisor latch, clear all interrupt enables, |
---|
| 121 | * and reset and |
---|
| 122 | * disable the FIFO's. |
---|
| 123 | */ |
---|
| 124 | |
---|
| 125 | (*setReg)(pNS16550, NS16550_LINE_CONTROL, 0x0); |
---|
[70a9451] | 126 | ns16550_enable_interrupts(minor, NS16550_DISABLE_ALL_INTR); |
---|
[0737710] | 127 | |
---|
| 128 | /* Set the divisor latch and set the baud rate. */ |
---|
| 129 | |
---|
[df49c60] | 130 | ulBaudDivisor = NS16550_Baud( |
---|
[229bcca8] | 131 | (uint32_t) Console_Port_Tbl[minor]->ulClock, |
---|
| 132 | (uint32_t) ((uintptr_t)Console_Port_Tbl[minor]->pDeviceParams) |
---|
[df49c60] | 133 | ); |
---|
[0737710] | 134 | ucDataByte = SP_LINE_DLAB; |
---|
| 135 | (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte); |
---|
[70a9451] | 136 | |
---|
| 137 | /* XXX */ |
---|
[e97e0e0] | 138 | (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, (uint8_t) (ulBaudDivisor & 0xffU)); |
---|
| 139 | (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, (uint8_t) ((ulBaudDivisor >> 8) & 0xffU)); |
---|
[0737710] | 140 | |
---|
| 141 | /* Clear the divisor latch and set the character size to eight bits */ |
---|
| 142 | /* with one stop bit and no parity checking. */ |
---|
| 143 | ucDataByte = EIGHT_BITS; |
---|
| 144 | (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte); |
---|
| 145 | |
---|
| 146 | /* Enable and reset transmit and receive FIFOs. TJA */ |
---|
| 147 | ucDataByte = SP_FIFO_ENABLE; |
---|
| 148 | (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte); |
---|
| 149 | |
---|
| 150 | ucDataByte = SP_FIFO_ENABLE | SP_FIFO_RXRST | SP_FIFO_TXRST; |
---|
| 151 | (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte); |
---|
| 152 | |
---|
[70a9451] | 153 | ns16550_enable_interrupts(minor, NS16550_DISABLE_ALL_INTR); |
---|
[0737710] | 154 | |
---|
| 155 | /* Set data terminal ready. */ |
---|
| 156 | /* And open interrupt tristate line */ |
---|
| 157 | (*setReg)(pNS16550, NS16550_MODEM_CONTROL,pns16550Context->ucModemCtrl); |
---|
| 158 | |
---|
| 159 | ucTrash = (*getReg)(pNS16550, NS16550_LINE_STATUS ); |
---|
| 160 | ucTrash = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER ); |
---|
| 161 | } |
---|
| 162 | |
---|
[cae0504] | 163 | /* |
---|
| 164 | * ns16550_open |
---|
| 165 | */ |
---|
| 166 | |
---|
[27045a82] | 167 | NS16550_STATIC int ns16550_open( |
---|
[ec5d4505] | 168 | int major, |
---|
| 169 | int minor, |
---|
| 170 | void *arg |
---|
[0737710] | 171 | ) |
---|
| 172 | { |
---|
[ec5d4505] | 173 | rtems_libio_open_close_args_t *oc = (rtems_libio_open_close_args_t *) arg; |
---|
| 174 | struct rtems_termios_tty *tty = (struct rtems_termios_tty *) oc->iop->data1; |
---|
[229bcca8] | 175 | console_tbl *c = Console_Port_Tbl [minor]; |
---|
[7cafde22] | 176 | console_data *d = &Console_Port_Data [minor]; |
---|
| 177 | |
---|
| 178 | d->termios_data = tty; |
---|
[0737710] | 179 | |
---|
[ec5d4505] | 180 | /* Assert DTR */ |
---|
| 181 | if (c->pDeviceFlow != &ns16550_flow_DTRCTS) { |
---|
| 182 | ns16550_assert_DTR( minor); |
---|
[0737710] | 183 | } |
---|
| 184 | |
---|
[ec5d4505] | 185 | /* Set initial baud */ |
---|
[642c500] | 186 | rtems_termios_set_initial_baud( tty, (intptr_t) c->pDeviceParams); |
---|
[ec5d4505] | 187 | |
---|
[7cafde22] | 188 | if (c->pDeviceFns->deviceOutputUsesInterrupts) { |
---|
[29badca6] | 189 | ns16550_initialize_interrupts( minor); |
---|
[7cafde22] | 190 | ns16550_enable_interrupts( minor, NS16550_ENABLE_ALL_INTR_EXCEPT_TX); |
---|
| 191 | } |
---|
| 192 | |
---|
[ec5d4505] | 193 | return RTEMS_SUCCESSFUL; |
---|
[0737710] | 194 | } |
---|
| 195 | |
---|
[cae0504] | 196 | /* |
---|
| 197 | * ns16550_close |
---|
| 198 | */ |
---|
| 199 | |
---|
[27045a82] | 200 | NS16550_STATIC int ns16550_close( |
---|
[0737710] | 201 | int major, |
---|
| 202 | int minor, |
---|
| 203 | void * arg |
---|
| 204 | ) |
---|
| 205 | { |
---|
[fbf7e58] | 206 | console_tbl *c = &Console_Port_Tbl [minor]; |
---|
| 207 | |
---|
[0737710] | 208 | /* |
---|
| 209 | * Negate DTR |
---|
| 210 | */ |
---|
[fbf7e58] | 211 | if (c->pDeviceFlow != &ns16550_flow_DTRCTS) { |
---|
[0737710] | 212 | ns16550_negate_DTR(minor); |
---|
| 213 | } |
---|
| 214 | |
---|
[7cafde22] | 215 | ns16550_enable_interrupts(minor, NS16550_DISABLE_ALL_INTR); |
---|
| 216 | |
---|
[fbf7e58] | 217 | if (c->pDeviceFns->deviceOutputUsesInterrupts) { |
---|
| 218 | ns16550_cleanup_interrupts(minor); |
---|
| 219 | } |
---|
| 220 | |
---|
[0737710] | 221 | return(RTEMS_SUCCESSFUL); |
---|
| 222 | } |
---|
| 223 | |
---|
[9151ec6] | 224 | /** |
---|
| 225 | * @brief Polled write for NS16550. |
---|
[0737710] | 226 | */ |
---|
[642c500] | 227 | NS16550_STATIC void ns16550_write_polled(int minor, char out) |
---|
[0737710] | 228 | { |
---|
[229bcca8] | 229 | console_tbl *c = Console_Port_Tbl [minor]; |
---|
[642c500] | 230 | uintptr_t port = c->ulCtrlPort1; |
---|
[ec5d4505] | 231 | getRegister_f get = c->getRegister; |
---|
| 232 | setRegister_f set = c->setRegister; |
---|
| 233 | uint32_t status = 0; |
---|
[9151ec6] | 234 | rtems_interrupt_level level; |
---|
| 235 | |
---|
[ec5d4505] | 236 | /* Save port interrupt mask */ |
---|
| 237 | uint32_t interrupt_mask = get( port, NS16550_INTERRUPT_ENABLE); |
---|
| 238 | |
---|
| 239 | /* Disable port interrupts */ |
---|
| 240 | ns16550_enable_interrupts( minor, NS16550_DISABLE_ALL_INTR); |
---|
| 241 | |
---|
| 242 | while (true) { |
---|
[9151ec6] | 243 | /* Try to transmit the character in a critical section */ |
---|
| 244 | rtems_interrupt_disable( level); |
---|
| 245 | |
---|
| 246 | /* Read the transmitter holding register and check it */ |
---|
| 247 | status = get( port, NS16550_LINE_STATUS); |
---|
| 248 | if ((status & SP_LSR_THOLD) != 0) { |
---|
| 249 | /* Transmit character */ |
---|
[ec5d4505] | 250 | set( port, NS16550_TRANSMIT_BUFFER, out); |
---|
[9151ec6] | 251 | |
---|
| 252 | /* Finished */ |
---|
| 253 | rtems_interrupt_enable( level); |
---|
| 254 | break; |
---|
| 255 | } else { |
---|
| 256 | rtems_interrupt_enable( level); |
---|
| 257 | } |
---|
| 258 | |
---|
| 259 | /* Wait for transmitter holding register to be empty */ |
---|
| 260 | do { |
---|
| 261 | status = get( port, NS16550_LINE_STATUS); |
---|
| 262 | } while ((status & SP_LSR_THOLD) == 0); |
---|
[0737710] | 263 | } |
---|
[ec5d4505] | 264 | |
---|
| 265 | /* Restore port interrupt mask */ |
---|
| 266 | set( port, NS16550_INTERRUPT_ENABLE, interrupt_mask); |
---|
[0737710] | 267 | } |
---|
| 268 | |
---|
| 269 | /* |
---|
| 270 | * These routines provide control of the RTS and DTR lines |
---|
| 271 | */ |
---|
[cae0504] | 272 | |
---|
[0737710] | 273 | /* |
---|
| 274 | * ns16550_assert_RTS |
---|
| 275 | */ |
---|
[cae0504] | 276 | |
---|
[27045a82] | 277 | NS16550_STATIC int ns16550_assert_RTS(int minor) |
---|
[0737710] | 278 | { |
---|
[ee4f57d] | 279 | uint32_t pNS16550; |
---|
| 280 | uint32_t Irql; |
---|
[0737710] | 281 | ns16550_context *pns16550Context; |
---|
| 282 | setRegister_f setReg; |
---|
| 283 | |
---|
| 284 | pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext; |
---|
| 285 | |
---|
[229bcca8] | 286 | pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1; |
---|
| 287 | setReg = Console_Port_Tbl[minor]->setRegister; |
---|
[0737710] | 288 | |
---|
| 289 | /* |
---|
| 290 | * Assert RTS |
---|
| 291 | */ |
---|
| 292 | rtems_interrupt_disable(Irql); |
---|
| 293 | pns16550Context->ucModemCtrl|=SP_MODEM_RTS; |
---|
| 294 | (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl); |
---|
| 295 | rtems_interrupt_enable(Irql); |
---|
| 296 | return 0; |
---|
| 297 | } |
---|
| 298 | |
---|
| 299 | /* |
---|
| 300 | * ns16550_negate_RTS |
---|
| 301 | */ |
---|
[cae0504] | 302 | |
---|
[27045a82] | 303 | NS16550_STATIC int ns16550_negate_RTS(int minor) |
---|
[0737710] | 304 | { |
---|
[ee4f57d] | 305 | uint32_t pNS16550; |
---|
| 306 | uint32_t Irql; |
---|
[0737710] | 307 | ns16550_context *pns16550Context; |
---|
| 308 | setRegister_f setReg; |
---|
| 309 | |
---|
| 310 | pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext; |
---|
| 311 | |
---|
[229bcca8] | 312 | pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1; |
---|
| 313 | setReg = Console_Port_Tbl[minor]->setRegister; |
---|
[0737710] | 314 | |
---|
| 315 | /* |
---|
| 316 | * Negate RTS |
---|
| 317 | */ |
---|
| 318 | rtems_interrupt_disable(Irql); |
---|
| 319 | pns16550Context->ucModemCtrl&=~SP_MODEM_RTS; |
---|
| 320 | (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl); |
---|
| 321 | rtems_interrupt_enable(Irql); |
---|
| 322 | return 0; |
---|
| 323 | } |
---|
| 324 | |
---|
| 325 | /* |
---|
| 326 | * These flow control routines utilise a connection from the local DTR |
---|
| 327 | * line to the remote CTS line |
---|
| 328 | */ |
---|
[cae0504] | 329 | |
---|
[0737710] | 330 | /* |
---|
| 331 | * ns16550_assert_DTR |
---|
| 332 | */ |
---|
[cae0504] | 333 | |
---|
[27045a82] | 334 | NS16550_STATIC int ns16550_assert_DTR(int minor) |
---|
[0737710] | 335 | { |
---|
[ee4f57d] | 336 | uint32_t pNS16550; |
---|
| 337 | uint32_t Irql; |
---|
[0737710] | 338 | ns16550_context *pns16550Context; |
---|
| 339 | setRegister_f setReg; |
---|
| 340 | |
---|
| 341 | pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext; |
---|
| 342 | |
---|
[229bcca8] | 343 | pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1; |
---|
| 344 | setReg = Console_Port_Tbl[minor]->setRegister; |
---|
[0737710] | 345 | |
---|
| 346 | /* |
---|
| 347 | * Assert DTR |
---|
| 348 | */ |
---|
| 349 | rtems_interrupt_disable(Irql); |
---|
| 350 | pns16550Context->ucModemCtrl|=SP_MODEM_DTR; |
---|
| 351 | (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl); |
---|
| 352 | rtems_interrupt_enable(Irql); |
---|
| 353 | return 0; |
---|
| 354 | } |
---|
| 355 | |
---|
| 356 | /* |
---|
| 357 | * ns16550_negate_DTR |
---|
| 358 | */ |
---|
[8739322] | 359 | |
---|
[27045a82] | 360 | NS16550_STATIC int ns16550_negate_DTR(int minor) |
---|
[0737710] | 361 | { |
---|
[ee4f57d] | 362 | uint32_t pNS16550; |
---|
| 363 | uint32_t Irql; |
---|
[0737710] | 364 | ns16550_context *pns16550Context; |
---|
| 365 | setRegister_f setReg; |
---|
| 366 | |
---|
| 367 | pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext; |
---|
| 368 | |
---|
[229bcca8] | 369 | pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1; |
---|
| 370 | setReg = Console_Port_Tbl[minor]->setRegister; |
---|
[0737710] | 371 | |
---|
| 372 | /* |
---|
| 373 | * Negate DTR |
---|
| 374 | */ |
---|
| 375 | rtems_interrupt_disable(Irql); |
---|
| 376 | pns16550Context->ucModemCtrl&=~SP_MODEM_DTR; |
---|
| 377 | (*setReg)(pNS16550, NS16550_MODEM_CONTROL,pns16550Context->ucModemCtrl); |
---|
| 378 | rtems_interrupt_enable(Irql); |
---|
| 379 | return 0; |
---|
| 380 | } |
---|
| 381 | |
---|
[8739322] | 382 | /* |
---|
| 383 | * ns16550_set_attributes |
---|
| 384 | * |
---|
| 385 | * This function sets the channel to reflect the requested termios |
---|
| 386 | * port settings. |
---|
| 387 | */ |
---|
| 388 | |
---|
| 389 | NS16550_STATIC int ns16550_set_attributes( |
---|
| 390 | int minor, |
---|
| 391 | const struct termios *t |
---|
| 392 | ) |
---|
| 393 | { |
---|
[ee4f57d] | 394 | uint32_t pNS16550; |
---|
| 395 | uint32_t ulBaudDivisor; |
---|
| 396 | uint8_t ucLineControl; |
---|
| 397 | uint32_t baud_requested; |
---|
[8739322] | 398 | setRegister_f setReg; |
---|
| 399 | getRegister_f getReg; |
---|
[ee4f57d] | 400 | uint32_t Irql; |
---|
[8739322] | 401 | |
---|
[229bcca8] | 402 | pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1; |
---|
| 403 | setReg = Console_Port_Tbl[minor]->setRegister; |
---|
| 404 | getReg = Console_Port_Tbl[minor]->getRegister; |
---|
[8739322] | 405 | |
---|
| 406 | /* |
---|
| 407 | * Calculate the baud rate divisor |
---|
| 408 | */ |
---|
| 409 | |
---|
| 410 | baud_requested = t->c_cflag & CBAUD; |
---|
| 411 | if (!baud_requested) |
---|
| 412 | baud_requested = B9600; /* default to 9600 baud */ |
---|
| 413 | |
---|
[df49c60] | 414 | ulBaudDivisor = NS16550_Baud( |
---|
[229bcca8] | 415 | (uint32_t) Console_Port_Tbl[minor]->ulClock, |
---|
[64a1529] | 416 | rtems_termios_baud_to_number(baud_requested) |
---|
[df49c60] | 417 | ); |
---|
[8739322] | 418 | |
---|
| 419 | ucLineControl = 0; |
---|
| 420 | |
---|
| 421 | /* |
---|
| 422 | * Parity |
---|
| 423 | */ |
---|
| 424 | |
---|
| 425 | if (t->c_cflag & PARENB) { |
---|
| 426 | ucLineControl |= SP_LINE_PAR; |
---|
| 427 | if (!(t->c_cflag & PARODD)) |
---|
| 428 | ucLineControl |= SP_LINE_ODD; |
---|
| 429 | } |
---|
| 430 | |
---|
| 431 | /* |
---|
| 432 | * Character Size |
---|
| 433 | */ |
---|
| 434 | |
---|
| 435 | if (t->c_cflag & CSIZE) { |
---|
| 436 | switch (t->c_cflag & CSIZE) { |
---|
| 437 | case CS5: ucLineControl |= FIVE_BITS; break; |
---|
| 438 | case CS6: ucLineControl |= SIX_BITS; break; |
---|
| 439 | case CS7: ucLineControl |= SEVEN_BITS; break; |
---|
| 440 | case CS8: ucLineControl |= EIGHT_BITS; break; |
---|
| 441 | } |
---|
| 442 | } else { |
---|
| 443 | ucLineControl |= EIGHT_BITS; /* default to 9600,8,N,1 */ |
---|
| 444 | } |
---|
| 445 | |
---|
| 446 | /* |
---|
| 447 | * Stop Bits |
---|
| 448 | */ |
---|
| 449 | |
---|
| 450 | if (t->c_cflag & CSTOPB) { |
---|
| 451 | ucLineControl |= SP_LINE_STOP; /* 2 stop bits */ |
---|
| 452 | } else { |
---|
| 453 | ; /* 1 stop bit */ |
---|
| 454 | } |
---|
| 455 | |
---|
| 456 | /* |
---|
| 457 | * Now actually set the chip |
---|
| 458 | */ |
---|
| 459 | |
---|
| 460 | rtems_interrupt_disable(Irql); |
---|
| 461 | |
---|
| 462 | /* |
---|
[a3d3d9a] | 463 | * Set the baud rate |
---|
[8739322] | 464 | */ |
---|
| 465 | |
---|
| 466 | (*setReg)(pNS16550, NS16550_LINE_CONTROL, SP_LINE_DLAB); |
---|
| 467 | /* XXX are these registers right? */ |
---|
| 468 | (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, ulBaudDivisor&0xff); |
---|
| 469 | (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, (ulBaudDivisor>>8)&0xff); |
---|
| 470 | |
---|
| 471 | /* |
---|
| 472 | * Now write the line control |
---|
| 473 | */ |
---|
| 474 | (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucLineControl ); |
---|
| 475 | |
---|
| 476 | rtems_interrupt_enable(Irql); |
---|
| 477 | |
---|
[07a3253d] | 478 | return 0; |
---|
[8739322] | 479 | } |
---|
| 480 | |
---|
[2e3f4398] | 481 | #if defined(BSP_FEATURE_IRQ_EXTENSION) || defined(BSP_FEATURE_IRQ_LEGACY) |
---|
[0737710] | 482 | |
---|
[ec5d4505] | 483 | /** |
---|
| 484 | * @brief Process interrupt. |
---|
| 485 | */ |
---|
| 486 | NS16550_STATIC void ns16550_process( int minor) |
---|
[0737710] | 487 | { |
---|
[229bcca8] | 488 | console_tbl *c = Console_Port_Tbl [minor]; |
---|
[e97e0e0] | 489 | console_data *d = &Console_Port_Data [minor]; |
---|
[ec5d4505] | 490 | ns16550_context *ctx = d->pDeviceContext; |
---|
| 491 | uint32_t port = c->ulCtrlPort1; |
---|
| 492 | getRegister_f get = c->getRegister; |
---|
| 493 | int i = 0; |
---|
| 494 | char buf [SP_FIFO_SIZE]; |
---|
| 495 | |
---|
| 496 | /* Iterate until no more interrupts are pending */ |
---|
[0737710] | 497 | do { |
---|
[ec5d4505] | 498 | /* Fetch received characters */ |
---|
| 499 | for (i = 0; i < SP_FIFO_SIZE; ++i) { |
---|
| 500 | if ((get( port, NS16550_LINE_STATUS) & SP_LSR_RDY) != 0) { |
---|
| 501 | buf [i] = (char) get(port, NS16550_RECEIVE_BUFFER); |
---|
| 502 | } else { |
---|
[0737710] | 503 | break; |
---|
| 504 | } |
---|
| 505 | } |
---|
| 506 | |
---|
[ec5d4505] | 507 | /* Enqueue fetched characters */ |
---|
| 508 | rtems_termios_enqueue_raw_characters( d->termios_data, buf, i); |
---|
[70a9451] | 509 | |
---|
[ec5d4505] | 510 | /* Check if we can dequeue transmitted characters */ |
---|
| 511 | if (ctx->transmitFifoChars > 0 |
---|
| 512 | && (get( port, NS16550_LINE_STATUS) & SP_LSR_THOLD) != 0) { |
---|
| 513 | unsigned chars = ctx->transmitFifoChars; |
---|
| 514 | |
---|
| 515 | /* |
---|
| 516 | * We finished the transmission, so clear the number of characters in the |
---|
| 517 | * transmit FIFO. |
---|
| 518 | */ |
---|
| 519 | ctx->transmitFifoChars = 0; |
---|
[70a9451] | 520 | |
---|
[ec5d4505] | 521 | /* Dequeue transmitted characters */ |
---|
| 522 | if (rtems_termios_dequeue_characters( d->termios_data, chars) == 0) { |
---|
| 523 | /* Nothing to do */ |
---|
[e97e0e0] | 524 | d->bActive = false; |
---|
[ec5d4505] | 525 | ns16550_enable_interrupts( minor, NS16550_ENABLE_ALL_INTR_EXCEPT_TX); |
---|
[0737710] | 526 | } |
---|
| 527 | } |
---|
[ec5d4505] | 528 | } while ((get( port, NS16550_INTERRUPT_ID) & SP_IID_0) == 0); |
---|
| 529 | } |
---|
| 530 | #endif |
---|
| 531 | |
---|
| 532 | /** |
---|
| 533 | * @brief Transmits up to @a len characters from @a buf. |
---|
| 534 | * |
---|
| 535 | * This routine is invoked either from task context with disabled interrupts to |
---|
| 536 | * start a new transmission process with exactly one character in case of an |
---|
| 537 | * idle output state or from the interrupt handler to refill the transmitter. |
---|
| 538 | * |
---|
| 539 | * Returns always zero. |
---|
| 540 | */ |
---|
[3ed964f9] | 541 | NS16550_STATIC ssize_t ns16550_write_support_int( |
---|
[ec5d4505] | 542 | int minor, |
---|
| 543 | const char *buf, |
---|
[3ed964f9] | 544 | size_t len |
---|
[ec5d4505] | 545 | ) |
---|
| 546 | { |
---|
[229bcca8] | 547 | console_tbl *c = Console_Port_Tbl [minor]; |
---|
[ec5d4505] | 548 | console_data *d = &Console_Port_Data [minor]; |
---|
| 549 | ns16550_context *ctx = d->pDeviceContext; |
---|
| 550 | uint32_t port = c->ulCtrlPort1; |
---|
| 551 | setRegister_f set = c->setRegister; |
---|
| 552 | int i = 0; |
---|
| 553 | int out = len > SP_FIFO_SIZE ? SP_FIFO_SIZE : len; |
---|
| 554 | |
---|
| 555 | for (i = 0; i < out; ++i) { |
---|
| 556 | set( port, NS16550_TRANSMIT_BUFFER, buf [i]); |
---|
| 557 | } |
---|
| 558 | |
---|
| 559 | if (len > 0) { |
---|
| 560 | ctx->transmitFifoChars = out; |
---|
| 561 | d->bActive = true; |
---|
| 562 | ns16550_enable_interrupts( minor, NS16550_ENABLE_ALL_INTR); |
---|
| 563 | } |
---|
[c163605f] | 564 | |
---|
[ec5d4505] | 565 | return 0; |
---|
[c163605f] | 566 | } |
---|
| 567 | |
---|
[0737710] | 568 | /* |
---|
[70a9451] | 569 | * ns16550_enable_interrupts |
---|
[0737710] | 570 | * |
---|
[70a9451] | 571 | * This routine initializes the port to have the specified interrupts masked. |
---|
[0737710] | 572 | */ |
---|
[27045a82] | 573 | NS16550_STATIC void ns16550_enable_interrupts( |
---|
[70a9451] | 574 | int minor, |
---|
| 575 | int mask |
---|
[0737710] | 576 | ) |
---|
| 577 | { |
---|
[ee4f57d] | 578 | uint32_t pNS16550; |
---|
[70a9451] | 579 | setRegister_f setReg; |
---|
[0737710] | 580 | |
---|
[229bcca8] | 581 | pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1; |
---|
| 582 | setReg = Console_Port_Tbl[minor]->setRegister; |
---|
[0737710] | 583 | |
---|
[70a9451] | 584 | (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, mask); |
---|
[0737710] | 585 | } |
---|
| 586 | |
---|
[2e3f4398] | 587 | #if defined(BSP_FEATURE_IRQ_EXTENSION) || defined(BSP_FEATURE_IRQ_LEGACY) |
---|
| 588 | NS16550_STATIC rtems_isr ns16550_isr(void *arg) |
---|
| 589 | { |
---|
[e97e0e0] | 590 | int minor = (int) arg; |
---|
| 591 | |
---|
| 592 | ns16550_process( minor); |
---|
[2e3f4398] | 593 | } |
---|
| 594 | #endif |
---|
[e97e0e0] | 595 | |
---|
[70a9451] | 596 | /* |
---|
| 597 | * ns16550_initialize_interrupts |
---|
| 598 | * |
---|
| 599 | * This routine initializes the port to operate in interrupt driver mode. |
---|
[a3d3d9a] | 600 | */ |
---|
[e97e0e0] | 601 | NS16550_STATIC void ns16550_initialize_interrupts( int minor) |
---|
[0737710] | 602 | { |
---|
[69be08ee] | 603 | #if defined(BSP_FEATURE_IRQ_EXTENSION) || defined(BSP_FEATURE_IRQ_LEGACY) |
---|
[d1e10fe6] | 604 | console_tbl *c = &Console_Port_Tbl [minor]; |
---|
[2e3f4398] | 605 | #endif |
---|
[e97e0e0] | 606 | console_data *d = &Console_Port_Data [minor]; |
---|
| 607 | |
---|
| 608 | d->bActive = false; |
---|
| 609 | |
---|
| 610 | #ifdef BSP_FEATURE_IRQ_EXTENSION |
---|
| 611 | { |
---|
| 612 | rtems_status_code sc = RTEMS_SUCCESSFUL; |
---|
| 613 | sc = rtems_interrupt_handler_install( |
---|
| 614 | c->ulIntVector, |
---|
| 615 | "NS16550", |
---|
| 616 | RTEMS_INTERRUPT_SHARED, |
---|
| 617 | ns16550_isr, |
---|
| 618 | (void *) minor |
---|
| 619 | ); |
---|
| 620 | if (sc != RTEMS_SUCCESSFUL) { |
---|
| 621 | /* FIXME */ |
---|
| 622 | printk( "%s: Error: Install interrupt handler\n", __func__); |
---|
| 623 | rtems_fatal_error_occurred( 0xdeadbeef); |
---|
| 624 | } |
---|
| 625 | } |
---|
[69be08ee] | 626 | #elif defined(BSP_FEATURE_IRQ_LEGACY) |
---|
[e97e0e0] | 627 | { |
---|
| 628 | int rv = 0; |
---|
| 629 | #ifdef BSP_FEATURE_IRQ_LEGACY_SHARED_HANDLER_SUPPORT |
---|
| 630 | rtems_irq_connect_data cd = { |
---|
| 631 | c->ulIntVector, |
---|
| 632 | ns16550_isr, |
---|
[95ef82c] | 633 | (void *) minor, |
---|
[e97e0e0] | 634 | NULL, |
---|
| 635 | NULL, |
---|
| 636 | NULL, |
---|
| 637 | NULL |
---|
| 638 | }; |
---|
| 639 | rv = BSP_install_rtems_shared_irq_handler( &cd); |
---|
| 640 | #else |
---|
| 641 | rtems_irq_connect_data cd = { |
---|
| 642 | c->ulIntVector, |
---|
| 643 | ns16550_isr, |
---|
[0fbd231] | 644 | (void *) minor, |
---|
[e97e0e0] | 645 | NULL, |
---|
| 646 | NULL, |
---|
| 647 | NULL |
---|
| 648 | }; |
---|
| 649 | rv = BSP_install_rtems_irq_handler( &cd); |
---|
| 650 | #endif |
---|
| 651 | if (rv == 0) { |
---|
| 652 | /* FIXME */ |
---|
| 653 | printk( "%s: Error: Install interrupt handler\n", __func__); |
---|
| 654 | rtems_fatal_error_occurred( 0xdeadbeef); |
---|
| 655 | } |
---|
| 656 | } |
---|
| 657 | #endif |
---|
[0737710] | 658 | } |
---|
| 659 | |
---|
[fbf7e58] | 660 | NS16550_STATIC void ns16550_cleanup_interrupts(int minor) |
---|
| 661 | { |
---|
| 662 | #if defined(BSP_FEATURE_IRQ_EXTENSION) |
---|
| 663 | rtems_status_code sc = RTEMS_SUCCESSFUL; |
---|
| 664 | console_tbl *c = &Console_Port_Tbl [minor]; |
---|
| 665 | sc = rtems_interrupt_handler_remove( |
---|
| 666 | c->ulIntVector, |
---|
| 667 | ns16550_isr, |
---|
| 668 | (void *) minor |
---|
| 669 | ); |
---|
| 670 | if (sc != RTEMS_SUCCESSFUL) { |
---|
| 671 | /* FIXME */ |
---|
| 672 | printk("%s: Error: Remove interrupt handler\n", __func__); |
---|
| 673 | rtems_fatal_error_occurred(0xdeadbeef); |
---|
| 674 | } |
---|
| 675 | #elif defined(BSP_FEATURE_IRQ_LEGACY) |
---|
| 676 | int rv = 0; |
---|
| 677 | console_tbl *c = &Console_Port_Tbl [minor]; |
---|
| 678 | rtems_irq_connect_data cd = { |
---|
| 679 | .name = c->ulIntVector, |
---|
| 680 | .hdl = ns16550_isr, |
---|
| 681 | .handle = (void *) minor |
---|
| 682 | }; |
---|
| 683 | rv = BSP_remove_rtems_irq_handler(&cd); |
---|
| 684 | if (rv == 0) { |
---|
| 685 | /* FIXME */ |
---|
| 686 | printk("%s: Error: Remove interrupt handler\n", __func__); |
---|
| 687 | rtems_fatal_error_occurred(0xdeadbeef); |
---|
| 688 | } |
---|
| 689 | #endif |
---|
| 690 | } |
---|
| 691 | |
---|
[a3d3d9a] | 692 | /* |
---|
[0737710] | 693 | * ns16550_write_support_polled |
---|
| 694 | * |
---|
| 695 | * Console Termios output entry point. |
---|
| 696 | * |
---|
| 697 | */ |
---|
[70a9451] | 698 | |
---|
[3ed964f9] | 699 | NS16550_STATIC ssize_t ns16550_write_support_polled( |
---|
[a3d3d9a] | 700 | int minor, |
---|
| 701 | const char *buf, |
---|
[3ed964f9] | 702 | size_t len |
---|
[0737710] | 703 | ) |
---|
| 704 | { |
---|
| 705 | int nwrite = 0; |
---|
| 706 | |
---|
| 707 | /* |
---|
| 708 | * poll each byte in the string out of the port. |
---|
| 709 | */ |
---|
| 710 | while (nwrite < len) { |
---|
| 711 | /* |
---|
| 712 | * transmit character |
---|
| 713 | */ |
---|
| 714 | ns16550_write_polled(minor, *buf++); |
---|
| 715 | nwrite++; |
---|
| 716 | } |
---|
| 717 | |
---|
| 718 | /* |
---|
| 719 | * return the number of bytes written. |
---|
| 720 | */ |
---|
| 721 | return nwrite; |
---|
| 722 | } |
---|
| 723 | |
---|
[a3d3d9a] | 724 | /* |
---|
| 725 | * ns16550_inbyte_nonblocking_polled |
---|
[0737710] | 726 | * |
---|
| 727 | * Console Termios polling input entry point. |
---|
| 728 | */ |
---|
| 729 | |
---|
[a3d3d9a] | 730 | NS16550_STATIC int ns16550_inbyte_nonblocking_polled( |
---|
| 731 | int minor |
---|
[0737710] | 732 | ) |
---|
| 733 | { |
---|
[ee4f57d] | 734 | uint32_t pNS16550; |
---|
[0737710] | 735 | unsigned char ucLineStatus; |
---|
| 736 | char cChar; |
---|
| 737 | getRegister_f getReg; |
---|
| 738 | |
---|
[229bcca8] | 739 | pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1; |
---|
| 740 | getReg = Console_Port_Tbl[minor]->getRegister; |
---|
[0737710] | 741 | |
---|
| 742 | ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS); |
---|
| 743 | if(ucLineStatus & SP_LSR_RDY) { |
---|
| 744 | cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER); |
---|
[8a2d4f2b] | 745 | return (int)cChar; |
---|
[0737710] | 746 | } else { |
---|
[8a2d4f2b] | 747 | return -1; |
---|
[0737710] | 748 | } |
---|
| 749 | } |
---|