mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-31 05:54:26 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			2023 lines
		
	
	
		
			56 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			2023 lines
		
	
	
		
			56 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| Signed-off-by: David Daney <ddaney@caviumnetworks.com>
 | |
| ---
 | |
|  arch/mips/include/asm/octeon/cvmx-mdio.h |  577 +++++++++++++++++++++
 | |
|  drivers/net/Kconfig                      |    8 +
 | |
|  drivers/net/Makefile                     |    1 +
 | |
|  drivers/net/octeon/Makefile              |   11 +
 | |
|  drivers/net/octeon/cvmx-mgmt-port.c      |  818 ++++++++++++++++++++++++++++++
 | |
|  drivers/net/octeon/cvmx-mgmt-port.h      |  168 ++++++
 | |
|  drivers/net/octeon/octeon-mgmt-port.c    |  389 ++++++++++++++
 | |
|  7 files changed, 1972 insertions(+), 0 deletions(-)
 | |
|  create mode 100644 arch/mips/include/asm/octeon/cvmx-mdio.h
 | |
|  create mode 100644 drivers/net/octeon/Makefile
 | |
|  create mode 100644 drivers/net/octeon/cvmx-mgmt-port.c
 | |
|  create mode 100644 drivers/net/octeon/cvmx-mgmt-port.h
 | |
|  create mode 100644 drivers/net/octeon/octeon-mgmt-port.c
 | |
| 
 | |
| --- /dev/null
 | |
| +++ b/arch/mips/include/asm/octeon/cvmx-mdio.h
 | |
| @@ -0,0 +1,577 @@
 | |
| +/***********************license start***************
 | |
| + * Author: Cavium Networks
 | |
| + *
 | |
| + * Contact: support@caviumnetworks.com
 | |
| + * This file is part of the OCTEON SDK
 | |
| + *
 | |
| + * Copyright (c) 2003-2008 Cavium Networks
 | |
| + *
 | |
| + * This file is free software; you can redistribute it and/or modify
 | |
| + * it under the terms of the GNU General Public License, Version 2, as
 | |
| + * published by the Free Software Foundation.
 | |
| + *
 | |
| + * This file is distributed in the hope that it will be useful, but
 | |
| + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
 | |
| + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
 | |
| + * NONINFRINGEMENT.  See the GNU General Public License for more
 | |
| + * details.
 | |
| + *
 | |
| + * You should have received a copy of the GNU General Public License
 | |
| + * along with this file; if not, write to the Free Software
 | |
| + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 | |
| + * or visit http://www.gnu.org/licenses/.
 | |
| + *
 | |
| + * This file may also be available under a different license from Cavium.
 | |
| + * Contact Cavium Networks for more information
 | |
| + ***********************license end**************************************/
 | |
| +
 | |
| +/**
 | |
| + *
 | |
| + * Interface to the SMI/MDIO hardware, including support for both IEEE 802.3
 | |
| + * clause 22 and clause 45 operations.
 | |
| + *
 | |
| + */
 | |
| +
 | |
| +#ifndef __CVMX_MIO_H__
 | |
| +#define __CVMX_MIO_H__
 | |
| +
 | |
| +#include "cvmx-smix-defs.h"
 | |
| +
 | |
| +/**
 | |
| + * PHY register 0 from the 802.3 spec
 | |
| + */
 | |
| +#define CVMX_MDIO_PHY_REG_CONTROL 0
 | |
| +union cvmx_mdio_phy_reg_control {
 | |
| +	uint16_t u16;
 | |
| +	struct {
 | |
| +		uint16_t reset:1;
 | |
| +		uint16_t loopback:1;
 | |
| +		uint16_t speed_lsb:1;
 | |
| +		uint16_t autoneg_enable:1;
 | |
| +		uint16_t power_down:1;
 | |
| +		uint16_t isolate:1;
 | |
| +		uint16_t restart_autoneg:1;
 | |
| +		uint16_t duplex:1;
 | |
| +		uint16_t collision_test:1;
 | |
| +		uint16_t speed_msb:1;
 | |
| +		uint16_t unidirectional_enable:1;
 | |
| +		uint16_t reserved_0_4:5;
 | |
| +	} s;
 | |
| +};
 | |
| +
 | |
| +/**
 | |
| + * PHY register 1 from the 802.3 spec
 | |
| + */
 | |
| +#define CVMX_MDIO_PHY_REG_STATUS 1
 | |
| +union cvmx_mdio_phy_reg_status {
 | |
| +	uint16_t u16;
 | |
| +	struct {
 | |
| +		uint16_t capable_100base_t4:1;
 | |
| +		uint16_t capable_100base_x_full:1;
 | |
| +		uint16_t capable_100base_x_half:1;
 | |
| +		uint16_t capable_10_full:1;
 | |
| +		uint16_t capable_10_half:1;
 | |
| +		uint16_t capable_100base_t2_full:1;
 | |
| +		uint16_t capable_100base_t2_half:1;
 | |
| +		uint16_t capable_extended_status:1;
 | |
| +		uint16_t capable_unidirectional:1;
 | |
| +		uint16_t capable_mf_preamble_suppression:1;
 | |
| +		uint16_t autoneg_complete:1;
 | |
| +		uint16_t remote_fault:1;
 | |
| +		uint16_t capable_autoneg:1;
 | |
| +		uint16_t link_status:1;
 | |
| +		uint16_t jabber_detect:1;
 | |
| +		uint16_t capable_extended_registers:1;
 | |
| +
 | |
| +	} s;
 | |
| +};
 | |
| +
 | |
| +/**
 | |
| + * PHY register 2 from the 802.3 spec
 | |
| + */
 | |
| +#define CVMX_MDIO_PHY_REG_ID1 2
 | |
| +union cvmx_mdio_phy_reg_id1 {
 | |
| +	uint16_t u16;
 | |
| +	struct {
 | |
| +		uint16_t oui_bits_3_18;
 | |
| +	} s;
 | |
| +};
 | |
| +
 | |
| +/**
 | |
| + * PHY register 3 from the 802.3 spec
 | |
| + */
 | |
| +#define CVMX_MDIO_PHY_REG_ID2 3
 | |
| +union cvmx_mdio_phy_reg_id2 {
 | |
| +	uint16_t u16;
 | |
| +	struct {
 | |
| +		uint16_t oui_bits_19_24:6;
 | |
| +		uint16_t model:6;
 | |
| +		uint16_t revision:4;
 | |
| +	} s;
 | |
| +};
 | |
| +
 | |
| +/**
 | |
| + * PHY register 4 from the 802.3 spec
 | |
| + */
 | |
| +#define CVMX_MDIO_PHY_REG_AUTONEG_ADVER 4
 | |
| +union cvmx_mdio_phy_reg_autoneg_adver {
 | |
| +	uint16_t u16;
 | |
| +	struct {
 | |
| +		uint16_t next_page:1;
 | |
| +		uint16_t reserved_14:1;
 | |
| +		uint16_t remote_fault:1;
 | |
| +		uint16_t reserved_12:1;
 | |
| +		uint16_t asymmetric_pause:1;
 | |
| +		uint16_t pause:1;
 | |
| +		uint16_t advert_100base_t4:1;
 | |
| +		uint16_t advert_100base_tx_full:1;
 | |
| +		uint16_t advert_100base_tx_half:1;
 | |
| +		uint16_t advert_10base_tx_full:1;
 | |
| +		uint16_t advert_10base_tx_half:1;
 | |
| +		uint16_t selector:5;
 | |
| +	} s;
 | |
| +};
 | |
| +
 | |
| +/**
 | |
| + * PHY register 5 from the 802.3 spec
 | |
| + */
 | |
| +#define CVMX_MDIO_PHY_REG_LINK_PARTNER_ABILITY 5
 | |
| +union cvmx_mdio_phy_reg_link_partner_ability {
 | |
| +	uint16_t u16;
 | |
| +	struct {
 | |
| +		uint16_t next_page:1;
 | |
| +		uint16_t ack:1;
 | |
| +		uint16_t remote_fault:1;
 | |
| +		uint16_t reserved_12:1;
 | |
| +		uint16_t asymmetric_pause:1;
 | |
| +		uint16_t pause:1;
 | |
| +		uint16_t advert_100base_t4:1;
 | |
| +		uint16_t advert_100base_tx_full:1;
 | |
| +		uint16_t advert_100base_tx_half:1;
 | |
| +		uint16_t advert_10base_tx_full:1;
 | |
| +		uint16_t advert_10base_tx_half:1;
 | |
| +		uint16_t selector:5;
 | |
| +	} s;
 | |
| +};
 | |
| +
 | |
| +/**
 | |
| + * PHY register 6 from the 802.3 spec
 | |
| + */
 | |
| +#define CVMX_MDIO_PHY_REG_AUTONEG_EXPANSION 6
 | |
| +union cvmx_mdio_phy_reg_autoneg_expansion {
 | |
| +	uint16_t u16;
 | |
| +	struct {
 | |
| +		uint16_t reserved_5_15:11;
 | |
| +		uint16_t parallel_detection_fault:1;
 | |
| +		uint16_t link_partner_next_page_capable:1;
 | |
| +		uint16_t local_next_page_capable:1;
 | |
| +		uint16_t page_received:1;
 | |
| +		uint16_t link_partner_autoneg_capable:1;
 | |
| +
 | |
| +	} s;
 | |
| +};
 | |
| +
 | |
| +/**
 | |
| + * PHY register 9 from the 802.3 spec
 | |
| + */
 | |
| +#define CVMX_MDIO_PHY_REG_CONTROL_1000 9
 | |
| +union cvmx_mdio_phy_reg_control_1000 {
 | |
| +	uint16_t u16;
 | |
| +	struct {
 | |
| +		uint16_t test_mode:3;
 | |
| +		uint16_t manual_master_slave:1;
 | |
| +		uint16_t master:1;
 | |
| +		uint16_t port_type:1;
 | |
| +		uint16_t advert_1000base_t_full:1;
 | |
| +		uint16_t advert_1000base_t_half:1;
 | |
| +		uint16_t reserved_0_7:8;
 | |
| +	} s;
 | |
| +};
 | |
| +
 | |
| +/**
 | |
| + * PHY register 10 from the 802.3 spec
 | |
| + */
 | |
| +#define CVMX_MDIO_PHY_REG_STATUS_1000 10
 | |
| +union cvmx_mdio_phy_reg_status_1000 {
 | |
| +	uint16_t u16;
 | |
| +	struct {
 | |
| +		uint16_t master_slave_fault:1;
 | |
| +		uint16_t is_master:1;
 | |
| +		uint16_t local_receiver_ok:1;
 | |
| +		uint16_t remote_receiver_ok:1;
 | |
| +		uint16_t remote_capable_1000base_t_full:1;
 | |
| +		uint16_t remote_capable_1000base_t_half:1;
 | |
| +		uint16_t reserved_8_9:2;
 | |
| +		uint16_t idle_error_count:8;
 | |
| +	} s;
 | |
| +};
 | |
| +
 | |
| +/**
 | |
| + * PHY register 15 from the 802.3 spec
 | |
| + */
 | |
| +#define CVMX_MDIO_PHY_REG_EXTENDED_STATUS 15
 | |
| +union cvmx_mdio_phy_reg_extended_status {
 | |
| +	uint16_t u16;
 | |
| +	struct {
 | |
| +		uint16_t capable_1000base_x_full:1;
 | |
| +		uint16_t capable_1000base_x_half:1;
 | |
| +		uint16_t capable_1000base_t_full:1;
 | |
| +		uint16_t capable_1000base_t_half:1;
 | |
| +		uint16_t reserved_0_11:12;
 | |
| +	} s;
 | |
| +};
 | |
| +
 | |
| +/**
 | |
| + * PHY register 13 from the 802.3 spec
 | |
| + */
 | |
| +#define CVMX_MDIO_PHY_REG_MMD_CONTROL 13
 | |
| +union cvmx_mdio_phy_reg_mmd_control {
 | |
| +	uint16_t u16;
 | |
| +	struct {
 | |
| +		uint16_t function:2;
 | |
| +		uint16_t reserved_5_13:9;
 | |
| +		uint16_t devad:5;
 | |
| +	} s;
 | |
| +};
 | |
| +
 | |
| +/**
 | |
| + * PHY register 14 from the 802.3 spec
 | |
| + */
 | |
| +#define CVMX_MDIO_PHY_REG_MMD_ADDRESS_DATA 14
 | |
| +union cvmx_mdio_phy_reg_mmd_address_data {
 | |
| +	uint16_t u16;
 | |
| +	struct {
 | |
| +		uint16_t address_data:16;
 | |
| +	} s;
 | |
| +};
 | |
| +
 | |
| +/* Operating request encodings. */
 | |
| +#define MDIO_CLAUSE_22_WRITE    0
 | |
| +#define MDIO_CLAUSE_22_READ     1
 | |
| +
 | |
| +#define MDIO_CLAUSE_45_ADDRESS  0
 | |
| +#define MDIO_CLAUSE_45_WRITE    1
 | |
| +#define MDIO_CLAUSE_45_READ_INC 2
 | |
| +#define MDIO_CLAUSE_45_READ     3
 | |
| +
 | |
| +/* MMD identifiers, mostly for accessing devices withing XENPAK modules. */
 | |
| +#define CVMX_MMD_DEVICE_PMA_PMD      1
 | |
| +#define CVMX_MMD_DEVICE_WIS          2
 | |
| +#define CVMX_MMD_DEVICE_PCS          3
 | |
| +#define CVMX_MMD_DEVICE_PHY_XS       4
 | |
| +#define CVMX_MMD_DEVICE_DTS_XS       5
 | |
| +#define CVMX_MMD_DEVICE_TC           6
 | |
| +#define CVMX_MMD_DEVICE_CL22_EXT     29
 | |
| +#define CVMX_MMD_DEVICE_VENDOR_1     30
 | |
| +#define CVMX_MMD_DEVICE_VENDOR_2     31
 | |
| +
 | |
| +/**
 | |
| + * Perform an MII read. This function is used to read PHY
 | |
| + * registers controlling auto negotiation.
 | |
| + *
 | |
| + * @bus_id:   MDIO bus number. Zero on most chips, but some chips (ex CN56XX)
 | |
| + *                 support multiple busses.
 | |
| + * @phy_id:   The MII phy id
 | |
| + * @location: Register location to read
 | |
| + *
 | |
| + * Returns Result from the read or -1 on failure
 | |
| + */
 | |
| +static inline int cvmx_mdio_read(int bus_id, int phy_id, int location)
 | |
| +{
 | |
| +	union cvmx_smix_cmd smi_cmd;
 | |
| +	union cvmx_smix_rd_dat smi_rd;
 | |
| +	int timeout = 1000;
 | |
| +
 | |
| +	smi_cmd.u64 = 0;
 | |
| +	smi_cmd.s.phy_op = MDIO_CLAUSE_22_READ;
 | |
| +	smi_cmd.s.phy_adr = phy_id;
 | |
| +	smi_cmd.s.reg_adr = location;
 | |
| +	cvmx_write_csr(CVMX_SMIX_CMD(bus_id), smi_cmd.u64);
 | |
| +
 | |
| +	do {
 | |
| +		cvmx_wait(1000);
 | |
| +		smi_rd.u64 = cvmx_read_csr(CVMX_SMIX_RD_DAT(bus_id));
 | |
| +	} while (smi_rd.s.pending && timeout--);
 | |
| +
 | |
| +	if (smi_rd.s.val)
 | |
| +		return smi_rd.s.dat;
 | |
| +	else
 | |
| +		return -1;
 | |
| +}
 | |
| +
 | |
| +/**
 | |
| + * Perform an MII write. This function is used to write PHY
 | |
| + * registers controlling auto negotiation.
 | |
| + *
 | |
| + * @bus_id:   MDIO bus number. Zero on most chips, but some chips (ex CN56XX)
 | |
| + *                 support multiple busses.
 | |
| + * @phy_id:   The MII phy id
 | |
| + * @location: Register location to write
 | |
| + * @val:      Value to write
 | |
| + *
 | |
| + * Returns -1 on error
 | |
| + *         0 on success
 | |
| + */
 | |
| +static inline int cvmx_mdio_write(int bus_id, int phy_id, int location, int val)
 | |
| +{
 | |
| +	union cvmx_smix_cmd smi_cmd;
 | |
| +	union cvmx_smix_wr_dat smi_wr;
 | |
| +	int timeout = 1000;
 | |
| +
 | |
| +	smi_wr.u64 = 0;
 | |
| +	smi_wr.s.dat = val;
 | |
| +	cvmx_write_csr(CVMX_SMIX_WR_DAT(bus_id), smi_wr.u64);
 | |
| +
 | |
| +	smi_cmd.u64 = 0;
 | |
| +	smi_cmd.s.phy_op = MDIO_CLAUSE_22_WRITE;
 | |
| +	smi_cmd.s.phy_adr = phy_id;
 | |
| +	smi_cmd.s.reg_adr = location;
 | |
| +	cvmx_write_csr(CVMX_SMIX_CMD(bus_id), smi_cmd.u64);
 | |
| +
 | |
| +	do {
 | |
| +		cvmx_wait(1000);
 | |
| +		smi_wr.u64 = cvmx_read_csr(CVMX_SMIX_WR_DAT(bus_id));
 | |
| +	} while (smi_wr.s.pending && --timeout);
 | |
| +	if (timeout <= 0)
 | |
| +		return -1;
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +/**
 | |
| + * Perform an IEEE 802.3 clause 45 MII read using clause 22 operations. This
 | |
| + * function is used to read PHY registers controlling auto negotiation.
 | |
| + *
 | |
| + * @bus_id:   MDIO bus number. Zero on most chips, but some chips (ex CN56XX)
 | |
| + *                 support multiple busses.
 | |
| + * @phy_id:   The MII phy id
 | |
| + * @device:   MDIO Managable Device (MMD) id
 | |
| + * @location: Register location to read
 | |
| + *
 | |
| + * Returns Result from the read or -1 on failure
 | |
| + */
 | |
| +
 | |
| +static inline int cvmx_mdio_45_via_22_read(int bus_id, int phy_id, int device,
 | |
| +					   int location)
 | |
| +{
 | |
| +	union cvmx_mdio_phy_reg_mmd_control mmd_control;
 | |
| +
 | |
| +	/*
 | |
| +	 * a) To Register 13, write the Function field to 00 (address)
 | |
| +	 *    and DEVAD field to the device address value for the
 | |
| +	 *    desired MMD;
 | |
| +	 */
 | |
| +	mmd_control.u16 = 0;
 | |
| +	mmd_control.s.function = MDIO_CLAUSE_45_ADDRESS;
 | |
| +	mmd_control.s.devad = device;
 | |
| +	cvmx_mdio_write(bus_id, phy_id, CVMX_MDIO_PHY_REG_MMD_CONTROL,
 | |
| +			mmd_control.u16);
 | |
| +
 | |
| +	/*
 | |
| +	 *  b) To Register 14, write the desired address value to the
 | |
| +	 *     MMD's address register;
 | |
| +	 */
 | |
| +	cvmx_mdio_write(bus_id, phy_id, CVMX_MDIO_PHY_REG_MMD_ADDRESS_DATA,
 | |
| +			location);
 | |
| +
 | |
| +	/*
 | |
| +	 * c) To Register 13, write the Function field to 01 (Data, no
 | |
| +	 *    post increment) and DEVAD field to the same device
 | |
| +	 *    address value for the desired MMD;
 | |
| +	 */
 | |
| +	mmd_control.u16 = 0;
 | |
| +	mmd_control.s.function = MDIO_CLAUSE_45_READ;
 | |
| +	mmd_control.s.devad = device;
 | |
| +	cvmx_mdio_write(bus_id, phy_id, CVMX_MDIO_PHY_REG_MMD_CONTROL,
 | |
| +			mmd_control.u16);
 | |
| +
 | |
| +	/*
 | |
| +	 * d) From Register 14, read the content of the MMD's selected
 | |
| +	 *    register.
 | |
| +	 */
 | |
| +	return cvmx_mdio_read(bus_id, phy_id,
 | |
| +			      CVMX_MDIO_PHY_REG_MMD_ADDRESS_DATA);
 | |
| +}
 | |
| +
 | |
| +/**
 | |
| + * Perform an IEEE 802.3 clause 45 MII write using clause 22
 | |
| + * operations. This function is used to write PHY registers
 | |
| + * controlling auto negotiation.
 | |
| + *
 | |
| + * @bus_id:   MDIO bus number. Zero on most chips, but some chips (ex CN56XX)
 | |
| + *                 support multiple busses.
 | |
| + * @phy_id:   The MII phy id
 | |
| + * @device:   MDIO Managable Device (MMD) id
 | |
| + * @location: Register location to write
 | |
| + * @val:      Value to write
 | |
| + *
 | |
| + * Returns -1 on error
 | |
| + *         0 on success
 | |
| + */
 | |
| +static inline int cvmx_mdio_45_via_22_write(int bus_id, int phy_id, int device,
 | |
| +					    int location, int val)
 | |
| +{
 | |
| +	union cvmx_mdio_phy_reg_mmd_control mmd_control;
 | |
| +
 | |
| +	/*
 | |
| +	 * a) To Register 13, write the Function field to 00 (address)
 | |
| +	 *    and DEVAD field to the device address value for the
 | |
| +	 *    desired MMD;
 | |
| +	 */
 | |
| +	mmd_control.u16 = 0;
 | |
| +	mmd_control.s.function = MDIO_CLAUSE_45_ADDRESS;
 | |
| +	mmd_control.s.devad = device;
 | |
| +	cvmx_mdio_write(bus_id, phy_id, CVMX_MDIO_PHY_REG_MMD_CONTROL,
 | |
| +			mmd_control.u16);
 | |
| +
 | |
| +	/*
 | |
| +	 * b) To Register 14, write the desired address value to the
 | |
| +	 *    MMD's address register;
 | |
| +	 */
 | |
| +	cvmx_mdio_write(bus_id, phy_id, CVMX_MDIO_PHY_REG_MMD_ADDRESS_DATA,
 | |
| +			location);
 | |
| +
 | |
| +	/*
 | |
| +	 * c) To Register 13, write the Function field to 01 (Data, no
 | |
| +	 *    post increment) and DEVAD field to the same device
 | |
| +	 *    address value for the desired MMD;
 | |
| +	 */
 | |
| +	mmd_control.u16 = 0;
 | |
| +	mmd_control.s.function = MDIO_CLAUSE_45_READ;
 | |
| +	mmd_control.s.devad = device;
 | |
| +	cvmx_mdio_write(bus_id, phy_id, CVMX_MDIO_PHY_REG_MMD_CONTROL,
 | |
| +			mmd_control.u16);
 | |
| +
 | |
| +	/*
 | |
| +	 * d) To Register 14, write the content of the MMD's selected
 | |
| +	 *    register.
 | |
| +	 */
 | |
| +	return cvmx_mdio_write(bus_id, phy_id,
 | |
| +			       CVMX_MDIO_PHY_REG_MMD_ADDRESS_DATA, val);
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +/**
 | |
| + * Perform an IEEE 802.3 clause 45 MII read. This function is used to read PHY
 | |
| + * registers controlling auto negotiation.
 | |
| + *
 | |
| + * @bus_id:   MDIO bus number. Zero on most chips, but some chips (ex CN56XX)
 | |
| + *                 support multiple busses.
 | |
| + * @phy_id:   The MII phy id
 | |
| + * @device:   MDIO Managable Device (MMD) id
 | |
| + * @location: Register location to read
 | |
| + *
 | |
| + * Returns Result from the read or -1 on failure
 | |
| + */
 | |
| +
 | |
| +static inline int cvmx_mdio_45_read(int bus_id, int phy_id, int device,
 | |
| +				    int location)
 | |
| +{
 | |
| +	union cvmx_smix_cmd smi_cmd;
 | |
| +	union cvmx_smix_rd_dat smi_rd;
 | |
| +	union cvmx_smix_wr_dat smi_wr;
 | |
| +	int timeout = 1000;
 | |
| +
 | |
| +	smi_wr.u64 = 0;
 | |
| +	smi_wr.s.dat = location;
 | |
| +	cvmx_write_csr(CVMX_SMIX_WR_DAT(bus_id), smi_wr.u64);
 | |
| +
 | |
| +	smi_cmd.u64 = 0;
 | |
| +	smi_cmd.s.phy_op = MDIO_CLAUSE_45_ADDRESS;
 | |
| +	smi_cmd.s.phy_adr = phy_id;
 | |
| +	smi_cmd.s.reg_adr = device;
 | |
| +	cvmx_write_csr(CVMX_SMIX_CMD(bus_id), smi_cmd.u64);
 | |
| +
 | |
| +	do {
 | |
| +		cvmx_wait(1000);
 | |
| +		smi_wr.u64 = cvmx_read_csr(CVMX_SMIX_WR_DAT(bus_id));
 | |
| +	} while (smi_wr.s.pending && --timeout);
 | |
| +	if (timeout <= 0)
 | |
| +		return -1;
 | |
| +
 | |
| +	smi_cmd.u64 = 0;
 | |
| +	smi_cmd.s.phy_op = MDIO_CLAUSE_45_READ;
 | |
| +	smi_cmd.s.phy_adr = phy_id;
 | |
| +	smi_cmd.s.reg_adr = device;
 | |
| +	cvmx_write_csr(CVMX_SMIX_CMD(bus_id), smi_cmd.u64);
 | |
| +
 | |
| +	do {
 | |
| +		cvmx_wait(1000);
 | |
| +		smi_rd.u64 = cvmx_read_csr(CVMX_SMIX_RD_DAT(bus_id));
 | |
| +	} while (smi_rd.s.pending && timeout--);
 | |
| +
 | |
| +	if (0 == timeout)
 | |
| +		cvmx_dprintf("cvmx_mdio_45_read: bus_id %d phy_id %2d "
 | |
| +			     "device %2d register %2d   TIME OUT\n",
 | |
| +			     bus_id, phy_id, device, location);
 | |
| +
 | |
| +	if (smi_rd.s.val)
 | |
| +		return smi_rd.s.dat;
 | |
| +	else {
 | |
| +		cvmx_dprintf("cvmx_mdio_45_read: bus_id %d phy_id %2d "
 | |
| +			     "device %2d register %2d   INVALID READ\n",
 | |
| +			     bus_id, phy_id, device, location);
 | |
| +		return -1;
 | |
| +	}
 | |
| +}
 | |
| +
 | |
| +/**
 | |
| + * Perform an IEEE 802.3 clause 45 MII write. This function is used to
 | |
| + * write PHY registers controlling auto negotiation.
 | |
| + *
 | |
| + * @bus_id:   MDIO bus number. Zero on most chips, but some chips (ex CN56XX)
 | |
| + *                 support multiple busses.
 | |
| + * @phy_id:   The MII phy id
 | |
| + * @device:   MDIO Managable Device (MMD) id
 | |
| + * @location: Register location to write
 | |
| + * @val:      Value to write
 | |
| + *
 | |
| + * Returns -1 on error
 | |
| + *         0 on success
 | |
| + */
 | |
| +static inline int cvmx_mdio_45_write(int bus_id, int phy_id, int device,
 | |
| +				     int location, int val)
 | |
| +{
 | |
| +	union cvmx_smix_cmd smi_cmd;
 | |
| +	union cvmx_smix_wr_dat smi_wr;
 | |
| +	int timeout = 1000;
 | |
| +
 | |
| +	smi_wr.u64 = 0;
 | |
| +	smi_wr.s.dat = location;
 | |
| +	cvmx_write_csr(CVMX_SMIX_WR_DAT(bus_id), smi_wr.u64);
 | |
| +
 | |
| +	smi_cmd.u64 = 0;
 | |
| +	smi_cmd.s.phy_op = MDIO_CLAUSE_45_ADDRESS;
 | |
| +	smi_cmd.s.phy_adr = phy_id;
 | |
| +	smi_cmd.s.reg_adr = device;
 | |
| +	cvmx_write_csr(CVMX_SMIX_CMD(bus_id), smi_cmd.u64);
 | |
| +
 | |
| +	do {
 | |
| +		cvmx_wait(1000);
 | |
| +		smi_wr.u64 = cvmx_read_csr(CVMX_SMIX_WR_DAT(bus_id));
 | |
| +	} while (smi_wr.s.pending && --timeout);
 | |
| +	if (timeout <= 0)
 | |
| +		return -1;
 | |
| +
 | |
| +	smi_wr.u64 = 0;
 | |
| +	smi_wr.s.dat = val;
 | |
| +	cvmx_write_csr(CVMX_SMIX_WR_DAT(bus_id), smi_wr.u64);
 | |
| +
 | |
| +	smi_cmd.u64 = 0;
 | |
| +	smi_cmd.s.phy_op = MDIO_CLAUSE_45_WRITE;
 | |
| +	smi_cmd.s.phy_adr = phy_id;
 | |
| +	smi_cmd.s.reg_adr = device;
 | |
| +	cvmx_write_csr(CVMX_SMIX_CMD(bus_id), smi_cmd.u64);
 | |
| +
 | |
| +	do {
 | |
| +		cvmx_wait(1000);
 | |
| +		smi_wr.u64 = cvmx_read_csr(CVMX_SMIX_WR_DAT(bus_id));
 | |
| +	} while (smi_wr.s.pending && --timeout);
 | |
| +	if (timeout <= 0)
 | |
| +		return -1;
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +#endif
 | |
| --- a/drivers/net/Kconfig
 | |
| +++ b/drivers/net/Kconfig
 | |
| @@ -2035,6 +2035,14 @@ config ATL2
 | |
|  	  To compile this driver as a module, choose M here.  The module
 | |
|  	  will be called atl2.
 | |
|  
 | |
| +config OCTEON_MGMT
 | |
| +	tristate "OCTEON Management port ethernet driver (CN5XXX)"
 | |
| +	depends on CPU_CAVIUM_OCTEON
 | |
| +	default y
 | |
| +	help
 | |
| +	  This option enables the ethernet driver for the management port on
 | |
| +	  CN52XX, CN57XX, CN56XX, CN55XX, and CN54XX chips.
 | |
| +
 | |
|  source "drivers/net/fs_enet/Kconfig"
 | |
|  
 | |
|  endif # NET_ETHERNET
 | |
| --- a/drivers/net/Makefile
 | |
| +++ b/drivers/net/Makefile
 | |
| @@ -234,6 +234,7 @@ pasemi_mac_driver-objs := pasemi_mac.o p
 | |
|  obj-$(CONFIG_MLX4_CORE) += mlx4/
 | |
|  obj-$(CONFIG_ENC28J60) += enc28j60.o
 | |
|  obj-$(CONFIG_ETHOC) += ethoc.o
 | |
| +obj-$(CONFIG_OCTEON_MGMT) += octeon/
 | |
|  
 | |
|  obj-$(CONFIG_XTENSA_XT2000_SONIC) += xtsonic.o
 | |
|  
 | |
| --- /dev/null
 | |
| +++ b/drivers/net/octeon/Makefile
 | |
| @@ -0,0 +1,11 @@
 | |
| +# Makefile for the Cavium OCTEON Ethernet drivers.
 | |
| +#
 | |
| +# This file is subject to the terms and conditions of the GNU General Public
 | |
| +# License.  See the file "COPYING" in the main directory of this archive
 | |
| +# for more details.
 | |
| +#
 | |
| +# Copyright (C) 2008 Cavium Networks
 | |
| +
 | |
| +obj-$(CONFIG_OCTEON_MGMT) += octeon_mgmt.o
 | |
| +
 | |
| +octeon_mgmt-objs := octeon-mgmt-port.o cvmx-mgmt-port.o
 | |
| \ No newline at end of file
 | |
| --- /dev/null
 | |
| +++ b/drivers/net/octeon/cvmx-mgmt-port.c
 | |
| @@ -0,0 +1,818 @@
 | |
| +/***********************license start***************
 | |
| + * Author: Cavium Networks
 | |
| + *
 | |
| + * Contact: support@caviumnetworks.com
 | |
| + * This file is part of the OCTEON SDK
 | |
| + *
 | |
| + * Copyright (c) 2003-2008 Cavium Networks
 | |
| + *
 | |
| + * This file is free software; you can redistribute it and/or modify
 | |
| + * it under the terms of the GNU General Public License, Version 2, as
 | |
| + * published by the Free Software Foundation.
 | |
| + *
 | |
| + * This file is distributed in the hope that it will be useful, but
 | |
| + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
 | |
| + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
 | |
| + * NONINFRINGEMENT.  See the GNU General Public License for more
 | |
| + * details.
 | |
| + *
 | |
| + * You should have received a copy of the GNU General Public License
 | |
| + * along with this file; if not, write to the Free Software
 | |
| + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 | |
| + * or visit http://www.gnu.org/licenses/.
 | |
| + *
 | |
| + * This file may also be available under a different license from Cavium.
 | |
| + * Contact Cavium Networks for more information
 | |
| + ***********************license end**************************************/
 | |
| +
 | |
| +/**
 | |
| + *
 | |
| + * Support functions for managing the MII management port
 | |
| + *
 | |
| + */
 | |
| +
 | |
| +#include <asm/octeon/octeon.h>
 | |
| +#include <asm/octeon/cvmx-spinlock.h>
 | |
| +#include <asm/octeon/cvmx-bootmem.h>
 | |
| +#include <asm/octeon/cvmx-mdio.h>
 | |
| +
 | |
| +#include <asm/octeon/cvmx-mixx-defs.h>
 | |
| +#include <asm/octeon/cvmx-agl-defs.h>
 | |
| +
 | |
| +#include "cvmx-mgmt-port.h"
 | |
| +
 | |
| +#define CVMX_MGMT_PORT_NUM_PORTS        2
 | |
| +/* Number of TX ring buffer entries and buffers */
 | |
| +#define CVMX_MGMT_PORT_NUM_TX_BUFFERS   16
 | |
| +/* Number of RX ring buffer entries and buffers */
 | |
| +#define CVMX_MGMT_PORT_NUM_RX_BUFFERS   128
 | |
| +
 | |
| +#define CVMX_MGMT_PORT_TX_BUFFER_SIZE   12288
 | |
| +#define CVMX_MGMT_PORT_RX_BUFFER_SIZE   1536
 | |
| +
 | |
| +/**
 | |
| + * Format of the TX/RX ring buffer entries
 | |
| + */
 | |
| +union cvmx_mgmt_port_ring_entry {
 | |
| +	uint64_t u64;
 | |
| +	struct {
 | |
| +		uint64_t reserved_62_63:2;
 | |
| +		/* Length of the buffer/packet in bytes */
 | |
| +		uint64_t len:14;
 | |
| +		/* The RX error code */
 | |
| +		uint64_t code:8;
 | |
| +		/* Physical address of the buffer */
 | |
| +		uint64_t addr:40;
 | |
| +	} s;
 | |
| +};
 | |
| +
 | |
| +/**
 | |
| + * Per port state required for each mgmt port
 | |
| + */
 | |
| +struct cvmx_mgmt_port_state {
 | |
| +	/* Used for exclusive access to this structure */
 | |
| +	cvmx_spinlock_t lock;
 | |
| +	/* Where the next TX will write in the tx_ring and tx_buffers */
 | |
| +	int tx_write_index;
 | |
| +	/* Where the next RX will be in the rx_ring and rx_buffers */
 | |
| +	int rx_read_index;
 | |
| +	/* The SMI/MDIO PHY address */
 | |
| +	int phy_id;
 | |
| +	/* Our MAC address */
 | |
| +	uint64_t mac;
 | |
| +	union cvmx_mgmt_port_ring_entry tx_ring[CVMX_MGMT_PORT_NUM_TX_BUFFERS];
 | |
| +	union cvmx_mgmt_port_ring_entry rx_ring[CVMX_MGMT_PORT_NUM_RX_BUFFERS];
 | |
| +	char tx_buffers[CVMX_MGMT_PORT_NUM_TX_BUFFERS]
 | |
| +	    [CVMX_MGMT_PORT_TX_BUFFER_SIZE];
 | |
| +	char rx_buffers[CVMX_MGMT_PORT_NUM_RX_BUFFERS]
 | |
| +	    [CVMX_MGMT_PORT_RX_BUFFER_SIZE];
 | |
| +};
 | |
| +
 | |
| +/**
 | |
| + * Pointers to each mgmt port's state
 | |
| + */
 | |
| +struct cvmx_mgmt_port_state *cvmx_mgmt_port_state_ptr;
 | |
| +
 | |
| +/**
 | |
| + * Return the number of management ports supported by this chip
 | |
| + *
 | |
| + * Returns Number of ports
 | |
| + */
 | |
| +int __cvmx_mgmt_port_num_ports(void)
 | |
| +{
 | |
| +	if (OCTEON_IS_MODEL(OCTEON_CN56XX))
 | |
| +		return 1;
 | |
| +	else if (OCTEON_IS_MODEL(OCTEON_CN52XX))
 | |
| +		return 2;
 | |
| +	else
 | |
| +		return 0;
 | |
| +}
 | |
| +
 | |
| +/**
 | |
| + * Called to initialize a management port for use. Multiple calls
 | |
| + * to this function accross applications is safe.
 | |
| + *
 | |
| + * @port:   Port to initialize
 | |
| + *
 | |
| + * Returns CVMX_MGMT_PORT_SUCCESS or an error code
 | |
| + */
 | |
| +enum cvmx_mgmt_port_result cvmx_mgmt_port_initialize(int port)
 | |
| +{
 | |
| +	char *alloc_name = "cvmx_mgmt_port";
 | |
| +	union cvmx_mixx_oring1 oring1;
 | |
| +	union cvmx_mixx_ctl mix_ctl;
 | |
| +
 | |
| +	if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports()))
 | |
| +		return CVMX_MGMT_PORT_INVALID_PARAM;
 | |
| +
 | |
| +	cvmx_mgmt_port_state_ptr =
 | |
| +	    cvmx_bootmem_alloc_named(CVMX_MGMT_PORT_NUM_PORTS *
 | |
| +				     sizeof(struct cvmx_mgmt_port_state), 128,
 | |
| +				     alloc_name);
 | |
| +	if (cvmx_mgmt_port_state_ptr) {
 | |
| +		memset(cvmx_mgmt_port_state_ptr, 0,
 | |
| +		       CVMX_MGMT_PORT_NUM_PORTS *
 | |
| +		       sizeof(struct cvmx_mgmt_port_state));
 | |
| +	} else {
 | |
| +		struct cvmx_bootmem_named_block_desc *block_desc =
 | |
| +		    cvmx_bootmem_find_named_block(alloc_name);
 | |
| +		if (block_desc)
 | |
| +			cvmx_mgmt_port_state_ptr =
 | |
| +			    cvmx_phys_to_ptr(block_desc->base_addr);
 | |
| +		else {
 | |
| +			cvmx_dprintf("ERROR: cvmx_mgmt_port_initialize: "
 | |
| +				     "Unable to get named block %s.\n",
 | |
| +				     alloc_name);
 | |
| +			return CVMX_MGMT_PORT_NO_MEMORY;
 | |
| +		}
 | |
| +	}
 | |
| +
 | |
| +	/*
 | |
| +	 * Reset the MIX block if the previous user had a different TX
 | |
| +	 * ring size.
 | |
| +	 */
 | |
| +	mix_ctl.u64 = cvmx_read_csr(CVMX_MIXX_CTL(port));
 | |
| +	if (!mix_ctl.s.reset) {
 | |
| +		oring1.u64 = cvmx_read_csr(CVMX_MIXX_ORING1(port));
 | |
| +		if (oring1.s.osize != CVMX_MGMT_PORT_NUM_TX_BUFFERS) {
 | |
| +			mix_ctl.u64 = cvmx_read_csr(CVMX_MIXX_CTL(port));
 | |
| +			mix_ctl.s.en = 0;
 | |
| +			cvmx_write_csr(CVMX_MIXX_CTL(port), mix_ctl.u64);
 | |
| +			do {
 | |
| +				mix_ctl.u64 =
 | |
| +				    cvmx_read_csr(CVMX_MIXX_CTL(port));
 | |
| +			} while (mix_ctl.s.busy);
 | |
| +			mix_ctl.s.reset = 1;
 | |
| +			cvmx_write_csr(CVMX_MIXX_CTL(port), mix_ctl.u64);
 | |
| +			cvmx_read_csr(CVMX_MIXX_CTL(port));
 | |
| +			memset(cvmx_mgmt_port_state_ptr + port, 0,
 | |
| +			       sizeof(struct cvmx_mgmt_port_state));
 | |
| +		}
 | |
| +	}
 | |
| +
 | |
| +	if (cvmx_mgmt_port_state_ptr[port].tx_ring[0].u64 == 0) {
 | |
| +		struct cvmx_mgmt_port_state *state =
 | |
| +			cvmx_mgmt_port_state_ptr + port;
 | |
| +		int i;
 | |
| +		union cvmx_mixx_bist mix_bist;
 | |
| +		union cvmx_agl_gmx_bist agl_gmx_bist;
 | |
| +		union cvmx_mixx_oring1 oring1;
 | |
| +		union cvmx_mixx_iring1 iring1;
 | |
| +		union cvmx_mixx_ctl mix_ctl;
 | |
| +
 | |
| +		/* Make sure BIST passed */
 | |
| +		mix_bist.u64 = cvmx_read_csr(CVMX_MIXX_BIST(port));
 | |
| +		if (mix_bist.u64)
 | |
| +			cvmx_dprintf("WARNING: cvmx_mgmt_port_initialize: "
 | |
| +				     "Managment port MIX failed BIST "
 | |
| +				     "(0x%016llx)\n",
 | |
| +				     (unsigned long long)mix_bist.u64);
 | |
| +
 | |
| +		agl_gmx_bist.u64 = cvmx_read_csr(CVMX_AGL_GMX_BIST);
 | |
| +		if (agl_gmx_bist.u64)
 | |
| +			cvmx_dprintf("WARNING: cvmx_mgmt_port_initialize: "
 | |
| +				     "Managment port AGL failed BIST "
 | |
| +				     "(0x%016llx)\n",
 | |
| +				     (unsigned long long)agl_gmx_bist.u64);
 | |
| +
 | |
| +		/* Clear all state information */
 | |
| +		memset(state, 0, sizeof(*state));
 | |
| +
 | |
| +		/* Take the control logic out of reset */
 | |
| +		mix_ctl.u64 = cvmx_read_csr(CVMX_MIXX_CTL(port));
 | |
| +		mix_ctl.s.reset = 0;
 | |
| +		cvmx_write_csr(CVMX_MIXX_CTL(port), mix_ctl.u64);
 | |
| +
 | |
| +		/* Set the PHY address */
 | |
| +		if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM)
 | |
| +			state->phy_id = -1;
 | |
| +		else
 | |
| +			/* Will need to be change to match the board */
 | |
| +			state->phy_id = port;
 | |
| +
 | |
| +		/* Create a default MAC address */
 | |
| +		state->mac = 0x000000dead000000ull;
 | |
| +		state->mac += 0xffffff & CAST64(state);
 | |
| +
 | |
| +		/* Setup the TX ring */
 | |
| +		for (i = 0; i < CVMX_MGMT_PORT_NUM_TX_BUFFERS; i++) {
 | |
| +			state->tx_ring[i].s.len = CVMX_MGMT_PORT_TX_BUFFER_SIZE;
 | |
| +			state->tx_ring[i].s.addr =
 | |
| +			    cvmx_ptr_to_phys(state->tx_buffers[i]);
 | |
| +		}
 | |
| +
 | |
| +		/* Tell the HW where the TX ring is */
 | |
| +		oring1.u64 = 0;
 | |
| +		oring1.s.obase = cvmx_ptr_to_phys(state->tx_ring) >> 3;
 | |
| +		oring1.s.osize = CVMX_MGMT_PORT_NUM_TX_BUFFERS;
 | |
| +		CVMX_SYNCWS;
 | |
| +		cvmx_write_csr(CVMX_MIXX_ORING1(port), oring1.u64);
 | |
| +
 | |
| +		/* Setup the RX ring */
 | |
| +		for (i = 0; i < CVMX_MGMT_PORT_NUM_RX_BUFFERS; i++) {
 | |
| +			/* This size is -8 due to an errata for CN56XX pass 1 */
 | |
| +			state->rx_ring[i].s.len =
 | |
| +			    CVMX_MGMT_PORT_RX_BUFFER_SIZE - 8;
 | |
| +			state->rx_ring[i].s.addr =
 | |
| +			    cvmx_ptr_to_phys(state->rx_buffers[i]);
 | |
| +		}
 | |
| +
 | |
| +		/* Tell the HW where the RX ring is */
 | |
| +		iring1.u64 = 0;
 | |
| +		iring1.s.ibase = cvmx_ptr_to_phys(state->rx_ring) >> 3;
 | |
| +		iring1.s.isize = CVMX_MGMT_PORT_NUM_RX_BUFFERS;
 | |
| +		CVMX_SYNCWS;
 | |
| +		cvmx_write_csr(CVMX_MIXX_IRING1(port), iring1.u64);
 | |
| +		cvmx_write_csr(CVMX_MIXX_IRING2(port),
 | |
| +			       CVMX_MGMT_PORT_NUM_RX_BUFFERS);
 | |
| +
 | |
| +		/* Disable the external input/output */
 | |
| +		cvmx_mgmt_port_disable(port);
 | |
| +
 | |
| +		/* Set the MAC address filtering up */
 | |
| +		cvmx_mgmt_port_set_mac(port, state->mac);
 | |
| +
 | |
| +		/*
 | |
| +		 * Set the default max size to an MTU of 1500 with L2
 | |
| +		 * and VLAN.
 | |
| +		 */
 | |
| +		cvmx_mgmt_port_set_max_packet_size(port, 1518);
 | |
| +
 | |
| +		/*
 | |
| +		 * Enable the port HW. Packets are not allowed until
 | |
| +		 * cvmx_mgmt_port_enable() is called.
 | |
| +		 */
 | |
| +		mix_ctl.u64 = 0;
 | |
| +		/* Strip the ending CRC */
 | |
| +		mix_ctl.s.crc_strip = 1;
 | |
| +		/* Enable the port */
 | |
| +		mix_ctl.s.en = 1;
 | |
| +		/* Arbitration mode */
 | |
| +		mix_ctl.s.nbtarb = 0;
 | |
| +		/* MII CB-request FIFO programmable high watermark */
 | |
| +		mix_ctl.s.mrq_hwm = 1;
 | |
| +		cvmx_write_csr(CVMX_MIXX_CTL(port), mix_ctl.u64);
 | |
| +
 | |
| +		if (OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X)
 | |
| +		    || OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X)) {
 | |
| +			/*
 | |
| +			 * Force compensation values, as they are not
 | |
| +			 * determined properly by HW.
 | |
| +			 */
 | |
| +			union cvmx_agl_gmx_drv_ctl drv_ctl;
 | |
| +
 | |
| +			drv_ctl.u64 = cvmx_read_csr(CVMX_AGL_GMX_DRV_CTL);
 | |
| +			if (port) {
 | |
| +				drv_ctl.s.byp_en1 = 1;
 | |
| +				drv_ctl.s.nctl1 = 6;
 | |
| +				drv_ctl.s.pctl1 = 6;
 | |
| +			} else {
 | |
| +				drv_ctl.s.byp_en = 1;
 | |
| +				drv_ctl.s.nctl = 6;
 | |
| +				drv_ctl.s.pctl = 6;
 | |
| +			}
 | |
| +			cvmx_write_csr(CVMX_AGL_GMX_DRV_CTL, drv_ctl.u64);
 | |
| +		}
 | |
| +	}
 | |
| +	return CVMX_MGMT_PORT_SUCCESS;
 | |
| +}
 | |
| +
 | |
| +/**
 | |
| + * Shutdown a management port. This currently disables packet IO
 | |
| + * but leaves all hardware and buffers. Another application can then
 | |
| + * call initialize() without redoing the hardware setup.
 | |
| + *
 | |
| + * @port:   Management port
 | |
| + *
 | |
| + * Returns CVMX_MGMT_PORT_SUCCESS or an error code
 | |
| + */
 | |
| +enum cvmx_mgmt_port_result cvmx_mgmt_port_shutdown(int port)
 | |
| +{
 | |
| +	if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports()))
 | |
| +		return CVMX_MGMT_PORT_INVALID_PARAM;
 | |
| +
 | |
| +	/* Stop packets from comming in */
 | |
| +	cvmx_mgmt_port_disable(port);
 | |
| +
 | |
| +	/*
 | |
| +	 * We don't free any memory so the next intialize can reuse
 | |
| +	 * the HW setup.
 | |
| +	 */
 | |
| +	return CVMX_MGMT_PORT_SUCCESS;
 | |
| +}
 | |
| +
 | |
| +/**
 | |
| + * Enable packet IO on a management port
 | |
| + *
 | |
| + * @port:   Management port
 | |
| + *
 | |
| + * Returns CVMX_MGMT_PORT_SUCCESS or an error code
 | |
| + */
 | |
| +enum cvmx_mgmt_port_result cvmx_mgmt_port_enable(int port)
 | |
| +{
 | |
| +	struct cvmx_mgmt_port_state *state;
 | |
| +	union cvmx_agl_gmx_prtx_cfg agl_gmx_prtx;
 | |
| +	union cvmx_agl_gmx_inf_mode agl_gmx_inf_mode;
 | |
| +	union cvmx_agl_gmx_rxx_frm_ctl rxx_frm_ctl;
 | |
| +
 | |
| +	if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports()))
 | |
| +		return CVMX_MGMT_PORT_INVALID_PARAM;
 | |
| +
 | |
| +	state = cvmx_mgmt_port_state_ptr + port;
 | |
| +
 | |
| +	cvmx_spinlock_lock(&state->lock);
 | |
| +
 | |
| +	rxx_frm_ctl.u64 = 0;
 | |
| +	rxx_frm_ctl.s.pre_align = 1;
 | |
| +	/*
 | |
| +	 * When set, disables the length check for non-min sized pkts
 | |
| +	 * with padding in the client data.
 | |
| +	 */
 | |
| +	rxx_frm_ctl.s.pad_len = 1;
 | |
| +	/* When set, disables the length check for VLAN pkts */
 | |
| +	rxx_frm_ctl.s.vlan_len = 1;
 | |
| +	/* When set, PREAMBLE checking is  less strict */
 | |
| +	rxx_frm_ctl.s.pre_free = 1;
 | |
| +	/* Control Pause Frames can match station SMAC */
 | |
| +	rxx_frm_ctl.s.ctl_smac = 0;
 | |
| +	/* Control Pause Frames can match globally assign Multicast address */
 | |
| +	rxx_frm_ctl.s.ctl_mcst = 1;
 | |
| +	/* Forward pause information to TX block */
 | |
| +	rxx_frm_ctl.s.ctl_bck = 1;
 | |
| +	/* Drop Control Pause Frames */
 | |
| +	rxx_frm_ctl.s.ctl_drp = 1;
 | |
| +	/* Strip off the preamble */
 | |
| +	rxx_frm_ctl.s.pre_strp = 1;
 | |
| +	/*
 | |
| +	 * This port is configured to send PREAMBLE+SFD to begin every
 | |
| +	 * frame.  GMX checks that the PREAMBLE is sent correctly.
 | |
| +	 */
 | |
| +	rxx_frm_ctl.s.pre_chk = 1;
 | |
| +	cvmx_write_csr(CVMX_AGL_GMX_RXX_FRM_CTL(port), rxx_frm_ctl.u64);
 | |
| +
 | |
| +	/* Enable the AGL block */
 | |
| +	agl_gmx_inf_mode.u64 = 0;
 | |
| +	agl_gmx_inf_mode.s.en = 1;
 | |
| +	cvmx_write_csr(CVMX_AGL_GMX_INF_MODE, agl_gmx_inf_mode.u64);
 | |
| +
 | |
| +	/* Configure the port duplex and enables */
 | |
| +	agl_gmx_prtx.u64 = cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port));
 | |
| +	agl_gmx_prtx.s.tx_en = 1;
 | |
| +	agl_gmx_prtx.s.rx_en = 1;
 | |
| +	if (cvmx_mgmt_port_get_link(port) < 0)
 | |
| +		agl_gmx_prtx.s.duplex = 0;
 | |
| +	else
 | |
| +		agl_gmx_prtx.s.duplex = 1;
 | |
| +	agl_gmx_prtx.s.en = 1;
 | |
| +	cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_prtx.u64);
 | |
| +
 | |
| +	cvmx_spinlock_unlock(&state->lock);
 | |
| +	return CVMX_MGMT_PORT_SUCCESS;
 | |
| +}
 | |
| +
 | |
| +/**
 | |
| + * Disable packet IO on a management port
 | |
| + *
 | |
| + * @port:   Management port
 | |
| + *
 | |
| + * Returns CVMX_MGMT_PORT_SUCCESS or an error code
 | |
| + */
 | |
| +enum cvmx_mgmt_port_result cvmx_mgmt_port_disable(int port)
 | |
| +{
 | |
| +	struct cvmx_mgmt_port_state *state;
 | |
| +	union cvmx_agl_gmx_prtx_cfg agl_gmx_prtx;
 | |
| +
 | |
| +	if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports()))
 | |
| +		return CVMX_MGMT_PORT_INVALID_PARAM;
 | |
| +
 | |
| +	state = cvmx_mgmt_port_state_ptr + port;
 | |
| +
 | |
| +	cvmx_spinlock_lock(&state->lock);
 | |
| +
 | |
| +	agl_gmx_prtx.u64 = cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port));
 | |
| +	agl_gmx_prtx.s.en = 0;
 | |
| +	cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_prtx.u64);
 | |
| +
 | |
| +	cvmx_spinlock_unlock(&state->lock);
 | |
| +	return CVMX_MGMT_PORT_SUCCESS;
 | |
| +}
 | |
| +
 | |
| +/**
 | |
| + * Send a packet out the management port. The packet is copied so
 | |
| + * the input buffer isn't used after this call.
 | |
| + *
 | |
| + * @port:       Management port
 | |
| + * @packet_len: Length of the packet to send. It does not include the final CRC
 | |
| + * @buffer:     Packet data
 | |
| + *
 | |
| + * Returns CVMX_MGMT_PORT_SUCCESS or an error code
 | |
| + */
 | |
| +enum cvmx_mgmt_port_result cvmx_mgmt_port_send(int port, int packet_len,
 | |
| +					       void *buffer)
 | |
| +{
 | |
| +	struct cvmx_mgmt_port_state *state;
 | |
| +	union cvmx_mixx_oring2 mix_oring2;
 | |
| +
 | |
| +	if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports()))
 | |
| +		return CVMX_MGMT_PORT_INVALID_PARAM;
 | |
| +
 | |
| +	/* Max sure the packet size is valid */
 | |
| +	if ((packet_len < 1) || (packet_len > CVMX_MGMT_PORT_TX_BUFFER_SIZE))
 | |
| +		return CVMX_MGMT_PORT_INVALID_PARAM;
 | |
| +
 | |
| +	if (buffer == NULL)
 | |
| +		return CVMX_MGMT_PORT_INVALID_PARAM;
 | |
| +
 | |
| +	state = cvmx_mgmt_port_state_ptr + port;
 | |
| +
 | |
| +	cvmx_spinlock_lock(&state->lock);
 | |
| +
 | |
| +	mix_oring2.u64 = cvmx_read_csr(CVMX_MIXX_ORING2(port));
 | |
| +	if (mix_oring2.s.odbell >= CVMX_MGMT_PORT_NUM_TX_BUFFERS - 1) {
 | |
| +		/* No room for another packet */
 | |
| +		cvmx_spinlock_unlock(&state->lock);
 | |
| +		return CVMX_MGMT_PORT_NO_MEMORY;
 | |
| +	} else {
 | |
| +		/* Copy the packet into the output buffer */
 | |
| +		memcpy(state->tx_buffers[state->tx_write_index], buffer,
 | |
| +		       packet_len);
 | |
| +		/* Insert the source MAC */
 | |
| +		memcpy(state->tx_buffers[state->tx_write_index] + 6,
 | |
| +		       ((char *)&state->mac) + 2, 6);
 | |
| +		/* Update the TX ring buffer entry size */
 | |
| +		state->tx_ring[state->tx_write_index].s.len = packet_len;
 | |
| +		/* Increment our TX index */
 | |
| +		state->tx_write_index =
 | |
| +		    (state->tx_write_index + 1) % CVMX_MGMT_PORT_NUM_TX_BUFFERS;
 | |
| +		/* Ring the doorbell, send ing the packet */
 | |
| +		CVMX_SYNCWS;
 | |
| +		cvmx_write_csr(CVMX_MIXX_ORING2(port), 1);
 | |
| +		if (cvmx_read_csr(CVMX_MIXX_ORCNT(port)))
 | |
| +			cvmx_write_csr(CVMX_MIXX_ORCNT(port),
 | |
| +				       cvmx_read_csr(CVMX_MIXX_ORCNT(port)));
 | |
| +
 | |
| +		cvmx_spinlock_unlock(&state->lock);
 | |
| +		return CVMX_MGMT_PORT_SUCCESS;
 | |
| +	}
 | |
| +}
 | |
| +
 | |
| +/**
 | |
| + * Receive a packet from the management port.
 | |
| + *
 | |
| + * @port:       Management port
 | |
| + * @buffer_len: Size of the buffer to receive the packet into
 | |
| + * @buffer:     Buffer to receive the packet into
 | |
| + *
 | |
| + * Returns The size of the packet, or a negative erorr code on failure. Zero
 | |
| + *         means that no packets were available.
 | |
| + */
 | |
| +int cvmx_mgmt_port_receive(int port, int buffer_len, void *buffer)
 | |
| +{
 | |
| +	union cvmx_mixx_ircnt mix_ircnt;
 | |
| +	struct cvmx_mgmt_port_state *state;
 | |
| +	int result;
 | |
| +
 | |
| +	if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports()))
 | |
| +		return CVMX_MGMT_PORT_INVALID_PARAM;
 | |
| +
 | |
| +	/* Max sure the buffer size is valid */
 | |
| +	if (buffer_len < 1)
 | |
| +		return CVMX_MGMT_PORT_INVALID_PARAM;
 | |
| +
 | |
| +	if (buffer == NULL)
 | |
| +		return CVMX_MGMT_PORT_INVALID_PARAM;
 | |
| +
 | |
| +	state = cvmx_mgmt_port_state_ptr + port;
 | |
| +
 | |
| +	cvmx_spinlock_lock(&state->lock);
 | |
| +
 | |
| +	/* Find out how many RX packets are pending */
 | |
| +	mix_ircnt.u64 = cvmx_read_csr(CVMX_MIXX_IRCNT(port));
 | |
| +	if (mix_ircnt.s.ircnt) {
 | |
| +		void *source = state->rx_buffers[state->rx_read_index];
 | |
| +		uint64_t *zero_check = source;
 | |
| +		/*
 | |
| +		 * CN56XX pass 1 has an errata where packets might
 | |
| +		 * start 8 bytes into the buffer instead of at their
 | |
| +		 * correct location. If the first 8 bytes is zero we
 | |
| +		 * assume this has happened.
 | |
| +		 */
 | |
| +		if (OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X)
 | |
| +		    && (*zero_check == 0))
 | |
| +			source += 8;
 | |
| +		/* Start off with zero bytes received */
 | |
| +		result = 0;
 | |
| +		/*
 | |
| +		 * While the completion code signals more data, copy
 | |
| +		 * the buffers into the user's data.
 | |
| +		 */
 | |
| +		while (state->rx_ring[state->rx_read_index].s.code == 16) {
 | |
| +			/* Only copy what will fit in the user's buffer */
 | |
| +			int length = state->rx_ring[state->rx_read_index].s.len;
 | |
| +			if (length > buffer_len)
 | |
| +				length = buffer_len;
 | |
| +			memcpy(buffer, source, length);
 | |
| +			/*
 | |
| +			 * Reduce the size of the buffer to the
 | |
| +			 * remaining space. If we run out we will
 | |
| +			 * signal an error when the code 15 buffer
 | |
| +			 * doesn't fit.
 | |
| +			 */
 | |
| +			buffer += length;
 | |
| +			buffer_len -= length;
 | |
| +			result += length;
 | |
| +			/*
 | |
| +			 * Update this buffer for reuse in future
 | |
| +			 * receives. This size is -8 due to an errata
 | |
| +			 * for CN56XX pass 1.
 | |
| +			 */
 | |
| +			state->rx_ring[state->rx_read_index].s.code = 0;
 | |
| +			state->rx_ring[state->rx_read_index].s.len =
 | |
| +			    CVMX_MGMT_PORT_RX_BUFFER_SIZE - 8;
 | |
| +			state->rx_read_index =
 | |
| +			    (state->rx_read_index +
 | |
| +			     1) % CVMX_MGMT_PORT_NUM_RX_BUFFERS;
 | |
| +			/*
 | |
| +			 * Zero the beginning of the buffer for use by
 | |
| +			 * the errata check.
 | |
| +			 */
 | |
| +			*zero_check = 0;
 | |
| +			CVMX_SYNCWS;
 | |
| +			/* Increment the number of RX buffers */
 | |
| +			cvmx_write_csr(CVMX_MIXX_IRING2(port), 1);
 | |
| +			source = state->rx_buffers[state->rx_read_index];
 | |
| +			zero_check = source;
 | |
| +		}
 | |
| +
 | |
| +		/* Check for the final good completion code */
 | |
| +		if (state->rx_ring[state->rx_read_index].s.code == 15) {
 | |
| +			if (buffer_len >=
 | |
| +			    state->rx_ring[state->rx_read_index].s.len) {
 | |
| +				int length =
 | |
| +				    state->rx_ring[state->rx_read_index].s.len;
 | |
| +				memcpy(buffer, source, length);
 | |
| +				result += length;
 | |
| +			} else {
 | |
| +				/* Not enough room for the packet */
 | |
| +				cvmx_dprintf("ERROR: cvmx_mgmt_port_receive: "
 | |
| +					"Packet (%d) larger than "
 | |
| +					"supplied buffer (%d)\n",
 | |
| +					state->rx_ring[state->rx_read_index].s.len,
 | |
| +					     buffer_len);
 | |
| +				result = CVMX_MGMT_PORT_NO_MEMORY;
 | |
| +			}
 | |
| +		} else {
 | |
| +			union cvmx_agl_gmx_prtx_cfg agl_gmx_prtx;
 | |
| +			cvmx_dprintf("ERROR: cvmx_mgmt_port_receive: Receive "
 | |
| +				"error code %d. Packet dropped(Len %d)\n",
 | |
| +				state->rx_ring[state->rx_read_index].s.code,
 | |
| +				state->rx_ring[state->rx_read_index].s.len +
 | |
| +				result);
 | |
| +			result = -state->rx_ring[state->rx_read_index].s.code;
 | |
| +
 | |
| +			/* Check to see if we need to change the duplex. */
 | |
| +			agl_gmx_prtx.u64 =
 | |
| +			    cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port));
 | |
| +			if (cvmx_mgmt_port_get_link(port) < 0)
 | |
| +				agl_gmx_prtx.s.duplex = 0;
 | |
| +			else
 | |
| +				agl_gmx_prtx.s.duplex = 1;
 | |
| +			cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port),
 | |
| +				       agl_gmx_prtx.u64);
 | |
| +		}
 | |
| +
 | |
| +		/*
 | |
| +		 * Clean out the ring buffer entry. This size is -8
 | |
| +		 * due to an errata for CN56XX pass 1.
 | |
| +		 */
 | |
| +		state->rx_ring[state->rx_read_index].s.code = 0;
 | |
| +		state->rx_ring[state->rx_read_index].s.len =
 | |
| +		    CVMX_MGMT_PORT_RX_BUFFER_SIZE - 8;
 | |
| +		state->rx_read_index =
 | |
| +		    (state->rx_read_index + 1) % CVMX_MGMT_PORT_NUM_RX_BUFFERS;
 | |
| +		/*
 | |
| +		 * Zero the beginning of the buffer for use by the
 | |
| +		 * errata check.
 | |
| +		 */
 | |
| +		*zero_check = 0;
 | |
| +		CVMX_SYNCWS;
 | |
| +		/* Increment the number of RX buffers */
 | |
| +		cvmx_write_csr(CVMX_MIXX_IRING2(port), 1);
 | |
| +		/* Decrement the pending RX count */
 | |
| +		cvmx_write_csr(CVMX_MIXX_IRCNT(port), 1);
 | |
| +	} else {
 | |
| +		/* No packets available */
 | |
| +		result = 0;
 | |
| +	}
 | |
| +	cvmx_spinlock_unlock(&state->lock);
 | |
| +	return result;
 | |
| +}
 | |
| +
 | |
| +/**
 | |
| + * Get the management port link status:
 | |
| + * 100 = 100Mbps, full duplex
 | |
| + * 10 = 10Mbps, full duplex
 | |
| + * 0 = Link down
 | |
| + * -10 = 10Mpbs, half duplex
 | |
| + * -100 = 100Mbps, half duplex
 | |
| + *
 | |
| + * @port:   Management port
 | |
| + *
 | |
| + * Returns
 | |
| + */
 | |
| +int cvmx_mgmt_port_get_link(int port)
 | |
| +{
 | |
| +	struct cvmx_mgmt_port_state *state;
 | |
| +	int phy_status;
 | |
| +	int duplex;
 | |
| +
 | |
| +	if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports()))
 | |
| +		return CVMX_MGMT_PORT_INVALID_PARAM;
 | |
| +
 | |
| +	state = cvmx_mgmt_port_state_ptr + port;
 | |
| +
 | |
| +	/* Assume 100Mbps if we don't know the PHY address */
 | |
| +	if (state->phy_id == -1)
 | |
| +		return 100;
 | |
| +
 | |
| +	/* Read the PHY state */
 | |
| +	phy_status =
 | |
| +	    cvmx_mdio_read(state->phy_id >> 8, state->phy_id & 0xff, 17);
 | |
| +
 | |
| +	/* Only return a link if the PHY has finished auto negotiation
 | |
| +	   and set the resolved bit (bit 11) */
 | |
| +	if (!(phy_status & (1 << 11)))
 | |
| +		return 0;
 | |
| +
 | |
| +	/* Create multiple factor to represent duplex */
 | |
| +	if ((phy_status >> 13) & 1)
 | |
| +		duplex = 1;
 | |
| +	else
 | |
| +		duplex = -1;
 | |
| +
 | |
| +	/* Speed is encoded on bits 15-14 */
 | |
| +	switch ((phy_status >> 14) & 3) {
 | |
| +	case 0:		/* 10 Mbps */
 | |
| +		return 10 * duplex;
 | |
| +	case 1:		/* 100 Mbps */
 | |
| +		return 100 * duplex;
 | |
| +	default:
 | |
| +		return 0;
 | |
| +	}
 | |
| +}
 | |
| +
 | |
| +/**
 | |
| + * Set the MAC address for a management port
 | |
| + *
 | |
| + * @port:   Management port
 | |
| + * @mac:    New MAC address. The lower 6 bytes are used.
 | |
| + *
 | |
| + * Returns CVMX_MGMT_PORT_SUCCESS or an error code
 | |
| + */
 | |
| +enum cvmx_mgmt_port_result cvmx_mgmt_port_set_mac(int port, uint64_t mac)
 | |
| +{
 | |
| +	struct cvmx_mgmt_port_state *state;
 | |
| +	union cvmx_agl_gmx_rxx_adr_ctl agl_gmx_rxx_adr_ctl;
 | |
| +
 | |
| +	if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports()))
 | |
| +		return CVMX_MGMT_PORT_INVALID_PARAM;
 | |
| +
 | |
| +	state = cvmx_mgmt_port_state_ptr + port;
 | |
| +
 | |
| +	cvmx_spinlock_lock(&state->lock);
 | |
| +
 | |
| +	agl_gmx_rxx_adr_ctl.u64 = 0;
 | |
| +	/* Only accept matching MAC addresses */
 | |
| +	agl_gmx_rxx_adr_ctl.s.cam_mode = 1;
 | |
| +	/* Drop multicast */
 | |
| +	agl_gmx_rxx_adr_ctl.s.mcst = 0;
 | |
| +	/* Allow broadcast */
 | |
| +	agl_gmx_rxx_adr_ctl.s.bcst = 1;
 | |
| +	cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CTL(port), agl_gmx_rxx_adr_ctl.u64);
 | |
| +
 | |
| +	/* Only using one of the CAMs */
 | |
| +	cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM0(port), (mac >> 40) & 0xff);
 | |
| +	cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM1(port), (mac >> 32) & 0xff);
 | |
| +	cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM2(port), (mac >> 24) & 0xff);
 | |
| +	cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM3(port), (mac >> 16) & 0xff);
 | |
| +	cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM4(port), (mac >> 8) & 0xff);
 | |
| +	cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM5(port), (mac >> 0) & 0xff);
 | |
| +	cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM_EN(port), 1);
 | |
| +	state->mac = mac;
 | |
| +
 | |
| +	cvmx_spinlock_unlock(&state->lock);
 | |
| +	return CVMX_MGMT_PORT_SUCCESS;
 | |
| +}
 | |
| +
 | |
| +/**
 | |
| + * Get the MAC address for a management port
 | |
| + *
 | |
| + * @port:   Management port
 | |
| + *
 | |
| + * Returns MAC address
 | |
| + */
 | |
| +uint64_t cvmx_mgmt_port_get_mac(int port)
 | |
| +{
 | |
| +	if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports()))
 | |
| +		return CVMX_MGMT_PORT_INVALID_PARAM;
 | |
| +
 | |
| +	return cvmx_mgmt_port_state_ptr[port].mac;
 | |
| +}
 | |
| +
 | |
| +/**
 | |
| + * Set the multicast list.
 | |
| + *
 | |
| + * @port:   Management port
 | |
| + * @flags:  Interface flags
 | |
| + *
 | |
| + * Returns
 | |
| + */
 | |
| +void cvmx_mgmt_port_set_multicast_list(int port, int flags)
 | |
| +{
 | |
| +	struct cvmx_mgmt_port_state *state;
 | |
| +	union cvmx_agl_gmx_rxx_adr_ctl agl_gmx_rxx_adr_ctl;
 | |
| +
 | |
| +	if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports()))
 | |
| +		return;
 | |
| +
 | |
| +	state = cvmx_mgmt_port_state_ptr + port;
 | |
| +
 | |
| +	cvmx_spinlock_lock(&state->lock);
 | |
| +
 | |
| +	agl_gmx_rxx_adr_ctl.u64 = cvmx_read_csr(CVMX_AGL_GMX_RXX_ADR_CTL(port));
 | |
| +
 | |
| +	/* Allow broadcast MAC addresses */
 | |
| +	if (!agl_gmx_rxx_adr_ctl.s.bcst)
 | |
| +		agl_gmx_rxx_adr_ctl.s.bcst = 1;
 | |
| +
 | |
| +	if ((flags & CVMX_IFF_ALLMULTI) || (flags & CVMX_IFF_PROMISC))
 | |
| +		/* Force accept multicast packets */
 | |
| +		agl_gmx_rxx_adr_ctl.s.mcst = 2;
 | |
| +	else
 | |
| +		/* Force reject multicast packets */
 | |
| +		agl_gmx_rxx_adr_ctl.s.mcst = 1;
 | |
| +
 | |
| +	if (flags & CVMX_IFF_PROMISC)
 | |
| +		/*
 | |
| +		 * Reject matches if promisc. Since CAM is shut off,
 | |
| +		 * should accept everything.
 | |
| +		 */
 | |
| +		agl_gmx_rxx_adr_ctl.s.cam_mode = 0;
 | |
| +	else
 | |
| +		/* Filter packets based on the CAM */
 | |
| +		agl_gmx_rxx_adr_ctl.s.cam_mode = 1;
 | |
| +
 | |
| +	cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CTL(port), agl_gmx_rxx_adr_ctl.u64);
 | |
| +
 | |
| +	if (flags & CVMX_IFF_PROMISC)
 | |
| +		cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM_EN(port), 0);
 | |
| +	else
 | |
| +		cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM_EN(port), 1);
 | |
| +
 | |
| +	cvmx_spinlock_unlock(&state->lock);
 | |
| +}
 | |
| +
 | |
| +/**
 | |
| + * Set the maximum packet allowed in. Size is specified
 | |
| + * including L2 but without FCS. A normal MTU would corespond
 | |
| + * to 1514 assuming the standard 14 byte L2 header.
 | |
| + *
 | |
| + * @port:   Management port
 | |
| + * @size_without_fcs:
 | |
| + *               Size in bytes without FCS
 | |
| + */
 | |
| +void cvmx_mgmt_port_set_max_packet_size(int port, int size_without_fcs)
 | |
| +{
 | |
| +	struct cvmx_mgmt_port_state *state;
 | |
| +
 | |
| +	if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports()))
 | |
| +		return;
 | |
| +
 | |
| +	state = cvmx_mgmt_port_state_ptr + port;
 | |
| +
 | |
| +	cvmx_spinlock_lock(&state->lock);
 | |
| +	cvmx_write_csr(CVMX_AGL_GMX_RXX_FRM_MAX(port), size_without_fcs);
 | |
| +	cvmx_write_csr(CVMX_AGL_GMX_RXX_JABBER(port),
 | |
| +		       (size_without_fcs + 7) & 0xfff8);
 | |
| +	cvmx_spinlock_unlock(&state->lock);
 | |
| +}
 | |
| --- /dev/null
 | |
| +++ b/drivers/net/octeon/cvmx-mgmt-port.h
 | |
| @@ -0,0 +1,168 @@
 | |
| +/***********************license start***************
 | |
| + * Author: Cavium Networks
 | |
| + *
 | |
| + * Contact: support@caviumnetworks.com
 | |
| + * This file is part of the OCTEON SDK
 | |
| + *
 | |
| + * Copyright (c) 2003-2008 Cavium Networks
 | |
| + *
 | |
| + * This file is free software; you can redistribute it and/or modify
 | |
| + * it under the terms of the GNU General Public License, Version 2, as
 | |
| + * published by the Free Software Foundation.
 | |
| + *
 | |
| + * This file is distributed in the hope that it will be useful, but
 | |
| + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
 | |
| + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
 | |
| + * NONINFRINGEMENT.  See the GNU General Public License for more
 | |
| + * details.
 | |
| + *
 | |
| + * You should have received a copy of the GNU General Public License
 | |
| + * along with this file; if not, write to the Free Software
 | |
| + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 | |
| + * or visit http://www.gnu.org/licenses/.
 | |
| + *
 | |
| + * This file may also be available under a different license from Cavium.
 | |
| + * Contact Cavium Networks for more information
 | |
| + ***********************license end**************************************/
 | |
| +
 | |
| +/**
 | |
| + *
 | |
| + * Support functions for managing the MII management port
 | |
| + *
 | |
| + */
 | |
| +
 | |
| +#ifndef __CVMX_MGMT_PORT_H__
 | |
| +#define __CVMX_MGMT_PORT_H__
 | |
| +
 | |
| +enum cvmx_mgmt_port_result {
 | |
| +	CVMX_MGMT_PORT_SUCCESS = 0,
 | |
| +	CVMX_MGMT_PORT_NO_MEMORY = -1,
 | |
| +	CVMX_MGMT_PORT_INVALID_PARAM = -2,
 | |
| +};
 | |
| +
 | |
| +/* Enumeration of Net Device interface flags. */
 | |
| +enum cvmx_mgmt_port_netdevice_flags {
 | |
| +	CVMX_IFF_PROMISC = 0x100,	/* receive all packets           */
 | |
| +	CVMX_IFF_ALLMULTI = 0x200,	/* receive all multicast packets */
 | |
| +};
 | |
| +
 | |
| +/**
 | |
| + * Called to initialize a management port for use. Multiple calls
 | |
| + * to this function accross applications is safe.
 | |
| + *
 | |
| + * @port:   Port to initialize
 | |
| + *
 | |
| + * Returns CVMX_MGMT_PORT_SUCCESS or an error code
 | |
| + */
 | |
| +extern enum cvmx_mgmt_port_result cvmx_mgmt_port_initialize(int port);
 | |
| +
 | |
| +/**
 | |
| + * Shutdown a management port. This currently disables packet IO
 | |
| + * but leaves all hardware and buffers. Another application can then
 | |
| + * call initialize() without redoing the hardware setup.
 | |
| + *
 | |
| + * @port:   Management port
 | |
| + *
 | |
| + * Returns CVMX_MGMT_PORT_SUCCESS or an error code
 | |
| + */
 | |
| +extern enum cvmx_mgmt_port_result cvmx_mgmt_port_shutdown(int port);
 | |
| +
 | |
| +/**
 | |
| + * Enable packet IO on a management port
 | |
| + *
 | |
| + * @port:   Management port
 | |
| + *
 | |
| + * Returns CVMX_MGMT_PORT_SUCCESS or an error code
 | |
| + */
 | |
| +extern enum cvmx_mgmt_port_result cvmx_mgmt_port_enable(int port);
 | |
| +
 | |
| +/**
 | |
| + * Disable packet IO on a management port
 | |
| + *
 | |
| + * @port:   Management port
 | |
| + *
 | |
| + * Returns CVMX_MGMT_PORT_SUCCESS or an error code
 | |
| + */
 | |
| +extern enum cvmx_mgmt_port_result cvmx_mgmt_port_disable(int port);
 | |
| +
 | |
| +/**
 | |
| + * Send a packet out the management port. The packet is copied so
 | |
| + * the input buffer isn't used after this call.
 | |
| + *
 | |
| + * @port:       Management port
 | |
| + * @packet_len: Length of the packet to send. It does not include the final CRC
 | |
| + * @buffer:     Packet data
 | |
| + *
 | |
| + * Returns CVMX_MGMT_PORT_SUCCESS or an error code
 | |
| + */
 | |
| +extern enum cvmx_mgmt_port_result cvmx_mgmt_port_send(int port, int packet_len,
 | |
| +						      void *buffer);
 | |
| +
 | |
| +/**
 | |
| + * Receive a packet from the management port.
 | |
| + *
 | |
| + * @port:       Management port
 | |
| + * @buffer_len: Size of the buffer to receive the packet into
 | |
| + * @buffer:     Buffer to receive the packet into
 | |
| + *
 | |
| + * Returns The size of the packet, or a negative erorr code on failure. Zero
 | |
| + *         means that no packets were available.
 | |
| + */
 | |
| +extern int cvmx_mgmt_port_receive(int port, int buffer_len, void *buffer);
 | |
| +
 | |
| +/**
 | |
| + * Get the management port link status:
 | |
| + * 100 = 100Mbps, full duplex
 | |
| + * 10 = 10Mbps, full duplex
 | |
| + * 0 = Link down
 | |
| + * -10 = 10Mpbs, half duplex
 | |
| + * -100 = 100Mbps, half duplex
 | |
| + *
 | |
| + * @port:   Management port
 | |
| + *
 | |
| + * Returns
 | |
| + */
 | |
| +extern int cvmx_mgmt_port_get_link(int port);
 | |
| +
 | |
| +/**
 | |
| + * Set the MAC address for a management port
 | |
| + *
 | |
| + * @port:   Management port
 | |
| + * @mac:    New MAC address. The lower 6 bytes are used.
 | |
| + *
 | |
| + * Returns CVMX_MGMT_PORT_SUCCESS or an error code
 | |
| + */
 | |
| +extern enum cvmx_mgmt_port_result cvmx_mgmt_port_set_mac(int port,
 | |
| +							 uint64_t mac);
 | |
| +
 | |
| +/**
 | |
| + * Get the MAC address for a management port
 | |
| + *
 | |
| + * @port:   Management port
 | |
| + *
 | |
| + * Returns MAC address
 | |
| + */
 | |
| +extern uint64_t cvmx_mgmt_port_get_mac(int port);
 | |
| +
 | |
| +/**
 | |
| + * Set the multicast list.
 | |
| + *
 | |
| + * @port:   Management port
 | |
| + * @flags:  Interface flags
 | |
| + *
 | |
| + * Returns
 | |
| + */
 | |
| +extern void cvmx_mgmt_port_set_multicast_list(int port, int flags);
 | |
| +
 | |
| +/**
 | |
| + * Set the maximum packet allowed in. Size is specified
 | |
| + * including L2 but without FCS. A normal MTU would corespond
 | |
| + * to 1514 assuming the standard 14 byte L2 header.
 | |
| + *
 | |
| + * @port:   Management port
 | |
| + * @size_without_crc:
 | |
| + *               Size in bytes without FCS
 | |
| + */
 | |
| +extern void cvmx_mgmt_port_set_max_packet_size(int port, int size_without_fcs);
 | |
| +
 | |
| +#endif /* __CVMX_MGMT_PORT_H__ */
 | |
| --- /dev/null
 | |
| +++ b/drivers/net/octeon/octeon-mgmt-port.c
 | |
| @@ -0,0 +1,389 @@
 | |
| +/*
 | |
| + *   Octeon Management Port Ethernet Driver
 | |
| + *
 | |
| + * This file is subject to the terms and conditions of the GNU General Public
 | |
| + * License.  See the file "COPYING" in the main directory of this archive
 | |
| + * for more details.
 | |
| + *
 | |
| + * Copyright (C) 2007, 2008 Cavium Networks
 | |
| + */
 | |
| +#include <linux/module.h>
 | |
| +#include <linux/kernel.h>
 | |
| +#include <linux/netdevice.h>
 | |
| +#include <linux/etherdevice.h>
 | |
| +#include <linux/ip.h>
 | |
| +#include <linux/string.h>
 | |
| +#include <linux/delay.h>
 | |
| +
 | |
| +#include <asm/octeon/octeon.h>
 | |
| +#include <asm/octeon/cvmx-mixx-defs.h>
 | |
| +#include <asm/octeon/cvmx-agl-defs.h>
 | |
| +
 | |
| +#include "cvmx-mgmt-port.h"
 | |
| +
 | |
| +static struct net_device *global_dev[2] = { NULL, NULL };
 | |
| +
 | |
| +#define DEBUGPRINT(format, ...) do {if (printk_ratelimit())		\
 | |
| +					printk(format, ##__VA_ARGS__);	\
 | |
| +				} while (0)
 | |
| +
 | |
| +/**
 | |
| + * This is the definition of the Ethernet driver's private
 | |
| + * driver state stored in dev->priv.
 | |
| + */
 | |
| +struct device_private {
 | |
| +	int port;
 | |
| +	struct net_device_stats stats;	/* Device statistics */
 | |
| +};
 | |
| +
 | |
| +
 | |
| +/**
 | |
| + * Packet transmit
 | |
| + *
 | |
| + * @param skb    Packet to send
 | |
| + * @param dev    Device info structure
 | |
| + * @return Always returns zero
 | |
| + */
 | |
| +static int packet_transmit(struct sk_buff *skb, struct net_device *dev)
 | |
| +{
 | |
| +	uint64_t flags;
 | |
| +	struct device_private *priv = netdev_priv(dev);
 | |
| +	enum cvmx_mgmt_port_result result;
 | |
| +	local_irq_save(flags);
 | |
| +	result = cvmx_mgmt_port_send(priv->port, skb->len, skb->data);
 | |
| +	local_irq_restore(flags);
 | |
| +	if (result == CVMX_MGMT_PORT_SUCCESS) {
 | |
| +		priv->stats.tx_packets++;
 | |
| +		priv->stats.tx_bytes += skb->len;
 | |
| +	} else {
 | |
| +		/* DEBUGPRINT("ERROR: cvmx_mgmt_port_send() failed with %d\n",
 | |
| +				result);
 | |
| +		*/
 | |
| +		priv->stats.tx_dropped++;
 | |
| +	}
 | |
| +	dev_kfree_skb(skb);
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +
 | |
| +/**
 | |
| + * Interrupt handler. The interrupt occurs whenever the POW
 | |
| + * transitions from 0->1 packets in our group.
 | |
| + *
 | |
| + * @param cpl
 | |
| + * @param dev_id
 | |
| + * @param regs
 | |
| + * @return
 | |
| + */
 | |
| +static irqreturn_t do_interrupt(int cpl, void *dev_id)
 | |
| +{
 | |
| +	uint64_t flags;
 | |
| +	struct sk_buff *skb;
 | |
| +	int result;
 | |
| +	char packet[2048];
 | |
| +	struct net_device *dev = (struct net_device *) dev_id;
 | |
| +	struct device_private *priv = netdev_priv(dev);
 | |
| +
 | |
| +	do {
 | |
| +		local_irq_save(flags);
 | |
| +		result = cvmx_mgmt_port_receive(priv->port, sizeof(packet),
 | |
| +						packet);
 | |
| +		local_irq_restore(flags);
 | |
| +
 | |
| +		/* Silently drop packets if we aren't up */
 | |
| +		if ((dev->flags & IFF_UP) == 0)
 | |
| +			continue;
 | |
| +
 | |
| +		if (result > 0) {
 | |
| +			skb = dev_alloc_skb(result);
 | |
| +			if (skb) {
 | |
| +				memcpy(skb_put(skb, result), packet, result);
 | |
| +				skb->protocol = eth_type_trans(skb, dev);
 | |
| +				skb->dev = dev;
 | |
| +				skb->ip_summed = CHECKSUM_NONE;
 | |
| +				priv->stats.rx_bytes += skb->len;
 | |
| +				priv->stats.rx_packets++;
 | |
| +				netif_rx(skb);
 | |
| +			} else {
 | |
| +				DEBUGPRINT("%s: Failed to allocate skbuff, "
 | |
| +					   "packet dropped\n",
 | |
| +					   dev->name);
 | |
| +				priv->stats.rx_dropped++;
 | |
| +			}
 | |
| +		} else if (result < 0) {
 | |
| +			DEBUGPRINT("%s: Receive error code %d, packet "
 | |
| +				   "dropped\n",
 | |
| +				   dev->name, result);
 | |
| +			priv->stats.rx_errors++;
 | |
| +		}
 | |
| +	} while (result != 0);
 | |
| +
 | |
| +	/* Clear any pending interrupts */
 | |
| +	cvmx_write_csr(CVMX_MIXX_ISR(priv->port),
 | |
| +		       cvmx_read_csr(CVMX_MIXX_ISR(priv->port)));
 | |
| +	cvmx_read_csr(CVMX_MIXX_ISR(priv->port));
 | |
| +
 | |
| +	return IRQ_HANDLED;
 | |
| +}
 | |
| +
 | |
| +
 | |
| +#ifdef CONFIG_NET_POLL_CONTROLLER
 | |
| +/**
 | |
| + * This is called when the kernel needs to manually poll the
 | |
| + * device. For Octeon, this is simply calling the interrupt
 | |
| + * handler. We actually poll all the devices, not just the
 | |
| + * one supplied.
 | |
| + *
 | |
| + * @param dev    Device to poll. Unused
 | |
| + */
 | |
| +static void device_poll_controller(struct net_device *dev)
 | |
| +{
 | |
| +	do_interrupt(0, dev);
 | |
| +}
 | |
| +#endif
 | |
| +
 | |
| +
 | |
| +/**
 | |
| + * Open a device for use. Device should be able to send and
 | |
| + * receive packets after this is called.
 | |
| + *
 | |
| + * @param dev    Device to bring up
 | |
| + * @return Zero on success
 | |
| + */
 | |
| +static int device_open(struct net_device *dev)
 | |
| +{
 | |
| +	/* Clear the statistics whenever the interface is brought up */
 | |
| +	struct device_private *priv = netdev_priv(dev);
 | |
| +	memset(&priv->stats, 0, sizeof(priv->stats));
 | |
| +	cvmx_mgmt_port_enable(priv->port);
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +
 | |
| +/**
 | |
| + * Stop an ethernet device. No more packets should be
 | |
| + * received from this device.
 | |
| + *
 | |
| + * @param dev    Device to bring down
 | |
| + * @return Zero on success
 | |
| + */
 | |
| +static int device_close(struct net_device *dev)
 | |
| +{
 | |
| +	struct device_private *priv = netdev_priv(dev);
 | |
| +	cvmx_mgmt_port_disable(priv->port);
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +
 | |
| +/**
 | |
| + * Get the low level ethernet statistics
 | |
| + *
 | |
| + * @param dev    Device to get the statistics from
 | |
| + * @return Pointer to the statistics
 | |
| + */
 | |
| +static struct net_device_stats *device_get_stats(struct net_device *dev)
 | |
| +{
 | |
| +	struct device_private *priv = netdev_priv(dev);
 | |
| +	return &priv->stats;
 | |
| +}
 | |
| +
 | |
| +/**
 | |
| + * Set the multicast list. Currently unimplemented.
 | |
| + *
 | |
| + * @param dev    Device to work on
 | |
| + */
 | |
| +static void ethernet_mgmt_port_set_multicast_list(struct net_device *dev)
 | |
| +{
 | |
| +	struct device_private *priv = netdev_priv(dev);
 | |
| +	int port = priv->port;
 | |
| +	int num_ports;
 | |
| +	if (OCTEON_IS_MODEL(OCTEON_CN52XX))
 | |
| +		num_ports = 2;
 | |
| +	else
 | |
| +		num_ports = 1;
 | |
| +	if (port < num_ports)
 | |
| +		cvmx_mgmt_port_set_multicast_list(port, dev->flags);
 | |
| +}
 | |
| +
 | |
| +/**
 | |
| + * Set the hardware MAC address for a management port device
 | |
| + *
 | |
| + * @param dev    Device to change the MAC address for
 | |
| + * @param addr   Address structure to change it too. MAC address is addr + 2.
 | |
| + * @return Zero on success
 | |
| + */
 | |
| +static int ethernet_mgmt_port_set_mac_address(struct net_device *dev,
 | |
| +					      void *addr)
 | |
| +{
 | |
| +	struct device_private *priv = netdev_priv(dev);
 | |
| +	union cvmx_agl_gmx_prtx_cfg agl_gmx_cfg;
 | |
| +	int port = priv->port;
 | |
| +	int num_ports;
 | |
| +
 | |
| +	if (OCTEON_IS_MODEL(OCTEON_CN52XX))
 | |
| +		num_ports = 2;
 | |
| +	else
 | |
| +		num_ports = 1;
 | |
| +
 | |
| +	memcpy(dev->dev_addr, addr + 2, 6);
 | |
| +
 | |
| +	if (port < num_ports) {
 | |
| +		int i;
 | |
| +		uint8_t *ptr = addr;
 | |
| +		uint64_t mac = 0;
 | |
| +		for (i = 0; i < 6; i++)
 | |
| +			mac = (mac<<8) | (uint64_t)(ptr[i+2]);
 | |
| +
 | |
| +		agl_gmx_cfg.u64 = cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port));
 | |
| +		cvmx_mgmt_port_set_mac(port, mac);
 | |
| +		ethernet_mgmt_port_set_multicast_list(dev);
 | |
| +		cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_cfg.u64);
 | |
| +	}
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +/**
 | |
| + * Per network device initialization
 | |
| + *
 | |
| + * @param dev    Device to initialize
 | |
| + * @return Zero on success
 | |
| + */
 | |
| +static int device_init(struct net_device *dev)
 | |
| +{
 | |
| +	struct device_private *priv = netdev_priv(dev);
 | |
| +	uint64_t mac = cvmx_mgmt_port_get_mac(priv->port);
 | |
| +
 | |
| +	dev->hard_start_xmit = packet_transmit;
 | |
| +	dev->get_stats = device_get_stats;
 | |
| +	dev->open = device_open;
 | |
| +	dev->stop = device_close;
 | |
| +#ifdef CONFIG_NET_POLL_CONTROLLER
 | |
| +	dev->poll_controller = device_poll_controller;
 | |
| +#endif
 | |
| +	dev->dev_addr[0] = (mac >> 40) & 0xff;
 | |
| +	dev->dev_addr[1] = (mac >> 32) & 0xff;
 | |
| +	dev->dev_addr[2] = (mac >> 24) & 0xff;
 | |
| +	dev->dev_addr[3] = (mac >> 16) & 0xff;
 | |
| +	dev->dev_addr[4] = (mac >> 8) & 0xff;
 | |
| +	dev->dev_addr[5] = (mac >> 0) & 0xff;
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +
 | |
| +/**
 | |
| + * Module/ driver initialization. Creates the linux network
 | |
| + * devices.
 | |
| + *
 | |
| + * @return Zero on success
 | |
| + */
 | |
| +static int __init ethernet_mgmt_port_init(void)
 | |
| +{
 | |
| +	struct net_device *dev;
 | |
| +	struct device_private *priv;
 | |
| +	union cvmx_mixx_irhwm mix_irhwm;
 | |
| +	union cvmx_mixx_intena mix_intena;
 | |
| +	int num_ports;
 | |
| +	int port;
 | |
| +
 | |
| +	if (!OCTEON_IS_MODEL(OCTEON_CN56XX) && !OCTEON_IS_MODEL(OCTEON_CN52XX))
 | |
| +		return 0;
 | |
| +
 | |
| +	if (OCTEON_IS_MODEL(OCTEON_CN52XX))
 | |
| +		num_ports = 2;
 | |
| +	else
 | |
| +		num_ports = 1;
 | |
| +
 | |
| +	printk("Octeon management port ethernet driver\n");
 | |
| +
 | |
| +	for (port = 0; port < num_ports; port++) {
 | |
| +		if (cvmx_mgmt_port_initialize(port) != CVMX_MGMT_PORT_SUCCESS) {
 | |
| +			pr_err("ERROR: cvmx_mgmt_port_initialize(%d) "
 | |
| +			       "failed\n", port);
 | |
| +			return -1;
 | |
| +		}
 | |
| +
 | |
| +		/* Setup is complete, create the virtual ethernet devices */
 | |
| +		dev = alloc_etherdev(sizeof(struct device_private));
 | |
| +		if (dev == NULL) {
 | |
| +			pr_err("ERROR: Failed to allocate ethernet device\n");
 | |
| +			return -1;
 | |
| +		}
 | |
| +
 | |
| +		dev->init = device_init;
 | |
| +		strcpy(dev->name, "mgmt%d");
 | |
| +
 | |
| +		/* Initialize the device private structure. */
 | |
| +		priv = netdev_priv(dev);
 | |
| +		memset(priv, 0, sizeof(struct device_private));
 | |
| +		priv->port = port;
 | |
| +
 | |
| +		if (register_netdev(dev) < 0) {
 | |
| +			pr_err("ERROR: Failed to register ethernet device\n");
 | |
| +			kfree(dev);
 | |
| +			return -1;
 | |
| +		}
 | |
| +
 | |
| +		/* Clear any pending interrupts */
 | |
| +		cvmx_write_csr(CVMX_MIXX_ISR(priv->port),
 | |
| +			       cvmx_read_csr(CVMX_MIXX_ISR(priv->port)));
 | |
| +
 | |
| +		/* Register an IRQ hander for to receive interrupts */
 | |
| +		dev->irq =
 | |
| +			(priv->port == 0) ? OCTEON_IRQ_MII0 : OCTEON_IRQ_MII1;
 | |
| +		if (request_irq(dev->irq, do_interrupt, IRQF_SHARED, dev->name,
 | |
| +			    dev))
 | |
| +			pr_err("ethernet-mgmt: Failed to assign "
 | |
| +			       "interrupt %d\n", dev->irq);
 | |
| +
 | |
| +		/* Interrupt every single RX packet */
 | |
| +		mix_irhwm.u64 = 0;
 | |
| +		mix_irhwm.s.irhwm = 0;
 | |
| +		cvmx_write_csr(CVMX_MIXX_IRHWM(priv->port), mix_irhwm.u64);
 | |
| +
 | |
| +		/* Enable receive interrupts */
 | |
| +		mix_intena.u64 = 0;
 | |
| +		mix_intena.s.ithena = 1;
 | |
| +		cvmx_write_csr(CVMX_MIXX_INTENA(priv->port), mix_intena.u64);
 | |
| +
 | |
| +		global_dev[priv->port] = dev;
 | |
| +
 | |
| +		dev->set_mac_address = ethernet_mgmt_port_set_mac_address;
 | |
| +		dev->set_multicast_list = ethernet_mgmt_port_set_multicast_list;
 | |
| +	}
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +
 | |
| +/**
 | |
| + * Module / driver shutdown
 | |
| + *
 | |
| + * @return Zero on success
 | |
| + */
 | |
| +static void __exit ethernet_mgmt_port_cleanup(void)
 | |
| +{
 | |
| +	int port;
 | |
| +	for (port = 0; port < 2; port++) {
 | |
| +		if (global_dev[port]) {
 | |
| +			struct device_private *priv =
 | |
| +				netdev_priv(global_dev[port]);
 | |
| +			/* Disable interrupt */
 | |
| +			cvmx_write_csr(CVMX_MIXX_IRHWM(priv->port), 0);
 | |
| +			cvmx_write_csr(CVMX_MIXX_INTENA(priv->port), 0);
 | |
| +			cvmx_mgmt_port_shutdown(priv->port);
 | |
| +
 | |
| +			/* Free the interrupt handler */
 | |
| +			free_irq(global_dev[port]->irq, global_dev[port]);
 | |
| +
 | |
| +			/* Free the ethernet devices */
 | |
| +			unregister_netdev(global_dev[port]);
 | |
| +			kfree(global_dev[port]);
 | |
| +			global_dev[port] = NULL;
 | |
| +		}
 | |
| +	}
 | |
| +}
 | |
| +
 | |
| +MODULE_LICENSE("GPL");
 | |
| +MODULE_AUTHOR("Cavium Networks <support@caviumnetworks.com>");
 | |
| +MODULE_DESCRIPTION("Cavium Networks Octeon management port ethernet driver.");
 | |
| +module_init(ethernet_mgmt_port_init);
 | |
| +module_exit(ethernet_mgmt_port_cleanup);
 |