📄 console.c
字号:
/* * console.c * * This file contains the MVME167 termios console package. Only asynchronous * I/O is supported. * * /dev/tty0 is channel 0, Serial Port 1/Console on the MVME712M. * /dev/tty1 is channel 1, Serial Port 2/TTY01 on the MVME712M. * /dev/tty2 is channel 2, Serial Port 3 on the MVME712M. * /dev/tty3 is channel 3, Serial Port 4 on the MVME712M. * * Normal I/O uses DMA for output, interrupts for input. /dev/console is * fixed to be /dev/tty01, Serial Port 2. Very limited support is provided * for polled I/O. Polled I/O is intended only for running the RTEMS test * suites. In all cases, Serial Port 1/Console is allocated to 167Bug and * is the dedicated debugger port. We configure GDB to use 167Bug for * debugging. When debugging with GDB or 167Bug, do not open /dev/tty00. * * Modern I/O chips often contain a number of I/O devices that can operate * almost independently of each other. Typically, in RTEMS, all devices in * an I/O chip are handled by a single device driver, but that need not be * always the case. Each device driver must supply six entry points in the * Device Driver Table: a device initialization function, as well as an open, * close, read, write and a control function. RTEMS assigns a device major * number to each device driver. This major device number is the index of the * device driver entries in the Device Driver Table, and it used to identify * a particular device driver. To distinguish multiple I/O sub-devices within * an I/O chip, RTEMS supports device minor numbers. When a I/O device is * initialized, the major number is supplied to the initialization function. * That function must register each sub-device with a separate name and minor * number (as well as the supplied major number). When an application opens a * device by name, the corresponding major and minor numbers are returned to * the caller to be used in subsequent I/O operations (although these details * are typically hidden within the library functions). * * Such a scheme recognizes that the initialization of the individual * sub-devices is generally not completely independent. For example, the * four serial ports of the CD2401 can be configured almost independently * from each other. One port could be configured to operate in asynchronous * mode with interrupt-driven I/O, while another port could be configured to * operate in HDLC mode with DMA I/O. However, a device reset command will * reset all four channels, and the width of DMA transfers and the number of * retries following bus errors selected applies to all four channels. * Consequently, when initializing one channel, one must be careful not to * destroy the configuration of other channels that are already configured. * * One problem with the RTEMS I/O initialization model is that no information * other than a device major number is passed to the initialization function. * Consequently, the sub-devices must be initialized with some pre-determined * configuration. To change the configuration of a sub-device, it is * necessary to either rewrite the initialization function, or to make a * series of rtems_io_control() calls after initialization. The first * approach is not very elegant. The second approach is acceptable if an * application is simply changing baud rates, parity or other such * asynchronous parameters (as supplied by the termios package). But what if * an application requires one channel to run in HDLC or Bisync mode and * another in async mode? With a single driver per I/O chip approach, the * device driver must support multiple protocols. This is feasible, but it * often means that an application that only does asynchronous I/O now links * in code for other unused protocols, thus wasting precious ROM space. * Worse, it requires that the sub-devices be initialized in some * configuration, and that configuration then changed through a series of * device driver control calls. There is no standard API in RTEMS to switch * a serial line to some synchronous protocol. * * A better approach is to treat each channel as a separate device, each with * its own device device driver. The application then supplies its own device * driver table with only the required protocols (drivers) on each line. The * problem with this approach is that the device drivers are not really * independent, given that the I/O sub-devices within a common chip are not * independent themselves. Consequently, the related device drivers must * share some information. In RTEMS, there is no standard location in which * to share information. * * This driver handles all four channels, i.e. it distinguishes the * sub-devices using minor device numbers. Only asynchronous I/O is * supported. The console is currently fixed to be channel 1 on the CD2401, * which corresponds to the TTY01 port (Serial Port 2) on the MVME712M * Transition Module. * * The CD2401 does either interrupt-driven or DMA I/O; it does not support * polling. In interrupt-driven or DMA I/O modes, interrupts from the CD2401 * are routed to the MC68040, and the processor generates an interrupt * acknowledge cycle directly to the CD2401 to obtain an interrupt vector. * The PCCchip2 supports a pseudo-polling mode in which interrupts from the * CD2401 are not routed to the MC68040, but can be detected by the processor * by reading the appropriate CD2401 registers. In this mode, interrupt * acknowledge cycles must be generated to the CD2401 by reading the * appropriate PCCchip2 registers. * * Interrupts from the four channels cannot be routed independently; either * all channels are used in the pseudo-polling mode, or all channels are used * in interrupt-driven/DMA mode. There is no advantage in using the speudo- * polling mode. Consenquently, this driver performs DMA input and output. * Output is performed directly from the termios raw output buffer, while * input is accumulated into a separate buffer. * * THIS MODULE IS NOT RE-ENTRANT! Simultaneous access to a device from * multiple tasks is likely to cause significant problems! Concurrency * control is implemented in the termios package. * * THE INTERRUPT LEVEL IS SET TO 1 FOR ALL CHANNELS. * If the CD2401 is to be used for high speed synchronous serial I/O, the * interrupt priority might need to be increased. * * ALL INTERRUPT HANDLERS ARE SHARED. * When adding extra device drivers, either rewrite the interrupt handlers * to demultiplex the interrupts, or install separate vectors. Common vectors * are currently used to catch spurious interrupts. We could already have * installed separate vectors for each channel and used the spurious * interrupt handler defined in some other BSPs, but handling spurious * interrupts from the CD2401 in this device driver allows us to record more * information on the source of the interrupts. Furthermore, we have observed * the occasional spurious interrupt from channel 0. We definitely do not * to call a debugger for those. * * All page references are to the MVME166/MVME167/MVME187 Single Board * Computer Programmer's Reference Guide (MVME187PG/D2) with the April * 1993 supplements/addenda (MVME187PG/D2A1). * * Copyright (c) 1998, National Research Council of Canada * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.com/license/LICENSE. */#define M167_INIT#include <stdarg.h>#include <stdio.h>#include <termios.h>#include <bsp.h> /* Must be before libio.h */#include <rtems/libio.h>/* Utility functions */void cd2401_udelay( unsigned long delay );void cd2401_chan_cmd( rtems_unsigned8 channel, rtems_unsigned8 cmd, rtems_unsigned8 wait );rtems_unsigned16 cd2401_bitrate_divisor( rtems_unsigned32 clkrate, rtems_unsigned32* bitrate );void cd2401_initialize( void );void cd2401_interrupts_initialize( rtems_boolean enable );/* ISRs */rtems_isr cd2401_modem_isr( rtems_vector_number vector );rtems_isr cd2401_re_isr( rtems_vector_number vector );rtems_isr cd2401_rx_isr( rtems_vector_number vector );rtems_isr cd2401_tx_isr( rtems_vector_number vector );/* Termios callbacks */int cd2401_firstOpen( int major, int minor, void *arg );int cd2401_lastClose( int major, int minor, void *arg );int cd2401_setAttributes( int minor, const struct termios *t );int cd2401_startRemoteTx( int minor );int cd2401_stopRemoteTx( int minor );int cd2401_write( int minor, const char *buf, int len );int cd2401_drainOutput( int minor );int _167Bug_pollRead( int minor );int _167Bug_pollWrite( int minor, const char *buf, int len );/* Printk function */static void _BSP_output_char( char c );BSP_output_char_function_type BSP_output_char = _BSP_output_char;/* Channel info *//* static */ volatile struct { void *tty; /* Really a struct rtems_termios_tty * */ int len; /* Record nb of chars being TX'ed */ const char *buf; /* Record where DMA is coming from */ rtems_unsigned32 spur_cnt; /* Nb of spurious ints so far */ rtems_unsigned32 spur_dev; /* Indo on last spurious int */ rtems_unsigned32 buserr_addr; /* Faulting address */ rtems_unsigned32 buserr_type; /* Reason of bus error during DMA */ rtems_unsigned8 own_buf_A; /* If true, buffer A belongs to the driver */ rtems_unsigned8 own_buf_B; /* If true, buffer B belongs to the driver */ rtems_unsigned8 txEmpty; /* If true, the output FIFO should be empty */} CD2401_Channel_Info[4];/* * The number of channels already opened. If zero, enable the interrupts. The * initial value must be 0. If initialized explicitly, the variable ends up * in the .data section. Its value is not re-initialized on system restart. * Furthermore, because the variable is changed, the .data section would not * be ROMable. We thus leave the variable uninitialized, which causes it to * be allocated in the .bss section, and rely on RTEMS to zero the .bss * section on every startup. */rtems_unsigned8 Init_count;/* Record previous handlers */rtems_isr_entry Prev_re_isr; /* Previous rx exception isr */rtems_isr_entry Prev_rx_isr; /* Previous rx isr */rtems_isr_entry Prev_tx_isr; /* Previous tx isr */rtems_isr_entry Prev_modem_isr; /* Previous modem/timer isr *//* Define the following symbol to trace the calls to this driver *//* #define CD2401_RECORD_DEBUG_INFO */#include "console-recording.c"/* * Utility functions. *//* * Assumes that clock ticks 1 million times per second. * * MAXIMUM DELAY IS ABOUT 20 ms * * Input parameters: * delay: Number of microseconds to delay. * * Output parameters: NONE * * Return values: NONE */ void cd2401_udelay( unsigned long delay){ unsigned long i = 20000; /* In case clock is off */ rtems_interval ticks_per_second, start_ticks, end_ticks, current_ticks; rtems_clock_get( RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticks_per_second ); rtems_clock_get( RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &start_ticks ); end_ticks = start_ticks + delay; do { rtems_clock_get(RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, ¤t_ticks); } while ( --i && (current_ticks <= end_ticks) ); CD2401_RECORD_DELAY_INFO(( start_ticks, end_ticks, current_ticks, i )); }/* * cd2401_chan_cmd * * Sends a CCR command to the specified channel. Waits for any unfinished * previous command to complete, then sends the specified command. Optionally * wait for the current command to finish before returning. * * Input parameters: * channel - CD2401 channel number * cmd - command byte * wait - if non-zero, wait for specified command to complete before * returning. * * Output parameters: NONE * * Return values: NONE */void cd2401_chan_cmd( rtems_unsigned8 channel, rtems_unsigned8 cmd, rtems_unsigned8 wait){ if ( channel < 4 ) { cd2401->car = channel; /* Select channel */ while ( cd2401->ccr != 0 ); /* Wait for completion of previous command */ cd2401->ccr = cmd; /* Send command */ if ( wait ) while( cd2401->ccr != 0 );/* Wait for completion */ } else { /* This may not be the best error message */ rtems_fatal_error_occurred( RTEMS_INVALID_NUMBER ); }}/* * cd2401_bitrate_divisor * * Compute the divisor and clock source to use to obtain the desired bitrate. * * Input parameters: * clkrate - system clock rate (CLK input frequency) * bitrate - the desired bitrate * * Output parameters: * bitrate - The actual bitrate achievable, to the nearest bps. * * Return values: * Returns divisor in lower byte and clock source in upper byte for the * specified bitrate. */rtems_unsigned16 cd2401_bitrate_divisor( rtems_unsigned32 clkrate, rtems_unsigned32* bitrate){ rtems_unsigned32 divisor; rtems_unsigned16 clksource; divisor = *bitrate << 3; /* temporary; multiply by 8 for CLK/8 */ divisor = (clkrate + (divisor>>1)) / divisor; /* divisor for clk0 (CLK/8) */ /* Use highest speed clock source for best precision - try clk0 to clk4 */ for( clksource = 0; clksource < 0x0400 && divisor > 0x100; clksource += 0x0100 ) divisor >>= 2; divisor--; /* adjustment, see specs */ if( divisor < 1 ) divisor = 1; else if( divisor > 0xFF ) divisor = 0xFF; *bitrate = clkrate / (1 << ((clksource >> 7)+3)) / (divisor+1); return( clksource | divisor );}/* * cd2401_initialize * * Initializes the CD2401 device. Individual channels on the chip are left in * their default reset state, and should be subsequently configured. * * Input parameters: NONE * * Output parameters: NONE * * Return values: NONE */void cd2401_initialize( void ){ int i; for ( i = 3; i >= 0; i-- ) { CD2401_Channel_Info[i].tty = NULL; CD2401_Channel_Info[i].len = 0; CD2401_Channel_Info[i].buf = NULL; CD2401_Channel_Info[i].spur_cnt = 0; CD2401_Channel_Info[i].spur_dev = 0; CD2401_Channel_Info[i].buserr_type = 0; CD2401_Channel_Info[i].buserr_addr = 0; CD2401_Channel_Info[i].own_buf_A = TRUE; CD2401_Channel_Info[i].own_buf_B = TRUE; CD2401_Channel_Info[i].txEmpty = TRUE; } /* * Normally, do a device reset here. If we do it, we will most likely clober * the port settings for 167Bug on channel 0. So we just shut up all the * ports by disabling their interrupts. */#if 0 cd2401->gfrcr = 0; /* So we can detect that device init is done */ cd2401_chan_cmd( 0x10, 0); /* Reset all */ while(cd2401->gfrcr == 0); /* Wait for reset all */#endif /* * The CL-CD2400/2401 manual (part no 542400-003) states on page 87 that * the LICR "contains the number of the interrupting channel being served. * The channel number is always that of the current acknowledged interrupt." * THE USER MUST PROGRAM CHANNEL NUMBER IN LICR! It is not set automatically * by the hardware, as suggested by the manual. * * The updated manual (part no 542400-007) has the story straight. The * CD2401 automatically initializes the LICR to contain the channel number * in bits 2 and 3. However, these bits are not preserved when the user * defined bits are written. * * The same vector number is used for all four channels. Different vector * numbers could be programmed for each channel, thus avoiding the need to * demultiplex the interrupts in the ISR. */ for ( i = 0; i < 4; i++ ) { cd2401->car = i; /* Select channel */ cd2401->livr = 0x5C; /* Motorola suggested value p. 3-15 */ cd2401->licr = i << 2; /* Don't rely on reset value */ cd2401->ier = 0; /* Disable all interrupts */ } /* * The content of the CD2401 xpilr registers must match the A7-A0 addresses * generated by the PCCchip2 during interrupt acknowledge cycles in order * for the CD2401 to recognize the IACK cycle and clear its interrupt * request. */ cd2401->mpilr = 0x01; /* Match pccchip2->modem_piack p. 3-27 */ cd2401->tpilr = 0x02; /* Match pccchip2->tx_piack p. 3-28 */ cd2401->rpilr = 0x03; /* Match pccchip2->rx_piack p. 3-29 */ /* Global CD2401 registers */ cd2401->dmr = 0; /* 16-bit DMA transfers when possible */ cd2401->bercnt = 0; /* Do not retry DMA upon bus errors */ /* * Setup timer prescaler period, which clocks timers 1 and 2 (or rx timeout * and tx delay). The prescaler is clocked by the system clock) / 2048. The * register must be in the range 0x0A..0xFF, ie. a rescaler period range of * about 1ms..26ms for a nominal system clock rate of 20MHz. */ cd2401->tpr = 0x0A; /* Same value as 167Bug */}/* * cd2401_interrupts_initialize * * This routine enables or disables the CD2401 interrupts to the MC68040. * Interrupts cannot be enabled/disabled on a per-channel basis. * * Input parameters: * enable - if true, enable the interrupts, else disable them. * * Output parameters: NONE * * Return values: NONE * * THE FIRST CD2401 CHANNEL OPENED SHOULD ENABLE INTERRUPTS. * THE LAST CD2401 CHANNEL CLOSED SHOULD DISABLE INTERRUPTS. */void cd2401_interrupts_initialize( rtems_boolean enable){ if ( enable ) { /* * Enable interrupts from the CD2401 in the PCCchip2. * During DMA transfers, the MC68040 supplies dirty data during read cycles * from the CD2401 and leaves the data dirty in its data cache if there is * a cache hit. The MC68040 updates the data cache during write cycles from * the CD2401 if there is a cache hit.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -