/**
 * Initialize and start the SPI interface.
 *
 * @param interface The identifier of the packet interface to configure and
 *                  use as a SPI interface.
 * @param mode      The operating mode for the SPI interface. The interface
 *                  can operate as a full duplex (both Tx and Rx data paths
 *                  active) or as a halfplex (either the Tx data path is
 *                  active or the Rx data path is active, but not both).
 * @param timeout   Timeout to wait for clock synchronization in seconds
 * @return Zero on success, negative of failure.
 */
int cvmx_spi_start_interface(int interface, cvmx_spi_mode_t mode, int timeout)
{
    uint64_t                     timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
    cvmx_spxx_trn4_ctl_t         spxx_trn4_ctl;
    cvmx_spxx_clk_stat_t         stat;
    cvmx_stxx_com_ctl_t          stxx_com_ctl;
    cvmx_srxx_com_ctl_t          srxx_com_ctl;
    cvmx_stxx_spi4_dat_t         stxx_spi4_dat;
    uint64_t                     count;
    cvmx_pko_reg_gmx_port_mode_t pko_mode;


    if (!(OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX)))
        return -1;

    cvmx_dprintf ("SPI%d: mode %s, cal_len: %d, cal_rep: %d\n",
            interface, modes[mode], CAL_LEN, CAL_REP);

      // Configure for 16 ports (PKO -> GMX FIFO partition setting)
      // ----------------------------------------------------------
    pko_mode.u64 = cvmx_read_csr(CVMX_PKO_REG_GMX_PORT_MODE);
    if (interface == 0) 
    {
        pko_mode.s.mode0 = 0;
    }
    else 
    {
        pko_mode.s.mode1 = 0;
    }
    cvmx_write_csr(CVMX_PKO_REG_GMX_PORT_MODE, pko_mode.u64);

      // Configure GMX
      // -------------------------------------------------
    cvmx_write_csr (CVMX_GMXX_TX_PRTS(interface), 0xA);
      // PRTS           [ 4: 0] ( 5b) = 10


      // Bringing up Spi4 Interface
      // -------------------------------------------------

      // Reset the Spi4 deskew logic
      // -------------------------------------------------
    cvmx_write_csr (CVMX_SPXX_DBG_DESKEW_CTL(interface), 0x00200000);
      // DLLDIS         [ 0: 0] ( 1b) = 0
      // DLLFRC         [ 1: 1] ( 1b) = 0
      // OFFDLY         [ 7: 2] ( 6b) = 0
      // BITSEL         [12: 8] ( 5b) = 0
      // OFFSET         [17:13] ( 5b) = 0
      // MUX            [18:18] ( 1b) = 0
      // INC            [19:19] ( 1b) = 0
      // DEC            [20:20] ( 1b) = 0
      // CLRDLY         [21:21] ( 1b) = 1    // Forces a reset
    cvmx_wait (100 * MS);

      // Setup the CLKDLY right in the middle
      // -------------------------------------------------
    cvmx_write_csr (CVMX_SPXX_CLK_CTL(interface), 0x00000830);
      // SRXDLCK        [ 0: 0] ( 1b) = 0
      // RCVTRN         [ 1: 1] ( 1b) = 0
      // DRPTRN         [ 2: 2] ( 1b) = 0
      // SNDTRN         [ 3: 3] ( 1b) = 0
      // STATRCV        [ 4: 4] ( 1b) = 1    // Enable status channel Rx
      // STATDRV        [ 5: 5] ( 1b) = 1    // Enable status channel Tx
      // RUNBIST        [ 6: 6] ( 1b) = 0
      // CLKDLY         [11: 7] ( 5b) = 10   // 16 is the middle of the range
      // SRXLCK         [12:12] ( 1b) = 0
      // STXLCK         [13:13] ( 1b) = 0
      // SEETRN         [14:14] ( 1b) = 0
    cvmx_wait (100 * MS);

      // Reset SRX0 DLL
      // -------------------------------------------------
    cvmx_write_csr (CVMX_SPXX_CLK_CTL(interface), 0x00000831);
      // SRXDLCK        [ 0: 0] ( 1b) = 1    // Restart the DLL
      // RCVTRN         [ 1: 1] ( 1b) = 0
      // DRPTRN         [ 2: 2] ( 1b) = 0
      // SNDTRN         [ 3: 3] ( 1b) = 0
      // STATRCV        [ 4: 4] ( 1b) = 1
      // STATDRV        [ 5: 5] ( 1b) = 1
      // RUNBIST        [ 6: 6] ( 1b) = 0
      // CLKDLY         [11: 7] ( 5b) = 10
      // SRXLCK         [12:12] ( 1b) = 0
      // STXLCK         [13:13] ( 1b) = 0
      // SEETRN         [14:14] ( 1b) = 0

      // Waiting for Inf0 Spi4 RX DLL to lock
      // -------------------------------------------------
    cvmx_wait (100 * MS);

      // Enable dynamic alignment
      // -------------------------------------------------
    spxx_trn4_ctl.u64 = 0;
    spxx_trn4_ctl.s.mux_en   = 1;
    spxx_trn4_ctl.s.macro_en = 1;
    spxx_trn4_ctl.s.maxdist  = 16;
    spxx_trn4_ctl.s.jitter   = 1;
    cvmx_write_csr (CVMX_SPXX_TRN4_CTL(interface), spxx_trn4_ctl.u64);
      // MUX_EN         [ 0: 0] ( 1b) = 1
      // MACRO_EN       [ 1: 1] ( 1b) = 1
      // MAXDIST        [ 6: 2] ( 5b) = 16
      // SET_BOOT       [ 7: 7] ( 1b) = 0
      // CLR_BOOT       [ 8: 8] ( 1b) = 1
      // JITTER         [11: 9] ( 3b) = 1
      // TRNTEST        [12:12] ( 1b) = 0
    cvmx_write_csr (CVMX_SPXX_DBG_DESKEW_CTL(interface), 0x0);
      // DLLDIS         [ 0: 0] ( 1b) = 0
      // DLLFRC         [ 1: 1] ( 1b) = 0
      // OFFDLY         [ 7: 2] ( 6b) = 0
      // BITSEL         [12: 8] ( 5b) = 0
      // OFFSET         [17:13] ( 5b) = 0
      // MUX            [18:18] ( 1b) = 0
      // INC            [19:19] ( 1b) = 0
      // DEC            [20:20] ( 1b) = 0
      // CLRDLY         [21:21] ( 1b) = 0

    if (mode & CVMX_SPI_MODE_RX_HALFPLEX) {
          // SRX0 Ports
          // -------------------------------------------------
        cvmx_write_csr (CVMX_SRXX_COM_CTL(interface), 0x00000090);
          // INF_EN         [ 0: 0] ( 1b) = 0
          // ST_EN          [ 3: 3] ( 1b) = 0
          // PRTS           [ 9: 4] ( 6b) = 9

          // SRX0 Calendar Table
          // -------------------------------------------------
        cvmx_write_csr (CVMX_SRXX_SPI4_CALX(0, interface), 0x00013210);
          // PRT0           [ 4: 0] ( 5b) = 0
          // PRT1           [ 9: 5] ( 5b) = 1
          // PRT2           [14:10] ( 5b) = 2
          // PRT3           [19:15] ( 5b) = 3
          // ODDPAR         [20:20] ( 1b) = 1
        cvmx_write_csr (CVMX_SRXX_SPI4_CALX(1, interface), 0x00017654);
          // PRT0           [ 4: 0] ( 5b) = 4
          // PRT1           [ 9: 5] ( 5b) = 5
          // PRT2           [14:10] ( 5b) = 6
          // PRT3           [19:15] ( 5b) = 7
          // ODDPAR         [20:20] ( 1b) = 1
        cvmx_write_csr (CVMX_SRXX_SPI4_CALX(2, interface), 0x00000098);
          // PRT0           [ 4: 0] ( 5b) = 8
          // PRT1           [ 9: 5] ( 5b) = 9
          // PRT2           [14:10] ( 5b) = 0
          // PRT3           [19:15] ( 5b) = 0
          // ODDPAR         [20:20] ( 1b) = 0
        cvmx_write_csr (CVMX_SRXX_SPI4_STAT(interface), (CAL_REP << 8) | CAL_LEN);
          // LEN            [ 7: 0] ( 8b) = a
          // M              [15: 8] ( 8b) = 1
    }

    if (mode & CVMX_SPI_MODE_TX_HALFPLEX) {
          // STX0 Config
          // -------------------------------------------------
        cvmx_write_csr (CVMX_STXX_ARB_CTL(interface), 0x0);
          // IGNTPA         [ 3: 3] ( 1b) = 0
          // MINTRN         [ 5: 5] ( 1b) = 0
        cvmx_write_csr (CVMX_GMXX_TX_SPI_MAX(interface), 0x0408);
          // MAX2           [15: 8] ( 8b) = 4
          // MAX1           [ 7: 0] ( 8b) = 8
        cvmx_write_csr (CVMX_GMXX_TX_SPI_THRESH(interface), 0x4);
          // THRESH         [ 5: 0] ( 6b) = 4
        cvmx_write_csr (CVMX_GMXX_TX_SPI_CTL(interface), 0x0);
          // ENFORCE        [ 2: 2] ( 1b) = 0
          // TPA_CLR        [ 1: 1] ( 1b) = 0
          // CONT_PKT       [ 0: 0] ( 1b) = 0

          // STX0 Training Control
          // -------------------------------------------------
        stxx_spi4_dat.u64 = 0;
	stxx_spi4_dat.s.alpha = 32;    /*Minimum needed by dynamic alignment*/
	stxx_spi4_dat.s.max_t = 0xFFFF;  /*Minimum interval is 0x20*/
        cvmx_write_csr (CVMX_STXX_SPI4_DAT(interface), stxx_spi4_dat.u64);
          // MAX_T          [15: 0] (16b) = 0
          // ALPHA          [31:16] (16b) = 0

          // STX0 Calendar Table
          // -------------------------------------------------
        cvmx_write_csr (CVMX_STXX_SPI4_CALX(0, interface), 0x00013210);
          // PRT0           [ 4: 0] ( 5b) = 0
          // PRT1           [ 9: 5] ( 5b) = 1
          // PRT2           [14:10] ( 5b) = 2
          // PRT3           [19:15] ( 5b) = 3
          // ODDPAR         [20:20] ( 1b) = 1
        cvmx_write_csr (CVMX_STXX_SPI4_CALX(1, interface), 0x00017654);
          // PRT0           [ 4: 0] ( 5b) = 4
          // PRT1           [ 9: 5] ( 5b) = 5
          // PRT2           [14:10] ( 5b) = 6
          // PRT3           [19:15] ( 5b) = 7
          // ODDPAR         [20:20] ( 1b) = 1
        cvmx_write_csr (CVMX_STXX_SPI4_CALX(2, interface), 0x00000098);
          // PRT0           [ 4: 0] ( 5b) = 8
          // PRT1           [ 9: 5] ( 5b) = 9
          // PRT2           [14:10] ( 5b) = 0
          // PRT3           [19:15] ( 5b) = 0
          // ODDPAR         [20:20] ( 1b) = 0
        cvmx_write_csr (CVMX_STXX_SPI4_STAT(interface), (CAL_REP << 8) | CAL_LEN);
          // LEN            [ 7: 0] ( 8b) = a
          // M              [15: 8] ( 8b) = 1
    }

      /* Regardless of operating mode, both Tx and Rx clocks must be present
       * for the SPI interface to operate.
       */
    cvmx_dprintf ("SPI%d: Waiting to see TsClk...\n", interface);
    count = 0;
    timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
    do {  /* Do we see the TsClk transitioning? */
        stat.u64 = cvmx_read_csr (CVMX_SPXX_CLK_STAT(interface));
        count = count + 1;
#ifdef DEBUG
        if ((count % 5000000) == 10) {
	    cvmx_dprintf ("SPI%d: CLK_STAT 0x%016llX\n"
                    "  s4 (%d,%d) d4 (%d,%d)\n",
                    interface, (unsigned long long)stat.u64,
                    stat.s.s4clk0, stat.s.s4clk1,
                    stat.s.d4clk0, stat.s.d4clk1);
        }
#endif
        if (cvmx_get_cycle() > timeout_time)
        {
            cvmx_dprintf ("SPI%d: Timeout\n", interface);
            return -1;
        }
    } while (stat.s.s4clk0 == 0 || stat.s.s4clk1 == 0);

    cvmx_dprintf ("SPI%d: Waiting to see RsClk...\n", interface);
    timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
    do {  /* Do we see the RsClk transitioning? */
        stat.u64 = cvmx_read_csr (CVMX_SPXX_CLK_STAT(interface));
        if (cvmx_get_cycle() > timeout_time)
        {
            cvmx_dprintf ("SPI%d: Timeout\n", interface);
            return -1;
        }
    } while (stat.s.d4clk0 == 0 || stat.s.d4clk1 == 0);


      // SRX0 & STX0 Inf0 Links are configured - begin training
      // -------------------------------------------------
    cvmx_write_csr (CVMX_SPXX_CLK_CTL(interface), 0x0000083f);
      // SRXDLCK        [ 0: 0] ( 1b) = 1
      // RCVTRN         [ 1: 1] ( 1b) = 1
      // DRPTRN         [ 2: 2] ( 1b) = 1    ...was 0
      // SNDTRN         [ 3: 3] ( 1b) = 1
      // STATRCV        [ 4: 4] ( 1b) = 1
      // STATDRV        [ 5: 5] ( 1b) = 1
      // RUNBIST        [ 6: 6] ( 1b) = 0
      // CLKDLY         [11: 7] ( 5b) = 10
      // SRXLCK         [12:12] ( 1b) = 0
      // STXLCK         [13:13] ( 1b) = 0
      // SEETRN         [14:14] ( 1b) = 0
    cvmx_wait (1000 * MS);

      // SRX0 clear the boot bit
      // -------------------------------------------------
    spxx_trn4_ctl.s.clr_boot = 1;
    cvmx_write_csr (CVMX_SPXX_TRN4_CTL(interface), spxx_trn4_ctl.u64);

      // Wait for the training sequence to complete
      // -------------------------------------------------
      // SPX0_CLK_STAT - SPX0_CLK_STAT[SRXTRN] should be 1 (bit8)
    cvmx_dprintf ("SPI%d: Waiting for training\n", interface);
    cvmx_wait (1000 * MS);
    timeout_time = cvmx_get_cycle() + 1000ull * MS * 600;  /* Wait a really long time here */
    do {
        stat.u64 = cvmx_read_csr (CVMX_SPXX_CLK_STAT(interface));
        if (cvmx_get_cycle() > timeout_time)
        {
            cvmx_dprintf ("SPI%d: Timeout\n", interface);
            return -1;
        }
    } while (stat.s.srxtrn == 0);

    if (mode & CVMX_SPI_MODE_RX_HALFPLEX) {
          // SRX0 interface should be good, send calendar data
          // -------------------------------------------------
        cvmx_dprintf ("SPI%d: Rx is synchronized, start sending calendar data\n", interface);
        srxx_com_ctl.u64 = 0;
        srxx_com_ctl.s.prts   = 9;
        srxx_com_ctl.s.inf_en = 1;
        srxx_com_ctl.s.st_en  = 1;
        cvmx_write_csr (CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64);
    }

    if (mode & CVMX_SPI_MODE_TX_HALFPLEX) {
          // STX0 has achieved sync
          // The corespondant board should be sending calendar data
          // Enable the STX0 STAT receiver.
          // -------------------------------------------------
        stxx_com_ctl.u64 = 0;
        stxx_com_ctl.s.inf_en = 1;
        stxx_com_ctl.s.st_en = 1;
        cvmx_write_csr (CVMX_STXX_COM_CTL(interface), stxx_com_ctl.u64);

          // Waiting for calendar sync on STX0 STAT
          // -------------------------------------------------
          // SPX0_CLK_STAT - SPX0_CLK_STAT[STXCAL] should be 1 (bit10)
        cvmx_dprintf ("SPI%d: Waiting to sync on STX[%d] STAT\n", interface, interface);
        timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
        do {
            stat.u64 = cvmx_read_csr (CVMX_SPXX_CLK_STAT (interface));
            if (cvmx_get_cycle() > timeout_time)
            {
                cvmx_dprintf ("SPI%d: Timeout\n", interface);
                return -1;
            }
        } while (stat.s.stxcal == 0);
    }

      // Inf0 is synched
      // -------------------------------------------------
      // SPX0 is up
    if (mode & CVMX_SPI_MODE_RX_HALFPLEX) {
        srxx_com_ctl.s.inf_en = 1;
        cvmx_write_csr (CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64);
        cvmx_dprintf ("SPI%d: Rx is now up\n", interface);
    }

    if (mode & CVMX_SPI_MODE_TX_HALFPLEX) {
        stxx_com_ctl.s.inf_en = 1;
        cvmx_write_csr (CVMX_STXX_COM_CTL(interface), stxx_com_ctl.u64);
        cvmx_dprintf ("SPI%d: Tx is now up\n", interface);
    }

    cvmx_write_csr (CVMX_GMXX_RXX_FRM_MIN (0,interface), 40);
    cvmx_write_csr (CVMX_GMXX_RXX_FRM_MAX (0,interface), 64*1024 - 4);
    cvmx_write_csr (CVMX_GMXX_RXX_JABBER  (0,interface), 64*1024 - 4);

    return 0;
}
예제 #2
0
/**
 * Callback to setup calendar and miscellaneous settings before clock detection
 *
 * @interface: The identifier of the packet interface to configure and
 *                  use as a SPI interface.
 * @mode:      The operating mode for the SPI interface. The interface
 *                  can operate as a full duplex (both Tx and Rx data paths
 *                  active) or as a halfplex (either the Tx data path is
 *                  active or the Rx data path is active, but not both).
 * @num_ports: Number of ports to configure on SPI
 *
 * Returns Zero on success, non-zero error code on failure (will cause
 * SPI initialization to abort)
 */
int cvmx_spi_calendar_setup_cb(int interface, cvmx_spi_mode_t mode,
			       int num_ports)
{
	int port;
	int index;
	if (mode & CVMX_SPI_MODE_RX_HALFPLEX) {
		union cvmx_srxx_com_ctl srxx_com_ctl;
		union cvmx_srxx_spi4_stat srxx_spi4_stat;

		/* SRX0 number of Ports */
		srxx_com_ctl.u64 = 0;
		srxx_com_ctl.s.prts = num_ports - 1;
		srxx_com_ctl.s.st_en = 0;
		srxx_com_ctl.s.inf_en = 0;
		cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64);

		/* SRX0 Calendar Table. This round robbins through all ports */
		port = 0;
		index = 0;
		while (port < num_ports) {
			union cvmx_srxx_spi4_calx srxx_spi4_calx;
			srxx_spi4_calx.u64 = 0;
			srxx_spi4_calx.s.prt0 = port++;
			srxx_spi4_calx.s.prt1 = port++;
			srxx_spi4_calx.s.prt2 = port++;
			srxx_spi4_calx.s.prt3 = port++;
			srxx_spi4_calx.s.oddpar =
			    ~(cvmx_dpop(srxx_spi4_calx.u64) & 1);
			cvmx_write_csr(CVMX_SRXX_SPI4_CALX(index, interface),
				       srxx_spi4_calx.u64);
			index++;
		}
		srxx_spi4_stat.u64 = 0;
		srxx_spi4_stat.s.len = num_ports;
		srxx_spi4_stat.s.m = 1;
		cvmx_write_csr(CVMX_SRXX_SPI4_STAT(interface),
			       srxx_spi4_stat.u64);
	}

	if (mode & CVMX_SPI_MODE_TX_HALFPLEX) {
		union cvmx_stxx_arb_ctl stxx_arb_ctl;
		union cvmx_gmxx_tx_spi_max gmxx_tx_spi_max;
		union cvmx_gmxx_tx_spi_thresh gmxx_tx_spi_thresh;
		union cvmx_gmxx_tx_spi_ctl gmxx_tx_spi_ctl;
		union cvmx_stxx_spi4_stat stxx_spi4_stat;
		union cvmx_stxx_spi4_dat stxx_spi4_dat;

		/* STX0 Config */
		stxx_arb_ctl.u64 = 0;
		stxx_arb_ctl.s.igntpa = 0;
		stxx_arb_ctl.s.mintrn = 0;
		cvmx_write_csr(CVMX_STXX_ARB_CTL(interface), stxx_arb_ctl.u64);

		gmxx_tx_spi_max.u64 = 0;
		gmxx_tx_spi_max.s.max1 = 8;
		gmxx_tx_spi_max.s.max2 = 4;
		gmxx_tx_spi_max.s.slice = 0;
		cvmx_write_csr(CVMX_GMXX_TX_SPI_MAX(interface),
			       gmxx_tx_spi_max.u64);

		gmxx_tx_spi_thresh.u64 = 0;
		gmxx_tx_spi_thresh.s.thresh = 4;
		cvmx_write_csr(CVMX_GMXX_TX_SPI_THRESH(interface),
			       gmxx_tx_spi_thresh.u64);

		gmxx_tx_spi_ctl.u64 = 0;
		gmxx_tx_spi_ctl.s.tpa_clr = 0;
		gmxx_tx_spi_ctl.s.cont_pkt = 0;
		cvmx_write_csr(CVMX_GMXX_TX_SPI_CTL(interface),
			       gmxx_tx_spi_ctl.u64);

		/* STX0 Training Control */
		stxx_spi4_dat.u64 = 0;
		/*Minimum needed by dynamic alignment */
		stxx_spi4_dat.s.alpha = 32;
		stxx_spi4_dat.s.max_t = 0xFFFF;	/*Minimum interval is 0x20 */
		cvmx_write_csr(CVMX_STXX_SPI4_DAT(interface),
			       stxx_spi4_dat.u64);

		/* STX0 Calendar Table. This round robbins through all ports */
		port = 0;
		index = 0;
		while (port < num_ports) {
			union cvmx_stxx_spi4_calx stxx_spi4_calx;
			stxx_spi4_calx.u64 = 0;
			stxx_spi4_calx.s.prt0 = port++;
			stxx_spi4_calx.s.prt1 = port++;
			stxx_spi4_calx.s.prt2 = port++;
			stxx_spi4_calx.s.prt3 = port++;
			stxx_spi4_calx.s.oddpar =
			    ~(cvmx_dpop(stxx_spi4_calx.u64) & 1);
			cvmx_write_csr(CVMX_STXX_SPI4_CALX(index, interface),
				       stxx_spi4_calx.u64);
			index++;
		}
		stxx_spi4_stat.u64 = 0;
		stxx_spi4_stat.s.len = num_ports;
		stxx_spi4_stat.s.m = 1;
		cvmx_write_csr(CVMX_STXX_SPI4_STAT(interface),
			       stxx_spi4_stat.u64);
	}

	return 0;
}
int cvmx_spi_calendar_setup_cb(int interface, cvmx_spi_mode_t mode,
                               int num_ports)
{
    int port;
    int index;
    if (mode & CVMX_SPI_MODE_RX_HALFPLEX) {
        union cvmx_srxx_com_ctl srxx_com_ctl;
        union cvmx_srxx_spi4_stat srxx_spi4_stat;


        srxx_com_ctl.u64 = 0;
        srxx_com_ctl.s.prts = num_ports - 1;
        srxx_com_ctl.s.st_en = 0;
        srxx_com_ctl.s.inf_en = 0;
        cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64);


        port = 0;
        index = 0;
        while (port < num_ports) {
            union cvmx_srxx_spi4_calx srxx_spi4_calx;
            srxx_spi4_calx.u64 = 0;
            srxx_spi4_calx.s.prt0 = port++;
            srxx_spi4_calx.s.prt1 = port++;
            srxx_spi4_calx.s.prt2 = port++;
            srxx_spi4_calx.s.prt3 = port++;
            srxx_spi4_calx.s.oddpar =
                ~(cvmx_dpop(srxx_spi4_calx.u64) & 1);
            cvmx_write_csr(CVMX_SRXX_SPI4_CALX(index, interface),
                           srxx_spi4_calx.u64);
            index++;
        }
        srxx_spi4_stat.u64 = 0;
        srxx_spi4_stat.s.len = num_ports;
        srxx_spi4_stat.s.m = 1;
        cvmx_write_csr(CVMX_SRXX_SPI4_STAT(interface),
                       srxx_spi4_stat.u64);
    }

    if (mode & CVMX_SPI_MODE_TX_HALFPLEX) {
        union cvmx_stxx_arb_ctl stxx_arb_ctl;
        union cvmx_gmxx_tx_spi_max gmxx_tx_spi_max;
        union cvmx_gmxx_tx_spi_thresh gmxx_tx_spi_thresh;
        union cvmx_gmxx_tx_spi_ctl gmxx_tx_spi_ctl;
        union cvmx_stxx_spi4_stat stxx_spi4_stat;
        union cvmx_stxx_spi4_dat stxx_spi4_dat;


        stxx_arb_ctl.u64 = 0;
        stxx_arb_ctl.s.igntpa = 0;
        stxx_arb_ctl.s.mintrn = 0;
        cvmx_write_csr(CVMX_STXX_ARB_CTL(interface), stxx_arb_ctl.u64);

        gmxx_tx_spi_max.u64 = 0;
        gmxx_tx_spi_max.s.max1 = 8;
        gmxx_tx_spi_max.s.max2 = 4;
        gmxx_tx_spi_max.s.slice = 0;
        cvmx_write_csr(CVMX_GMXX_TX_SPI_MAX(interface),
                       gmxx_tx_spi_max.u64);

        gmxx_tx_spi_thresh.u64 = 0;
        gmxx_tx_spi_thresh.s.thresh = 4;
        cvmx_write_csr(CVMX_GMXX_TX_SPI_THRESH(interface),
                       gmxx_tx_spi_thresh.u64);

        gmxx_tx_spi_ctl.u64 = 0;
        gmxx_tx_spi_ctl.s.tpa_clr = 0;
        gmxx_tx_spi_ctl.s.cont_pkt = 0;
        cvmx_write_csr(CVMX_GMXX_TX_SPI_CTL(interface),
                       gmxx_tx_spi_ctl.u64);


        stxx_spi4_dat.u64 = 0;

        stxx_spi4_dat.s.alpha = 32;
        stxx_spi4_dat.s.max_t = 0xFFFF;
        cvmx_write_csr(CVMX_STXX_SPI4_DAT(interface),
                       stxx_spi4_dat.u64);


        port = 0;
        index = 0;
        while (port < num_ports) {
            union cvmx_stxx_spi4_calx stxx_spi4_calx;
            stxx_spi4_calx.u64 = 0;
            stxx_spi4_calx.s.prt0 = port++;
            stxx_spi4_calx.s.prt1 = port++;
            stxx_spi4_calx.s.prt2 = port++;
            stxx_spi4_calx.s.prt3 = port++;
            stxx_spi4_calx.s.oddpar =
                ~(cvmx_dpop(stxx_spi4_calx.u64) & 1);
            cvmx_write_csr(CVMX_STXX_SPI4_CALX(index, interface),
                           stxx_spi4_calx.u64);
            index++;
        }
        stxx_spi4_stat.u64 = 0;
        stxx_spi4_stat.s.len = num_ports;
        stxx_spi4_stat.s.m = 1;
        cvmx_write_csr(CVMX_STXX_SPI4_STAT(interface),
                       stxx_spi4_stat.u64);
    }

    return 0;
}