// Function to initialize the device.  Called at bootstrap time.
static bool 
quicc_sxx_serial_init(struct cyg_devtab_entry *tab)
{
    serial_channel *chan = (serial_channel *)tab->priv;
    quicc_sxx_serial_info *smc_chan = (quicc_sxx_serial_info *)chan->dev_priv;
    volatile EPPC *eppc = (volatile EPPC *)eppc_base();
    int TxBD, RxBD;
    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 = %s\n", smc_chan->channel, smc_chan->int_num, tab->name);
#endif
#ifdef CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC_SMC1
    if (chan == &quicc_sxx_serial_channel_smc1) {
        TxBD = _mpc8xx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_TxNUM);
        RxBD = _mpc8xx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_RxNUM);
        quicc_smc_serial_init_info(&quicc_sxx_serial_info_smc1,
                                   &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],
                                   RxBD, 
                                   CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_RxNUM,
                                   CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC1_RxSIZE,
                                   &quicc_smc1_rxbuf[0],
                                   0xC0, // PortB mask
                                   QUICC_CPM_SMC1
            );
    }
#endif
#ifdef CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC_SMC2
    if (chan == &quicc_sxx_serial_channel_smc2) {
        TxBD = _mpc8xx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_TxNUM);
        RxBD = _mpc8xx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_RxNUM);
        quicc_smc_serial_init_info(&quicc_sxx_serial_info_smc2,
                                   &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],
                                   RxBD, 
                                   CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_RxNUM,
                                   CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SMC2_RxSIZE,
                                   &quicc_smc2_rxbuf[0],
                                   0xC00, // PortB mask
                                   QUICC_CPM_SMC2
            );
    }
#endif
#ifdef CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC_SCC1
    if (chan == &quicc_sxx_serial_channel_scc1) {
        TxBD = _mpc8xx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC1_TxNUM);
        RxBD = _mpc8xx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC1_RxNUM);
        quicc_scc_serial_init_info(&quicc_sxx_serial_info_scc1,
                                   &eppc->pram[0].scc.pscc.u, // PRAM
                                   &eppc->scc_regs[0],        // Control registersn
                                   TxBD, 
                                   CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC1_TxNUM,
                                   CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC1_TxSIZE,
                                   &quicc_scc1_txbuf[0],
                                   RxBD, 
                                   CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC1_RxNUM,
                                   CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC1_RxSIZE,
                                   &quicc_scc1_rxbuf[0],
                                   0x0003, // PortA mask
                                   0x1000, // PortB mask
                                   0x0800, // PortC mask
                                   QUICC_CPM_SCC1
            );
    }
#endif
#ifdef CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC_SCC2
    if (chan == &quicc_sxx_serial_channel_scc2) {
        TxBD = _mpc8xx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC2_TxNUM);
        RxBD = _mpc8xx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC2_RxNUM);
        quicc_scc_serial_init_info(&quicc_sxx_serial_info_scc2,
                                   &eppc->pram[1].scc.pscc.u, // PRAM
                                   &eppc->scc_regs[1],        // Control registersn
                                   TxBD, 
                                   CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC2_TxNUM,
                                   CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC2_TxSIZE,
                                   &quicc_scc2_txbuf[0],
                                   RxBD, 
                                   CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC2_RxNUM,
                                   CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC2_RxSIZE,
                                   &quicc_scc2_rxbuf[0],
                                   0x000C, // PortA mask
                                   0x2000, // PortB mask
                                   0x0C00, // PortC mask
                                   QUICC_CPM_SCC2
            );
    }
#endif
#ifdef CYGPKG_IO_SERIAL_POWERPC_QUICC_SMC_SCC3
    if (chan == &quicc_sxx_serial_channel_scc3) {
        TxBD = _mpc8xx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC3_TxNUM);
        RxBD = _mpc8xx_allocBd(sizeof(struct cp_bufdesc)*CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC3_RxNUM);
        quicc_scc_serial_init_info(&quicc_sxx_serial_info_scc3,
                                   &eppc->pram[2].scc.pscc.u, // PRAM
                                   &eppc->scc_regs[2],        // Control registersn
                                   TxBD, 
                                   CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC3_TxNUM,
                                   CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC3_TxSIZE,
                                   &quicc_scc3_txbuf[0],
                                   RxBD, 
                                   CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC3_RxNUM,
                                   CYGNUM_IO_SERIAL_POWERPC_QUICC_SMC_SCC3_RxSIZE,
                                   &quicc_scc3_rxbuf[0],
#if defined(CYGHWR_HAL_POWERPC_MPC8XX_850)
                                   0x0000, // PortA mask
                                   0x00C0, // PortB mask
                                   0x0000, // PortC mask
#elif defined(CYGHWR_HAL_POWERPC_MPC8XX_852T)
                                   0x0030, // PortA mask
                                   0x0000, // PortB mask
                                   0x0000, // PortC mask
#elif defined(CYGHWR_HAL_POWERPC_MPC8XX_823)
                                   0x0000, // PortA mask
                                   0x00C0, // PortB mask
                                   0x0000, // PortC mask
#else
#error "Cannot route SCC3"
#endif
                                   QUICC_CPM_SCC3
            );
    }
#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_sxx_serial_ISR,
                                 quicc_sxx_serial_DSR,
                                 &smc_chan->serial_interrupt_handle,
                                 &smc_chan->serial_interrupt);
        cyg_drv_interrupt_attach(smc_chan->serial_interrupt_handle);
        cyg_drv_interrupt_unmask(smc_chan->int_num);
    }
    if (smc_chan->type == _SMC_CHAN) {
        quicc_smc_serial_config_port(chan, &chan->config, true);
    } else {
        quicc_scc_serial_config_port(chan, &chan->config, true);
    }
    if (cache_state)
        HAL_DCACHE_ENABLE();
    return true;
}
Example #2
0
/*
 *  Initialize SMCX as a uart.
 *
 *  Comments below reference Motorola's "MPC860 User Manual".
 *  The basic initialization steps are from Section 16.15.8
 *  of that manual.
 */	
static void
cyg_hal_smcx_init_channel(struct port_info *info, int port)
{
    EPPC *eppc = eppc_base();
    int i;
    volatile struct smc_uart_pram *uart_pram = (volatile struct smc_uart_pram *)((char *)eppc + info->pram);
    volatile struct smc_regs *regs = (volatile struct smc_regs *)((char *)eppc + info->regs);
    struct cp_bufdesc *txbd, *rxbd;

    if (info->init) return;
    info->init = 1;

    switch (port) {
#if CYGNUM_HAL_QUICC_SMC1 > 0
    case QUICC_CPM_SMC1:
        /*
         *  Set up the PortB pins for UART operation.
         *  Set PAR and DIR to allow SMCTXD1 and SMRXD1
         *  (Table 16-39)
         */
        eppc->pip_pbpar |= 0xc0;
        eppc->pip_pbdir &= ~0xc0;

        break;
#endif
#if CYGNUM_HAL_QUICC_SMC2 > 0
    case QUICC_CPM_SMC2:
        /*
         *  Set up the PortA pins for UART operation.
         *  Set PAR and DIR to allow SMCTXD2 and SMRXD2
         *  (Table 16-39)
         */
        eppc->pio_papar |= 0xc0;
        eppc->pio_padir &= ~0xc0;
        eppc->pio_paodr &= ~0xc0;

        break;
#endif
    }

    // Set up baud rate generator.  These are allocated from a
    // pool, based on the port number and type.  The allocator
    // will arrange to have the selected baud rate clock steered
    // to this device.
    info->brg = _mpc8xx_allocate_brg(port);
    *(info->brg) = 0x10000 | (UART_BIT_RATE(UART_BAUD_RATE)<<1);

    /*
     *  Set pointers to buffer descriptors.
     *  (Sections 16.15.4.1, 16.15.7.12, and 16.15.7.13)
     */
    uart_pram->rbase = _mpc8xx_allocBd(sizeof(struct cp_bufdesc)*info->Rxnum + info->Rxnum);
    uart_pram->tbase = _mpc8xx_allocBd(sizeof(struct cp_bufdesc)*info->Txnum + info->Txnum);

    /*
     *  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;

    /* max receive buffer length */
    uart_pram->mrblr = 1;

    /* disable max_idle feature */
    uart_pram->max_idl = 0;

    /* 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 */
    rxbd = (struct cp_bufdesc *)((char *)eppc + uart_pram->rbase);
    info->next_rxbd = rxbd;
    for (i = 0;  i < info->Rxnum;  i++) {
        rxbd->length = 0;
        rxbd->buffer = ((char *)eppc + (uart_pram->rbase+(info->Rxnum*sizeof(struct cp_bufdesc))))+i;
        rxbd->ctrl   = QUICC_BD_CTL_Ready | QUICC_BD_CTL_Int;
        rxbd++;
    }
    rxbd--;
    rxbd->ctrl   |= QUICC_BD_CTL_Wrap;

    /* setup TX buffer descriptor */
    txbd = (struct cp_bufdesc *)((char *)eppc + uart_pram->tbase);
    txbd->length = 1;
    txbd->buffer = ((char *)eppc + (uart_pram->tbase+(info->Txnum*sizeof(struct cp_bufdesc))));
    txbd->ctrl   = 0x2000;

    /*
     *  Clear any previous events. Mask interrupts.
     *  (Section 16.15.7.14 and 16.15.7.15)
     */
    regs->smc_smce = 0xff;
    regs->smc_smcm = 1; // RX interrupts only, for ctrl-c

    /*
     *  Set 8,n,1 characters, then also enable rx and tx.
     *  (Section 16.15.7.11)
     */
    regs->smc_smcmr = 0x4820;
    regs->smc_smcmr = 0x4823;

    /*
     *  Init Rx & Tx params for SMCx
     */
    eppc->cp_cr = QUICC_CPM_CR_INIT_TXRX | port | QUICC_CPM_CR_BUSY;

    info->irq = 0;  // Interrupts not enabled
}
Example #3
0
/*
 *  Initialize an SCC as a uart.
 *
 *  Comments below reference Motorola's "MPC860 User Manual".
 *  The basic initialization steps are from Section 16.15.8
 *  of that manual.
 */	
static void
cyg_hal_sccx_init_channel(struct port_info *info, int port)
{
    EPPC *eppc = eppc_base();
    int i;
    volatile struct uart_pram *uart_pram = (volatile struct uart_pram *)((char *)eppc + info->pram);
    volatile struct scc_regs *regs = (volatile struct scc_regs *)((char *)eppc + info->regs);
    struct cp_bufdesc *txbd, *rxbd;

    if (info->init) return;
    info->init = 1;

    /*
     *  Set up the Port pins for UART operation.
     */
    switch (port) {
#if CYGNUM_HAL_QUICC_SCC1 > 0
    case QUICC_CPM_SCC1:
        eppc->pio_papar |= 0x03;
        eppc->pio_padir &= ~0x03;
        eppc->pio_paodr &= ~0x03;

        /* CTS on PortC.11 */
        eppc->pio_pcdir &= 0x800;
        eppc->pio_pcpar &= 0x800;
        eppc->pio_pcso  |= 0x800;

        /* RTS on PortB.19 */
        eppc->pip_pbpar |= 0x1000;
        eppc->pip_pbdir |= 0x1000;

        break;
#endif
#if CYGNUM_HAL_QUICC_SCC2 > 0
    case QUICC_CPM_SCC2:
#error FIXME
        eppc->pio_papar |= 0x0C;
        eppc->pio_padir &= ~0x0C;
        eppc->pio_paodr &= ~0x0C;

        /* CTS on PortC.11 */
        eppc->pio_pcdir &= 0xC00;
        eppc->pio_pcpar &= 0xC00;
        eppc->pio_pcso  |= 0xC00;

        /* RTS on PortB.19 */
        eppc->pip_pbpar |= 0x2000;
        eppc->pip_pbdir |= 0x2000;

        break;
#endif
#if CYGNUM_HAL_QUICC_SCC3 > 0
    case QUICC_CPM_SCC3:
#if defined(CYGHWR_HAL_POWERPC_MPC8XX_850)
#if 0
// CAUTION!  Enabling these bits made the port get stuck :-(
        /* CTS/RTS/CD on PortC.4/5/13 */
        eppc->pio_pcdir &= 0x0C04;
        eppc->pio_pcpar &= 0x0C00;
//        eppc->pio_pcpar |= 0x0004;
        eppc->pio_pcso  |= 0x0C00;
#endif

        /* RxD/TxD on PortB.24/25 */
        eppc->pip_pbpar |= 0x00C0;
        eppc->pip_pbdir |= 0x00C0;
        eppc->pip_pbodr &= ~0x00C0;

#elif defined(CYGHWR_HAL_POWERPC_MPC8XX_852T)
        eppc->pio_papar |= 0x30;
        eppc->pio_padir &= ~0x30;
        eppc->pio_paodr &= ~0x30;
#else
#error "Cannot route SCC3 I/O"
#endif // 850T
        break;
#endif // SCC3
    }

    // Set up baud rate generator.  These are allocated from a
    // pool, based on the port number and type.  The allocator
    // will arrange to have the selected baud rate clock steered
    // to this device.
    info->brg = _mpc8xx_allocate_brg(port);
    *(info->brg) = 0x10000 | (UART_BIT_RATE(UART_BAUD_RATE)<<1);

    /*
     *  Set pointers to buffer descriptors.
     */
    uart_pram->rbase = _mpc8xx_allocBd(sizeof(struct cp_bufdesc)*info->Rxnum + info->Rxnum);
    uart_pram->tbase = _mpc8xx_allocBd(sizeof(struct cp_bufdesc)*info->Txnum + info->Txnum);

    /*
     *  SDMA & LCD bus request level 5
     */
    eppc->dma_sdcr = 1;

    /*
     *  Set Rx and Tx function code
     */
    uart_pram->rfcr = 0x18;
    uart_pram->tfcr = 0x18;

    /* max receive buffer length */
    uart_pram->mrblr = 1;

    /* disable max_idle feature */
    uart_pram->max_idl = 0;

    /* 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;

    /* character mask */
    uart_pram->rccm  = 0xC0FF;

    /* control characters */
    for (i = 0;  i < 8;  i++) {
        uart_pram->cc[i] = 0x8000;  // Mark unused
    }

    /* setup RX buffer descriptors */
    rxbd = (struct cp_bufdesc *)((char *)eppc + uart_pram->rbase);
    info->next_rxbd = rxbd;
    for (i = 0;  i < info->Rxnum;  i++) {
        rxbd->length = 0;
        rxbd->buffer = ((char *)eppc + (uart_pram->rbase+(info->Rxnum*sizeof(struct cp_bufdesc))))+i;
        rxbd->ctrl   = QUICC_BD_CTL_Ready | QUICC_BD_CTL_Int;
        rxbd++;
    }
    rxbd--;
    rxbd->ctrl   |= QUICC_BD_CTL_Wrap;

    /* setup TX buffer descriptor */
    txbd = (struct cp_bufdesc *)((char *)eppc + uart_pram->tbase);
    txbd->length = 0;
    txbd->buffer = ((char *)eppc + (uart_pram->tbase+(info->Txnum*sizeof(struct cp_bufdesc))));
    txbd->ctrl   = 0x2000;

    /*
     *  Clear any previous events. Mask interrupts.
     *  (Section 16.15.7.14 and 16.15.7.15)
     */
    regs->scc_scce = 0xffff;
    regs->scc_sccm = 1; // RX interrupts only, for ctrl-c

    /*
     *  Set 8,n,1 characters
     */
    regs->scc_psmr = (3<<12);
    regs->scc_gsmr_h = 0x20;          // 8bit FIFO
    regs->scc_gsmr_l = 0x00028004;    // 16x TxCLK, 16x RxCLK, UART

    /*
     *  Init Rx & Tx params for SCCX
     */
    eppc->cp_cr = QUICC_CPM_CR_INIT_TXRX | port | QUICC_CPM_CR_BUSY;

    regs->scc_gsmr_l |= 0x30;         // Enable Rx, Tx

    info->irq = 0;
}
Example #4
0
//
// Initialize the interface - performed at system startup
// This function must set up the interface, including arranging to
// handle interrupts, etc, so that it may be "started" cheaply later.
//
static bool 
quicc_eth_init(struct cyg_netdevtab_entry *tab)
{
    struct eth_drv_sc *sc = (struct eth_drv_sc *)tab->device_instance;
    struct quicc_eth_info *qi = (struct quicc_eth_info *)sc->driver_private;
    volatile EPPC *eppc = (volatile EPPC *)eppc_base();
    struct cp_bufdesc *rxbd, *txbd;
    unsigned char *RxBUF, *TxBUF, *ep, *ap; 
    volatile struct ethernet_pram *enet_pram;
    volatile struct scc_regs *scc;
    int TxBD, RxBD;
    int cache_state;
    int i;
    bool esa_ok = false;

#ifdef QUICC_ETH_FETCH_ESA
    QUICC_ETH_FETCH_ESA(esa_ok);
#endif

    if (!esa_ok) {
#if defined(CYGPKG_REDBOOT) && \
    defined(CYGSEM_REDBOOT_FLASH_CONFIG)
        esa_ok = flash_get_config("quicc_esa", enaddr, CONFIG_ESA);
#else
        esa_ok = CYGACC_CALL_IF_FLASH_CFG_OP(CYGNUM_CALL_IF_FLASH_CFG_GET,         
                                             "quicc_esa", enaddr, CONFIG_ESA);
#endif
        if (!esa_ok) {
            // Can't figure out ESA
            diag_printf("QUICC_ETH - Warning! ESA unknown\n");
            memcpy(&enaddr, &_default_enaddr, sizeof(enaddr));
        }
    }

    // Ensure consistent state between cache and what the QUICC sees
    HAL_DCACHE_IS_ENABLED(cache_state);
    HAL_DCACHE_SYNC();
    HAL_DCACHE_DISABLE();

#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
    // Set up to handle interrupts
    cyg_drv_interrupt_create(QUICC_ETH_INT,
                             CYGARC_SIU_PRIORITY_HIGH,
                             (cyg_addrword_t)sc, //  Data item passed to interrupt handler
                             (cyg_ISR_t *)quicc_eth_isr,
                             (cyg_DSR_t *)eth_drv_dsr,
                             &quicc_eth_interrupt_handle,
                             &quicc_eth_interrupt);
    cyg_drv_interrupt_attach(quicc_eth_interrupt_handle);
    cyg_drv_interrupt_acknowledge(QUICC_ETH_INT);
    cyg_drv_interrupt_unmask(QUICC_ETH_INT);
#endif

    qi->pram = enet_pram = &eppc->pram[QUICC_ETH_SCC].enet_scc;
    qi->ctl = scc = &eppc->scc_regs[QUICC_ETH_SCC];  // Use SCCx

    // Shut down ethernet, in case it is already running
    scc->scc_gsmr_l &= ~(QUICC_SCC_GSML_ENR | QUICC_SCC_GSML_ENT);

    memset((void *)enet_pram, 0, sizeof(*enet_pram));

    TxBD = _mpc8xx_allocBd(CYGNUM_DEVS_ETH_POWERPC_QUICC_TxNUM * sizeof(struct cp_bufdesc));
    RxBD = _mpc8xx_allocBd(CYGNUM_DEVS_ETH_POWERPC_QUICC_RxNUM * sizeof(struct cp_bufdesc));

    txbd = (struct cp_bufdesc *)((char *)eppc + TxBD);
    rxbd = (struct cp_bufdesc *)((char *)eppc + RxBD);
    qi->tbase = txbd;
    qi->txbd = txbd;    
    qi->tnext = txbd;
    qi->rbase = rxbd;
    qi->rxbd = rxbd;
    qi->rnext = rxbd;
    qi->txactive = 0;

    RxBUF = &quicc_eth_rxbufs[0][0];
    TxBUF = &quicc_eth_txbufs[0][0];

    // setup buffer descriptors
    for (i = 0;  i < CYGNUM_DEVS_ETH_POWERPC_QUICC_RxNUM;  i++) {
        rxbd->length = 0;
        rxbd->buffer = RxBUF;
        rxbd->ctrl   = QUICC_BD_CTL_Ready | QUICC_BD_CTL_Int;
        RxBUF += CYGNUM_DEVS_ETH_POWERPC_QUICC_BUFSIZE;
        rxbd++;
    }
    rxbd--;
    rxbd->ctrl |= QUICC_BD_CTL_Wrap;  // Last buffer
    for (i = 0;  i < CYGNUM_DEVS_ETH_POWERPC_QUICC_TxNUM;  i++) {
        txbd->length = 0;
        txbd->buffer = TxBUF;
        txbd->ctrl   = 0;
        TxBUF += CYGNUM_DEVS_ETH_POWERPC_QUICC_BUFSIZE;
        txbd++;
    }
    txbd--;
    txbd->ctrl |= QUICC_BD_CTL_Wrap;  // Last buffer

    // Set up parallel ports for connection to ethernet tranceiver
    eppc->pio_papar |= (QUICC_ETH_PA_RXD | QUICC_ETH_PA_TXD);
    eppc->pio_padir &= ~(QUICC_ETH_PA_RXD | QUICC_ETH_PA_TXD);
    eppc->pio_paodr &= ~QUICC_ETH_PA_TXD;

    eppc->pio_pcpar &= ~(QUICC_ETH_PC_COLLISION | QUICC_ETH_PC_Rx_ENABLE);
    eppc->pio_pcdir &= ~(QUICC_ETH_PC_COLLISION | QUICC_ETH_PC_Rx_ENABLE);
    eppc->pio_pcso  |= (QUICC_ETH_PC_COLLISION | QUICC_ETH_PC_Rx_ENABLE);

    eppc->pio_papar |= (QUICC_ETH_PA_Tx_CLOCK | QUICC_ETH_PA_Rx_CLOCK);
    eppc->pio_padir &= ~(QUICC_ETH_PA_Tx_CLOCK | QUICC_ETH_PA_Rx_CLOCK);

    // Set up clock routing
    eppc->si_sicr &= ~QUICC_ETH_SICR_MASK;
    eppc->si_sicr |= QUICC_ETH_SICR_ENET;
    eppc->si_sicr &= ~QUICC_ETH_SICR_ENABLE;

    // Set up DMA mode
    eppc->dma_sdcr = 0x0001;

    // Initialize shared PRAM
    enet_pram->rbase = RxBD;
    enet_pram->tbase = TxBD;

    // Set Big Endian mode
    enet_pram->rfcr = QUICC_SCC_FCR_BE;
    enet_pram->tfcr = QUICC_SCC_FCR_BE;

    // Size of receive buffers
    enet_pram->mrblr = CYGNUM_DEVS_ETH_POWERPC_QUICC_BUFSIZE;

    // Initialize CRC calculations
    enet_pram->c_pres = 0xFFFFFFFF;
    enet_pram->c_mask = 0xDEBB20E3;  // Actual CRC formula
    enet_pram->crcec = 0;
    enet_pram->alec = 0;
    enet_pram->disfc = 0;

    // Frame padding
    enet_pram->pads = 0x8888;
    enet_pram->pads = 0x0000;

    // Retries
    enet_pram->ret_lim = 15;
    enet_pram->ret_cnt = 0;

    // Frame sizes
    enet_pram->mflr = IEEE_8023_MAX_FRAME;
    enet_pram->minflr = IEEE_8023_MIN_FRAME;
    enet_pram->maxd1 = CYGNUM_DEVS_ETH_POWERPC_QUICC_BUFSIZE;
    enet_pram->maxd2 = CYGNUM_DEVS_ETH_POWERPC_QUICC_BUFSIZE;

    // Group address hash
    enet_pram->gaddr1 = 0;
    enet_pram->gaddr2 = 0;
    enet_pram->gaddr3 = 0;
    enet_pram->gaddr4 = 0;

    // Device physical address
    ep = &enaddr[sizeof(enaddr)];
    ap = (unsigned char *)&enet_pram->paddr_h;
    for (i = 0;  i < sizeof(enaddr);  i++) {
        *ap++ = *--ep;
    }

    // Persistence counter
    enet_pram->p_per = 0; 

    // Individual address filter
    enet_pram->iaddr1 = 0;
    enet_pram->iaddr2 = 0;
    enet_pram->iaddr3 = 0;
    enet_pram->iaddr4 = 0;

    // Temp address
    enet_pram->taddr_h = 0;
    enet_pram->taddr_m = 0;
    enet_pram->taddr_l = 0;

    // Initialize the CPM (set up buffer pointers, etc).
    eppc->cp_cr = QUICC_CPM_SCCx | QUICC_CPM_CR_INIT_TXRX | QUICC_CPM_CR_BUSY;
    while (eppc->cp_cr & QUICC_CPM_CR_BUSY) ;

    // Clear any pending interrupt/exceptions
    scc->scc_scce = 0xFFFF;

    // Enable interrupts
    scc->scc_sccm = QUICC_SCCE_INTS | QUICC_SCCE_GRC | QUICC_SCCE_BSY;

    // Set up SCCx to run in ethernet mode
    scc->scc_gsmr_h = 0;
    scc->scc_gsmr_l = QUICC_SCC_GSML_TCI | QUICC_SCC_GSML_TPL_48 |
        QUICC_SCC_GSML_TPP_01 | QUICC_SCC_GSML_MODE_ENET;

    // Sync delimiters
    scc->scc_dsr = 0xD555;

    // Protocol specifics (as if GSML wasn't enough)
    scc->scc_psmr = QUICC_PMSR_ENET_CRC | QUICC_PMSR_SEARCH_AFTER_22 |
        QUICC_PMSR_RCV_SHORT_FRAMES;

#ifdef QUICC_ETH_ENABLE
    QUICC_ETH_ENABLE();
#endif

#ifdef QUICC_ETH_RESET_PHY
    QUICC_ETH_RESET_PHY();
#endif

    // Enable ethernet interface
#ifdef QUICC_ETH_PC_Tx_ENABLE
    eppc->pio_pcpar |= QUICC_ETH_PC_Tx_ENABLE;
    eppc->pio_pcdir &= ~QUICC_ETH_PC_Tx_ENABLE;
#else
    eppc->pip_pbpar |= QUICC_ETH_PB_Tx_ENABLE;
    eppc->pip_pbdir |= QUICC_ETH_PB_Tx_ENABLE;
#endif

    if (cache_state)
        HAL_DCACHE_ENABLE();

    // Initialize upper level driver
    (sc->funs->eth_drv->init)(sc, (unsigned char *)&enaddr);

    // Set LED state
    clear_led(LED_TxACTIVE);
    clear_led(LED_RxACTIVE);

    return true;
}