diff options
author | florian <florian@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2009-05-06 18:12:49 +0000 |
---|---|---|
committer | florian <florian@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2009-05-06 18:12:49 +0000 |
commit | 154dbffb39af92e0b869922b286dee063508b5a1 (patch) | |
tree | cf4299f4d762af3e1b6e019c52aec7df513f54bd /target/linux/cavium-octeon/patches/006-octeon_mgmt_driver.patch | |
parent | f9a305d04432a0a09a1e99ca3bb57932c72cb355 (diff) |
[cavium-octeon] add support for the Cavium Octeon SoC, tested on a Mototech evaluation board
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@15640 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux/cavium-octeon/patches/006-octeon_mgmt_driver.patch')
-rw-r--r-- | target/linux/cavium-octeon/patches/006-octeon_mgmt_driver.patch | 2048 |
1 files changed, 2048 insertions, 0 deletions
diff --git a/target/linux/cavium-octeon/patches/006-octeon_mgmt_driver.patch b/target/linux/cavium-octeon/patches/006-octeon_mgmt_driver.patch new file mode 100644 index 000000000..ebac42ef1 --- /dev/null +++ b/target/linux/cavium-octeon/patches/006-octeon_mgmt_driver.patch @@ -0,0 +1,2048 @@ +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 + +diff --git a/arch/mips/include/asm/octeon/cvmx-mdio.h b/arch/mips/include/asm/octeon/cvmx-mdio.h +new file mode 100644 +index 0000000..89b0cc8 +--- /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 +diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig +index e9625a5..8c9d29e 100644 +--- a/drivers/net/Kconfig ++++ b/drivers/net/Kconfig +@@ -1864,6 +1864,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 +diff --git a/drivers/net/Makefile b/drivers/net/Makefile +index 4a92305..4cbc22e 100644 +--- a/drivers/net/Makefile ++++ b/drivers/net/Makefile +@@ -228,6 +228,7 @@ obj-$(CONFIG_PASEMI_MAC) += pasemi_mac_driver.o + pasemi_mac_driver-objs := pasemi_mac.o pasemi_mac_ethtool.o + obj-$(CONFIG_MLX4_CORE) += mlx4/ + obj-$(CONFIG_ENC28J60) += enc28j60.o ++obj-$(CONFIG_OCTEON_MGMT) += octeon/ + + obj-$(CONFIG_XTENSA_XT2000_SONIC) += xtsonic.o + +diff --git a/drivers/net/octeon/Makefile b/drivers/net/octeon/Makefile +new file mode 100644 +index 0000000..f32f394 +--- /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 +diff --git a/drivers/net/octeon/cvmx-mgmt-port.c b/drivers/net/octeon/cvmx-mgmt-port.c +new file mode 100644 +index 0000000..f60255a +--- /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); ++} +diff --git a/drivers/net/octeon/cvmx-mgmt-port.h b/drivers/net/octeon/cvmx-mgmt-port.h +new file mode 100644 +index 0000000..622168c +--- /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__ */ +diff --git a/drivers/net/octeon/octeon-mgmt-port.c b/drivers/net/octeon/octeon-mgmt-port.c +new file mode 100644 +index 0000000..9cffbb5 +--- /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); +-- +1.5.6.5 + +-- +To unsubscribe from this list: send the line "unsubscribe netdev" in +the body of a message to majordomo@vger.kernel.org +More majordomo info at http://vger.kernel.org/majordomo-info.html |