/* This file contains the termios TTY driver for the Motorola MC68360 SCC ports. * * COPYRIGHT (c) 1989-1999. * On-Line Applications Research Corporation (OAR). * * 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. * * $Id$ */ #include #include #include #include #include #include #include #include #include "m68360.h" #include #include #include #include #define MC68360_LENGHT_SIZE 100 int mc68360_length_array[ MC68360_LENGHT_SIZE ]; int mc68360_length_count=0; void mc68360_Show_length_array() { int i; for (i=0; ipSCCR->scce ); printk( " 0x%04x\n", ptr->pSCCR->scce ); } #define TX_BUFFER_ADDRESS( _ptr ) \ ((char *)ptr->txBuf - (char *)ptr->chip->board_data->baseaddr) #define RX_BUFFER_ADDRESS( _ptr ) \ ((char *)ptr->rxBuf - (char *)ptr->chip->board_data->baseaddr) /************************************************************************** * Function: mc68360_sccBRGC * ************************************************************************** * Description: * * * * This function is called to compute the divisor register values for * * a given baud rate. * * * * * * Inputs: * * * * int baud - Baud rate (in bps). * * * * Output: * * * * int - baud rate generator configuration. * * * **************************************************************************/ static int mc68360_sccBRGC(int baud, int m360_clock_rate) { int data; /* * configure baud rate generator for 16x bit rate, where..... * b = desired baud rate * clk = system clock (33mhz) * d = clock dividor value * * for b > 300 : d = clk/(b*16) * for b<= 300 : d = (clk/ (b*16*16))-1) */ SYNC(); if( baud > 300 ) data = 33333333 / (baud * 16 ); else data = (33333333 / (baud * 16 * 16) ) - 1; data *= 2; data &= 0x00001ffe ; /* really data = 0x010000 | data | ((baud>300)? 0 : 1 ) ; */ data |= ((baud>300)? 0 : 1 ) ; data |= 0x010000 ; return data; } /************************************************************************** * Function: sccInterruptHandler * ************************************************************************** * Description: * * * * This is the interrupt service routine for the console UART. It * * handles both receive and transmit interrupts. The bulk of the * * work is done by termios. * * * * Inputs: * * * * chip - structure of chip specific information * * * * Output: * * * * none * * * **************************************************************************/ void mc68360_sccInterruptHandler( rtems_irq_hdl_param handle ) { volatile m360_t *m360; int port; uint16_t status; uint16_t length; int i; char data; int clear_isr; M68360_t chip = (M68360_t)handle; for (port=0; port<4; port++) { clear_isr = FALSE; m360 = chip->m360; /* * Handle a RX interrupt. */ if ( scc_read16("scce", &chip->port[port].pSCCR->scce) & 0x1) { clear_isr = TRUE; scc_write16("scce", &chip->port[port].pSCCR->scce, 0x1 ); status =scc_read16( "sccRxBd->status", &chip->port[port].sccRxBd->status); while ((status & M360_BD_EMPTY) == 0) { length= scc_read16("sccRxBd->length",&chip->port[port].sccRxBd->length); for (i=0;iport[port].rxBuf[i]; rtems_termios_enqueue_raw_characters( Console_Port_Data[ chip->port[port].minor ].termios_data, &data, 1); } scc_write16( "sccRxBd->status", &chip->port[port].sccRxBd->status, M360_BD_EMPTY | M360_BD_WRAP | M360_BD_INTERRUPT ); status =scc_read16( "sccRxBd->status", &chip->port[port].sccRxBd->status); } } /* * Handle a TX interrupt. */ if (scc_read16("scce", &chip->port[port].pSCCR->scce) & 0x2) { clear_isr = TRUE; scc_write16("scce", &chip->port[port].pSCCR->scce, 0x2); status = scc_read16("sccTxBd->status", &chip->port[port].sccTxBd->status); if ((status & M360_BD_EMPTY) == 0) { scc_write16("sccTxBd->status",&chip->port[port].sccTxBd->status,0); rtems_termios_dequeue_characters( Console_Port_Data[chip->port[port].minor].termios_data, chip->port[port].sccTxBd->length); } } /* * Clear SCC interrupt-in-service bit. */ if ( clear_isr ) scc_write32( "cisr", &m360->cisr, (0x80000000 >> chip->port[port].channel) ); } } /* * mc68360_scc_open * * This function opens a port for communication. * * Default state is 9600 baud, 8 bits, No parity, and 1 stop bit. */ int mc68360_scc_open( int major, int minor, void * arg ) { return RTEMS_SUCCESSFUL; } /* * mc68360_scc_initialize_interrupts * * This routine initializes the console's receive and transmit * ring buffers and loads the appropriate vectors to handle the interrupts. */ void mc68360_scc_initialize_interrupts(int minor) { M68360_serial_ports_t ptr; volatile m360_t *m360; uint32_t data; uint32_t buffers_start; uint32_t tmp_u32; #ifdef DEBUG_360 printk("mc68360_scc_initialize_interrupts: minor %d\n", minor ); printk("Console_Port_Tbl[minor].pDeviceParams 0x%08x\n", Console_Port_Tbl[minor].pDeviceParams ); #endif ptr = Console_Port_Tbl[minor].pDeviceParams; m360 = ptr->chip->m360; #ifdef DEBUG_360 printk("m360 0x%08x baseaddr 0x%08x\n", m360, ptr->chip->board_data->baseaddr); #endif buffers_start = ptr->chip->board_data->baseaddr + 0x00200000 + ( (M68360_RX_BUF_SIZE + M68360_TX_BUF_SIZE) * (ptr->channel-1)); ptr->rxBuf = (uint8_t *) buffers_start; ptr->txBuf = (uint8_t *)(buffers_start + M68360_RX_BUF_SIZE); #ifdef DEBUG_360 printk("rxBuf 0x%08x txBuf 0x%08x\n", ptr->rxBuf, ptr->txBuf ); #endif /* * Set Channel Drive Enable bits in EPLD */ data = PMCQ1_Read_EPLD(ptr->chip->board_data->baseaddr, PMCQ1_DRIVER_ENABLE ); SYNC(); data |= (PMCQ1_DRIVER_ENABLE_3 | PMCQ1_DRIVER_ENABLE_2 | PMCQ1_DRIVER_ENABLE_1 | PMCQ1_DRIVER_ENABLE_0); PMCQ1_Write_EPLD(ptr->chip->board_data->baseaddr, PMCQ1_DRIVER_ENABLE, data); data = PMCQ1_Read_EPLD(ptr->chip->board_data->baseaddr, PMCQ1_DRIVER_ENABLE ); SYNC(); /* * Disable the receiver and the transmitter. */ SYNC(); tmp_u32 = scc_read32( "gsmr_l", &ptr->pSCCR->gsmr_l ); tmp_u32 &= (~(M360_GSMR_ENR | M360_GSMR_ENT ) ) ; scc_write32( "gsmr_l", &ptr->pSCCR->gsmr_l, tmp_u32 ); /* * Disable Interrupt Error and Interrupt Breakpoint * Set SAID to 4 XXX - Shouldn't it be 7 for slave mode * Set SAISM to 7 */ SYNC(); scc_write16( "sdcr", &m360->sdcr, 0x0740 ); /* * Clear status -- reserved interrupt, SDMA channel error, SDMA breakpoint */ scc_write8( "sdsr", &m360->sdsr, 0x07 ); SYNC(); /* * Initialize timer information in RISC Controller Configuration Register */ scc_write16( "rccr", &m360->rccr, 0x8100 ); SYNC(); /* * XXX */ scc_write16( "papar", &m360->papar, 0xffff ); scc_write16( "padir", &m360->padir, 0x5500 ); /* From Memo */ scc_write16( "paodr", &m360->paodr, 0x0000 ); SYNC(); /* * XXX */ scc_write32( "pbpar", &m360->pbpar, 0x00000000 ); scc_write32( "pbdir", &m360->pbdir, 0x0003ffff ); scc_write32( "pbdat", &m360->pbdat, 0x0000003f ); SYNC(); /* * XXX */ scc_write16( "pcpar", &m360->pcpar, 0x0000 ); scc_write16( "pcdir", &m360->pcdir, 0x0000 ); scc_write16( "pcso", &m360->pcso, 0x0000 ); SYNC(); /* * configure baud rate generator for 16x bit rate, where..... * b = desired baud rate * clk = system clock (33mhz) * d = clock dividor value * * for b > 300 : d = clk/(b*16) * for b<= 300 : d = (clk/ (b*16*16))-1) */ SYNC(); if( ptr->baud > 300 ) data = 33333333 / (ptr->baud * 16 ); else data = (33333333 / (ptr->baud * 16 * 16) ) - 1; data *= 2 ; data &= 0x00001ffe ; /* really data = 0x010000 | data | ((baud>300)? 0 : 1 ) ; */ data |= ((ptr->baud>300)? 0 : 1 ) ; data |= 0x010000 ; scc_write32( "pBRGC", ptr->pBRGC, data ); data = (((ptr->channel-1)*8) | (ptr->channel-1)) ; data = data << ((ptr->channel-1)*8) ; data |= scc_read32( "sicr", &m360->sicr ); scc_write32( "sicr", &m360->sicr, data ); /* * initialise SCC parameter ram */ SYNC(); scc_write16( "pSCCB->rbase", &ptr->pSCCB->rbase, (char *)(ptr->sccRxBd) - (char *)m360 ); scc_write16( "pSCCB->tbase", &ptr->pSCCB->tbase, (char *)(ptr->sccTxBd) - (char *)m360 ); scc_write8( "pSCCB->rfcr", &ptr->pSCCB->rfcr, 0x15 ); /* 0x15 0x18 */ scc_write8( "pSCCB->tfcr", &ptr->pSCCB->tfcr, 0x15 ); /* 0x15 0x18 */ scc_write16( "pSCCB->mrblr", &ptr->pSCCB->mrblr, M68360_RX_BUF_SIZE ); /* * initialise tx and rx scc parameters */ SYNC(); data = M360_CR_INIT_TX_RX_PARAMS | 0x01; data |= (M360_CR_CH_NUM * (ptr->channel-1) ); scc_write16( "CR", &m360->cr, data ); /* * initialise uart specific parameter RAM */ SYNC(); scc_write16( "pSCCB->un.uart.max_idl", &ptr->pSCCB->un.uart.max_idl, 15000 ); scc_write16( "pSCCB->un.uart.brkcr", &ptr->pSCCB->un.uart.brkcr, 0x0001 ); scc_write16( "pSCCB->un.uart.parec", &ptr->pSCCB->un.uart.parec, 0x0000 ); scc_write16( "pSCCB->un,uart.frmec", &ptr->pSCCB->un.uart.frmec, 0x0000 ); scc_write16( "pSCCB->un.uart.nosec", &ptr->pSCCB->un.uart.nosec, 0x0000 ); scc_write16( "pSCCB->un.uart.brkec", &ptr->pSCCB->un.uart.brkec, 0x0000 ); scc_write16( "pSCCB->un.uart.uaddr0", &ptr->pSCCB->un.uart.uaddr[0], 0x0000 ); scc_write16( "pSCCB->un.uart.uaddr1", &ptr->pSCCB->un.uart.uaddr[1], 0x0000 ); scc_write16( "pSCCB->un.uart.toseq", &ptr->pSCCB->un.uart.toseq, 0x0000 ); scc_write16( "pSCCB->un.uart.char0", &ptr->pSCCB->un.uart.character[0], 0x0039 ); scc_write16( "pSCCB->un.uart.char1", &ptr->pSCCB->un.uart.character[1], 0x8000 ); scc_write16( "pSCCB->un.uart.char2", &ptr->pSCCB->un.uart.character[2], 0x8000 ); scc_write16( "pSCCB->un.uart.char3", &ptr->pSCCB->un.uart.character[3], 0x8000 ); scc_write16( "pSCCB->un.uart.char4", &ptr->pSCCB->un.uart.character[4], 0x8000 ); scc_write16( "pSCCB->un.uart.char5", &ptr->pSCCB->un.uart.character[5], 0x8000 ); scc_write16( "pSCCB->un.uart.char6", &ptr->pSCCB->un.uart.character[6], 0x8000 ); scc_write16( "pSCCB->un.uart.char7", &ptr->pSCCB->un.uart.character[7], 0x8000 ); scc_write16( "pSCCB->un.uart.rccm", &ptr->pSCCB->un.uart.rccm, 0xc0ff ); /* * setup buffer descriptor stuff */ SYNC(); scc_write16( "sccRxBd->status", &ptr->sccRxBd->status, 0x0000 ); SYNC(); scc_write16( "sccRxBd->length", &ptr->sccRxBd->length, 0x0000 ); scc_write16( "sccRxBd->status", &ptr->sccRxBd->status, M360_BD_EMPTY | M360_BD_WRAP | M360_BD_INTERRUPT ); /* XXX Radstone Example writes RX buffer ptr as two u16's */ scc_write32( "sccRxBd->buffer", &ptr->sccRxBd->buffer, RX_BUFFER_ADDRESS( ptr ) ); SYNC(); scc_write16( "sccTxBd->status", &ptr->sccTxBd->status, 0x0000 ); SYNC(); scc_write16( "sccTxBd->length", &ptr->sccTxBd->length, 0x0000 ); /* XXX Radstone Example writes TX buffer ptr as two u16's */ scc_write32( "sccTxBd->buffer", &ptr->sccTxBd->buffer, TX_BUFFER_ADDRESS( ptr ) ); /* * clear previous events and set interrupt priorities */ scc_write16( "pSCCR->scce", &ptr->pSCCR->scce, 0x1bef ); /* From memo */ SYNC(); SYNC(); scc_write32( "cicr", &m360->cicr, 0x001b9f40 ); SYNC(); /* scc_write32( "cicr", &m360->cicr, scc_read32( "cicr", &m360->cicr ) ); */ scc_write16( "pSCCR->sccm", &ptr->pSCCR->sccm, M360_SCCE_TX | M360_SCCE_RX ); data = scc_read32("cimr", &m360->cimr); data |= (0x80000000 >> ptr->channel); scc_write32( "cimr", &m360->cimr, data ); SYNC(); scc_write32( "cipr", &m360->cipr, scc_read32( "cipr", &m360->cipr ) ); scc_write32( "pSCCR->gsmr_h", &ptr->pSCCR->gsmr_h, M360_GSMR_RFW ); scc_write32( "pSCCR->gsmr_l", &ptr->pSCCR->gsmr_l, (M360_GSMR_TDCR_16X | M360_GSMR_RDCR_16X | M360_GSMR_MODE_UART) ); scc_write16( "pSCCR->dsr", &ptr->pSCCR->dsr, 0x7e7e ); SYNC(); scc_write16( "pSCCR->psmr", &ptr->pSCCR->psmr, (M360_PSMR_CL8 | M360_PSMR_UM_NORMAL | M360_PSMR_TPM_ODD) ); SYNC(); /* * Enable the receiver and the transmitter. */ SYNC(); data = scc_read32( "pSCCR->gsmr_l", &ptr->pSCCR->gsmr_l); scc_write32( "pSCCR->gsmr_l", &ptr->pSCCR->gsmr_l, (data | M360_GSMR_ENR | M360_GSMR_ENT) ); data = PMCQ1_Read_EPLD(ptr->chip->board_data->baseaddr, PMCQ1_INT_MASK ); data &= (~PMCQ1_INT_MASK_QUICC); PMCQ1_Write_EPLD(ptr->chip->board_data->baseaddr, PMCQ1_INT_MASK, data ); data = PMCQ1_Read_EPLD(ptr->chip->board_data->baseaddr, PMCQ1_INT_STATUS ); data &= (~PMCQ1_INT_STATUS_QUICC); PMCQ1_Write_EPLD(ptr->chip->board_data->baseaddr, PMCQ1_INT_STATUS, data ); } /* * mc68360_scc_write_support_int * * Console Termios output entry point when using interrupt driven output. */ int mc68360_scc_write_support_int( int minor, const char *buf, int len ) { rtems_interrupt_level Irql; M68360_serial_ports_t ptr; mc68360_length_array[ mc68360_length_count ] = len; mc68360_length_count++; if ( mc68360_length_count >= MC68360_LENGHT_SIZE ) mc68360_length_count=0; ptr = Console_Port_Tbl[minor].pDeviceParams; /* * We are using interrupt driven output and termios only sends us * one character at a time. */ if ( !len ) return 0; /* * */ #ifdef DEBUG_360 printk("mc68360_scc_write_support_int: char 0x%x length %d\n", (unsigned int)*buf, len ); #endif /* * We must copy the data from the global memory space to MC68360 space */ rtems_interrupt_disable(Irql); scc_write16( "sccTxBd->status", &ptr->sccTxBd->status, 0 ); memcpy((void *) ptr->txBuf, buf, len); scc_write32( "sccTxBd->buffer", &ptr->sccTxBd->buffer, TX_BUFFER_ADDRESS(ptr->txBuf) ); scc_write16( "sccTxBd->length", &ptr->sccTxBd->length, len ); scc_write16( "sccTxBd->status", &ptr->sccTxBd->status, (M360_BD_READY | M360_BD_WRAP | M360_BD_INTERRUPT) ); rtems_interrupt_enable(Irql); return len; } /* * mc68360_scc_write_polled * * This routine polls out the requested character. */ void mc68360_scc_write_polled( int minor, char cChar ) { #ifdef DEBUG_360 printk("mc68360_scc_write_polled: %c\n", cChar); #endif } /* * mc68681_set_attributes * * This function sets the DUART channel to reflect the requested termios * port settings. */ int mc68360_scc_set_attributes( int minor, const struct termios *t ) { int baud; volatile m360_t *m360; M68360_serial_ports_t ptr; ptr = Console_Port_Tbl[minor].pDeviceParams; m360 = ptr->chip->m360; switch (t->c_cflag & CBAUD) { case B50: baud = 50; break; case B75: baud = 75; break; case B110: baud = 110; break; case B134: baud = 134; break; case B150: baud = 150; break; case B200: baud = 200; break; case B300: baud = 300; break; case B600: baud = 600; break; case B1200: baud = 1200; break; case B1800: baud = 1800; break; case B2400: baud = 2400; break; case B4800: baud = 4800; break; case B9600: baud = 9600; break; case B19200: baud = 19200; break; case B38400: baud = 38400; break; case B57600: baud = 57600; break; case B115200: baud = 115200; break; case B230400: baud = 230400; break; case B460800: baud = 460800; break; default: baud = -1; break; } if (baud > 0) { scc_write32( "pBRGC", ptr->pBRGC, mc68360_sccBRGC(baud, ptr->chip->m360_clock_rate) ); } return 0; } /* * mc68360_scc_close * * This function shuts down the requested port. */ int mc68360_scc_close( int major, int minor, void *arg ) { return(RTEMS_SUCCESSFUL); } /* * mc68360_scc_inbyte_nonblocking_polled * * Console Termios polling input entry point. */ int mc68360_scc_inbyte_nonblocking_polled( int minor ) { return -1; } /* * mc68360_scc_write_support_polled * * Console Termios output entry point when using polled output. * */ int mc68360_scc_write_support_polled( int minor, const char *buf, int len ) { printk("mc68360_scc_write_support_polled: minor %d char %c len %d\n", minor, buf, len ); return 0; } /* * mc68360_scc_init * * This function initializes the DUART to a quiecsent state. */ void mc68360_scc_init(int minor) { #ifdef DEBUG_360 printk("mc68360_scc_init\n"); #endif } int mc68360_scc_create_chip( PPMCQ1BoardData BoardData, uint8_t int_vector ) { M68360_t chip; int i; #ifdef DEBUG_360 printk("mc68360_scc_create_chip\n"); #endif /* * Create console structure for this card * XXX - Note Does this need to be moved up to if a QUICC is fitted * section? */ if ((chip = malloc(sizeof(struct _m68360_per_chip))) == NULL) { printk("Error Unable to allocate memory for _m68360_per_chip\n"); return RTEMS_IO_ERROR; } chip->next = M68360_chips; chip->m360 = (void *)BoardData->baseaddr; chip->m360_interrupt = int_vector; chip->m360_clock_rate = 25000000; chip->board_data = BoardData; M68360_chips = chip; for (i=1; i<=4; i++) { chip->port[i-1].channel = i; chip->port[i-1].chip = chip; chip->port[i-1].baud = 9600; switch( i ) { case 1: chip->port[i-1].pBRGC = &chip->m360->brgc1; chip->port[i-1].pSCCB = (m360SCCparms_t *) &chip->m360->scc1p; chip->port[i-1].pSCCR = &chip->m360->scc1; M360SetupMemory( chip ); /* Do this first time through */ break; case 2: chip->port[i-1].pBRGC = &chip->m360->brgc2; chip->port[i-1].pSCCB = &chip->m360->scc2p; chip->port[i-1].pSCCR = &chip->m360->scc2; break; case 3: chip->port[i-1].pBRGC = &chip->m360->brgc3; chip->port[i-1].pSCCB = &chip->m360->scc3p; chip->port[i-1].pSCCR = &chip->m360->scc3; break; case 4: chip->port[i-1].pBRGC = &chip->m360->brgc4; chip->port[i-1].pSCCB = &chip->m360->scc4p; chip->port[i-1].pSCCR = &chip->m360->scc4; break; default: printk("Invalid mc68360 channel %d\n", i); return RTEMS_IO_ERROR; } /* * Allocate buffer descriptors. */ chip->port[i-1].sccRxBd = M360AllocateBufferDescriptors(chip, 1); chip->port[i-1].sccTxBd = M360AllocateBufferDescriptors(chip, 1); } rsPMCQ1QuiccIntConnect( chip->board_data->busNo, chip->board_data->slotNo, chip->board_data->funcNo, &mc68360_sccInterruptHandler, chip ); return RTEMS_SUCCESSFUL; } console_fns mc68360_scc_fns = { libchip_serial_default_probe, /* deviceProbe */ mc68360_scc_open, /* deviceFirstOpen */ NULL, /* deviceLastClose */ NULL, /* deviceRead */ mc68360_scc_write_support_int, /* deviceWrite */ mc68360_scc_initialize_interrupts, /* deviceInitialize */ mc68360_scc_write_polled, /* deviceWritePolled */ mc68360_scc_set_attributes, /* deviceSetAttributes */ TRUE /* deviceOutputUsesInterrupts */ }; console_fns mc68360_scc_polled = { libchip_serial_default_probe, /* deviceProbe */ mc68360_scc_open, /* deviceFirstOpen */ mc68360_scc_close, /* deviceLastClose */ mc68360_scc_inbyte_nonblocking_polled, /* deviceRead */ mc68360_scc_write_support_polled, /* deviceWrite */ mc68360_scc_init, /* deviceInitialize */ mc68360_scc_write_polled, /* deviceWritePolled */ mc68360_scc_set_attributes, /* deviceSetAttributes */ FALSE /* deviceOutputUsesInterrupts */ };