Source to ./dev_mpc860.c
/*
* Cisco router simulation platform.
* Copyright (c) 2005,2006 Christophe Fillot ([email protected])
*
* MPC860 internal devices.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "utils.h"
#include "net.h"
#include "cpu.h"
#include "vm.h"
#include "dynamips.h"
#include "memory.h"
#include "device.h"
#include "net_io.h"
#include "dev_mpc860.h"
/* Debugging flags */
#define DEBUG_ACCESS 0
#define DEBUG_UNKNOWN 1
#define DEBUG_IDMA 0
#define DEBUG_SPI 0
#define DEBUG_SCC 0
#define DEBUG_FEC 0
#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
#define MPC860_CIPR_SCC2 0x20000000
#define MPC860_CIPR_SCC3 0x10000000
#define MPC860_CIPR_SCC4 0x08000000
#define MPC860_CIPR_PC14 0x04000000
#define MPC860_CIPR_TIMER1 0x02000000
#define MPC860_CIPR_PC13 0x01000000
#define MPC860_CIPR_PC12 0x00800000
#define MPC860_CIPR_SDMA 0x00400000
#define MPC860_CIPR_IDMA1 0x00200000
#define MPC860_CIPR_IDMA2 0x00100000
#define MPC860_CIPR_TIMER2 0x00040000
#define MPC860_CIPR_RTT 0x00020000
#define MPC860_CIPR_I2C 0x00010000
#define MPC860_CIPR_PC11 0x00008000
#define MPC860_CIPR_PC10 0x00004000
#define MPC860_CIPR_TIMER3 0x00001000
#define MPC860_CIPR_PC9 0x00000800
#define MPC860_CIPR_PC8 0x00000400
#define MPC860_CIPR_PC7 0x00000200
#define MPC860_CIPR_TIMER4 0x00000080
#define MPC860_CIPR_PC6 0x00000040
#define MPC860_CIPR_SPI 0x00000020
#define MPC860_CIPR_SMC1 0x00000010
#define MPC860_CIPR_SMC2 0x00000008
#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 */
#define MPC860_IDSR_AD 0x0004 /* Auxiliary done */
/* Offsets of IDMA channels (from DPRAM base) */
#define MPC860_IDMA1_BASE 0x1cc0
#define MPC860_IDMA2_BASE 0x1dc0
/* Size of an IDMA buffer descriptor */
#define MPC860_IDMA_BD_SIZE 16
/* IDMA Buffer Descriptor Control Word */
#define MPC860_IDMA_CTRL_V 0x8000 /* Valid Bit */
#define MPC860_IDMA_CTRL_W 0x2000 /* Wrap */
#define MPC860_IDMA_CTRL_I 0x1000 /* Interrupt for this BD */
#define MPC860_IDMA_CTRL_L 0x0800 /* Last buffer of chain */
#define MPC860_IDMA_CTRL_CM 0x0200 /* Continuous mode */
/* IDMA buffer descriptor */
struct mpc860_idma_bd {
m_uint16_t offset; /* Offset in DPRAM memory */
m_uint16_t ctrl; /* Control Word */
m_uint8_t dfcr,sfcr; /* Src/Dst Function code registers */
m_uint32_t buf_len; /* Buffer Length */
m_uint32_t src_bp; /* Source buffer pointer */
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;
vm_obj_t vm_obj;
struct vdevice dev;
struct pci_device *pci_dev;
vm_instance_t *vm;
/* SIU Interrupt Pending Register and Interrupt Mask Register */
m_uint32_t sipend,simask;
/* CPM Interrupt Configuration Register */
m_uint32_t cicr;
/* CPM Interrupt Pending Register and Interrupt Mask Register */
m_uint32_t cipr,cimr;
/* 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];
};
/* Log a MPC message */
#define MPC_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg)
/* ======================================================================== */
/* DPRAM access routines */
static inline m_uint8_t dpram_r8(struct mpc860_data *d,m_uint16_t offset)
{
return(d->dpram[offset]);
}
static inline void dpram_w8(struct mpc860_data *d,m_uint16_t offset,
m_uint8_t val)
{
d->dpram[offset] = val;
}
static inline m_uint16_t dpram_r16(struct mpc860_data *d,m_uint16_t offset)
{
m_uint16_t val;
val = (m_uint16_t)d->dpram[offset] << 8;
val |= d->dpram[offset+1];
return(val);
}
static inline void dpram_w16(struct mpc860_data *d,m_uint16_t offset,
m_uint16_t val)
{
d->dpram[offset] = val >> 8;
d->dpram[offset+1] = val & 0xFF;
}
static inline m_uint32_t dpram_r32(struct mpc860_data *d,m_uint16_t offset)
{
m_uint32_t val;
val = d->dpram[offset] << 24;
val |= d->dpram[offset+1] << 16;
val |= d->dpram[offset+2] << 8;
val |= d->dpram[offset+3];
return(val);
}
static inline void dpram_w32(struct mpc860_data *d,m_uint16_t offset,
m_uint32_t val)
{
d->dpram[offset] = val >> 24;
d->dpram[offset+1] = val >> 16;
d->dpram[offset+2] = val >> 8;
d->dpram[offset+3] = val;
}
/* ======================================================================== */
/* Update interrupt status */
static void mpc860_update_irq_status(struct mpc860_data *d)
{
cpu_ppc_t *cpu = CPU_PPC32(d->vm->boot_cpu);
cpu->irq_pending = d->sipend & d->simask;
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)
{
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,siu_bit);
}
/* ======================================================================== */
/* IDMA */
/* ======================================================================== */
/* Update an IDMA status register */
static int mpc860_idma_update_idsr(struct mpc860_data *d,u_int id)
{
u_int cpm_int;
switch(id) {
case 0:
cpm_int = MPC860_CIPR_IDMA1;
break;
case 1:
cpm_int = MPC860_CIPR_IDMA2;
break;
default:
return(-1);
}
if (d->idsr[id] & d->idmr[id])
d->cipr |= cpm_int;
else
d->cipr &= ~cpm_int;
mpc860_update_cpm_int_status(d);
return(0);
}
/* Process to an IDMA transfer for the specified buffer descriptor */
static void mpc860_idma_transfer(struct mpc860_data *d,
struct mpc860_idma_bd *bd)
{
physmem_dma_transfer(d->vm,bd->src_bp,bd->dst_bp,bd->buf_len);
}
/* Fetch an IDMA descriptor from Dual-Port RAM */
static int mpc860_idma_fetch_bd(struct mpc860_data *d,m_uint16_t bd_addr,
struct mpc860_idma_bd *bd)
{
if ((bd_addr < MPC860_DPRAM_OFFSET) || (bd_addr > MPC860_DPRAM_END))
return(-1);
bd->offset = bd_addr - MPC860_DPRAM_OFFSET;
/* Fetch control word */
bd->ctrl = dpram_r16(d,bd->offset+0x00);
/* Fetch function code registers */
bd->dfcr = dpram_r8(d,bd->offset+0x02);
bd->sfcr = dpram_r8(d,bd->offset+0x03);
/* Fetch buffer length, source and destination addresses */
bd->buf_len = dpram_r32(d,bd->offset+0x04);
bd->src_bp = dpram_r32(d,bd->offset+0x08);
bd->dst_bp = dpram_r32(d,bd->offset+0x0c);
#if DEBUG_IDMA
MPC_LOG(d,"fetched IDMA BD at 0x%4.4x, src_bp=0x%8.8x, dst_bp=0x%8.8x "
"len=%d\n",bd->offset,bd->src_bp,bd->dst_bp,bd->buf_len);
#endif
return(0);
}
/* Start an IDMA channel */
static int mpc860_idma_start_channel(struct mpc860_data *d,u_int id)
{
struct mpc860_idma_bd bd;
m_uint16_t dma_base,ibase,bd_offset;
switch(id) {
case 0:
dma_base = MPC860_IDMA1_BASE;
break;
case 1:
dma_base = MPC860_IDMA2_BASE;
break;
default:
return(-1);
}
/* Get the IBASE register (offset 0) */
ibase = bd_offset = dpram_r16(d,dma_base+0x00);
while(1) {
/* Fetch a descriptor */
if (mpc860_idma_fetch_bd(d,bd_offset,&bd) == -1)
return(-1);
if (!(bd.ctrl & MPC860_IDMA_CTRL_V)) {
d->idsr[id] |= MPC860_IDSR_OB;
break;
}
/* Run the DMA transfer */
mpc860_idma_transfer(d,&bd);
/* Clear the Valid bit */
bd.ctrl &= ~MPC860_IDMA_CTRL_V;
dpram_w16(d,bd_offset-MPC860_DPRAM_OFFSET+0x00,bd.ctrl);
/* Generate an interrupt for this buffer ? */
if (bd.ctrl & MPC860_IDMA_CTRL_I)
d->idsr[id] |= MPC860_IDSR_AD;
/* Stop if this is the last buffer of chain */
if (bd.ctrl & MPC860_IDMA_CTRL_L) {
d->idsr[id] |= MPC860_IDSR_DONE;
break;
}
bd_offset += sizeof(MPC860_IDMA_BD_SIZE);
}
mpc860_idma_update_idsr(d,id);
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;i<MPC860_TXRING_PASS_COUNT;i++)
if (!mpc860_scc_handle_tx_ring_single(d,scc_chan))
break;
return(TRUE);
}
/* Handle RX packet for an SCC channel */
static int mpc860_scc_handle_rx_pkt(netio_desc_t *nio,
u_char *pkt,ssize_t pkt_len,
struct mpc860_data *d,void *arg)
{
struct mpc860_scc_bd rxd0,crxd,*prxd;
struct mpc860_scc_chan *chan;
u_int scc_chan = (u_int)(u_long)arg;
scc_chan_info_t *scc_info;
m_uint16_t bd_offset;
ssize_t clen,tot_len;
u_char *pkt_ptr;
u_int mrblr;
int irq = FALSE;
scc_info = &scc_chan_info[scc_chan];
chan = &d->scc_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 *)(u_long)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;i<MPC860_TXRING_PASS_COUNT;i++)
if (!mpc860_fec_handle_tx_ring_single(d))
break;
return(TRUE);
}
/* Handle RX packet for the Fast Ethernet Controller */
static int mpc860_fec_handle_rx_pkt(netio_desc_t *nio,
u_char *pkt,ssize_t pkt_len,
struct mpc860_data *d,void *arg)
{
n_eth_hdr_t *hdr = (n_eth_hdr_t *)pkt;
struct mpc860_fec_bd rxd0,crxd,*prxd;
ssize_t clen,tot_len;
u_char *pkt_ptr;
if (!d->fec_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()
*/
void *dev_mpc860_access(cpu_gen_t *cpu,struct vdevice *dev,m_uint32_t offset,
u_int op_size,u_int op_type,m_uint64_t *data)
{
struct mpc860_data *d = dev->priv_data;
if (op_type == MTS_READ)
*data = 0x0;
#if DEBUG_ACCESS
if (op_type == MTS_READ) {
cpu_log(cpu,d->name,
"read from offset 0x%x, pc=0x%llx (size=%u)\n",
offset,cpu_get_pc(cpu),op_size);
} else {
cpu_log(cpu,d->name,
"write to offset 0x%x, value=0x%llx, pc=0x%llx (size=%u)\n",
offset,*data,cpu_get_pc(cpu),op_size);
}
#endif
/* Handle dual-port RAM access */
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 MPC860_REG_SWSR:
break;
/* SIU Interrupt Pending Register */
case MPC860_REG_SIPEND:
if (op_type == MTS_READ)
*data = d->sipend;
break;
/* SIU Interrupt Mask Register */
case MPC860_REG_SIMASK:
if (op_type == MTS_READ) {
*data = d->simask;
} else {
d->simask = *data;
mpc860_update_irq_status(d);
}
break;
/*
* Cisco 2600:
* Bit 30: 0=NM in slot 1
*/
case MPC860_REG_PIPR:
if (op_type == MTS_READ)
*data = 0x3F00F600;
break;
/* PISCR - Periodic Interrupt Status and Control Register */
case MPC860_REG_PISCR:
if (op_type == MTS_WRITE) {
if (*data & 0x80) {
d->sipend &= ~0x40000000;
mpc860_update_irq_status(d);
}
}
break;
case MPC860_REG_TBSCR:
if (op_type == MTS_READ)
*data = 0x45;
break;
/* IDMA1 Status and Mask Registers */
case MPC860_REG_IDSR1:
if (op_type == MTS_READ) {
*data = d->idsr[0];
} else {
d->idsr[0] &= ~(*data);
}
break;
case MPC860_REG_IDMR1:
if (op_type == MTS_READ)
*data = d->idmr[0];
else
d->idmr[0] = *data;
break;
/* IDMA2 Status and Mask Registers */
case MPC860_REG_IDSR2:
if (op_type == MTS_READ)
*data = d->idsr[1];
else
d->idsr[1] &= ~(*data);
break;
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 MPC860_REG_CIPR:
if (op_type == MTS_READ)
*data = d->cipr;
else {
d->cipr &= ~(*data);
mpc860_update_cpm_int_status(d);
}
break;
/* CIMR - CPM Interrupt Mask Register */
case MPC860_REG_CIMR:
if (op_type == MTS_READ)
*data = d->cimr;
else {
d->cimr = *data;
mpc860_update_cpm_int_status(d);
}
break;
/* PCSO - Port C Special Options Register */
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;
/* PCDAT - Port C Data Register */
case MPC860_REG_PCDAT:
if (op_type == MTS_WRITE)
d->pcdat = *data;
else
*data = d->pcdat;
break;
/* 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:
if (op_type == MTS_READ) {
cpu_log(cpu,d->name,
"read from unknown addr 0x%x, pc=0x%llx (size=%u)\n",
offset,cpu_get_pc(cpu),op_size);
} else {
cpu_log(cpu,d->name,
"write to addr 0x%x, value=0x%llx, pc=0x%llx (size=%u)\n",
offset,*data,cpu_get_pc(cpu),op_size);
}
#endif
}
return NULL;
}
/* Set IRQ pending status */
void mpc860_set_pending_irq(struct mpc860_data *d,m_uint32_t val)
{
d->sipend |= 1 << val;
mpc860_update_irq_status(d);
}
/* Clear a pending IRQ */
void mpc860_clear_pending_irq(struct mpc860_data *d,m_uint32_t val)
{
d->sipend &= ~(1 << val);
mpc860_update_irq_status(d);
}
/* Shutdown the MPC860 device */
void dev_mpc860_shutdown(vm_instance_t *vm,struct mpc860_data *d)
{
if (d != NULL) {
/* Remove the device */
dev_remove(vm,&d->dev);
/* Free the structure itself */
free(d);
}
}
/* Create the MPC860 device */
int dev_mpc860_init(vm_instance_t *vm,char *name,
m_uint64_t paddr,m_uint32_t len)
{
struct mpc860_data *d;
if (!(d = malloc(sizeof(*d)))) {
fprintf(stderr,"mpc860: unable to create device data.\n");
return(-1);
}
memset(d,0,sizeof(*d));
d->name = name;
d->vm = vm;
vm_object_init(&d->vm_obj);
d->vm_obj.name = name;
d->vm_obj.data = d;
d->vm_obj.shutdown = (vm_shutdown_t)dev_mpc860_shutdown;
dev_init(&d->dev);
d->dev.name = name;
d->dev.priv_data = d;
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);
vm_object_add(vm,&d->vm_obj);
return(0);
}