Mercurial > ecos
diff packages/devs/serial/powerpc/quicc/current/src/quicc_smc_serial.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 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/devs/serial/powerpc/quicc/current/src/quicc_smc_serial.c @@ -0,0 +1,743 @@ +//========================================================================== +// +// io/serial/powerpc/quicc_smc_serial.c +// +// PowerPC QUICC (SMC) Serial I/O Interface Module (interrupt driven) +// +//========================================================================== +//####COPYRIGHTBEGIN#### +// +// ------------------------------------------- +// The contents of this file are subject to the Red Hat eCos Public License +// Version 1.1 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://www.redhat.com/ +// +// Software distributed under the License is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +// License for the specific language governing rights and limitations under +// the License. +// +// The Original Code is eCos - Embedded Configurable Operating System, +// released September 30, 1998. +// +// The Initial Developer of the Original Code is Red Hat. +// Portions created by Red Hat are +// Copyright (C) 1998, 1999, 2000 Red Hat, Inc. +// All Rights Reserved. +// ------------------------------------------- +// +//####COPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): gthomas +// Contributors: gthomas +// Date: 1999-06-20 +// Purpose: QUICC SMC Serial I/O module (interrupt driven version) +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <pkgconf/system.h> +#include <pkgconf/io_serial.h> +#include <pkgconf/io.h> +#include <cyg/io/io.h> +#include <cyg/hal/hal_intr.h> +#include <cyg/io/devtab.h> +#include <cyg/io/serial.h> +#include <cyg/infra/diag.h> +#include <cyg/hal/hal_cache.h> +#include CYGBLD_HAL_PLATFORM_H + +#ifdef CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC + +// Buffer descriptor control bits +#define QUICC_BD_CTL_Ready 0x8000 // Buffer contains data (tx) or is empty (rx) +#define QUICC_BD_CTL_Wrap 0x2000 // Last buffer in list +#define QUICC_BD_CTL_Int 0x1000 // Generate interrupt when empty (tx) or full (rx) +#define QUICC_BD_CTL_MASK 0xB000 // User settable bits + +// SMC Mode Register +#define QUICC_SMCMR_CLEN(n) ((n+1)<<11) // Character length +#define QUICC_SMCMR_SB(n) ((n-1)<<10) // Stop bits (1 or 2) +#define QUICC_SMCMR_PE(n) (n<<9) // Parity enable (0=disable, 1=enable) +#define QUICC_SMCMR_PM(n) (n<<8) // Parity mode (0=odd, 1=even) +#define QUICC_SMCMR_UART (2<<4) // UART mode +#define QUICC_SMCMR_TEN (1<<1) // Enable transmitter +#define QUICC_SMCMR_REN (1<<0) // Enable receiver + +// SMC Events (interrupts) +#define QUICC_SMCE_BRK 0x10 // Break received +#define QUICC_SMCE_BSY 0x04 // Busy - receive buffer overrun +#define QUICC_SMCE_TX 0x02 // Tx interrupt +#define QUICC_SMCE_RX 0x01 // Rx interrupt + +// SMC Commands +#define QUICC_SMC_CMD_InitTxRx (0<<8) +#define QUICC_SMC_CMD_InitTx (1<<8) +#define QUICC_SMC_CMD_InitRx (2<<8) +#define QUICC_SMC_CMD_StopTx (4<<8) +#define QUICC_SMC_CMD_RestartTx (6<<8) +#define QUICC_SMC_CMD_Reset 0x8000 +#define QUICC_SMC_CMD_Go 0x0001 + +#include "quicc_smc_serial.h" + +typedef struct quicc_smc_serial_info { + CYG_ADDRWORD channel; // Which channel SMC1/SMC2 + CYG_WORD int_num; // Interrupt number + cyg_uint32 *brg; // Which baud rate generator + volatile struct smc_uart_pram *pram; // Parameter RAM pointer + volatile struct smc_regs *ctl; // SMC control registers + volatile struct cp_bufdesc *txbd, *rxbd; // Next Tx,Rx descriptor to use + struct cp_bufdesc *tbase, *rbase; // First Tx,Rx descriptor + int txsize, rxsize; // Length of individual buffers + cyg_interrupt serial_interrupt; + cyg_handle_t serial_interrupt_handle; + bool tx_enabled; +} quicc_smc_serial_info; + +static bool quicc_smc_serial_init(struct cyg_devtab_entry *tab); +static bool quicc_smc_serial_putc(serial_channel *chan, unsigned char c); +static Cyg_ErrNo quicc_smc_serial_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name); +static unsigned char quicc_smc_serial_getc(serial_channel *chan); +static bool quicc_smc_serial_set_config(serial_channel *chan, cyg_serial_info_t *config); +static void quicc_smc_serial_start_xmit(serial_channel *chan); +static void quicc_smc_serial_stop_xmit(serial_channel *chan); + +static cyg_uint32 quicc_smc_serial_ISR(cyg_vector_t vector, cyg_addrword_t data); +static void quicc_smc_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data); + +static SERIAL_FUNS(quicc_smc_serial_funs, + quicc_smc_serial_putc, + quicc_smc_serial_getc, + quicc_smc_serial_set_config, + quicc_smc_serial_start_xmit, + quicc_smc_serial_stop_xmit + ); + +#ifdef CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC_SMC1 +static quicc_smc_serial_info quicc_smc_serial_info1 = { + 0x90, // Channel indicator + CYGNUM_HAL_INTERRUPT_CPM_SMC1 // interrupt +}; +#if CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_BUFSIZE > 0 +static unsigned char quicc_smc_serial_out_buf1[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_BUFSIZE]; +static unsigned char quicc_smc_serial_in_buf1[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_BUFSIZE]; + +static SERIAL_CHANNEL_USING_INTERRUPTS(quicc_smc_serial_channel1, + quicc_smc_serial_funs, + quicc_smc_serial_info1, + CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_BAUD), + CYG_SERIAL_STOP_DEFAULT, + CYG_SERIAL_PARITY_DEFAULT, + CYG_SERIAL_WORD_LENGTH_DEFAULT, + CYG_SERIAL_FLAGS_DEFAULT, + &quicc_smc_serial_out_buf1[0], sizeof(quicc_smc_serial_out_buf1), + &quicc_smc_serial_in_buf1[0], sizeof(quicc_smc_serial_in_buf1) + ); +#else +static SERIAL_CHANNEL(quicc_smc_serial_channel1, + quicc_smc_serial_funs, + quicc_smc_serial_info1, + CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_BAUD), + CYG_SERIAL_STOP_DEFAULT, + CYG_SERIAL_PARITY_DEFAULT, + CYG_SERIAL_WORD_LENGTH_DEFAULT, + CYG_SERIAL_FLAGS_DEFAULT + ); +#endif + +static unsigned char quicc_smc1_txbuf[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_TxNUM][CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_TxSIZE]; +static unsigned char quicc_smc1_rxbuf[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_RxNUM][CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_RxSIZE]; + +DEVTAB_ENTRY(quicc_smc_serial_io1, + CYGDAT_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_NAME, + 0, // Does not depend on a lower level interface + &cyg_io_serial_devio, + quicc_smc_serial_init, + quicc_smc_serial_lookup, // Serial driver may need initializing + &quicc_smc_serial_channel1 + ); +#endif // CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC_SMC1 + +#ifdef CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC_SMC2 +static quicc_smc_serial_info quicc_smc_serial_info2 = { + 0xD0, // Channel indicator + CYGNUM_HAL_INTERRUPT_CPM_SMC2_PIP // interrupt +}; +#if CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_BUFSIZE > 0 +static unsigned char quicc_smc_serial_out_buf2[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_BUFSIZE]; +static unsigned char quicc_smc_serial_in_buf2[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_BUFSIZE]; + +static SERIAL_CHANNEL_USING_INTERRUPTS(quicc_smc_serial_channel2, + quicc_smc_serial_funs, + quicc_smc_serial_info2, + CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_BAUD), + CYG_SERIAL_STOP_DEFAULT, + CYG_SERIAL_PARITY_DEFAULT, + CYG_SERIAL_WORD_LENGTH_DEFAULT, + CYG_SERIAL_FLAGS_DEFAULT, + &quicc_smc_serial_out_buf2[0], sizeof(quicc_smc_serial_out_buf2), + &quicc_smc_serial_in_buf2[0], sizeof(quicc_smc_serial_in_buf2) + ); +#else +static SERIAL_CHANNEL(quicc_smc_serial_channel2, + quicc_smc_serial_funs, + quicc_smc_serial_info2, + CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_BAUD), + CYG_SERIAL_STOP_DEFAULT, + CYG_SERIAL_PARITY_DEFAULT, + CYG_SERIAL_WORD_LENGTH_DEFAULT, + CYG_SERIAL_FLAGS_DEFAULT + ); +#endif +static unsigned char quicc_smc2_txbuf[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_TxNUM][CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_TxSIZE]; +static unsigned char quicc_smc2_rxbuf[CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_RxNUM][CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_RxSIZE]; + +DEVTAB_ENTRY(quicc_smc_serial_io2, + CYGDAT_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_NAME, + 0, // Does not depend on a lower level interface + &cyg_io_serial_devio, + quicc_smc_serial_init, + quicc_smc_serial_lookup, // Serial driver may need initializing + &quicc_smc_serial_channel2 + ); +#endif // CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC_SMC2 + +#ifdef CYGDBG_DIAG_BUF +extern int enable_diag_uart; +#endif // CYGDBG_DIAG_BUF + +// Internal function to actually configure the hardware to desired baud rate, etc. +static bool +quicc_smc_serial_config_port(serial_channel *chan, cyg_serial_info_t *new_config, bool init) +{ + quicc_smc_serial_info *smc_chan = (quicc_smc_serial_info *)chan->dev_priv; + unsigned int baud_divisor = select_baud[new_config->baud]; + cyg_uint32 _lcr; + EPPC *eppc = eppc_base(); + if (baud_divisor == 0) return false; + // Disable channel during setup + smc_chan->ctl->smc_smcmr = QUICC_SMCMR_UART; // Disabled, UART mode + // Disable port interrupts while changing hardware + _lcr = select_word_length[new_config->word_length - CYGNUM_SERIAL_WORD_LENGTH_5] | + select_stop_bits[new_config->stop] | + select_parity[new_config->parity]; + // Stop transmitter while changing baud rate + eppc->cp_cr = smc_chan->channel | QUICC_SMC_CMD_Go | QUICC_SMC_CMD_StopTx; + // Set baud rate generator + *smc_chan->brg = 0x10000 | (UART_BITRATE(baud_divisor)<<1); +#ifdef XX_CYGDBG_DIAG_BUF + enable_diag_uart = 0; + diag_printf("Set BAUD RATE[%x], %d = %x, tstate = %x\n", smc_chan->brg, baud_divisor, *smc_chan->brg, smc_chan->pram->tstate); + enable_diag_uart = 1; +#endif // CYGDBG_DIAG_BUF + + // Enable channel with new configuration + smc_chan->ctl->smc_smcmr = QUICC_SMCMR_UART|QUICC_SMCMR_TEN|QUICC_SMCMR_REN|_lcr; + eppc->cp_cr = smc_chan->channel | QUICC_SMC_CMD_Go | QUICC_SMC_CMD_RestartTx; + if (new_config != &chan->config) { + chan->config = *new_config; + } + return true; +} + +// Function to set up internal tables for device. +static void +quicc_smc_serial_init_info(quicc_smc_serial_info *smc_chan, + volatile struct smc_uart_pram *uart_pram, + volatile struct smc_regs *ctl, + int TxBD, int TxNUM, int TxSIZE, + cyg_uint8 *TxBUF, + int RxBD, int RxNUM, int RxSIZE, + cyg_uint8 *RxBUF, + int portBmask, + int BRG, int SIpos) +{ + EPPC *eppc = eppc_base(); + struct cp_bufdesc *txbd, *rxbd; + cyg_uint32 simode = 0; + int i; + + // Disable channel during setup + ctl->smc_smcmr = QUICC_SMCMR_UART; // Disabled, UART mode + smc_chan->pram = uart_pram; + smc_chan->ctl = ctl; + /* + * SDMA & LCD bus request level 5 + * (Section 16.10.2.1) + */ + eppc->dma_sdcr = 1; + switch (BRG) { + case 1: + smc_chan->brg = (cyg_uint32 *)&eppc->brgc1; + simode = 0; + break; + case 2: + smc_chan->brg = (cyg_uint32 *)&eppc->brgc2; + simode = 1; + break; + case 3: + smc_chan->brg = (cyg_uint32 *)&eppc->brgc3; + simode = 2; + break; + case 4: + smc_chan->brg = (cyg_uint32 *)&eppc->brgc4; + simode = 3; + break; + } + // NMSI mode, BRGn to SMCm (Section 16.12.5.2) + eppc->si_simode = (eppc->si_simode & ~(0xF<<SIpos)) | (simode<<SIpos); + /* + * Set up the PortB pins for UART operation. + * Set PAR and DIR to allow SMCTXDx and SMRXDx + * (Table 16-39) + */ + eppc->pip_pbpar |= portBmask; + eppc->pip_pbdir &= ~portBmask; + /* + * Reset Rx & Tx params + */ + eppc->cp_cr = smc_chan->channel | QUICC_SMC_CMD_Go | QUICC_SMC_CMD_InitTxRx; + /* + * SDMA & LCD bus request level 5 + * (Section 16.10.2.1) + */ + eppc->dma_sdcr = 1; + /* + * Set Rx and Tx function code + * (Section 16.15.4.2) + */ + uart_pram->rfcr = 0x18; + uart_pram->tfcr = 0x18; + /* + * Set pointers to buffer descriptors. + * (Sections 16.15.4.1, 16.15.7.12, and 16.15.7.13) + */ + uart_pram->rbase = RxBD; + uart_pram->tbase = TxBD; + /* tx and rx buffer descriptors */ + txbd = (struct cp_bufdesc *)((char *)eppc + TxBD); + rxbd = (struct cp_bufdesc *)((char *)eppc + RxBD); + smc_chan->txbd = txbd; + smc_chan->tbase = txbd; + smc_chan->txsize = TxSIZE; + smc_chan->rxbd = rxbd; + smc_chan->rbase = rxbd; + smc_chan->rxsize = RxSIZE; + /* max receive buffer length */ + uart_pram->mrblr = RxSIZE; + /* set max_idle feature - generate interrupt after 4 chars idle period */ + uart_pram->max_idl = 4; + /* no last brk char received */ + uart_pram->brkln = 0; + /* no break condition occurred */ + uart_pram->brkec = 0; + /* 1 break char sent on top XMIT */ + uart_pram->brkcr = 1; + /* setup RX buffer descriptors */ + for (i = 0; i < RxNUM; i++) { + rxbd->length = 0; + rxbd->buffer = RxBUF; + rxbd->ctrl = QUICC_BD_CTL_Ready | QUICC_BD_CTL_Int; + if (i == (RxNUM-1)) rxbd->ctrl |= QUICC_BD_CTL_Wrap; // Last buffer + RxBUF += RxSIZE; + rxbd++; + } + /* setup TX buffer descriptors */ + for (i = 0; i < TxNUM; i++) { + txbd->length = 0; + txbd->buffer = TxBUF; + txbd->ctrl = 0; + if (i == (TxNUM-1)) txbd->ctrl |= QUICC_BD_CTL_Wrap; // Last buffer + TxBUF += TxSIZE; + txbd++; + } + /* + * Clear any previous events. Enable interrupts. + * (Section 16.15.7.14 and 16.15.7.15) + */ + ctl->smc_smce = 0xFF; + ctl->smc_smcm = QUICC_SMCE_BSY|QUICC_SMCE_TX|QUICC_SMCE_RX; +} + +// Function to initialize the device. Called at bootstrap time. +static bool +quicc_smc_serial_init(struct cyg_devtab_entry *tab) +{ + serial_channel *chan = (serial_channel *)tab->priv; + quicc_smc_serial_info *smc_chan = (quicc_smc_serial_info *)chan->dev_priv; + volatile EPPC *eppc = (volatile EPPC *)eppc_base(); + int TxBD, RxBD; + static int first_init = 1; + int cache_state; + HAL_DCACHE_IS_ENABLED(cache_state); + HAL_DCACHE_SYNC(); + HAL_DCACHE_DISABLE(); +#ifdef CYGDBG_IO_INIT + diag_printf("QUICC_SMC SERIAL init - dev: %x.%d\n", smc_chan->channel, smc_chan->int_num); +#endif + if (first_init) { + // Set up tables since many fields are dynamic [computed at runtime] + first_init = 0; +#ifdef CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC_SMC1 + eppc->cp_cr = QUICC_SMC_CMD_Reset | QUICC_SMC_CMD_Go; // Totally reset CP + while (eppc->cp_cr & QUICC_SMC_CMD_Reset) ; + TxBD = 0x2800; // Note: this should be configurable + RxBD = TxBD + CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_TxNUM*8; + quicc_smc_serial_init_info(&quicc_smc_serial_info1, + &eppc->pram[2].scc.pothers.smc_modem.psmc.u, // PRAM + &eppc->smc_regs[0], // Control registers + TxBD, + CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_TxNUM, + CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_TxSIZE, + &quicc_smc1_txbuf[0][0], + RxBD, + CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_RxNUM, + CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_RxSIZE, + &quicc_smc1_rxbuf[0][0], + 0xC0, // PortB mask + CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_BRG, + 12 // SI mask position + ); + TxBD = RxBD + CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_RxNUM*8; +#else +#ifdef CYGPKG_HAL_POWERPC_MBX + // Ensure the SMC1 side is initialized first and use shared mem + // above where it plays: + diag_init(); // (pull in constructor that inits diag channel) + TxBD = 0x2830; // Note: this should be inferred from the chip state +#else + // there is no diag device wanting to use the QUICC, so prepare it + // for SMC2 use only. + eppc->cp_cr = QUICC_SMC_CMD_Reset | QUICC_SMC_CMD_Go; // Totally reset CP + while (eppc->cp_cr & QUICC_SMC_CMD_Reset) ; + TxBD = 0x2800; // Note: this should be configurable +#endif +#endif +#ifdef CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC_SMC2 + RxBD = TxBD + CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_TxNUM*8; + quicc_smc_serial_init_info(&quicc_smc_serial_info2, + &eppc->pram[3].scc.pothers.smc_modem.psmc.u, // PRAM + &eppc->smc_regs[1], // Control registers + TxBD, + CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_TxNUM, + CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_TxSIZE, + &quicc_smc2_txbuf[0][0], + RxBD, + CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_RxNUM, + CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_RxSIZE, + &quicc_smc2_rxbuf[0][0], + 0xC00, // PortB mask + CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_BRG, + 28 // SI mask position + ); +#endif + } + (chan->callbacks->serial_init)(chan); // Really only required for interrupt driven devices + if (chan->out_cbuf.len != 0) { + cyg_drv_interrupt_create(smc_chan->int_num, + CYGARC_SIU_PRIORITY_HIGH, // Priority - unused (but asserted) + (cyg_addrword_t)chan, // Data item passed to interrupt handler + quicc_smc_serial_ISR, + quicc_smc_serial_DSR, + &smc_chan->serial_interrupt_handle, + &smc_chan->serial_interrupt); + cyg_drv_interrupt_attach(smc_chan->serial_interrupt_handle); + cyg_drv_interrupt_mask(smc_chan->int_num); + smc_chan->tx_enabled = false; + } + quicc_smc_serial_config_port(chan, &chan->config, true); + if (cache_state) + HAL_DCACHE_ENABLE(); + return true; +} + +// This routine is called when the device is "looked" up (i.e. attached) +static Cyg_ErrNo +quicc_smc_serial_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name) +{ + serial_channel *chan = (serial_channel *)(*tab)->priv; + (chan->callbacks->serial_init)(chan); // Really only required for interrupt driven devices + return ENOERR; +} + +// Force the current transmit buffer to be sent +static void +quicc_smc_serial_flush(quicc_smc_serial_info *smc_chan) +{ + volatile struct cp_bufdesc *txbd = smc_chan->txbd; + if ((txbd->length > 0) && ((txbd->ctrl & QUICC_BD_CTL_Ready) == 0)) { + txbd->ctrl |= QUICC_BD_CTL_Ready|QUICC_BD_CTL_Int; // Signal buffer ready + if (txbd->ctrl & QUICC_BD_CTL_Wrap) { + txbd = smc_chan->tbase; + } else { + txbd++; + } + smc_chan->txbd = txbd; + } +} + +// Send a character to the device output buffer. +// Return 'true' if character is sent to device +static bool +quicc_smc_serial_putc(serial_channel *chan, unsigned char c) +{ + quicc_smc_serial_info *smc_chan = (quicc_smc_serial_info *)chan->dev_priv; + volatile struct cp_bufdesc *txbd, *txfirst; + EPPC *eppc = eppc_base(); + bool res; + cyg_drv_dsr_lock(); // Avoid race condition testing pointers + txbd = (struct cp_bufdesc *)((char *)eppc + smc_chan->pram->tbptr); + txfirst = txbd; + // Scan for a non-busy buffer + while (txbd->ctrl & QUICC_BD_CTL_Ready) { + // This buffer is busy, move to next one + if (txbd->ctrl & QUICC_BD_CTL_Wrap) { + txbd = smc_chan->tbase; + } else { + txbd++; + } + if (txbd == txfirst) break; // Went all the way around + } + smc_chan->txbd = txbd; + if ((txbd->ctrl & (QUICC_BD_CTL_Ready|QUICC_BD_CTL_Int)) == 0) { + // Transmit buffer is not full/busy + txbd->buffer[txbd->length++] = c; + if (txbd->length == smc_chan->txsize) { + // This buffer is now full, tell SMC to start processing it + quicc_smc_serial_flush(smc_chan); + } + res = true; + } else { + // No space + res = false; + } + cyg_drv_dsr_unlock(); + return res; +} + +// Fetch a character from the device input buffer, waiting if necessary +static unsigned char +quicc_smc_serial_getc(serial_channel *chan) +{ + unsigned char c; + quicc_smc_serial_info *smc_chan = (quicc_smc_serial_info *)chan->dev_priv; + volatile struct cp_bufdesc *rxbd = smc_chan->rxbd; + while ((rxbd->ctrl & QUICC_BD_CTL_Ready) != 0) ; + c = rxbd->buffer[0]; + rxbd->length = smc_chan->rxsize; + rxbd->ctrl |= QUICC_BD_CTL_Ready; + if (rxbd->ctrl & QUICC_BD_CTL_Wrap) { + rxbd = smc_chan->rbase; + } else { + rxbd++; + } + smc_chan->rxbd = (struct cp_bufdesc *)rxbd; + return c; +} + +// Set up the device characteristics; baud rate, etc. +static bool +quicc_smc_serial_set_config(serial_channel *chan, cyg_serial_info_t *config) +{ + bool res = quicc_smc_serial_config_port(chan, config, false); + // FIXME - The documentation says that you can't change the baud rate + // again until at least two BRG input clocks have occurred. + return res; +} + +// Enable the transmitter (interrupt) on the device +static void +quicc_smc_serial_start_xmit(serial_channel *chan) +{ + quicc_smc_serial_info *smc_chan = (quicc_smc_serial_info *)chan->dev_priv; + if (smc_chan->txbd->length == 0) { + // See if there is anything to put in this buffer, just to get it going + cyg_drv_dsr_lock(); + (chan->callbacks->xmt_char)(chan); + cyg_drv_dsr_unlock(); + } + if (smc_chan->txbd->length != 0) { + // Make sure it gets started + quicc_smc_serial_flush(smc_chan); + } + smc_chan->tx_enabled = true; + cyg_drv_interrupt_unmask(smc_chan->int_num); +} + +// Disable the transmitter on the device +static void +quicc_smc_serial_stop_xmit(serial_channel *chan) +{ + quicc_smc_serial_info *smc_chan = (quicc_smc_serial_info *)chan->dev_priv; + // If anything is in the last buffer, need to get it started + if (smc_chan->txbd->length != 0) { + quicc_smc_serial_flush(smc_chan); + // Note: interrupt will get masked after this buffer finishes + } else { + cyg_drv_interrupt_mask(smc_chan->int_num); + } + smc_chan->tx_enabled = false; +} + +// Serial I/O - low level interrupt handler (ISR) +static cyg_uint32 +quicc_smc_serial_ISR(cyg_vector_t vector, cyg_addrword_t data) +{ + serial_channel *chan = (serial_channel *)data; + quicc_smc_serial_info *smc_chan = (quicc_smc_serial_info *)chan->dev_priv; + cyg_drv_interrupt_mask(smc_chan->int_num); + return CYG_ISR_CALL_DSR; // Cause DSR to be run +} + +// Serial I/O - high level interrupt handler (DSR) +static void +quicc_smc_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data) +{ + serial_channel *chan = (serial_channel *)data; + quicc_smc_serial_info *smc_chan = (quicc_smc_serial_info *)chan->dev_priv; + volatile struct smc_regs *ctl = smc_chan->ctl; + volatile struct cp_bufdesc *txbd; + volatile struct cp_bufdesc *rxbd = smc_chan->rxbd; + struct cp_bufdesc *rxlast; + int i, cache_state; +#ifdef CYGDBG_DIAG_BUF + int _time, _stime; + externC cyg_tick_count_t cyg_current_time(void); + cyg_drv_isr_lock(); + enable_diag_uart = 0; + HAL_CLOCK_READ(&_time); + _stime = (int)cyg_current_time(); + diag_printf("DSR start - CE: %x, time: %x.%x\n", ctl->smc_smce, _stime, _time); + enable_diag_uart = 1; +#endif // CYGDBG_DIAG_BUF + if (ctl->smc_smce & QUICC_SMCE_TX) { +#ifdef XX_CYGDBG_DIAG_BUF + enable_diag_uart = 0; + txbd = smc_chan->tbase; + for (i = 0; i < CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_TxNUM; i++, txbd++) { + diag_printf("Tx BD: %x, length: %d, ctl: %x\n", txbd, txbd->length, txbd->ctrl); + } + enable_diag_uart = 1; +#endif // CYGDBG_DIAG_BUF + // Transmit interrupt + ctl->smc_smce = QUICC_SMCE_TX; // Reset interrupt state; + txbd = smc_chan->tbase; // First buffer + while (true) { + if ((txbd->ctrl & (QUICC_BD_CTL_Ready|QUICC_BD_CTL_Int)) == QUICC_BD_CTL_Int) { +#ifdef XX_CYGDBG_DIAG_BUF + enable_diag_uart = 0; + HAL_CLOCK_READ(&_time); + _stime = (int)cyg_current_time(); + diag_printf("TX Done - Tx: %x, length: %d, time: %x.%x\n", txbd, txbd->length, _stime, _time); + enable_diag_uart = 1; +#endif // CYGDBG_DIAG_BUF + txbd->length = 0; + txbd->ctrl &= ~QUICC_BD_CTL_Int; // Reset interrupt bit + } + if (txbd->ctrl & QUICC_BD_CTL_Wrap) { + txbd = smc_chan->tbase; + break; + } else { + txbd++; + } + } + (chan->callbacks->xmt_char)(chan); + } + while (ctl->smc_smce & QUICC_SMCE_RX) { + // Receive interrupt + ctl->smc_smce = QUICC_SMCE_RX; // Reset interrupt state; + rxlast = (struct cp_bufdesc *) ( + (char *)eppc_base() + smc_chan->pram->rbptr ); +#ifdef CYGDBG_DIAG_BUF + enable_diag_uart = 0; + HAL_CLOCK_READ(&_time); + _stime = (int)cyg_current_time(); + diag_printf("Scan RX - rxbd: %x, rbptr: %x, time: %x.%x\n", rxbd, rxlast, _stime, _time); +#endif // CYGDBG_DIAG_BUF + while (rxbd != rxlast) { + if ((rxbd->ctrl & QUICC_BD_CTL_Ready) == 0) { +#ifdef CYGDBG_DIAG_BUF + diag_printf("rxbuf: %x, flags: %x, length: %d\n", rxbd, rxbd->ctrl, rxbd->length); + diag_dump_buf(rxbd->buffer, rxbd->length); +#endif // CYGDBG_DIAG_BUF + for (i = 0; i < rxbd->length; i++) { + (chan->callbacks->rcv_char)(chan, rxbd->buffer[i]); + } + // Note: the MBX860 does not seem to snoop/invalidate the data cache properly! + HAL_DCACHE_IS_ENABLED(cache_state); + if (cache_state) { + HAL_DCACHE_INVALIDATE(rxbd->buffer, smc_chan->rxsize); // Make sure no stale data + } + rxbd->length = 0; + rxbd->ctrl |= QUICC_BD_CTL_Ready; + } + if (rxbd->ctrl & QUICC_BD_CTL_Wrap) { + rxbd = smc_chan->rbase; + } else { + rxbd++; + } + } +#ifdef CYGDBG_DIAG_BUF + enable_diag_uart = 1; +#endif // CYGDBG_DIAG_BUF + smc_chan->rxbd = (struct cp_bufdesc *)rxbd; + } + if (ctl->smc_smce & QUICC_SMCE_BSY) { +#ifdef CYGDBG_DIAG_BUF + enable_diag_uart = 0; + diag_printf("RX BUSY interrupt\n"); + enable_diag_uart = 1; +#endif // CYGDBG_DIAG_BUF + ctl->smc_smce = QUICC_SMCE_BSY; // Reset interrupt state; + } +#ifdef CYGDBG_DIAG_BUF + enable_diag_uart = 0; + HAL_CLOCK_READ(&_time); + _stime = (int)cyg_current_time(); + diag_printf("DSR done - CE: %x, time: %x.%x\n", ctl->smc_smce, _stime, _time); + enable_diag_uart = 1; +#endif // CYGDBG_DIAG_BUF + cyg_drv_interrupt_acknowledge(smc_chan->int_num); + cyg_drv_interrupt_unmask(smc_chan->int_num); +#ifdef CYGDBG_DIAG_BUF + cyg_drv_isr_unlock(); +#endif // CYGDBG_DIAG_BUF +} + +void +show_rxbd(int dump_all) +{ +#ifdef CYGDBG_DIAG_BUF + EPPC *eppc = eppc_base(); + struct smc_uart_pram *pram = &eppc->pram[2].scc.pothers.smc_modem.psmc.u; + struct cp_bufdesc *rxbd = (struct cp_bufdesc *)((char *)eppc+pram->rbase); + int _enable = enable_diag_uart; + enable_diag_uart = 0; +#if 1 + diag_printf("SMC Mask: %x, Events: %x, Rbase: %x, Rbptr: %x\n", + eppc->smc_regs[0].smc_smcm, eppc->smc_regs[0].smc_smce, + pram->rbase, pram->rbptr); + while (true) { + diag_printf("Rx BD: %x, ctl: %x, length: %d\n", rxbd, rxbd->ctrl, rxbd->length); + if (rxbd->ctrl & QUICC_BD_CTL_Wrap) break; + rxbd++; + } +#endif + enable_diag_uart = _enable; + if (dump_all) dump_diag_buf(); +#endif // CYGDBG_DIAG_BUF +} +#endif // CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC + +// ------------------------------------------------------------------------ +// EOF powerpc/quicc_smc_serial.c
