--- upstream/dynamips-0.2.7/dev_mpc860.c 2007/10/06 16:29:14 10 +++ upstream/dynamips-0.2.8-RC1/dev_mpc860.c 2007/10/06 16:33:40 11 @@ -22,13 +22,51 @@ /* Debugging flags */ #define DEBUG_ACCESS 0 #define DEBUG_UNKNOWN 1 -#define DEBUG_IDMA 1 +#define DEBUG_IDMA 0 +#define DEBUG_SPI 0 +#define DEBUG_SCC 0 +#define DEBUG_FEC 1 + +#define MPC860_TXRING_PASS_COUNT 16 /* Dual-Port RAM */ #define MPC860_DPRAM_OFFSET 0x2000 #define MPC860_DPRAM_SIZE 0x2000 #define MPC860_DPRAM_END (MPC860_DPRAM_OFFSET + MPC860_DPRAM_SIZE) +/* MPC860 registers */ +#define MPC860_REG_SWSR 0x000e /* Software Service Register */ +#define MPC860_REG_SIPEND 0x0010 /* SIU Interrupt Pending Register */ +#define MPC860_REG_SIMASK 0x0014 /* SIU Interrupt Mask Register */ +#define MPC860_REG_PIPR 0x00f0 /* PCMCIA Interface Input Pins Reg. */ +#define MPC860_REG_TBSCR 0x0200 /* Timebase Status and Control Reg. */ +#define MPC860_REG_PISCR 0x0240 /* Periodic Int. Status and Ctrl Reg. */ +#define MPC860_REG_IDSR1 0x0910 /* IDMA1 Status Register */ +#define MPC860_REG_IDMR1 0x0914 /* IDMA1 Mask Register */ +#define MPC860_REG_IDSR2 0x0918 /* IDMA2 Status Register */ +#define MPC860_REG_IDMR2 0x091c /* IDMA2 Mask Register */ +#define MPC860_REG_CICR 0x0940 /* CPM Int Config Register */ +#define MPC860_REG_CIPR 0x0944 /* CPM Int Pending Register */ +#define MPC860_REG_CIMR 0x0948 /* CPM Int Mask Register */ +#define MPC860_REG_PCSO 0x0964 /* Port C Special Options Register */ +#define MPC860_REG_PCDAT 0x0966 /* Port C Data Register */ +#define MPC860_REG_CPCR 0x09c0 /* CP Command Register */ +#define MPC860_REG_SCC_BASE 0x0a00 /* SCC register base */ +#define MPC860_REG_SPMODE 0x0aa0 /* SPI Mode Register */ +#define MPC860_REG_SPIE 0x0aa6 /* SPI Event Register */ +#define MPC860_REG_SPIM 0x0aaa /* SPI Mask Register */ +#define MPC860_REG_SPCOM 0x0aad /* SPI Command Register */ +#define MPC860_REG_PBDAT 0x0ac4 /* Port B Data Register */ +#define MPC860_REG_FEC_BASE 0x0e00 /* FEC register base */ +#define MPC860_REG_FEC_END 0x0f84 /* FEC register end */ + +/* ======================================================================== */ + +/* CICR (CPM Interrupt Config Register) */ +#define MPC860_CICR_IRL_MASK 0x0000E000 /* Interrupt Level */ +#define MPC860_CICR_IRL_SHIFT 13 +#define MPC860_CICR_IEN 0x00000080 /* Interrupt Enable */ + /* CIPR (CPM Interrupt Pending Register) */ #define MPC860_CIPR_PC15 0x80000000 #define MPC860_CIPR_SCC1 0x40000000 @@ -59,6 +97,28 @@ #define MPC860_CIPR_PC5 0x00000004 #define MPC860_CIPR_PC4 0x00000002 +/* CPCR (CP Command Register) */ +#define MPC860_CPCR_RST 0x8000 /* CP reset command */ +#define MPC860_CPCR_FLG 0x0001 /* Command Semaphore Flag */ + +#define MPC860_CPCR_CHNUM_MASK 0x00F0 /* Channel Number */ +#define MPC860_CPCR_CHNUM_SHIFT 4 + +#define MPC860_CPCR_OPCODE_MASK 0x0F00 /* Opcode */ +#define MPC860_CPCR_OPCODE_SHIFT 8 + +/* CP channels */ +#define MPC860_CHAN_SCC1 0x00 +#define MPC860_CHAN_I2C_IDMA1 0x01 +#define MPC860_CHAN_SCC2 0x04 +#define MPC860_CHAN_SPI_IDMA2_RT 0x05 +#define MPC860_CHAN_SCC3 0x08 +#define MPC860_CHAN_SMC1 0x09 +#define MPC860_CHAN_SCC4 0x0c +#define MPC860_CHAN_SMC2_PIP 0x0d + +/* ======================================================================== */ + /* IDMA Status Register */ #define MPC860_IDSR_OB 0x0001 /* Out of Buffers */ #define MPC860_IDSR_DONE 0x0002 /* Buffer chain done */ @@ -89,6 +149,217 @@ m_uint32_t dst_bp; /* Destination buffer pointer */ }; +/* ======================================================================== */ + +/* SPI Mode Register (SPMODE) */ +#define MPC860_SPMODE_LOOP 0x4000 /* Loop mode */ +#define MPC860_SPMODE_CI 0x2000 /* Clock Invert */ +#define MPC860_SPMODE_CP 0x1000 /* Clock Phase */ +#define MPC860_SPMODE_DIV16 0x0800 /* Divide by 16 (SPI clock generator) */ +#define MPC860_SPMODE_REV 0x0400 /* Reverse Data */ +#define MPC860_SPMODE_MS 0x0200 /* Master/Slave mode select */ +#define MPC860_SPMODE_EN 0x0100 /* Enable SPI */ + +#define MPC860_SPMODE_LEN_MASK 0x00F0 /* Data length (4 - 11 bits) */ +#define MPC860_SPMODE_LEN_SHIFT 4 + +#define MPC860_SPMODE_PM_MASK 0x000F /* Prescale Modulus Select */ + +/* SPI Event/Mask Registers (SPIE/SPIM) */ +#define MPC860_SPIE_MME 0x20 /* MultiMaster Error */ +#define MPC860_SPIE_TXE 0x10 /* TX Error */ +#define MPC860_SPIE_BSY 0x04 /* Busy (no RX buffer available) */ +#define MPC860_SPIE_TXB 0x02 /* TX Buffer */ +#define MPC860_SPIE_RXB 0x01 /* RX Buffer */ + +/* SPI Command Register (SPCOM) */ +#define MPC860_SPCOM_STR 0x80 /* Start Transmit */ + +/* Offsets of SPI parameters (from DPRAM base) */ +#define MPC860_SPI_BASE 0x1d80 +#define MPC860_SPI_BASE_ADDR 0x1dac + +/* Size of an SPI buffer descriptor */ +#define MPC860_SPI_BD_SIZE 8 + +/* SPI RX Buffer Descriptor Control Word */ +#define MPC860_SPI_RXBD_CTRL_E 0x8000 /* Empty */ +#define MPC860_SPI_RXBD_CTRL_W 0x2000 /* Wrap */ +#define MPC860_SPI_RXBD_CTRL_I 0x1000 /* Interrupt */ +#define MPC860_SPI_RXBD_CTRL_L 0x0800 /* Last */ +#define MPC860_SPI_RXBD_CTRL_CM 0x0200 /* Continuous Mode */ +#define MPC860_SPI_RXBD_CTRL_OV 0x0002 /* Overrun */ +#define MPC860_SPI_RXBD_CTRL_ME 0x0001 /* MultiMaster Error */ + +/* SPI TX Buffer Descriptor Control Word */ +#define MPC860_SPI_TXBD_CTRL_R 0x8000 /* Ready Bit */ +#define MPC860_SPI_TXBD_CTRL_W 0x2000 /* Wrap */ +#define MPC860_SPI_TXBD_CTRL_I 0x1000 /* Interrupt */ +#define MPC860_SPI_TXBD_CTRL_L 0x0800 /* Last */ +#define MPC860_SPI_TXBD_CTRL_CM 0x0200 /* Continuous Mode */ +#define MPC860_SPI_TXBD_CTRL_UN 0x0002 /* Underrun */ +#define MPC860_SPI_TXBD_CTRL_ME 0x0001 /* MultiMaster Error */ + +/* SPI buffer descriptor */ +struct mpc860_spi_bd { + m_uint16_t offset; /* Offset in DPRAM memory */ + + m_uint16_t ctrl; /* Control Word */ + m_uint16_t buf_len; /* Buffer Length */ + m_uint32_t bp; /* Buffer Pointer */ +}; + +/* ======================================================================== */ + +/* Number of SCC channels */ +#define MPC860_SCC_NR_CHAN 4 + +/* Maximum buffer size for SCC */ +#define MPC860_SCC_MAX_PKT_SIZE 32768 + +/* Offsets of SCC channels (from DPRAM base) */ +#define MPC860_SCC1_BASE 0x1c00 +#define MPC860_SCC2_BASE 0x1d00 +#define MPC860_SCC3_BASE 0x1e00 +#define MPC860_SCC4_BASE 0x1f00 + +/* GSMR Low register */ +#define MPC860_GSMRL_MODE_MASK 0x0000000F + +/* SCC Modes */ +#define MPC860_SCC_MODE_HDLC 0x00 +#define MPC860_SCC_MODE_UART 0x04 +#define MPC860_SCC_MODE_BISYNC 0x08 +#define MPC860_SCC_MODE_ETH 0x0c + +/* SCC Event (SCCE) register */ +#define MPC860_SCCE_TXB 0x0002 /* TX buffer sent */ +#define MPC860_SCCE_RXB 0x0001 /* RX buffer ready */ + +/* Size of an SCC buffer descriptor */ +#define MPC860_SCC_BD_SIZE 8 + +/* SCC RX Buffer Descriptor Control Word */ +#define MPC860_SCC_RXBD_CTRL_E 0x8000 /* Empty */ +#define MPC860_SCC_RXBD_CTRL_W 0x2000 /* Wrap */ +#define MPC860_SCC_RXBD_CTRL_I 0x1000 /* Interrupt */ +#define MPC860_SCC_RXBD_CTRL_L 0x0800 /* Last */ +#define MPC860_SCC_RXBD_CTRL_F 0x0400 /* First */ +#define MPC860_SCC_RXBD_CTRL_CM 0x0200 /* Continuous Mode */ +#define MPC860_SCC_RXBD_CTRL_OV 0x0002 /* Overrun */ + +/* SCC TX Buffer Descriptor Control Word */ +#define MPC860_SCC_TXBD_CTRL_R 0x8000 /* Ready Bit */ +#define MPC860_SCC_TXBD_CTRL_W 0x2000 /* Wrap */ +#define MPC860_SCC_TXBD_CTRL_I 0x1000 /* Interrupt */ +#define MPC860_SCC_TXBD_CTRL_L 0x0800 /* Last */ +#define MPC860_SCC_TXBD_CTRL_TC 0x0400 /* Send TX CRC */ +#define MPC860_SCC_TXBD_CTRL_CM 0x0200 /* Continuous Mode */ +#define MPC860_SCC_TXBD_CTRL_UN 0x0002 /* Underrun */ + +/* SCC buffer descriptor */ +struct mpc860_scc_bd { + m_uint16_t offset; /* Offset in DPRAM memory */ + + m_uint16_t ctrl; /* Control Word */ + m_uint16_t buf_len; /* Buffer Length */ + m_uint32_t bp; /* Buffer Pointer */ +}; + +/* ======================================================================== */ + +/* FEC Ethernet Control Register */ +#define MPC860_ECNTRL_FEC_PIN_MUX 0x00000004 /* FEC enable */ +#define MPC860_ECNTRL_ETHER_EN 0x00000002 /* Ethernet Enable */ +#define MPC860_ECNTRL_RESET 0x00000001 /* Reset Ethernet controller */ + +/* FEC Interrupt Vector Register */ +#define MPC860_IVEC_ILEVEL_MASK 0xE0000000 /* Interrupt Level */ +#define MPC860_IVEC_ILEVEL_SHIFT 29 + +/* FEC Interrupt Event Register */ +#define MPC860_IEVENT_HBERR 0x80000000 /* Hearbeat Error */ +#define MPC860_IEVENT_BABR 0x40000000 /* Babbling Receive Error */ +#define MPC860_IEVENT_BABT 0x20000000 /* Babbling Transmit Error */ +#define MPC860_IEVENT_GRA 0x10000000 /* Graceful Stop Complete */ +#define MPC860_IEVENT_TFINT 0x08000000 /* Transmit Frame Interrupt */ +#define MPC860_IEVENT_TXB 0x04000000 /* Transmit Buffer Interrupt */ +#define MPC860_IEVENT_RFINT 0x02000000 /* Receive Frame Interrupt */ +#define MPC860_IEVENT_RXB 0x01000000 /* Receive Buffer Interrupt */ +#define MPC860_IEVENT_MII 0x00800000 /* MII Interrupt */ +#define MPC860_IEVENT_EBERR 0x00400000 /* Ethernet Bus Error */ + +/* MII data register */ +#define MPC860_MII_OP_MASK 0x30000000 /* Opcode (10:read,11:write) */ +#define MPC860_MII_OP_SHIFT 28 +#define MPC860_MII_PHY_MASK 0x0F800000 /* PHY device */ +#define MPC860_MII_PHY_SHIFT 23 +#define MPC860_MII_REG_MASK 0x007C0000 /* PHY device */ +#define MPC860_MII_REG_SHIFT 18 + +/* Size of an FEC buffer descriptor */ +#define MPC860_FEC_BD_SIZE 8 + +/* Maximum packet size for FEC */ +#define MPC860_FEC_MAX_PKT_SIZE 2048 + +/* FEC RX Buffer Descriptor Control Word */ +#define MPC860_FEC_RXBD_CTRL_E 0x8000 /* Empty */ +#define MPC860_FEC_RXBD_CTRL_RO1 0x4000 /* For software use */ +#define MPC860_FEC_RXBD_CTRL_W 0x2000 /* Wrap */ +#define MPC860_FEC_RXBD_CTRL_RO2 0x1000 /* For software use */ +#define MPC860_FEC_RXBD_CTRL_L 0x0800 /* Last */ +#define MPC860_FEC_RXBD_CTRL_M 0x0100 /* Miss */ +#define MPC860_FEC_RXBD_CTRL_BC 0x0080 /* Broadcast DA */ +#define MPC860_FEC_RXBD_CTRL_MC 0x0040 /* Multicast DA */ +#define MPC860_FEC_RXBD_CTRL_LG 0x0020 /* RX Frame length violation */ +#define MPC860_FEC_RXBD_CTRL_NO 0x0010 /* RX non-octet aligned frame */ +#define MPC860_FEC_RXBD_CTRL_SH 0x0008 /* Short Frame */ +#define MPC860_FEC_RXBD_CTRL_CR 0x0004 /* RX CRC Error */ +#define MPC860_FEC_RXBD_CTRL_OV 0x0002 /* Overrun */ +#define MPC860_FEC_RXBD_CTRL_TR 0x0001 /* Truncated Frame */ + +/* FEC TX Buffer Descriptor Control Word */ +#define MPC860_FEC_TXBD_CTRL_R 0x8000 /* Ready Bit */ +#define MPC860_FEC_TXBD_CTRL_TO1 0x4000 /* For software use */ +#define MPC860_FEC_TXBD_CTRL_W 0x2000 /* Wrap */ +#define MPC860_FEC_TXBD_CTRL_TO2 0x1000 /* For software use */ +#define MPC860_FEC_TXBD_CTRL_L 0x0800 /* Last */ +#define MPC860_FEC_TXBD_CTRL_TC 0x0400 /* Send TX CRC */ +#define MPC860_FEC_TXBD_CTRL_DEF 0x0200 /* Defer Indication */ +#define MPC860_FEC_TXBD_CTRL_HB 0x0100 /* Heartbeat Error */ +#define MPC860_FEC_TXBD_CTRL_LC 0x0080 /* Late Collision */ +#define MPC860_FEC_TXBD_CTRL_RL 0x0040 /* Retranmission Limit */ +#define MPC860_FEC_TXBD_CTRL_UN 0x0002 /* Underrun */ +#define MPC860_FEC_TXBD_CTRL_CSL 0x0001 /* Carrier Sense Lost */ + +/* FEC buffer descriptor */ +struct mpc860_fec_bd { + m_uint32_t bd_addr; /* Address in external memory */ + + m_uint16_t ctrl; /* Control Word */ + m_uint16_t buf_len; /* Buffer Length */ + m_uint32_t bp; /* Buffer Pointer */ +}; + +/* ======================================================================== */ + +struct mpc860_scc_chan { + netio_desc_t *nio; + + /* General SCC mode register (high and low parts) */ + m_uint32_t gsmr_hi,gsmr_lo; + + /* Protocol-Specific mode register */ + m_uint32_t psmr; + + /* SCC Event and Mask registers */ + m_uint16_t scce,sccm; + + /* TX packet */ + u_char tx_pkt[MPC860_SCC_MAX_PKT_SIZE]; +}; + /* MPC860 private data */ struct mpc860_data { char *name; @@ -109,6 +380,32 @@ /* IDMA status and mask registers */ m_uint8_t idsr[2],idmr[2]; + /* Port B Data Register */ + m_uint32_t pbdat,pcdat; + + /* SPI callback for TX data */ + mpc860_spi_tx_callback_t spi_tx_callback; + void *spi_user_arg; + + /* SCC channels */ + struct mpc860_scc_chan scc_chan[MPC860_SCC_NR_CHAN]; + + /* FEC (Fast Ethernet Controller) */ + m_uint32_t fec_rdes_start,fec_xdes_start; + m_uint32_t fec_rdes_current,fec_xdes_current; + m_uint32_t fec_rbuf_size; + + /* FEC Interrupt Event/Mask registers */ + m_uint32_t fec_ievent,fec_imask,fec_ivec; + m_uint32_t fec_ecntrl; + + /* FEC NetIO */ + netio_desc_t *fec_nio; + + /* FEC MII registers */ + m_uint32_t fec_mii_data; + m_uint16_t fec_mii_regs[32]; + /* Dual-Port RAM */ m_uint8_t dpram[MPC860_DPRAM_SIZE]; }; @@ -177,15 +474,30 @@ cpu->irq_check = cpu->irq_pending; } +/* Map level to SIU Interrupt Pending Register bit */ +static inline u_int mpc860_get_siu_lvl(u_int level) +{ + return(16 + ((7 - level) << 1)); +} + /* Update CPM interrupt status */ static void mpc860_update_cpm_int_status(struct mpc860_data *d) { - if (d->cipr & d->cimr) - mpc860_set_pending_irq(d,24); + u_int level,siu_bit; + + level = (d->cicr & MPC860_CICR_IRL_MASK) >> MPC860_CICR_IRL_SHIFT; + siu_bit = mpc860_get_siu_lvl(level); + + if ((d->cipr & d->cimr) && (d->cicr & MPC860_CICR_IEN)) + mpc860_set_pending_irq(d,siu_bit); else - mpc860_clear_pending_irq(d,24); + mpc860_clear_pending_irq(d,siu_bit); } +/* ======================================================================== */ +/* IDMA */ +/* ======================================================================== */ + /* Update an IDMA status register */ static int mpc860_idma_update_idsr(struct mpc860_data *d,u_int id) { @@ -222,13 +534,10 @@ static int mpc860_idma_fetch_bd(struct mpc860_data *d,m_uint16_t bd_addr, struct mpc860_idma_bd *bd) { - void *ptr; - if ((bd_addr < MPC860_DPRAM_OFFSET) || (bd_addr > MPC860_DPRAM_END)) return(-1); bd->offset = bd_addr - MPC860_DPRAM_OFFSET; - ptr = &d->dpram[bd->offset]; /* Fetch control word */ bd->ctrl = dpram_r16(d,bd->offset+0x00); @@ -304,6 +613,1120 @@ return(0); } +/* ======================================================================== */ +/* SPI (Serial Peripheral Interface) */ +/* ======================================================================== */ + +/* Initialize SPI RX parameters */ +static void mpc860_spi_init_rx_params(struct mpc860_data *d) +{ + m_uint16_t spi_base,rbase; + + spi_base = dpram_r16(d,MPC860_SPI_BASE_ADDR); + + /* Get the RBASE (offset 0) and store it in RBPTR */ + rbase = dpram_r16(d,spi_base+0x00); + dpram_w16(d,spi_base+0x10,rbase); +} + +/* Initialize SPI TX parameters */ +static void mpc860_spi_init_tx_params(struct mpc860_data *d) +{ + m_uint16_t spi_base,tbase; + + spi_base = dpram_r16(d,MPC860_SPI_BASE_ADDR); + + /* Get the TBASE (offset 2) and store it in TBPTR */ + tbase = dpram_r16(d,spi_base+0x02); + dpram_w16(d,spi_base+0x20,tbase); +} + +/* Initialize SPI RX/TX parameters */ +static void mpc860_spi_init_rx_tx_params(struct mpc860_data *d) +{ + mpc860_spi_init_rx_params(d); + mpc860_spi_init_tx_params(d); +} + +/* Fetch a SPI buffer descriptor */ +static int mpc860_spi_fetch_bd(struct mpc860_data *d,m_uint16_t bd_addr, + struct mpc860_spi_bd *bd) +{ + void *ptr; + + if ((bd_addr < MPC860_DPRAM_OFFSET) || (bd_addr > MPC860_DPRAM_END)) + return(-1); + + bd->offset = bd_addr - MPC860_DPRAM_OFFSET; + ptr = &d->dpram[bd->offset]; + + /* Fetch control word */ + bd->ctrl = dpram_r16(d,bd->offset+0x00); + + /* Fetch buffer length and buffer pointer */ + bd->buf_len = dpram_r16(d,bd->offset+0x02); + bd->bp = dpram_r32(d,bd->offset+0x04); + +#if DEBUG_SPI + MPC_LOG(d,"fetched SPI BD at 0x%4.4x, bp=0x%8.8x, len=%d\n", + bd->offset,bd->bp,bd->buf_len); +#endif + + return(0); +} + +/* Start SPI transmit */ +static int mpc860_spi_start_tx(struct mpc860_data *d) +{ + struct mpc860_spi_bd bd; + m_uint16_t bd_offset; + m_uint16_t spi_base; + u_char buffer[512]; + u_int buf_len; + + spi_base = dpram_r16(d,MPC860_SPI_BASE_ADDR); + + /* Get the TBPTR (offset 0x20) register */ + bd_offset = dpram_r16(d,spi_base+0x20); + + while(1) { + /* Fetch a TX descriptor */ + if (mpc860_spi_fetch_bd(d,bd_offset,&bd) == -1) + return(FALSE); + + /* If the descriptor is not ready, stop now */ + if (!(bd.ctrl & MPC860_SPI_TXBD_CTRL_R)) + return(FALSE); + + /* Extract the data */ + buf_len = bd.buf_len; + + if (bd.buf_len > sizeof(buffer)) { + MPC_LOG(d,"SPI: buffer too small for transmit.\n"); + buf_len = sizeof(buffer); + } + + physmem_copy_from_vm(d->vm,buffer,bd.bp,buf_len); + + /* Send the data to the user callback (if specified) */ + if (d->spi_tx_callback != NULL) + d->spi_tx_callback(d,buffer,buf_len,d->spi_user_arg); + + /* Clear the Ready bit of the TX descriptor */ + bd.ctrl &= ~MPC860_SPI_TXBD_CTRL_R; + dpram_w16(d,bd.offset+0x00,bd.ctrl); + + /* Set pointer on next TX descriptor (wrap ring if necessary) */ + if (bd.ctrl & MPC860_SPI_TXBD_CTRL_W) { + bd_offset = dpram_r16(d,spi_base+0x02); + } else { + bd_offset += MPC860_SPI_BD_SIZE; + } + dpram_w16(d,spi_base+0x20,bd_offset); + + /* Stop if this is the last buffer in chain */ + if (bd.ctrl & MPC860_SPI_TXBD_CTRL_L) + break; + } + + return(TRUE); +} + +/* Put a buffer into SPI receive buffers */ +int mpc860_spi_receive(struct mpc860_data *d,u_char *buffer,u_int len) +{ + struct mpc860_spi_bd bd; + m_uint16_t bd_offset; + m_uint16_t spi_base; + u_int clen,mrblr; + + spi_base = dpram_r16(d,MPC860_SPI_BASE_ADDR); + + /* Get the RBPTR (offset 0x10) */ + bd_offset = dpram_r16(d,spi_base+0x10); + + /* Get the maximum buffer size */ + mrblr = dpram_r16(d,spi_base+0x06); + + while(len > 0) { + /* Fetch a RX descriptor */ + if (mpc860_spi_fetch_bd(d,bd_offset,&bd) == -1) + return(FALSE); + + /* If the buffer is not empty, do not use it */ + if (!(bd.ctrl & MPC860_SPI_RXBD_CTRL_E)) + return(FALSE); + + /* Write data into the RX buffer */ + clen = m_min(mrblr,len); + physmem_copy_to_vm(d->vm,buffer,bd.bp,clen); + buffer += clen; + len -= clen; + + /* Update the length field */ + dpram_w16(d,bd.offset+0x02,clen); + + /* If no more data, set the "Last" bit */ + if (!len) + bd.ctrl |= MPC860_SPI_RXBD_CTRL_L; + + /* Clear the Empty bit of the RX descriptor */ + bd.ctrl &= ~MPC860_SPI_RXBD_CTRL_E; + dpram_w16(d,bd.offset+0x00,bd.ctrl); + + /* Set pointer on next RX descriptor */ + if (bd.ctrl & MPC860_SPI_RXBD_CTRL_W) { + bd_offset = dpram_r16(d,spi_base+0x00); + } else { + bd_offset += MPC860_SPI_BD_SIZE; + } + dpram_w16(d,spi_base+0x10,bd_offset); + } + + if (len > 0) + MPC_LOG(d,"SPI: no buffers available for receive.\n"); + + return(0); +} + +/* Set SPI TX callback */ +void mpc860_spi_set_tx_callback(struct mpc860_data *d, + mpc860_spi_tx_callback_t cbk, + void *user_arg) +{ + d->spi_tx_callback = cbk; + d->spi_user_arg = user_arg; +} + +/* ======================================================================== */ +/* SCC (Serial Communication Controller) */ +/* ======================================================================== */ + +typedef struct { + u_int cipr_irq; + m_uint32_t dpram_base; +}scc_chan_info_t; + +static scc_chan_info_t scc_chan_info[MPC860_SCC_NR_CHAN] = { + { MPC860_CIPR_SCC1, MPC860_SCC1_BASE }, + { MPC860_CIPR_SCC2, MPC860_SCC2_BASE }, + { MPC860_CIPR_SCC3, MPC860_SCC3_BASE }, + { MPC860_CIPR_SCC4, MPC860_SCC4_BASE }, +}; + +/* Initialize SCC RX parameters */ +static void mpc860_scc_init_rx_params(struct mpc860_data *d,u_int scc_chan) +{ + m_uint16_t scc_base,rbase; + + scc_base = scc_chan_info[scc_chan].dpram_base; + + /* Get the RBASE (offset 0) and store it in RBPTR */ + rbase = dpram_r16(d,scc_base+0x00); + dpram_w16(d,scc_base+0x10,rbase); +} + +/* Initialize SCC TX parameters */ +static void mpc860_scc_init_tx_params(struct mpc860_data *d,u_int scc_chan) +{ + m_uint16_t scc_base,tbase; + + scc_base = scc_chan_info[scc_chan].dpram_base; + + /* Get the TBASE (offset 2) and store it in TBPTR */ + tbase = dpram_r16(d,scc_base+0x02); + dpram_w16(d,scc_base+0x20,tbase); +} + +/* Initialize SCC RX/TX parameters */ +static void mpc860_scc_init_rx_tx_params(struct mpc860_data *d,u_int scc_chan) +{ + mpc860_scc_init_rx_params(d,scc_chan); + mpc860_scc_init_tx_params(d,scc_chan); +} + +/* Set an SCC interrupt */ +static int mpc860_scc_update_irq(struct mpc860_data *d,u_int scc_chan) +{ + struct mpc860_scc_chan *chan = &d->scc_chan[scc_chan]; + + if (chan->scce & chan->sccm) + d->cipr |= scc_chan_info[scc_chan].cipr_irq; + else + d->cipr &= ~scc_chan_info[scc_chan].cipr_irq; + + mpc860_update_cpm_int_status(d); + return(0); +} + +/* Fetch a SCC buffer descriptor */ +static int mpc860_scc_fetch_bd(struct mpc860_data *d,m_uint16_t bd_addr, + struct mpc860_scc_bd *bd) +{ + void *ptr; + + if ((bd_addr < MPC860_DPRAM_OFFSET) || (bd_addr > MPC860_DPRAM_END)) + return(-1); + + bd->offset = bd_addr - MPC860_DPRAM_OFFSET; + ptr = &d->dpram[bd->offset]; + + /* Fetch control word */ + bd->ctrl = dpram_r16(d,bd->offset+0x00); + + /* Fetch buffer length and buffer pointer */ + bd->buf_len = dpram_r16(d,bd->offset+0x02); + bd->bp = dpram_r32(d,bd->offset+0x04); + +#if DEBUG_SCC + MPC_LOG(d,"fetched SCC BD at 0x%4.4x, bp=0x%8.8x, len=%d\n", + bd->offset,bd->bp,bd->buf_len); +#endif + + return(0); +} + +/* Handle the TX ring of an SCC channel (transmit a single packet) */ +static int mpc860_scc_handle_tx_ring_single(struct mpc860_data *d, + u_int scc_chan) +{ + struct mpc860_scc_bd txd0,ctxd,*ptxd; + struct mpc860_scc_chan *chan; + scc_chan_info_t *scc_info; + m_uint16_t bd_offset; + m_uint32_t clen,tot_len; + u_char *pkt_ptr; + int done = FALSE; + int irq = FALSE; + + scc_info = &scc_chan_info[scc_chan]; + chan = &d->scc_chan[scc_chan]; + + /* Get the TBPTR (offset 0x20) register */ + bd_offset = dpram_r16(d,scc_info->dpram_base+0x20); + + /* Try to acquire the first descriptor */ + ptxd = &txd0; + mpc860_scc_fetch_bd(d,bd_offset,ptxd); + + /* If we don't own the first descriptor, we cannot transmit */ + if (!(txd0.ctrl & MPC860_SCC_TXBD_CTRL_R)) + return(FALSE); + + /* Empty packet for now */ + pkt_ptr = chan->tx_pkt; + tot_len = 0; + + do { + /* Copy data into the buffer */ + clen = ptxd->buf_len; + physmem_copy_from_vm(d->vm,pkt_ptr,ptxd->bp,clen); + pkt_ptr += clen; + tot_len += clen; + + /* Signal IRQ ? */ + if (ptxd->ctrl & MPC860_SCC_TXBD_CTRL_I) + irq = TRUE; + + /* + * Clear the ready bit (except for the first descriptor, + * which is cleared when the full packet has been sent). + */ + if (ptxd != &txd0) { + ptxd->ctrl &= ~MPC860_SCC_TXBD_CTRL_R; + dpram_w16(d,ptxd->offset+0x00,ptxd->ctrl); + } + + /* Set pointer on next TX descriptor (wrap ring if necessary) */ + if (ptxd->ctrl & MPC860_SCC_TXBD_CTRL_W) { + bd_offset = dpram_r16(d,scc_info->dpram_base+0x02); + } else { + bd_offset += MPC860_SCC_BD_SIZE; + } + dpram_w16(d,scc_info->dpram_base+0x20,bd_offset); + + /* If this is the last descriptor, we have finished */ + if (!(ptxd->ctrl & MPC860_SCC_TXBD_CTRL_L)) { + mpc860_scc_fetch_bd(d,bd_offset,&ctxd); + ptxd = &ctxd; + } else { + done = TRUE; + } + }while(!done); + + if (tot_len != 0) { +#if DEBUG_SCC + MPC_LOG(d,"SCC%u: sending packet of %u bytes\n",scc_chan+1,tot_len); + mem_dump(log_file,chan->tx_pkt,tot_len); +#endif + /* send packet on wire */ + netio_send(chan->nio,chan->tx_pkt,tot_len); + } + + /* Clear the Ready bit of the first TX descriptor */ + txd0.ctrl &= ~MPC860_SCC_TXBD_CTRL_R; + dpram_w16(d,txd0.offset+0x00,txd0.ctrl); + + /* Trigger SCC IRQ */ + if (irq) { + chan->scce |= MPC860_SCCE_TXB; + mpc860_scc_update_irq(d,scc_chan); + } + + return(TRUE); +} + +/* Handle the TX ring of the specified SCC channel (multiple pkts possible) */ +static int mpc860_scc_handle_tx_ring(struct mpc860_data *d,u_int scc_chan) +{ + int i; + + for(i=0;iscc_chan[scc_chan]; + + /* Get the RBPTR (offset 0x10) register */ + bd_offset = dpram_r16(d,scc_info->dpram_base+0x10); + + /* Get the maximum buffer size */ + mrblr = dpram_r16(d,scc_info->dpram_base+0x06); + + /* Try to acquire the first descriptor */ + prxd = &rxd0; + mpc860_scc_fetch_bd(d,bd_offset,prxd); + + /* If we don't own the first descriptor, we cannot transmit */ + if (!(rxd0.ctrl & MPC860_SCC_RXBD_CTRL_E)) + return(FALSE); + + pkt_ptr = pkt; + tot_len = pkt_len; + + while(tot_len > 0) { + /* Write data into the RX buffer */ + clen = m_min(mrblr,tot_len); + physmem_copy_to_vm(d->vm,pkt_ptr,prxd->bp,clen); + pkt_ptr += clen; + tot_len -= clen; + + /* Signal IRQ ? */ + if (prxd->ctrl & MPC860_SCC_RXBD_CTRL_I) + irq = TRUE; + + /* Set the Last flag if we have finished */ + if (!tot_len) { + /* Set the full length */ + switch(chan->gsmr_lo & MPC860_GSMRL_MODE_MASK) { + case MPC860_SCC_MODE_ETH: + pkt_len += 4; + break; + case MPC860_SCC_MODE_HDLC: + pkt_len += 2; + break; + } + + dpram_w16(d,prxd->offset+0x02,pkt_len); + prxd->ctrl |= MPC860_SCC_RXBD_CTRL_L; + } else { + /* Update the length field */ + dpram_w16(d,prxd->offset+0x02,clen); + } + + /* + * Clear the empty bit (except for the first descriptor, + * which is cleared when the full packet has been stored). + */ + if (prxd != &rxd0) { + prxd->ctrl &= ~MPC860_SCC_RXBD_CTRL_E; + dpram_w16(d,prxd->offset+0x00,prxd->ctrl); + } + + /* Set pointer on next RX descriptor (wrap ring if necessary) */ + if (prxd->ctrl & MPC860_SCC_RXBD_CTRL_W) { + bd_offset = dpram_r16(d,scc_info->dpram_base+0x00); + } else { + bd_offset += MPC860_SCC_BD_SIZE; + } + dpram_w16(d,scc_info->dpram_base+0x10,bd_offset); + + /* If this is the last descriptor, we have finished */ + if (!tot_len) { + mpc860_scc_fetch_bd(d,bd_offset,&crxd); + prxd = &crxd; + } + } + + /* Clear the Empty bit of the first RX descriptor and set First bit */ + rxd0.ctrl &= ~MPC860_SCC_RXBD_CTRL_E; + rxd0.ctrl |= MPC860_SCC_RXBD_CTRL_F; + dpram_w16(d,rxd0.offset+0x00,rxd0.ctrl); + + /* Trigger SCC IRQ */ + if (irq) { + d->scc_chan[scc_chan].scce |= MPC860_SCCE_RXB; + mpc860_scc_update_irq(d,scc_chan); + } + + return(TRUE); +} + +/* Set NIO for the specified SCC channel */ +int mpc860_scc_set_nio(struct mpc860_data *d,u_int scc_chan,netio_desc_t *nio) +{ + struct mpc860_scc_chan *chan; + + if (!d || (scc_chan >= MPC860_SCC_NR_CHAN)) + return(-1); + + chan = &d->scc_chan[scc_chan]; + + /* check that a NIO is not already bound */ + if (chan->nio != NULL) + return(-1); + + chan->nio = nio; + netio_rxl_add(nio,(netio_rx_handler_t)mpc860_scc_handle_rx_pkt, + d,(void *)scc_chan); + return(0); +} + +/* Unset NIO of the specified SCC channel */ +int mpc860_scc_unset_nio(struct mpc860_data *d,u_int scc_chan) +{ + struct mpc860_scc_chan *chan; + + if (!d || (scc_chan >= MPC860_SCC_NR_CHAN)) + return(-1); + + chan = &d->scc_chan[scc_chan]; + + if (chan->nio != NULL) { + netio_rxl_remove(chan->nio); + chan->nio = NULL; + } + + return(0); +} + +/* + * SCC register access. + * + * SCC1: 0x0a00 to 0x0a1f + * SCC2: 0x0a20 to 0x0a3f + * SCC3: 0x0a40 to 0x0a5f + * SCC4: 0x0a60 to 0x0a7f + */ +static int dev_mpc860_scc_access(struct mpc860_data *d,m_uint32_t offset, + u_int op_size,u_int op_type,m_uint64_t *data) +{ + struct mpc860_scc_chan *chan; + u_int scc_chan,reg; + + /* Extract channel ID and register */ + scc_chan = (offset >> 5) & 0x03; + reg = offset & 0x1F; + + chan = &d->scc_chan[scc_chan]; + + switch(reg) { + /* GSMRL - General SCC mode register (Low part) */ + case 0x00: + if (op_type == MTS_READ) + *data = chan->gsmr_lo; + else + chan->gsmr_lo = *data; + break; + + /* GSMRH - General SCC mode register (High part) */ + case 0x04: + if (op_type == MTS_READ) + *data = chan->gsmr_hi; + else + chan->gsmr_hi = *data; + break; + + /* PSMR - Protocol-Specific Mode Register */ + case 0x08: + if (op_type == MTS_READ) + *data = chan->psmr; + else + chan->psmr = *data; + break; + + /* TOD - Transmit On Demand */ + case 0x0c: + if ((op_type == MTS_WRITE) && (*data & 0x8000)) + mpc860_scc_handle_tx_ring(d,scc_chan); + break; + + /* SCCE - SCC Event Register */ + case 0x10: + if (op_type == MTS_READ) + *data = chan->scce; + else { + chan->scce &= ~(*data); + mpc860_scc_update_irq(d,scc_chan); + } + break; + + /* SCCM - SCC Mask Register */ + case 0x14: + if (op_type == MTS_READ) + *data = chan->sccm; + else { + chan->sccm = *data; + mpc860_scc_update_irq(d,scc_chan); + } + break; + } + + return(0); +} + +/* ======================================================================== */ +/* FEC (Fast Ethernet Controller) */ +/* ======================================================================== */ + +/* Trigger interrupt for FEC */ +static void mpc860_fec_update_irq_status(struct mpc860_data *d) +{ + u_int level,siu_bit; + + level = (d->fec_ivec & MPC860_IVEC_ILEVEL_MASK) >> MPC860_IVEC_ILEVEL_SHIFT; + siu_bit = mpc860_get_siu_lvl(level); + + if (d->fec_ievent & d->fec_imask) + mpc860_set_pending_irq(d,siu_bit); + else + mpc860_clear_pending_irq(d,siu_bit); +} + +/* Fetch a FEC buffer descriptor, located in external memory */ +static int mpc860_fec_fetch_bd(struct mpc860_data *d,m_uint32_t bd_addr, + struct mpc860_fec_bd *bd) +{ + m_uint32_t w0,w1; + + /* Set BD address */ + bd->bd_addr = bd_addr; + + w0 = physmem_copy_u32_from_vm(d->vm,bd_addr); + w1 = physmem_copy_u32_from_vm(d->vm,bd_addr+4); + + bd->ctrl = w0 >> 16; + bd->buf_len = w0 & 0xFFFF; + bd->bp = w1; + +#if DEBUG_FEC + MPC_LOG(d,"fetched FEC BD at 0x%8.8x, bp=0x%8.8x, len=%d\n", + bd->bd_addr,bd->bp,bd->buf_len); +#endif + + return(0); +} + +/* Handle the TX ring of the FEC (transmit a single packet) */ +static int mpc860_fec_handle_tx_ring_single(struct mpc860_data *d) +{ + u_char tx_pkt[MPC860_FEC_MAX_PKT_SIZE]; + struct mpc860_fec_bd txd0,ctxd,*ptxd; + m_uint32_t clen,tot_len; + u_char *pkt_ptr; + int done = FALSE; + + if (!d->fec_xdes_current) + return(FALSE); + + /* Try to acquire the first descriptor */ + ptxd = &txd0; + mpc860_fec_fetch_bd(d,d->fec_xdes_current,ptxd); + + /* If we don't own the first descriptor, we cannot transmit */ + if (!(txd0.ctrl & MPC860_FEC_TXBD_CTRL_R)) + return(FALSE); + + /* Empty packet for now */ + pkt_ptr = tx_pkt; + tot_len = 0; + + do { + /* Copy data into the buffer */ + clen = ptxd->buf_len; + physmem_copy_from_vm(d->vm,pkt_ptr,ptxd->bp,clen); + pkt_ptr += clen; + tot_len += clen; + + /* + * Clear the ready bit (except for the first descriptor, + * which is cleared when the full packet has been sent). + */ + if (ptxd != &txd0) { + ptxd->ctrl &= ~MPC860_FEC_TXBD_CTRL_R; + physmem_copy_u16_to_vm(d->vm,ptxd->bd_addr+0x00,ptxd->ctrl); + } + + /* Set pointer on next TX descriptor (wrap ring if necessary) */ + if (ptxd->ctrl & MPC860_FEC_TXBD_CTRL_W) { + d->fec_xdes_current = d->fec_xdes_start; + } else { + d->fec_xdes_current += MPC860_FEC_BD_SIZE; + } + + /* If this is the last descriptor, we have finished */ + if (!(ptxd->ctrl & MPC860_FEC_TXBD_CTRL_L)) { + mpc860_fec_fetch_bd(d,d->fec_xdes_current,&ctxd); + ptxd = &ctxd; + } else { + done = TRUE; + } + }while(!done); + + if (tot_len != 0) { +#if DEBUG_FEC + MPC_LOG(d,"FEC: sending packet of %u bytes\n",tot_len); + mem_dump(log_file,tx_pkt,tot_len); +#endif + /* send packet on wire */ + netio_send(d->fec_nio,tx_pkt,tot_len); + } + + /* Clear the Ready bit of the first TX descriptor */ + txd0.ctrl &= ~MPC860_FEC_TXBD_CTRL_R; + physmem_copy_u16_to_vm(d->vm,txd0.bd_addr+0x00,txd0.ctrl); + + /* Trigger FEC IRQ */ + d->fec_ievent |= MPC860_IEVENT_TFINT | MPC860_IEVENT_TXB; + mpc860_fec_update_irq_status(d); + return(TRUE); +} + +/* Handle the TX ring of the FEC (multiple pkts possible) */ +static int mpc860_fec_handle_tx_ring(struct mpc860_data *d) +{ + int i; + + for(i=0;ifec_rdes_current) + return(FALSE); + + /* Try to acquire the first descriptor */ + prxd = &rxd0; + mpc860_fec_fetch_bd(d,d->fec_rdes_current,prxd); + + /* If we don't own the first descriptor, we cannot transmit */ + if (!(rxd0.ctrl & MPC860_FEC_RXBD_CTRL_E)) + return(FALSE); + + pkt_ptr = pkt; + tot_len = pkt_len; + + while(tot_len > 0) { + /* Write data into the RX buffer */ + clen = m_min(d->fec_rbuf_size,tot_len); + physmem_copy_to_vm(d->vm,pkt_ptr,prxd->bp,clen); + pkt_ptr += clen; + tot_len -= clen; + + /* Set the Last flag if we have finished */ + if (!tot_len) { + /* Set the full length */ + physmem_copy_u16_to_vm(d->vm,prxd->bd_addr+0x02,pkt_len+4); + prxd->ctrl |= MPC860_FEC_RXBD_CTRL_L; + + if (eth_addr_is_bcast(&hdr->daddr)) + prxd->ctrl |= MPC860_FEC_RXBD_CTRL_BC; + else if (eth_addr_is_mcast(&hdr->daddr)) + prxd->ctrl |= MPC860_FEC_RXBD_CTRL_MC; + } else { + /* Update the length field */ + physmem_copy_u16_to_vm(d->vm,prxd->bd_addr+0x02,clen); + } + + /* + * Clear the empty bit (except for the first descriptor, + * which is cleared when the full packet has been stored). + */ + if (prxd != &rxd0) { + prxd->ctrl &= ~MPC860_FEC_RXBD_CTRL_E; + physmem_copy_u16_to_vm(d->vm,prxd->bd_addr+0x00,prxd->ctrl); + } + + /* Set pointer on next RX descriptor (wrap ring if necessary) */ + if (prxd->ctrl & MPC860_FEC_RXBD_CTRL_W) { + d->fec_rdes_current = d->fec_rdes_start; + } else { + d->fec_rdes_current += MPC860_FEC_BD_SIZE; + } + + /* If this is the last descriptor, we have finished */ + if (!tot_len) { + mpc860_fec_fetch_bd(d,d->fec_rdes_current,&crxd); + prxd = &crxd; + } + } + + /* Clear the Empty bit of the first RX descriptor */ + rxd0.ctrl &= ~MPC860_FEC_RXBD_CTRL_E; + physmem_copy_u16_to_vm(d->vm,rxd0.bd_addr+0x00,rxd0.ctrl); + + /* Trigger FEC IRQ */ + d->fec_ievent |= MPC860_IEVENT_RFINT | MPC860_IEVENT_RXB; + mpc860_fec_update_irq_status(d); + return(TRUE); +} + +/* MII register read access */ +static void mpc860_fec_mii_read_access(struct mpc860_data *d, + u_int phy,u_int reg) +{ + m_uint16_t res; + + res = d->fec_mii_regs[reg]; + + switch(reg) { + case 0x00: + res = 0x1100; + break; + case 0x01: + if (d->fec_nio) + res = 0x7829; + else + res = 0; + break; + case 0x02: + res = 0x7810; + break; + case 0x03: + res = 0x0003; + break; + case 0x04: + res = 0x1E1; + break; + case 0x05: + res = 0x41E1; + break; + case 0x06: + res = 0x0004; + break; + case 0x10: + res = 0x0084; + break; + case 0x11: + res = 0x4780; + break; + case 0x12: + res = 0x4000; + break; + case 0x13: + res = 0x0094; + break; + case 0x14: + res = 0x28c8; + break; + default: + res = 0; + } + + d->fec_mii_data &= 0xFFFF0000; + d->fec_mii_data |= res; +} + +/* MII register read access */ +static void mpc860_fec_mii_write_access(struct mpc860_data *d, + u_int phy,u_int reg) +{ +#if DEBUG_FEC + MPC_LOG(d,"FEC: Writing 0x%8.8x to MII reg %d\n", + d->fec_mii_data & 0xFFFF,reg); +#endif + d->fec_mii_regs[reg] = d->fec_mii_data & 0xFFFF; +} + +/* MII register access */ +static void mpc860_fec_mii_access(struct mpc860_data *d) +{ + u_int op,phy,reg; + + op = (d->fec_mii_data & MPC860_MII_OP_MASK) >> MPC860_MII_OP_SHIFT; + phy = (d->fec_mii_data & MPC860_MII_PHY_MASK) >> MPC860_MII_PHY_SHIFT; + reg = (d->fec_mii_data & MPC860_MII_REG_MASK) >> MPC860_MII_REG_SHIFT; + + switch(op) { + /* MII write */ + case 0x01: + mpc860_fec_mii_write_access(d,phy,reg); + break; + + /* MII read */ + case 0x02: + mpc860_fec_mii_read_access(d,phy,reg); + break; + + default: + MPC_LOG(d,"FEC: unknown MII opcode %u\n",op); + } + + /* MII access completed */ + d->fec_ievent |= MPC860_IEVENT_MII; + mpc860_fec_update_irq_status(d); +} + +/* + * FEC register access (0xE00 to 0xF84). + */ +static int dev_mpc860_fec_access(struct mpc860_data *d,m_uint32_t offset, + u_int op_size,u_int op_type,m_uint64_t *data) +{ + switch(offset) { + /* R_DES_START: Beginning of RxBD ring */ + case 0xE10: + if (op_type == MTS_READ) + *data = d->fec_rdes_start; + else + d->fec_rdes_start = *data & 0xFFFFFFFC; + break; + + /* X_DES_START: Beginning of TxBD ring */ + case 0xE14: + if (op_type == MTS_READ) + *data = d->fec_xdes_start; + else + d->fec_xdes_start = *data & 0xFFFFFFFC; + break; + + /* R_BUFF_SIZE: Receive Buffer Size */ + case 0xE18: + if (op_type == MTS_READ) + *data = d->fec_rbuf_size; + else + d->fec_rbuf_size = *data & 0x7F0; + break; + + /* ECNTRL */ + case 0xE40: + if (op_type == MTS_READ) { + *data = d->fec_ecntrl; + } else { + if (*data & MPC860_ECNTRL_RESET) + d->fec_ecntrl = 0; + else { + if (!(*data & MPC860_ECNTRL_ETHER_EN)) { + d->fec_xdes_current = d->fec_xdes_start; + d->fec_rdes_current = d->fec_rdes_start; + } + + d->fec_ecntrl = *data; + } + } + break; + + /* IEVENT: Interrupt Event Register */ + case 0xE44: + if (op_type == MTS_READ) { + *data = d->fec_ievent; + } else { + d->fec_ievent &= ~(*data); + mpc860_fec_update_irq_status(d); + } + break; + + /* IMASK: Interrupt Mask Register */ + case 0xE48: + if (op_type == MTS_READ) { + *data = d->fec_imask; + } else { + d->fec_imask = *data; + mpc860_fec_update_irq_status(d); + } + break; + + /* IVEC: Interrupt Vector Register */ + case 0xE4C: + if (op_type == MTS_READ) + *data = d->fec_ivec; + else + d->fec_ivec = *data; + break; + + /* X_DES_ACTIVE: TxBD Active Register */ + case 0xE54: + mpc860_fec_handle_tx_ring(d); + //printf("x_des_active set\n"); + break; + + /* MII_DATA */ + case 0xE80: + if (op_type == MTS_READ) { + *data = d->fec_mii_data; + } else { + d->fec_mii_data = *data; + mpc860_fec_mii_access(d); + } + break; + } + + return(0); +} + +/* Set NIO for the Fast Ethernet Controller */ +int mpc860_fec_set_nio(struct mpc860_data *d,netio_desc_t *nio) +{ + /* check that a NIO is not already bound */ + if (!d || (d->fec_nio != NULL)) + return(-1); + + d->fec_nio = nio; + netio_rxl_add(nio,(netio_rx_handler_t)mpc860_fec_handle_rx_pkt,d,NULL); + return(0); +} + +/* Unset NIO of the Fast Ethernet Controller */ +int mpc860_fec_unset_nio(struct mpc860_data *d) +{ + if (!d) + return(-1); + + if (d->fec_nio != NULL) { + netio_rxl_remove(d->fec_nio); + d->fec_nio = NULL; + } + + return(0); +} + +/* ======================================================================== */ + +#define MPC860_CP_FOP(chan,op) (((chan) << 4) + (op)) + +/* Execute a command sent through CP Command Register (CPCR) */ +static void mpc860_exec_cpcr(struct mpc860_data *d,m_uint32_t cpcr) +{ + u_int channel,opcode,fop; + + channel = (cpcr >> 4) & 0x0F; + opcode = (cpcr >> 8) & 0x0F; + + fop = MPC860_CP_FOP(channel,opcode); + + switch(fop) { + /* SPI - Init RX and TX params */ + case MPC860_CP_FOP(MPC860_CHAN_SPI_IDMA2_RT,0): + mpc860_spi_init_rx_tx_params(d); + break; + + /* SPI - Init RX params */ + case MPC860_CP_FOP(MPC860_CHAN_SPI_IDMA2_RT,1): + mpc860_spi_init_rx_params(d); + break; + + /* SPI - Init TX params */ + case MPC860_CP_FOP(MPC860_CHAN_SPI_IDMA2_RT,2): + mpc860_spi_init_tx_params(d); + break; + + /* SCC1 - Init RX and TX params */ + case MPC860_CP_FOP(MPC860_CHAN_SCC1,0): + mpc860_scc_init_rx_tx_params(d,0); + break; + + /* SCC1 - Init RX params */ + case MPC860_CP_FOP(MPC860_CHAN_SCC1,1): + mpc860_scc_init_rx_params(d,0); + break; + + /* SCC1 - Init TX params */ + case MPC860_CP_FOP(MPC860_CHAN_SCC1,2): + mpc860_scc_init_tx_params(d,0); + break; + + /* SCC2 - Init RX and TX params */ + case MPC860_CP_FOP(MPC860_CHAN_SCC2,0): + mpc860_scc_init_rx_tx_params(d,1); + break; + + /* SCC2 - Init RX params */ + case MPC860_CP_FOP(MPC860_CHAN_SCC2,1): + mpc860_scc_init_rx_params(d,1); + break; + + /* SCC2 - Init TX params */ + case MPC860_CP_FOP(MPC860_CHAN_SCC2,2): + mpc860_scc_init_tx_params(d,1); + break; + + /* SCC3 - Init RX and TX params */ + case MPC860_CP_FOP(MPC860_CHAN_SCC3,0): + mpc860_scc_init_rx_tx_params(d,2); + break; + + /* SCC3 - Init RX params */ + case MPC860_CP_FOP(MPC860_CHAN_SCC3,1): + mpc860_scc_init_rx_params(d,2); + break; + + /* SCC3 - Init TX params */ + case MPC860_CP_FOP(MPC860_CHAN_SCC3,2): + mpc860_scc_init_tx_params(d,2); + break; + + /* SCC4 - Init RX and TX params */ + case MPC860_CP_FOP(MPC860_CHAN_SCC4,0): + mpc860_scc_init_rx_tx_params(d,3); + break; + + /* SCC4 - Init RX params */ + case MPC860_CP_FOP(MPC860_CHAN_SCC4,1): + mpc860_scc_init_rx_params(d,3); + break; + + /* SCC4 - Init TX params */ + case MPC860_CP_FOP(MPC860_CHAN_SCC4,2): + mpc860_scc_init_tx_params(d,3); + break; + + default: + MPC_LOG(d,"CPCR: unknown cmd: channel=0x%4.4x, opcode=0x%4.4x\n", + channel,opcode); + } +} + /* * dev_mpc860_access() */ @@ -331,19 +1754,34 @@ if ((offset >= MPC860_DPRAM_OFFSET) && (offset < MPC860_DPRAM_END)) return(d->dpram + (offset - MPC860_DPRAM_OFFSET)); + /* Handle SCC channels */ + if ((offset >= MPC860_REG_SCC_BASE) && + (offset < (MPC860_REG_SCC_BASE + (4 * 0x20)))) + { + dev_mpc860_scc_access(d,offset,op_size,op_type,data); + return NULL; + } + + /* Handle Fast Ethernet Controller (FEC) registers */ + if ((offset >= MPC860_REG_FEC_BASE) && (offset <= MPC860_REG_FEC_END)) + { + dev_mpc860_fec_access(d,offset,op_size,op_type,data); + return NULL; + } + switch(offset) { /* SWSR - Software Service Register (Watchdog) */ - case 0x000e: + case MPC860_REG_SWSR: break; /* SIU Interrupt Pending Register */ - case 0x0010: + case MPC860_REG_SIPEND: if (op_type == MTS_READ) *data = d->sipend; break; /* SIU Interrupt Mask Register */ - case 0x0014: + case MPC860_REG_SIMASK: if (op_type == MTS_READ) { *data = d->simask; } else { @@ -356,13 +1794,13 @@ * Cisco 2600: * Bit 30: 0=NM in slot 1 */ - case 0x00f0: + case MPC860_REG_PIPR: if (op_type == MTS_READ) *data = 0x3F00F600; break; /* PISCR - Periodic Interrupt Status and Control Register */ - case 0x0240: + case MPC860_REG_PISCR: if (op_type == MTS_WRITE) { if (*data & 0x80) { d->sipend &= ~0x40000000; @@ -371,13 +1809,13 @@ } break; - case 0x200: + case MPC860_REG_TBSCR: if (op_type == MTS_READ) *data = 0x45; break; /* IDMA1 Status and Mask Registers */ - case 0x910: + case MPC860_REG_IDSR1: if (op_type == MTS_READ) { *data = d->idsr[0]; } else { @@ -385,7 +1823,7 @@ } break; - case 0x914: + case MPC860_REG_IDMR1: if (op_type == MTS_READ) *data = d->idmr[0]; else @@ -393,22 +1831,30 @@ break; /* IDMA2 Status and Mask Registers */ - case 0x918: + case MPC860_REG_IDSR2: if (op_type == MTS_READ) *data = d->idsr[1]; else d->idsr[1] &= ~(*data); break; - case 0x91c: + case MPC860_REG_IDMR2: if (op_type == MTS_READ) *data = d->idmr[1]; else d->idmr[1] = *data; break; + /* CICR - CPM Interrupt Configuration Register */ + case MPC860_REG_CICR: + if (op_type == MTS_READ) + *data = d->cicr; + else + d->cicr = *data; + break; + /* CIPR - CPM Interrupt Pending Register */ - case 0x944: + case MPC860_REG_CIPR: if (op_type == MTS_READ) *data = d->cipr; else { @@ -418,7 +1864,7 @@ break; /* CIMR - CPM Interrupt Mask Register */ - case 0x948: + case MPC860_REG_CIMR: if (op_type == MTS_READ) *data = d->cimr; else { @@ -428,23 +1874,44 @@ break; /* PCSO - Port C Special Options Register */ - case 0x964: + case MPC860_REG_PCSO: if (op_type == MTS_WRITE) { if (*data & 0x01) { +#if DEBUG_IDMA MPC_LOG(d,"activating IDMA0\n"); +#endif mpc860_idma_start_channel(d,0); } } break; - case 0x0966: + /* PCDAT - Port C Data Register */ + case MPC860_REG_PCDAT: + if (op_type == MTS_WRITE) + d->pcdat = *data; + else + *data = d->pcdat; break; - case 0x9c0: - if (op_type == MTS_WRITE) { - printf("OPCODE=0x%llx, CHANNEL=0x%llx\n", - (*data >> 8) & 0xF, (*data >> 4) & 0xF); - } + /* PBDAT - Port B Data Register */ + case MPC860_REG_PBDAT: + if (op_type == MTS_WRITE) + d->pbdat = *data; + else + *data = d->pbdat; + break; + + /* CPCR - CP Command Register */ + case MPC860_REG_CPCR: + if (op_type == MTS_WRITE) + mpc860_exec_cpcr(d,(m_uint32_t)(*data)); + break; + + /* SPCOM - SPI Command Register */ + case MPC860_REG_SPCOM: + if ((op_type == MTS_WRITE) && (*data & MPC860_SPCOM_STR)) + mpc860_spi_start_tx(d); + break; #if DEBUG_UNKNOWN default: @@ -515,6 +1982,9 @@ d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.handler = dev_mpc860_access; + + /* Set the default SPI base address */ + dpram_w16(d,MPC860_SPI_BASE_ADDR,MPC860_SPI_BASE); /* Map this device to the VM */ vm_bind_device(vm,&d->dev);