162306a36Sopenharmony_ci/***********************license start***************
262306a36Sopenharmony_ci * Author: Cavium Networks
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Contact: support@caviumnetworks.com
562306a36Sopenharmony_ci * This file is part of the OCTEON SDK
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 2003-2018 Cavium, Inc.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * This file is free software; you can redistribute it and/or modify
1062306a36Sopenharmony_ci * it under the terms of the GNU General Public License, Version 2, as
1162306a36Sopenharmony_ci * published by the Free Software Foundation.
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * This file is distributed in the hope that it will be useful, but
1462306a36Sopenharmony_ci * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
1562306a36Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
1662306a36Sopenharmony_ci * NONINFRINGEMENT.  See the GNU General Public License for more
1762306a36Sopenharmony_ci * details.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * You should have received a copy of the GNU General Public License
2062306a36Sopenharmony_ci * along with this file; if not, write to the Free Software
2162306a36Sopenharmony_ci * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2262306a36Sopenharmony_ci * or visit http://www.gnu.org/licenses/.
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * This file may also be available under a different license from Cavium.
2562306a36Sopenharmony_ci * Contact Cavium Networks for more information
2662306a36Sopenharmony_ci ***********************license end**************************************/
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/*
2962306a36Sopenharmony_ci * Functions for SPI initialization, configuration,
3062306a36Sopenharmony_ci * and monitoring.
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ci#include <asm/octeon/octeon.h>
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include <asm/octeon/cvmx-config.h>
3562306a36Sopenharmony_ci#include <asm/octeon/cvmx-spi.h>
3662306a36Sopenharmony_ci#include <asm/octeon/cvmx-helper.h>
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#include <asm/octeon/cvmx-pip-defs.h>
3962306a36Sopenharmony_ci#include <asm/octeon/cvmx-pko-defs.h>
4062306a36Sopenharmony_ci#include <asm/octeon/cvmx-spxx-defs.h>
4162306a36Sopenharmony_ci#include <asm/octeon/cvmx-stxx-defs.h>
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/*
4462306a36Sopenharmony_ci * CVMX_HELPER_SPI_TIMEOUT is used to determine how long the SPI
4562306a36Sopenharmony_ci * initialization routines wait for SPI training. You can override the
4662306a36Sopenharmony_ci * value using executive-config.h if necessary.
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_ci#ifndef CVMX_HELPER_SPI_TIMEOUT
4962306a36Sopenharmony_ci#define CVMX_HELPER_SPI_TIMEOUT 10
5062306a36Sopenharmony_ci#endif
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ciint __cvmx_helper_spi_enumerate(int interface)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	if ((cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) &&
5562306a36Sopenharmony_ci	    cvmx_spi4000_is_present(interface)) {
5662306a36Sopenharmony_ci		return 10;
5762306a36Sopenharmony_ci	} else {
5862306a36Sopenharmony_ci		return 16;
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/**
6362306a36Sopenharmony_ci * Probe a SPI interface and determine the number of ports
6462306a36Sopenharmony_ci * connected to it. The SPI interface should still be down after
6562306a36Sopenharmony_ci * this call.
6662306a36Sopenharmony_ci *
6762306a36Sopenharmony_ci * @interface: Interface to probe
6862306a36Sopenharmony_ci *
6962306a36Sopenharmony_ci * Returns Number of ports on the interface. Zero to disable.
7062306a36Sopenharmony_ci */
7162306a36Sopenharmony_ciint __cvmx_helper_spi_probe(int interface)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	int num_ports = 0;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if ((cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) &&
7662306a36Sopenharmony_ci	    cvmx_spi4000_is_present(interface)) {
7762306a36Sopenharmony_ci		num_ports = 10;
7862306a36Sopenharmony_ci	} else {
7962306a36Sopenharmony_ci		union cvmx_pko_reg_crc_enable enable;
8062306a36Sopenharmony_ci		num_ports = 16;
8162306a36Sopenharmony_ci		/*
8262306a36Sopenharmony_ci		 * Unlike the SPI4000, most SPI devices don't
8362306a36Sopenharmony_ci		 * automatically put on the L2 CRC. For everything
8462306a36Sopenharmony_ci		 * except for the SPI4000 have PKO append the L2 CRC
8562306a36Sopenharmony_ci		 * to the packet.
8662306a36Sopenharmony_ci		 */
8762306a36Sopenharmony_ci		enable.u64 = cvmx_read_csr(CVMX_PKO_REG_CRC_ENABLE);
8862306a36Sopenharmony_ci		enable.s.enable |= 0xffff << (interface * 16);
8962306a36Sopenharmony_ci		cvmx_write_csr(CVMX_PKO_REG_CRC_ENABLE, enable.u64);
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci	__cvmx_helper_setup_gmx(interface, num_ports);
9262306a36Sopenharmony_ci	return num_ports;
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci/**
9662306a36Sopenharmony_ci * Bringup and enable a SPI interface. After this call packet I/O
9762306a36Sopenharmony_ci * should be fully functional. This is called with IPD enabled but
9862306a36Sopenharmony_ci * PKO disabled.
9962306a36Sopenharmony_ci *
10062306a36Sopenharmony_ci * @interface: Interface to bring up
10162306a36Sopenharmony_ci *
10262306a36Sopenharmony_ci * Returns Zero on success, negative on failure
10362306a36Sopenharmony_ci */
10462306a36Sopenharmony_ciint __cvmx_helper_spi_enable(int interface)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	/*
10762306a36Sopenharmony_ci	 * Normally the ethernet L2 CRC is checked and stripped in the
10862306a36Sopenharmony_ci	 * GMX block.  When you are using SPI, this isn' the case and
10962306a36Sopenharmony_ci	 * IPD needs to check the L2 CRC.
11062306a36Sopenharmony_ci	 */
11162306a36Sopenharmony_ci	int num_ports = cvmx_helper_ports_on_interface(interface);
11262306a36Sopenharmony_ci	int ipd_port;
11362306a36Sopenharmony_ci	for (ipd_port = interface * 16; ipd_port < interface * 16 + num_ports;
11462306a36Sopenharmony_ci	     ipd_port++) {
11562306a36Sopenharmony_ci		union cvmx_pip_prt_cfgx port_config;
11662306a36Sopenharmony_ci		port_config.u64 = cvmx_read_csr(CVMX_PIP_PRT_CFGX(ipd_port));
11762306a36Sopenharmony_ci		port_config.s.crc_en = 1;
11862306a36Sopenharmony_ci		cvmx_write_csr(CVMX_PIP_PRT_CFGX(ipd_port), port_config.u64);
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) {
12262306a36Sopenharmony_ci		cvmx_spi_start_interface(interface, CVMX_SPI_MODE_DUPLEX,
12362306a36Sopenharmony_ci					 CVMX_HELPER_SPI_TIMEOUT, num_ports);
12462306a36Sopenharmony_ci		if (cvmx_spi4000_is_present(interface))
12562306a36Sopenharmony_ci			cvmx_spi4000_initialize(interface);
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci	__cvmx_interrupt_spxx_int_msk_enable(interface);
12862306a36Sopenharmony_ci	__cvmx_interrupt_stxx_int_msk_enable(interface);
12962306a36Sopenharmony_ci	__cvmx_interrupt_gmxx_enable(interface);
13062306a36Sopenharmony_ci	return 0;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci/**
13462306a36Sopenharmony_ci * Return the link state of an IPD/PKO port as returned by
13562306a36Sopenharmony_ci * auto negotiation. The result of this function may not match
13662306a36Sopenharmony_ci * Octeon's link config if auto negotiation has changed since
13762306a36Sopenharmony_ci * the last call to cvmx_helper_link_set().
13862306a36Sopenharmony_ci *
13962306a36Sopenharmony_ci * @ipd_port: IPD/PKO port to query
14062306a36Sopenharmony_ci *
14162306a36Sopenharmony_ci * Returns Link state
14262306a36Sopenharmony_ci */
14362306a36Sopenharmony_ciunion cvmx_helper_link_info __cvmx_helper_spi_link_get(int ipd_port)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	union cvmx_helper_link_info result;
14662306a36Sopenharmony_ci	int interface = cvmx_helper_get_interface_num(ipd_port);
14762306a36Sopenharmony_ci	int index = cvmx_helper_get_interface_index_num(ipd_port);
14862306a36Sopenharmony_ci	result.u64 = 0;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM) {
15162306a36Sopenharmony_ci		/* The simulator gives you a simulated full duplex link */
15262306a36Sopenharmony_ci		result.s.link_up = 1;
15362306a36Sopenharmony_ci		result.s.full_duplex = 1;
15462306a36Sopenharmony_ci		result.s.speed = 10000;
15562306a36Sopenharmony_ci	} else if (cvmx_spi4000_is_present(interface)) {
15662306a36Sopenharmony_ci		union cvmx_gmxx_rxx_rx_inbnd inband =
15762306a36Sopenharmony_ci		    cvmx_spi4000_check_speed(interface, index);
15862306a36Sopenharmony_ci		result.s.link_up = inband.s.status;
15962306a36Sopenharmony_ci		result.s.full_duplex = inband.s.duplex;
16062306a36Sopenharmony_ci		switch (inband.s.speed) {
16162306a36Sopenharmony_ci		case 0: /* 10 Mbps */
16262306a36Sopenharmony_ci			result.s.speed = 10;
16362306a36Sopenharmony_ci			break;
16462306a36Sopenharmony_ci		case 1: /* 100 Mbps */
16562306a36Sopenharmony_ci			result.s.speed = 100;
16662306a36Sopenharmony_ci			break;
16762306a36Sopenharmony_ci		case 2: /* 1 Gbps */
16862306a36Sopenharmony_ci			result.s.speed = 1000;
16962306a36Sopenharmony_ci			break;
17062306a36Sopenharmony_ci		case 3: /* Illegal */
17162306a36Sopenharmony_ci			result.s.speed = 0;
17262306a36Sopenharmony_ci			result.s.link_up = 0;
17362306a36Sopenharmony_ci			break;
17462306a36Sopenharmony_ci		}
17562306a36Sopenharmony_ci	} else {
17662306a36Sopenharmony_ci		/* For generic SPI we can't determine the link, just return some
17762306a36Sopenharmony_ci		   sane results */
17862306a36Sopenharmony_ci		result.s.link_up = 1;
17962306a36Sopenharmony_ci		result.s.full_duplex = 1;
18062306a36Sopenharmony_ci		result.s.speed = 10000;
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci	return result;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci/**
18662306a36Sopenharmony_ci * Configure an IPD/PKO port for the specified link state. This
18762306a36Sopenharmony_ci * function does not influence auto negotiation at the PHY level.
18862306a36Sopenharmony_ci * The passed link state must always match the link state returned
18962306a36Sopenharmony_ci * by cvmx_helper_link_get().
19062306a36Sopenharmony_ci *
19162306a36Sopenharmony_ci * @ipd_port:  IPD/PKO port to configure
19262306a36Sopenharmony_ci * @link_info: The new link state
19362306a36Sopenharmony_ci *
19462306a36Sopenharmony_ci * Returns Zero on success, negative on failure
19562306a36Sopenharmony_ci */
19662306a36Sopenharmony_ciint __cvmx_helper_spi_link_set(int ipd_port, union cvmx_helper_link_info link_info)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	/* Nothing to do. If we have a SPI4000 then the setup was already performed
19962306a36Sopenharmony_ci	   by cvmx_spi4000_check_speed(). If not then there isn't any link
20062306a36Sopenharmony_ci	   info */
20162306a36Sopenharmony_ci	return 0;
20262306a36Sopenharmony_ci}
203