diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:49:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:49:45 +0000 |
commit | 2c3c1048746a4622d8c89a29670120dc8fab93c4 (patch) | |
tree | 848558de17fb3008cdf4d861b01ac7781903ce39 /drivers/net/ethernet/amd/xgbe | |
parent | Initial commit. (diff) | |
download | linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.tar.xz linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.zip |
Adding upstream version 6.1.76.upstream/6.1.76
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/net/ethernet/amd/xgbe')
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/Makefile | 12 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-common.h | 1725 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-dcb.c | 293 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c | 525 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-desc.c | 676 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-dev.c | 3654 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 2813 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c | 860 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-i2c.c | 514 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-main.c | 490 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | 1677 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-pci.c | 538 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c | 853 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c | 3470 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-platform.c | 629 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-ptp.c | 279 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe.h | 1391 |
17 files changed, 20399 insertions, 0 deletions
diff --git a/drivers/net/ethernet/amd/xgbe/Makefile b/drivers/net/ethernet/amd/xgbe/Makefile new file mode 100644 index 000000000..620785ffb --- /dev/null +++ b/drivers/net/ethernet/amd/xgbe/Makefile @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_AMD_XGBE) += amd-xgbe.o + +amd-xgbe-objs := xgbe-main.o xgbe-drv.o xgbe-dev.o \ + xgbe-desc.o xgbe-ethtool.o xgbe-mdio.o \ + xgbe-ptp.o \ + xgbe-i2c.o xgbe-phy-v1.o xgbe-phy-v2.o \ + xgbe-platform.o + +amd-xgbe-$(CONFIG_PCI) += xgbe-pci.o +amd-xgbe-$(CONFIG_AMD_XGBE_DCB) += xgbe-dcb.o +amd-xgbe-$(CONFIG_DEBUG_FS) += xgbe-debugfs.o diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h new file mode 100644 index 000000000..466273b22 --- /dev/null +++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h @@ -0,0 +1,1725 @@ +/* + * AMD 10Gb Ethernet driver + * + * This file is available to you under your choice of the following two + * licenses: + * + * License 1: GPLv2 + * + * Copyright (c) 2014-2016 Advanced Micro Devices, Inc. + * + * This file is free software; you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or (at + * your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * + * License 2: Modified BSD + * + * Copyright (c) 2014-2016 Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __XGBE_COMMON_H__ +#define __XGBE_COMMON_H__ + +/* DMA register offsets */ +#define DMA_MR 0x3000 +#define DMA_SBMR 0x3004 +#define DMA_ISR 0x3008 +#define DMA_AXIARCR 0x3010 +#define DMA_AXIAWCR 0x3018 +#define DMA_AXIAWARCR 0x301c +#define DMA_DSR0 0x3020 +#define DMA_DSR1 0x3024 +#define DMA_TXEDMACR 0x3040 +#define DMA_RXEDMACR 0x3044 + +/* DMA register entry bit positions and sizes */ +#define DMA_ISR_MACIS_INDEX 17 +#define DMA_ISR_MACIS_WIDTH 1 +#define DMA_ISR_MTLIS_INDEX 16 +#define DMA_ISR_MTLIS_WIDTH 1 +#define DMA_MR_INTM_INDEX 12 +#define DMA_MR_INTM_WIDTH 2 +#define DMA_MR_SWR_INDEX 0 +#define DMA_MR_SWR_WIDTH 1 +#define DMA_RXEDMACR_RDPS_INDEX 0 +#define DMA_RXEDMACR_RDPS_WIDTH 3 +#define DMA_SBMR_AAL_INDEX 12 +#define DMA_SBMR_AAL_WIDTH 1 +#define DMA_SBMR_EAME_INDEX 11 +#define DMA_SBMR_EAME_WIDTH 1 +#define DMA_SBMR_BLEN_INDEX 1 +#define DMA_SBMR_BLEN_WIDTH 7 +#define DMA_SBMR_RD_OSR_LMT_INDEX 16 +#define DMA_SBMR_RD_OSR_LMT_WIDTH 6 +#define DMA_SBMR_UNDEF_INDEX 0 +#define DMA_SBMR_UNDEF_WIDTH 1 +#define DMA_SBMR_WR_OSR_LMT_INDEX 24 +#define DMA_SBMR_WR_OSR_LMT_WIDTH 6 +#define DMA_TXEDMACR_TDPS_INDEX 0 +#define DMA_TXEDMACR_TDPS_WIDTH 3 + +/* DMA register values */ +#define DMA_SBMR_BLEN_256 256 +#define DMA_SBMR_BLEN_128 128 +#define DMA_SBMR_BLEN_64 64 +#define DMA_SBMR_BLEN_32 32 +#define DMA_SBMR_BLEN_16 16 +#define DMA_SBMR_BLEN_8 8 +#define DMA_SBMR_BLEN_4 4 +#define DMA_DSR_RPS_WIDTH 4 +#define DMA_DSR_TPS_WIDTH 4 +#define DMA_DSR_Q_WIDTH (DMA_DSR_RPS_WIDTH + DMA_DSR_TPS_WIDTH) +#define DMA_DSR0_RPS_START 8 +#define DMA_DSR0_TPS_START 12 +#define DMA_DSRX_FIRST_QUEUE 3 +#define DMA_DSRX_INC 4 +#define DMA_DSRX_QPR 4 +#define DMA_DSRX_RPS_START 0 +#define DMA_DSRX_TPS_START 4 +#define DMA_TPS_STOPPED 0x00 +#define DMA_TPS_SUSPENDED 0x06 + +/* DMA channel register offsets + * Multiple channels can be active. The first channel has registers + * that begin at 0x3100. Each subsequent channel has registers that + * are accessed using an offset of 0x80 from the previous channel. + */ +#define DMA_CH_BASE 0x3100 +#define DMA_CH_INC 0x80 + +#define DMA_CH_CR 0x00 +#define DMA_CH_TCR 0x04 +#define DMA_CH_RCR 0x08 +#define DMA_CH_TDLR_HI 0x10 +#define DMA_CH_TDLR_LO 0x14 +#define DMA_CH_RDLR_HI 0x18 +#define DMA_CH_RDLR_LO 0x1c +#define DMA_CH_TDTR_LO 0x24 +#define DMA_CH_RDTR_LO 0x2c +#define DMA_CH_TDRLR 0x30 +#define DMA_CH_RDRLR 0x34 +#define DMA_CH_IER 0x38 +#define DMA_CH_RIWT 0x3c +#define DMA_CH_CATDR_LO 0x44 +#define DMA_CH_CARDR_LO 0x4c +#define DMA_CH_CATBR_HI 0x50 +#define DMA_CH_CATBR_LO 0x54 +#define DMA_CH_CARBR_HI 0x58 +#define DMA_CH_CARBR_LO 0x5c +#define DMA_CH_SR 0x60 + +/* DMA channel register entry bit positions and sizes */ +#define DMA_CH_CR_PBLX8_INDEX 16 +#define DMA_CH_CR_PBLX8_WIDTH 1 +#define DMA_CH_CR_SPH_INDEX 24 +#define DMA_CH_CR_SPH_WIDTH 1 +#define DMA_CH_IER_AIE20_INDEX 15 +#define DMA_CH_IER_AIE20_WIDTH 1 +#define DMA_CH_IER_AIE_INDEX 14 +#define DMA_CH_IER_AIE_WIDTH 1 +#define DMA_CH_IER_FBEE_INDEX 12 +#define DMA_CH_IER_FBEE_WIDTH 1 +#define DMA_CH_IER_NIE20_INDEX 16 +#define DMA_CH_IER_NIE20_WIDTH 1 +#define DMA_CH_IER_NIE_INDEX 15 +#define DMA_CH_IER_NIE_WIDTH 1 +#define DMA_CH_IER_RBUE_INDEX 7 +#define DMA_CH_IER_RBUE_WIDTH 1 +#define DMA_CH_IER_RIE_INDEX 6 +#define DMA_CH_IER_RIE_WIDTH 1 +#define DMA_CH_IER_RSE_INDEX 8 +#define DMA_CH_IER_RSE_WIDTH 1 +#define DMA_CH_IER_TBUE_INDEX 2 +#define DMA_CH_IER_TBUE_WIDTH 1 +#define DMA_CH_IER_TIE_INDEX 0 +#define DMA_CH_IER_TIE_WIDTH 1 +#define DMA_CH_IER_TXSE_INDEX 1 +#define DMA_CH_IER_TXSE_WIDTH 1 +#define DMA_CH_RCR_PBL_INDEX 16 +#define DMA_CH_RCR_PBL_WIDTH 6 +#define DMA_CH_RCR_RBSZ_INDEX 1 +#define DMA_CH_RCR_RBSZ_WIDTH 14 +#define DMA_CH_RCR_SR_INDEX 0 +#define DMA_CH_RCR_SR_WIDTH 1 +#define DMA_CH_RIWT_RWT_INDEX 0 +#define DMA_CH_RIWT_RWT_WIDTH 8 +#define DMA_CH_SR_FBE_INDEX 12 +#define DMA_CH_SR_FBE_WIDTH 1 +#define DMA_CH_SR_RBU_INDEX 7 +#define DMA_CH_SR_RBU_WIDTH 1 +#define DMA_CH_SR_RI_INDEX 6 +#define DMA_CH_SR_RI_WIDTH 1 +#define DMA_CH_SR_RPS_INDEX 8 +#define DMA_CH_SR_RPS_WIDTH 1 +#define DMA_CH_SR_TBU_INDEX 2 +#define DMA_CH_SR_TBU_WIDTH 1 +#define DMA_CH_SR_TI_INDEX 0 +#define DMA_CH_SR_TI_WIDTH 1 +#define DMA_CH_SR_TPS_INDEX 1 +#define DMA_CH_SR_TPS_WIDTH 1 +#define DMA_CH_TCR_OSP_INDEX 4 +#define DMA_CH_TCR_OSP_WIDTH 1 +#define DMA_CH_TCR_PBL_INDEX 16 +#define DMA_CH_TCR_PBL_WIDTH 6 +#define DMA_CH_TCR_ST_INDEX 0 +#define DMA_CH_TCR_ST_WIDTH 1 +#define DMA_CH_TCR_TSE_INDEX 12 +#define DMA_CH_TCR_TSE_WIDTH 1 + +/* DMA channel register values */ +#define DMA_OSP_DISABLE 0x00 +#define DMA_OSP_ENABLE 0x01 +#define DMA_PBL_1 1 +#define DMA_PBL_2 2 +#define DMA_PBL_4 4 +#define DMA_PBL_8 8 +#define DMA_PBL_16 16 +#define DMA_PBL_32 32 +#define DMA_PBL_64 64 /* 8 x 8 */ +#define DMA_PBL_128 128 /* 8 x 16 */ +#define DMA_PBL_256 256 /* 8 x 32 */ +#define DMA_PBL_X8_DISABLE 0x00 +#define DMA_PBL_X8_ENABLE 0x01 + +/* MAC register offsets */ +#define MAC_TCR 0x0000 +#define MAC_RCR 0x0004 +#define MAC_PFR 0x0008 +#define MAC_WTR 0x000c +#define MAC_HTR0 0x0010 +#define MAC_VLANTR 0x0050 +#define MAC_VLANHTR 0x0058 +#define MAC_VLANIR 0x0060 +#define MAC_IVLANIR 0x0064 +#define MAC_RETMR 0x006c +#define MAC_Q0TFCR 0x0070 +#define MAC_RFCR 0x0090 +#define MAC_RQC0R 0x00a0 +#define MAC_RQC1R 0x00a4 +#define MAC_RQC2R 0x00a8 +#define MAC_RQC3R 0x00ac +#define MAC_ISR 0x00b0 +#define MAC_IER 0x00b4 +#define MAC_RTSR 0x00b8 +#define MAC_PMTCSR 0x00c0 +#define MAC_RWKPFR 0x00c4 +#define MAC_LPICSR 0x00d0 +#define MAC_LPITCR 0x00d4 +#define MAC_TIR 0x00e0 +#define MAC_VR 0x0110 +#define MAC_DR 0x0114 +#define MAC_HWF0R 0x011c +#define MAC_HWF1R 0x0120 +#define MAC_HWF2R 0x0124 +#define MAC_MDIOSCAR 0x0200 +#define MAC_MDIOSCCDR 0x0204 +#define MAC_MDIOISR 0x0214 +#define MAC_MDIOIER 0x0218 +#define MAC_MDIOCL22R 0x0220 +#define MAC_GPIOCR 0x0278 +#define MAC_GPIOSR 0x027c +#define MAC_MACA0HR 0x0300 +#define MAC_MACA0LR 0x0304 +#define MAC_MACA1HR 0x0308 +#define MAC_MACA1LR 0x030c +#define MAC_RSSCR 0x0c80 +#define MAC_RSSAR 0x0c88 +#define MAC_RSSDR 0x0c8c +#define MAC_TSCR 0x0d00 +#define MAC_SSIR 0x0d04 +#define MAC_STSR 0x0d08 +#define MAC_STNR 0x0d0c +#define MAC_STSUR 0x0d10 +#define MAC_STNUR 0x0d14 +#define MAC_TSAR 0x0d18 +#define MAC_TSSR 0x0d20 +#define MAC_TXSNR 0x0d30 +#define MAC_TXSSR 0x0d34 + +#define MAC_QTFCR_INC 4 +#define MAC_MACA_INC 4 +#define MAC_HTR_INC 4 + +#define MAC_RQC2_INC 4 +#define MAC_RQC2_Q_PER_REG 4 + +/* MAC register entry bit positions and sizes */ +#define MAC_HWF0R_ADDMACADRSEL_INDEX 18 +#define MAC_HWF0R_ADDMACADRSEL_WIDTH 5 +#define MAC_HWF0R_ARPOFFSEL_INDEX 9 +#define MAC_HWF0R_ARPOFFSEL_WIDTH 1 +#define MAC_HWF0R_EEESEL_INDEX 13 +#define MAC_HWF0R_EEESEL_WIDTH 1 +#define MAC_HWF0R_GMIISEL_INDEX 1 +#define MAC_HWF0R_GMIISEL_WIDTH 1 +#define MAC_HWF0R_MGKSEL_INDEX 7 +#define MAC_HWF0R_MGKSEL_WIDTH 1 +#define MAC_HWF0R_MMCSEL_INDEX 8 +#define MAC_HWF0R_MMCSEL_WIDTH 1 +#define MAC_HWF0R_RWKSEL_INDEX 6 +#define MAC_HWF0R_RWKSEL_WIDTH 1 +#define MAC_HWF0R_RXCOESEL_INDEX 16 +#define MAC_HWF0R_RXCOESEL_WIDTH 1 +#define MAC_HWF0R_SAVLANINS_INDEX 27 +#define MAC_HWF0R_SAVLANINS_WIDTH 1 +#define MAC_HWF0R_SMASEL_INDEX 5 +#define MAC_HWF0R_SMASEL_WIDTH 1 +#define MAC_HWF0R_TSSEL_INDEX 12 +#define MAC_HWF0R_TSSEL_WIDTH 1 +#define MAC_HWF0R_TSSTSSEL_INDEX 25 +#define MAC_HWF0R_TSSTSSEL_WIDTH 2 +#define MAC_HWF0R_TXCOESEL_INDEX 14 +#define MAC_HWF0R_TXCOESEL_WIDTH 1 +#define MAC_HWF0R_VLHASH_INDEX 4 +#define MAC_HWF0R_VLHASH_WIDTH 1 +#define MAC_HWF0R_VXN_INDEX 29 +#define MAC_HWF0R_VXN_WIDTH 1 +#define MAC_HWF1R_ADDR64_INDEX 14 +#define MAC_HWF1R_ADDR64_WIDTH 2 +#define MAC_HWF1R_ADVTHWORD_INDEX 13 +#define MAC_HWF1R_ADVTHWORD_WIDTH 1 +#define MAC_HWF1R_DBGMEMA_INDEX 19 +#define MAC_HWF1R_DBGMEMA_WIDTH 1 +#define MAC_HWF1R_DCBEN_INDEX 16 +#define MAC_HWF1R_DCBEN_WIDTH 1 +#define MAC_HWF1R_HASHTBLSZ_INDEX 24 +#define MAC_HWF1R_HASHTBLSZ_WIDTH 3 +#define MAC_HWF1R_L3L4FNUM_INDEX 27 +#define MAC_HWF1R_L3L4FNUM_WIDTH 4 +#define MAC_HWF1R_NUMTC_INDEX 21 +#define MAC_HWF1R_NUMTC_WIDTH 3 +#define MAC_HWF1R_RSSEN_INDEX 20 +#define MAC_HWF1R_RSSEN_WIDTH 1 +#define MAC_HWF1R_RXFIFOSIZE_INDEX 0 +#define MAC_HWF1R_RXFIFOSIZE_WIDTH 5 +#define MAC_HWF1R_SPHEN_INDEX 17 +#define MAC_HWF1R_SPHEN_WIDTH 1 +#define MAC_HWF1R_TSOEN_INDEX 18 +#define MAC_HWF1R_TSOEN_WIDTH 1 +#define MAC_HWF1R_TXFIFOSIZE_INDEX 6 +#define MAC_HWF1R_TXFIFOSIZE_WIDTH 5 +#define MAC_HWF2R_AUXSNAPNUM_INDEX 28 +#define MAC_HWF2R_AUXSNAPNUM_WIDTH 3 +#define MAC_HWF2R_PPSOUTNUM_INDEX 24 +#define MAC_HWF2R_PPSOUTNUM_WIDTH 3 +#define MAC_HWF2R_RXCHCNT_INDEX 12 +#define MAC_HWF2R_RXCHCNT_WIDTH 4 +#define MAC_HWF2R_RXQCNT_INDEX 0 +#define MAC_HWF2R_RXQCNT_WIDTH 4 +#define MAC_HWF2R_TXCHCNT_INDEX 18 +#define MAC_HWF2R_TXCHCNT_WIDTH 4 +#define MAC_HWF2R_TXQCNT_INDEX 6 +#define MAC_HWF2R_TXQCNT_WIDTH 4 +#define MAC_IER_TSIE_INDEX 12 +#define MAC_IER_TSIE_WIDTH 1 +#define MAC_ISR_MMCRXIS_INDEX 9 +#define MAC_ISR_MMCRXIS_WIDTH 1 +#define MAC_ISR_MMCTXIS_INDEX 10 +#define MAC_ISR_MMCTXIS_WIDTH 1 +#define MAC_ISR_PMTIS_INDEX 4 +#define MAC_ISR_PMTIS_WIDTH 1 +#define MAC_ISR_SMI_INDEX 1 +#define MAC_ISR_SMI_WIDTH 1 +#define MAC_ISR_TSIS_INDEX 12 +#define MAC_ISR_TSIS_WIDTH 1 +#define MAC_MACA1HR_AE_INDEX 31 +#define MAC_MACA1HR_AE_WIDTH 1 +#define MAC_MDIOIER_SNGLCOMPIE_INDEX 12 +#define MAC_MDIOIER_SNGLCOMPIE_WIDTH 1 +#define MAC_MDIOISR_SNGLCOMPINT_INDEX 12 +#define MAC_MDIOISR_SNGLCOMPINT_WIDTH 1 +#define MAC_MDIOSCAR_DA_INDEX 21 +#define MAC_MDIOSCAR_DA_WIDTH 5 +#define MAC_MDIOSCAR_PA_INDEX 16 +#define MAC_MDIOSCAR_PA_WIDTH 5 +#define MAC_MDIOSCAR_RA_INDEX 0 +#define MAC_MDIOSCAR_RA_WIDTH 16 +#define MAC_MDIOSCCDR_BUSY_INDEX 22 +#define MAC_MDIOSCCDR_BUSY_WIDTH 1 +#define MAC_MDIOSCCDR_CMD_INDEX 16 +#define MAC_MDIOSCCDR_CMD_WIDTH 2 +#define MAC_MDIOSCCDR_CR_INDEX 19 +#define MAC_MDIOSCCDR_CR_WIDTH 3 +#define MAC_MDIOSCCDR_DATA_INDEX 0 +#define MAC_MDIOSCCDR_DATA_WIDTH 16 +#define MAC_MDIOSCCDR_SADDR_INDEX 18 +#define MAC_MDIOSCCDR_SADDR_WIDTH 1 +#define MAC_PFR_HMC_INDEX 2 +#define MAC_PFR_HMC_WIDTH 1 +#define MAC_PFR_HPF_INDEX 10 +#define MAC_PFR_HPF_WIDTH 1 +#define MAC_PFR_HUC_INDEX 1 +#define MAC_PFR_HUC_WIDTH 1 +#define MAC_PFR_PM_INDEX 4 +#define MAC_PFR_PM_WIDTH 1 +#define MAC_PFR_PR_INDEX 0 +#define MAC_PFR_PR_WIDTH 1 +#define MAC_PFR_VTFE_INDEX 16 +#define MAC_PFR_VTFE_WIDTH 1 +#define MAC_PFR_VUCC_INDEX 22 +#define MAC_PFR_VUCC_WIDTH 1 +#define MAC_PMTCSR_MGKPKTEN_INDEX 1 +#define MAC_PMTCSR_MGKPKTEN_WIDTH 1 +#define MAC_PMTCSR_PWRDWN_INDEX 0 +#define MAC_PMTCSR_PWRDWN_WIDTH 1 +#define MAC_PMTCSR_RWKFILTRST_INDEX 31 +#define MAC_PMTCSR_RWKFILTRST_WIDTH 1 +#define MAC_PMTCSR_RWKPKTEN_INDEX 2 +#define MAC_PMTCSR_RWKPKTEN_WIDTH 1 +#define MAC_Q0TFCR_PT_INDEX 16 +#define MAC_Q0TFCR_PT_WIDTH 16 +#define MAC_Q0TFCR_TFE_INDEX 1 +#define MAC_Q0TFCR_TFE_WIDTH 1 +#define MAC_RCR_ACS_INDEX 1 +#define MAC_RCR_ACS_WIDTH 1 +#define MAC_RCR_CST_INDEX 2 +#define MAC_RCR_CST_WIDTH 1 +#define MAC_RCR_DCRCC_INDEX 3 +#define MAC_RCR_DCRCC_WIDTH 1 +#define MAC_RCR_HDSMS_INDEX 12 +#define MAC_RCR_HDSMS_WIDTH 3 +#define MAC_RCR_IPC_INDEX 9 +#define MAC_RCR_IPC_WIDTH 1 +#define MAC_RCR_JE_INDEX 8 +#define MAC_RCR_JE_WIDTH 1 +#define MAC_RCR_LM_INDEX 10 +#define MAC_RCR_LM_WIDTH 1 +#define MAC_RCR_RE_INDEX 0 +#define MAC_RCR_RE_WIDTH 1 +#define MAC_RFCR_PFCE_INDEX 8 +#define MAC_RFCR_PFCE_WIDTH 1 +#define MAC_RFCR_RFE_INDEX 0 +#define MAC_RFCR_RFE_WIDTH 1 +#define MAC_RFCR_UP_INDEX 1 +#define MAC_RFCR_UP_WIDTH 1 +#define MAC_RQC0R_RXQ0EN_INDEX 0 +#define MAC_RQC0R_RXQ0EN_WIDTH 2 +#define MAC_RSSAR_ADDRT_INDEX 2 +#define MAC_RSSAR_ADDRT_WIDTH 1 +#define MAC_RSSAR_CT_INDEX 1 +#define MAC_RSSAR_CT_WIDTH 1 +#define MAC_RSSAR_OB_INDEX 0 +#define MAC_RSSAR_OB_WIDTH 1 +#define MAC_RSSAR_RSSIA_INDEX 8 +#define MAC_RSSAR_RSSIA_WIDTH 8 +#define MAC_RSSCR_IP2TE_INDEX 1 +#define MAC_RSSCR_IP2TE_WIDTH 1 +#define MAC_RSSCR_RSSE_INDEX 0 +#define MAC_RSSCR_RSSE_WIDTH 1 +#define MAC_RSSCR_TCP4TE_INDEX 2 +#define MAC_RSSCR_TCP4TE_WIDTH 1 +#define MAC_RSSCR_UDP4TE_INDEX 3 +#define MAC_RSSCR_UDP4TE_WIDTH 1 +#define MAC_RSSDR_DMCH_INDEX 0 +#define MAC_RSSDR_DMCH_WIDTH 4 +#define MAC_SSIR_SNSINC_INDEX 8 +#define MAC_SSIR_SNSINC_WIDTH 8 +#define MAC_SSIR_SSINC_INDEX 16 +#define MAC_SSIR_SSINC_WIDTH 8 +#define MAC_TCR_SS_INDEX 29 +#define MAC_TCR_SS_WIDTH 2 +#define MAC_TCR_TE_INDEX 0 +#define MAC_TCR_TE_WIDTH 1 +#define MAC_TCR_VNE_INDEX 24 +#define MAC_TCR_VNE_WIDTH 1 +#define MAC_TCR_VNM_INDEX 25 +#define MAC_TCR_VNM_WIDTH 1 +#define MAC_TIR_TNID_INDEX 0 +#define MAC_TIR_TNID_WIDTH 16 +#define MAC_TSCR_AV8021ASMEN_INDEX 28 +#define MAC_TSCR_AV8021ASMEN_WIDTH 1 +#define MAC_TSCR_SNAPTYPSEL_INDEX 16 +#define MAC_TSCR_SNAPTYPSEL_WIDTH 2 +#define MAC_TSCR_TSADDREG_INDEX 5 +#define MAC_TSCR_TSADDREG_WIDTH 1 +#define MAC_TSCR_TSCFUPDT_INDEX 1 +#define MAC_TSCR_TSCFUPDT_WIDTH 1 +#define MAC_TSCR_TSCTRLSSR_INDEX 9 +#define MAC_TSCR_TSCTRLSSR_WIDTH 1 +#define MAC_TSCR_TSENA_INDEX 0 +#define MAC_TSCR_TSENA_WIDTH 1 +#define MAC_TSCR_TSENALL_INDEX 8 +#define MAC_TSCR_TSENALL_WIDTH 1 +#define MAC_TSCR_TSEVNTENA_INDEX 14 +#define MAC_TSCR_TSEVNTENA_WIDTH 1 +#define MAC_TSCR_TSINIT_INDEX 2 +#define MAC_TSCR_TSINIT_WIDTH 1 +#define MAC_TSCR_TSIPENA_INDEX 11 +#define MAC_TSCR_TSIPENA_WIDTH 1 +#define MAC_TSCR_TSIPV4ENA_INDEX 13 +#define MAC_TSCR_TSIPV4ENA_WIDTH 1 +#define MAC_TSCR_TSIPV6ENA_INDEX 12 +#define MAC_TSCR_TSIPV6ENA_WIDTH 1 +#define MAC_TSCR_TSMSTRENA_INDEX 15 +#define MAC_TSCR_TSMSTRENA_WIDTH 1 +#define MAC_TSCR_TSVER2ENA_INDEX 10 +#define MAC_TSCR_TSVER2ENA_WIDTH 1 +#define MAC_TSCR_TXTSSTSM_INDEX 24 +#define MAC_TSCR_TXTSSTSM_WIDTH 1 +#define MAC_TSSR_TXTSC_INDEX 15 +#define MAC_TSSR_TXTSC_WIDTH 1 +#define MAC_TXSNR_TXTSSTSMIS_INDEX 31 +#define MAC_TXSNR_TXTSSTSMIS_WIDTH 1 +#define MAC_VLANHTR_VLHT_INDEX 0 +#define MAC_VLANHTR_VLHT_WIDTH 16 +#define MAC_VLANIR_VLTI_INDEX 20 +#define MAC_VLANIR_VLTI_WIDTH 1 +#define MAC_VLANIR_CSVL_INDEX 19 +#define MAC_VLANIR_CSVL_WIDTH 1 +#define MAC_VLANTR_DOVLTC_INDEX 20 +#define MAC_VLANTR_DOVLTC_WIDTH 1 +#define MAC_VLANTR_ERSVLM_INDEX 19 +#define MAC_VLANTR_ERSVLM_WIDTH 1 +#define MAC_VLANTR_ESVL_INDEX 18 +#define MAC_VLANTR_ESVL_WIDTH 1 +#define MAC_VLANTR_ETV_INDEX 16 +#define MAC_VLANTR_ETV_WIDTH 1 +#define MAC_VLANTR_EVLS_INDEX 21 +#define MAC_VLANTR_EVLS_WIDTH 2 +#define MAC_VLANTR_EVLRXS_INDEX 24 +#define MAC_VLANTR_EVLRXS_WIDTH 1 +#define MAC_VLANTR_VL_INDEX 0 +#define MAC_VLANTR_VL_WIDTH 16 +#define MAC_VLANTR_VTHM_INDEX 25 +#define MAC_VLANTR_VTHM_WIDTH 1 +#define MAC_VLANTR_VTIM_INDEX 17 +#define MAC_VLANTR_VTIM_WIDTH 1 +#define MAC_VR_DEVID_INDEX 8 +#define MAC_VR_DEVID_WIDTH 8 +#define MAC_VR_SNPSVER_INDEX 0 +#define MAC_VR_SNPSVER_WIDTH 8 +#define MAC_VR_USERVER_INDEX 16 +#define MAC_VR_USERVER_WIDTH 8 + +/* MMC register offsets */ +#define MMC_CR 0x0800 +#define MMC_RISR 0x0804 +#define MMC_TISR 0x0808 +#define MMC_RIER 0x080c +#define MMC_TIER 0x0810 +#define MMC_TXOCTETCOUNT_GB_LO 0x0814 +#define MMC_TXOCTETCOUNT_GB_HI 0x0818 +#define MMC_TXFRAMECOUNT_GB_LO 0x081c +#define MMC_TXFRAMECOUNT_GB_HI 0x0820 +#define MMC_TXBROADCASTFRAMES_G_LO 0x0824 +#define MMC_TXBROADCASTFRAMES_G_HI 0x0828 +#define MMC_TXMULTICASTFRAMES_G_LO 0x082c +#define MMC_TXMULTICASTFRAMES_G_HI 0x0830 +#define MMC_TX64OCTETS_GB_LO 0x0834 +#define MMC_TX64OCTETS_GB_HI 0x0838 +#define MMC_TX65TO127OCTETS_GB_LO 0x083c +#define MMC_TX65TO127OCTETS_GB_HI 0x0840 +#define MMC_TX128TO255OCTETS_GB_LO 0x0844 +#define MMC_TX128TO255OCTETS_GB_HI 0x0848 +#define MMC_TX256TO511OCTETS_GB_LO 0x084c +#define MMC_TX256TO511OCTETS_GB_HI 0x0850 +#define MMC_TX512TO1023OCTETS_GB_LO 0x0854 +#define MMC_TX512TO1023OCTETS_GB_HI 0x0858 +#define MMC_TX1024TOMAXOCTETS_GB_LO 0x085c +#define MMC_TX1024TOMAXOCTETS_GB_HI 0x0860 +#define MMC_TXUNICASTFRAMES_GB_LO 0x0864 +#define MMC_TXUNICASTFRAMES_GB_HI 0x0868 +#define MMC_TXMULTICASTFRAMES_GB_LO 0x086c +#define MMC_TXMULTICASTFRAMES_GB_HI 0x0870 +#define MMC_TXBROADCASTFRAMES_GB_LO 0x0874 +#define MMC_TXBROADCASTFRAMES_GB_HI 0x0878 +#define MMC_TXUNDERFLOWERROR_LO 0x087c +#define MMC_TXUNDERFLOWERROR_HI 0x0880 +#define MMC_TXOCTETCOUNT_G_LO 0x0884 +#define MMC_TXOCTETCOUNT_G_HI 0x0888 +#define MMC_TXFRAMECOUNT_G_LO 0x088c +#define MMC_TXFRAMECOUNT_G_HI 0x0890 +#define MMC_TXPAUSEFRAMES_LO 0x0894 +#define MMC_TXPAUSEFRAMES_HI 0x0898 +#define MMC_TXVLANFRAMES_G_LO 0x089c +#define MMC_TXVLANFRAMES_G_HI 0x08a0 +#define MMC_RXFRAMECOUNT_GB_LO 0x0900 +#define MMC_RXFRAMECOUNT_GB_HI 0x0904 +#define MMC_RXOCTETCOUNT_GB_LO 0x0908 +#define MMC_RXOCTETCOUNT_GB_HI 0x090c +#define MMC_RXOCTETCOUNT_G_LO 0x0910 +#define MMC_RXOCTETCOUNT_G_HI 0x0914 +#define MMC_RXBROADCASTFRAMES_G_LO 0x0918 +#define MMC_RXBROADCASTFRAMES_G_HI 0x091c +#define MMC_RXMULTICASTFRAMES_G_LO 0x0920 +#define MMC_RXMULTICASTFRAMES_G_HI 0x0924 +#define MMC_RXCRCERROR_LO 0x0928 +#define MMC_RXCRCERROR_HI 0x092c +#define MMC_RXRUNTERROR 0x0930 +#define MMC_RXJABBERERROR 0x0934 +#define MMC_RXUNDERSIZE_G 0x0938 +#define MMC_RXOVERSIZE_G 0x093c +#define MMC_RX64OCTETS_GB_LO 0x0940 +#define MMC_RX64OCTETS_GB_HI 0x0944 +#define MMC_RX65TO127OCTETS_GB_LO 0x0948 +#define MMC_RX65TO127OCTETS_GB_HI 0x094c +#define MMC_RX128TO255OCTETS_GB_LO 0x0950 +#define MMC_RX128TO255OCTETS_GB_HI 0x0954 +#define MMC_RX256TO511OCTETS_GB_LO 0x0958 +#define MMC_RX256TO511OCTETS_GB_HI 0x095c +#define MMC_RX512TO1023OCTETS_GB_LO 0x0960 +#define MMC_RX512TO1023OCTETS_GB_HI 0x0964 +#define MMC_RX1024TOMAXOCTETS_GB_LO 0x0968 +#define MMC_RX1024TOMAXOCTETS_GB_HI 0x096c +#define MMC_RXUNICASTFRAMES_G_LO 0x0970 +#define MMC_RXUNICASTFRAMES_G_HI 0x0974 +#define MMC_RXLENGTHERROR_LO 0x0978 +#define MMC_RXLENGTHERROR_HI 0x097c +#define MMC_RXOUTOFRANGETYPE_LO 0x0980 +#define MMC_RXOUTOFRANGETYPE_HI 0x0984 +#define MMC_RXPAUSEFRAMES_LO 0x0988 +#define MMC_RXPAUSEFRAMES_HI 0x098c +#define MMC_RXFIFOOVERFLOW_LO 0x0990 +#define MMC_RXFIFOOVERFLOW_HI 0x0994 +#define MMC_RXVLANFRAMES_GB_LO 0x0998 +#define MMC_RXVLANFRAMES_GB_HI 0x099c +#define MMC_RXWATCHDOGERROR 0x09a0 + +/* MMC register entry bit positions and sizes */ +#define MMC_CR_CR_INDEX 0 +#define MMC_CR_CR_WIDTH 1 +#define MMC_CR_CSR_INDEX 1 +#define MMC_CR_CSR_WIDTH 1 +#define MMC_CR_ROR_INDEX 2 +#define MMC_CR_ROR_WIDTH 1 +#define MMC_CR_MCF_INDEX 3 +#define MMC_CR_MCF_WIDTH 1 +#define MMC_CR_MCT_INDEX 4 +#define MMC_CR_MCT_WIDTH 2 +#define MMC_RIER_ALL_INTERRUPTS_INDEX 0 +#define MMC_RIER_ALL_INTERRUPTS_WIDTH 23 +#define MMC_RISR_RXFRAMECOUNT_GB_INDEX 0 +#define MMC_RISR_RXFRAMECOUNT_GB_WIDTH 1 +#define MMC_RISR_RXOCTETCOUNT_GB_INDEX 1 +#define MMC_RISR_RXOCTETCOUNT_GB_WIDTH 1 +#define MMC_RISR_RXOCTETCOUNT_G_INDEX 2 +#define MMC_RISR_RXOCTETCOUNT_G_WIDTH 1 +#define MMC_RISR_RXBROADCASTFRAMES_G_INDEX 3 +#define MMC_RISR_RXBROADCASTFRAMES_G_WIDTH 1 +#define MMC_RISR_RXMULTICASTFRAMES_G_INDEX 4 +#define MMC_RISR_RXMULTICASTFRAMES_G_WIDTH 1 +#define MMC_RISR_RXCRCERROR_INDEX 5 +#define MMC_RISR_RXCRCERROR_WIDTH 1 +#define MMC_RISR_RXRUNTERROR_INDEX 6 +#define MMC_RISR_RXRUNTERROR_WIDTH 1 +#define MMC_RISR_RXJABBERERROR_INDEX 7 +#define MMC_RISR_RXJABBERERROR_WIDTH 1 +#define MMC_RISR_RXUNDERSIZE_G_INDEX 8 +#define MMC_RISR_RXUNDERSIZE_G_WIDTH 1 +#define MMC_RISR_RXOVERSIZE_G_INDEX 9 +#define MMC_RISR_RXOVERSIZE_G_WIDTH 1 +#define MMC_RISR_RX64OCTETS_GB_INDEX 10 +#define MMC_RISR_RX64OCTETS_GB_WIDTH 1 +#define MMC_RISR_RX65TO127OCTETS_GB_INDEX 11 +#define MMC_RISR_RX65TO127OCTETS_GB_WIDTH 1 +#define MMC_RISR_RX128TO255OCTETS_GB_INDEX 12 +#define MMC_RISR_RX128TO255OCTETS_GB_WIDTH 1 +#define MMC_RISR_RX256TO511OCTETS_GB_INDEX 13 +#define MMC_RISR_RX256TO511OCTETS_GB_WIDTH 1 +#define MMC_RISR_RX512TO1023OCTETS_GB_INDEX 14 +#define MMC_RISR_RX512TO1023OCTETS_GB_WIDTH 1 +#define MMC_RISR_RX1024TOMAXOCTETS_GB_INDEX 15 +#define MMC_RISR_RX1024TOMAXOCTETS_GB_WIDTH 1 +#define MMC_RISR_RXUNICASTFRAMES_G_INDEX 16 +#define MMC_RISR_RXUNICASTFRAMES_G_WIDTH 1 +#define MMC_RISR_RXLENGTHERROR_INDEX 17 +#define MMC_RISR_RXLENGTHERROR_WIDTH 1 +#define MMC_RISR_RXOUTOFRANGETYPE_INDEX 18 +#define MMC_RISR_RXOUTOFRANGETYPE_WIDTH 1 +#define MMC_RISR_RXPAUSEFRAMES_INDEX 19 +#define MMC_RISR_RXPAUSEFRAMES_WIDTH 1 +#define MMC_RISR_RXFIFOOVERFLOW_INDEX 20 +#define MMC_RISR_RXFIFOOVERFLOW_WIDTH 1 +#define MMC_RISR_RXVLANFRAMES_GB_INDEX 21 +#define MMC_RISR_RXVLANFRAMES_GB_WIDTH 1 +#define MMC_RISR_RXWATCHDOGERROR_INDEX 22 +#define MMC_RISR_RXWATCHDOGERROR_WIDTH 1 +#define MMC_TIER_ALL_INTERRUPTS_INDEX 0 +#define MMC_TIER_ALL_INTERRUPTS_WIDTH 18 +#define MMC_TISR_TXOCTETCOUNT_GB_INDEX 0 +#define MMC_TISR_TXOCTETCOUNT_GB_WIDTH 1 +#define MMC_TISR_TXFRAMECOUNT_GB_INDEX 1 +#define MMC_TISR_TXFRAMECOUNT_GB_WIDTH 1 +#define MMC_TISR_TXBROADCASTFRAMES_G_INDEX 2 +#define MMC_TISR_TXBROADCASTFRAMES_G_WIDTH 1 +#define MMC_TISR_TXMULTICASTFRAMES_G_INDEX 3 +#define MMC_TISR_TXMULTICASTFRAMES_G_WIDTH 1 +#define MMC_TISR_TX64OCTETS_GB_INDEX 4 +#define MMC_TISR_TX64OCTETS_GB_WIDTH 1 +#define MMC_TISR_TX65TO127OCTETS_GB_INDEX 5 +#define MMC_TISR_TX65TO127OCTETS_GB_WIDTH 1 +#define MMC_TISR_TX128TO255OCTETS_GB_INDEX 6 +#define MMC_TISR_TX128TO255OCTETS_GB_WIDTH 1 +#define MMC_TISR_TX256TO511OCTETS_GB_INDEX 7 +#define MMC_TISR_TX256TO511OCTETS_GB_WIDTH 1 +#define MMC_TISR_TX512TO1023OCTETS_GB_INDEX 8 +#define MMC_TISR_TX512TO1023OCTETS_GB_WIDTH 1 +#define MMC_TISR_TX1024TOMAXOCTETS_GB_INDEX 9 +#define MMC_TISR_TX1024TOMAXOCTETS_GB_WIDTH 1 +#define MMC_TISR_TXUNICASTFRAMES_GB_INDEX 10 +#define MMC_TISR_TXUNICASTFRAMES_GB_WIDTH 1 +#define MMC_TISR_TXMULTICASTFRAMES_GB_INDEX 11 +#define MMC_TISR_TXMULTICASTFRAMES_GB_WIDTH 1 +#define MMC_TISR_TXBROADCASTFRAMES_GB_INDEX 12 +#define MMC_TISR_TXBROADCASTFRAMES_GB_WIDTH 1 +#define MMC_TISR_TXUNDERFLOWERROR_INDEX 13 +#define MMC_TISR_TXUNDERFLOWERROR_WIDTH 1 +#define MMC_TISR_TXOCTETCOUNT_G_INDEX 14 +#define MMC_TISR_TXOCTETCOUNT_G_WIDTH 1 +#define MMC_TISR_TXFRAMECOUNT_G_INDEX 15 +#define MMC_TISR_TXFRAMECOUNT_G_WIDTH 1 +#define MMC_TISR_TXPAUSEFRAMES_INDEX 16 +#define MMC_TISR_TXPAUSEFRAMES_WIDTH 1 +#define MMC_TISR_TXVLANFRAMES_G_INDEX 17 +#define MMC_TISR_TXVLANFRAMES_G_WIDTH 1 + +/* MTL register offsets */ +#define MTL_OMR 0x1000 +#define MTL_FDCR 0x1008 +#define MTL_FDSR 0x100c +#define MTL_FDDR 0x1010 +#define MTL_ISR 0x1020 +#define MTL_RQDCM0R 0x1030 +#define MTL_TCPM0R 0x1040 +#define MTL_TCPM1R 0x1044 + +#define MTL_RQDCM_INC 4 +#define MTL_RQDCM_Q_PER_REG 4 +#define MTL_TCPM_INC 4 +#define MTL_TCPM_TC_PER_REG 4 + +/* MTL register entry bit positions and sizes */ +#define MTL_OMR_ETSALG_INDEX 5 +#define MTL_OMR_ETSALG_WIDTH 2 +#define MTL_OMR_RAA_INDEX 2 +#define MTL_OMR_RAA_WIDTH 1 + +/* MTL queue register offsets + * Multiple queues can be active. The first queue has registers + * that begin at 0x1100. Each subsequent queue has registers that + * are accessed using an offset of 0x80 from the previous queue. + */ +#define MTL_Q_BASE 0x1100 +#define MTL_Q_INC 0x80 + +#define MTL_Q_TQOMR 0x00 +#define MTL_Q_TQUR 0x04 +#define MTL_Q_TQDR 0x08 +#define MTL_Q_RQOMR 0x40 +#define MTL_Q_RQMPOCR 0x44 +#define MTL_Q_RQDR 0x48 +#define MTL_Q_RQFCR 0x50 +#define MTL_Q_IER 0x70 +#define MTL_Q_ISR 0x74 + +/* MTL queue register entry bit positions and sizes */ +#define MTL_Q_RQDR_PRXQ_INDEX 16 +#define MTL_Q_RQDR_PRXQ_WIDTH 14 +#define MTL_Q_RQDR_RXQSTS_INDEX 4 +#define MTL_Q_RQDR_RXQSTS_WIDTH 2 +#define MTL_Q_RQFCR_RFA_INDEX 1 +#define MTL_Q_RQFCR_RFA_WIDTH 6 +#define MTL_Q_RQFCR_RFD_INDEX 17 +#define MTL_Q_RQFCR_RFD_WIDTH 6 +#define MTL_Q_RQOMR_EHFC_INDEX 7 +#define MTL_Q_RQOMR_EHFC_WIDTH 1 +#define MTL_Q_RQOMR_RQS_INDEX 16 +#define MTL_Q_RQOMR_RQS_WIDTH 9 +#define MTL_Q_RQOMR_RSF_INDEX 5 +#define MTL_Q_RQOMR_RSF_WIDTH 1 +#define MTL_Q_RQOMR_RTC_INDEX 0 +#define MTL_Q_RQOMR_RTC_WIDTH 2 +#define MTL_Q_TQDR_TRCSTS_INDEX 1 +#define MTL_Q_TQDR_TRCSTS_WIDTH 2 +#define MTL_Q_TQDR_TXQSTS_INDEX 4 +#define MTL_Q_TQDR_TXQSTS_WIDTH 1 +#define MTL_Q_TQOMR_FTQ_INDEX 0 +#define MTL_Q_TQOMR_FTQ_WIDTH 1 +#define MTL_Q_TQOMR_Q2TCMAP_INDEX 8 +#define MTL_Q_TQOMR_Q2TCMAP_WIDTH 3 +#define MTL_Q_TQOMR_TQS_INDEX 16 +#define MTL_Q_TQOMR_TQS_WIDTH 10 +#define MTL_Q_TQOMR_TSF_INDEX 1 +#define MTL_Q_TQOMR_TSF_WIDTH 1 +#define MTL_Q_TQOMR_TTC_INDEX 4 +#define MTL_Q_TQOMR_TTC_WIDTH 3 +#define MTL_Q_TQOMR_TXQEN_INDEX 2 +#define MTL_Q_TQOMR_TXQEN_WIDTH 2 + +/* MTL queue register value */ +#define MTL_RSF_DISABLE 0x00 +#define MTL_RSF_ENABLE 0x01 +#define MTL_TSF_DISABLE 0x00 +#define MTL_TSF_ENABLE 0x01 + +#define MTL_RX_THRESHOLD_64 0x00 +#define MTL_RX_THRESHOLD_96 0x02 +#define MTL_RX_THRESHOLD_128 0x03 +#define MTL_TX_THRESHOLD_32 0x01 +#define MTL_TX_THRESHOLD_64 0x00 +#define MTL_TX_THRESHOLD_96 0x02 +#define MTL_TX_THRESHOLD_128 0x03 +#define MTL_TX_THRESHOLD_192 0x04 +#define MTL_TX_THRESHOLD_256 0x05 +#define MTL_TX_THRESHOLD_384 0x06 +#define MTL_TX_THRESHOLD_512 0x07 + +#define MTL_ETSALG_WRR 0x00 +#define MTL_ETSALG_WFQ 0x01 +#define MTL_ETSALG_DWRR 0x02 +#define MTL_RAA_SP 0x00 +#define MTL_RAA_WSP 0x01 + +#define MTL_Q_DISABLED 0x00 +#define MTL_Q_ENABLED 0x02 + +/* MTL traffic class register offsets + * Multiple traffic classes can be active. The first class has registers + * that begin at 0x1100. Each subsequent queue has registers that + * are accessed using an offset of 0x80 from the previous queue. + */ +#define MTL_TC_BASE MTL_Q_BASE +#define MTL_TC_INC MTL_Q_INC + +#define MTL_TC_ETSCR 0x10 +#define MTL_TC_ETSSR 0x14 +#define MTL_TC_QWR 0x18 + +/* MTL traffic class register entry bit positions and sizes */ +#define MTL_TC_ETSCR_TSA_INDEX 0 +#define MTL_TC_ETSCR_TSA_WIDTH 2 +#define MTL_TC_QWR_QW_INDEX 0 +#define MTL_TC_QWR_QW_WIDTH 21 + +/* MTL traffic class register value */ +#define MTL_TSA_SP 0x00 +#define MTL_TSA_ETS 0x02 + +/* PCS register offsets */ +#define PCS_V1_WINDOW_SELECT 0x03fc +#define PCS_V2_WINDOW_DEF 0x9060 +#define PCS_V2_WINDOW_SELECT 0x9064 +#define PCS_V2_RV_WINDOW_DEF 0x1060 +#define PCS_V2_RV_WINDOW_SELECT 0x1064 +#define PCS_V2_YC_WINDOW_DEF 0x18060 +#define PCS_V2_YC_WINDOW_SELECT 0x18064 + +/* PCS register entry bit positions and sizes */ +#define PCS_V2_WINDOW_DEF_OFFSET_INDEX 6 +#define PCS_V2_WINDOW_DEF_OFFSET_WIDTH 14 +#define PCS_V2_WINDOW_DEF_SIZE_INDEX 2 +#define PCS_V2_WINDOW_DEF_SIZE_WIDTH 4 + +/* SerDes integration register offsets */ +#define SIR0_KR_RT_1 0x002c +#define SIR0_STATUS 0x0040 +#define SIR1_SPEED 0x0000 + +/* SerDes integration register entry bit positions and sizes */ +#define SIR0_KR_RT_1_RESET_INDEX 11 +#define SIR0_KR_RT_1_RESET_WIDTH 1 +#define SIR0_STATUS_RX_READY_INDEX 0 +#define SIR0_STATUS_RX_READY_WIDTH 1 +#define SIR0_STATUS_TX_READY_INDEX 8 +#define SIR0_STATUS_TX_READY_WIDTH 1 +#define SIR1_SPEED_CDR_RATE_INDEX 12 +#define SIR1_SPEED_CDR_RATE_WIDTH 4 +#define SIR1_SPEED_DATARATE_INDEX 4 +#define SIR1_SPEED_DATARATE_WIDTH 2 +#define SIR1_SPEED_PLLSEL_INDEX 3 +#define SIR1_SPEED_PLLSEL_WIDTH 1 +#define SIR1_SPEED_RATECHANGE_INDEX 6 +#define SIR1_SPEED_RATECHANGE_WIDTH 1 +#define SIR1_SPEED_TXAMP_INDEX 8 +#define SIR1_SPEED_TXAMP_WIDTH 4 +#define SIR1_SPEED_WORDMODE_INDEX 0 +#define SIR1_SPEED_WORDMODE_WIDTH 3 + +/* SerDes RxTx register offsets */ +#define RXTX_REG6 0x0018 +#define RXTX_REG20 0x0050 +#define RXTX_REG22 0x0058 +#define RXTX_REG114 0x01c8 +#define RXTX_REG129 0x0204 + +/* SerDes RxTx register entry bit positions and sizes */ +#define RXTX_REG6_RESETB_RXD_INDEX 8 +#define RXTX_REG6_RESETB_RXD_WIDTH 1 +#define RXTX_REG20_BLWC_ENA_INDEX 2 +#define RXTX_REG20_BLWC_ENA_WIDTH 1 +#define RXTX_REG114_PQ_REG_INDEX 9 +#define RXTX_REG114_PQ_REG_WIDTH 7 +#define RXTX_REG129_RXDFE_CONFIG_INDEX 14 +#define RXTX_REG129_RXDFE_CONFIG_WIDTH 2 + +/* MAC Control register offsets */ +#define XP_PROP_0 0x0000 +#define XP_PROP_1 0x0004 +#define XP_PROP_2 0x0008 +#define XP_PROP_3 0x000c +#define XP_PROP_4 0x0010 +#define XP_PROP_5 0x0014 +#define XP_MAC_ADDR_LO 0x0020 +#define XP_MAC_ADDR_HI 0x0024 +#define XP_ECC_ISR 0x0030 +#define XP_ECC_IER 0x0034 +#define XP_ECC_CNT0 0x003c +#define XP_ECC_CNT1 0x0040 +#define XP_DRIVER_INT_REQ 0x0060 +#define XP_DRIVER_INT_RO 0x0064 +#define XP_DRIVER_SCRATCH_0 0x0068 +#define XP_DRIVER_SCRATCH_1 0x006c +#define XP_INT_REISSUE_EN 0x0074 +#define XP_INT_EN 0x0078 +#define XP_I2C_MUTEX 0x0080 +#define XP_MDIO_MUTEX 0x0084 + +/* MAC Control register entry bit positions and sizes */ +#define XP_DRIVER_INT_REQ_REQUEST_INDEX 0 +#define XP_DRIVER_INT_REQ_REQUEST_WIDTH 1 +#define XP_DRIVER_INT_RO_STATUS_INDEX 0 +#define XP_DRIVER_INT_RO_STATUS_WIDTH 1 +#define XP_DRIVER_SCRATCH_0_COMMAND_INDEX 0 +#define XP_DRIVER_SCRATCH_0_COMMAND_WIDTH 8 +#define XP_DRIVER_SCRATCH_0_SUB_COMMAND_INDEX 8 +#define XP_DRIVER_SCRATCH_0_SUB_COMMAND_WIDTH 8 +#define XP_ECC_CNT0_RX_DED_INDEX 24 +#define XP_ECC_CNT0_RX_DED_WIDTH 8 +#define XP_ECC_CNT0_RX_SEC_INDEX 16 +#define XP_ECC_CNT0_RX_SEC_WIDTH 8 +#define XP_ECC_CNT0_TX_DED_INDEX 8 +#define XP_ECC_CNT0_TX_DED_WIDTH 8 +#define XP_ECC_CNT0_TX_SEC_INDEX 0 +#define XP_ECC_CNT0_TX_SEC_WIDTH 8 +#define XP_ECC_CNT1_DESC_DED_INDEX 8 +#define XP_ECC_CNT1_DESC_DED_WIDTH 8 +#define XP_ECC_CNT1_DESC_SEC_INDEX 0 +#define XP_ECC_CNT1_DESC_SEC_WIDTH 8 +#define XP_ECC_IER_DESC_DED_INDEX 5 +#define XP_ECC_IER_DESC_DED_WIDTH 1 +#define XP_ECC_IER_DESC_SEC_INDEX 4 +#define XP_ECC_IER_DESC_SEC_WIDTH 1 +#define XP_ECC_IER_RX_DED_INDEX 3 +#define XP_ECC_IER_RX_DED_WIDTH 1 +#define XP_ECC_IER_RX_SEC_INDEX 2 +#define XP_ECC_IER_RX_SEC_WIDTH 1 +#define XP_ECC_IER_TX_DED_INDEX 1 +#define XP_ECC_IER_TX_DED_WIDTH 1 +#define XP_ECC_IER_TX_SEC_INDEX 0 +#define XP_ECC_IER_TX_SEC_WIDTH 1 +#define XP_ECC_ISR_DESC_DED_INDEX 5 +#define XP_ECC_ISR_DESC_DED_WIDTH 1 +#define XP_ECC_ISR_DESC_SEC_INDEX 4 +#define XP_ECC_ISR_DESC_SEC_WIDTH 1 +#define XP_ECC_ISR_RX_DED_INDEX 3 +#define XP_ECC_ISR_RX_DED_WIDTH 1 +#define XP_ECC_ISR_RX_SEC_INDEX 2 +#define XP_ECC_ISR_RX_SEC_WIDTH 1 +#define XP_ECC_ISR_TX_DED_INDEX 1 +#define XP_ECC_ISR_TX_DED_WIDTH 1 +#define XP_ECC_ISR_TX_SEC_INDEX 0 +#define XP_ECC_ISR_TX_SEC_WIDTH 1 +#define XP_I2C_MUTEX_BUSY_INDEX 31 +#define XP_I2C_MUTEX_BUSY_WIDTH 1 +#define XP_I2C_MUTEX_ID_INDEX 29 +#define XP_I2C_MUTEX_ID_WIDTH 2 +#define XP_I2C_MUTEX_ACTIVE_INDEX 0 +#define XP_I2C_MUTEX_ACTIVE_WIDTH 1 +#define XP_MAC_ADDR_HI_VALID_INDEX 31 +#define XP_MAC_ADDR_HI_VALID_WIDTH 1 +#define XP_PROP_0_CONN_TYPE_INDEX 28 +#define XP_PROP_0_CONN_TYPE_WIDTH 3 +#define XP_PROP_0_MDIO_ADDR_INDEX 16 +#define XP_PROP_0_MDIO_ADDR_WIDTH 5 +#define XP_PROP_0_PORT_ID_INDEX 0 +#define XP_PROP_0_PORT_ID_WIDTH 8 +#define XP_PROP_0_PORT_MODE_INDEX 8 +#define XP_PROP_0_PORT_MODE_WIDTH 4 +#define XP_PROP_0_PORT_SPEEDS_INDEX 22 +#define XP_PROP_0_PORT_SPEEDS_WIDTH 5 +#define XP_PROP_1_MAX_RX_DMA_INDEX 24 +#define XP_PROP_1_MAX_RX_DMA_WIDTH 5 +#define XP_PROP_1_MAX_RX_QUEUES_INDEX 8 +#define XP_PROP_1_MAX_RX_QUEUES_WIDTH 5 +#define XP_PROP_1_MAX_TX_DMA_INDEX 16 +#define XP_PROP_1_MAX_TX_DMA_WIDTH 5 +#define XP_PROP_1_MAX_TX_QUEUES_INDEX 0 +#define XP_PROP_1_MAX_TX_QUEUES_WIDTH 5 +#define XP_PROP_2_RX_FIFO_SIZE_INDEX 16 +#define XP_PROP_2_RX_FIFO_SIZE_WIDTH 16 +#define XP_PROP_2_TX_FIFO_SIZE_INDEX 0 +#define XP_PROP_2_TX_FIFO_SIZE_WIDTH 16 +#define XP_PROP_3_GPIO_MASK_INDEX 28 +#define XP_PROP_3_GPIO_MASK_WIDTH 4 +#define XP_PROP_3_GPIO_MOD_ABS_INDEX 20 +#define XP_PROP_3_GPIO_MOD_ABS_WIDTH 4 +#define XP_PROP_3_GPIO_RATE_SELECT_INDEX 16 +#define XP_PROP_3_GPIO_RATE_SELECT_WIDTH 4 +#define XP_PROP_3_GPIO_RX_LOS_INDEX 24 +#define XP_PROP_3_GPIO_RX_LOS_WIDTH 4 +#define XP_PROP_3_GPIO_TX_FAULT_INDEX 12 +#define XP_PROP_3_GPIO_TX_FAULT_WIDTH 4 +#define XP_PROP_3_GPIO_ADDR_INDEX 8 +#define XP_PROP_3_GPIO_ADDR_WIDTH 3 +#define XP_PROP_3_MDIO_RESET_INDEX 0 +#define XP_PROP_3_MDIO_RESET_WIDTH 2 +#define XP_PROP_3_MDIO_RESET_I2C_ADDR_INDEX 8 +#define XP_PROP_3_MDIO_RESET_I2C_ADDR_WIDTH 3 +#define XP_PROP_3_MDIO_RESET_I2C_GPIO_INDEX 12 +#define XP_PROP_3_MDIO_RESET_I2C_GPIO_WIDTH 4 +#define XP_PROP_3_MDIO_RESET_INT_GPIO_INDEX 4 +#define XP_PROP_3_MDIO_RESET_INT_GPIO_WIDTH 2 +#define XP_PROP_4_MUX_ADDR_HI_INDEX 8 +#define XP_PROP_4_MUX_ADDR_HI_WIDTH 5 +#define XP_PROP_4_MUX_ADDR_LO_INDEX 0 +#define XP_PROP_4_MUX_ADDR_LO_WIDTH 3 +#define XP_PROP_4_MUX_CHAN_INDEX 4 +#define XP_PROP_4_MUX_CHAN_WIDTH 3 +#define XP_PROP_4_REDRV_ADDR_INDEX 16 +#define XP_PROP_4_REDRV_ADDR_WIDTH 7 +#define XP_PROP_4_REDRV_IF_INDEX 23 +#define XP_PROP_4_REDRV_IF_WIDTH 1 +#define XP_PROP_4_REDRV_LANE_INDEX 24 +#define XP_PROP_4_REDRV_LANE_WIDTH 3 +#define XP_PROP_4_REDRV_MODEL_INDEX 28 +#define XP_PROP_4_REDRV_MODEL_WIDTH 3 +#define XP_PROP_4_REDRV_PRESENT_INDEX 31 +#define XP_PROP_4_REDRV_PRESENT_WIDTH 1 + +/* I2C Control register offsets */ +#define IC_CON 0x0000 +#define IC_TAR 0x0004 +#define IC_DATA_CMD 0x0010 +#define IC_INTR_STAT 0x002c +#define IC_INTR_MASK 0x0030 +#define IC_RAW_INTR_STAT 0x0034 +#define IC_CLR_INTR 0x0040 +#define IC_CLR_TX_ABRT 0x0054 +#define IC_CLR_STOP_DET 0x0060 +#define IC_ENABLE 0x006c +#define IC_TXFLR 0x0074 +#define IC_RXFLR 0x0078 +#define IC_TX_ABRT_SOURCE 0x0080 +#define IC_ENABLE_STATUS 0x009c +#define IC_COMP_PARAM_1 0x00f4 + +/* I2C Control register entry bit positions and sizes */ +#define IC_COMP_PARAM_1_MAX_SPEED_MODE_INDEX 2 +#define IC_COMP_PARAM_1_MAX_SPEED_MODE_WIDTH 2 +#define IC_COMP_PARAM_1_RX_BUFFER_DEPTH_INDEX 8 +#define IC_COMP_PARAM_1_RX_BUFFER_DEPTH_WIDTH 8 +#define IC_COMP_PARAM_1_TX_BUFFER_DEPTH_INDEX 16 +#define IC_COMP_PARAM_1_TX_BUFFER_DEPTH_WIDTH 8 +#define IC_CON_MASTER_MODE_INDEX 0 +#define IC_CON_MASTER_MODE_WIDTH 1 +#define IC_CON_RESTART_EN_INDEX 5 +#define IC_CON_RESTART_EN_WIDTH 1 +#define IC_CON_RX_FIFO_FULL_HOLD_INDEX 9 +#define IC_CON_RX_FIFO_FULL_HOLD_WIDTH 1 +#define IC_CON_SLAVE_DISABLE_INDEX 6 +#define IC_CON_SLAVE_DISABLE_WIDTH 1 +#define IC_CON_SPEED_INDEX 1 +#define IC_CON_SPEED_WIDTH 2 +#define IC_DATA_CMD_CMD_INDEX 8 +#define IC_DATA_CMD_CMD_WIDTH 1 +#define IC_DATA_CMD_STOP_INDEX 9 +#define IC_DATA_CMD_STOP_WIDTH 1 +#define IC_ENABLE_ABORT_INDEX 1 +#define IC_ENABLE_ABORT_WIDTH 1 +#define IC_ENABLE_EN_INDEX 0 +#define IC_ENABLE_EN_WIDTH 1 +#define IC_ENABLE_STATUS_EN_INDEX 0 +#define IC_ENABLE_STATUS_EN_WIDTH 1 +#define IC_INTR_MASK_TX_EMPTY_INDEX 4 +#define IC_INTR_MASK_TX_EMPTY_WIDTH 1 +#define IC_RAW_INTR_STAT_RX_FULL_INDEX 2 +#define IC_RAW_INTR_STAT_RX_FULL_WIDTH 1 +#define IC_RAW_INTR_STAT_STOP_DET_INDEX 9 +#define IC_RAW_INTR_STAT_STOP_DET_WIDTH 1 +#define IC_RAW_INTR_STAT_TX_ABRT_INDEX 6 +#define IC_RAW_INTR_STAT_TX_ABRT_WIDTH 1 +#define IC_RAW_INTR_STAT_TX_EMPTY_INDEX 4 +#define IC_RAW_INTR_STAT_TX_EMPTY_WIDTH 1 + +/* I2C Control register value */ +#define IC_TX_ABRT_7B_ADDR_NOACK 0x0001 +#define IC_TX_ABRT_ARB_LOST 0x1000 + +/* Descriptor/Packet entry bit positions and sizes */ +#define RX_PACKET_ERRORS_CRC_INDEX 2 +#define RX_PACKET_ERRORS_CRC_WIDTH 1 +#define RX_PACKET_ERRORS_FRAME_INDEX 3 +#define RX_PACKET_ERRORS_FRAME_WIDTH 1 +#define RX_PACKET_ERRORS_LENGTH_INDEX 0 +#define RX_PACKET_ERRORS_LENGTH_WIDTH 1 +#define RX_PACKET_ERRORS_OVERRUN_INDEX 1 +#define RX_PACKET_ERRORS_OVERRUN_WIDTH 1 + +#define RX_PACKET_ATTRIBUTES_CSUM_DONE_INDEX 0 +#define RX_PACKET_ATTRIBUTES_CSUM_DONE_WIDTH 1 +#define RX_PACKET_ATTRIBUTES_VLAN_CTAG_INDEX 1 +#define RX_PACKET_ATTRIBUTES_VLAN_CTAG_WIDTH 1 +#define RX_PACKET_ATTRIBUTES_LAST_INDEX 2 +#define RX_PACKET_ATTRIBUTES_LAST_WIDTH 1 +#define RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_INDEX 3 +#define RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_WIDTH 1 +#define RX_PACKET_ATTRIBUTES_CONTEXT_INDEX 4 +#define RX_PACKET_ATTRIBUTES_CONTEXT_WIDTH 1 +#define RX_PACKET_ATTRIBUTES_RX_TSTAMP_INDEX 5 +#define RX_PACKET_ATTRIBUTES_RX_TSTAMP_WIDTH 1 +#define RX_PACKET_ATTRIBUTES_RSS_HASH_INDEX 6 +#define RX_PACKET_ATTRIBUTES_RSS_HASH_WIDTH 1 +#define RX_PACKET_ATTRIBUTES_FIRST_INDEX 7 +#define RX_PACKET_ATTRIBUTES_FIRST_WIDTH 1 +#define RX_PACKET_ATTRIBUTES_TNP_INDEX 8 +#define RX_PACKET_ATTRIBUTES_TNP_WIDTH 1 +#define RX_PACKET_ATTRIBUTES_TNPCSUM_DONE_INDEX 9 +#define RX_PACKET_ATTRIBUTES_TNPCSUM_DONE_WIDTH 1 + +#define RX_NORMAL_DESC0_OVT_INDEX 0 +#define RX_NORMAL_DESC0_OVT_WIDTH 16 +#define RX_NORMAL_DESC2_HL_INDEX 0 +#define RX_NORMAL_DESC2_HL_WIDTH 10 +#define RX_NORMAL_DESC2_TNP_INDEX 11 +#define RX_NORMAL_DESC2_TNP_WIDTH 1 +#define RX_NORMAL_DESC3_CDA_INDEX 27 +#define RX_NORMAL_DESC3_CDA_WIDTH 1 +#define RX_NORMAL_DESC3_CTXT_INDEX 30 +#define RX_NORMAL_DESC3_CTXT_WIDTH 1 +#define RX_NORMAL_DESC3_ES_INDEX 15 +#define RX_NORMAL_DESC3_ES_WIDTH 1 +#define RX_NORMAL_DESC3_ETLT_INDEX 16 +#define RX_NORMAL_DESC3_ETLT_WIDTH 4 +#define RX_NORMAL_DESC3_FD_INDEX 29 +#define RX_NORMAL_DESC3_FD_WIDTH 1 +#define RX_NORMAL_DESC3_INTE_INDEX 30 +#define RX_NORMAL_DESC3_INTE_WIDTH 1 +#define RX_NORMAL_DESC3_L34T_INDEX 20 +#define RX_NORMAL_DESC3_L34T_WIDTH 4 +#define RX_NORMAL_DESC3_LD_INDEX 28 +#define RX_NORMAL_DESC3_LD_WIDTH 1 +#define RX_NORMAL_DESC3_OWN_INDEX 31 +#define RX_NORMAL_DESC3_OWN_WIDTH 1 +#define RX_NORMAL_DESC3_PL_INDEX 0 +#define RX_NORMAL_DESC3_PL_WIDTH 14 +#define RX_NORMAL_DESC3_RSV_INDEX 26 +#define RX_NORMAL_DESC3_RSV_WIDTH 1 + +#define RX_DESC3_L34T_IPV4_TCP 1 +#define RX_DESC3_L34T_IPV4_UDP 2 +#define RX_DESC3_L34T_IPV4_ICMP 3 +#define RX_DESC3_L34T_IPV4_UNKNOWN 7 +#define RX_DESC3_L34T_IPV6_TCP 9 +#define RX_DESC3_L34T_IPV6_UDP 10 +#define RX_DESC3_L34T_IPV6_ICMP 11 +#define RX_DESC3_L34T_IPV6_UNKNOWN 15 + +#define RX_CONTEXT_DESC3_TSA_INDEX 4 +#define RX_CONTEXT_DESC3_TSA_WIDTH 1 +#define RX_CONTEXT_DESC3_TSD_INDEX 6 +#define RX_CONTEXT_DESC3_TSD_WIDTH 1 + +#define TX_PACKET_ATTRIBUTES_CSUM_ENABLE_INDEX 0 +#define TX_PACKET_ATTRIBUTES_CSUM_ENABLE_WIDTH 1 +#define TX_PACKET_ATTRIBUTES_TSO_ENABLE_INDEX 1 +#define TX_PACKET_ATTRIBUTES_TSO_ENABLE_WIDTH 1 +#define TX_PACKET_ATTRIBUTES_VLAN_CTAG_INDEX 2 +#define TX_PACKET_ATTRIBUTES_VLAN_CTAG_WIDTH 1 +#define TX_PACKET_ATTRIBUTES_PTP_INDEX 3 +#define TX_PACKET_ATTRIBUTES_PTP_WIDTH 1 +#define TX_PACKET_ATTRIBUTES_VXLAN_INDEX 4 +#define TX_PACKET_ATTRIBUTES_VXLAN_WIDTH 1 + +#define TX_CONTEXT_DESC2_MSS_INDEX 0 +#define TX_CONTEXT_DESC2_MSS_WIDTH 15 +#define TX_CONTEXT_DESC3_CTXT_INDEX 30 +#define TX_CONTEXT_DESC3_CTXT_WIDTH 1 +#define TX_CONTEXT_DESC3_TCMSSV_INDEX 26 +#define TX_CONTEXT_DESC3_TCMSSV_WIDTH 1 +#define TX_CONTEXT_DESC3_VLTV_INDEX 16 +#define TX_CONTEXT_DESC3_VLTV_WIDTH 1 +#define TX_CONTEXT_DESC3_VT_INDEX 0 +#define TX_CONTEXT_DESC3_VT_WIDTH 16 + +#define TX_NORMAL_DESC2_HL_B1L_INDEX 0 +#define TX_NORMAL_DESC2_HL_B1L_WIDTH 14 +#define TX_NORMAL_DESC2_IC_INDEX 31 +#define TX_NORMAL_DESC2_IC_WIDTH 1 +#define TX_NORMAL_DESC2_TTSE_INDEX 30 +#define TX_NORMAL_DESC2_TTSE_WIDTH 1 +#define TX_NORMAL_DESC2_VTIR_INDEX 14 +#define TX_NORMAL_DESC2_VTIR_WIDTH 2 +#define TX_NORMAL_DESC3_CIC_INDEX 16 +#define TX_NORMAL_DESC3_CIC_WIDTH 2 +#define TX_NORMAL_DESC3_CPC_INDEX 26 +#define TX_NORMAL_DESC3_CPC_WIDTH 2 +#define TX_NORMAL_DESC3_CTXT_INDEX 30 +#define TX_NORMAL_DESC3_CTXT_WIDTH 1 +#define TX_NORMAL_DESC3_FD_INDEX 29 +#define TX_NORMAL_DESC3_FD_WIDTH 1 +#define TX_NORMAL_DESC3_FL_INDEX 0 +#define TX_NORMAL_DESC3_FL_WIDTH 15 +#define TX_NORMAL_DESC3_LD_INDEX 28 +#define TX_NORMAL_DESC3_LD_WIDTH 1 +#define TX_NORMAL_DESC3_OWN_INDEX 31 +#define TX_NORMAL_DESC3_OWN_WIDTH 1 +#define TX_NORMAL_DESC3_TCPHDRLEN_INDEX 19 +#define TX_NORMAL_DESC3_TCPHDRLEN_WIDTH 4 +#define TX_NORMAL_DESC3_TCPPL_INDEX 0 +#define TX_NORMAL_DESC3_TCPPL_WIDTH 18 +#define TX_NORMAL_DESC3_TSE_INDEX 18 +#define TX_NORMAL_DESC3_TSE_WIDTH 1 +#define TX_NORMAL_DESC3_VNP_INDEX 23 +#define TX_NORMAL_DESC3_VNP_WIDTH 3 + +#define TX_NORMAL_DESC2_VLAN_INSERT 0x2 +#define TX_NORMAL_DESC3_VXLAN_PACKET 0x3 + +/* MDIO undefined or vendor specific registers */ +#ifndef MDIO_PMA_10GBR_PMD_CTRL +#define MDIO_PMA_10GBR_PMD_CTRL 0x0096 +#endif + +#ifndef MDIO_PMA_10GBR_FECCTRL +#define MDIO_PMA_10GBR_FECCTRL 0x00ab +#endif + +#ifndef MDIO_PMA_RX_CTRL1 +#define MDIO_PMA_RX_CTRL1 0x8051 +#endif + +#ifndef MDIO_PCS_DIG_CTRL +#define MDIO_PCS_DIG_CTRL 0x8000 +#endif + +#ifndef MDIO_PCS_DIGITAL_STAT +#define MDIO_PCS_DIGITAL_STAT 0x8010 +#endif + +#ifndef MDIO_AN_XNP +#define MDIO_AN_XNP 0x0016 +#endif + +#ifndef MDIO_AN_LPX +#define MDIO_AN_LPX 0x0019 +#endif + +#ifndef MDIO_AN_COMP_STAT +#define MDIO_AN_COMP_STAT 0x0030 +#endif + +#ifndef MDIO_AN_INTMASK +#define MDIO_AN_INTMASK 0x8001 +#endif + +#ifndef MDIO_AN_INT +#define MDIO_AN_INT 0x8002 +#endif + +#ifndef MDIO_VEND2_AN_ADVERTISE +#define MDIO_VEND2_AN_ADVERTISE 0x0004 +#endif + +#ifndef MDIO_VEND2_AN_LP_ABILITY +#define MDIO_VEND2_AN_LP_ABILITY 0x0005 +#endif + +#ifndef MDIO_VEND2_AN_CTRL +#define MDIO_VEND2_AN_CTRL 0x8001 +#endif + +#ifndef MDIO_VEND2_AN_STAT +#define MDIO_VEND2_AN_STAT 0x8002 +#endif + +#ifndef MDIO_VEND2_PMA_CDR_CONTROL +#define MDIO_VEND2_PMA_CDR_CONTROL 0x8056 +#endif + +#ifndef MDIO_VEND2_PMA_MISC_CTRL0 +#define MDIO_VEND2_PMA_MISC_CTRL0 0x8090 +#endif + +#ifndef MDIO_CTRL1_SPEED1G +#define MDIO_CTRL1_SPEED1G (MDIO_CTRL1_SPEED10G & ~BMCR_SPEED100) +#endif + +#ifndef MDIO_VEND2_CTRL1_AN_ENABLE +#define MDIO_VEND2_CTRL1_AN_ENABLE BIT(12) +#endif + +#ifndef MDIO_VEND2_CTRL1_AN_RESTART +#define MDIO_VEND2_CTRL1_AN_RESTART BIT(9) +#endif + +#ifndef MDIO_VEND2_CTRL1_SS6 +#define MDIO_VEND2_CTRL1_SS6 BIT(6) +#endif + +#ifndef MDIO_VEND2_CTRL1_SS13 +#define MDIO_VEND2_CTRL1_SS13 BIT(13) +#endif + +/* MDIO mask values */ +#define XGBE_AN_CL73_INT_CMPLT BIT(0) +#define XGBE_AN_CL73_INC_LINK BIT(1) +#define XGBE_AN_CL73_PG_RCV BIT(2) +#define XGBE_AN_CL73_INT_MASK 0x07 + +#define XGBE_XNP_MCF_NULL_MESSAGE 0x001 +#define XGBE_XNP_ACK_PROCESSED BIT(12) +#define XGBE_XNP_MP_FORMATTED BIT(13) +#define XGBE_XNP_NP_EXCHANGE BIT(15) + +#define XGBE_KR_TRAINING_START BIT(0) +#define XGBE_KR_TRAINING_ENABLE BIT(1) + +#define XGBE_PCS_CL37_BP BIT(12) +#define XGBE_PCS_PSEQ_STATE_MASK 0x1c +#define XGBE_PCS_PSEQ_STATE_POWER_GOOD 0x10 + +#define XGBE_AN_CL37_INT_CMPLT BIT(0) +#define XGBE_AN_CL37_INT_MASK 0x01 + +#define XGBE_AN_CL37_HD_MASK 0x40 +#define XGBE_AN_CL37_FD_MASK 0x20 + +#define XGBE_AN_CL37_PCS_MODE_MASK 0x06 +#define XGBE_AN_CL37_PCS_MODE_BASEX 0x00 +#define XGBE_AN_CL37_PCS_MODE_SGMII 0x04 +#define XGBE_AN_CL37_TX_CONFIG_MASK 0x08 +#define XGBE_AN_CL37_MII_CTRL_8BIT 0x0100 + +#define XGBE_PMA_CDR_TRACK_EN_MASK 0x01 +#define XGBE_PMA_CDR_TRACK_EN_OFF 0x00 +#define XGBE_PMA_CDR_TRACK_EN_ON 0x01 + +#define XGBE_PMA_RX_RST_0_MASK BIT(4) +#define XGBE_PMA_RX_RST_0_RESET_ON 0x10 +#define XGBE_PMA_RX_RST_0_RESET_OFF 0x00 + +#define XGBE_PMA_PLL_CTRL_MASK BIT(15) +#define XGBE_PMA_PLL_CTRL_ENABLE BIT(15) +#define XGBE_PMA_PLL_CTRL_DISABLE 0x0000 + +/* Bit setting and getting macros + * The get macro will extract the current bit field value from within + * the variable + * + * The set macro will clear the current bit field value within the + * variable and then set the bit field of the variable to the + * specified value + */ +#define GET_BITS(_var, _index, _width) \ + (((_var) >> (_index)) & ((0x1 << (_width)) - 1)) + +#define SET_BITS(_var, _index, _width, _val) \ +do { \ + (_var) &= ~(((0x1 << (_width)) - 1) << (_index)); \ + (_var) |= (((_val) & ((0x1 << (_width)) - 1)) << (_index)); \ +} while (0) + +#define GET_BITS_LE(_var, _index, _width) \ + ((le32_to_cpu((_var)) >> (_index)) & ((0x1 << (_width)) - 1)) + +#define SET_BITS_LE(_var, _index, _width, _val) \ +do { \ + (_var) &= cpu_to_le32(~(((0x1 << (_width)) - 1) << (_index))); \ + (_var) |= cpu_to_le32((((_val) & \ + ((0x1 << (_width)) - 1)) << (_index))); \ +} while (0) + +/* Bit setting and getting macros based on register fields + * The get macro uses the bit field definitions formed using the input + * names to extract the current bit field value from within the + * variable + * + * The set macro uses the bit field definitions formed using the input + * names to set the bit field of the variable to the specified value + */ +#define XGMAC_GET_BITS(_var, _prefix, _field) \ + GET_BITS((_var), \ + _prefix##_##_field##_INDEX, \ + _prefix##_##_field##_WIDTH) + +#define XGMAC_SET_BITS(_var, _prefix, _field, _val) \ + SET_BITS((_var), \ + _prefix##_##_field##_INDEX, \ + _prefix##_##_field##_WIDTH, (_val)) + +#define XGMAC_GET_BITS_LE(_var, _prefix, _field) \ + GET_BITS_LE((_var), \ + _prefix##_##_field##_INDEX, \ + _prefix##_##_field##_WIDTH) + +#define XGMAC_SET_BITS_LE(_var, _prefix, _field, _val) \ + SET_BITS_LE((_var), \ + _prefix##_##_field##_INDEX, \ + _prefix##_##_field##_WIDTH, (_val)) + +/* Macros for reading or writing registers + * The ioread macros will get bit fields or full values using the + * register definitions formed using the input names + * + * The iowrite macros will set bit fields or full values using the + * register definitions formed using the input names + */ +#define XGMAC_IOREAD(_pdata, _reg) \ + ioread32((_pdata)->xgmac_regs + _reg) + +#define XGMAC_IOREAD_BITS(_pdata, _reg, _field) \ + GET_BITS(XGMAC_IOREAD((_pdata), _reg), \ + _reg##_##_field##_INDEX, \ + _reg##_##_field##_WIDTH) + +#define XGMAC_IOWRITE(_pdata, _reg, _val) \ + iowrite32((_val), (_pdata)->xgmac_regs + _reg) + +#define XGMAC_IOWRITE_BITS(_pdata, _reg, _field, _val) \ +do { \ + u32 reg_val = XGMAC_IOREAD((_pdata), _reg); \ + SET_BITS(reg_val, \ + _reg##_##_field##_INDEX, \ + _reg##_##_field##_WIDTH, (_val)); \ + XGMAC_IOWRITE((_pdata), _reg, reg_val); \ +} while (0) + +/* Macros for reading or writing MTL queue or traffic class registers + * Similar to the standard read and write macros except that the + * base register value is calculated by the queue or traffic class number + */ +#define XGMAC_MTL_IOREAD(_pdata, _n, _reg) \ + ioread32((_pdata)->xgmac_regs + \ + MTL_Q_BASE + ((_n) * MTL_Q_INC) + _reg) + +#define XGMAC_MTL_IOREAD_BITS(_pdata, _n, _reg, _field) \ + GET_BITS(XGMAC_MTL_IOREAD((_pdata), (_n), _reg), \ + _reg##_##_field##_INDEX, \ + _reg##_##_field##_WIDTH) + +#define XGMAC_MTL_IOWRITE(_pdata, _n, _reg, _val) \ + iowrite32((_val), (_pdata)->xgmac_regs + \ + MTL_Q_BASE + ((_n) * MTL_Q_INC) + _reg) + +#define XGMAC_MTL_IOWRITE_BITS(_pdata, _n, _reg, _field, _val) \ +do { \ + u32 reg_val = XGMAC_MTL_IOREAD((_pdata), (_n), _reg); \ + SET_BITS(reg_val, \ + _reg##_##_field##_INDEX, \ + _reg##_##_field##_WIDTH, (_val)); \ + XGMAC_MTL_IOWRITE((_pdata), (_n), _reg, reg_val); \ +} while (0) + +/* Macros for reading or writing DMA channel registers + * Similar to the standard read and write macros except that the + * base register value is obtained from the ring + */ +#define XGMAC_DMA_IOREAD(_channel, _reg) \ + ioread32((_channel)->dma_regs + _reg) + +#define XGMAC_DMA_IOREAD_BITS(_channel, _reg, _field) \ + GET_BITS(XGMAC_DMA_IOREAD((_channel), _reg), \ + _reg##_##_field##_INDEX, \ + _reg##_##_field##_WIDTH) + +#define XGMAC_DMA_IOWRITE(_channel, _reg, _val) \ + iowrite32((_val), (_channel)->dma_regs + _reg) + +#define XGMAC_DMA_IOWRITE_BITS(_channel, _reg, _field, _val) \ +do { \ + u32 reg_val = XGMAC_DMA_IOREAD((_channel), _reg); \ + SET_BITS(reg_val, \ + _reg##_##_field##_INDEX, \ + _reg##_##_field##_WIDTH, (_val)); \ + XGMAC_DMA_IOWRITE((_channel), _reg, reg_val); \ +} while (0) + +/* Macros for building, reading or writing register values or bits + * within the register values of XPCS registers. + */ +#define XPCS_GET_BITS(_var, _prefix, _field) \ + GET_BITS((_var), \ + _prefix##_##_field##_INDEX, \ + _prefix##_##_field##_WIDTH) + +#define XPCS_SET_BITS(_var, _prefix, _field, _val) \ + SET_BITS((_var), \ + _prefix##_##_field##_INDEX, \ + _prefix##_##_field##_WIDTH, (_val)) + +#define XPCS32_IOWRITE(_pdata, _off, _val) \ + iowrite32(_val, (_pdata)->xpcs_regs + (_off)) + +#define XPCS32_IOREAD(_pdata, _off) \ + ioread32((_pdata)->xpcs_regs + (_off)) + +#define XPCS16_IOWRITE(_pdata, _off, _val) \ + iowrite16(_val, (_pdata)->xpcs_regs + (_off)) + +#define XPCS16_IOREAD(_pdata, _off) \ + ioread16((_pdata)->xpcs_regs + (_off)) + +/* Macros for building, reading or writing register values or bits + * within the register values of SerDes integration registers. + */ +#define XSIR_GET_BITS(_var, _prefix, _field) \ + GET_BITS((_var), \ + _prefix##_##_field##_INDEX, \ + _prefix##_##_field##_WIDTH) + +#define XSIR_SET_BITS(_var, _prefix, _field, _val) \ + SET_BITS((_var), \ + _prefix##_##_field##_INDEX, \ + _prefix##_##_field##_WIDTH, (_val)) + +#define XSIR0_IOREAD(_pdata, _reg) \ + ioread16((_pdata)->sir0_regs + _reg) + +#define XSIR0_IOREAD_BITS(_pdata, _reg, _field) \ + GET_BITS(XSIR0_IOREAD((_pdata), _reg), \ + _reg##_##_field##_INDEX, \ + _reg##_##_field##_WIDTH) + +#define XSIR0_IOWRITE(_pdata, _reg, _val) \ + iowrite16((_val), (_pdata)->sir0_regs + _reg) + +#define XSIR0_IOWRITE_BITS(_pdata, _reg, _field, _val) \ +do { \ + u16 reg_val = XSIR0_IOREAD((_pdata), _reg); \ + SET_BITS(reg_val, \ + _reg##_##_field##_INDEX, \ + _reg##_##_field##_WIDTH, (_val)); \ + XSIR0_IOWRITE((_pdata), _reg, reg_val); \ +} while (0) + +#define XSIR1_IOREAD(_pdata, _reg) \ + ioread16((_pdata)->sir1_regs + _reg) + +#define XSIR1_IOREAD_BITS(_pdata, _reg, _field) \ + GET_BITS(XSIR1_IOREAD((_pdata), _reg), \ + _reg##_##_field##_INDEX, \ + _reg##_##_field##_WIDTH) + +#define XSIR1_IOWRITE(_pdata, _reg, _val) \ + iowrite16((_val), (_pdata)->sir1_regs + _reg) + +#define XSIR1_IOWRITE_BITS(_pdata, _reg, _field, _val) \ +do { \ + u16 reg_val = XSIR1_IOREAD((_pdata), _reg); \ + SET_BITS(reg_val, \ + _reg##_##_field##_INDEX, \ + _reg##_##_field##_WIDTH, (_val)); \ + XSIR1_IOWRITE((_pdata), _reg, reg_val); \ +} while (0) + +/* Macros for building, reading or writing register values or bits + * within the register values of SerDes RxTx registers. + */ +#define XRXTX_IOREAD(_pdata, _reg) \ + ioread16((_pdata)->rxtx_regs + _reg) + +#define XRXTX_IOREAD_BITS(_pdata, _reg, _field) \ + GET_BITS(XRXTX_IOREAD((_pdata), _reg), \ + _reg##_##_field##_INDEX, \ + _reg##_##_field##_WIDTH) + +#define XRXTX_IOWRITE(_pdata, _reg, _val) \ + iowrite16((_val), (_pdata)->rxtx_regs + _reg) + +#define XRXTX_IOWRITE_BITS(_pdata, _reg, _field, _val) \ +do { \ + u16 reg_val = XRXTX_IOREAD((_pdata), _reg); \ + SET_BITS(reg_val, \ + _reg##_##_field##_INDEX, \ + _reg##_##_field##_WIDTH, (_val)); \ + XRXTX_IOWRITE((_pdata), _reg, reg_val); \ +} while (0) + +/* Macros for building, reading or writing register values or bits + * within the register values of MAC Control registers. + */ +#define XP_GET_BITS(_var, _prefix, _field) \ + GET_BITS((_var), \ + _prefix##_##_field##_INDEX, \ + _prefix##_##_field##_WIDTH) + +#define XP_SET_BITS(_var, _prefix, _field, _val) \ + SET_BITS((_var), \ + _prefix##_##_field##_INDEX, \ + _prefix##_##_field##_WIDTH, (_val)) + +#define XP_IOREAD(_pdata, _reg) \ + ioread32((_pdata)->xprop_regs + (_reg)) + +#define XP_IOREAD_BITS(_pdata, _reg, _field) \ + GET_BITS(XP_IOREAD((_pdata), (_reg)), \ + _reg##_##_field##_INDEX, \ + _reg##_##_field##_WIDTH) + +#define XP_IOWRITE(_pdata, _reg, _val) \ + iowrite32((_val), (_pdata)->xprop_regs + (_reg)) + +#define XP_IOWRITE_BITS(_pdata, _reg, _field, _val) \ +do { \ + u32 reg_val = XP_IOREAD((_pdata), (_reg)); \ + SET_BITS(reg_val, \ + _reg##_##_field##_INDEX, \ + _reg##_##_field##_WIDTH, (_val)); \ + XP_IOWRITE((_pdata), (_reg), reg_val); \ +} while (0) + +/* Macros for building, reading or writing register values or bits + * within the register values of I2C Control registers. + */ +#define XI2C_GET_BITS(_var, _prefix, _field) \ + GET_BITS((_var), \ + _prefix##_##_field##_INDEX, \ + _prefix##_##_field##_WIDTH) + +#define XI2C_SET_BITS(_var, _prefix, _field, _val) \ + SET_BITS((_var), \ + _prefix##_##_field##_INDEX, \ + _prefix##_##_field##_WIDTH, (_val)) + +#define XI2C_IOREAD(_pdata, _reg) \ + ioread32((_pdata)->xi2c_regs + (_reg)) + +#define XI2C_IOREAD_BITS(_pdata, _reg, _field) \ + GET_BITS(XI2C_IOREAD((_pdata), (_reg)), \ + _reg##_##_field##_INDEX, \ + _reg##_##_field##_WIDTH) + +#define XI2C_IOWRITE(_pdata, _reg, _val) \ + iowrite32((_val), (_pdata)->xi2c_regs + (_reg)) + +#define XI2C_IOWRITE_BITS(_pdata, _reg, _field, _val) \ +do { \ + u32 reg_val = XI2C_IOREAD((_pdata), (_reg)); \ + SET_BITS(reg_val, \ + _reg##_##_field##_INDEX, \ + _reg##_##_field##_WIDTH, (_val)); \ + XI2C_IOWRITE((_pdata), (_reg), reg_val); \ +} while (0) + +/* Macros for building, reading or writing register values or bits + * using MDIO. Different from above because of the use of standardized + * Linux include values. No shifting is performed with the bit + * operations, everything works on mask values. + */ +#define XMDIO_READ(_pdata, _mmd, _reg) \ + ((_pdata)->hw_if.read_mmd_regs((_pdata), 0, \ + MII_ADDR_C45 | (_mmd << 16) | ((_reg) & 0xffff))) + +#define XMDIO_READ_BITS(_pdata, _mmd, _reg, _mask) \ + (XMDIO_READ((_pdata), _mmd, _reg) & _mask) + +#define XMDIO_WRITE(_pdata, _mmd, _reg, _val) \ + ((_pdata)->hw_if.write_mmd_regs((_pdata), 0, \ + MII_ADDR_C45 | (_mmd << 16) | ((_reg) & 0xffff), (_val))) + +#define XMDIO_WRITE_BITS(_pdata, _mmd, _reg, _mask, _val) \ +do { \ + u32 mmd_val = XMDIO_READ((_pdata), _mmd, _reg); \ + mmd_val &= ~_mask; \ + mmd_val |= (_val); \ + XMDIO_WRITE((_pdata), _mmd, _reg, mmd_val); \ +} while (0) + +#endif diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dcb.c b/drivers/net/ethernet/amd/xgbe/xgbe-dcb.c new file mode 100644 index 000000000..c68ace804 --- /dev/null +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dcb.c @@ -0,0 +1,293 @@ +/* + * AMD 10Gb Ethernet driver + * + * This file is available to you under your choice of the following two + * licenses: + * + * License 1: GPLv2 + * + * Copyright (c) 2014-2016 Advanced Micro Devices, Inc. + * + * This file is free software; you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or (at + * your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * + * License 2: Modified BSD + * + * Copyright (c) 2014-2016 Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/netdevice.h> +#include <net/dcbnl.h> + +#include "xgbe.h" +#include "xgbe-common.h" + +static int xgbe_dcb_ieee_getets(struct net_device *netdev, + struct ieee_ets *ets) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + + /* Set number of supported traffic classes */ + ets->ets_cap = pdata->hw_feat.tc_cnt; + + if (pdata->ets) { + ets->cbs = pdata->ets->cbs; + memcpy(ets->tc_tx_bw, pdata->ets->tc_tx_bw, + sizeof(ets->tc_tx_bw)); + memcpy(ets->tc_tsa, pdata->ets->tc_tsa, + sizeof(ets->tc_tsa)); + memcpy(ets->prio_tc, pdata->ets->prio_tc, + sizeof(ets->prio_tc)); + } + + return 0; +} + +static int xgbe_dcb_ieee_setets(struct net_device *netdev, + struct ieee_ets *ets) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + unsigned int i, tc_ets, tc_ets_weight; + u8 max_tc = 0; + + tc_ets = 0; + tc_ets_weight = 0; + for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { + netif_dbg(pdata, drv, netdev, + "TC%u: tx_bw=%hhu, rx_bw=%hhu, tsa=%hhu\n", i, + ets->tc_tx_bw[i], ets->tc_rx_bw[i], + ets->tc_tsa[i]); + netif_dbg(pdata, drv, netdev, "PRIO%u: TC=%hhu\n", i, + ets->prio_tc[i]); + + max_tc = max_t(u8, max_tc, ets->prio_tc[i]); + if ((ets->tc_tx_bw[i] || ets->tc_tsa[i])) + max_tc = max_t(u8, max_tc, i); + + switch (ets->tc_tsa[i]) { + case IEEE_8021QAZ_TSA_STRICT: + break; + case IEEE_8021QAZ_TSA_ETS: + tc_ets = 1; + tc_ets_weight += ets->tc_tx_bw[i]; + break; + default: + netif_err(pdata, drv, netdev, + "unsupported TSA algorithm (%hhu)\n", + ets->tc_tsa[i]); + return -EINVAL; + } + } + + /* Check maximum traffic class requested */ + if (max_tc >= pdata->hw_feat.tc_cnt) { + netif_err(pdata, drv, netdev, + "exceeded number of supported traffic classes\n"); + return -EINVAL; + } + + /* Weights must add up to 100% */ + if (tc_ets && (tc_ets_weight != 100)) { + netif_err(pdata, drv, netdev, + "sum of ETS algorithm weights is not 100 (%u)\n", + tc_ets_weight); + return -EINVAL; + } + + if (!pdata->ets) { + pdata->ets = devm_kzalloc(pdata->dev, sizeof(*pdata->ets), + GFP_KERNEL); + if (!pdata->ets) + return -ENOMEM; + } + + pdata->num_tcs = max_tc + 1; + memcpy(pdata->ets, ets, sizeof(*pdata->ets)); + + pdata->hw_if.config_dcb_tc(pdata); + + return 0; +} + +static int xgbe_dcb_ieee_getpfc(struct net_device *netdev, + struct ieee_pfc *pfc) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + + /* Set number of supported PFC traffic classes */ + pfc->pfc_cap = pdata->hw_feat.tc_cnt; + + if (pdata->pfc) { + pfc->pfc_en = pdata->pfc->pfc_en; + pfc->mbc = pdata->pfc->mbc; + pfc->delay = pdata->pfc->delay; + } + + return 0; +} + +static int xgbe_dcb_ieee_setpfc(struct net_device *netdev, + struct ieee_pfc *pfc) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + + netif_dbg(pdata, drv, netdev, + "cap=%d, en=%#x, mbc=%d, delay=%d\n", + pfc->pfc_cap, pfc->pfc_en, pfc->mbc, pfc->delay); + + /* Check PFC for supported number of traffic classes */ + if (pfc->pfc_en & ~((1 << pdata->hw_feat.tc_cnt) - 1)) { + netif_err(pdata, drv, netdev, + "PFC requested for unsupported traffic class\n"); + return -EINVAL; + } + + if (!pdata->pfc) { + pdata->pfc = devm_kzalloc(pdata->dev, sizeof(*pdata->pfc), + GFP_KERNEL); + if (!pdata->pfc) + return -ENOMEM; + } + + memcpy(pdata->pfc, pfc, sizeof(*pdata->pfc)); + + pdata->hw_if.config_dcb_pfc(pdata); + + return 0; +} + +static u8 xgbe_dcb_getdcbx(struct net_device *netdev) +{ + return DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE; +} + +static u8 xgbe_dcb_setdcbx(struct net_device *netdev, u8 dcbx) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + u8 support = xgbe_dcb_getdcbx(netdev); + + netif_dbg(pdata, drv, netdev, "DCBX=%#hhx\n", dcbx); + + if (dcbx & ~support) + return 1; + + if ((dcbx & support) != support) + return 1; + + return 0; +} + +static const struct dcbnl_rtnl_ops xgbe_dcbnl_ops = { + /* IEEE 802.1Qaz std */ + .ieee_getets = xgbe_dcb_ieee_getets, + .ieee_setets = xgbe_dcb_ieee_setets, + .ieee_getpfc = xgbe_dcb_ieee_getpfc, + .ieee_setpfc = xgbe_dcb_ieee_setpfc, + + /* DCBX configuration */ + .getdcbx = xgbe_dcb_getdcbx, + .setdcbx = xgbe_dcb_setdcbx, +}; + +const struct dcbnl_rtnl_ops *xgbe_get_dcbnl_ops(void) +{ + return &xgbe_dcbnl_ops; +} diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c new file mode 100644 index 000000000..b0a6c96b6 --- /dev/null +++ b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c @@ -0,0 +1,525 @@ +/* + * AMD 10Gb Ethernet driver + * + * This file is available to you under your choice of the following two + * licenses: + * + * License 1: GPLv2 + * + * Copyright (c) 2014 Advanced Micro Devices, Inc. + * + * This file is free software; you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or (at + * your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * + * License 2: Modified BSD + * + * Copyright (c) 2014 Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/debugfs.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include "xgbe.h" +#include "xgbe-common.h" + +static ssize_t xgbe_common_read(char __user *buffer, size_t count, + loff_t *ppos, unsigned int value) +{ + char *buf; + ssize_t len; + + if (*ppos != 0) + return 0; + + buf = kasprintf(GFP_KERNEL, "0x%08x\n", value); + if (!buf) + return -ENOMEM; + + if (count < strlen(buf)) { + kfree(buf); + return -ENOSPC; + } + + len = simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); + kfree(buf); + + return len; +} + +static ssize_t xgbe_common_write(const char __user *buffer, size_t count, + loff_t *ppos, unsigned int *value) +{ + char workarea[32]; + ssize_t len; + int ret; + + if (*ppos != 0) + return -EINVAL; + + if (count >= sizeof(workarea)) + return -ENOSPC; + + len = simple_write_to_buffer(workarea, sizeof(workarea) - 1, ppos, + buffer, count); + if (len < 0) + return len; + + workarea[len] = '\0'; + ret = kstrtouint(workarea, 16, value); + if (ret) + return -EIO; + + return len; +} + +static ssize_t xgmac_reg_addr_read(struct file *filp, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct xgbe_prv_data *pdata = filp->private_data; + + return xgbe_common_read(buffer, count, ppos, pdata->debugfs_xgmac_reg); +} + +static ssize_t xgmac_reg_addr_write(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct xgbe_prv_data *pdata = filp->private_data; + + return xgbe_common_write(buffer, count, ppos, + &pdata->debugfs_xgmac_reg); +} + +static ssize_t xgmac_reg_value_read(struct file *filp, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct xgbe_prv_data *pdata = filp->private_data; + unsigned int value; + + value = XGMAC_IOREAD(pdata, pdata->debugfs_xgmac_reg); + + return xgbe_common_read(buffer, count, ppos, value); +} + +static ssize_t xgmac_reg_value_write(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct xgbe_prv_data *pdata = filp->private_data; + unsigned int value; + ssize_t len; + + len = xgbe_common_write(buffer, count, ppos, &value); + if (len < 0) + return len; + + XGMAC_IOWRITE(pdata, pdata->debugfs_xgmac_reg, value); + + return len; +} + +static const struct file_operations xgmac_reg_addr_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = xgmac_reg_addr_read, + .write = xgmac_reg_addr_write, +}; + +static const struct file_operations xgmac_reg_value_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = xgmac_reg_value_read, + .write = xgmac_reg_value_write, +}; + +static ssize_t xpcs_mmd_read(struct file *filp, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct xgbe_prv_data *pdata = filp->private_data; + + return xgbe_common_read(buffer, count, ppos, pdata->debugfs_xpcs_mmd); +} + +static ssize_t xpcs_mmd_write(struct file *filp, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct xgbe_prv_data *pdata = filp->private_data; + + return xgbe_common_write(buffer, count, ppos, + &pdata->debugfs_xpcs_mmd); +} + +static ssize_t xpcs_reg_addr_read(struct file *filp, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct xgbe_prv_data *pdata = filp->private_data; + + return xgbe_common_read(buffer, count, ppos, pdata->debugfs_xpcs_reg); +} + +static ssize_t xpcs_reg_addr_write(struct file *filp, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct xgbe_prv_data *pdata = filp->private_data; + + return xgbe_common_write(buffer, count, ppos, + &pdata->debugfs_xpcs_reg); +} + +static ssize_t xpcs_reg_value_read(struct file *filp, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct xgbe_prv_data *pdata = filp->private_data; + unsigned int value; + + value = XMDIO_READ(pdata, pdata->debugfs_xpcs_mmd, + pdata->debugfs_xpcs_reg); + + return xgbe_common_read(buffer, count, ppos, value); +} + +static ssize_t xpcs_reg_value_write(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct xgbe_prv_data *pdata = filp->private_data; + unsigned int value; + ssize_t len; + + len = xgbe_common_write(buffer, count, ppos, &value); + if (len < 0) + return len; + + XMDIO_WRITE(pdata, pdata->debugfs_xpcs_mmd, pdata->debugfs_xpcs_reg, + value); + + return len; +} + +static const struct file_operations xpcs_mmd_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = xpcs_mmd_read, + .write = xpcs_mmd_write, +}; + +static const struct file_operations xpcs_reg_addr_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = xpcs_reg_addr_read, + .write = xpcs_reg_addr_write, +}; + +static const struct file_operations xpcs_reg_value_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = xpcs_reg_value_read, + .write = xpcs_reg_value_write, +}; + +static ssize_t xprop_reg_addr_read(struct file *filp, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct xgbe_prv_data *pdata = filp->private_data; + + return xgbe_common_read(buffer, count, ppos, pdata->debugfs_xprop_reg); +} + +static ssize_t xprop_reg_addr_write(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct xgbe_prv_data *pdata = filp->private_data; + + return xgbe_common_write(buffer, count, ppos, + &pdata->debugfs_xprop_reg); +} + +static ssize_t xprop_reg_value_read(struct file *filp, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct xgbe_prv_data *pdata = filp->private_data; + unsigned int value; + + value = XP_IOREAD(pdata, pdata->debugfs_xprop_reg); + + return xgbe_common_read(buffer, count, ppos, value); +} + +static ssize_t xprop_reg_value_write(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct xgbe_prv_data *pdata = filp->private_data; + unsigned int value; + ssize_t len; + + len = xgbe_common_write(buffer, count, ppos, &value); + if (len < 0) + return len; + + XP_IOWRITE(pdata, pdata->debugfs_xprop_reg, value); + + return len; +} + +static const struct file_operations xprop_reg_addr_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = xprop_reg_addr_read, + .write = xprop_reg_addr_write, +}; + +static const struct file_operations xprop_reg_value_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = xprop_reg_value_read, + .write = xprop_reg_value_write, +}; + +static ssize_t xi2c_reg_addr_read(struct file *filp, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct xgbe_prv_data *pdata = filp->private_data; + + return xgbe_common_read(buffer, count, ppos, pdata->debugfs_xi2c_reg); +} + +static ssize_t xi2c_reg_addr_write(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct xgbe_prv_data *pdata = filp->private_data; + + return xgbe_common_write(buffer, count, ppos, + &pdata->debugfs_xi2c_reg); +} + +static ssize_t xi2c_reg_value_read(struct file *filp, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct xgbe_prv_data *pdata = filp->private_data; + unsigned int value; + + value = XI2C_IOREAD(pdata, pdata->debugfs_xi2c_reg); + + return xgbe_common_read(buffer, count, ppos, value); +} + +static ssize_t xi2c_reg_value_write(struct file *filp, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct xgbe_prv_data *pdata = filp->private_data; + unsigned int value; + ssize_t len; + + len = xgbe_common_write(buffer, count, ppos, &value); + if (len < 0) + return len; + + XI2C_IOWRITE(pdata, pdata->debugfs_xi2c_reg, value); + + return len; +} + +static const struct file_operations xi2c_reg_addr_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = xi2c_reg_addr_read, + .write = xi2c_reg_addr_write, +}; + +static const struct file_operations xi2c_reg_value_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = xi2c_reg_value_read, + .write = xi2c_reg_value_write, +}; + +void xgbe_debugfs_init(struct xgbe_prv_data *pdata) +{ + char *buf; + + /* Set defaults */ + pdata->debugfs_xgmac_reg = 0; + pdata->debugfs_xpcs_mmd = 1; + pdata->debugfs_xpcs_reg = 0; + + buf = kasprintf(GFP_KERNEL, "amd-xgbe-%s", pdata->netdev->name); + if (!buf) + return; + + pdata->xgbe_debugfs = debugfs_create_dir(buf, NULL); + + debugfs_create_file("xgmac_register", 0600, pdata->xgbe_debugfs, pdata, + &xgmac_reg_addr_fops); + + debugfs_create_file("xgmac_register_value", 0600, pdata->xgbe_debugfs, + pdata, &xgmac_reg_value_fops); + + debugfs_create_file("xpcs_mmd", 0600, pdata->xgbe_debugfs, pdata, + &xpcs_mmd_fops); + + debugfs_create_file("xpcs_register", 0600, pdata->xgbe_debugfs, pdata, + &xpcs_reg_addr_fops); + + debugfs_create_file("xpcs_register_value", 0600, pdata->xgbe_debugfs, + pdata, &xpcs_reg_value_fops); + + if (pdata->xprop_regs) { + debugfs_create_file("xprop_register", 0600, pdata->xgbe_debugfs, + pdata, &xprop_reg_addr_fops); + + debugfs_create_file("xprop_register_value", 0600, + pdata->xgbe_debugfs, pdata, + &xprop_reg_value_fops); + } + + if (pdata->xi2c_regs) { + debugfs_create_file("xi2c_register", 0600, pdata->xgbe_debugfs, + pdata, &xi2c_reg_addr_fops); + + debugfs_create_file("xi2c_register_value", 0600, + pdata->xgbe_debugfs, pdata, + &xi2c_reg_value_fops); + } + + if (pdata->vdata->an_cdr_workaround) { + debugfs_create_bool("an_cdr_workaround", 0600, + pdata->xgbe_debugfs, + &pdata->debugfs_an_cdr_workaround); + + debugfs_create_bool("an_cdr_track_early", 0600, + pdata->xgbe_debugfs, + &pdata->debugfs_an_cdr_track_early); + } + + kfree(buf); +} + +void xgbe_debugfs_exit(struct xgbe_prv_data *pdata) +{ + debugfs_remove_recursive(pdata->xgbe_debugfs); + pdata->xgbe_debugfs = NULL; +} + +void xgbe_debugfs_rename(struct xgbe_prv_data *pdata) +{ + char *buf; + + if (!pdata->xgbe_debugfs) + return; + + buf = kasprintf(GFP_KERNEL, "amd-xgbe-%s", pdata->netdev->name); + if (!buf) + return; + + if (!strcmp(pdata->xgbe_debugfs->d_name.name, buf)) + goto out; + + debugfs_rename(pdata->xgbe_debugfs->d_parent, pdata->xgbe_debugfs, + pdata->xgbe_debugfs->d_parent, buf); + +out: + kfree(buf); +} diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-desc.c b/drivers/net/ethernet/amd/xgbe/xgbe-desc.c new file mode 100644 index 000000000..230726d7b --- /dev/null +++ b/drivers/net/ethernet/amd/xgbe/xgbe-desc.c @@ -0,0 +1,676 @@ +/* + * AMD 10Gb Ethernet driver + * + * This file is available to you under your choice of the following two + * licenses: + * + * License 1: GPLv2 + * + * Copyright (c) 2014 Advanced Micro Devices, Inc. + * + * This file is free software; you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or (at + * your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * + * License 2: Modified BSD + * + * Copyright (c) 2014 Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "xgbe.h" +#include "xgbe-common.h" + +static void xgbe_unmap_rdata(struct xgbe_prv_data *, struct xgbe_ring_data *); + +static void xgbe_free_ring(struct xgbe_prv_data *pdata, + struct xgbe_ring *ring) +{ + struct xgbe_ring_data *rdata; + unsigned int i; + + if (!ring) + return; + + if (ring->rdata) { + for (i = 0; i < ring->rdesc_count; i++) { + rdata = XGBE_GET_DESC_DATA(ring, i); + xgbe_unmap_rdata(pdata, rdata); + } + + kfree(ring->rdata); + ring->rdata = NULL; + } + + if (ring->rx_hdr_pa.pages) { + dma_unmap_page(pdata->dev, ring->rx_hdr_pa.pages_dma, + ring->rx_hdr_pa.pages_len, DMA_FROM_DEVICE); + put_page(ring->rx_hdr_pa.pages); + + ring->rx_hdr_pa.pages = NULL; + ring->rx_hdr_pa.pages_len = 0; + ring->rx_hdr_pa.pages_offset = 0; + ring->rx_hdr_pa.pages_dma = 0; + } + + if (ring->rx_buf_pa.pages) { + dma_unmap_page(pdata->dev, ring->rx_buf_pa.pages_dma, + ring->rx_buf_pa.pages_len, DMA_FROM_DEVICE); + put_page(ring->rx_buf_pa.pages); + + ring->rx_buf_pa.pages = NULL; + ring->rx_buf_pa.pages_len = 0; + ring->rx_buf_pa.pages_offset = 0; + ring->rx_buf_pa.pages_dma = 0; + } + + if (ring->rdesc) { + dma_free_coherent(pdata->dev, + (sizeof(struct xgbe_ring_desc) * + ring->rdesc_count), + ring->rdesc, ring->rdesc_dma); + ring->rdesc = NULL; + } +} + +static void xgbe_free_ring_resources(struct xgbe_prv_data *pdata) +{ + struct xgbe_channel *channel; + unsigned int i; + + DBGPR("-->xgbe_free_ring_resources\n"); + + for (i = 0; i < pdata->channel_count; i++) { + channel = pdata->channel[i]; + xgbe_free_ring(pdata, channel->tx_ring); + xgbe_free_ring(pdata, channel->rx_ring); + } + + DBGPR("<--xgbe_free_ring_resources\n"); +} + +static void *xgbe_alloc_node(size_t size, int node) +{ + void *mem; + + mem = kzalloc_node(size, GFP_KERNEL, node); + if (!mem) + mem = kzalloc(size, GFP_KERNEL); + + return mem; +} + +static void *xgbe_dma_alloc_node(struct device *dev, size_t size, + dma_addr_t *dma, int node) +{ + void *mem; + int cur_node = dev_to_node(dev); + + set_dev_node(dev, node); + mem = dma_alloc_coherent(dev, size, dma, GFP_KERNEL); + set_dev_node(dev, cur_node); + + if (!mem) + mem = dma_alloc_coherent(dev, size, dma, GFP_KERNEL); + + return mem; +} + +static int xgbe_init_ring(struct xgbe_prv_data *pdata, + struct xgbe_ring *ring, unsigned int rdesc_count) +{ + size_t size; + + if (!ring) + return 0; + + /* Descriptors */ + size = rdesc_count * sizeof(struct xgbe_ring_desc); + + ring->rdesc_count = rdesc_count; + ring->rdesc = xgbe_dma_alloc_node(pdata->dev, size, &ring->rdesc_dma, + ring->node); + if (!ring->rdesc) + return -ENOMEM; + + /* Descriptor information */ + size = rdesc_count * sizeof(struct xgbe_ring_data); + + ring->rdata = xgbe_alloc_node(size, ring->node); + if (!ring->rdata) + return -ENOMEM; + + netif_dbg(pdata, drv, pdata->netdev, + "rdesc=%p, rdesc_dma=%pad, rdata=%p, node=%d\n", + ring->rdesc, &ring->rdesc_dma, ring->rdata, ring->node); + + return 0; +} + +static int xgbe_alloc_ring_resources(struct xgbe_prv_data *pdata) +{ + struct xgbe_channel *channel; + unsigned int i; + int ret; + + for (i = 0; i < pdata->channel_count; i++) { + channel = pdata->channel[i]; + netif_dbg(pdata, drv, pdata->netdev, "%s - Tx ring:\n", + channel->name); + + ret = xgbe_init_ring(pdata, channel->tx_ring, + pdata->tx_desc_count); + if (ret) { + netdev_alert(pdata->netdev, + "error initializing Tx ring\n"); + goto err_ring; + } + + netif_dbg(pdata, drv, pdata->netdev, "%s - Rx ring:\n", + channel->name); + + ret = xgbe_init_ring(pdata, channel->rx_ring, + pdata->rx_desc_count); + if (ret) { + netdev_alert(pdata->netdev, + "error initializing Rx ring\n"); + goto err_ring; + } + } + + return 0; + +err_ring: + xgbe_free_ring_resources(pdata); + + return ret; +} + +static int xgbe_alloc_pages(struct xgbe_prv_data *pdata, + struct xgbe_page_alloc *pa, int alloc_order, + int node) +{ + struct page *pages = NULL; + dma_addr_t pages_dma; + gfp_t gfp; + int order; + +again: + order = alloc_order; + + /* Try to obtain pages, decreasing order if necessary */ + gfp = GFP_ATOMIC | __GFP_COMP | __GFP_NOWARN; + while (order >= 0) { + pages = alloc_pages_node(node, gfp, order); + if (pages) + break; + + order--; + } + + /* If we couldn't get local pages, try getting from anywhere */ + if (!pages && (node != NUMA_NO_NODE)) { + node = NUMA_NO_NODE; + goto again; + } + + if (!pages) + return -ENOMEM; + + /* Map the pages */ + pages_dma = dma_map_page(pdata->dev, pages, 0, + PAGE_SIZE << order, DMA_FROM_DEVICE); + if (dma_mapping_error(pdata->dev, pages_dma)) { + put_page(pages); + return -ENOMEM; + } + + pa->pages = pages; + pa->pages_len = PAGE_SIZE << order; + pa->pages_offset = 0; + pa->pages_dma = pages_dma; + + return 0; +} + +static void xgbe_set_buffer_data(struct xgbe_buffer_data *bd, + struct xgbe_page_alloc *pa, + unsigned int len) +{ + get_page(pa->pages); + bd->pa = *pa; + + bd->dma_base = pa->pages_dma; + bd->dma_off = pa->pages_offset; + bd->dma_len = len; + + pa->pages_offset += len; + if ((pa->pages_offset + len) > pa->pages_len) { + /* This data descriptor is responsible for unmapping page(s) */ + bd->pa_unmap = *pa; + + /* Get a new allocation next time */ + pa->pages = NULL; + pa->pages_len = 0; + pa->pages_offset = 0; + pa->pages_dma = 0; + } +} + +static int xgbe_map_rx_buffer(struct xgbe_prv_data *pdata, + struct xgbe_ring *ring, + struct xgbe_ring_data *rdata) +{ + int ret; + + if (!ring->rx_hdr_pa.pages) { + ret = xgbe_alloc_pages(pdata, &ring->rx_hdr_pa, 0, ring->node); + if (ret) + return ret; + } + + if (!ring->rx_buf_pa.pages) { + ret = xgbe_alloc_pages(pdata, &ring->rx_buf_pa, + PAGE_ALLOC_COSTLY_ORDER, ring->node); + if (ret) + return ret; + } + + /* Set up the header page info */ + xgbe_set_buffer_data(&rdata->rx.hdr, &ring->rx_hdr_pa, + XGBE_SKB_ALLOC_SIZE); + + /* Set up the buffer page info */ + xgbe_set_buffer_data(&rdata->rx.buf, &ring->rx_buf_pa, + pdata->rx_buf_size); + + return 0; +} + +static void xgbe_wrapper_tx_descriptor_init(struct xgbe_prv_data *pdata) +{ + struct xgbe_hw_if *hw_if = &pdata->hw_if; + struct xgbe_channel *channel; + struct xgbe_ring *ring; + struct xgbe_ring_data *rdata; + struct xgbe_ring_desc *rdesc; + dma_addr_t rdesc_dma; + unsigned int i, j; + + DBGPR("-->xgbe_wrapper_tx_descriptor_init\n"); + + for (i = 0; i < pdata->channel_count; i++) { + channel = pdata->channel[i]; + ring = channel->tx_ring; + if (!ring) + break; + + rdesc = ring->rdesc; + rdesc_dma = ring->rdesc_dma; + + for (j = 0; j < ring->rdesc_count; j++) { + rdata = XGBE_GET_DESC_DATA(ring, j); + + rdata->rdesc = rdesc; + rdata->rdesc_dma = rdesc_dma; + + rdesc++; + rdesc_dma += sizeof(struct xgbe_ring_desc); + } + + ring->cur = 0; + ring->dirty = 0; + memset(&ring->tx, 0, sizeof(ring->tx)); + + hw_if->tx_desc_init(channel); + } + + DBGPR("<--xgbe_wrapper_tx_descriptor_init\n"); +} + +static void xgbe_wrapper_rx_descriptor_init(struct xgbe_prv_data *pdata) +{ + struct xgbe_hw_if *hw_if = &pdata->hw_if; + struct xgbe_channel *channel; + struct xgbe_ring *ring; + struct xgbe_ring_desc *rdesc; + struct xgbe_ring_data *rdata; + dma_addr_t rdesc_dma; + unsigned int i, j; + + DBGPR("-->xgbe_wrapper_rx_descriptor_init\n"); + + for (i = 0; i < pdata->channel_count; i++) { + channel = pdata->channel[i]; + ring = channel->rx_ring; + if (!ring) + break; + + rdesc = ring->rdesc; + rdesc_dma = ring->rdesc_dma; + + for (j = 0; j < ring->rdesc_count; j++) { + rdata = XGBE_GET_DESC_DATA(ring, j); + + rdata->rdesc = rdesc; + rdata->rdesc_dma = rdesc_dma; + + if (xgbe_map_rx_buffer(pdata, ring, rdata)) + break; + + rdesc++; + rdesc_dma += sizeof(struct xgbe_ring_desc); + } + + ring->cur = 0; + ring->dirty = 0; + + hw_if->rx_desc_init(channel); + } + + DBGPR("<--xgbe_wrapper_rx_descriptor_init\n"); +} + +static void xgbe_unmap_rdata(struct xgbe_prv_data *pdata, + struct xgbe_ring_data *rdata) +{ + if (rdata->skb_dma) { + if (rdata->mapped_as_page) { + dma_unmap_page(pdata->dev, rdata->skb_dma, + rdata->skb_dma_len, DMA_TO_DEVICE); + } else { + dma_unmap_single(pdata->dev, rdata->skb_dma, + rdata->skb_dma_len, DMA_TO_DEVICE); + } + rdata->skb_dma = 0; + rdata->skb_dma_len = 0; + } + + if (rdata->skb) { + dev_kfree_skb_any(rdata->skb); + rdata->skb = NULL; + } + + if (rdata->rx.hdr.pa.pages) + put_page(rdata->rx.hdr.pa.pages); + + if (rdata->rx.hdr.pa_unmap.pages) { + dma_unmap_page(pdata->dev, rdata->rx.hdr.pa_unmap.pages_dma, + rdata->rx.hdr.pa_unmap.pages_len, + DMA_FROM_DEVICE); + put_page(rdata->rx.hdr.pa_unmap.pages); + } + + if (rdata->rx.buf.pa.pages) + put_page(rdata->rx.buf.pa.pages); + + if (rdata->rx.buf.pa_unmap.pages) { + dma_unmap_page(pdata->dev, rdata->rx.buf.pa_unmap.pages_dma, + rdata->rx.buf.pa_unmap.pages_len, + DMA_FROM_DEVICE); + put_page(rdata->rx.buf.pa_unmap.pages); + } + + memset(&rdata->tx, 0, sizeof(rdata->tx)); + memset(&rdata->rx, 0, sizeof(rdata->rx)); + + rdata->mapped_as_page = 0; + + if (rdata->state_saved) { + rdata->state_saved = 0; + rdata->state.skb = NULL; + rdata->state.len = 0; + rdata->state.error = 0; + } +} + +static int xgbe_map_tx_skb(struct xgbe_channel *channel, struct sk_buff *skb) +{ + struct xgbe_prv_data *pdata = channel->pdata; + struct xgbe_ring *ring = channel->tx_ring; + struct xgbe_ring_data *rdata; + struct xgbe_packet_data *packet; + skb_frag_t *frag; + dma_addr_t skb_dma; + unsigned int start_index, cur_index; + unsigned int offset, tso, vlan, datalen, len; + unsigned int i; + + DBGPR("-->xgbe_map_tx_skb: cur = %d\n", ring->cur); + + offset = 0; + start_index = ring->cur; + cur_index = ring->cur; + + packet = &ring->packet_data; + packet->rdesc_count = 0; + packet->length = 0; + + tso = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, + TSO_ENABLE); + vlan = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, + VLAN_CTAG); + + /* Save space for a context descriptor if needed */ + if ((tso && (packet->mss != ring->tx.cur_mss)) || + (vlan && (packet->vlan_ctag != ring->tx.cur_vlan_ctag))) + cur_index++; + rdata = XGBE_GET_DESC_DATA(ring, cur_index); + + if (tso) { + /* Map the TSO header */ + skb_dma = dma_map_single(pdata->dev, skb->data, + packet->header_len, DMA_TO_DEVICE); + if (dma_mapping_error(pdata->dev, skb_dma)) { + netdev_alert(pdata->netdev, "dma_map_single failed\n"); + goto err_out; + } + rdata->skb_dma = skb_dma; + rdata->skb_dma_len = packet->header_len; + netif_dbg(pdata, tx_queued, pdata->netdev, + "skb header: index=%u, dma=%pad, len=%u\n", + cur_index, &skb_dma, packet->header_len); + + offset = packet->header_len; + + packet->length += packet->header_len; + + cur_index++; + rdata = XGBE_GET_DESC_DATA(ring, cur_index); + } + + /* Map the (remainder of the) packet */ + for (datalen = skb_headlen(skb) - offset; datalen; ) { + len = min_t(unsigned int, datalen, XGBE_TX_MAX_BUF_SIZE); + + skb_dma = dma_map_single(pdata->dev, skb->data + offset, len, + DMA_TO_DEVICE); + if (dma_mapping_error(pdata->dev, skb_dma)) { + netdev_alert(pdata->netdev, "dma_map_single failed\n"); + goto err_out; + } + rdata->skb_dma = skb_dma; + rdata->skb_dma_len = len; + netif_dbg(pdata, tx_queued, pdata->netdev, + "skb data: index=%u, dma=%pad, len=%u\n", + cur_index, &skb_dma, len); + + datalen -= len; + offset += len; + + packet->length += len; + + cur_index++; + rdata = XGBE_GET_DESC_DATA(ring, cur_index); + } + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + netif_dbg(pdata, tx_queued, pdata->netdev, + "mapping frag %u\n", i); + + frag = &skb_shinfo(skb)->frags[i]; + offset = 0; + + for (datalen = skb_frag_size(frag); datalen; ) { + len = min_t(unsigned int, datalen, + XGBE_TX_MAX_BUF_SIZE); + + skb_dma = skb_frag_dma_map(pdata->dev, frag, offset, + len, DMA_TO_DEVICE); + if (dma_mapping_error(pdata->dev, skb_dma)) { + netdev_alert(pdata->netdev, + "skb_frag_dma_map failed\n"); + goto err_out; + } + rdata->skb_dma = skb_dma; + rdata->skb_dma_len = len; + rdata->mapped_as_page = 1; + netif_dbg(pdata, tx_queued, pdata->netdev, + "skb frag: index=%u, dma=%pad, len=%u\n", + cur_index, &skb_dma, len); + + datalen -= len; + offset += len; + + packet->length += len; + + cur_index++; + rdata = XGBE_GET_DESC_DATA(ring, cur_index); + } + } + + /* Save the skb address in the last entry. We always have some data + * that has been mapped so rdata is always advanced past the last + * piece of mapped data - use the entry pointed to by cur_index - 1. + */ + rdata = XGBE_GET_DESC_DATA(ring, cur_index - 1); + rdata->skb = skb; + + /* Save the number of descriptor entries used */ + packet->rdesc_count = cur_index - start_index; + + DBGPR("<--xgbe_map_tx_skb: count=%u\n", packet->rdesc_count); + + return packet->rdesc_count; + +err_out: + while (start_index < cur_index) { + rdata = XGBE_GET_DESC_DATA(ring, start_index++); + xgbe_unmap_rdata(pdata, rdata); + } + + DBGPR("<--xgbe_map_tx_skb: count=0\n"); + + return 0; +} + +void xgbe_init_function_ptrs_desc(struct xgbe_desc_if *desc_if) +{ + DBGPR("-->xgbe_init_function_ptrs_desc\n"); + + desc_if->alloc_ring_resources = xgbe_alloc_ring_resources; + desc_if->free_ring_resources = xgbe_free_ring_resources; + desc_if->map_tx_skb = xgbe_map_tx_skb; + desc_if->map_rx_buffer = xgbe_map_rx_buffer; + desc_if->unmap_rdata = xgbe_unmap_rdata; + desc_if->wrapper_tx_desc_init = xgbe_wrapper_tx_descriptor_init; + desc_if->wrapper_rx_desc_init = xgbe_wrapper_rx_descriptor_init; + + DBGPR("<--xgbe_init_function_ptrs_desc\n"); +} diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c new file mode 100644 index 000000000..4030d619e --- /dev/null +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -0,0 +1,3654 @@ +/* + * AMD 10Gb Ethernet driver + * + * This file is available to you under your choice of the following two + * licenses: + * + * License 1: GPLv2 + * + * Copyright (c) 2014-2016 Advanced Micro Devices, Inc. + * + * This file is free software; you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or (at + * your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * + * License 2: Modified BSD + * + * Copyright (c) 2014-2016 Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/phy.h> +#include <linux/mdio.h> +#include <linux/clk.h> +#include <linux/bitrev.h> +#include <linux/crc32.h> +#include <linux/crc32poly.h> + +#include "xgbe.h" +#include "xgbe-common.h" + +static inline unsigned int xgbe_get_max_frame(struct xgbe_prv_data *pdata) +{ + return pdata->netdev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; +} + +static unsigned int xgbe_usec_to_riwt(struct xgbe_prv_data *pdata, + unsigned int usec) +{ + unsigned long rate; + unsigned int ret; + + DBGPR("-->xgbe_usec_to_riwt\n"); + + rate = pdata->sysclk_rate; + + /* + * Convert the input usec value to the watchdog timer value. Each + * watchdog timer value is equivalent to 256 clock cycles. + * Calculate the required value as: + * ( usec * ( system_clock_mhz / 10^6 ) / 256 + */ + ret = (usec * (rate / 1000000)) / 256; + + DBGPR("<--xgbe_usec_to_riwt\n"); + + return ret; +} + +static unsigned int xgbe_riwt_to_usec(struct xgbe_prv_data *pdata, + unsigned int riwt) +{ + unsigned long rate; + unsigned int ret; + + DBGPR("-->xgbe_riwt_to_usec\n"); + + rate = pdata->sysclk_rate; + + /* + * Convert the input watchdog timer value to the usec value. Each + * watchdog timer value is equivalent to 256 clock cycles. + * Calculate the required value as: + * ( riwt * 256 ) / ( system_clock_mhz / 10^6 ) + */ + ret = (riwt * 256) / (rate / 1000000); + + DBGPR("<--xgbe_riwt_to_usec\n"); + + return ret; +} + +static int xgbe_config_pbl_val(struct xgbe_prv_data *pdata) +{ + unsigned int pblx8, pbl; + unsigned int i; + + pblx8 = DMA_PBL_X8_DISABLE; + pbl = pdata->pbl; + + if (pdata->pbl > 32) { + pblx8 = DMA_PBL_X8_ENABLE; + pbl >>= 3; + } + + for (i = 0; i < pdata->channel_count; i++) { + XGMAC_DMA_IOWRITE_BITS(pdata->channel[i], DMA_CH_CR, PBLX8, + pblx8); + + if (pdata->channel[i]->tx_ring) + XGMAC_DMA_IOWRITE_BITS(pdata->channel[i], DMA_CH_TCR, + PBL, pbl); + + if (pdata->channel[i]->rx_ring) + XGMAC_DMA_IOWRITE_BITS(pdata->channel[i], DMA_CH_RCR, + PBL, pbl); + } + + return 0; +} + +static int xgbe_config_osp_mode(struct xgbe_prv_data *pdata) +{ + unsigned int i; + + for (i = 0; i < pdata->channel_count; i++) { + if (!pdata->channel[i]->tx_ring) + break; + + XGMAC_DMA_IOWRITE_BITS(pdata->channel[i], DMA_CH_TCR, OSP, + pdata->tx_osp_mode); + } + + return 0; +} + +static int xgbe_config_rsf_mode(struct xgbe_prv_data *pdata, unsigned int val) +{ + unsigned int i; + + for (i = 0; i < pdata->rx_q_count; i++) + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQOMR, RSF, val); + + return 0; +} + +static int xgbe_config_tsf_mode(struct xgbe_prv_data *pdata, unsigned int val) +{ + unsigned int i; + + for (i = 0; i < pdata->tx_q_count; i++) + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_TQOMR, TSF, val); + + return 0; +} + +static int xgbe_config_rx_threshold(struct xgbe_prv_data *pdata, + unsigned int val) +{ + unsigned int i; + + for (i = 0; i < pdata->rx_q_count; i++) + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQOMR, RTC, val); + + return 0; +} + +static int xgbe_config_tx_threshold(struct xgbe_prv_data *pdata, + unsigned int val) +{ + unsigned int i; + + for (i = 0; i < pdata->tx_q_count; i++) + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_TQOMR, TTC, val); + + return 0; +} + +static int xgbe_config_rx_coalesce(struct xgbe_prv_data *pdata) +{ + unsigned int i; + + for (i = 0; i < pdata->channel_count; i++) { + if (!pdata->channel[i]->rx_ring) + break; + + XGMAC_DMA_IOWRITE_BITS(pdata->channel[i], DMA_CH_RIWT, RWT, + pdata->rx_riwt); + } + + return 0; +} + +static int xgbe_config_tx_coalesce(struct xgbe_prv_data *pdata) +{ + return 0; +} + +static void xgbe_config_rx_buffer_size(struct xgbe_prv_data *pdata) +{ + unsigned int i; + + for (i = 0; i < pdata->channel_count; i++) { + if (!pdata->channel[i]->rx_ring) + break; + + XGMAC_DMA_IOWRITE_BITS(pdata->channel[i], DMA_CH_RCR, RBSZ, + pdata->rx_buf_size); + } +} + +static void xgbe_config_tso_mode(struct xgbe_prv_data *pdata) +{ + unsigned int i; + + for (i = 0; i < pdata->channel_count; i++) { + if (!pdata->channel[i]->tx_ring) + break; + + XGMAC_DMA_IOWRITE_BITS(pdata->channel[i], DMA_CH_TCR, TSE, 1); + } +} + +static void xgbe_config_sph_mode(struct xgbe_prv_data *pdata) +{ + unsigned int i; + + for (i = 0; i < pdata->channel_count; i++) { + if (!pdata->channel[i]->rx_ring) + break; + + XGMAC_DMA_IOWRITE_BITS(pdata->channel[i], DMA_CH_CR, SPH, 1); + } + + XGMAC_IOWRITE_BITS(pdata, MAC_RCR, HDSMS, XGBE_SPH_HDSMS_SIZE); +} + +static int xgbe_write_rss_reg(struct xgbe_prv_data *pdata, unsigned int type, + unsigned int index, unsigned int val) +{ + unsigned int wait; + int ret = 0; + + mutex_lock(&pdata->rss_mutex); + + if (XGMAC_IOREAD_BITS(pdata, MAC_RSSAR, OB)) { + ret = -EBUSY; + goto unlock; + } + + XGMAC_IOWRITE(pdata, MAC_RSSDR, val); + + XGMAC_IOWRITE_BITS(pdata, MAC_RSSAR, RSSIA, index); + XGMAC_IOWRITE_BITS(pdata, MAC_RSSAR, ADDRT, type); + XGMAC_IOWRITE_BITS(pdata, MAC_RSSAR, CT, 0); + XGMAC_IOWRITE_BITS(pdata, MAC_RSSAR, OB, 1); + + wait = 1000; + while (wait--) { + if (!XGMAC_IOREAD_BITS(pdata, MAC_RSSAR, OB)) + goto unlock; + + usleep_range(1000, 1500); + } + + ret = -EBUSY; + +unlock: + mutex_unlock(&pdata->rss_mutex); + + return ret; +} + +static int xgbe_write_rss_hash_key(struct xgbe_prv_data *pdata) +{ + unsigned int key_regs = sizeof(pdata->rss_key) / sizeof(u32); + unsigned int *key = (unsigned int *)&pdata->rss_key; + int ret; + + while (key_regs--) { + ret = xgbe_write_rss_reg(pdata, XGBE_RSS_HASH_KEY_TYPE, + key_regs, *key++); + if (ret) + return ret; + } + + return 0; +} + +static int xgbe_write_rss_lookup_table(struct xgbe_prv_data *pdata) +{ + unsigned int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(pdata->rss_table); i++) { + ret = xgbe_write_rss_reg(pdata, + XGBE_RSS_LOOKUP_TABLE_TYPE, i, + pdata->rss_table[i]); + if (ret) + return ret; + } + + return 0; +} + +static int xgbe_set_rss_hash_key(struct xgbe_prv_data *pdata, const u8 *key) +{ + memcpy(pdata->rss_key, key, sizeof(pdata->rss_key)); + + return xgbe_write_rss_hash_key(pdata); +} + +static int xgbe_set_rss_lookup_table(struct xgbe_prv_data *pdata, + const u32 *table) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(pdata->rss_table); i++) + XGMAC_SET_BITS(pdata->rss_table[i], MAC_RSSDR, DMCH, table[i]); + + return xgbe_write_rss_lookup_table(pdata); +} + +static int xgbe_enable_rss(struct xgbe_prv_data *pdata) +{ + int ret; + + if (!pdata->hw_feat.rss) + return -EOPNOTSUPP; + + /* Program the hash key */ + ret = xgbe_write_rss_hash_key(pdata); + if (ret) + return ret; + + /* Program the lookup table */ + ret = xgbe_write_rss_lookup_table(pdata); + if (ret) + return ret; + + /* Set the RSS options */ + XGMAC_IOWRITE(pdata, MAC_RSSCR, pdata->rss_options); + + /* Enable RSS */ + XGMAC_IOWRITE_BITS(pdata, MAC_RSSCR, RSSE, 1); + + return 0; +} + +static int xgbe_disable_rss(struct xgbe_prv_data *pdata) +{ + if (!pdata->hw_feat.rss) + return -EOPNOTSUPP; + + XGMAC_IOWRITE_BITS(pdata, MAC_RSSCR, RSSE, 0); + + return 0; +} + +static void xgbe_config_rss(struct xgbe_prv_data *pdata) +{ + int ret; + + if (!pdata->hw_feat.rss) + return; + + if (pdata->netdev->features & NETIF_F_RXHASH) + ret = xgbe_enable_rss(pdata); + else + ret = xgbe_disable_rss(pdata); + + if (ret) + netdev_err(pdata->netdev, + "error configuring RSS, RSS disabled\n"); +} + +static bool xgbe_is_pfc_queue(struct xgbe_prv_data *pdata, + unsigned int queue) +{ + unsigned int prio, tc; + + for (prio = 0; prio < IEEE_8021QAZ_MAX_TCS; prio++) { + /* Does this queue handle the priority? */ + if (pdata->prio2q_map[prio] != queue) + continue; + + /* Get the Traffic Class for this priority */ + tc = pdata->ets->prio_tc[prio]; + + /* Check if PFC is enabled for this traffic class */ + if (pdata->pfc->pfc_en & (1 << tc)) + return true; + } + + return false; +} + +static void xgbe_set_vxlan_id(struct xgbe_prv_data *pdata) +{ + /* Program the VXLAN port */ + XGMAC_IOWRITE_BITS(pdata, MAC_TIR, TNID, pdata->vxlan_port); + + netif_dbg(pdata, drv, pdata->netdev, "VXLAN tunnel id set to %hx\n", + pdata->vxlan_port); +} + +static void xgbe_enable_vxlan(struct xgbe_prv_data *pdata) +{ + if (!pdata->hw_feat.vxn) + return; + + /* Program the VXLAN port */ + xgbe_set_vxlan_id(pdata); + + /* Allow for IPv6/UDP zero-checksum VXLAN packets */ + XGMAC_IOWRITE_BITS(pdata, MAC_PFR, VUCC, 1); + + /* Enable VXLAN tunneling mode */ + XGMAC_IOWRITE_BITS(pdata, MAC_TCR, VNM, 0); + XGMAC_IOWRITE_BITS(pdata, MAC_TCR, VNE, 1); + + netif_dbg(pdata, drv, pdata->netdev, "VXLAN acceleration enabled\n"); +} + +static void xgbe_disable_vxlan(struct xgbe_prv_data *pdata) +{ + if (!pdata->hw_feat.vxn) + return; + + /* Disable tunneling mode */ + XGMAC_IOWRITE_BITS(pdata, MAC_TCR, VNE, 0); + + /* Clear IPv6/UDP zero-checksum VXLAN packets setting */ + XGMAC_IOWRITE_BITS(pdata, MAC_PFR, VUCC, 0); + + /* Clear the VXLAN port */ + XGMAC_IOWRITE_BITS(pdata, MAC_TIR, TNID, 0); + + netif_dbg(pdata, drv, pdata->netdev, "VXLAN acceleration disabled\n"); +} + +static unsigned int xgbe_get_fc_queue_count(struct xgbe_prv_data *pdata) +{ + unsigned int max_q_count = XGMAC_MAX_FLOW_CONTROL_QUEUES; + + /* From MAC ver 30H the TFCR is per priority, instead of per queue */ + if (XGMAC_GET_BITS(pdata->hw_feat.version, MAC_VR, SNPSVER) >= 0x30) + return max_q_count; + else + return min_t(unsigned int, pdata->tx_q_count, max_q_count); +} + +static int xgbe_disable_tx_flow_control(struct xgbe_prv_data *pdata) +{ + unsigned int reg, reg_val; + unsigned int i, q_count; + + /* Clear MTL flow control */ + for (i = 0; i < pdata->rx_q_count; i++) + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQOMR, EHFC, 0); + + /* Clear MAC flow control */ + q_count = xgbe_get_fc_queue_count(pdata); + reg = MAC_Q0TFCR; + for (i = 0; i < q_count; i++) { + reg_val = XGMAC_IOREAD(pdata, reg); + XGMAC_SET_BITS(reg_val, MAC_Q0TFCR, TFE, 0); + XGMAC_IOWRITE(pdata, reg, reg_val); + + reg += MAC_QTFCR_INC; + } + + return 0; +} + +static int xgbe_enable_tx_flow_control(struct xgbe_prv_data *pdata) +{ + struct ieee_pfc *pfc = pdata->pfc; + struct ieee_ets *ets = pdata->ets; + unsigned int reg, reg_val; + unsigned int i, q_count; + + /* Set MTL flow control */ + for (i = 0; i < pdata->rx_q_count; i++) { + unsigned int ehfc = 0; + + if (pdata->rx_rfd[i]) { + /* Flow control thresholds are established */ + if (pfc && ets) { + if (xgbe_is_pfc_queue(pdata, i)) + ehfc = 1; + } else { + ehfc = 1; + } + } + + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQOMR, EHFC, ehfc); + + netif_dbg(pdata, drv, pdata->netdev, + "flow control %s for RXq%u\n", + ehfc ? "enabled" : "disabled", i); + } + + /* Set MAC flow control */ + q_count = xgbe_get_fc_queue_count(pdata); + reg = MAC_Q0TFCR; + for (i = 0; i < q_count; i++) { + reg_val = XGMAC_IOREAD(pdata, reg); + + /* Enable transmit flow control */ + XGMAC_SET_BITS(reg_val, MAC_Q0TFCR, TFE, 1); + /* Set pause time */ + XGMAC_SET_BITS(reg_val, MAC_Q0TFCR, PT, 0xffff); + + XGMAC_IOWRITE(pdata, reg, reg_val); + + reg += MAC_QTFCR_INC; + } + + return 0; +} + +static int xgbe_disable_rx_flow_control(struct xgbe_prv_data *pdata) +{ + XGMAC_IOWRITE_BITS(pdata, MAC_RFCR, RFE, 0); + + return 0; +} + +static int xgbe_enable_rx_flow_control(struct xgbe_prv_data *pdata) +{ + XGMAC_IOWRITE_BITS(pdata, MAC_RFCR, RFE, 1); + + return 0; +} + +static int xgbe_config_tx_flow_control(struct xgbe_prv_data *pdata) +{ + struct ieee_pfc *pfc = pdata->pfc; + + if (pdata->tx_pause || (pfc && pfc->pfc_en)) + xgbe_enable_tx_flow_control(pdata); + else + xgbe_disable_tx_flow_control(pdata); + + return 0; +} + +static int xgbe_config_rx_flow_control(struct xgbe_prv_data *pdata) +{ + struct ieee_pfc *pfc = pdata->pfc; + + if (pdata->rx_pause || (pfc && pfc->pfc_en)) + xgbe_enable_rx_flow_control(pdata); + else + xgbe_disable_rx_flow_control(pdata); + + return 0; +} + +static void xgbe_config_flow_control(struct xgbe_prv_data *pdata) +{ + struct ieee_pfc *pfc = pdata->pfc; + + xgbe_config_tx_flow_control(pdata); + xgbe_config_rx_flow_control(pdata); + + XGMAC_IOWRITE_BITS(pdata, MAC_RFCR, PFCE, + (pfc && pfc->pfc_en) ? 1 : 0); +} + +static void xgbe_enable_dma_interrupts(struct xgbe_prv_data *pdata) +{ + struct xgbe_channel *channel; + unsigned int i, ver; + + /* Set the interrupt mode if supported */ + if (pdata->channel_irq_mode) + XGMAC_IOWRITE_BITS(pdata, DMA_MR, INTM, + pdata->channel_irq_mode); + + ver = XGMAC_GET_BITS(pdata->hw_feat.version, MAC_VR, SNPSVER); + + for (i = 0; i < pdata->channel_count; i++) { + channel = pdata->channel[i]; + + /* Clear all the interrupts which are set */ + XGMAC_DMA_IOWRITE(channel, DMA_CH_SR, + XGMAC_DMA_IOREAD(channel, DMA_CH_SR)); + + /* Clear all interrupt enable bits */ + channel->curr_ier = 0; + + /* Enable following interrupts + * NIE - Normal Interrupt Summary Enable + * AIE - Abnormal Interrupt Summary Enable + * FBEE - Fatal Bus Error Enable + */ + if (ver < 0x21) { + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, NIE20, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, AIE20, 1); + } else { + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, NIE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, AIE, 1); + } + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, FBEE, 1); + + if (channel->tx_ring) { + /* Enable the following Tx interrupts + * TIE - Transmit Interrupt Enable (unless using + * per channel interrupts in edge triggered + * mode) + */ + if (!pdata->per_channel_irq || pdata->channel_irq_mode) + XGMAC_SET_BITS(channel->curr_ier, + DMA_CH_IER, TIE, 1); + } + if (channel->rx_ring) { + /* Enable following Rx interrupts + * RBUE - Receive Buffer Unavailable Enable + * RIE - Receive Interrupt Enable (unless using + * per channel interrupts in edge triggered + * mode) + */ + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RBUE, 1); + if (!pdata->per_channel_irq || pdata->channel_irq_mode) + XGMAC_SET_BITS(channel->curr_ier, + DMA_CH_IER, RIE, 1); + } + + XGMAC_DMA_IOWRITE(channel, DMA_CH_IER, channel->curr_ier); + } +} + +static void xgbe_enable_mtl_interrupts(struct xgbe_prv_data *pdata) +{ + unsigned int mtl_q_isr; + unsigned int q_count, i; + + q_count = max(pdata->hw_feat.tx_q_cnt, pdata->hw_feat.rx_q_cnt); + for (i = 0; i < q_count; i++) { + /* Clear all the interrupts which are set */ + mtl_q_isr = XGMAC_MTL_IOREAD(pdata, i, MTL_Q_ISR); + XGMAC_MTL_IOWRITE(pdata, i, MTL_Q_ISR, mtl_q_isr); + + /* No MTL interrupts to be enabled */ + XGMAC_MTL_IOWRITE(pdata, i, MTL_Q_IER, 0); + } +} + +static void xgbe_enable_mac_interrupts(struct xgbe_prv_data *pdata) +{ + unsigned int mac_ier = 0; + + /* Enable Timestamp interrupt */ + XGMAC_SET_BITS(mac_ier, MAC_IER, TSIE, 1); + + XGMAC_IOWRITE(pdata, MAC_IER, mac_ier); + + /* Enable all counter interrupts */ + XGMAC_IOWRITE_BITS(pdata, MMC_RIER, ALL_INTERRUPTS, 0xffffffff); + XGMAC_IOWRITE_BITS(pdata, MMC_TIER, ALL_INTERRUPTS, 0xffffffff); + + /* Enable MDIO single command completion interrupt */ + XGMAC_IOWRITE_BITS(pdata, MAC_MDIOIER, SNGLCOMPIE, 1); +} + +static void xgbe_enable_ecc_interrupts(struct xgbe_prv_data *pdata) +{ + unsigned int ecc_isr, ecc_ier = 0; + + if (!pdata->vdata->ecc_support) + return; + + /* Clear all the interrupts which are set */ + ecc_isr = XP_IOREAD(pdata, XP_ECC_ISR); + XP_IOWRITE(pdata, XP_ECC_ISR, ecc_isr); + + /* Enable ECC interrupts */ + XP_SET_BITS(ecc_ier, XP_ECC_IER, TX_DED, 1); + XP_SET_BITS(ecc_ier, XP_ECC_IER, TX_SEC, 1); + XP_SET_BITS(ecc_ier, XP_ECC_IER, RX_DED, 1); + XP_SET_BITS(ecc_ier, XP_ECC_IER, RX_SEC, 1); + XP_SET_BITS(ecc_ier, XP_ECC_IER, DESC_DED, 1); + XP_SET_BITS(ecc_ier, XP_ECC_IER, DESC_SEC, 1); + + XP_IOWRITE(pdata, XP_ECC_IER, ecc_ier); +} + +static void xgbe_disable_ecc_ded(struct xgbe_prv_data *pdata) +{ + unsigned int ecc_ier; + + ecc_ier = XP_IOREAD(pdata, XP_ECC_IER); + + /* Disable ECC DED interrupts */ + XP_SET_BITS(ecc_ier, XP_ECC_IER, TX_DED, 0); + XP_SET_BITS(ecc_ier, XP_ECC_IER, RX_DED, 0); + XP_SET_BITS(ecc_ier, XP_ECC_IER, DESC_DED, 0); + + XP_IOWRITE(pdata, XP_ECC_IER, ecc_ier); +} + +static void xgbe_disable_ecc_sec(struct xgbe_prv_data *pdata, + enum xgbe_ecc_sec sec) +{ + unsigned int ecc_ier; + + ecc_ier = XP_IOREAD(pdata, XP_ECC_IER); + + /* Disable ECC SEC interrupt */ + switch (sec) { + case XGBE_ECC_SEC_TX: + XP_SET_BITS(ecc_ier, XP_ECC_IER, TX_SEC, 0); + break; + case XGBE_ECC_SEC_RX: + XP_SET_BITS(ecc_ier, XP_ECC_IER, RX_SEC, 0); + break; + case XGBE_ECC_SEC_DESC: + XP_SET_BITS(ecc_ier, XP_ECC_IER, DESC_SEC, 0); + break; + } + + XP_IOWRITE(pdata, XP_ECC_IER, ecc_ier); +} + +static int xgbe_set_speed(struct xgbe_prv_data *pdata, int speed) +{ + unsigned int ss; + + switch (speed) { + case SPEED_1000: + ss = 0x03; + break; + case SPEED_2500: + ss = 0x02; + break; + case SPEED_10000: + ss = 0x00; + break; + default: + return -EINVAL; + } + + if (XGMAC_IOREAD_BITS(pdata, MAC_TCR, SS) != ss) + XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, ss); + + return 0; +} + +static int xgbe_enable_rx_vlan_stripping(struct xgbe_prv_data *pdata) +{ + /* Put the VLAN tag in the Rx descriptor */ + XGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, EVLRXS, 1); + + /* Don't check the VLAN type */ + XGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, DOVLTC, 1); + + /* Check only C-TAG (0x8100) packets */ + XGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, ERSVLM, 0); + + /* Don't consider an S-TAG (0x88A8) packet as a VLAN packet */ + XGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, ESVL, 0); + + /* Enable VLAN tag stripping */ + XGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, EVLS, 0x3); + + return 0; +} + +static int xgbe_disable_rx_vlan_stripping(struct xgbe_prv_data *pdata) +{ + XGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, EVLS, 0); + + return 0; +} + +static int xgbe_enable_rx_vlan_filtering(struct xgbe_prv_data *pdata) +{ + /* Enable VLAN filtering */ + XGMAC_IOWRITE_BITS(pdata, MAC_PFR, VTFE, 1); + + /* Enable VLAN Hash Table filtering */ + XGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, VTHM, 1); + + /* Disable VLAN tag inverse matching */ + XGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, VTIM, 0); + + /* Only filter on the lower 12-bits of the VLAN tag */ + XGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, ETV, 1); + + /* In order for the VLAN Hash Table filtering to be effective, + * the VLAN tag identifier in the VLAN Tag Register must not + * be zero. Set the VLAN tag identifier to "1" to enable the + * VLAN Hash Table filtering. This implies that a VLAN tag of + * 1 will always pass filtering. + */ + XGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, VL, 1); + + return 0; +} + +static int xgbe_disable_rx_vlan_filtering(struct xgbe_prv_data *pdata) +{ + /* Disable VLAN filtering */ + XGMAC_IOWRITE_BITS(pdata, MAC_PFR, VTFE, 0); + + return 0; +} + +static u32 xgbe_vid_crc32_le(__le16 vid_le) +{ + u32 crc = ~0; + u32 temp = 0; + unsigned char *data = (unsigned char *)&vid_le; + unsigned char data_byte = 0; + int i, bits; + + bits = get_bitmask_order(VLAN_VID_MASK); + for (i = 0; i < bits; i++) { + if ((i % 8) == 0) + data_byte = data[i / 8]; + + temp = ((crc & 1) ^ data_byte) & 1; + crc >>= 1; + data_byte >>= 1; + + if (temp) + crc ^= CRC32_POLY_LE; + } + + return crc; +} + +static int xgbe_update_vlan_hash_table(struct xgbe_prv_data *pdata) +{ + u32 crc; + u16 vid; + __le16 vid_le; + u16 vlan_hash_table = 0; + + /* Generate the VLAN Hash Table value */ + for_each_set_bit(vid, pdata->active_vlans, VLAN_N_VID) { + /* Get the CRC32 value of the VLAN ID */ + vid_le = cpu_to_le16(vid); + crc = bitrev32(~xgbe_vid_crc32_le(vid_le)) >> 28; + + vlan_hash_table |= (1 << crc); + } + + /* Set the VLAN Hash Table filtering register */ + XGMAC_IOWRITE_BITS(pdata, MAC_VLANHTR, VLHT, vlan_hash_table); + + return 0; +} + +static int xgbe_set_promiscuous_mode(struct xgbe_prv_data *pdata, + unsigned int enable) +{ + unsigned int val = enable ? 1 : 0; + + if (XGMAC_IOREAD_BITS(pdata, MAC_PFR, PR) == val) + return 0; + + netif_dbg(pdata, drv, pdata->netdev, "%s promiscuous mode\n", + enable ? "entering" : "leaving"); + XGMAC_IOWRITE_BITS(pdata, MAC_PFR, PR, val); + + /* Hardware will still perform VLAN filtering in promiscuous mode */ + if (enable) { + xgbe_disable_rx_vlan_filtering(pdata); + } else { + if (pdata->netdev->features & NETIF_F_HW_VLAN_CTAG_FILTER) + xgbe_enable_rx_vlan_filtering(pdata); + } + + return 0; +} + +static int xgbe_set_all_multicast_mode(struct xgbe_prv_data *pdata, + unsigned int enable) +{ + unsigned int val = enable ? 1 : 0; + + if (XGMAC_IOREAD_BITS(pdata, MAC_PFR, PM) == val) + return 0; + + netif_dbg(pdata, drv, pdata->netdev, "%s allmulti mode\n", + enable ? "entering" : "leaving"); + XGMAC_IOWRITE_BITS(pdata, MAC_PFR, PM, val); + + return 0; +} + +static void xgbe_set_mac_reg(struct xgbe_prv_data *pdata, + struct netdev_hw_addr *ha, unsigned int *mac_reg) +{ + unsigned int mac_addr_hi, mac_addr_lo; + u8 *mac_addr; + + mac_addr_lo = 0; + mac_addr_hi = 0; + + if (ha) { + mac_addr = (u8 *)&mac_addr_lo; + mac_addr[0] = ha->addr[0]; + mac_addr[1] = ha->addr[1]; + mac_addr[2] = ha->addr[2]; + mac_addr[3] = ha->addr[3]; + mac_addr = (u8 *)&mac_addr_hi; + mac_addr[0] = ha->addr[4]; + mac_addr[1] = ha->addr[5]; + + netif_dbg(pdata, drv, pdata->netdev, + "adding mac address %pM at %#x\n", + ha->addr, *mac_reg); + + XGMAC_SET_BITS(mac_addr_hi, MAC_MACA1HR, AE, 1); + } + + XGMAC_IOWRITE(pdata, *mac_reg, mac_addr_hi); + *mac_reg += MAC_MACA_INC; + XGMAC_IOWRITE(pdata, *mac_reg, mac_addr_lo); + *mac_reg += MAC_MACA_INC; +} + +static void xgbe_set_mac_addn_addrs(struct xgbe_prv_data *pdata) +{ + struct net_device *netdev = pdata->netdev; + struct netdev_hw_addr *ha; + unsigned int mac_reg; + unsigned int addn_macs; + + mac_reg = MAC_MACA1HR; + addn_macs = pdata->hw_feat.addn_mac; + + if (netdev_uc_count(netdev) > addn_macs) { + xgbe_set_promiscuous_mode(pdata, 1); + } else { + netdev_for_each_uc_addr(ha, netdev) { + xgbe_set_mac_reg(pdata, ha, &mac_reg); + addn_macs--; + } + + if (netdev_mc_count(netdev) > addn_macs) { + xgbe_set_all_multicast_mode(pdata, 1); + } else { + netdev_for_each_mc_addr(ha, netdev) { + xgbe_set_mac_reg(pdata, ha, &mac_reg); + addn_macs--; + } + } + } + + /* Clear remaining additional MAC address entries */ + while (addn_macs--) + xgbe_set_mac_reg(pdata, NULL, &mac_reg); +} + +static void xgbe_set_mac_hash_table(struct xgbe_prv_data *pdata) +{ + struct net_device *netdev = pdata->netdev; + struct netdev_hw_addr *ha; + unsigned int hash_reg; + unsigned int hash_table_shift, hash_table_count; + u32 hash_table[XGBE_MAC_HASH_TABLE_SIZE]; + u32 crc; + unsigned int i; + + hash_table_shift = 26 - (pdata->hw_feat.hash_table_size >> 7); + hash_table_count = pdata->hw_feat.hash_table_size / 32; + memset(hash_table, 0, sizeof(hash_table)); + + /* Build the MAC Hash Table register values */ + netdev_for_each_uc_addr(ha, netdev) { + crc = bitrev32(~crc32_le(~0, ha->addr, ETH_ALEN)); + crc >>= hash_table_shift; + hash_table[crc >> 5] |= (1 << (crc & 0x1f)); + } + + netdev_for_each_mc_addr(ha, netdev) { + crc = bitrev32(~crc32_le(~0, ha->addr, ETH_ALEN)); + crc >>= hash_table_shift; + hash_table[crc >> 5] |= (1 << (crc & 0x1f)); + } + + /* Set the MAC Hash Table registers */ + hash_reg = MAC_HTR0; + for (i = 0; i < hash_table_count; i++) { + XGMAC_IOWRITE(pdata, hash_reg, hash_table[i]); + hash_reg += MAC_HTR_INC; + } +} + +static int xgbe_add_mac_addresses(struct xgbe_prv_data *pdata) +{ + if (pdata->hw_feat.hash_table_size) + xgbe_set_mac_hash_table(pdata); + else + xgbe_set_mac_addn_addrs(pdata); + + return 0; +} + +static int xgbe_set_mac_address(struct xgbe_prv_data *pdata, const u8 *addr) +{ + unsigned int mac_addr_hi, mac_addr_lo; + + mac_addr_hi = (addr[5] << 8) | (addr[4] << 0); + mac_addr_lo = (addr[3] << 24) | (addr[2] << 16) | + (addr[1] << 8) | (addr[0] << 0); + + XGMAC_IOWRITE(pdata, MAC_MACA0HR, mac_addr_hi); + XGMAC_IOWRITE(pdata, MAC_MACA0LR, mac_addr_lo); + + return 0; +} + +static int xgbe_config_rx_mode(struct xgbe_prv_data *pdata) +{ + struct net_device *netdev = pdata->netdev; + unsigned int pr_mode, am_mode; + + pr_mode = ((netdev->flags & IFF_PROMISC) != 0); + am_mode = ((netdev->flags & IFF_ALLMULTI) != 0); + + xgbe_set_promiscuous_mode(pdata, pr_mode); + xgbe_set_all_multicast_mode(pdata, am_mode); + + xgbe_add_mac_addresses(pdata); + + return 0; +} + +static int xgbe_clr_gpio(struct xgbe_prv_data *pdata, unsigned int gpio) +{ + unsigned int reg; + + if (gpio > 15) + return -EINVAL; + + reg = XGMAC_IOREAD(pdata, MAC_GPIOSR); + + reg &= ~(1 << (gpio + 16)); + XGMAC_IOWRITE(pdata, MAC_GPIOSR, reg); + + return 0; +} + +static int xgbe_set_gpio(struct xgbe_prv_data *pdata, unsigned int gpio) +{ + unsigned int reg; + + if (gpio > 15) + return -EINVAL; + + reg = XGMAC_IOREAD(pdata, MAC_GPIOSR); + + reg |= (1 << (gpio + 16)); + XGMAC_IOWRITE(pdata, MAC_GPIOSR, reg); + + return 0; +} + +static int xgbe_read_mmd_regs_v2(struct xgbe_prv_data *pdata, int prtad, + int mmd_reg) +{ + unsigned long flags; + unsigned int mmd_address, index, offset; + int mmd_data; + + if (mmd_reg & MII_ADDR_C45) + mmd_address = mmd_reg & ~MII_ADDR_C45; + else + mmd_address = (pdata->mdio_mmd << 16) | (mmd_reg & 0xffff); + + /* The PCS registers are accessed using mmio. The underlying + * management interface uses indirect addressing to access the MMD + * register sets. This requires accessing of the PCS register in two + * phases, an address phase and a data phase. + * + * The mmio interface is based on 16-bit offsets and values. All + * register offsets must therefore be adjusted by left shifting the + * offset 1 bit and reading 16 bits of data. + */ + mmd_address <<= 1; + index = mmd_address & ~pdata->xpcs_window_mask; + offset = pdata->xpcs_window + (mmd_address & pdata->xpcs_window_mask); + + spin_lock_irqsave(&pdata->xpcs_lock, flags); + XPCS32_IOWRITE(pdata, pdata->xpcs_window_sel_reg, index); + mmd_data = XPCS16_IOREAD(pdata, offset); + spin_unlock_irqrestore(&pdata->xpcs_lock, flags); + + return mmd_data; +} + +static void xgbe_write_mmd_regs_v2(struct xgbe_prv_data *pdata, int prtad, + int mmd_reg, int mmd_data) +{ + unsigned long flags; + unsigned int mmd_address, index, offset; + + if (mmd_reg & MII_ADDR_C45) + mmd_address = mmd_reg & ~MII_ADDR_C45; + else + mmd_address = (pdata->mdio_mmd << 16) | (mmd_reg & 0xffff); + + /* The PCS registers are accessed using mmio. The underlying + * management interface uses indirect addressing to access the MMD + * register sets. This requires accessing of the PCS register in two + * phases, an address phase and a data phase. + * + * The mmio interface is based on 16-bit offsets and values. All + * register offsets must therefore be adjusted by left shifting the + * offset 1 bit and writing 16 bits of data. + */ + mmd_address <<= 1; + index = mmd_address & ~pdata->xpcs_window_mask; + offset = pdata->xpcs_window + (mmd_address & pdata->xpcs_window_mask); + + spin_lock_irqsave(&pdata->xpcs_lock, flags); + XPCS32_IOWRITE(pdata, pdata->xpcs_window_sel_reg, index); + XPCS16_IOWRITE(pdata, offset, mmd_data); + spin_unlock_irqrestore(&pdata->xpcs_lock, flags); +} + +static int xgbe_read_mmd_regs_v1(struct xgbe_prv_data *pdata, int prtad, + int mmd_reg) +{ + unsigned long flags; + unsigned int mmd_address; + int mmd_data; + + if (mmd_reg & MII_ADDR_C45) + mmd_address = mmd_reg & ~MII_ADDR_C45; + else + mmd_address = (pdata->mdio_mmd << 16) | (mmd_reg & 0xffff); + + /* The PCS registers are accessed using mmio. The underlying APB3 + * management interface uses indirect addressing to access the MMD + * register sets. This requires accessing of the PCS register in two + * phases, an address phase and a data phase. + * + * The mmio interface is based on 32-bit offsets and values. All + * register offsets must therefore be adjusted by left shifting the + * offset 2 bits and reading 32 bits of data. + */ + spin_lock_irqsave(&pdata->xpcs_lock, flags); + XPCS32_IOWRITE(pdata, PCS_V1_WINDOW_SELECT, mmd_address >> 8); + mmd_data = XPCS32_IOREAD(pdata, (mmd_address & 0xff) << 2); + spin_unlock_irqrestore(&pdata->xpcs_lock, flags); + + return mmd_data; +} + +static void xgbe_write_mmd_regs_v1(struct xgbe_prv_data *pdata, int prtad, + int mmd_reg, int mmd_data) +{ + unsigned int mmd_address; + unsigned long flags; + + if (mmd_reg & MII_ADDR_C45) + mmd_address = mmd_reg & ~MII_ADDR_C45; + else + mmd_address = (pdata->mdio_mmd << 16) | (mmd_reg & 0xffff); + + /* The PCS registers are accessed using mmio. The underlying APB3 + * management interface uses indirect addressing to access the MMD + * register sets. This requires accessing of the PCS register in two + * phases, an address phase and a data phase. + * + * The mmio interface is based on 32-bit offsets and values. All + * register offsets must therefore be adjusted by left shifting the + * offset 2 bits and writing 32 bits of data. + */ + spin_lock_irqsave(&pdata->xpcs_lock, flags); + XPCS32_IOWRITE(pdata, PCS_V1_WINDOW_SELECT, mmd_address >> 8); + XPCS32_IOWRITE(pdata, (mmd_address & 0xff) << 2, mmd_data); + spin_unlock_irqrestore(&pdata->xpcs_lock, flags); +} + +static int xgbe_read_mmd_regs(struct xgbe_prv_data *pdata, int prtad, + int mmd_reg) +{ + switch (pdata->vdata->xpcs_access) { + case XGBE_XPCS_ACCESS_V1: + return xgbe_read_mmd_regs_v1(pdata, prtad, mmd_reg); + + case XGBE_XPCS_ACCESS_V2: + default: + return xgbe_read_mmd_regs_v2(pdata, prtad, mmd_reg); + } +} + +static void xgbe_write_mmd_regs(struct xgbe_prv_data *pdata, int prtad, + int mmd_reg, int mmd_data) +{ + switch (pdata->vdata->xpcs_access) { + case XGBE_XPCS_ACCESS_V1: + return xgbe_write_mmd_regs_v1(pdata, prtad, mmd_reg, mmd_data); + + case XGBE_XPCS_ACCESS_V2: + default: + return xgbe_write_mmd_regs_v2(pdata, prtad, mmd_reg, mmd_data); + } +} + +static unsigned int xgbe_create_mdio_sca(int port, int reg) +{ + unsigned int mdio_sca, da; + + da = (reg & MII_ADDR_C45) ? reg >> 16 : 0; + + mdio_sca = 0; + XGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, RA, reg); + XGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, PA, port); + XGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, DA, da); + + return mdio_sca; +} + +static int xgbe_write_ext_mii_regs(struct xgbe_prv_data *pdata, int addr, + int reg, u16 val) +{ + unsigned int mdio_sca, mdio_sccd; + + reinit_completion(&pdata->mdio_complete); + + mdio_sca = xgbe_create_mdio_sca(addr, reg); + XGMAC_IOWRITE(pdata, MAC_MDIOSCAR, mdio_sca); + + mdio_sccd = 0; + XGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, DATA, val); + XGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, CMD, 1); + XGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, BUSY, 1); + XGMAC_IOWRITE(pdata, MAC_MDIOSCCDR, mdio_sccd); + + if (!wait_for_completion_timeout(&pdata->mdio_complete, HZ)) { + netdev_err(pdata->netdev, "mdio write operation timed out\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int xgbe_read_ext_mii_regs(struct xgbe_prv_data *pdata, int addr, + int reg) +{ + unsigned int mdio_sca, mdio_sccd; + + reinit_completion(&pdata->mdio_complete); + + mdio_sca = xgbe_create_mdio_sca(addr, reg); + XGMAC_IOWRITE(pdata, MAC_MDIOSCAR, mdio_sca); + + mdio_sccd = 0; + XGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, CMD, 3); + XGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, BUSY, 1); + XGMAC_IOWRITE(pdata, MAC_MDIOSCCDR, mdio_sccd); + + if (!wait_for_completion_timeout(&pdata->mdio_complete, HZ)) { + netdev_err(pdata->netdev, "mdio read operation timed out\n"); + return -ETIMEDOUT; + } + + return XGMAC_IOREAD_BITS(pdata, MAC_MDIOSCCDR, DATA); +} + +static int xgbe_set_ext_mii_mode(struct xgbe_prv_data *pdata, unsigned int port, + enum xgbe_mdio_mode mode) +{ + unsigned int reg_val = XGMAC_IOREAD(pdata, MAC_MDIOCL22R); + + switch (mode) { + case XGBE_MDIO_MODE_CL22: + if (port > XGMAC_MAX_C22_PORT) + return -EINVAL; + reg_val |= (1 << port); + break; + case XGBE_MDIO_MODE_CL45: + break; + default: + return -EINVAL; + } + + XGMAC_IOWRITE(pdata, MAC_MDIOCL22R, reg_val); + + return 0; +} + +static int xgbe_tx_complete(struct xgbe_ring_desc *rdesc) +{ + return !XGMAC_GET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, OWN); +} + +static int xgbe_disable_rx_csum(struct xgbe_prv_data *pdata) +{ + XGMAC_IOWRITE_BITS(pdata, MAC_RCR, IPC, 0); + + return 0; +} + +static int xgbe_enable_rx_csum(struct xgbe_prv_data *pdata) +{ + XGMAC_IOWRITE_BITS(pdata, MAC_RCR, IPC, 1); + + return 0; +} + +static void xgbe_tx_desc_reset(struct xgbe_ring_data *rdata) +{ + struct xgbe_ring_desc *rdesc = rdata->rdesc; + + /* Reset the Tx descriptor + * Set buffer 1 (lo) address to zero + * Set buffer 1 (hi) address to zero + * Reset all other control bits (IC, TTSE, B2L & B1L) + * Reset all other control bits (OWN, CTXT, FD, LD, CPC, CIC, etc) + */ + rdesc->desc0 = 0; + rdesc->desc1 = 0; + rdesc->desc2 = 0; + rdesc->desc3 = 0; + + /* Make sure ownership is written to the descriptor */ + dma_wmb(); +} + +static void xgbe_tx_desc_init(struct xgbe_channel *channel) +{ + struct xgbe_ring *ring = channel->tx_ring; + struct xgbe_ring_data *rdata; + int i; + int start_index = ring->cur; + + DBGPR("-->tx_desc_init\n"); + + /* Initialze all descriptors */ + for (i = 0; i < ring->rdesc_count; i++) { + rdata = XGBE_GET_DESC_DATA(ring, i); + + /* Initialize Tx descriptor */ + xgbe_tx_desc_reset(rdata); + } + + /* Update the total number of Tx descriptors */ + XGMAC_DMA_IOWRITE(channel, DMA_CH_TDRLR, ring->rdesc_count - 1); + + /* Update the starting address of descriptor ring */ + rdata = XGBE_GET_DESC_DATA(ring, start_index); + XGMAC_DMA_IOWRITE(channel, DMA_CH_TDLR_HI, + upper_32_bits(rdata->rdesc_dma)); + XGMAC_DMA_IOWRITE(channel, DMA_CH_TDLR_LO, + lower_32_bits(rdata->rdesc_dma)); + + DBGPR("<--tx_desc_init\n"); +} + +static void xgbe_rx_desc_reset(struct xgbe_prv_data *pdata, + struct xgbe_ring_data *rdata, unsigned int index) +{ + struct xgbe_ring_desc *rdesc = rdata->rdesc; + unsigned int rx_usecs = pdata->rx_usecs; + unsigned int rx_frames = pdata->rx_frames; + unsigned int inte; + dma_addr_t hdr_dma, buf_dma; + + if (!rx_usecs && !rx_frames) { + /* No coalescing, interrupt for every descriptor */ + inte = 1; + } else { + /* Set interrupt based on Rx frame coalescing setting */ + if (rx_frames && !((index + 1) % rx_frames)) + inte = 1; + else + inte = 0; + } + + /* Reset the Rx descriptor + * Set buffer 1 (lo) address to header dma address (lo) + * Set buffer 1 (hi) address to header dma address (hi) + * Set buffer 2 (lo) address to buffer dma address (lo) + * Set buffer 2 (hi) address to buffer dma address (hi) and + * set control bits OWN and INTE + */ + hdr_dma = rdata->rx.hdr.dma_base + rdata->rx.hdr.dma_off; + buf_dma = rdata->rx.buf.dma_base + rdata->rx.buf.dma_off; + rdesc->desc0 = cpu_to_le32(lower_32_bits(hdr_dma)); + rdesc->desc1 = cpu_to_le32(upper_32_bits(hdr_dma)); + rdesc->desc2 = cpu_to_le32(lower_32_bits(buf_dma)); + rdesc->desc3 = cpu_to_le32(upper_32_bits(buf_dma)); + + XGMAC_SET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, INTE, inte); + + /* Since the Rx DMA engine is likely running, make sure everything + * is written to the descriptor(s) before setting the OWN bit + * for the descriptor + */ + dma_wmb(); + + XGMAC_SET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, OWN, 1); + + /* Make sure ownership is written to the descriptor */ + dma_wmb(); +} + +static void xgbe_rx_desc_init(struct xgbe_channel *channel) +{ + struct xgbe_prv_data *pdata = channel->pdata; + struct xgbe_ring *ring = channel->rx_ring; + struct xgbe_ring_data *rdata; + unsigned int start_index = ring->cur; + unsigned int i; + + DBGPR("-->rx_desc_init\n"); + + /* Initialize all descriptors */ + for (i = 0; i < ring->rdesc_count; i++) { + rdata = XGBE_GET_DESC_DATA(ring, i); + + /* Initialize Rx descriptor */ + xgbe_rx_desc_reset(pdata, rdata, i); + } + + /* Update the total number of Rx descriptors */ + XGMAC_DMA_IOWRITE(channel, DMA_CH_RDRLR, ring->rdesc_count - 1); + + /* Update the starting address of descriptor ring */ + rdata = XGBE_GET_DESC_DATA(ring, start_index); + XGMAC_DMA_IOWRITE(channel, DMA_CH_RDLR_HI, + upper_32_bits(rdata->rdesc_dma)); + XGMAC_DMA_IOWRITE(channel, DMA_CH_RDLR_LO, + lower_32_bits(rdata->rdesc_dma)); + + /* Update the Rx Descriptor Tail Pointer */ + rdata = XGBE_GET_DESC_DATA(ring, start_index + ring->rdesc_count - 1); + XGMAC_DMA_IOWRITE(channel, DMA_CH_RDTR_LO, + lower_32_bits(rdata->rdesc_dma)); + + DBGPR("<--rx_desc_init\n"); +} + +static void xgbe_update_tstamp_addend(struct xgbe_prv_data *pdata, + unsigned int addend) +{ + unsigned int count = 10000; + + /* Set the addend register value and tell the device */ + XGMAC_IOWRITE(pdata, MAC_TSAR, addend); + XGMAC_IOWRITE_BITS(pdata, MAC_TSCR, TSADDREG, 1); + + /* Wait for addend update to complete */ + while (--count && XGMAC_IOREAD_BITS(pdata, MAC_TSCR, TSADDREG)) + udelay(5); + + if (!count) + netdev_err(pdata->netdev, + "timed out updating timestamp addend register\n"); +} + +static void xgbe_set_tstamp_time(struct xgbe_prv_data *pdata, unsigned int sec, + unsigned int nsec) +{ + unsigned int count = 10000; + + /* Set the time values and tell the device */ + XGMAC_IOWRITE(pdata, MAC_STSUR, sec); + XGMAC_IOWRITE(pdata, MAC_STNUR, nsec); + XGMAC_IOWRITE_BITS(pdata, MAC_TSCR, TSINIT, 1); + + /* Wait for time update to complete */ + while (--count && XGMAC_IOREAD_BITS(pdata, MAC_TSCR, TSINIT)) + udelay(5); + + if (!count) + netdev_err(pdata->netdev, "timed out initializing timestamp\n"); +} + +static u64 xgbe_get_tstamp_time(struct xgbe_prv_data *pdata) +{ + u64 nsec; + + nsec = XGMAC_IOREAD(pdata, MAC_STSR); + nsec *= NSEC_PER_SEC; + nsec += XGMAC_IOREAD(pdata, MAC_STNR); + + return nsec; +} + +static u64 xgbe_get_tx_tstamp(struct xgbe_prv_data *pdata) +{ + unsigned int tx_snr, tx_ssr; + u64 nsec; + + if (pdata->vdata->tx_tstamp_workaround) { + tx_snr = XGMAC_IOREAD(pdata, MAC_TXSNR); + tx_ssr = XGMAC_IOREAD(pdata, MAC_TXSSR); + } else { + tx_ssr = XGMAC_IOREAD(pdata, MAC_TXSSR); + tx_snr = XGMAC_IOREAD(pdata, MAC_TXSNR); + } + + if (XGMAC_GET_BITS(tx_snr, MAC_TXSNR, TXTSSTSMIS)) + return 0; + + nsec = tx_ssr; + nsec *= NSEC_PER_SEC; + nsec += tx_snr; + + return nsec; +} + +static void xgbe_get_rx_tstamp(struct xgbe_packet_data *packet, + struct xgbe_ring_desc *rdesc) +{ + u64 nsec; + + if (XGMAC_GET_BITS_LE(rdesc->desc3, RX_CONTEXT_DESC3, TSA) && + !XGMAC_GET_BITS_LE(rdesc->desc3, RX_CONTEXT_DESC3, TSD)) { + nsec = le32_to_cpu(rdesc->desc1); + nsec <<= 32; + nsec |= le32_to_cpu(rdesc->desc0); + if (nsec != 0xffffffffffffffffULL) { + packet->rx_tstamp = nsec; + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + RX_TSTAMP, 1); + } + } +} + +static int xgbe_config_tstamp(struct xgbe_prv_data *pdata, + unsigned int mac_tscr) +{ + /* Set one nano-second accuracy */ + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSCTRLSSR, 1); + + /* Set fine timestamp update */ + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSCFUPDT, 1); + + /* Overwrite earlier timestamps */ + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TXTSSTSM, 1); + + XGMAC_IOWRITE(pdata, MAC_TSCR, mac_tscr); + + /* Exit if timestamping is not enabled */ + if (!XGMAC_GET_BITS(mac_tscr, MAC_TSCR, TSENA)) + return 0; + + /* Initialize time registers */ + XGMAC_IOWRITE_BITS(pdata, MAC_SSIR, SSINC, XGBE_TSTAMP_SSINC); + XGMAC_IOWRITE_BITS(pdata, MAC_SSIR, SNSINC, XGBE_TSTAMP_SNSINC); + xgbe_update_tstamp_addend(pdata, pdata->tstamp_addend); + xgbe_set_tstamp_time(pdata, 0, 0); + + /* Initialize the timecounter */ + timecounter_init(&pdata->tstamp_tc, &pdata->tstamp_cc, + ktime_to_ns(ktime_get_real())); + + return 0; +} + +static void xgbe_tx_start_xmit(struct xgbe_channel *channel, + struct xgbe_ring *ring) +{ + struct xgbe_prv_data *pdata = channel->pdata; + struct xgbe_ring_data *rdata; + + /* Make sure everything is written before the register write */ + wmb(); + + /* Issue a poll command to Tx DMA by writing address + * of next immediate free descriptor */ + rdata = XGBE_GET_DESC_DATA(ring, ring->cur); + XGMAC_DMA_IOWRITE(channel, DMA_CH_TDTR_LO, + lower_32_bits(rdata->rdesc_dma)); + + /* Start the Tx timer */ + if (pdata->tx_usecs && !channel->tx_timer_active) { + channel->tx_timer_active = 1; + mod_timer(&channel->tx_timer, + jiffies + usecs_to_jiffies(pdata->tx_usecs)); + } + + ring->tx.xmit_more = 0; +} + +static void xgbe_dev_xmit(struct xgbe_channel *channel) +{ + struct xgbe_prv_data *pdata = channel->pdata; + struct xgbe_ring *ring = channel->tx_ring; + struct xgbe_ring_data *rdata; + struct xgbe_ring_desc *rdesc; + struct xgbe_packet_data *packet = &ring->packet_data; + unsigned int tx_packets, tx_bytes; + unsigned int csum, tso, vlan, vxlan; + unsigned int tso_context, vlan_context; + unsigned int tx_set_ic; + int start_index = ring->cur; + int cur_index = ring->cur; + int i; + + DBGPR("-->xgbe_dev_xmit\n"); + + tx_packets = packet->tx_packets; + tx_bytes = packet->tx_bytes; + + csum = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, + CSUM_ENABLE); + tso = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, + TSO_ENABLE); + vlan = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, + VLAN_CTAG); + vxlan = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, + VXLAN); + + if (tso && (packet->mss != ring->tx.cur_mss)) + tso_context = 1; + else + tso_context = 0; + + if (vlan && (packet->vlan_ctag != ring->tx.cur_vlan_ctag)) + vlan_context = 1; + else + vlan_context = 0; + + /* Determine if an interrupt should be generated for this Tx: + * Interrupt: + * - Tx frame count exceeds the frame count setting + * - Addition of Tx frame count to the frame count since the + * last interrupt was set exceeds the frame count setting + * No interrupt: + * - No frame count setting specified (ethtool -C ethX tx-frames 0) + * - Addition of Tx frame count to the frame count since the + * last interrupt was set does not exceed the frame count setting + */ + ring->coalesce_count += tx_packets; + if (!pdata->tx_frames) + tx_set_ic = 0; + else if (tx_packets > pdata->tx_frames) + tx_set_ic = 1; + else if ((ring->coalesce_count % pdata->tx_frames) < tx_packets) + tx_set_ic = 1; + else + tx_set_ic = 0; + + rdata = XGBE_GET_DESC_DATA(ring, cur_index); + rdesc = rdata->rdesc; + + /* Create a context descriptor if this is a TSO packet */ + if (tso_context || vlan_context) { + if (tso_context) { + netif_dbg(pdata, tx_queued, pdata->netdev, + "TSO context descriptor, mss=%u\n", + packet->mss); + + /* Set the MSS size */ + XGMAC_SET_BITS_LE(rdesc->desc2, TX_CONTEXT_DESC2, + MSS, packet->mss); + + /* Mark it as a CONTEXT descriptor */ + XGMAC_SET_BITS_LE(rdesc->desc3, TX_CONTEXT_DESC3, + CTXT, 1); + + /* Indicate this descriptor contains the MSS */ + XGMAC_SET_BITS_LE(rdesc->desc3, TX_CONTEXT_DESC3, + TCMSSV, 1); + + ring->tx.cur_mss = packet->mss; + } + + if (vlan_context) { + netif_dbg(pdata, tx_queued, pdata->netdev, + "VLAN context descriptor, ctag=%u\n", + packet->vlan_ctag); + + /* Mark it as a CONTEXT descriptor */ + XGMAC_SET_BITS_LE(rdesc->desc3, TX_CONTEXT_DESC3, + CTXT, 1); + + /* Set the VLAN tag */ + XGMAC_SET_BITS_LE(rdesc->desc3, TX_CONTEXT_DESC3, + VT, packet->vlan_ctag); + + /* Indicate this descriptor contains the VLAN tag */ + XGMAC_SET_BITS_LE(rdesc->desc3, TX_CONTEXT_DESC3, + VLTV, 1); + + ring->tx.cur_vlan_ctag = packet->vlan_ctag; + } + + cur_index++; + rdata = XGBE_GET_DESC_DATA(ring, cur_index); + rdesc = rdata->rdesc; + } + + /* Update buffer address (for TSO this is the header) */ + rdesc->desc0 = cpu_to_le32(lower_32_bits(rdata->skb_dma)); + rdesc->desc1 = cpu_to_le32(upper_32_bits(rdata->skb_dma)); + + /* Update the buffer length */ + XGMAC_SET_BITS_LE(rdesc->desc2, TX_NORMAL_DESC2, HL_B1L, + rdata->skb_dma_len); + + /* VLAN tag insertion check */ + if (vlan) + XGMAC_SET_BITS_LE(rdesc->desc2, TX_NORMAL_DESC2, VTIR, + TX_NORMAL_DESC2_VLAN_INSERT); + + /* Timestamp enablement check */ + if (XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, PTP)) + XGMAC_SET_BITS_LE(rdesc->desc2, TX_NORMAL_DESC2, TTSE, 1); + + /* Mark it as First Descriptor */ + XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, FD, 1); + + /* Mark it as a NORMAL descriptor */ + XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, CTXT, 0); + + /* Set OWN bit if not the first descriptor */ + if (cur_index != start_index) + XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, OWN, 1); + + if (tso) { + /* Enable TSO */ + XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, TSE, 1); + XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, TCPPL, + packet->tcp_payload_len); + XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, TCPHDRLEN, + packet->tcp_header_len / 4); + + pdata->ext_stats.tx_tso_packets += tx_packets; + } else { + /* Enable CRC and Pad Insertion */ + XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, CPC, 0); + + /* Enable HW CSUM */ + if (csum) + XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, + CIC, 0x3); + + /* Set the total length to be transmitted */ + XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, FL, + packet->length); + } + + if (vxlan) { + XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, VNP, + TX_NORMAL_DESC3_VXLAN_PACKET); + + pdata->ext_stats.tx_vxlan_packets += packet->tx_packets; + } + + for (i = cur_index - start_index + 1; i < packet->rdesc_count; i++) { + cur_index++; + rdata = XGBE_GET_DESC_DATA(ring, cur_index); + rdesc = rdata->rdesc; + + /* Update buffer address */ + rdesc->desc0 = cpu_to_le32(lower_32_bits(rdata->skb_dma)); + rdesc->desc1 = cpu_to_le32(upper_32_bits(rdata->skb_dma)); + + /* Update the buffer length */ + XGMAC_SET_BITS_LE(rdesc->desc2, TX_NORMAL_DESC2, HL_B1L, + rdata->skb_dma_len); + + /* Set OWN bit */ + XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, OWN, 1); + + /* Mark it as NORMAL descriptor */ + XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, CTXT, 0); + + /* Enable HW CSUM */ + if (csum) + XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, + CIC, 0x3); + } + + /* Set LAST bit for the last descriptor */ + XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, LD, 1); + + /* Set IC bit based on Tx coalescing settings */ + if (tx_set_ic) + XGMAC_SET_BITS_LE(rdesc->desc2, TX_NORMAL_DESC2, IC, 1); + + /* Save the Tx info to report back during cleanup */ + rdata->tx.packets = tx_packets; + rdata->tx.bytes = tx_bytes; + + pdata->ext_stats.txq_packets[channel->queue_index] += tx_packets; + pdata->ext_stats.txq_bytes[channel->queue_index] += tx_bytes; + + /* In case the Tx DMA engine is running, make sure everything + * is written to the descriptor(s) before setting the OWN bit + * for the first descriptor + */ + dma_wmb(); + + /* Set OWN bit for the first descriptor */ + rdata = XGBE_GET_DESC_DATA(ring, start_index); + rdesc = rdata->rdesc; + XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, OWN, 1); + + if (netif_msg_tx_queued(pdata)) + xgbe_dump_tx_desc(pdata, ring, start_index, + packet->rdesc_count, 1); + + /* Make sure ownership is written to the descriptor */ + smp_wmb(); + + ring->cur = cur_index + 1; + if (!netdev_xmit_more() || + netif_xmit_stopped(netdev_get_tx_queue(pdata->netdev, + channel->queue_index))) + xgbe_tx_start_xmit(channel, ring); + else + ring->tx.xmit_more = 1; + + DBGPR(" %s: descriptors %u to %u written\n", + channel->name, start_index & (ring->rdesc_count - 1), + (ring->cur - 1) & (ring->rdesc_count - 1)); + + DBGPR("<--xgbe_dev_xmit\n"); +} + +static int xgbe_dev_read(struct xgbe_channel *channel) +{ + struct xgbe_prv_data *pdata = channel->pdata; + struct xgbe_ring *ring = channel->rx_ring; + struct xgbe_ring_data *rdata; + struct xgbe_ring_desc *rdesc; + struct xgbe_packet_data *packet = &ring->packet_data; + struct net_device *netdev = pdata->netdev; + unsigned int err, etlt, l34t; + + DBGPR("-->xgbe_dev_read: cur = %d\n", ring->cur); + + rdata = XGBE_GET_DESC_DATA(ring, ring->cur); + rdesc = rdata->rdesc; + + /* Check for data availability */ + if (XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, OWN)) + return 1; + + /* Make sure descriptor fields are read after reading the OWN bit */ + dma_rmb(); + + if (netif_msg_rx_status(pdata)) + xgbe_dump_rx_desc(pdata, ring, ring->cur); + + if (XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, CTXT)) { + /* Timestamp Context Descriptor */ + xgbe_get_rx_tstamp(packet, rdesc); + + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + CONTEXT, 1); + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + CONTEXT_NEXT, 0); + return 0; + } + + /* Normal Descriptor, be sure Context Descriptor bit is off */ + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, CONTEXT, 0); + + /* Indicate if a Context Descriptor is next */ + if (XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, CDA)) + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + CONTEXT_NEXT, 1); + + /* Get the header length */ + if (XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, FD)) { + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + FIRST, 1); + rdata->rx.hdr_len = XGMAC_GET_BITS_LE(rdesc->desc2, + RX_NORMAL_DESC2, HL); + if (rdata->rx.hdr_len) + pdata->ext_stats.rx_split_header_packets++; + } else { + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + FIRST, 0); + } + + /* Get the RSS hash */ + if (XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, RSV)) { + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + RSS_HASH, 1); + + packet->rss_hash = le32_to_cpu(rdesc->desc1); + + l34t = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, L34T); + switch (l34t) { + case RX_DESC3_L34T_IPV4_TCP: + case RX_DESC3_L34T_IPV4_UDP: + case RX_DESC3_L34T_IPV6_TCP: + case RX_DESC3_L34T_IPV6_UDP: + packet->rss_hash_type = PKT_HASH_TYPE_L4; + break; + default: + packet->rss_hash_type = PKT_HASH_TYPE_L3; + } + } + + /* Not all the data has been transferred for this packet */ + if (!XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, LD)) + return 0; + + /* This is the last of the data for this packet */ + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + LAST, 1); + + /* Get the packet length */ + rdata->rx.len = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, PL); + + /* Set checksum done indicator as appropriate */ + if (netdev->features & NETIF_F_RXCSUM) { + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + CSUM_DONE, 1); + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + TNPCSUM_DONE, 1); + } + + /* Set the tunneled packet indicator */ + if (XGMAC_GET_BITS_LE(rdesc->desc2, RX_NORMAL_DESC2, TNP)) { + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + TNP, 1); + pdata->ext_stats.rx_vxlan_packets++; + + l34t = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, L34T); + switch (l34t) { + case RX_DESC3_L34T_IPV4_UNKNOWN: + case RX_DESC3_L34T_IPV6_UNKNOWN: + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + TNPCSUM_DONE, 0); + break; + } + } + + /* Check for errors (only valid in last descriptor) */ + err = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, ES); + etlt = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, ETLT); + netif_dbg(pdata, rx_status, netdev, "err=%u, etlt=%#x\n", err, etlt); + + if (!err || !etlt) { + /* No error if err is 0 or etlt is 0 */ + if ((etlt == 0x09) && + (netdev->features & NETIF_F_HW_VLAN_CTAG_RX)) { + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + VLAN_CTAG, 1); + packet->vlan_ctag = XGMAC_GET_BITS_LE(rdesc->desc0, + RX_NORMAL_DESC0, + OVT); + netif_dbg(pdata, rx_status, netdev, "vlan-ctag=%#06x\n", + packet->vlan_ctag); + } + } else { + unsigned int tnp = XGMAC_GET_BITS(packet->attributes, + RX_PACKET_ATTRIBUTES, TNP); + + if ((etlt == 0x05) || (etlt == 0x06)) { + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + CSUM_DONE, 0); + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + TNPCSUM_DONE, 0); + pdata->ext_stats.rx_csum_errors++; + } else if (tnp && ((etlt == 0x09) || (etlt == 0x0a))) { + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + CSUM_DONE, 0); + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + TNPCSUM_DONE, 0); + pdata->ext_stats.rx_vxlan_csum_errors++; + } else { + XGMAC_SET_BITS(packet->errors, RX_PACKET_ERRORS, + FRAME, 1); + } + } + + pdata->ext_stats.rxq_packets[channel->queue_index]++; + pdata->ext_stats.rxq_bytes[channel->queue_index] += rdata->rx.len; + + DBGPR("<--xgbe_dev_read: %s - descriptor=%u (cur=%d)\n", channel->name, + ring->cur & (ring->rdesc_count - 1), ring->cur); + + return 0; +} + +static int xgbe_is_context_desc(struct xgbe_ring_desc *rdesc) +{ + /* Rx and Tx share CTXT bit, so check TDES3.CTXT bit */ + return XGMAC_GET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, CTXT); +} + +static int xgbe_is_last_desc(struct xgbe_ring_desc *rdesc) +{ + /* Rx and Tx share LD bit, so check TDES3.LD bit */ + return XGMAC_GET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, LD); +} + +static int xgbe_enable_int(struct xgbe_channel *channel, + enum xgbe_int int_id) +{ + switch (int_id) { + case XGMAC_INT_DMA_CH_SR_TI: + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, TIE, 1); + break; + case XGMAC_INT_DMA_CH_SR_TPS: + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, TXSE, 1); + break; + case XGMAC_INT_DMA_CH_SR_TBU: + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, TBUE, 1); + break; + case XGMAC_INT_DMA_CH_SR_RI: + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RIE, 1); + break; + case XGMAC_INT_DMA_CH_SR_RBU: + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RBUE, 1); + break; + case XGMAC_INT_DMA_CH_SR_RPS: + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RSE, 1); + break; + case XGMAC_INT_DMA_CH_SR_TI_RI: + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, TIE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RIE, 1); + break; + case XGMAC_INT_DMA_CH_SR_FBE: + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, FBEE, 1); + break; + case XGMAC_INT_DMA_ALL: + channel->curr_ier |= channel->saved_ier; + break; + default: + return -1; + } + + XGMAC_DMA_IOWRITE(channel, DMA_CH_IER, channel->curr_ier); + + return 0; +} + +static int xgbe_disable_int(struct xgbe_channel *channel, + enum xgbe_int int_id) +{ + switch (int_id) { + case XGMAC_INT_DMA_CH_SR_TI: + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, TIE, 0); + break; + case XGMAC_INT_DMA_CH_SR_TPS: + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, TXSE, 0); + break; + case XGMAC_INT_DMA_CH_SR_TBU: + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, TBUE, 0); + break; + case XGMAC_INT_DMA_CH_SR_RI: + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RIE, 0); + break; + case XGMAC_INT_DMA_CH_SR_RBU: + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RBUE, 0); + break; + case XGMAC_INT_DMA_CH_SR_RPS: + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RSE, 0); + break; + case XGMAC_INT_DMA_CH_SR_TI_RI: + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, TIE, 0); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RIE, 0); + break; + case XGMAC_INT_DMA_CH_SR_FBE: + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, FBEE, 0); + break; + case XGMAC_INT_DMA_ALL: + channel->saved_ier = channel->curr_ier; + channel->curr_ier = 0; + break; + default: + return -1; + } + + XGMAC_DMA_IOWRITE(channel, DMA_CH_IER, channel->curr_ier); + + return 0; +} + +static int __xgbe_exit(struct xgbe_prv_data *pdata) +{ + unsigned int count = 2000; + + DBGPR("-->xgbe_exit\n"); + + /* Issue a software reset */ + XGMAC_IOWRITE_BITS(pdata, DMA_MR, SWR, 1); + usleep_range(10, 15); + + /* Poll Until Poll Condition */ + while (--count && XGMAC_IOREAD_BITS(pdata, DMA_MR, SWR)) + usleep_range(500, 600); + + if (!count) + return -EBUSY; + + DBGPR("<--xgbe_exit\n"); + + return 0; +} + +static int xgbe_exit(struct xgbe_prv_data *pdata) +{ + int ret; + + /* To guard against possible incorrectly generated interrupts, + * issue the software reset twice. + */ + ret = __xgbe_exit(pdata); + if (ret) + return ret; + + return __xgbe_exit(pdata); +} + +static int xgbe_flush_tx_queues(struct xgbe_prv_data *pdata) +{ + unsigned int i, count; + + if (XGMAC_GET_BITS(pdata->hw_feat.version, MAC_VR, SNPSVER) < 0x21) + return 0; + + for (i = 0; i < pdata->tx_q_count; i++) + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_TQOMR, FTQ, 1); + + /* Poll Until Poll Condition */ + for (i = 0; i < pdata->tx_q_count; i++) { + count = 2000; + while (--count && XGMAC_MTL_IOREAD_BITS(pdata, i, + MTL_Q_TQOMR, FTQ)) + usleep_range(500, 600); + + if (!count) + return -EBUSY; + } + + return 0; +} + +static void xgbe_config_dma_bus(struct xgbe_prv_data *pdata) +{ + unsigned int sbmr; + + sbmr = XGMAC_IOREAD(pdata, DMA_SBMR); + + /* Set enhanced addressing mode */ + XGMAC_SET_BITS(sbmr, DMA_SBMR, EAME, 1); + + /* Set the System Bus mode */ + XGMAC_SET_BITS(sbmr, DMA_SBMR, UNDEF, 1); + XGMAC_SET_BITS(sbmr, DMA_SBMR, BLEN, pdata->blen >> 2); + XGMAC_SET_BITS(sbmr, DMA_SBMR, AAL, pdata->aal); + XGMAC_SET_BITS(sbmr, DMA_SBMR, RD_OSR_LMT, pdata->rd_osr_limit - 1); + XGMAC_SET_BITS(sbmr, DMA_SBMR, WR_OSR_LMT, pdata->wr_osr_limit - 1); + + XGMAC_IOWRITE(pdata, DMA_SBMR, sbmr); + + /* Set descriptor fetching threshold */ + if (pdata->vdata->tx_desc_prefetch) + XGMAC_IOWRITE_BITS(pdata, DMA_TXEDMACR, TDPS, + pdata->vdata->tx_desc_prefetch); + + if (pdata->vdata->rx_desc_prefetch) + XGMAC_IOWRITE_BITS(pdata, DMA_RXEDMACR, RDPS, + pdata->vdata->rx_desc_prefetch); +} + +static void xgbe_config_dma_cache(struct xgbe_prv_data *pdata) +{ + XGMAC_IOWRITE(pdata, DMA_AXIARCR, pdata->arcr); + XGMAC_IOWRITE(pdata, DMA_AXIAWCR, pdata->awcr); + if (pdata->awarcr) + XGMAC_IOWRITE(pdata, DMA_AXIAWARCR, pdata->awarcr); +} + +static void xgbe_config_mtl_mode(struct xgbe_prv_data *pdata) +{ + unsigned int i; + + /* Set Tx to weighted round robin scheduling algorithm */ + XGMAC_IOWRITE_BITS(pdata, MTL_OMR, ETSALG, MTL_ETSALG_WRR); + + /* Set Tx traffic classes to use WRR algorithm with equal weights */ + for (i = 0; i < pdata->hw_feat.tc_cnt; i++) { + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_TC_ETSCR, TSA, + MTL_TSA_ETS); + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_TC_QWR, QW, 1); + } + + /* Set Rx to strict priority algorithm */ + XGMAC_IOWRITE_BITS(pdata, MTL_OMR, RAA, MTL_RAA_SP); +} + +static void xgbe_queue_flow_control_threshold(struct xgbe_prv_data *pdata, + unsigned int queue, + unsigned int q_fifo_size) +{ + unsigned int frame_fifo_size; + unsigned int rfa, rfd; + + frame_fifo_size = XGMAC_FLOW_CONTROL_ALIGN(xgbe_get_max_frame(pdata)); + + if (pdata->pfcq[queue] && (q_fifo_size > pdata->pfc_rfa)) { + /* PFC is active for this queue */ + rfa = pdata->pfc_rfa; + rfd = rfa + frame_fifo_size; + if (rfd > XGMAC_FLOW_CONTROL_MAX) + rfd = XGMAC_FLOW_CONTROL_MAX; + if (rfa >= XGMAC_FLOW_CONTROL_MAX) + rfa = XGMAC_FLOW_CONTROL_MAX - XGMAC_FLOW_CONTROL_UNIT; + } else { + /* This path deals with just maximum frame sizes which are + * limited to a jumbo frame of 9,000 (plus headers, etc.) + * so we can never exceed the maximum allowable RFA/RFD + * values. + */ + if (q_fifo_size <= 2048) { + /* rx_rfd to zero to signal no flow control */ + pdata->rx_rfa[queue] = 0; + pdata->rx_rfd[queue] = 0; + return; + } + + if (q_fifo_size <= 4096) { + /* Between 2048 and 4096 */ + pdata->rx_rfa[queue] = 0; /* Full - 1024 bytes */ + pdata->rx_rfd[queue] = 1; /* Full - 1536 bytes */ + return; + } + + if (q_fifo_size <= frame_fifo_size) { + /* Between 4096 and max-frame */ + pdata->rx_rfa[queue] = 2; /* Full - 2048 bytes */ + pdata->rx_rfd[queue] = 5; /* Full - 3584 bytes */ + return; + } + + if (q_fifo_size <= (frame_fifo_size * 3)) { + /* Between max-frame and 3 max-frames, + * trigger if we get just over a frame of data and + * resume when we have just under half a frame left. + */ + rfa = q_fifo_size - frame_fifo_size; + rfd = rfa + (frame_fifo_size / 2); + } else { + /* Above 3 max-frames - trigger when just over + * 2 frames of space available + */ + rfa = frame_fifo_size * 2; + rfa += XGMAC_FLOW_CONTROL_UNIT; + rfd = rfa + frame_fifo_size; + } + } + + pdata->rx_rfa[queue] = XGMAC_FLOW_CONTROL_VALUE(rfa); + pdata->rx_rfd[queue] = XGMAC_FLOW_CONTROL_VALUE(rfd); +} + +static void xgbe_calculate_flow_control_threshold(struct xgbe_prv_data *pdata, + unsigned int *fifo) +{ + unsigned int q_fifo_size; + unsigned int i; + + for (i = 0; i < pdata->rx_q_count; i++) { + q_fifo_size = (fifo[i] + 1) * XGMAC_FIFO_UNIT; + + xgbe_queue_flow_control_threshold(pdata, i, q_fifo_size); + } +} + +static void xgbe_config_flow_control_threshold(struct xgbe_prv_data *pdata) +{ + unsigned int i; + + for (i = 0; i < pdata->rx_q_count; i++) { + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQFCR, RFA, + pdata->rx_rfa[i]); + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQFCR, RFD, + pdata->rx_rfd[i]); + } +} + +static unsigned int xgbe_get_tx_fifo_size(struct xgbe_prv_data *pdata) +{ + /* The configured value may not be the actual amount of fifo RAM */ + return min_t(unsigned int, pdata->tx_max_fifo_size, + pdata->hw_feat.tx_fifo_size); +} + +static unsigned int xgbe_get_rx_fifo_size(struct xgbe_prv_data *pdata) +{ + /* The configured value may not be the actual amount of fifo RAM */ + return min_t(unsigned int, pdata->rx_max_fifo_size, + pdata->hw_feat.rx_fifo_size); +} + +static void xgbe_calculate_equal_fifo(unsigned int fifo_size, + unsigned int queue_count, + unsigned int *fifo) +{ + unsigned int q_fifo_size; + unsigned int p_fifo; + unsigned int i; + + q_fifo_size = fifo_size / queue_count; + + /* Calculate the fifo setting by dividing the queue's fifo size + * by the fifo allocation increment (with 0 representing the + * base allocation increment so decrement the result by 1). + */ + p_fifo = q_fifo_size / XGMAC_FIFO_UNIT; + if (p_fifo) + p_fifo--; + + /* Distribute the fifo equally amongst the queues */ + for (i = 0; i < queue_count; i++) + fifo[i] = p_fifo; +} + +static unsigned int xgbe_set_nonprio_fifos(unsigned int fifo_size, + unsigned int queue_count, + unsigned int *fifo) +{ + unsigned int i; + + BUILD_BUG_ON_NOT_POWER_OF_2(XGMAC_FIFO_MIN_ALLOC); + + if (queue_count <= IEEE_8021QAZ_MAX_TCS) + return fifo_size; + + /* Rx queues 9 and up are for specialized packets, + * such as PTP or DCB control packets, etc. and + * don't require a large fifo + */ + for (i = IEEE_8021QAZ_MAX_TCS; i < queue_count; i++) { + fifo[i] = (XGMAC_FIFO_MIN_ALLOC / XGMAC_FIFO_UNIT) - 1; + fifo_size -= XGMAC_FIFO_MIN_ALLOC; + } + + return fifo_size; +} + +static unsigned int xgbe_get_pfc_delay(struct xgbe_prv_data *pdata) +{ + unsigned int delay; + + /* If a delay has been provided, use that */ + if (pdata->pfc->delay) + return pdata->pfc->delay / 8; + + /* Allow for two maximum size frames */ + delay = xgbe_get_max_frame(pdata); + delay += XGMAC_ETH_PREAMBLE; + delay *= 2; + + /* Allow for PFC frame */ + delay += XGMAC_PFC_DATA_LEN; + delay += ETH_HLEN + ETH_FCS_LEN; + delay += XGMAC_ETH_PREAMBLE; + + /* Allow for miscellaneous delays (LPI exit, cable, etc.) */ + delay += XGMAC_PFC_DELAYS; + + return delay; +} + +static unsigned int xgbe_get_pfc_queues(struct xgbe_prv_data *pdata) +{ + unsigned int count, prio_queues; + unsigned int i; + + if (!pdata->pfc->pfc_en) + return 0; + + count = 0; + prio_queues = XGMAC_PRIO_QUEUES(pdata->rx_q_count); + for (i = 0; i < prio_queues; i++) { + if (!xgbe_is_pfc_queue(pdata, i)) + continue; + + pdata->pfcq[i] = 1; + count++; + } + + return count; +} + +static void xgbe_calculate_dcb_fifo(struct xgbe_prv_data *pdata, + unsigned int fifo_size, + unsigned int *fifo) +{ + unsigned int q_fifo_size, rem_fifo, addn_fifo; + unsigned int prio_queues; + unsigned int pfc_count; + unsigned int i; + + q_fifo_size = XGMAC_FIFO_ALIGN(xgbe_get_max_frame(pdata)); + prio_queues = XGMAC_PRIO_QUEUES(pdata->rx_q_count); + pfc_count = xgbe_get_pfc_queues(pdata); + + if (!pfc_count || ((q_fifo_size * prio_queues) > fifo_size)) { + /* No traffic classes with PFC enabled or can't do lossless */ + xgbe_calculate_equal_fifo(fifo_size, prio_queues, fifo); + return; + } + + /* Calculate how much fifo we have to play with */ + rem_fifo = fifo_size - (q_fifo_size * prio_queues); + + /* Calculate how much more than base fifo PFC needs, which also + * becomes the threshold activation point (RFA) + */ + pdata->pfc_rfa = xgbe_get_pfc_delay(pdata); + pdata->pfc_rfa = XGMAC_FLOW_CONTROL_ALIGN(pdata->pfc_rfa); + + if (pdata->pfc_rfa > q_fifo_size) { + addn_fifo = pdata->pfc_rfa - q_fifo_size; + addn_fifo = XGMAC_FIFO_ALIGN(addn_fifo); + } else { + addn_fifo = 0; + } + + /* Calculate DCB fifo settings: + * - distribute remaining fifo between the VLAN priority + * queues based on traffic class PFC enablement and overall + * priority (0 is lowest priority, so start at highest) + */ + i = prio_queues; + while (i > 0) { + i--; + + fifo[i] = (q_fifo_size / XGMAC_FIFO_UNIT) - 1; + + if (!pdata->pfcq[i] || !addn_fifo) + continue; + + if (addn_fifo > rem_fifo) { + netdev_warn(pdata->netdev, + "RXq%u cannot set needed fifo size\n", i); + if (!rem_fifo) + continue; + + addn_fifo = rem_fifo; + } + + fifo[i] += (addn_fifo / XGMAC_FIFO_UNIT); + rem_fifo -= addn_fifo; + } + + if (rem_fifo) { + unsigned int inc_fifo = rem_fifo / prio_queues; + + /* Distribute remaining fifo across queues */ + for (i = 0; i < prio_queues; i++) + fifo[i] += (inc_fifo / XGMAC_FIFO_UNIT); + } +} + +static void xgbe_config_tx_fifo_size(struct xgbe_prv_data *pdata) +{ + unsigned int fifo_size; + unsigned int fifo[XGBE_MAX_QUEUES]; + unsigned int i; + + fifo_size = xgbe_get_tx_fifo_size(pdata); + + xgbe_calculate_equal_fifo(fifo_size, pdata->tx_q_count, fifo); + + for (i = 0; i < pdata->tx_q_count; i++) + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_TQOMR, TQS, fifo[i]); + + netif_info(pdata, drv, pdata->netdev, + "%d Tx hardware queues, %d byte fifo per queue\n", + pdata->tx_q_count, ((fifo[0] + 1) * XGMAC_FIFO_UNIT)); +} + +static void xgbe_config_rx_fifo_size(struct xgbe_prv_data *pdata) +{ + unsigned int fifo_size; + unsigned int fifo[XGBE_MAX_QUEUES]; + unsigned int prio_queues; + unsigned int i; + + /* Clear any DCB related fifo/queue information */ + memset(pdata->pfcq, 0, sizeof(pdata->pfcq)); + pdata->pfc_rfa = 0; + + fifo_size = xgbe_get_rx_fifo_size(pdata); + prio_queues = XGMAC_PRIO_QUEUES(pdata->rx_q_count); + + /* Assign a minimum fifo to the non-VLAN priority queues */ + fifo_size = xgbe_set_nonprio_fifos(fifo_size, pdata->rx_q_count, fifo); + + if (pdata->pfc && pdata->ets) + xgbe_calculate_dcb_fifo(pdata, fifo_size, fifo); + else + xgbe_calculate_equal_fifo(fifo_size, prio_queues, fifo); + + for (i = 0; i < pdata->rx_q_count; i++) + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQOMR, RQS, fifo[i]); + + xgbe_calculate_flow_control_threshold(pdata, fifo); + xgbe_config_flow_control_threshold(pdata); + + if (pdata->pfc && pdata->ets && pdata->pfc->pfc_en) { + netif_info(pdata, drv, pdata->netdev, + "%u Rx hardware queues\n", pdata->rx_q_count); + for (i = 0; i < pdata->rx_q_count; i++) + netif_info(pdata, drv, pdata->netdev, + "RxQ%u, %u byte fifo queue\n", i, + ((fifo[i] + 1) * XGMAC_FIFO_UNIT)); + } else { + netif_info(pdata, drv, pdata->netdev, + "%u Rx hardware queues, %u byte fifo per queue\n", + pdata->rx_q_count, + ((fifo[0] + 1) * XGMAC_FIFO_UNIT)); + } +} + +static void xgbe_config_queue_mapping(struct xgbe_prv_data *pdata) +{ + unsigned int qptc, qptc_extra, queue; + unsigned int prio_queues; + unsigned int ppq, ppq_extra, prio; + unsigned int mask; + unsigned int i, j, reg, reg_val; + + /* Map the MTL Tx Queues to Traffic Classes + * Note: Tx Queues >= Traffic Classes + */ + qptc = pdata->tx_q_count / pdata->hw_feat.tc_cnt; + qptc_extra = pdata->tx_q_count % pdata->hw_feat.tc_cnt; + + for (i = 0, queue = 0; i < pdata->hw_feat.tc_cnt; i++) { + for (j = 0; j < qptc; j++) { + netif_dbg(pdata, drv, pdata->netdev, + "TXq%u mapped to TC%u\n", queue, i); + XGMAC_MTL_IOWRITE_BITS(pdata, queue, MTL_Q_TQOMR, + Q2TCMAP, i); + pdata->q2tc_map[queue++] = i; + } + + if (i < qptc_extra) { + netif_dbg(pdata, drv, pdata->netdev, + "TXq%u mapped to TC%u\n", queue, i); + XGMAC_MTL_IOWRITE_BITS(pdata, queue, MTL_Q_TQOMR, + Q2TCMAP, i); + pdata->q2tc_map[queue++] = i; + } + } + + /* Map the 8 VLAN priority values to available MTL Rx queues */ + prio_queues = XGMAC_PRIO_QUEUES(pdata->rx_q_count); + ppq = IEEE_8021QAZ_MAX_TCS / prio_queues; + ppq_extra = IEEE_8021QAZ_MAX_TCS % prio_queues; + + reg = MAC_RQC2R; + reg_val = 0; + for (i = 0, prio = 0; i < prio_queues;) { + mask = 0; + for (j = 0; j < ppq; j++) { + netif_dbg(pdata, drv, pdata->netdev, + "PRIO%u mapped to RXq%u\n", prio, i); + mask |= (1 << prio); + pdata->prio2q_map[prio++] = i; + } + + if (i < ppq_extra) { + netif_dbg(pdata, drv, pdata->netdev, + "PRIO%u mapped to RXq%u\n", prio, i); + mask |= (1 << prio); + pdata->prio2q_map[prio++] = i; + } + + reg_val |= (mask << ((i++ % MAC_RQC2_Q_PER_REG) << 3)); + + if ((i % MAC_RQC2_Q_PER_REG) && (i != prio_queues)) + continue; + + XGMAC_IOWRITE(pdata, reg, reg_val); + reg += MAC_RQC2_INC; + reg_val = 0; + } + + /* Select dynamic mapping of MTL Rx queue to DMA Rx channel */ + reg = MTL_RQDCM0R; + reg_val = 0; + for (i = 0; i < pdata->rx_q_count;) { + reg_val |= (0x80 << ((i++ % MTL_RQDCM_Q_PER_REG) << 3)); + + if ((i % MTL_RQDCM_Q_PER_REG) && (i != pdata->rx_q_count)) + continue; + + XGMAC_IOWRITE(pdata, reg, reg_val); + + reg += MTL_RQDCM_INC; + reg_val = 0; + } +} + +static void xgbe_config_tc(struct xgbe_prv_data *pdata) +{ + unsigned int offset, queue, prio; + u8 i; + + netdev_reset_tc(pdata->netdev); + if (!pdata->num_tcs) + return; + + netdev_set_num_tc(pdata->netdev, pdata->num_tcs); + + for (i = 0, queue = 0, offset = 0; i < pdata->num_tcs; i++) { + while ((queue < pdata->tx_q_count) && + (pdata->q2tc_map[queue] == i)) + queue++; + + netif_dbg(pdata, drv, pdata->netdev, "TC%u using TXq%u-%u\n", + i, offset, queue - 1); + netdev_set_tc_queue(pdata->netdev, i, queue - offset, offset); + offset = queue; + } + + if (!pdata->ets) + return; + + for (prio = 0; prio < IEEE_8021QAZ_MAX_TCS; prio++) + netdev_set_prio_tc_map(pdata->netdev, prio, + pdata->ets->prio_tc[prio]); +} + +static void xgbe_config_dcb_tc(struct xgbe_prv_data *pdata) +{ + struct ieee_ets *ets = pdata->ets; + unsigned int total_weight, min_weight, weight; + unsigned int mask, reg, reg_val; + unsigned int i, prio; + + if (!ets) + return; + + /* Set Tx to deficit weighted round robin scheduling algorithm (when + * traffic class is using ETS algorithm) + */ + XGMAC_IOWRITE_BITS(pdata, MTL_OMR, ETSALG, MTL_ETSALG_DWRR); + + /* Set Traffic Class algorithms */ + total_weight = pdata->netdev->mtu * pdata->hw_feat.tc_cnt; + min_weight = total_weight / 100; + if (!min_weight) + min_weight = 1; + + for (i = 0; i < pdata->hw_feat.tc_cnt; i++) { + /* Map the priorities to the traffic class */ + mask = 0; + for (prio = 0; prio < IEEE_8021QAZ_MAX_TCS; prio++) { + if (ets->prio_tc[prio] == i) + mask |= (1 << prio); + } + mask &= 0xff; + + netif_dbg(pdata, drv, pdata->netdev, "TC%u PRIO mask=%#x\n", + i, mask); + reg = MTL_TCPM0R + (MTL_TCPM_INC * (i / MTL_TCPM_TC_PER_REG)); + reg_val = XGMAC_IOREAD(pdata, reg); + + reg_val &= ~(0xff << ((i % MTL_TCPM_TC_PER_REG) << 3)); + reg_val |= (mask << ((i % MTL_TCPM_TC_PER_REG) << 3)); + + XGMAC_IOWRITE(pdata, reg, reg_val); + + /* Set the traffic class algorithm */ + switch (ets->tc_tsa[i]) { + case IEEE_8021QAZ_TSA_STRICT: + netif_dbg(pdata, drv, pdata->netdev, + "TC%u using SP\n", i); + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_TC_ETSCR, TSA, + MTL_TSA_SP); + break; + case IEEE_8021QAZ_TSA_ETS: + weight = total_weight * ets->tc_tx_bw[i] / 100; + weight = clamp(weight, min_weight, total_weight); + + netif_dbg(pdata, drv, pdata->netdev, + "TC%u using DWRR (weight %u)\n", i, weight); + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_TC_ETSCR, TSA, + MTL_TSA_ETS); + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_TC_QWR, QW, + weight); + break; + } + } + + xgbe_config_tc(pdata); +} + +static void xgbe_config_dcb_pfc(struct xgbe_prv_data *pdata) +{ + if (!test_bit(XGBE_DOWN, &pdata->dev_state)) { + /* Just stop the Tx queues while Rx fifo is changed */ + netif_tx_stop_all_queues(pdata->netdev); + + /* Suspend Rx so that fifo's can be adjusted */ + pdata->hw_if.disable_rx(pdata); + } + + xgbe_config_rx_fifo_size(pdata); + xgbe_config_flow_control(pdata); + + if (!test_bit(XGBE_DOWN, &pdata->dev_state)) { + /* Resume Rx */ + pdata->hw_if.enable_rx(pdata); + + /* Resume Tx queues */ + netif_tx_start_all_queues(pdata->netdev); + } +} + +static void xgbe_config_mac_address(struct xgbe_prv_data *pdata) +{ + xgbe_set_mac_address(pdata, pdata->netdev->dev_addr); + + /* Filtering is done using perfect filtering and hash filtering */ + if (pdata->hw_feat.hash_table_size) { + XGMAC_IOWRITE_BITS(pdata, MAC_PFR, HPF, 1); + XGMAC_IOWRITE_BITS(pdata, MAC_PFR, HUC, 1); + XGMAC_IOWRITE_BITS(pdata, MAC_PFR, HMC, 1); + } +} + +static void xgbe_config_jumbo_enable(struct xgbe_prv_data *pdata) +{ + unsigned int val; + + val = (pdata->netdev->mtu > XGMAC_STD_PACKET_MTU) ? 1 : 0; + + XGMAC_IOWRITE_BITS(pdata, MAC_RCR, JE, val); +} + +static void xgbe_config_mac_speed(struct xgbe_prv_data *pdata) +{ + xgbe_set_speed(pdata, pdata->phy_speed); +} + +static void xgbe_config_checksum_offload(struct xgbe_prv_data *pdata) +{ + if (pdata->netdev->features & NETIF_F_RXCSUM) + xgbe_enable_rx_csum(pdata); + else + xgbe_disable_rx_csum(pdata); +} + +static void xgbe_config_vlan_support(struct xgbe_prv_data *pdata) +{ + /* Indicate that VLAN Tx CTAGs come from context descriptors */ + XGMAC_IOWRITE_BITS(pdata, MAC_VLANIR, CSVL, 0); + XGMAC_IOWRITE_BITS(pdata, MAC_VLANIR, VLTI, 1); + + /* Set the current VLAN Hash Table register value */ + xgbe_update_vlan_hash_table(pdata); + + if (pdata->netdev->features & NETIF_F_HW_VLAN_CTAG_FILTER) + xgbe_enable_rx_vlan_filtering(pdata); + else + xgbe_disable_rx_vlan_filtering(pdata); + + if (pdata->netdev->features & NETIF_F_HW_VLAN_CTAG_RX) + xgbe_enable_rx_vlan_stripping(pdata); + else + xgbe_disable_rx_vlan_stripping(pdata); +} + +static u64 xgbe_mmc_read(struct xgbe_prv_data *pdata, unsigned int reg_lo) +{ + bool read_hi; + u64 val; + + if (pdata->vdata->mmc_64bit) { + switch (reg_lo) { + /* These registers are always 32 bit */ + case MMC_RXRUNTERROR: + case MMC_RXJABBERERROR: + case MMC_RXUNDERSIZE_G: + case MMC_RXOVERSIZE_G: + case MMC_RXWATCHDOGERROR: + read_hi = false; + break; + + default: + read_hi = true; + } + } else { + switch (reg_lo) { + /* These registers are always 64 bit */ + case MMC_TXOCTETCOUNT_GB_LO: + case MMC_TXOCTETCOUNT_G_LO: + case MMC_RXOCTETCOUNT_GB_LO: + case MMC_RXOCTETCOUNT_G_LO: + read_hi = true; + break; + + default: + read_hi = false; + } + } + + val = XGMAC_IOREAD(pdata, reg_lo); + + if (read_hi) + val |= ((u64)XGMAC_IOREAD(pdata, reg_lo + 4) << 32); + + return val; +} + +static void xgbe_tx_mmc_int(struct xgbe_prv_data *pdata) +{ + struct xgbe_mmc_stats *stats = &pdata->mmc_stats; + unsigned int mmc_isr = XGMAC_IOREAD(pdata, MMC_TISR); + + if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXOCTETCOUNT_GB)) + stats->txoctetcount_gb += + xgbe_mmc_read(pdata, MMC_TXOCTETCOUNT_GB_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXFRAMECOUNT_GB)) + stats->txframecount_gb += + xgbe_mmc_read(pdata, MMC_TXFRAMECOUNT_GB_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXBROADCASTFRAMES_G)) + stats->txbroadcastframes_g += + xgbe_mmc_read(pdata, MMC_TXBROADCASTFRAMES_G_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXMULTICASTFRAMES_G)) + stats->txmulticastframes_g += + xgbe_mmc_read(pdata, MMC_TXMULTICASTFRAMES_G_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TX64OCTETS_GB)) + stats->tx64octets_gb += + xgbe_mmc_read(pdata, MMC_TX64OCTETS_GB_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TX65TO127OCTETS_GB)) + stats->tx65to127octets_gb += + xgbe_mmc_read(pdata, MMC_TX65TO127OCTETS_GB_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TX128TO255OCTETS_GB)) + stats->tx128to255octets_gb += + xgbe_mmc_read(pdata, MMC_TX128TO255OCTETS_GB_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TX256TO511OCTETS_GB)) + stats->tx256to511octets_gb += + xgbe_mmc_read(pdata, MMC_TX256TO511OCTETS_GB_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TX512TO1023OCTETS_GB)) + stats->tx512to1023octets_gb += + xgbe_mmc_read(pdata, MMC_TX512TO1023OCTETS_GB_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TX1024TOMAXOCTETS_GB)) + stats->tx1024tomaxoctets_gb += + xgbe_mmc_read(pdata, MMC_TX1024TOMAXOCTETS_GB_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXUNICASTFRAMES_GB)) + stats->txunicastframes_gb += + xgbe_mmc_read(pdata, MMC_TXUNICASTFRAMES_GB_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXMULTICASTFRAMES_GB)) + stats->txmulticastframes_gb += + xgbe_mmc_read(pdata, MMC_TXMULTICASTFRAMES_GB_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXBROADCASTFRAMES_GB)) + stats->txbroadcastframes_g += + xgbe_mmc_read(pdata, MMC_TXBROADCASTFRAMES_GB_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXUNDERFLOWERROR)) + stats->txunderflowerror += + xgbe_mmc_read(pdata, MMC_TXUNDERFLOWERROR_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXOCTETCOUNT_G)) + stats->txoctetcount_g += + xgbe_mmc_read(pdata, MMC_TXOCTETCOUNT_G_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXFRAMECOUNT_G)) + stats->txframecount_g += + xgbe_mmc_read(pdata, MMC_TXFRAMECOUNT_G_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXPAUSEFRAMES)) + stats->txpauseframes += + xgbe_mmc_read(pdata, MMC_TXPAUSEFRAMES_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXVLANFRAMES_G)) + stats->txvlanframes_g += + xgbe_mmc_read(pdata, MMC_TXVLANFRAMES_G_LO); +} + +static void xgbe_rx_mmc_int(struct xgbe_prv_data *pdata) +{ + struct xgbe_mmc_stats *stats = &pdata->mmc_stats; + unsigned int mmc_isr = XGMAC_IOREAD(pdata, MMC_RISR); + + if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXFRAMECOUNT_GB)) + stats->rxframecount_gb += + xgbe_mmc_read(pdata, MMC_RXFRAMECOUNT_GB_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXOCTETCOUNT_GB)) + stats->rxoctetcount_gb += + xgbe_mmc_read(pdata, MMC_RXOCTETCOUNT_GB_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXOCTETCOUNT_G)) + stats->rxoctetcount_g += + xgbe_mmc_read(pdata, MMC_RXOCTETCOUNT_G_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXBROADCASTFRAMES_G)) + stats->rxbroadcastframes_g += + xgbe_mmc_read(pdata, MMC_RXBROADCASTFRAMES_G_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXMULTICASTFRAMES_G)) + stats->rxmulticastframes_g += + xgbe_mmc_read(pdata, MMC_RXMULTICASTFRAMES_G_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXCRCERROR)) + stats->rxcrcerror += + xgbe_mmc_read(pdata, MMC_RXCRCERROR_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXRUNTERROR)) + stats->rxrunterror += + xgbe_mmc_read(pdata, MMC_RXRUNTERROR); + + if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXJABBERERROR)) + stats->rxjabbererror += + xgbe_mmc_read(pdata, MMC_RXJABBERERROR); + + if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXUNDERSIZE_G)) + stats->rxundersize_g += + xgbe_mmc_read(pdata, MMC_RXUNDERSIZE_G); + + if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXOVERSIZE_G)) + stats->rxoversize_g += + xgbe_mmc_read(pdata, MMC_RXOVERSIZE_G); + + if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RX64OCTETS_GB)) + stats->rx64octets_gb += + xgbe_mmc_read(pdata, MMC_RX64OCTETS_GB_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RX65TO127OCTETS_GB)) + stats->rx65to127octets_gb += + xgbe_mmc_read(pdata, MMC_RX65TO127OCTETS_GB_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RX128TO255OCTETS_GB)) + stats->rx128to255octets_gb += + xgbe_mmc_read(pdata, MMC_RX128TO255OCTETS_GB_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RX256TO511OCTETS_GB)) + stats->rx256to511octets_gb += + xgbe_mmc_read(pdata, MMC_RX256TO511OCTETS_GB_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RX512TO1023OCTETS_GB)) + stats->rx512to1023octets_gb += + xgbe_mmc_read(pdata, MMC_RX512TO1023OCTETS_GB_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RX1024TOMAXOCTETS_GB)) + stats->rx1024tomaxoctets_gb += + xgbe_mmc_read(pdata, MMC_RX1024TOMAXOCTETS_GB_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXUNICASTFRAMES_G)) + stats->rxunicastframes_g += + xgbe_mmc_read(pdata, MMC_RXUNICASTFRAMES_G_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXLENGTHERROR)) + stats->rxlengtherror += + xgbe_mmc_read(pdata, MMC_RXLENGTHERROR_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXOUTOFRANGETYPE)) + stats->rxoutofrangetype += + xgbe_mmc_read(pdata, MMC_RXOUTOFRANGETYPE_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXPAUSEFRAMES)) + stats->rxpauseframes += + xgbe_mmc_read(pdata, MMC_RXPAUSEFRAMES_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXFIFOOVERFLOW)) + stats->rxfifooverflow += + xgbe_mmc_read(pdata, MMC_RXFIFOOVERFLOW_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXVLANFRAMES_GB)) + stats->rxvlanframes_gb += + xgbe_mmc_read(pdata, MMC_RXVLANFRAMES_GB_LO); + + if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXWATCHDOGERROR)) + stats->rxwatchdogerror += + xgbe_mmc_read(pdata, MMC_RXWATCHDOGERROR); +} + +static void xgbe_read_mmc_stats(struct xgbe_prv_data *pdata) +{ + struct xgbe_mmc_stats *stats = &pdata->mmc_stats; + + /* Freeze counters */ + XGMAC_IOWRITE_BITS(pdata, MMC_CR, MCF, 1); + + stats->txoctetcount_gb += + xgbe_mmc_read(pdata, MMC_TXOCTETCOUNT_GB_LO); + + stats->txframecount_gb += + xgbe_mmc_read(pdata, MMC_TXFRAMECOUNT_GB_LO); + + stats->txbroadcastframes_g += + xgbe_mmc_read(pdata, MMC_TXBROADCASTFRAMES_G_LO); + + stats->txmulticastframes_g += + xgbe_mmc_read(pdata, MMC_TXMULTICASTFRAMES_G_LO); + + stats->tx64octets_gb += + xgbe_mmc_read(pdata, MMC_TX64OCTETS_GB_LO); + + stats->tx65to127octets_gb += + xgbe_mmc_read(pdata, MMC_TX65TO127OCTETS_GB_LO); + + stats->tx128to255octets_gb += + xgbe_mmc_read(pdata, MMC_TX128TO255OCTETS_GB_LO); + + stats->tx256to511octets_gb += + xgbe_mmc_read(pdata, MMC_TX256TO511OCTETS_GB_LO); + + stats->tx512to1023octets_gb += + xgbe_mmc_read(pdata, MMC_TX512TO1023OCTETS_GB_LO); + + stats->tx1024tomaxoctets_gb += + xgbe_mmc_read(pdata, MMC_TX1024TOMAXOCTETS_GB_LO); + + stats->txunicastframes_gb += + xgbe_mmc_read(pdata, MMC_TXUNICASTFRAMES_GB_LO); + + stats->txmulticastframes_gb += + xgbe_mmc_read(pdata, MMC_TXMULTICASTFRAMES_GB_LO); + + stats->txbroadcastframes_g += + xgbe_mmc_read(pdata, MMC_TXBROADCASTFRAMES_GB_LO); + + stats->txunderflowerror += + xgbe_mmc_read(pdata, MMC_TXUNDERFLOWERROR_LO); + + stats->txoctetcount_g += + xgbe_mmc_read(pdata, MMC_TXOCTETCOUNT_G_LO); + + stats->txframecount_g += + xgbe_mmc_read(pdata, MMC_TXFRAMECOUNT_G_LO); + + stats->txpauseframes += + xgbe_mmc_read(pdata, MMC_TXPAUSEFRAMES_LO); + + stats->txvlanframes_g += + xgbe_mmc_read(pdata, MMC_TXVLANFRAMES_G_LO); + + stats->rxframecount_gb += + xgbe_mmc_read(pdata, MMC_RXFRAMECOUNT_GB_LO); + + stats->rxoctetcount_gb += + xgbe_mmc_read(pdata, MMC_RXOCTETCOUNT_GB_LO); + + stats->rxoctetcount_g += + xgbe_mmc_read(pdata, MMC_RXOCTETCOUNT_G_LO); + + stats->rxbroadcastframes_g += + xgbe_mmc_read(pdata, MMC_RXBROADCASTFRAMES_G_LO); + + stats->rxmulticastframes_g += + xgbe_mmc_read(pdata, MMC_RXMULTICASTFRAMES_G_LO); + + stats->rxcrcerror += + xgbe_mmc_read(pdata, MMC_RXCRCERROR_LO); + + stats->rxrunterror += + xgbe_mmc_read(pdata, MMC_RXRUNTERROR); + + stats->rxjabbererror += + xgbe_mmc_read(pdata, MMC_RXJABBERERROR); + + stats->rxundersize_g += + xgbe_mmc_read(pdata, MMC_RXUNDERSIZE_G); + + stats->rxoversize_g += + xgbe_mmc_read(pdata, MMC_RXOVERSIZE_G); + + stats->rx64octets_gb += + xgbe_mmc_read(pdata, MMC_RX64OCTETS_GB_LO); + + stats->rx65to127octets_gb += + xgbe_mmc_read(pdata, MMC_RX65TO127OCTETS_GB_LO); + + stats->rx128to255octets_gb += + xgbe_mmc_read(pdata, MMC_RX128TO255OCTETS_GB_LO); + + stats->rx256to511octets_gb += + xgbe_mmc_read(pdata, MMC_RX256TO511OCTETS_GB_LO); + + stats->rx512to1023octets_gb += + xgbe_mmc_read(pdata, MMC_RX512TO1023OCTETS_GB_LO); + + stats->rx1024tomaxoctets_gb += + xgbe_mmc_read(pdata, MMC_RX1024TOMAXOCTETS_GB_LO); + + stats->rxunicastframes_g += + xgbe_mmc_read(pdata, MMC_RXUNICASTFRAMES_G_LO); + + stats->rxlengtherror += + xgbe_mmc_read(pdata, MMC_RXLENGTHERROR_LO); + + stats->rxoutofrangetype += + xgbe_mmc_read(pdata, MMC_RXOUTOFRANGETYPE_LO); + + stats->rxpauseframes += + xgbe_mmc_read(pdata, MMC_RXPAUSEFRAMES_LO); + + stats->rxfifooverflow += + xgbe_mmc_read(pdata, MMC_RXFIFOOVERFLOW_LO); + + stats->rxvlanframes_gb += + xgbe_mmc_read(pdata, MMC_RXVLANFRAMES_GB_LO); + + stats->rxwatchdogerror += + xgbe_mmc_read(pdata, MMC_RXWATCHDOGERROR); + + /* Un-freeze counters */ + XGMAC_IOWRITE_BITS(pdata, MMC_CR, MCF, 0); +} + +static void xgbe_config_mmc(struct xgbe_prv_data *pdata) +{ + /* Set counters to reset on read */ + XGMAC_IOWRITE_BITS(pdata, MMC_CR, ROR, 1); + + /* Reset the counters */ + XGMAC_IOWRITE_BITS(pdata, MMC_CR, CR, 1); +} + +static void xgbe_txq_prepare_tx_stop(struct xgbe_prv_data *pdata, + unsigned int queue) +{ + unsigned int tx_status; + unsigned long tx_timeout; + + /* The Tx engine cannot be stopped if it is actively processing + * packets. Wait for the Tx queue to empty the Tx fifo. Don't + * wait forever though... + */ + tx_timeout = jiffies + (XGBE_DMA_STOP_TIMEOUT * HZ); + while (time_before(jiffies, tx_timeout)) { + tx_status = XGMAC_MTL_IOREAD(pdata, queue, MTL_Q_TQDR); + if ((XGMAC_GET_BITS(tx_status, MTL_Q_TQDR, TRCSTS) != 1) && + (XGMAC_GET_BITS(tx_status, MTL_Q_TQDR, TXQSTS) == 0)) + break; + + usleep_range(500, 1000); + } + + if (!time_before(jiffies, tx_timeout)) + netdev_info(pdata->netdev, + "timed out waiting for Tx queue %u to empty\n", + queue); +} + +static void xgbe_prepare_tx_stop(struct xgbe_prv_data *pdata, + unsigned int queue) +{ + unsigned int tx_dsr, tx_pos, tx_qidx; + unsigned int tx_status; + unsigned long tx_timeout; + + if (XGMAC_GET_BITS(pdata->hw_feat.version, MAC_VR, SNPSVER) > 0x20) + return xgbe_txq_prepare_tx_stop(pdata, queue); + + /* Calculate the status register to read and the position within */ + if (queue < DMA_DSRX_FIRST_QUEUE) { + tx_dsr = DMA_DSR0; + tx_pos = (queue * DMA_DSR_Q_WIDTH) + DMA_DSR0_TPS_START; + } else { + tx_qidx = queue - DMA_DSRX_FIRST_QUEUE; + + tx_dsr = DMA_DSR1 + ((tx_qidx / DMA_DSRX_QPR) * DMA_DSRX_INC); + tx_pos = ((tx_qidx % DMA_DSRX_QPR) * DMA_DSR_Q_WIDTH) + + DMA_DSRX_TPS_START; + } + + /* The Tx engine cannot be stopped if it is actively processing + * descriptors. Wait for the Tx engine to enter the stopped or + * suspended state. Don't wait forever though... + */ + tx_timeout = jiffies + (XGBE_DMA_STOP_TIMEOUT * HZ); + while (time_before(jiffies, tx_timeout)) { + tx_status = XGMAC_IOREAD(pdata, tx_dsr); + tx_status = GET_BITS(tx_status, tx_pos, DMA_DSR_TPS_WIDTH); + if ((tx_status == DMA_TPS_STOPPED) || + (tx_status == DMA_TPS_SUSPENDED)) + break; + + usleep_range(500, 1000); + } + + if (!time_before(jiffies, tx_timeout)) + netdev_info(pdata->netdev, + "timed out waiting for Tx DMA channel %u to stop\n", + queue); +} + +static void xgbe_enable_tx(struct xgbe_prv_data *pdata) +{ + unsigned int i; + + /* Enable each Tx DMA channel */ + for (i = 0; i < pdata->channel_count; i++) { + if (!pdata->channel[i]->tx_ring) + break; + + XGMAC_DMA_IOWRITE_BITS(pdata->channel[i], DMA_CH_TCR, ST, 1); + } + + /* Enable each Tx queue */ + for (i = 0; i < pdata->tx_q_count; i++) + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_TQOMR, TXQEN, + MTL_Q_ENABLED); + + /* Enable MAC Tx */ + XGMAC_IOWRITE_BITS(pdata, MAC_TCR, TE, 1); +} + +static void xgbe_disable_tx(struct xgbe_prv_data *pdata) +{ + unsigned int i; + + /* Prepare for Tx DMA channel stop */ + for (i = 0; i < pdata->tx_q_count; i++) + xgbe_prepare_tx_stop(pdata, i); + + /* Disable MAC Tx */ + XGMAC_IOWRITE_BITS(pdata, MAC_TCR, TE, 0); + + /* Disable each Tx queue */ + for (i = 0; i < pdata->tx_q_count; i++) + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_TQOMR, TXQEN, 0); + + /* Disable each Tx DMA channel */ + for (i = 0; i < pdata->channel_count; i++) { + if (!pdata->channel[i]->tx_ring) + break; + + XGMAC_DMA_IOWRITE_BITS(pdata->channel[i], DMA_CH_TCR, ST, 0); + } +} + +static void xgbe_prepare_rx_stop(struct xgbe_prv_data *pdata, + unsigned int queue) +{ + unsigned int rx_status; + unsigned long rx_timeout; + + /* The Rx engine cannot be stopped if it is actively processing + * packets. Wait for the Rx queue to empty the Rx fifo. Don't + * wait forever though... + */ + rx_timeout = jiffies + (XGBE_DMA_STOP_TIMEOUT * HZ); + while (time_before(jiffies, rx_timeout)) { + rx_status = XGMAC_MTL_IOREAD(pdata, queue, MTL_Q_RQDR); + if ((XGMAC_GET_BITS(rx_status, MTL_Q_RQDR, PRXQ) == 0) && + (XGMAC_GET_BITS(rx_status, MTL_Q_RQDR, RXQSTS) == 0)) + break; + + usleep_range(500, 1000); + } + + if (!time_before(jiffies, rx_timeout)) + netdev_info(pdata->netdev, + "timed out waiting for Rx queue %u to empty\n", + queue); +} + +static void xgbe_enable_rx(struct xgbe_prv_data *pdata) +{ + unsigned int reg_val, i; + + /* Enable each Rx DMA channel */ + for (i = 0; i < pdata->channel_count; i++) { + if (!pdata->channel[i]->rx_ring) + break; + + XGMAC_DMA_IOWRITE_BITS(pdata->channel[i], DMA_CH_RCR, SR, 1); + } + + /* Enable each Rx queue */ + reg_val = 0; + for (i = 0; i < pdata->rx_q_count; i++) + reg_val |= (0x02 << (i << 1)); + XGMAC_IOWRITE(pdata, MAC_RQC0R, reg_val); + + /* Enable MAC Rx */ + XGMAC_IOWRITE_BITS(pdata, MAC_RCR, DCRCC, 1); + XGMAC_IOWRITE_BITS(pdata, MAC_RCR, CST, 1); + XGMAC_IOWRITE_BITS(pdata, MAC_RCR, ACS, 1); + XGMAC_IOWRITE_BITS(pdata, MAC_RCR, RE, 1); +} + +static void xgbe_disable_rx(struct xgbe_prv_data *pdata) +{ + unsigned int i; + + /* Disable MAC Rx */ + XGMAC_IOWRITE_BITS(pdata, MAC_RCR, DCRCC, 0); + XGMAC_IOWRITE_BITS(pdata, MAC_RCR, CST, 0); + XGMAC_IOWRITE_BITS(pdata, MAC_RCR, ACS, 0); + XGMAC_IOWRITE_BITS(pdata, MAC_RCR, RE, 0); + + /* Prepare for Rx DMA channel stop */ + for (i = 0; i < pdata->rx_q_count; i++) + xgbe_prepare_rx_stop(pdata, i); + + /* Disable each Rx queue */ + XGMAC_IOWRITE(pdata, MAC_RQC0R, 0); + + /* Disable each Rx DMA channel */ + for (i = 0; i < pdata->channel_count; i++) { + if (!pdata->channel[i]->rx_ring) + break; + + XGMAC_DMA_IOWRITE_BITS(pdata->channel[i], DMA_CH_RCR, SR, 0); + } +} + +static void xgbe_powerup_tx(struct xgbe_prv_data *pdata) +{ + unsigned int i; + + /* Enable each Tx DMA channel */ + for (i = 0; i < pdata->channel_count; i++) { + if (!pdata->channel[i]->tx_ring) + break; + + XGMAC_DMA_IOWRITE_BITS(pdata->channel[i], DMA_CH_TCR, ST, 1); + } + + /* Enable MAC Tx */ + XGMAC_IOWRITE_BITS(pdata, MAC_TCR, TE, 1); +} + +static void xgbe_powerdown_tx(struct xgbe_prv_data *pdata) +{ + unsigned int i; + + /* Prepare for Tx DMA channel stop */ + for (i = 0; i < pdata->tx_q_count; i++) + xgbe_prepare_tx_stop(pdata, i); + + /* Disable MAC Tx */ + XGMAC_IOWRITE_BITS(pdata, MAC_TCR, TE, 0); + + /* Disable each Tx DMA channel */ + for (i = 0; i < pdata->channel_count; i++) { + if (!pdata->channel[i]->tx_ring) + break; + + XGMAC_DMA_IOWRITE_BITS(pdata->channel[i], DMA_CH_TCR, ST, 0); + } +} + +static void xgbe_powerup_rx(struct xgbe_prv_data *pdata) +{ + unsigned int i; + + /* Enable each Rx DMA channel */ + for (i = 0; i < pdata->channel_count; i++) { + if (!pdata->channel[i]->rx_ring) + break; + + XGMAC_DMA_IOWRITE_BITS(pdata->channel[i], DMA_CH_RCR, SR, 1); + } +} + +static void xgbe_powerdown_rx(struct xgbe_prv_data *pdata) +{ + unsigned int i; + + /* Disable each Rx DMA channel */ + for (i = 0; i < pdata->channel_count; i++) { + if (!pdata->channel[i]->rx_ring) + break; + + XGMAC_DMA_IOWRITE_BITS(pdata->channel[i], DMA_CH_RCR, SR, 0); + } +} + +static int xgbe_init(struct xgbe_prv_data *pdata) +{ + struct xgbe_desc_if *desc_if = &pdata->desc_if; + int ret; + + DBGPR("-->xgbe_init\n"); + + /* Flush Tx queues */ + ret = xgbe_flush_tx_queues(pdata); + if (ret) { + netdev_err(pdata->netdev, "error flushing TX queues\n"); + return ret; + } + + /* + * Initialize DMA related features + */ + xgbe_config_dma_bus(pdata); + xgbe_config_dma_cache(pdata); + xgbe_config_osp_mode(pdata); + xgbe_config_pbl_val(pdata); + xgbe_config_rx_coalesce(pdata); + xgbe_config_tx_coalesce(pdata); + xgbe_config_rx_buffer_size(pdata); + xgbe_config_tso_mode(pdata); + xgbe_config_sph_mode(pdata); + xgbe_config_rss(pdata); + desc_if->wrapper_tx_desc_init(pdata); + desc_if->wrapper_rx_desc_init(pdata); + xgbe_enable_dma_interrupts(pdata); + + /* + * Initialize MTL related features + */ + xgbe_config_mtl_mode(pdata); + xgbe_config_queue_mapping(pdata); + xgbe_config_tsf_mode(pdata, pdata->tx_sf_mode); + xgbe_config_rsf_mode(pdata, pdata->rx_sf_mode); + xgbe_config_tx_threshold(pdata, pdata->tx_threshold); + xgbe_config_rx_threshold(pdata, pdata->rx_threshold); + xgbe_config_tx_fifo_size(pdata); + xgbe_config_rx_fifo_size(pdata); + /*TODO: Error Packet and undersized good Packet forwarding enable + (FEP and FUP) + */ + xgbe_config_dcb_tc(pdata); + xgbe_enable_mtl_interrupts(pdata); + + /* + * Initialize MAC related features + */ + xgbe_config_mac_address(pdata); + xgbe_config_rx_mode(pdata); + xgbe_config_jumbo_enable(pdata); + xgbe_config_flow_control(pdata); + xgbe_config_mac_speed(pdata); + xgbe_config_checksum_offload(pdata); + xgbe_config_vlan_support(pdata); + xgbe_config_mmc(pdata); + xgbe_enable_mac_interrupts(pdata); + + /* + * Initialize ECC related features + */ + xgbe_enable_ecc_interrupts(pdata); + + DBGPR("<--xgbe_init\n"); + + return 0; +} + +void xgbe_init_function_ptrs_dev(struct xgbe_hw_if *hw_if) +{ + DBGPR("-->xgbe_init_function_ptrs\n"); + + hw_if->tx_complete = xgbe_tx_complete; + + hw_if->set_mac_address = xgbe_set_mac_address; + hw_if->config_rx_mode = xgbe_config_rx_mode; + + hw_if->enable_rx_csum = xgbe_enable_rx_csum; + hw_if->disable_rx_csum = xgbe_disable_rx_csum; + + hw_if->enable_rx_vlan_stripping = xgbe_enable_rx_vlan_stripping; + hw_if->disable_rx_vlan_stripping = xgbe_disable_rx_vlan_stripping; + hw_if->enable_rx_vlan_filtering = xgbe_enable_rx_vlan_filtering; + hw_if->disable_rx_vlan_filtering = xgbe_disable_rx_vlan_filtering; + hw_if->update_vlan_hash_table = xgbe_update_vlan_hash_table; + + hw_if->read_mmd_regs = xgbe_read_mmd_regs; + hw_if->write_mmd_regs = xgbe_write_mmd_regs; + + hw_if->set_speed = xgbe_set_speed; + + hw_if->set_ext_mii_mode = xgbe_set_ext_mii_mode; + hw_if->read_ext_mii_regs = xgbe_read_ext_mii_regs; + hw_if->write_ext_mii_regs = xgbe_write_ext_mii_regs; + + hw_if->set_gpio = xgbe_set_gpio; + hw_if->clr_gpio = xgbe_clr_gpio; + + hw_if->enable_tx = xgbe_enable_tx; + hw_if->disable_tx = xgbe_disable_tx; + hw_if->enable_rx = xgbe_enable_rx; + hw_if->disable_rx = xgbe_disable_rx; + + hw_if->powerup_tx = xgbe_powerup_tx; + hw_if->powerdown_tx = xgbe_powerdown_tx; + hw_if->powerup_rx = xgbe_powerup_rx; + hw_if->powerdown_rx = xgbe_powerdown_rx; + + hw_if->dev_xmit = xgbe_dev_xmit; + hw_if->dev_read = xgbe_dev_read; + hw_if->enable_int = xgbe_enable_int; + hw_if->disable_int = xgbe_disable_int; + hw_if->init = xgbe_init; + hw_if->exit = xgbe_exit; + + /* Descriptor related Sequences have to be initialized here */ + hw_if->tx_desc_init = xgbe_tx_desc_init; + hw_if->rx_desc_init = xgbe_rx_desc_init; + hw_if->tx_desc_reset = xgbe_tx_desc_reset; + hw_if->rx_desc_reset = xgbe_rx_desc_reset; + hw_if->is_last_desc = xgbe_is_last_desc; + hw_if->is_context_desc = xgbe_is_context_desc; + hw_if->tx_start_xmit = xgbe_tx_start_xmit; + + /* For FLOW ctrl */ + hw_if->config_tx_flow_control = xgbe_config_tx_flow_control; + hw_if->config_rx_flow_control = xgbe_config_rx_flow_control; + + /* For RX coalescing */ + hw_if->config_rx_coalesce = xgbe_config_rx_coalesce; + hw_if->config_tx_coalesce = xgbe_config_tx_coalesce; + hw_if->usec_to_riwt = xgbe_usec_to_riwt; + hw_if->riwt_to_usec = xgbe_riwt_to_usec; + + /* For RX and TX threshold config */ + hw_if->config_rx_threshold = xgbe_config_rx_threshold; + hw_if->config_tx_threshold = xgbe_config_tx_threshold; + + /* For RX and TX Store and Forward Mode config */ + hw_if->config_rsf_mode = xgbe_config_rsf_mode; + hw_if->config_tsf_mode = xgbe_config_tsf_mode; + + /* For TX DMA Operating on Second Frame config */ + hw_if->config_osp_mode = xgbe_config_osp_mode; + + /* For MMC statistics support */ + hw_if->tx_mmc_int = xgbe_tx_mmc_int; + hw_if->rx_mmc_int = xgbe_rx_mmc_int; + hw_if->read_mmc_stats = xgbe_read_mmc_stats; + + /* For PTP config */ + hw_if->config_tstamp = xgbe_config_tstamp; + hw_if->update_tstamp_addend = xgbe_update_tstamp_addend; + hw_if->set_tstamp_time = xgbe_set_tstamp_time; + hw_if->get_tstamp_time = xgbe_get_tstamp_time; + hw_if->get_tx_tstamp = xgbe_get_tx_tstamp; + + /* For Data Center Bridging config */ + hw_if->config_tc = xgbe_config_tc; + hw_if->config_dcb_tc = xgbe_config_dcb_tc; + hw_if->config_dcb_pfc = xgbe_config_dcb_pfc; + + /* For Receive Side Scaling */ + hw_if->enable_rss = xgbe_enable_rss; + hw_if->disable_rss = xgbe_disable_rss; + hw_if->set_rss_hash_key = xgbe_set_rss_hash_key; + hw_if->set_rss_lookup_table = xgbe_set_rss_lookup_table; + + /* For ECC */ + hw_if->disable_ecc_ded = xgbe_disable_ecc_ded; + hw_if->disable_ecc_sec = xgbe_disable_ecc_sec; + + /* For VXLAN */ + hw_if->enable_vxlan = xgbe_enable_vxlan; + hw_if->disable_vxlan = xgbe_disable_vxlan; + hw_if->set_vxlan_id = xgbe_set_vxlan_id; + + DBGPR("<--xgbe_init_function_ptrs\n"); +} diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c new file mode 100644 index 000000000..6b73648b3 --- /dev/null +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -0,0 +1,2813 @@ +/* + * AMD 10Gb Ethernet driver + * + * This file is available to you under your choice of the following two + * licenses: + * + * License 1: GPLv2 + * + * Copyright (c) 2014-2016 Advanced Micro Devices, Inc. + * + * This file is free software; you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or (at + * your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * + * License 2: Modified BSD + * + * Copyright (c) 2014-2016 Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/tcp.h> +#include <linux/if_vlan.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/if_ether.h> +#include <linux/net_tstamp.h> +#include <linux/phy.h> +#include <net/vxlan.h> + +#include "xgbe.h" +#include "xgbe-common.h" + +static unsigned int ecc_sec_info_threshold = 10; +static unsigned int ecc_sec_warn_threshold = 10000; +static unsigned int ecc_sec_period = 600; +static unsigned int ecc_ded_threshold = 2; +static unsigned int ecc_ded_period = 600; + +#ifdef CONFIG_AMD_XGBE_HAVE_ECC +/* Only expose the ECC parameters if supported */ +module_param(ecc_sec_info_threshold, uint, 0644); +MODULE_PARM_DESC(ecc_sec_info_threshold, + " ECC corrected error informational threshold setting"); + +module_param(ecc_sec_warn_threshold, uint, 0644); +MODULE_PARM_DESC(ecc_sec_warn_threshold, + " ECC corrected error warning threshold setting"); + +module_param(ecc_sec_period, uint, 0644); +MODULE_PARM_DESC(ecc_sec_period, " ECC corrected error period (in seconds)"); + +module_param(ecc_ded_threshold, uint, 0644); +MODULE_PARM_DESC(ecc_ded_threshold, " ECC detected error threshold setting"); + +module_param(ecc_ded_period, uint, 0644); +MODULE_PARM_DESC(ecc_ded_period, " ECC detected error period (in seconds)"); +#endif + +static int xgbe_one_poll(struct napi_struct *, int); +static int xgbe_all_poll(struct napi_struct *, int); +static void xgbe_stop(struct xgbe_prv_data *); + +static void *xgbe_alloc_node(size_t size, int node) +{ + void *mem; + + mem = kzalloc_node(size, GFP_KERNEL, node); + if (!mem) + mem = kzalloc(size, GFP_KERNEL); + + return mem; +} + +static void xgbe_free_channels(struct xgbe_prv_data *pdata) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(pdata->channel); i++) { + if (!pdata->channel[i]) + continue; + + kfree(pdata->channel[i]->rx_ring); + kfree(pdata->channel[i]->tx_ring); + kfree(pdata->channel[i]); + + pdata->channel[i] = NULL; + } + + pdata->channel_count = 0; +} + +static int xgbe_alloc_channels(struct xgbe_prv_data *pdata) +{ + struct xgbe_channel *channel; + struct xgbe_ring *ring; + unsigned int count, i; + unsigned int cpu; + int node; + + count = max_t(unsigned int, pdata->tx_ring_count, pdata->rx_ring_count); + for (i = 0; i < count; i++) { + /* Attempt to use a CPU on the node the device is on */ + cpu = cpumask_local_spread(i, dev_to_node(pdata->dev)); + + /* Set the allocation node based on the returned CPU */ + node = cpu_to_node(cpu); + + channel = xgbe_alloc_node(sizeof(*channel), node); + if (!channel) + goto err_mem; + pdata->channel[i] = channel; + + snprintf(channel->name, sizeof(channel->name), "channel-%u", i); + channel->pdata = pdata; + channel->queue_index = i; + channel->dma_regs = pdata->xgmac_regs + DMA_CH_BASE + + (DMA_CH_INC * i); + channel->node = node; + cpumask_set_cpu(cpu, &channel->affinity_mask); + + if (pdata->per_channel_irq) + channel->dma_irq = pdata->channel_irq[i]; + + if (i < pdata->tx_ring_count) { + ring = xgbe_alloc_node(sizeof(*ring), node); + if (!ring) + goto err_mem; + + spin_lock_init(&ring->lock); + ring->node = node; + + channel->tx_ring = ring; + } + + if (i < pdata->rx_ring_count) { + ring = xgbe_alloc_node(sizeof(*ring), node); + if (!ring) + goto err_mem; + + spin_lock_init(&ring->lock); + ring->node = node; + + channel->rx_ring = ring; + } + + netif_dbg(pdata, drv, pdata->netdev, + "%s: cpu=%u, node=%d\n", channel->name, cpu, node); + + netif_dbg(pdata, drv, pdata->netdev, + "%s: dma_regs=%p, dma_irq=%d, tx=%p, rx=%p\n", + channel->name, channel->dma_regs, channel->dma_irq, + channel->tx_ring, channel->rx_ring); + } + + pdata->channel_count = count; + + return 0; + +err_mem: + xgbe_free_channels(pdata); + + return -ENOMEM; +} + +static inline unsigned int xgbe_tx_avail_desc(struct xgbe_ring *ring) +{ + return (ring->rdesc_count - (ring->cur - ring->dirty)); +} + +static inline unsigned int xgbe_rx_dirty_desc(struct xgbe_ring *ring) +{ + return (ring->cur - ring->dirty); +} + +static int xgbe_maybe_stop_tx_queue(struct xgbe_channel *channel, + struct xgbe_ring *ring, unsigned int count) +{ + struct xgbe_prv_data *pdata = channel->pdata; + + if (count > xgbe_tx_avail_desc(ring)) { + netif_info(pdata, drv, pdata->netdev, + "Tx queue stopped, not enough descriptors available\n"); + netif_stop_subqueue(pdata->netdev, channel->queue_index); + ring->tx.queue_stopped = 1; + + /* If we haven't notified the hardware because of xmit_more + * support, tell it now + */ + if (ring->tx.xmit_more) + pdata->hw_if.tx_start_xmit(channel, ring); + + return NETDEV_TX_BUSY; + } + + return 0; +} + +static int xgbe_calc_rx_buf_size(struct net_device *netdev, unsigned int mtu) +{ + unsigned int rx_buf_size; + + rx_buf_size = mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; + rx_buf_size = clamp_val(rx_buf_size, XGBE_RX_MIN_BUF_SIZE, PAGE_SIZE); + + rx_buf_size = (rx_buf_size + XGBE_RX_BUF_ALIGN - 1) & + ~(XGBE_RX_BUF_ALIGN - 1); + + return rx_buf_size; +} + +static void xgbe_enable_rx_tx_int(struct xgbe_prv_data *pdata, + struct xgbe_channel *channel) +{ + struct xgbe_hw_if *hw_if = &pdata->hw_if; + enum xgbe_int int_id; + + if (channel->tx_ring && channel->rx_ring) + int_id = XGMAC_INT_DMA_CH_SR_TI_RI; + else if (channel->tx_ring) + int_id = XGMAC_INT_DMA_CH_SR_TI; + else if (channel->rx_ring) + int_id = XGMAC_INT_DMA_CH_SR_RI; + else + return; + + hw_if->enable_int(channel, int_id); +} + +static void xgbe_enable_rx_tx_ints(struct xgbe_prv_data *pdata) +{ + unsigned int i; + + for (i = 0; i < pdata->channel_count; i++) + xgbe_enable_rx_tx_int(pdata, pdata->channel[i]); +} + +static void xgbe_disable_rx_tx_int(struct xgbe_prv_data *pdata, + struct xgbe_channel *channel) +{ + struct xgbe_hw_if *hw_if = &pdata->hw_if; + enum xgbe_int int_id; + + if (channel->tx_ring && channel->rx_ring) + int_id = XGMAC_INT_DMA_CH_SR_TI_RI; + else if (channel->tx_ring) + int_id = XGMAC_INT_DMA_CH_SR_TI; + else if (channel->rx_ring) + int_id = XGMAC_INT_DMA_CH_SR_RI; + else + return; + + hw_if->disable_int(channel, int_id); +} + +static void xgbe_disable_rx_tx_ints(struct xgbe_prv_data *pdata) +{ + unsigned int i; + + for (i = 0; i < pdata->channel_count; i++) + xgbe_disable_rx_tx_int(pdata, pdata->channel[i]); +} + +static bool xgbe_ecc_sec(struct xgbe_prv_data *pdata, unsigned long *period, + unsigned int *count, const char *area) +{ + if (time_before(jiffies, *period)) { + (*count)++; + } else { + *period = jiffies + (ecc_sec_period * HZ); + *count = 1; + } + + if (*count > ecc_sec_info_threshold) + dev_warn_once(pdata->dev, + "%s ECC corrected errors exceed informational threshold\n", + area); + + if (*count > ecc_sec_warn_threshold) { + dev_warn_once(pdata->dev, + "%s ECC corrected errors exceed warning threshold\n", + area); + return true; + } + + return false; +} + +static bool xgbe_ecc_ded(struct xgbe_prv_data *pdata, unsigned long *period, + unsigned int *count, const char *area) +{ + if (time_before(jiffies, *period)) { + (*count)++; + } else { + *period = jiffies + (ecc_ded_period * HZ); + *count = 1; + } + + if (*count > ecc_ded_threshold) { + netdev_alert(pdata->netdev, + "%s ECC detected errors exceed threshold\n", + area); + return true; + } + + return false; +} + +static void xgbe_ecc_isr_task(struct tasklet_struct *t) +{ + struct xgbe_prv_data *pdata = from_tasklet(pdata, t, tasklet_ecc); + unsigned int ecc_isr; + bool stop = false; + + /* Mask status with only the interrupts we care about */ + ecc_isr = XP_IOREAD(pdata, XP_ECC_ISR); + ecc_isr &= XP_IOREAD(pdata, XP_ECC_IER); + netif_dbg(pdata, intr, pdata->netdev, "ECC_ISR=%#010x\n", ecc_isr); + + if (XP_GET_BITS(ecc_isr, XP_ECC_ISR, TX_DED)) { + stop |= xgbe_ecc_ded(pdata, &pdata->tx_ded_period, + &pdata->tx_ded_count, "TX fifo"); + } + + if (XP_GET_BITS(ecc_isr, XP_ECC_ISR, RX_DED)) { + stop |= xgbe_ecc_ded(pdata, &pdata->rx_ded_period, + &pdata->rx_ded_count, "RX fifo"); + } + + if (XP_GET_BITS(ecc_isr, XP_ECC_ISR, DESC_DED)) { + stop |= xgbe_ecc_ded(pdata, &pdata->desc_ded_period, + &pdata->desc_ded_count, + "descriptor cache"); + } + + if (stop) { + pdata->hw_if.disable_ecc_ded(pdata); + schedule_work(&pdata->stopdev_work); + goto out; + } + + if (XP_GET_BITS(ecc_isr, XP_ECC_ISR, TX_SEC)) { + if (xgbe_ecc_sec(pdata, &pdata->tx_sec_period, + &pdata->tx_sec_count, "TX fifo")) + pdata->hw_if.disable_ecc_sec(pdata, XGBE_ECC_SEC_TX); + } + + if (XP_GET_BITS(ecc_isr, XP_ECC_ISR, RX_SEC)) + if (xgbe_ecc_sec(pdata, &pdata->rx_sec_period, + &pdata->rx_sec_count, "RX fifo")) + pdata->hw_if.disable_ecc_sec(pdata, XGBE_ECC_SEC_RX); + + if (XP_GET_BITS(ecc_isr, XP_ECC_ISR, DESC_SEC)) + if (xgbe_ecc_sec(pdata, &pdata->desc_sec_period, + &pdata->desc_sec_count, "descriptor cache")) + pdata->hw_if.disable_ecc_sec(pdata, XGBE_ECC_SEC_DESC); + +out: + /* Clear all ECC interrupts */ + XP_IOWRITE(pdata, XP_ECC_ISR, ecc_isr); + + /* Reissue interrupt if status is not clear */ + if (pdata->vdata->irq_reissue_support) + XP_IOWRITE(pdata, XP_INT_REISSUE_EN, 1 << 1); +} + +static irqreturn_t xgbe_ecc_isr(int irq, void *data) +{ + struct xgbe_prv_data *pdata = data; + + if (pdata->isr_as_tasklet) + tasklet_schedule(&pdata->tasklet_ecc); + else + xgbe_ecc_isr_task(&pdata->tasklet_ecc); + + return IRQ_HANDLED; +} + +static void xgbe_isr_task(struct tasklet_struct *t) +{ + struct xgbe_prv_data *pdata = from_tasklet(pdata, t, tasklet_dev); + struct xgbe_hw_if *hw_if = &pdata->hw_if; + struct xgbe_channel *channel; + unsigned int dma_isr, dma_ch_isr; + unsigned int mac_isr, mac_tssr, mac_mdioisr; + unsigned int i; + + /* The DMA interrupt status register also reports MAC and MTL + * interrupts. So for polling mode, we just need to check for + * this register to be non-zero + */ + dma_isr = XGMAC_IOREAD(pdata, DMA_ISR); + if (!dma_isr) + goto isr_done; + + netif_dbg(pdata, intr, pdata->netdev, "DMA_ISR=%#010x\n", dma_isr); + + for (i = 0; i < pdata->channel_count; i++) { + if (!(dma_isr & (1 << i))) + continue; + + channel = pdata->channel[i]; + + dma_ch_isr = XGMAC_DMA_IOREAD(channel, DMA_CH_SR); + netif_dbg(pdata, intr, pdata->netdev, "DMA_CH%u_ISR=%#010x\n", + i, dma_ch_isr); + + /* The TI or RI interrupt bits may still be set even if using + * per channel DMA interrupts. Check to be sure those are not + * enabled before using the private data napi structure. + */ + if (!pdata->per_channel_irq && + (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, TI) || + XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, RI))) { + if (napi_schedule_prep(&pdata->napi)) { + /* Disable Tx and Rx interrupts */ + xgbe_disable_rx_tx_ints(pdata); + + /* Turn on polling */ + __napi_schedule(&pdata->napi); + } + } else { + /* Don't clear Rx/Tx status if doing per channel DMA + * interrupts, these will be cleared by the ISR for + * per channel DMA interrupts. + */ + XGMAC_SET_BITS(dma_ch_isr, DMA_CH_SR, TI, 0); + XGMAC_SET_BITS(dma_ch_isr, DMA_CH_SR, RI, 0); + } + + if (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, RBU)) + pdata->ext_stats.rx_buffer_unavailable++; + + /* Restart the device on a Fatal Bus Error */ + if (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, FBE)) + schedule_work(&pdata->restart_work); + + /* Clear interrupt signals */ + XGMAC_DMA_IOWRITE(channel, DMA_CH_SR, dma_ch_isr); + } + + if (XGMAC_GET_BITS(dma_isr, DMA_ISR, MACIS)) { + mac_isr = XGMAC_IOREAD(pdata, MAC_ISR); + + netif_dbg(pdata, intr, pdata->netdev, "MAC_ISR=%#010x\n", + mac_isr); + + if (XGMAC_GET_BITS(mac_isr, MAC_ISR, MMCTXIS)) + hw_if->tx_mmc_int(pdata); + + if (XGMAC_GET_BITS(mac_isr, MAC_ISR, MMCRXIS)) + hw_if->rx_mmc_int(pdata); + + if (XGMAC_GET_BITS(mac_isr, MAC_ISR, TSIS)) { + mac_tssr = XGMAC_IOREAD(pdata, MAC_TSSR); + + netif_dbg(pdata, intr, pdata->netdev, + "MAC_TSSR=%#010x\n", mac_tssr); + + if (XGMAC_GET_BITS(mac_tssr, MAC_TSSR, TXTSC)) { + /* Read Tx Timestamp to clear interrupt */ + pdata->tx_tstamp = + hw_if->get_tx_tstamp(pdata); + queue_work(pdata->dev_workqueue, + &pdata->tx_tstamp_work); + } + } + + if (XGMAC_GET_BITS(mac_isr, MAC_ISR, SMI)) { + mac_mdioisr = XGMAC_IOREAD(pdata, MAC_MDIOISR); + + netif_dbg(pdata, intr, pdata->netdev, + "MAC_MDIOISR=%#010x\n", mac_mdioisr); + + if (XGMAC_GET_BITS(mac_mdioisr, MAC_MDIOISR, + SNGLCOMPINT)) + complete(&pdata->mdio_complete); + } + } + +isr_done: + /* If there is not a separate AN irq, handle it here */ + if (pdata->dev_irq == pdata->an_irq) + pdata->phy_if.an_isr(pdata); + + /* If there is not a separate ECC irq, handle it here */ + if (pdata->vdata->ecc_support && (pdata->dev_irq == pdata->ecc_irq)) + xgbe_ecc_isr_task(&pdata->tasklet_ecc); + + /* If there is not a separate I2C irq, handle it here */ + if (pdata->vdata->i2c_support && (pdata->dev_irq == pdata->i2c_irq)) + pdata->i2c_if.i2c_isr(pdata); + + /* Reissue interrupt if status is not clear */ + if (pdata->vdata->irq_reissue_support) { + unsigned int reissue_mask; + + reissue_mask = 1 << 0; + if (!pdata->per_channel_irq) + reissue_mask |= 0xffff << 4; + + XP_IOWRITE(pdata, XP_INT_REISSUE_EN, reissue_mask); + } +} + +static irqreturn_t xgbe_isr(int irq, void *data) +{ + struct xgbe_prv_data *pdata = data; + + if (pdata->isr_as_tasklet) + tasklet_schedule(&pdata->tasklet_dev); + else + xgbe_isr_task(&pdata->tasklet_dev); + + return IRQ_HANDLED; +} + +static irqreturn_t xgbe_dma_isr(int irq, void *data) +{ + struct xgbe_channel *channel = data; + struct xgbe_prv_data *pdata = channel->pdata; + unsigned int dma_status; + + /* Per channel DMA interrupts are enabled, so we use the per + * channel napi structure and not the private data napi structure + */ + if (napi_schedule_prep(&channel->napi)) { + /* Disable Tx and Rx interrupts */ + if (pdata->channel_irq_mode) + xgbe_disable_rx_tx_int(pdata, channel); + else + disable_irq_nosync(channel->dma_irq); + + /* Turn on polling */ + __napi_schedule_irqoff(&channel->napi); + } + + /* Clear Tx/Rx signals */ + dma_status = 0; + XGMAC_SET_BITS(dma_status, DMA_CH_SR, TI, 1); + XGMAC_SET_BITS(dma_status, DMA_CH_SR, RI, 1); + XGMAC_DMA_IOWRITE(channel, DMA_CH_SR, dma_status); + + return IRQ_HANDLED; +} + +static void xgbe_tx_timer(struct timer_list *t) +{ + struct xgbe_channel *channel = from_timer(channel, t, tx_timer); + struct xgbe_prv_data *pdata = channel->pdata; + struct napi_struct *napi; + + DBGPR("-->xgbe_tx_timer\n"); + + napi = (pdata->per_channel_irq) ? &channel->napi : &pdata->napi; + + if (napi_schedule_prep(napi)) { + /* Disable Tx and Rx interrupts */ + if (pdata->per_channel_irq) + if (pdata->channel_irq_mode) + xgbe_disable_rx_tx_int(pdata, channel); + else + disable_irq_nosync(channel->dma_irq); + else + xgbe_disable_rx_tx_ints(pdata); + + /* Turn on polling */ + __napi_schedule(napi); + } + + channel->tx_timer_active = 0; + + DBGPR("<--xgbe_tx_timer\n"); +} + +static void xgbe_service(struct work_struct *work) +{ + struct xgbe_prv_data *pdata = container_of(work, + struct xgbe_prv_data, + service_work); + + pdata->phy_if.phy_status(pdata); +} + +static void xgbe_service_timer(struct timer_list *t) +{ + struct xgbe_prv_data *pdata = from_timer(pdata, t, service_timer); + struct xgbe_channel *channel; + unsigned int i; + + queue_work(pdata->dev_workqueue, &pdata->service_work); + + mod_timer(&pdata->service_timer, jiffies + HZ); + + if (!pdata->tx_usecs) + return; + + for (i = 0; i < pdata->channel_count; i++) { + channel = pdata->channel[i]; + if (!channel->tx_ring || channel->tx_timer_active) + break; + channel->tx_timer_active = 1; + mod_timer(&channel->tx_timer, + jiffies + usecs_to_jiffies(pdata->tx_usecs)); + } +} + +static void xgbe_init_timers(struct xgbe_prv_data *pdata) +{ + struct xgbe_channel *channel; + unsigned int i; + + timer_setup(&pdata->service_timer, xgbe_service_timer, 0); + + for (i = 0; i < pdata->channel_count; i++) { + channel = pdata->channel[i]; + if (!channel->tx_ring) + break; + + timer_setup(&channel->tx_timer, xgbe_tx_timer, 0); + } +} + +static void xgbe_start_timers(struct xgbe_prv_data *pdata) +{ + mod_timer(&pdata->service_timer, jiffies + HZ); +} + +static void xgbe_stop_timers(struct xgbe_prv_data *pdata) +{ + struct xgbe_channel *channel; + unsigned int i; + + del_timer_sync(&pdata->service_timer); + + for (i = 0; i < pdata->channel_count; i++) { + channel = pdata->channel[i]; + if (!channel->tx_ring) + break; + + /* Deactivate the Tx timer */ + del_timer_sync(&channel->tx_timer); + channel->tx_timer_active = 0; + } +} + +void xgbe_get_all_hw_features(struct xgbe_prv_data *pdata) +{ + unsigned int mac_hfr0, mac_hfr1, mac_hfr2; + struct xgbe_hw_features *hw_feat = &pdata->hw_feat; + + mac_hfr0 = XGMAC_IOREAD(pdata, MAC_HWF0R); + mac_hfr1 = XGMAC_IOREAD(pdata, MAC_HWF1R); + mac_hfr2 = XGMAC_IOREAD(pdata, MAC_HWF2R); + + memset(hw_feat, 0, sizeof(*hw_feat)); + + hw_feat->version = XGMAC_IOREAD(pdata, MAC_VR); + + /* Hardware feature register 0 */ + hw_feat->gmii = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, GMIISEL); + hw_feat->vlhash = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, VLHASH); + hw_feat->sma = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, SMASEL); + hw_feat->rwk = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, RWKSEL); + hw_feat->mgk = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, MGKSEL); + hw_feat->mmc = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, MMCSEL); + hw_feat->aoe = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, ARPOFFSEL); + hw_feat->ts = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, TSSEL); + hw_feat->eee = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, EEESEL); + hw_feat->tx_coe = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, TXCOESEL); + hw_feat->rx_coe = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, RXCOESEL); + hw_feat->addn_mac = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, + ADDMACADRSEL); + hw_feat->ts_src = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, TSSTSSEL); + hw_feat->sa_vlan_ins = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, SAVLANINS); + hw_feat->vxn = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, VXN); + + /* Hardware feature register 1 */ + hw_feat->rx_fifo_size = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, + RXFIFOSIZE); + hw_feat->tx_fifo_size = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, + TXFIFOSIZE); + hw_feat->adv_ts_hi = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, ADVTHWORD); + hw_feat->dma_width = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, ADDR64); + hw_feat->dcb = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, DCBEN); + hw_feat->sph = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, SPHEN); + hw_feat->tso = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, TSOEN); + hw_feat->dma_debug = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, DBGMEMA); + hw_feat->rss = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, RSSEN); + hw_feat->tc_cnt = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, NUMTC); + hw_feat->hash_table_size = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, + HASHTBLSZ); + hw_feat->l3l4_filter_num = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, + L3L4FNUM); + + /* Hardware feature register 2 */ + hw_feat->rx_q_cnt = XGMAC_GET_BITS(mac_hfr2, MAC_HWF2R, RXQCNT); + hw_feat->tx_q_cnt = XGMAC_GET_BITS(mac_hfr2, MAC_HWF2R, TXQCNT); + hw_feat->rx_ch_cnt = XGMAC_GET_BITS(mac_hfr2, MAC_HWF2R, RXCHCNT); + hw_feat->tx_ch_cnt = XGMAC_GET_BITS(mac_hfr2, MAC_HWF2R, TXCHCNT); + hw_feat->pps_out_num = XGMAC_GET_BITS(mac_hfr2, MAC_HWF2R, PPSOUTNUM); + hw_feat->aux_snap_num = XGMAC_GET_BITS(mac_hfr2, MAC_HWF2R, AUXSNAPNUM); + + /* Translate the Hash Table size into actual number */ + switch (hw_feat->hash_table_size) { + case 0: + break; + case 1: + hw_feat->hash_table_size = 64; + break; + case 2: + hw_feat->hash_table_size = 128; + break; + case 3: + hw_feat->hash_table_size = 256; + break; + } + + /* Translate the address width setting into actual number */ + switch (hw_feat->dma_width) { + case 0: + hw_feat->dma_width = 32; + break; + case 1: + hw_feat->dma_width = 40; + break; + case 2: + hw_feat->dma_width = 48; + break; + default: + hw_feat->dma_width = 32; + } + + /* The Queue, Channel and TC counts are zero based so increment them + * to get the actual number + */ + hw_feat->rx_q_cnt++; + hw_feat->tx_q_cnt++; + hw_feat->rx_ch_cnt++; + hw_feat->tx_ch_cnt++; + hw_feat->tc_cnt++; + + /* Translate the fifo sizes into actual numbers */ + hw_feat->rx_fifo_size = 1 << (hw_feat->rx_fifo_size + 7); + hw_feat->tx_fifo_size = 1 << (hw_feat->tx_fifo_size + 7); + + if (netif_msg_probe(pdata)) { + dev_dbg(pdata->dev, "Hardware features:\n"); + + /* Hardware feature register 0 */ + dev_dbg(pdata->dev, " 1GbE support : %s\n", + hw_feat->gmii ? "yes" : "no"); + dev_dbg(pdata->dev, " VLAN hash filter : %s\n", + hw_feat->vlhash ? "yes" : "no"); + dev_dbg(pdata->dev, " MDIO interface : %s\n", + hw_feat->sma ? "yes" : "no"); + dev_dbg(pdata->dev, " Wake-up packet support : %s\n", + hw_feat->rwk ? "yes" : "no"); + dev_dbg(pdata->dev, " Magic packet support : %s\n", + hw_feat->mgk ? "yes" : "no"); + dev_dbg(pdata->dev, " Management counters : %s\n", + hw_feat->mmc ? "yes" : "no"); + dev_dbg(pdata->dev, " ARP offload : %s\n", + hw_feat->aoe ? "yes" : "no"); + dev_dbg(pdata->dev, " IEEE 1588-2008 Timestamp : %s\n", + hw_feat->ts ? "yes" : "no"); + dev_dbg(pdata->dev, " Energy Efficient Ethernet : %s\n", + hw_feat->eee ? "yes" : "no"); + dev_dbg(pdata->dev, " TX checksum offload : %s\n", + hw_feat->tx_coe ? "yes" : "no"); + dev_dbg(pdata->dev, " RX checksum offload : %s\n", + hw_feat->rx_coe ? "yes" : "no"); + dev_dbg(pdata->dev, " Additional MAC addresses : %u\n", + hw_feat->addn_mac); + dev_dbg(pdata->dev, " Timestamp source : %s\n", + (hw_feat->ts_src == 1) ? "internal" : + (hw_feat->ts_src == 2) ? "external" : + (hw_feat->ts_src == 3) ? "internal/external" : "n/a"); + dev_dbg(pdata->dev, " SA/VLAN insertion : %s\n", + hw_feat->sa_vlan_ins ? "yes" : "no"); + dev_dbg(pdata->dev, " VXLAN/NVGRE support : %s\n", + hw_feat->vxn ? "yes" : "no"); + + /* Hardware feature register 1 */ + dev_dbg(pdata->dev, " RX fifo size : %u\n", + hw_feat->rx_fifo_size); + dev_dbg(pdata->dev, " TX fifo size : %u\n", + hw_feat->tx_fifo_size); + dev_dbg(pdata->dev, " IEEE 1588 high word : %s\n", + hw_feat->adv_ts_hi ? "yes" : "no"); + dev_dbg(pdata->dev, " DMA width : %u\n", + hw_feat->dma_width); + dev_dbg(pdata->dev, " Data Center Bridging : %s\n", + hw_feat->dcb ? "yes" : "no"); + dev_dbg(pdata->dev, " Split header : %s\n", + hw_feat->sph ? "yes" : "no"); + dev_dbg(pdata->dev, " TCP Segmentation Offload : %s\n", + hw_feat->tso ? "yes" : "no"); + dev_dbg(pdata->dev, " Debug memory interface : %s\n", + hw_feat->dma_debug ? "yes" : "no"); + dev_dbg(pdata->dev, " Receive Side Scaling : %s\n", + hw_feat->rss ? "yes" : "no"); + dev_dbg(pdata->dev, " Traffic Class count : %u\n", + hw_feat->tc_cnt); + dev_dbg(pdata->dev, " Hash table size : %u\n", + hw_feat->hash_table_size); + dev_dbg(pdata->dev, " L3/L4 Filters : %u\n", + hw_feat->l3l4_filter_num); + + /* Hardware feature register 2 */ + dev_dbg(pdata->dev, " RX queue count : %u\n", + hw_feat->rx_q_cnt); + dev_dbg(pdata->dev, " TX queue count : %u\n", + hw_feat->tx_q_cnt); + dev_dbg(pdata->dev, " RX DMA channel count : %u\n", + hw_feat->rx_ch_cnt); + dev_dbg(pdata->dev, " TX DMA channel count : %u\n", + hw_feat->rx_ch_cnt); + dev_dbg(pdata->dev, " PPS outputs : %u\n", + hw_feat->pps_out_num); + dev_dbg(pdata->dev, " Auxiliary snapshot inputs : %u\n", + hw_feat->aux_snap_num); + } +} + +static int xgbe_vxlan_set_port(struct net_device *netdev, unsigned int table, + unsigned int entry, struct udp_tunnel_info *ti) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + + pdata->vxlan_port = be16_to_cpu(ti->port); + pdata->hw_if.enable_vxlan(pdata); + + return 0; +} + +static int xgbe_vxlan_unset_port(struct net_device *netdev, unsigned int table, + unsigned int entry, struct udp_tunnel_info *ti) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + + pdata->hw_if.disable_vxlan(pdata); + pdata->vxlan_port = 0; + + return 0; +} + +static const struct udp_tunnel_nic_info xgbe_udp_tunnels = { + .set_port = xgbe_vxlan_set_port, + .unset_port = xgbe_vxlan_unset_port, + .flags = UDP_TUNNEL_NIC_INFO_OPEN_ONLY, + .tables = { + { .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_VXLAN, }, + }, +}; + +const struct udp_tunnel_nic_info *xgbe_get_udp_tunnel_info(void) +{ + return &xgbe_udp_tunnels; +} + +static void xgbe_napi_enable(struct xgbe_prv_data *pdata, unsigned int add) +{ + struct xgbe_channel *channel; + unsigned int i; + + if (pdata->per_channel_irq) { + for (i = 0; i < pdata->channel_count; i++) { + channel = pdata->channel[i]; + if (add) + netif_napi_add(pdata->netdev, &channel->napi, + xgbe_one_poll); + + napi_enable(&channel->napi); + } + } else { + if (add) + netif_napi_add(pdata->netdev, &pdata->napi, + xgbe_all_poll); + + napi_enable(&pdata->napi); + } +} + +static void xgbe_napi_disable(struct xgbe_prv_data *pdata, unsigned int del) +{ + struct xgbe_channel *channel; + unsigned int i; + + if (pdata->per_channel_irq) { + for (i = 0; i < pdata->channel_count; i++) { + channel = pdata->channel[i]; + napi_disable(&channel->napi); + + if (del) + netif_napi_del(&channel->napi); + } + } else { + napi_disable(&pdata->napi); + + if (del) + netif_napi_del(&pdata->napi); + } +} + +static int xgbe_request_irqs(struct xgbe_prv_data *pdata) +{ + struct xgbe_channel *channel; + struct net_device *netdev = pdata->netdev; + unsigned int i; + int ret; + + tasklet_setup(&pdata->tasklet_dev, xgbe_isr_task); + tasklet_setup(&pdata->tasklet_ecc, xgbe_ecc_isr_task); + + ret = devm_request_irq(pdata->dev, pdata->dev_irq, xgbe_isr, 0, + netdev_name(netdev), pdata); + if (ret) { + netdev_alert(netdev, "error requesting irq %d\n", + pdata->dev_irq); + return ret; + } + + if (pdata->vdata->ecc_support && (pdata->dev_irq != pdata->ecc_irq)) { + ret = devm_request_irq(pdata->dev, pdata->ecc_irq, xgbe_ecc_isr, + 0, pdata->ecc_name, pdata); + if (ret) { + netdev_alert(netdev, "error requesting ecc irq %d\n", + pdata->ecc_irq); + goto err_dev_irq; + } + } + + if (!pdata->per_channel_irq) + return 0; + + for (i = 0; i < pdata->channel_count; i++) { + channel = pdata->channel[i]; + snprintf(channel->dma_irq_name, + sizeof(channel->dma_irq_name) - 1, + "%s-TxRx-%u", netdev_name(netdev), + channel->queue_index); + + ret = devm_request_irq(pdata->dev, channel->dma_irq, + xgbe_dma_isr, 0, + channel->dma_irq_name, channel); + if (ret) { + netdev_alert(netdev, "error requesting irq %d\n", + channel->dma_irq); + goto err_dma_irq; + } + + irq_set_affinity_hint(channel->dma_irq, + &channel->affinity_mask); + } + + return 0; + +err_dma_irq: + /* Using an unsigned int, 'i' will go to UINT_MAX and exit */ + for (i--; i < pdata->channel_count; i--) { + channel = pdata->channel[i]; + + irq_set_affinity_hint(channel->dma_irq, NULL); + devm_free_irq(pdata->dev, channel->dma_irq, channel); + } + + if (pdata->vdata->ecc_support && (pdata->dev_irq != pdata->ecc_irq)) + devm_free_irq(pdata->dev, pdata->ecc_irq, pdata); + +err_dev_irq: + devm_free_irq(pdata->dev, pdata->dev_irq, pdata); + + return ret; +} + +static void xgbe_free_irqs(struct xgbe_prv_data *pdata) +{ + struct xgbe_channel *channel; + unsigned int i; + + devm_free_irq(pdata->dev, pdata->dev_irq, pdata); + + tasklet_kill(&pdata->tasklet_dev); + tasklet_kill(&pdata->tasklet_ecc); + + if (pdata->vdata->ecc_support && (pdata->dev_irq != pdata->ecc_irq)) + devm_free_irq(pdata->dev, pdata->ecc_irq, pdata); + + if (!pdata->per_channel_irq) + return; + + for (i = 0; i < pdata->channel_count; i++) { + channel = pdata->channel[i]; + + irq_set_affinity_hint(channel->dma_irq, NULL); + devm_free_irq(pdata->dev, channel->dma_irq, channel); + } +} + +void xgbe_init_tx_coalesce(struct xgbe_prv_data *pdata) +{ + struct xgbe_hw_if *hw_if = &pdata->hw_if; + + DBGPR("-->xgbe_init_tx_coalesce\n"); + + pdata->tx_usecs = XGMAC_INIT_DMA_TX_USECS; + pdata->tx_frames = XGMAC_INIT_DMA_TX_FRAMES; + + hw_if->config_tx_coalesce(pdata); + + DBGPR("<--xgbe_init_tx_coalesce\n"); +} + +void xgbe_init_rx_coalesce(struct xgbe_prv_data *pdata) +{ + struct xgbe_hw_if *hw_if = &pdata->hw_if; + + DBGPR("-->xgbe_init_rx_coalesce\n"); + + pdata->rx_riwt = hw_if->usec_to_riwt(pdata, XGMAC_INIT_DMA_RX_USECS); + pdata->rx_usecs = XGMAC_INIT_DMA_RX_USECS; + pdata->rx_frames = XGMAC_INIT_DMA_RX_FRAMES; + + hw_if->config_rx_coalesce(pdata); + + DBGPR("<--xgbe_init_rx_coalesce\n"); +} + +static void xgbe_free_tx_data(struct xgbe_prv_data *pdata) +{ + struct xgbe_desc_if *desc_if = &pdata->desc_if; + struct xgbe_ring *ring; + struct xgbe_ring_data *rdata; + unsigned int i, j; + + DBGPR("-->xgbe_free_tx_data\n"); + + for (i = 0; i < pdata->channel_count; i++) { + ring = pdata->channel[i]->tx_ring; + if (!ring) + break; + + for (j = 0; j < ring->rdesc_count; j++) { + rdata = XGBE_GET_DESC_DATA(ring, j); + desc_if->unmap_rdata(pdata, rdata); + } + } + + DBGPR("<--xgbe_free_tx_data\n"); +} + +static void xgbe_free_rx_data(struct xgbe_prv_data *pdata) +{ + struct xgbe_desc_if *desc_if = &pdata->desc_if; + struct xgbe_ring *ring; + struct xgbe_ring_data *rdata; + unsigned int i, j; + + DBGPR("-->xgbe_free_rx_data\n"); + + for (i = 0; i < pdata->channel_count; i++) { + ring = pdata->channel[i]->rx_ring; + if (!ring) + break; + + for (j = 0; j < ring->rdesc_count; j++) { + rdata = XGBE_GET_DESC_DATA(ring, j); + desc_if->unmap_rdata(pdata, rdata); + } + } + + DBGPR("<--xgbe_free_rx_data\n"); +} + +static int xgbe_phy_reset(struct xgbe_prv_data *pdata) +{ + pdata->phy_link = -1; + pdata->phy_speed = SPEED_UNKNOWN; + + return pdata->phy_if.phy_reset(pdata); +} + +int xgbe_powerdown(struct net_device *netdev, unsigned int caller) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct xgbe_hw_if *hw_if = &pdata->hw_if; + unsigned long flags; + + DBGPR("-->xgbe_powerdown\n"); + + if (!netif_running(netdev) || + (caller == XGMAC_IOCTL_CONTEXT && pdata->power_down)) { + netdev_alert(netdev, "Device is already powered down\n"); + DBGPR("<--xgbe_powerdown\n"); + return -EINVAL; + } + + spin_lock_irqsave(&pdata->lock, flags); + + if (caller == XGMAC_DRIVER_CONTEXT) + netif_device_detach(netdev); + + netif_tx_stop_all_queues(netdev); + + xgbe_stop_timers(pdata); + flush_workqueue(pdata->dev_workqueue); + + hw_if->powerdown_tx(pdata); + hw_if->powerdown_rx(pdata); + + xgbe_napi_disable(pdata, 0); + + pdata->power_down = 1; + + spin_unlock_irqrestore(&pdata->lock, flags); + + DBGPR("<--xgbe_powerdown\n"); + + return 0; +} + +int xgbe_powerup(struct net_device *netdev, unsigned int caller) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct xgbe_hw_if *hw_if = &pdata->hw_if; + unsigned long flags; + + DBGPR("-->xgbe_powerup\n"); + + if (!netif_running(netdev) || + (caller == XGMAC_IOCTL_CONTEXT && !pdata->power_down)) { + netdev_alert(netdev, "Device is already powered up\n"); + DBGPR("<--xgbe_powerup\n"); + return -EINVAL; + } + + spin_lock_irqsave(&pdata->lock, flags); + + pdata->power_down = 0; + + xgbe_napi_enable(pdata, 0); + + hw_if->powerup_tx(pdata); + hw_if->powerup_rx(pdata); + + if (caller == XGMAC_DRIVER_CONTEXT) + netif_device_attach(netdev); + + netif_tx_start_all_queues(netdev); + + xgbe_start_timers(pdata); + + spin_unlock_irqrestore(&pdata->lock, flags); + + DBGPR("<--xgbe_powerup\n"); + + return 0; +} + +static void xgbe_free_memory(struct xgbe_prv_data *pdata) +{ + struct xgbe_desc_if *desc_if = &pdata->desc_if; + + /* Free the ring descriptors and buffers */ + desc_if->free_ring_resources(pdata); + + /* Free the channel and ring structures */ + xgbe_free_channels(pdata); +} + +static int xgbe_alloc_memory(struct xgbe_prv_data *pdata) +{ + struct xgbe_desc_if *desc_if = &pdata->desc_if; + struct net_device *netdev = pdata->netdev; + int ret; + + if (pdata->new_tx_ring_count) { + pdata->tx_ring_count = pdata->new_tx_ring_count; + pdata->tx_q_count = pdata->tx_ring_count; + pdata->new_tx_ring_count = 0; + } + + if (pdata->new_rx_ring_count) { + pdata->rx_ring_count = pdata->new_rx_ring_count; + pdata->new_rx_ring_count = 0; + } + + /* Calculate the Rx buffer size before allocating rings */ + pdata->rx_buf_size = xgbe_calc_rx_buf_size(netdev, netdev->mtu); + + /* Allocate the channel and ring structures */ + ret = xgbe_alloc_channels(pdata); + if (ret) + return ret; + + /* Allocate the ring descriptors and buffers */ + ret = desc_if->alloc_ring_resources(pdata); + if (ret) + goto err_channels; + + /* Initialize the service and Tx timers */ + xgbe_init_timers(pdata); + + return 0; + +err_channels: + xgbe_free_memory(pdata); + + return ret; +} + +static int xgbe_start(struct xgbe_prv_data *pdata) +{ + struct xgbe_hw_if *hw_if = &pdata->hw_if; + struct xgbe_phy_if *phy_if = &pdata->phy_if; + struct net_device *netdev = pdata->netdev; + unsigned int i; + int ret; + + /* Set the number of queues */ + ret = netif_set_real_num_tx_queues(netdev, pdata->tx_ring_count); + if (ret) { + netdev_err(netdev, "error setting real tx queue count\n"); + return ret; + } + + ret = netif_set_real_num_rx_queues(netdev, pdata->rx_ring_count); + if (ret) { + netdev_err(netdev, "error setting real rx queue count\n"); + return ret; + } + + /* Set RSS lookup table data for programming */ + for (i = 0; i < XGBE_RSS_MAX_TABLE_SIZE; i++) + XGMAC_SET_BITS(pdata->rss_table[i], MAC_RSSDR, DMCH, + i % pdata->rx_ring_count); + + ret = hw_if->init(pdata); + if (ret) + return ret; + + xgbe_napi_enable(pdata, 1); + + ret = xgbe_request_irqs(pdata); + if (ret) + goto err_napi; + + ret = phy_if->phy_start(pdata); + if (ret) + goto err_irqs; + + hw_if->enable_tx(pdata); + hw_if->enable_rx(pdata); + + udp_tunnel_nic_reset_ntf(netdev); + + netif_tx_start_all_queues(netdev); + + xgbe_start_timers(pdata); + queue_work(pdata->dev_workqueue, &pdata->service_work); + + clear_bit(XGBE_STOPPED, &pdata->dev_state); + + return 0; + +err_irqs: + xgbe_free_irqs(pdata); + +err_napi: + xgbe_napi_disable(pdata, 1); + + hw_if->exit(pdata); + + return ret; +} + +static void xgbe_stop(struct xgbe_prv_data *pdata) +{ + struct xgbe_hw_if *hw_if = &pdata->hw_if; + struct xgbe_phy_if *phy_if = &pdata->phy_if; + struct xgbe_channel *channel; + struct net_device *netdev = pdata->netdev; + struct netdev_queue *txq; + unsigned int i; + + DBGPR("-->xgbe_stop\n"); + + if (test_bit(XGBE_STOPPED, &pdata->dev_state)) + return; + + netif_tx_stop_all_queues(netdev); + netif_carrier_off(pdata->netdev); + + xgbe_stop_timers(pdata); + flush_workqueue(pdata->dev_workqueue); + + xgbe_vxlan_unset_port(netdev, 0, 0, NULL); + + hw_if->disable_tx(pdata); + hw_if->disable_rx(pdata); + + phy_if->phy_stop(pdata); + + xgbe_free_irqs(pdata); + + xgbe_napi_disable(pdata, 1); + + hw_if->exit(pdata); + + for (i = 0; i < pdata->channel_count; i++) { + channel = pdata->channel[i]; + if (!channel->tx_ring) + continue; + + txq = netdev_get_tx_queue(netdev, channel->queue_index); + netdev_tx_reset_queue(txq); + } + + set_bit(XGBE_STOPPED, &pdata->dev_state); + + DBGPR("<--xgbe_stop\n"); +} + +static void xgbe_stopdev(struct work_struct *work) +{ + struct xgbe_prv_data *pdata = container_of(work, + struct xgbe_prv_data, + stopdev_work); + + rtnl_lock(); + + xgbe_stop(pdata); + + xgbe_free_tx_data(pdata); + xgbe_free_rx_data(pdata); + + rtnl_unlock(); + + netdev_alert(pdata->netdev, "device stopped\n"); +} + +void xgbe_full_restart_dev(struct xgbe_prv_data *pdata) +{ + /* If not running, "restart" will happen on open */ + if (!netif_running(pdata->netdev)) + return; + + xgbe_stop(pdata); + + xgbe_free_memory(pdata); + xgbe_alloc_memory(pdata); + + xgbe_start(pdata); +} + +void xgbe_restart_dev(struct xgbe_prv_data *pdata) +{ + /* If not running, "restart" will happen on open */ + if (!netif_running(pdata->netdev)) + return; + + xgbe_stop(pdata); + + xgbe_free_tx_data(pdata); + xgbe_free_rx_data(pdata); + + xgbe_start(pdata); +} + +static void xgbe_restart(struct work_struct *work) +{ + struct xgbe_prv_data *pdata = container_of(work, + struct xgbe_prv_data, + restart_work); + + rtnl_lock(); + + xgbe_restart_dev(pdata); + + rtnl_unlock(); +} + +static void xgbe_tx_tstamp(struct work_struct *work) +{ + struct xgbe_prv_data *pdata = container_of(work, + struct xgbe_prv_data, + tx_tstamp_work); + struct skb_shared_hwtstamps hwtstamps; + u64 nsec; + unsigned long flags; + + spin_lock_irqsave(&pdata->tstamp_lock, flags); + if (!pdata->tx_tstamp_skb) + goto unlock; + + if (pdata->tx_tstamp) { + nsec = timecounter_cyc2time(&pdata->tstamp_tc, + pdata->tx_tstamp); + + memset(&hwtstamps, 0, sizeof(hwtstamps)); + hwtstamps.hwtstamp = ns_to_ktime(nsec); + skb_tstamp_tx(pdata->tx_tstamp_skb, &hwtstamps); + } + + dev_kfree_skb_any(pdata->tx_tstamp_skb); + + pdata->tx_tstamp_skb = NULL; + +unlock: + spin_unlock_irqrestore(&pdata->tstamp_lock, flags); +} + +static int xgbe_get_hwtstamp_settings(struct xgbe_prv_data *pdata, + struct ifreq *ifreq) +{ + if (copy_to_user(ifreq->ifr_data, &pdata->tstamp_config, + sizeof(pdata->tstamp_config))) + return -EFAULT; + + return 0; +} + +static int xgbe_set_hwtstamp_settings(struct xgbe_prv_data *pdata, + struct ifreq *ifreq) +{ + struct hwtstamp_config config; + unsigned int mac_tscr; + + if (copy_from_user(&config, ifreq->ifr_data, sizeof(config))) + return -EFAULT; + + mac_tscr = 0; + + switch (config.tx_type) { + case HWTSTAMP_TX_OFF: + break; + + case HWTSTAMP_TX_ON: + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSENA, 1); + break; + + default: + return -ERANGE; + } + + switch (config.rx_filter) { + case HWTSTAMP_FILTER_NONE: + break; + + case HWTSTAMP_FILTER_NTP_ALL: + case HWTSTAMP_FILTER_ALL: + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSENALL, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSENA, 1); + break; + + /* PTP v2, UDP, any kind of event packet */ + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSVER2ENA, 1); + fallthrough; /* to PTP v1, UDP, any kind of event packet */ + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPV4ENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPV6ENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, SNAPTYPSEL, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSENA, 1); + break; + + /* PTP v2, UDP, Sync packet */ + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSVER2ENA, 1); + fallthrough; /* to PTP v1, UDP, Sync packet */ + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPV4ENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPV6ENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSEVNTENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSENA, 1); + break; + + /* PTP v2, UDP, Delay_req packet */ + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSVER2ENA, 1); + fallthrough; /* to PTP v1, UDP, Delay_req packet */ + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPV4ENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPV6ENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSEVNTENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSMSTRENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSENA, 1); + break; + + /* 802.AS1, Ethernet, any kind of event packet */ + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, AV8021ASMEN, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, SNAPTYPSEL, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSENA, 1); + break; + + /* 802.AS1, Ethernet, Sync packet */ + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, AV8021ASMEN, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSEVNTENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSENA, 1); + break; + + /* 802.AS1, Ethernet, Delay_req packet */ + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, AV8021ASMEN, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSMSTRENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSEVNTENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSENA, 1); + break; + + /* PTP v2/802.AS1, any layer, any kind of event packet */ + case HWTSTAMP_FILTER_PTP_V2_EVENT: + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSVER2ENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPV4ENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPV6ENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, SNAPTYPSEL, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSENA, 1); + break; + + /* PTP v2/802.AS1, any layer, Sync packet */ + case HWTSTAMP_FILTER_PTP_V2_SYNC: + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSVER2ENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPV4ENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPV6ENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSEVNTENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSENA, 1); + break; + + /* PTP v2/802.AS1, any layer, Delay_req packet */ + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSVER2ENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPV4ENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSIPV6ENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSMSTRENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSEVNTENA, 1); + XGMAC_SET_BITS(mac_tscr, MAC_TSCR, TSENA, 1); + break; + + default: + return -ERANGE; + } + + pdata->hw_if.config_tstamp(pdata, mac_tscr); + + memcpy(&pdata->tstamp_config, &config, sizeof(config)); + + return 0; +} + +static void xgbe_prep_tx_tstamp(struct xgbe_prv_data *pdata, + struct sk_buff *skb, + struct xgbe_packet_data *packet) +{ + unsigned long flags; + + if (XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, PTP)) { + spin_lock_irqsave(&pdata->tstamp_lock, flags); + if (pdata->tx_tstamp_skb) { + /* Another timestamp in progress, ignore this one */ + XGMAC_SET_BITS(packet->attributes, + TX_PACKET_ATTRIBUTES, PTP, 0); + } else { + pdata->tx_tstamp_skb = skb_get(skb); + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + } + spin_unlock_irqrestore(&pdata->tstamp_lock, flags); + } + + skb_tx_timestamp(skb); +} + +static void xgbe_prep_vlan(struct sk_buff *skb, struct xgbe_packet_data *packet) +{ + if (skb_vlan_tag_present(skb)) + packet->vlan_ctag = skb_vlan_tag_get(skb); +} + +static int xgbe_prep_tso(struct sk_buff *skb, struct xgbe_packet_data *packet) +{ + int ret; + + if (!XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, + TSO_ENABLE)) + return 0; + + ret = skb_cow_head(skb, 0); + if (ret) + return ret; + + if (XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, VXLAN)) { + packet->header_len = skb_inner_tcp_all_headers(skb); + packet->tcp_header_len = inner_tcp_hdrlen(skb); + } else { + packet->header_len = skb_tcp_all_headers(skb); + packet->tcp_header_len = tcp_hdrlen(skb); + } + packet->tcp_payload_len = skb->len - packet->header_len; + packet->mss = skb_shinfo(skb)->gso_size; + + DBGPR(" packet->header_len=%u\n", packet->header_len); + DBGPR(" packet->tcp_header_len=%u, packet->tcp_payload_len=%u\n", + packet->tcp_header_len, packet->tcp_payload_len); + DBGPR(" packet->mss=%u\n", packet->mss); + + /* Update the number of packets that will ultimately be transmitted + * along with the extra bytes for each extra packet + */ + packet->tx_packets = skb_shinfo(skb)->gso_segs; + packet->tx_bytes += (packet->tx_packets - 1) * packet->header_len; + + return 0; +} + +static bool xgbe_is_vxlan(struct sk_buff *skb) +{ + if (!skb->encapsulation) + return false; + + if (skb->ip_summed != CHECKSUM_PARTIAL) + return false; + + switch (skb->protocol) { + case htons(ETH_P_IP): + if (ip_hdr(skb)->protocol != IPPROTO_UDP) + return false; + break; + + case htons(ETH_P_IPV6): + if (ipv6_hdr(skb)->nexthdr != IPPROTO_UDP) + return false; + break; + + default: + return false; + } + + if (skb->inner_protocol_type != ENCAP_TYPE_ETHER || + skb->inner_protocol != htons(ETH_P_TEB) || + (skb_inner_mac_header(skb) - skb_transport_header(skb) != + sizeof(struct udphdr) + sizeof(struct vxlanhdr))) + return false; + + return true; +} + +static int xgbe_is_tso(struct sk_buff *skb) +{ + if (skb->ip_summed != CHECKSUM_PARTIAL) + return 0; + + if (!skb_is_gso(skb)) + return 0; + + DBGPR(" TSO packet to be processed\n"); + + return 1; +} + +static void xgbe_packet_info(struct xgbe_prv_data *pdata, + struct xgbe_ring *ring, struct sk_buff *skb, + struct xgbe_packet_data *packet) +{ + skb_frag_t *frag; + unsigned int context_desc; + unsigned int len; + unsigned int i; + + packet->skb = skb; + + context_desc = 0; + packet->rdesc_count = 0; + + packet->tx_packets = 1; + packet->tx_bytes = skb->len; + + if (xgbe_is_tso(skb)) { + /* TSO requires an extra descriptor if mss is different */ + if (skb_shinfo(skb)->gso_size != ring->tx.cur_mss) { + context_desc = 1; + packet->rdesc_count++; + } + + /* TSO requires an extra descriptor for TSO header */ + packet->rdesc_count++; + + XGMAC_SET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, + TSO_ENABLE, 1); + XGMAC_SET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, + CSUM_ENABLE, 1); + } else if (skb->ip_summed == CHECKSUM_PARTIAL) + XGMAC_SET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, + CSUM_ENABLE, 1); + + if (xgbe_is_vxlan(skb)) + XGMAC_SET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, + VXLAN, 1); + + if (skb_vlan_tag_present(skb)) { + /* VLAN requires an extra descriptor if tag is different */ + if (skb_vlan_tag_get(skb) != ring->tx.cur_vlan_ctag) + /* We can share with the TSO context descriptor */ + if (!context_desc) { + context_desc = 1; + packet->rdesc_count++; + } + + XGMAC_SET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, + VLAN_CTAG, 1); + } + + if ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && + (pdata->tstamp_config.tx_type == HWTSTAMP_TX_ON)) + XGMAC_SET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, + PTP, 1); + + for (len = skb_headlen(skb); len;) { + packet->rdesc_count++; + len -= min_t(unsigned int, len, XGBE_TX_MAX_BUF_SIZE); + } + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + frag = &skb_shinfo(skb)->frags[i]; + for (len = skb_frag_size(frag); len; ) { + packet->rdesc_count++; + len -= min_t(unsigned int, len, XGBE_TX_MAX_BUF_SIZE); + } + } +} + +static int xgbe_open(struct net_device *netdev) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + int ret; + + /* Create the various names based on netdev name */ + snprintf(pdata->an_name, sizeof(pdata->an_name) - 1, "%s-pcs", + netdev_name(netdev)); + + snprintf(pdata->ecc_name, sizeof(pdata->ecc_name) - 1, "%s-ecc", + netdev_name(netdev)); + + snprintf(pdata->i2c_name, sizeof(pdata->i2c_name) - 1, "%s-i2c", + netdev_name(netdev)); + + /* Create workqueues */ + pdata->dev_workqueue = + create_singlethread_workqueue(netdev_name(netdev)); + if (!pdata->dev_workqueue) { + netdev_err(netdev, "device workqueue creation failed\n"); + return -ENOMEM; + } + + pdata->an_workqueue = + create_singlethread_workqueue(pdata->an_name); + if (!pdata->an_workqueue) { + netdev_err(netdev, "phy workqueue creation failed\n"); + ret = -ENOMEM; + goto err_dev_wq; + } + + /* Reset the phy settings */ + ret = xgbe_phy_reset(pdata); + if (ret) + goto err_an_wq; + + /* Enable the clocks */ + ret = clk_prepare_enable(pdata->sysclk); + if (ret) { + netdev_alert(netdev, "dma clk_prepare_enable failed\n"); + goto err_an_wq; + } + + ret = clk_prepare_enable(pdata->ptpclk); + if (ret) { + netdev_alert(netdev, "ptp clk_prepare_enable failed\n"); + goto err_sysclk; + } + + INIT_WORK(&pdata->service_work, xgbe_service); + INIT_WORK(&pdata->restart_work, xgbe_restart); + INIT_WORK(&pdata->stopdev_work, xgbe_stopdev); + INIT_WORK(&pdata->tx_tstamp_work, xgbe_tx_tstamp); + + ret = xgbe_alloc_memory(pdata); + if (ret) + goto err_ptpclk; + + ret = xgbe_start(pdata); + if (ret) + goto err_mem; + + clear_bit(XGBE_DOWN, &pdata->dev_state); + + return 0; + +err_mem: + xgbe_free_memory(pdata); + +err_ptpclk: + clk_disable_unprepare(pdata->ptpclk); + +err_sysclk: + clk_disable_unprepare(pdata->sysclk); + +err_an_wq: + destroy_workqueue(pdata->an_workqueue); + +err_dev_wq: + destroy_workqueue(pdata->dev_workqueue); + + return ret; +} + +static int xgbe_close(struct net_device *netdev) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + + /* Stop the device */ + xgbe_stop(pdata); + + xgbe_free_memory(pdata); + + /* Disable the clocks */ + clk_disable_unprepare(pdata->ptpclk); + clk_disable_unprepare(pdata->sysclk); + + destroy_workqueue(pdata->an_workqueue); + + destroy_workqueue(pdata->dev_workqueue); + + set_bit(XGBE_DOWN, &pdata->dev_state); + + return 0; +} + +static netdev_tx_t xgbe_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct xgbe_hw_if *hw_if = &pdata->hw_if; + struct xgbe_desc_if *desc_if = &pdata->desc_if; + struct xgbe_channel *channel; + struct xgbe_ring *ring; + struct xgbe_packet_data *packet; + struct netdev_queue *txq; + netdev_tx_t ret; + + DBGPR("-->xgbe_xmit: skb->len = %d\n", skb->len); + + channel = pdata->channel[skb->queue_mapping]; + txq = netdev_get_tx_queue(netdev, channel->queue_index); + ring = channel->tx_ring; + packet = &ring->packet_data; + + ret = NETDEV_TX_OK; + + if (skb->len == 0) { + netif_err(pdata, tx_err, netdev, + "empty skb received from stack\n"); + dev_kfree_skb_any(skb); + goto tx_netdev_return; + } + + /* Calculate preliminary packet info */ + memset(packet, 0, sizeof(*packet)); + xgbe_packet_info(pdata, ring, skb, packet); + + /* Check that there are enough descriptors available */ + ret = xgbe_maybe_stop_tx_queue(channel, ring, packet->rdesc_count); + if (ret) + goto tx_netdev_return; + + ret = xgbe_prep_tso(skb, packet); + if (ret) { + netif_err(pdata, tx_err, netdev, + "error processing TSO packet\n"); + dev_kfree_skb_any(skb); + goto tx_netdev_return; + } + xgbe_prep_vlan(skb, packet); + + if (!desc_if->map_tx_skb(channel, skb)) { + dev_kfree_skb_any(skb); + goto tx_netdev_return; + } + + xgbe_prep_tx_tstamp(pdata, skb, packet); + + /* Report on the actual number of bytes (to be) sent */ + netdev_tx_sent_queue(txq, packet->tx_bytes); + + /* Configure required descriptor fields for transmission */ + hw_if->dev_xmit(channel); + + if (netif_msg_pktdata(pdata)) + xgbe_print_pkt(netdev, skb, true); + + /* Stop the queue in advance if there may not be enough descriptors */ + xgbe_maybe_stop_tx_queue(channel, ring, XGBE_TX_MAX_DESCS); + + ret = NETDEV_TX_OK; + +tx_netdev_return: + return ret; +} + +static void xgbe_set_rx_mode(struct net_device *netdev) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct xgbe_hw_if *hw_if = &pdata->hw_if; + + DBGPR("-->xgbe_set_rx_mode\n"); + + hw_if->config_rx_mode(pdata); + + DBGPR("<--xgbe_set_rx_mode\n"); +} + +static int xgbe_set_mac_address(struct net_device *netdev, void *addr) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct xgbe_hw_if *hw_if = &pdata->hw_if; + struct sockaddr *saddr = addr; + + DBGPR("-->xgbe_set_mac_address\n"); + + if (!is_valid_ether_addr(saddr->sa_data)) + return -EADDRNOTAVAIL; + + eth_hw_addr_set(netdev, saddr->sa_data); + + hw_if->set_mac_address(pdata, netdev->dev_addr); + + DBGPR("<--xgbe_set_mac_address\n"); + + return 0; +} + +static int xgbe_ioctl(struct net_device *netdev, struct ifreq *ifreq, int cmd) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + int ret; + + switch (cmd) { + case SIOCGHWTSTAMP: + ret = xgbe_get_hwtstamp_settings(pdata, ifreq); + break; + + case SIOCSHWTSTAMP: + ret = xgbe_set_hwtstamp_settings(pdata, ifreq); + break; + + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +static int xgbe_change_mtu(struct net_device *netdev, int mtu) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + int ret; + + DBGPR("-->xgbe_change_mtu\n"); + + ret = xgbe_calc_rx_buf_size(netdev, mtu); + if (ret < 0) + return ret; + + pdata->rx_buf_size = ret; + netdev->mtu = mtu; + + xgbe_restart_dev(pdata); + + DBGPR("<--xgbe_change_mtu\n"); + + return 0; +} + +static void xgbe_tx_timeout(struct net_device *netdev, unsigned int txqueue) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + + netdev_warn(netdev, "tx timeout, device restarting\n"); + schedule_work(&pdata->restart_work); +} + +static void xgbe_get_stats64(struct net_device *netdev, + struct rtnl_link_stats64 *s) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct xgbe_mmc_stats *pstats = &pdata->mmc_stats; + + DBGPR("-->%s\n", __func__); + + pdata->hw_if.read_mmc_stats(pdata); + + s->rx_packets = pstats->rxframecount_gb; + s->rx_bytes = pstats->rxoctetcount_gb; + s->rx_errors = pstats->rxframecount_gb - + pstats->rxbroadcastframes_g - + pstats->rxmulticastframes_g - + pstats->rxunicastframes_g; + s->multicast = pstats->rxmulticastframes_g; + s->rx_length_errors = pstats->rxlengtherror; + s->rx_crc_errors = pstats->rxcrcerror; + s->rx_fifo_errors = pstats->rxfifooverflow; + + s->tx_packets = pstats->txframecount_gb; + s->tx_bytes = pstats->txoctetcount_gb; + s->tx_errors = pstats->txframecount_gb - pstats->txframecount_g; + s->tx_dropped = netdev->stats.tx_dropped; + + DBGPR("<--%s\n", __func__); +} + +static int xgbe_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, + u16 vid) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct xgbe_hw_if *hw_if = &pdata->hw_if; + + DBGPR("-->%s\n", __func__); + + set_bit(vid, pdata->active_vlans); + hw_if->update_vlan_hash_table(pdata); + + DBGPR("<--%s\n", __func__); + + return 0; +} + +static int xgbe_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, + u16 vid) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct xgbe_hw_if *hw_if = &pdata->hw_if; + + DBGPR("-->%s\n", __func__); + + clear_bit(vid, pdata->active_vlans); + hw_if->update_vlan_hash_table(pdata); + + DBGPR("<--%s\n", __func__); + + return 0; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void xgbe_poll_controller(struct net_device *netdev) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct xgbe_channel *channel; + unsigned int i; + + DBGPR("-->xgbe_poll_controller\n"); + + if (pdata->per_channel_irq) { + for (i = 0; i < pdata->channel_count; i++) { + channel = pdata->channel[i]; + xgbe_dma_isr(channel->dma_irq, channel); + } + } else { + disable_irq(pdata->dev_irq); + xgbe_isr(pdata->dev_irq, pdata); + enable_irq(pdata->dev_irq); + } + + DBGPR("<--xgbe_poll_controller\n"); +} +#endif /* End CONFIG_NET_POLL_CONTROLLER */ + +static int xgbe_setup_tc(struct net_device *netdev, enum tc_setup_type type, + void *type_data) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct tc_mqprio_qopt *mqprio = type_data; + u8 tc; + + if (type != TC_SETUP_QDISC_MQPRIO) + return -EOPNOTSUPP; + + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + tc = mqprio->num_tc; + + if (tc > pdata->hw_feat.tc_cnt) + return -EINVAL; + + pdata->num_tcs = tc; + pdata->hw_if.config_tc(pdata); + + return 0; +} + +static netdev_features_t xgbe_fix_features(struct net_device *netdev, + netdev_features_t features) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + netdev_features_t vxlan_base; + + vxlan_base = NETIF_F_GSO_UDP_TUNNEL | NETIF_F_RX_UDP_TUNNEL_PORT; + + if (!pdata->hw_feat.vxn) + return features; + + /* VXLAN CSUM requires VXLAN base */ + if ((features & NETIF_F_GSO_UDP_TUNNEL_CSUM) && + !(features & NETIF_F_GSO_UDP_TUNNEL)) { + netdev_notice(netdev, + "forcing tx udp tunnel support\n"); + features |= NETIF_F_GSO_UDP_TUNNEL; + } + + /* Can't do one without doing the other */ + if ((features & vxlan_base) != vxlan_base) { + netdev_notice(netdev, + "forcing both tx and rx udp tunnel support\n"); + features |= vxlan_base; + } + + if (features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) { + if (!(features & NETIF_F_GSO_UDP_TUNNEL_CSUM)) { + netdev_notice(netdev, + "forcing tx udp tunnel checksumming on\n"); + features |= NETIF_F_GSO_UDP_TUNNEL_CSUM; + } + } else { + if (features & NETIF_F_GSO_UDP_TUNNEL_CSUM) { + netdev_notice(netdev, + "forcing tx udp tunnel checksumming off\n"); + features &= ~NETIF_F_GSO_UDP_TUNNEL_CSUM; + } + } + + return features; +} + +static int xgbe_set_features(struct net_device *netdev, + netdev_features_t features) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct xgbe_hw_if *hw_if = &pdata->hw_if; + netdev_features_t rxhash, rxcsum, rxvlan, rxvlan_filter; + int ret = 0; + + rxhash = pdata->netdev_features & NETIF_F_RXHASH; + rxcsum = pdata->netdev_features & NETIF_F_RXCSUM; + rxvlan = pdata->netdev_features & NETIF_F_HW_VLAN_CTAG_RX; + rxvlan_filter = pdata->netdev_features & NETIF_F_HW_VLAN_CTAG_FILTER; + + if ((features & NETIF_F_RXHASH) && !rxhash) + ret = hw_if->enable_rss(pdata); + else if (!(features & NETIF_F_RXHASH) && rxhash) + ret = hw_if->disable_rss(pdata); + if (ret) + return ret; + + if ((features & NETIF_F_RXCSUM) && !rxcsum) + hw_if->enable_rx_csum(pdata); + else if (!(features & NETIF_F_RXCSUM) && rxcsum) + hw_if->disable_rx_csum(pdata); + + if ((features & NETIF_F_HW_VLAN_CTAG_RX) && !rxvlan) + hw_if->enable_rx_vlan_stripping(pdata); + else if (!(features & NETIF_F_HW_VLAN_CTAG_RX) && rxvlan) + hw_if->disable_rx_vlan_stripping(pdata); + + if ((features & NETIF_F_HW_VLAN_CTAG_FILTER) && !rxvlan_filter) + hw_if->enable_rx_vlan_filtering(pdata); + else if (!(features & NETIF_F_HW_VLAN_CTAG_FILTER) && rxvlan_filter) + hw_if->disable_rx_vlan_filtering(pdata); + + pdata->netdev_features = features; + + DBGPR("<--xgbe_set_features\n"); + + return 0; +} + +static netdev_features_t xgbe_features_check(struct sk_buff *skb, + struct net_device *netdev, + netdev_features_t features) +{ + features = vlan_features_check(skb, features); + features = vxlan_features_check(skb, features); + + return features; +} + +static const struct net_device_ops xgbe_netdev_ops = { + .ndo_open = xgbe_open, + .ndo_stop = xgbe_close, + .ndo_start_xmit = xgbe_xmit, + .ndo_set_rx_mode = xgbe_set_rx_mode, + .ndo_set_mac_address = xgbe_set_mac_address, + .ndo_validate_addr = eth_validate_addr, + .ndo_eth_ioctl = xgbe_ioctl, + .ndo_change_mtu = xgbe_change_mtu, + .ndo_tx_timeout = xgbe_tx_timeout, + .ndo_get_stats64 = xgbe_get_stats64, + .ndo_vlan_rx_add_vid = xgbe_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = xgbe_vlan_rx_kill_vid, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = xgbe_poll_controller, +#endif + .ndo_setup_tc = xgbe_setup_tc, + .ndo_fix_features = xgbe_fix_features, + .ndo_set_features = xgbe_set_features, + .ndo_features_check = xgbe_features_check, +}; + +const struct net_device_ops *xgbe_get_netdev_ops(void) +{ + return &xgbe_netdev_ops; +} + +static void xgbe_rx_refresh(struct xgbe_channel *channel) +{ + struct xgbe_prv_data *pdata = channel->pdata; + struct xgbe_hw_if *hw_if = &pdata->hw_if; + struct xgbe_desc_if *desc_if = &pdata->desc_if; + struct xgbe_ring *ring = channel->rx_ring; + struct xgbe_ring_data *rdata; + + while (ring->dirty != ring->cur) { + rdata = XGBE_GET_DESC_DATA(ring, ring->dirty); + + /* Reset rdata values */ + desc_if->unmap_rdata(pdata, rdata); + + if (desc_if->map_rx_buffer(pdata, ring, rdata)) + break; + + hw_if->rx_desc_reset(pdata, rdata, ring->dirty); + + ring->dirty++; + } + + /* Make sure everything is written before the register write */ + wmb(); + + /* Update the Rx Tail Pointer Register with address of + * the last cleaned entry */ + rdata = XGBE_GET_DESC_DATA(ring, ring->dirty - 1); + XGMAC_DMA_IOWRITE(channel, DMA_CH_RDTR_LO, + lower_32_bits(rdata->rdesc_dma)); +} + +static struct sk_buff *xgbe_create_skb(struct xgbe_prv_data *pdata, + struct napi_struct *napi, + struct xgbe_ring_data *rdata, + unsigned int len) +{ + struct sk_buff *skb; + u8 *packet; + + skb = napi_alloc_skb(napi, rdata->rx.hdr.dma_len); + if (!skb) + return NULL; + + /* Pull in the header buffer which may contain just the header + * or the header plus data + */ + dma_sync_single_range_for_cpu(pdata->dev, rdata->rx.hdr.dma_base, + rdata->rx.hdr.dma_off, + rdata->rx.hdr.dma_len, DMA_FROM_DEVICE); + + packet = page_address(rdata->rx.hdr.pa.pages) + + rdata->rx.hdr.pa.pages_offset; + skb_copy_to_linear_data(skb, packet, len); + skb_put(skb, len); + + return skb; +} + +static unsigned int xgbe_rx_buf1_len(struct xgbe_ring_data *rdata, + struct xgbe_packet_data *packet) +{ + /* Always zero if not the first descriptor */ + if (!XGMAC_GET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, FIRST)) + return 0; + + /* First descriptor with split header, return header length */ + if (rdata->rx.hdr_len) + return rdata->rx.hdr_len; + + /* First descriptor but not the last descriptor and no split header, + * so the full buffer was used + */ + if (!XGMAC_GET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, LAST)) + return rdata->rx.hdr.dma_len; + + /* First descriptor and last descriptor and no split header, so + * calculate how much of the buffer was used + */ + return min_t(unsigned int, rdata->rx.hdr.dma_len, rdata->rx.len); +} + +static unsigned int xgbe_rx_buf2_len(struct xgbe_ring_data *rdata, + struct xgbe_packet_data *packet, + unsigned int len) +{ + /* Always the full buffer if not the last descriptor */ + if (!XGMAC_GET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, LAST)) + return rdata->rx.buf.dma_len; + + /* Last descriptor so calculate how much of the buffer was used + * for the last bit of data + */ + return rdata->rx.len - len; +} + +static int xgbe_tx_poll(struct xgbe_channel *channel) +{ + struct xgbe_prv_data *pdata = channel->pdata; + struct xgbe_hw_if *hw_if = &pdata->hw_if; + struct xgbe_desc_if *desc_if = &pdata->desc_if; + struct xgbe_ring *ring = channel->tx_ring; + struct xgbe_ring_data *rdata; + struct xgbe_ring_desc *rdesc; + struct net_device *netdev = pdata->netdev; + struct netdev_queue *txq; + int processed = 0; + unsigned int tx_packets = 0, tx_bytes = 0; + unsigned int cur; + + DBGPR("-->xgbe_tx_poll\n"); + + /* Nothing to do if there isn't a Tx ring for this channel */ + if (!ring) + return 0; + + cur = ring->cur; + + /* Be sure we get ring->cur before accessing descriptor data */ + smp_rmb(); + + txq = netdev_get_tx_queue(netdev, channel->queue_index); + + while ((processed < XGBE_TX_DESC_MAX_PROC) && + (ring->dirty != cur)) { + rdata = XGBE_GET_DESC_DATA(ring, ring->dirty); + rdesc = rdata->rdesc; + + if (!hw_if->tx_complete(rdesc)) + break; + + /* Make sure descriptor fields are read after reading the OWN + * bit */ + dma_rmb(); + + if (netif_msg_tx_done(pdata)) + xgbe_dump_tx_desc(pdata, ring, ring->dirty, 1, 0); + + if (hw_if->is_last_desc(rdesc)) { + tx_packets += rdata->tx.packets; + tx_bytes += rdata->tx.bytes; + } + + /* Free the SKB and reset the descriptor for re-use */ + desc_if->unmap_rdata(pdata, rdata); + hw_if->tx_desc_reset(rdata); + + processed++; + ring->dirty++; + } + + if (!processed) + return 0; + + netdev_tx_completed_queue(txq, tx_packets, tx_bytes); + + if ((ring->tx.queue_stopped == 1) && + (xgbe_tx_avail_desc(ring) > XGBE_TX_DESC_MIN_FREE)) { + ring->tx.queue_stopped = 0; + netif_tx_wake_queue(txq); + } + + DBGPR("<--xgbe_tx_poll: processed=%d\n", processed); + + return processed; +} + +static int xgbe_rx_poll(struct xgbe_channel *channel, int budget) +{ + struct xgbe_prv_data *pdata = channel->pdata; + struct xgbe_hw_if *hw_if = &pdata->hw_if; + struct xgbe_ring *ring = channel->rx_ring; + struct xgbe_ring_data *rdata; + struct xgbe_packet_data *packet; + struct net_device *netdev = pdata->netdev; + struct napi_struct *napi; + struct sk_buff *skb; + struct skb_shared_hwtstamps *hwtstamps; + unsigned int last, error, context_next, context; + unsigned int len, buf1_len, buf2_len, max_len; + unsigned int received = 0; + int packet_count = 0; + + DBGPR("-->xgbe_rx_poll: budget=%d\n", budget); + + /* Nothing to do if there isn't a Rx ring for this channel */ + if (!ring) + return 0; + + last = 0; + context_next = 0; + + napi = (pdata->per_channel_irq) ? &channel->napi : &pdata->napi; + + rdata = XGBE_GET_DESC_DATA(ring, ring->cur); + packet = &ring->packet_data; + while (packet_count < budget) { + DBGPR(" cur = %d\n", ring->cur); + + /* First time in loop see if we need to restore state */ + if (!received && rdata->state_saved) { + skb = rdata->state.skb; + error = rdata->state.error; + len = rdata->state.len; + } else { + memset(packet, 0, sizeof(*packet)); + skb = NULL; + error = 0; + len = 0; + } + +read_again: + rdata = XGBE_GET_DESC_DATA(ring, ring->cur); + + if (xgbe_rx_dirty_desc(ring) > (XGBE_RX_DESC_CNT >> 3)) + xgbe_rx_refresh(channel); + + if (hw_if->dev_read(channel)) + break; + + received++; + ring->cur++; + + last = XGMAC_GET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + LAST); + context_next = XGMAC_GET_BITS(packet->attributes, + RX_PACKET_ATTRIBUTES, + CONTEXT_NEXT); + context = XGMAC_GET_BITS(packet->attributes, + RX_PACKET_ATTRIBUTES, + CONTEXT); + + /* Earlier error, just drain the remaining data */ + if ((!last || context_next) && error) + goto read_again; + + if (error || packet->errors) { + if (packet->errors) + netif_err(pdata, rx_err, netdev, + "error in received packet\n"); + dev_kfree_skb(skb); + goto next_packet; + } + + if (!context) { + /* Get the data length in the descriptor buffers */ + buf1_len = xgbe_rx_buf1_len(rdata, packet); + len += buf1_len; + buf2_len = xgbe_rx_buf2_len(rdata, packet, len); + len += buf2_len; + + if (buf2_len > rdata->rx.buf.dma_len) { + /* Hardware inconsistency within the descriptors + * that has resulted in a length underflow. + */ + error = 1; + goto skip_data; + } + + if (!skb) { + skb = xgbe_create_skb(pdata, napi, rdata, + buf1_len); + if (!skb) { + error = 1; + goto skip_data; + } + } + + if (buf2_len) { + dma_sync_single_range_for_cpu(pdata->dev, + rdata->rx.buf.dma_base, + rdata->rx.buf.dma_off, + rdata->rx.buf.dma_len, + DMA_FROM_DEVICE); + + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, + rdata->rx.buf.pa.pages, + rdata->rx.buf.pa.pages_offset, + buf2_len, + rdata->rx.buf.dma_len); + rdata->rx.buf.pa.pages = NULL; + } + } + +skip_data: + if (!last || context_next) + goto read_again; + + if (!skb || error) { + dev_kfree_skb(skb); + goto next_packet; + } + + /* Be sure we don't exceed the configured MTU */ + max_len = netdev->mtu + ETH_HLEN; + if (!(netdev->features & NETIF_F_HW_VLAN_CTAG_RX) && + (skb->protocol == htons(ETH_P_8021Q))) + max_len += VLAN_HLEN; + + if (skb->len > max_len) { + netif_err(pdata, rx_err, netdev, + "packet length exceeds configured MTU\n"); + dev_kfree_skb(skb); + goto next_packet; + } + + if (netif_msg_pktdata(pdata)) + xgbe_print_pkt(netdev, skb, false); + + skb_checksum_none_assert(skb); + if (XGMAC_GET_BITS(packet->attributes, + RX_PACKET_ATTRIBUTES, CSUM_DONE)) + skb->ip_summed = CHECKSUM_UNNECESSARY; + + if (XGMAC_GET_BITS(packet->attributes, + RX_PACKET_ATTRIBUTES, TNP)) { + skb->encapsulation = 1; + + if (XGMAC_GET_BITS(packet->attributes, + RX_PACKET_ATTRIBUTES, TNPCSUM_DONE)) + skb->csum_level = 1; + } + + if (XGMAC_GET_BITS(packet->attributes, + RX_PACKET_ATTRIBUTES, VLAN_CTAG)) + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), + packet->vlan_ctag); + + if (XGMAC_GET_BITS(packet->attributes, + RX_PACKET_ATTRIBUTES, RX_TSTAMP)) { + u64 nsec; + + nsec = timecounter_cyc2time(&pdata->tstamp_tc, + packet->rx_tstamp); + hwtstamps = skb_hwtstamps(skb); + hwtstamps->hwtstamp = ns_to_ktime(nsec); + } + + if (XGMAC_GET_BITS(packet->attributes, + RX_PACKET_ATTRIBUTES, RSS_HASH)) + skb_set_hash(skb, packet->rss_hash, + packet->rss_hash_type); + + skb->dev = netdev; + skb->protocol = eth_type_trans(skb, netdev); + skb_record_rx_queue(skb, channel->queue_index); + + napi_gro_receive(napi, skb); + +next_packet: + packet_count++; + } + + /* Check if we need to save state before leaving */ + if (received && (!last || context_next)) { + rdata = XGBE_GET_DESC_DATA(ring, ring->cur); + rdata->state_saved = 1; + rdata->state.skb = skb; + rdata->state.len = len; + rdata->state.error = error; + } + + DBGPR("<--xgbe_rx_poll: packet_count = %d\n", packet_count); + + return packet_count; +} + +static int xgbe_one_poll(struct napi_struct *napi, int budget) +{ + struct xgbe_channel *channel = container_of(napi, struct xgbe_channel, + napi); + struct xgbe_prv_data *pdata = channel->pdata; + int processed = 0; + + DBGPR("-->xgbe_one_poll: budget=%d\n", budget); + + /* Cleanup Tx ring first */ + xgbe_tx_poll(channel); + + /* Process Rx ring next */ + processed = xgbe_rx_poll(channel, budget); + + /* If we processed everything, we are done */ + if ((processed < budget) && napi_complete_done(napi, processed)) { + /* Enable Tx and Rx interrupts */ + if (pdata->channel_irq_mode) + xgbe_enable_rx_tx_int(pdata, channel); + else + enable_irq(channel->dma_irq); + } + + DBGPR("<--xgbe_one_poll: received = %d\n", processed); + + return processed; +} + +static int xgbe_all_poll(struct napi_struct *napi, int budget) +{ + struct xgbe_prv_data *pdata = container_of(napi, struct xgbe_prv_data, + napi); + struct xgbe_channel *channel; + int ring_budget; + int processed, last_processed; + unsigned int i; + + DBGPR("-->xgbe_all_poll: budget=%d\n", budget); + + processed = 0; + ring_budget = budget / pdata->rx_ring_count; + do { + last_processed = processed; + + for (i = 0; i < pdata->channel_count; i++) { + channel = pdata->channel[i]; + + /* Cleanup Tx ring first */ + xgbe_tx_poll(channel); + + /* Process Rx ring next */ + if (ring_budget > (budget - processed)) + ring_budget = budget - processed; + processed += xgbe_rx_poll(channel, ring_budget); + } + } while ((processed < budget) && (processed != last_processed)); + + /* If we processed everything, we are done */ + if ((processed < budget) && napi_complete_done(napi, processed)) { + /* Enable Tx and Rx interrupts */ + xgbe_enable_rx_tx_ints(pdata); + } + + DBGPR("<--xgbe_all_poll: received = %d\n", processed); + + return processed; +} + +void xgbe_dump_tx_desc(struct xgbe_prv_data *pdata, struct xgbe_ring *ring, + unsigned int idx, unsigned int count, unsigned int flag) +{ + struct xgbe_ring_data *rdata; + struct xgbe_ring_desc *rdesc; + + while (count--) { + rdata = XGBE_GET_DESC_DATA(ring, idx); + rdesc = rdata->rdesc; + netdev_dbg(pdata->netdev, + "TX_NORMAL_DESC[%d %s] = %08x:%08x:%08x:%08x\n", idx, + (flag == 1) ? "QUEUED FOR TX" : "TX BY DEVICE", + le32_to_cpu(rdesc->desc0), + le32_to_cpu(rdesc->desc1), + le32_to_cpu(rdesc->desc2), + le32_to_cpu(rdesc->desc3)); + idx++; + } +} + +void xgbe_dump_rx_desc(struct xgbe_prv_data *pdata, struct xgbe_ring *ring, + unsigned int idx) +{ + struct xgbe_ring_data *rdata; + struct xgbe_ring_desc *rdesc; + + rdata = XGBE_GET_DESC_DATA(ring, idx); + rdesc = rdata->rdesc; + netdev_dbg(pdata->netdev, + "RX_NORMAL_DESC[%d RX BY DEVICE] = %08x:%08x:%08x:%08x\n", + idx, le32_to_cpu(rdesc->desc0), le32_to_cpu(rdesc->desc1), + le32_to_cpu(rdesc->desc2), le32_to_cpu(rdesc->desc3)); +} + +void xgbe_print_pkt(struct net_device *netdev, struct sk_buff *skb, bool tx_rx) +{ + struct ethhdr *eth = (struct ethhdr *)skb->data; + unsigned char buffer[128]; + unsigned int i; + + netdev_dbg(netdev, "\n************** SKB dump ****************\n"); + + netdev_dbg(netdev, "%s packet of %d bytes\n", + (tx_rx ? "TX" : "RX"), skb->len); + + netdev_dbg(netdev, "Dst MAC addr: %pM\n", eth->h_dest); + netdev_dbg(netdev, "Src MAC addr: %pM\n", eth->h_source); + netdev_dbg(netdev, "Protocol: %#06x\n", ntohs(eth->h_proto)); + + for (i = 0; i < skb->len; i += 32) { + unsigned int len = min(skb->len - i, 32U); + + hex_dump_to_buffer(&skb->data[i], len, 32, 1, + buffer, sizeof(buffer), false); + netdev_dbg(netdev, " %#06x: %s\n", i, buffer); + } + + netdev_dbg(netdev, "\n************** SKB dump ****************\n"); +} diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c new file mode 100644 index 000000000..32fab5e77 --- /dev/null +++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c @@ -0,0 +1,860 @@ +/* + * AMD 10Gb Ethernet driver + * + * This file is available to you under your choice of the following two + * licenses: + * + * License 1: GPLv2 + * + * Copyright (c) 2014-2016 Advanced Micro Devices, Inc. + * + * This file is free software; you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or (at + * your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * + * License 2: Modified BSD + * + * Copyright (c) 2014-2016 Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/spinlock.h> +#include <linux/phy.h> +#include <linux/net_tstamp.h> + +#include "xgbe.h" +#include "xgbe-common.h" + +struct xgbe_stats { + char stat_string[ETH_GSTRING_LEN]; + int stat_size; + int stat_offset; +}; + +#define XGMAC_MMC_STAT(_string, _var) \ + { _string, \ + sizeof_field(struct xgbe_mmc_stats, _var), \ + offsetof(struct xgbe_prv_data, mmc_stats._var), \ + } + +#define XGMAC_EXT_STAT(_string, _var) \ + { _string, \ + sizeof_field(struct xgbe_ext_stats, _var), \ + offsetof(struct xgbe_prv_data, ext_stats._var), \ + } + +static const struct xgbe_stats xgbe_gstring_stats[] = { + XGMAC_MMC_STAT("tx_bytes", txoctetcount_gb), + XGMAC_MMC_STAT("tx_packets", txframecount_gb), + XGMAC_MMC_STAT("tx_unicast_packets", txunicastframes_gb), + XGMAC_MMC_STAT("tx_broadcast_packets", txbroadcastframes_gb), + XGMAC_MMC_STAT("tx_multicast_packets", txmulticastframes_gb), + XGMAC_MMC_STAT("tx_vlan_packets", txvlanframes_g), + XGMAC_EXT_STAT("tx_vxlan_packets", tx_vxlan_packets), + XGMAC_EXT_STAT("tx_tso_packets", tx_tso_packets), + XGMAC_MMC_STAT("tx_64_byte_packets", tx64octets_gb), + XGMAC_MMC_STAT("tx_65_to_127_byte_packets", tx65to127octets_gb), + XGMAC_MMC_STAT("tx_128_to_255_byte_packets", tx128to255octets_gb), + XGMAC_MMC_STAT("tx_256_to_511_byte_packets", tx256to511octets_gb), + XGMAC_MMC_STAT("tx_512_to_1023_byte_packets", tx512to1023octets_gb), + XGMAC_MMC_STAT("tx_1024_to_max_byte_packets", tx1024tomaxoctets_gb), + XGMAC_MMC_STAT("tx_underflow_errors", txunderflowerror), + XGMAC_MMC_STAT("tx_pause_frames", txpauseframes), + + XGMAC_MMC_STAT("rx_bytes", rxoctetcount_gb), + XGMAC_MMC_STAT("rx_packets", rxframecount_gb), + XGMAC_MMC_STAT("rx_unicast_packets", rxunicastframes_g), + XGMAC_MMC_STAT("rx_broadcast_packets", rxbroadcastframes_g), + XGMAC_MMC_STAT("rx_multicast_packets", rxmulticastframes_g), + XGMAC_MMC_STAT("rx_vlan_packets", rxvlanframes_gb), + XGMAC_EXT_STAT("rx_vxlan_packets", rx_vxlan_packets), + XGMAC_MMC_STAT("rx_64_byte_packets", rx64octets_gb), + XGMAC_MMC_STAT("rx_65_to_127_byte_packets", rx65to127octets_gb), + XGMAC_MMC_STAT("rx_128_to_255_byte_packets", rx128to255octets_gb), + XGMAC_MMC_STAT("rx_256_to_511_byte_packets", rx256to511octets_gb), + XGMAC_MMC_STAT("rx_512_to_1023_byte_packets", rx512to1023octets_gb), + XGMAC_MMC_STAT("rx_1024_to_max_byte_packets", rx1024tomaxoctets_gb), + XGMAC_MMC_STAT("rx_undersize_packets", rxundersize_g), + XGMAC_MMC_STAT("rx_oversize_packets", rxoversize_g), + XGMAC_MMC_STAT("rx_crc_errors", rxcrcerror), + XGMAC_MMC_STAT("rx_crc_errors_small_packets", rxrunterror), + XGMAC_MMC_STAT("rx_crc_errors_giant_packets", rxjabbererror), + XGMAC_MMC_STAT("rx_length_errors", rxlengtherror), + XGMAC_MMC_STAT("rx_out_of_range_errors", rxoutofrangetype), + XGMAC_MMC_STAT("rx_fifo_overflow_errors", rxfifooverflow), + XGMAC_MMC_STAT("rx_watchdog_errors", rxwatchdogerror), + XGMAC_EXT_STAT("rx_csum_errors", rx_csum_errors), + XGMAC_EXT_STAT("rx_vxlan_csum_errors", rx_vxlan_csum_errors), + XGMAC_MMC_STAT("rx_pause_frames", rxpauseframes), + XGMAC_EXT_STAT("rx_split_header_packets", rx_split_header_packets), + XGMAC_EXT_STAT("rx_buffer_unavailable", rx_buffer_unavailable), +}; + +#define XGBE_STATS_COUNT ARRAY_SIZE(xgbe_gstring_stats) + +static void xgbe_get_strings(struct net_device *netdev, u32 stringset, u8 *data) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + int i; + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < XGBE_STATS_COUNT; i++) { + memcpy(data, xgbe_gstring_stats[i].stat_string, + ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } + for (i = 0; i < pdata->tx_ring_count; i++) { + sprintf(data, "txq_%u_packets", i); + data += ETH_GSTRING_LEN; + sprintf(data, "txq_%u_bytes", i); + data += ETH_GSTRING_LEN; + } + for (i = 0; i < pdata->rx_ring_count; i++) { + sprintf(data, "rxq_%u_packets", i); + data += ETH_GSTRING_LEN; + sprintf(data, "rxq_%u_bytes", i); + data += ETH_GSTRING_LEN; + } + break; + } +} + +static void xgbe_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, u64 *data) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + u8 *stat; + int i; + + pdata->hw_if.read_mmc_stats(pdata); + for (i = 0; i < XGBE_STATS_COUNT; i++) { + stat = (u8 *)pdata + xgbe_gstring_stats[i].stat_offset; + *data++ = *(u64 *)stat; + } + for (i = 0; i < pdata->tx_ring_count; i++) { + *data++ = pdata->ext_stats.txq_packets[i]; + *data++ = pdata->ext_stats.txq_bytes[i]; + } + for (i = 0; i < pdata->rx_ring_count; i++) { + *data++ = pdata->ext_stats.rxq_packets[i]; + *data++ = pdata->ext_stats.rxq_bytes[i]; + } +} + +static int xgbe_get_sset_count(struct net_device *netdev, int stringset) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + int ret; + + switch (stringset) { + case ETH_SS_STATS: + ret = XGBE_STATS_COUNT + + (pdata->tx_ring_count * 2) + + (pdata->rx_ring_count * 2); + break; + + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +static void xgbe_get_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *pause) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + + pause->autoneg = pdata->phy.pause_autoneg; + pause->tx_pause = pdata->phy.tx_pause; + pause->rx_pause = pdata->phy.rx_pause; +} + +static int xgbe_set_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *pause) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + int ret = 0; + + if (pause->autoneg && (pdata->phy.autoneg != AUTONEG_ENABLE)) { + netdev_err(netdev, + "autoneg disabled, pause autoneg not available\n"); + return -EINVAL; + } + + pdata->phy.pause_autoneg = pause->autoneg; + pdata->phy.tx_pause = pause->tx_pause; + pdata->phy.rx_pause = pause->rx_pause; + + XGBE_CLR_ADV(lks, Pause); + XGBE_CLR_ADV(lks, Asym_Pause); + + if (pause->rx_pause) { + XGBE_SET_ADV(lks, Pause); + XGBE_SET_ADV(lks, Asym_Pause); + } + + if (pause->tx_pause) { + /* Equivalent to XOR of Asym_Pause */ + if (XGBE_ADV(lks, Asym_Pause)) + XGBE_CLR_ADV(lks, Asym_Pause); + else + XGBE_SET_ADV(lks, Asym_Pause); + } + + if (netif_running(netdev)) + ret = pdata->phy_if.phy_config_aneg(pdata); + + return ret; +} + +static int xgbe_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + + cmd->base.phy_address = pdata->phy.address; + + if (netif_carrier_ok(netdev)) { + cmd->base.speed = pdata->phy.speed; + cmd->base.duplex = pdata->phy.duplex; + } else { + cmd->base.speed = SPEED_UNKNOWN; + cmd->base.duplex = DUPLEX_UNKNOWN; + } + + cmd->base.autoneg = pdata->phy.autoneg; + cmd->base.port = PORT_NONE; + + XGBE_LM_COPY(cmd, supported, lks, supported); + XGBE_LM_COPY(cmd, advertising, lks, advertising); + XGBE_LM_COPY(cmd, lp_advertising, lks, lp_advertising); + + return 0; +} + +static int xgbe_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *cmd) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising); + u32 speed; + int ret; + + speed = cmd->base.speed; + + if (cmd->base.phy_address != pdata->phy.address) { + netdev_err(netdev, "invalid phy address %hhu\n", + cmd->base.phy_address); + return -EINVAL; + } + + if ((cmd->base.autoneg != AUTONEG_ENABLE) && + (cmd->base.autoneg != AUTONEG_DISABLE)) { + netdev_err(netdev, "unsupported autoneg %hhu\n", + cmd->base.autoneg); + return -EINVAL; + } + + if (cmd->base.autoneg == AUTONEG_DISABLE) { + if (!pdata->phy_if.phy_valid_speed(pdata, speed)) { + netdev_err(netdev, "unsupported speed %u\n", speed); + return -EINVAL; + } + + if (cmd->base.duplex != DUPLEX_FULL) { + netdev_err(netdev, "unsupported duplex %hhu\n", + cmd->base.duplex); + return -EINVAL; + } + } + + netif_dbg(pdata, link, netdev, + "requested advertisement 0x%*pb, phy supported 0x%*pb\n", + __ETHTOOL_LINK_MODE_MASK_NBITS, cmd->link_modes.advertising, + __ETHTOOL_LINK_MODE_MASK_NBITS, lks->link_modes.supported); + + linkmode_and(advertising, cmd->link_modes.advertising, + lks->link_modes.supported); + + if ((cmd->base.autoneg == AUTONEG_ENABLE) && + bitmap_empty(advertising, __ETHTOOL_LINK_MODE_MASK_NBITS)) { + netdev_err(netdev, + "unsupported requested advertisement\n"); + return -EINVAL; + } + + ret = 0; + pdata->phy.autoneg = cmd->base.autoneg; + pdata->phy.speed = speed; + pdata->phy.duplex = cmd->base.duplex; + linkmode_copy(lks->link_modes.advertising, advertising); + + if (cmd->base.autoneg == AUTONEG_ENABLE) + XGBE_SET_ADV(lks, Autoneg); + else + XGBE_CLR_ADV(lks, Autoneg); + + if (netif_running(netdev)) + ret = pdata->phy_if.phy_config_aneg(pdata); + + return ret; +} + +static void xgbe_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *drvinfo) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct xgbe_hw_features *hw_feat = &pdata->hw_feat; + + strscpy(drvinfo->driver, XGBE_DRV_NAME, sizeof(drvinfo->driver)); + strscpy(drvinfo->bus_info, dev_name(pdata->dev), + sizeof(drvinfo->bus_info)); + snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), "%d.%d.%d", + XGMAC_GET_BITS(hw_feat->version, MAC_VR, USERVER), + XGMAC_GET_BITS(hw_feat->version, MAC_VR, DEVID), + XGMAC_GET_BITS(hw_feat->version, MAC_VR, SNPSVER)); +} + +static u32 xgbe_get_msglevel(struct net_device *netdev) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + + return pdata->msg_enable; +} + +static void xgbe_set_msglevel(struct net_device *netdev, u32 msglevel) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + + pdata->msg_enable = msglevel; +} + +static int xgbe_get_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + + memset(ec, 0, sizeof(struct ethtool_coalesce)); + + ec->rx_coalesce_usecs = pdata->rx_usecs; + ec->rx_max_coalesced_frames = pdata->rx_frames; + + ec->tx_max_coalesced_frames = pdata->tx_frames; + + return 0; +} + +static int xgbe_set_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct xgbe_hw_if *hw_if = &pdata->hw_if; + unsigned int rx_frames, rx_riwt, rx_usecs; + unsigned int tx_frames; + + rx_riwt = hw_if->usec_to_riwt(pdata, ec->rx_coalesce_usecs); + rx_usecs = ec->rx_coalesce_usecs; + rx_frames = ec->rx_max_coalesced_frames; + + /* Use smallest possible value if conversion resulted in zero */ + if (rx_usecs && !rx_riwt) + rx_riwt = 1; + + /* Check the bounds of values for Rx */ + if (rx_riwt > XGMAC_MAX_DMA_RIWT) { + netdev_err(netdev, "rx-usec is limited to %d usecs\n", + hw_if->riwt_to_usec(pdata, XGMAC_MAX_DMA_RIWT)); + return -EINVAL; + } + if (rx_frames > pdata->rx_desc_count) { + netdev_err(netdev, "rx-frames is limited to %d frames\n", + pdata->rx_desc_count); + return -EINVAL; + } + + tx_frames = ec->tx_max_coalesced_frames; + + /* Check the bounds of values for Tx */ + if (tx_frames > pdata->tx_desc_count) { + netdev_err(netdev, "tx-frames is limited to %d frames\n", + pdata->tx_desc_count); + return -EINVAL; + } + + pdata->rx_riwt = rx_riwt; + pdata->rx_usecs = rx_usecs; + pdata->rx_frames = rx_frames; + hw_if->config_rx_coalesce(pdata); + + pdata->tx_frames = tx_frames; + hw_if->config_tx_coalesce(pdata); + + return 0; +} + +static int xgbe_get_rxnfc(struct net_device *netdev, + struct ethtool_rxnfc *rxnfc, u32 *rule_locs) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + + switch (rxnfc->cmd) { + case ETHTOOL_GRXRINGS: + rxnfc->data = pdata->rx_ring_count; + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static u32 xgbe_get_rxfh_key_size(struct net_device *netdev) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + + return sizeof(pdata->rss_key); +} + +static u32 xgbe_get_rxfh_indir_size(struct net_device *netdev) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + + return ARRAY_SIZE(pdata->rss_table); +} + +static int xgbe_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, + u8 *hfunc) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + unsigned int i; + + if (indir) { + for (i = 0; i < ARRAY_SIZE(pdata->rss_table); i++) + indir[i] = XGMAC_GET_BITS(pdata->rss_table[i], + MAC_RSSDR, DMCH); + } + + if (key) + memcpy(key, pdata->rss_key, sizeof(pdata->rss_key)); + + if (hfunc) + *hfunc = ETH_RSS_HASH_TOP; + + return 0; +} + +static int xgbe_set_rxfh(struct net_device *netdev, const u32 *indir, + const u8 *key, const u8 hfunc) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct xgbe_hw_if *hw_if = &pdata->hw_if; + unsigned int ret; + + if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) { + netdev_err(netdev, "unsupported hash function\n"); + return -EOPNOTSUPP; + } + + if (indir) { + ret = hw_if->set_rss_lookup_table(pdata, indir); + if (ret) + return ret; + } + + if (key) { + ret = hw_if->set_rss_hash_key(pdata, key); + if (ret) + return ret; + } + + return 0; +} + +static int xgbe_get_ts_info(struct net_device *netdev, + struct ethtool_ts_info *ts_info) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + + ts_info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + + if (pdata->ptp_clock) + ts_info->phc_index = ptp_clock_index(pdata->ptp_clock); + else + ts_info->phc_index = -1; + + ts_info->tx_types = (1 << HWTSTAMP_TX_OFF) | (1 << HWTSTAMP_TX_ON); + ts_info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | + (1 << HWTSTAMP_FILTER_PTP_V1_L4_EVENT) | + (1 << HWTSTAMP_FILTER_PTP_V1_L4_SYNC) | + (1 << HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) | + (1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT) | + (1 << HWTSTAMP_FILTER_PTP_V2_L4_SYNC) | + (1 << HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ) | + (1 << HWTSTAMP_FILTER_PTP_V2_EVENT) | + (1 << HWTSTAMP_FILTER_PTP_V2_SYNC) | + (1 << HWTSTAMP_FILTER_PTP_V2_DELAY_REQ) | + (1 << HWTSTAMP_FILTER_ALL); + + return 0; +} + +static int xgbe_get_module_info(struct net_device *netdev, + struct ethtool_modinfo *modinfo) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + + return pdata->phy_if.module_info(pdata, modinfo); +} + +static int xgbe_get_module_eeprom(struct net_device *netdev, + struct ethtool_eeprom *eeprom, u8 *data) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + + return pdata->phy_if.module_eeprom(pdata, eeprom, data); +} + +static void +xgbe_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ringparam, + struct kernel_ethtool_ringparam *kernel_ringparam, + struct netlink_ext_ack *extack) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + + ringparam->rx_max_pending = XGBE_RX_DESC_CNT_MAX; + ringparam->tx_max_pending = XGBE_TX_DESC_CNT_MAX; + ringparam->rx_pending = pdata->rx_desc_count; + ringparam->tx_pending = pdata->tx_desc_count; +} + +static int xgbe_set_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ringparam, + struct kernel_ethtool_ringparam *kernel_ringparam, + struct netlink_ext_ack *extack) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + unsigned int rx, tx; + + if (ringparam->rx_mini_pending || ringparam->rx_jumbo_pending) { + netdev_err(netdev, "unsupported ring parameter\n"); + return -EINVAL; + } + + if ((ringparam->rx_pending < XGBE_RX_DESC_CNT_MIN) || + (ringparam->rx_pending > XGBE_RX_DESC_CNT_MAX)) { + netdev_err(netdev, + "rx ring parameter must be between %u and %u\n", + XGBE_RX_DESC_CNT_MIN, XGBE_RX_DESC_CNT_MAX); + return -EINVAL; + } + + if ((ringparam->tx_pending < XGBE_TX_DESC_CNT_MIN) || + (ringparam->tx_pending > XGBE_TX_DESC_CNT_MAX)) { + netdev_err(netdev, + "tx ring parameter must be between %u and %u\n", + XGBE_TX_DESC_CNT_MIN, XGBE_TX_DESC_CNT_MAX); + return -EINVAL; + } + + rx = __rounddown_pow_of_two(ringparam->rx_pending); + if (rx != ringparam->rx_pending) + netdev_notice(netdev, + "rx ring parameter rounded to power of two: %u\n", + rx); + + tx = __rounddown_pow_of_two(ringparam->tx_pending); + if (tx != ringparam->tx_pending) + netdev_notice(netdev, + "tx ring parameter rounded to power of two: %u\n", + tx); + + if ((rx == pdata->rx_desc_count) && + (tx == pdata->tx_desc_count)) + goto out; + + pdata->rx_desc_count = rx; + pdata->tx_desc_count = tx; + + xgbe_restart_dev(pdata); + +out: + return 0; +} + +static void xgbe_get_channels(struct net_device *netdev, + struct ethtool_channels *channels) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + unsigned int rx, tx, combined; + + /* Calculate maximums allowed: + * - Take into account the number of available IRQs + * - Do not take into account the number of online CPUs so that + * the user can over-subscribe if desired + * - Tx is additionally limited by the number of hardware queues + */ + rx = min(pdata->hw_feat.rx_ch_cnt, pdata->rx_max_channel_count); + rx = min(rx, pdata->channel_irq_count); + tx = min(pdata->hw_feat.tx_ch_cnt, pdata->tx_max_channel_count); + tx = min(tx, pdata->channel_irq_count); + tx = min(tx, pdata->tx_max_q_count); + + combined = min(rx, tx); + + channels->max_combined = combined; + channels->max_rx = rx ? rx - 1 : 0; + channels->max_tx = tx ? tx - 1 : 0; + + /* Get current settings based on device state */ + rx = pdata->new_rx_ring_count ? : pdata->rx_ring_count; + tx = pdata->new_tx_ring_count ? : pdata->tx_ring_count; + + combined = min(rx, tx); + rx -= combined; + tx -= combined; + + channels->combined_count = combined; + channels->rx_count = rx; + channels->tx_count = tx; +} + +static void xgbe_print_set_channels_input(struct net_device *netdev, + struct ethtool_channels *channels) +{ + netdev_err(netdev, "channel inputs: combined=%u, rx-only=%u, tx-only=%u\n", + channels->combined_count, channels->rx_count, + channels->tx_count); +} + +static int xgbe_set_channels(struct net_device *netdev, + struct ethtool_channels *channels) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + unsigned int rx, rx_curr, tx, tx_curr, combined; + + /* Calculate maximums allowed: + * - Take into account the number of available IRQs + * - Do not take into account the number of online CPUs so that + * the user can over-subscribe if desired + * - Tx is additionally limited by the number of hardware queues + */ + rx = min(pdata->hw_feat.rx_ch_cnt, pdata->rx_max_channel_count); + rx = min(rx, pdata->channel_irq_count); + tx = min(pdata->hw_feat.tx_ch_cnt, pdata->tx_max_channel_count); + tx = min(tx, pdata->tx_max_q_count); + tx = min(tx, pdata->channel_irq_count); + + combined = min(rx, tx); + + /* Should not be setting other count */ + if (channels->other_count) { + netdev_err(netdev, + "other channel count must be zero\n"); + return -EINVAL; + } + + /* Require at least one Combined (Rx and Tx) channel */ + if (!channels->combined_count) { + netdev_err(netdev, + "at least one combined Rx/Tx channel is required\n"); + xgbe_print_set_channels_input(netdev, channels); + return -EINVAL; + } + + /* Check combined channels */ + if (channels->combined_count > combined) { + netdev_err(netdev, + "combined channel count cannot exceed %u\n", + combined); + xgbe_print_set_channels_input(netdev, channels); + return -EINVAL; + } + + /* Can have some Rx-only or Tx-only channels, but not both */ + if (channels->rx_count && channels->tx_count) { + netdev_err(netdev, + "cannot specify both Rx-only and Tx-only channels\n"); + xgbe_print_set_channels_input(netdev, channels); + return -EINVAL; + } + + /* Check that we don't exceed the maximum number of channels */ + if ((channels->combined_count + channels->rx_count) > rx) { + netdev_err(netdev, + "total Rx channels (%u) requested exceeds maximum available (%u)\n", + channels->combined_count + channels->rx_count, rx); + xgbe_print_set_channels_input(netdev, channels); + return -EINVAL; + } + + if ((channels->combined_count + channels->tx_count) > tx) { + netdev_err(netdev, + "total Tx channels (%u) requested exceeds maximum available (%u)\n", + channels->combined_count + channels->tx_count, tx); + xgbe_print_set_channels_input(netdev, channels); + return -EINVAL; + } + + rx = channels->combined_count + channels->rx_count; + tx = channels->combined_count + channels->tx_count; + + rx_curr = pdata->new_rx_ring_count ? : pdata->rx_ring_count; + tx_curr = pdata->new_tx_ring_count ? : pdata->tx_ring_count; + + if ((rx == rx_curr) && (tx == tx_curr)) + goto out; + + pdata->new_rx_ring_count = rx; + pdata->new_tx_ring_count = tx; + + xgbe_full_restart_dev(pdata); + +out: + return 0; +} + +static const struct ethtool_ops xgbe_ethtool_ops = { + .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS | + ETHTOOL_COALESCE_MAX_FRAMES, + .get_drvinfo = xgbe_get_drvinfo, + .get_msglevel = xgbe_get_msglevel, + .set_msglevel = xgbe_set_msglevel, + .get_link = ethtool_op_get_link, + .get_coalesce = xgbe_get_coalesce, + .set_coalesce = xgbe_set_coalesce, + .get_pauseparam = xgbe_get_pauseparam, + .set_pauseparam = xgbe_set_pauseparam, + .get_strings = xgbe_get_strings, + .get_ethtool_stats = xgbe_get_ethtool_stats, + .get_sset_count = xgbe_get_sset_count, + .get_rxnfc = xgbe_get_rxnfc, + .get_rxfh_key_size = xgbe_get_rxfh_key_size, + .get_rxfh_indir_size = xgbe_get_rxfh_indir_size, + .get_rxfh = xgbe_get_rxfh, + .set_rxfh = xgbe_set_rxfh, + .get_ts_info = xgbe_get_ts_info, + .get_link_ksettings = xgbe_get_link_ksettings, + .set_link_ksettings = xgbe_set_link_ksettings, + .get_module_info = xgbe_get_module_info, + .get_module_eeprom = xgbe_get_module_eeprom, + .get_ringparam = xgbe_get_ringparam, + .set_ringparam = xgbe_set_ringparam, + .get_channels = xgbe_get_channels, + .set_channels = xgbe_set_channels, +}; + +const struct ethtool_ops *xgbe_get_ethtool_ops(void) +{ + return &xgbe_ethtool_ops; +} diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-i2c.c b/drivers/net/ethernet/amd/xgbe/xgbe-i2c.c new file mode 100644 index 000000000..a9ccc4258 --- /dev/null +++ b/drivers/net/ethernet/amd/xgbe/xgbe-i2c.c @@ -0,0 +1,514 @@ +/* + * AMD 10Gb Ethernet driver + * + * This file is available to you under your choice of the following two + * licenses: + * + * License 1: GPLv2 + * + * Copyright (c) 2016 Advanced Micro Devices, Inc. + * + * This file is free software; you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or (at + * your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * + * License 2: Modified BSD + * + * Copyright (c) 2016 Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/kmod.h> +#include <linux/delay.h> +#include <linux/completion.h> +#include <linux/mutex.h> + +#include "xgbe.h" +#include "xgbe-common.h" + +#define XGBE_ABORT_COUNT 500 +#define XGBE_DISABLE_COUNT 1000 + +#define XGBE_STD_SPEED 1 + +#define XGBE_INTR_RX_FULL BIT(IC_RAW_INTR_STAT_RX_FULL_INDEX) +#define XGBE_INTR_TX_EMPTY BIT(IC_RAW_INTR_STAT_TX_EMPTY_INDEX) +#define XGBE_INTR_TX_ABRT BIT(IC_RAW_INTR_STAT_TX_ABRT_INDEX) +#define XGBE_INTR_STOP_DET BIT(IC_RAW_INTR_STAT_STOP_DET_INDEX) +#define XGBE_DEFAULT_INT_MASK (XGBE_INTR_RX_FULL | \ + XGBE_INTR_TX_EMPTY | \ + XGBE_INTR_TX_ABRT | \ + XGBE_INTR_STOP_DET) + +#define XGBE_I2C_READ BIT(8) +#define XGBE_I2C_STOP BIT(9) + +static int xgbe_i2c_abort(struct xgbe_prv_data *pdata) +{ + unsigned int wait = XGBE_ABORT_COUNT; + + /* Must be enabled to recognize the abort request */ + XI2C_IOWRITE_BITS(pdata, IC_ENABLE, EN, 1); + + /* Issue the abort */ + XI2C_IOWRITE_BITS(pdata, IC_ENABLE, ABORT, 1); + + while (wait--) { + if (!XI2C_IOREAD_BITS(pdata, IC_ENABLE, ABORT)) + return 0; + + usleep_range(500, 600); + } + + return -EBUSY; +} + +static int xgbe_i2c_set_enable(struct xgbe_prv_data *pdata, bool enable) +{ + unsigned int wait = XGBE_DISABLE_COUNT; + unsigned int mode = enable ? 1 : 0; + + while (wait--) { + XI2C_IOWRITE_BITS(pdata, IC_ENABLE, EN, mode); + if (XI2C_IOREAD_BITS(pdata, IC_ENABLE_STATUS, EN) == mode) + return 0; + + usleep_range(100, 110); + } + + return -EBUSY; +} + +static int xgbe_i2c_disable(struct xgbe_prv_data *pdata) +{ + unsigned int ret; + + ret = xgbe_i2c_set_enable(pdata, false); + if (ret) { + /* Disable failed, try an abort */ + ret = xgbe_i2c_abort(pdata); + if (ret) + return ret; + + /* Abort succeeded, try to disable again */ + ret = xgbe_i2c_set_enable(pdata, false); + } + + return ret; +} + +static int xgbe_i2c_enable(struct xgbe_prv_data *pdata) +{ + return xgbe_i2c_set_enable(pdata, true); +} + +static void xgbe_i2c_clear_all_interrupts(struct xgbe_prv_data *pdata) +{ + XI2C_IOREAD(pdata, IC_CLR_INTR); +} + +static void xgbe_i2c_disable_interrupts(struct xgbe_prv_data *pdata) +{ + XI2C_IOWRITE(pdata, IC_INTR_MASK, 0); +} + +static void xgbe_i2c_enable_interrupts(struct xgbe_prv_data *pdata) +{ + XI2C_IOWRITE(pdata, IC_INTR_MASK, XGBE_DEFAULT_INT_MASK); +} + +static void xgbe_i2c_write(struct xgbe_prv_data *pdata) +{ + struct xgbe_i2c_op_state *state = &pdata->i2c.op_state; + unsigned int tx_slots; + unsigned int cmd; + + /* Configured to never receive Rx overflows, so fill up Tx fifo */ + tx_slots = pdata->i2c.tx_fifo_size - XI2C_IOREAD(pdata, IC_TXFLR); + while (tx_slots && state->tx_len) { + if (state->op->cmd == XGBE_I2C_CMD_READ) + cmd = XGBE_I2C_READ; + else + cmd = *state->tx_buf++; + + if (state->tx_len == 1) + XI2C_SET_BITS(cmd, IC_DATA_CMD, STOP, 1); + + XI2C_IOWRITE(pdata, IC_DATA_CMD, cmd); + + tx_slots--; + state->tx_len--; + } + + /* No more Tx operations, so ignore TX_EMPTY and return */ + if (!state->tx_len) + XI2C_IOWRITE_BITS(pdata, IC_INTR_MASK, TX_EMPTY, 0); +} + +static void xgbe_i2c_read(struct xgbe_prv_data *pdata) +{ + struct xgbe_i2c_op_state *state = &pdata->i2c.op_state; + unsigned int rx_slots; + + /* Anything to be read? */ + if (state->op->cmd != XGBE_I2C_CMD_READ) + return; + + rx_slots = XI2C_IOREAD(pdata, IC_RXFLR); + while (rx_slots && state->rx_len) { + *state->rx_buf++ = XI2C_IOREAD(pdata, IC_DATA_CMD); + state->rx_len--; + rx_slots--; + } +} + +static void xgbe_i2c_clear_isr_interrupts(struct xgbe_prv_data *pdata, + unsigned int isr) +{ + struct xgbe_i2c_op_state *state = &pdata->i2c.op_state; + + if (isr & XGBE_INTR_TX_ABRT) { + state->tx_abort_source = XI2C_IOREAD(pdata, IC_TX_ABRT_SOURCE); + XI2C_IOREAD(pdata, IC_CLR_TX_ABRT); + } + + if (isr & XGBE_INTR_STOP_DET) + XI2C_IOREAD(pdata, IC_CLR_STOP_DET); +} + +static void xgbe_i2c_isr_task(struct tasklet_struct *t) +{ + struct xgbe_prv_data *pdata = from_tasklet(pdata, t, tasklet_i2c); + struct xgbe_i2c_op_state *state = &pdata->i2c.op_state; + unsigned int isr; + + isr = XI2C_IOREAD(pdata, IC_RAW_INTR_STAT); + if (!isr) + goto reissue_check; + + netif_dbg(pdata, intr, pdata->netdev, + "I2C interrupt received: status=%#010x\n", isr); + + xgbe_i2c_clear_isr_interrupts(pdata, isr); + + if (isr & XGBE_INTR_TX_ABRT) { + netif_dbg(pdata, link, pdata->netdev, + "I2C TX_ABRT received (%#010x) for target %#04x\n", + state->tx_abort_source, state->op->target); + + xgbe_i2c_disable_interrupts(pdata); + + state->ret = -EIO; + goto out; + } + + /* Check for data in the Rx fifo */ + xgbe_i2c_read(pdata); + + /* Fill up the Tx fifo next */ + xgbe_i2c_write(pdata); + +out: + /* Complete on an error or STOP condition */ + if (state->ret || XI2C_GET_BITS(isr, IC_RAW_INTR_STAT, STOP_DET)) + complete(&pdata->i2c_complete); + +reissue_check: + /* Reissue interrupt if status is not clear */ + if (pdata->vdata->irq_reissue_support) + XP_IOWRITE(pdata, XP_INT_REISSUE_EN, 1 << 2); +} + +static irqreturn_t xgbe_i2c_isr(int irq, void *data) +{ + struct xgbe_prv_data *pdata = (struct xgbe_prv_data *)data; + + if (pdata->isr_as_tasklet) + tasklet_schedule(&pdata->tasklet_i2c); + else + xgbe_i2c_isr_task(&pdata->tasklet_i2c); + + return IRQ_HANDLED; +} + +static void xgbe_i2c_set_mode(struct xgbe_prv_data *pdata) +{ + unsigned int reg; + + reg = XI2C_IOREAD(pdata, IC_CON); + XI2C_SET_BITS(reg, IC_CON, MASTER_MODE, 1); + XI2C_SET_BITS(reg, IC_CON, SLAVE_DISABLE, 1); + XI2C_SET_BITS(reg, IC_CON, RESTART_EN, 1); + XI2C_SET_BITS(reg, IC_CON, SPEED, XGBE_STD_SPEED); + XI2C_SET_BITS(reg, IC_CON, RX_FIFO_FULL_HOLD, 1); + XI2C_IOWRITE(pdata, IC_CON, reg); +} + +static void xgbe_i2c_get_features(struct xgbe_prv_data *pdata) +{ + struct xgbe_i2c *i2c = &pdata->i2c; + unsigned int reg; + + reg = XI2C_IOREAD(pdata, IC_COMP_PARAM_1); + i2c->max_speed_mode = XI2C_GET_BITS(reg, IC_COMP_PARAM_1, + MAX_SPEED_MODE); + i2c->rx_fifo_size = XI2C_GET_BITS(reg, IC_COMP_PARAM_1, + RX_BUFFER_DEPTH); + i2c->tx_fifo_size = XI2C_GET_BITS(reg, IC_COMP_PARAM_1, + TX_BUFFER_DEPTH); + + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "I2C features: %s=%u, %s=%u, %s=%u\n", + "MAX_SPEED_MODE", i2c->max_speed_mode, + "RX_BUFFER_DEPTH", i2c->rx_fifo_size, + "TX_BUFFER_DEPTH", i2c->tx_fifo_size); +} + +static void xgbe_i2c_set_target(struct xgbe_prv_data *pdata, unsigned int addr) +{ + XI2C_IOWRITE(pdata, IC_TAR, addr); +} + +static irqreturn_t xgbe_i2c_combined_isr(struct xgbe_prv_data *pdata) +{ + xgbe_i2c_isr_task(&pdata->tasklet_i2c); + + return IRQ_HANDLED; +} + +static int xgbe_i2c_xfer(struct xgbe_prv_data *pdata, struct xgbe_i2c_op *op) +{ + struct xgbe_i2c_op_state *state = &pdata->i2c.op_state; + int ret; + + mutex_lock(&pdata->i2c_mutex); + + reinit_completion(&pdata->i2c_complete); + + ret = xgbe_i2c_disable(pdata); + if (ret) { + netdev_err(pdata->netdev, "failed to disable i2c master\n"); + goto unlock; + } + + xgbe_i2c_set_target(pdata, op->target); + + memset(state, 0, sizeof(*state)); + state->op = op; + state->tx_len = op->len; + state->tx_buf = op->buf; + state->rx_len = op->len; + state->rx_buf = op->buf; + + xgbe_i2c_clear_all_interrupts(pdata); + ret = xgbe_i2c_enable(pdata); + if (ret) { + netdev_err(pdata->netdev, "failed to enable i2c master\n"); + goto unlock; + } + + /* Enabling the interrupts will cause the TX FIFO empty interrupt to + * fire and begin to process the command via the ISR. + */ + xgbe_i2c_enable_interrupts(pdata); + + if (!wait_for_completion_timeout(&pdata->i2c_complete, HZ)) { + netdev_err(pdata->netdev, "i2c operation timed out\n"); + ret = -ETIMEDOUT; + goto disable; + } + + ret = state->ret; + if (ret) { + if (state->tx_abort_source & IC_TX_ABRT_7B_ADDR_NOACK) + ret = -ENOTCONN; + else if (state->tx_abort_source & IC_TX_ABRT_ARB_LOST) + ret = -EAGAIN; + } + +disable: + xgbe_i2c_disable_interrupts(pdata); + xgbe_i2c_disable(pdata); + +unlock: + mutex_unlock(&pdata->i2c_mutex); + + return ret; +} + +static void xgbe_i2c_stop(struct xgbe_prv_data *pdata) +{ + if (!pdata->i2c.started) + return; + + netif_dbg(pdata, link, pdata->netdev, "stopping I2C\n"); + + pdata->i2c.started = 0; + + xgbe_i2c_disable_interrupts(pdata); + xgbe_i2c_disable(pdata); + xgbe_i2c_clear_all_interrupts(pdata); + + if (pdata->dev_irq != pdata->i2c_irq) { + devm_free_irq(pdata->dev, pdata->i2c_irq, pdata); + tasklet_kill(&pdata->tasklet_i2c); + } +} + +static int xgbe_i2c_start(struct xgbe_prv_data *pdata) +{ + int ret; + + if (pdata->i2c.started) + return 0; + + netif_dbg(pdata, link, pdata->netdev, "starting I2C\n"); + + /* If we have a separate I2C irq, enable it */ + if (pdata->dev_irq != pdata->i2c_irq) { + tasklet_setup(&pdata->tasklet_i2c, xgbe_i2c_isr_task); + + ret = devm_request_irq(pdata->dev, pdata->i2c_irq, + xgbe_i2c_isr, 0, pdata->i2c_name, + pdata); + if (ret) { + netdev_err(pdata->netdev, "i2c irq request failed\n"); + return ret; + } + } + + pdata->i2c.started = 1; + + return 0; +} + +static int xgbe_i2c_init(struct xgbe_prv_data *pdata) +{ + int ret; + + xgbe_i2c_disable_interrupts(pdata); + + ret = xgbe_i2c_disable(pdata); + if (ret) { + dev_err(pdata->dev, "failed to disable i2c master\n"); + return ret; + } + + xgbe_i2c_get_features(pdata); + + xgbe_i2c_set_mode(pdata); + + xgbe_i2c_clear_all_interrupts(pdata); + + return 0; +} + +void xgbe_init_function_ptrs_i2c(struct xgbe_i2c_if *i2c_if) +{ + i2c_if->i2c_init = xgbe_i2c_init; + + i2c_if->i2c_start = xgbe_i2c_start; + i2c_if->i2c_stop = xgbe_i2c_stop; + + i2c_if->i2c_xfer = xgbe_i2c_xfer; + + i2c_if->i2c_isr = xgbe_i2c_combined_isr; +} diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c new file mode 100644 index 000000000..0e8698928 --- /dev/null +++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c @@ -0,0 +1,490 @@ +/* + * AMD 10Gb Ethernet driver + * + * This file is available to you under your choice of the following two + * licenses: + * + * License 1: GPLv2 + * + * Copyright (c) 2014-2016 Advanced Micro Devices, Inc. + * + * This file is free software; you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or (at + * your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * + * License 2: Modified BSD + * + * Copyright (c) 2014-2016 Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/spinlock.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/io.h> +#include <linux/notifier.h> + +#include "xgbe.h" +#include "xgbe-common.h" + +MODULE_AUTHOR("Tom Lendacky <thomas.lendacky@amd.com>"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION(XGBE_DRV_DESC); + +static int debug = -1; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, " Network interface message level setting"); + +static const u32 default_msg_level = (NETIF_MSG_LINK | NETIF_MSG_IFDOWN | + NETIF_MSG_IFUP); + +static void xgbe_default_config(struct xgbe_prv_data *pdata) +{ + DBGPR("-->xgbe_default_config\n"); + + pdata->blen = DMA_SBMR_BLEN_64; + pdata->pbl = DMA_PBL_128; + pdata->aal = 1; + pdata->rd_osr_limit = 8; + pdata->wr_osr_limit = 8; + pdata->tx_sf_mode = MTL_TSF_ENABLE; + pdata->tx_threshold = MTL_TX_THRESHOLD_64; + pdata->tx_osp_mode = DMA_OSP_ENABLE; + pdata->rx_sf_mode = MTL_RSF_DISABLE; + pdata->rx_threshold = MTL_RX_THRESHOLD_64; + pdata->pause_autoneg = 1; + pdata->tx_pause = 1; + pdata->rx_pause = 1; + pdata->phy_speed = SPEED_UNKNOWN; + pdata->power_down = 0; + + DBGPR("<--xgbe_default_config\n"); +} + +static void xgbe_init_all_fptrs(struct xgbe_prv_data *pdata) +{ + xgbe_init_function_ptrs_dev(&pdata->hw_if); + xgbe_init_function_ptrs_phy(&pdata->phy_if); + xgbe_init_function_ptrs_i2c(&pdata->i2c_if); + xgbe_init_function_ptrs_desc(&pdata->desc_if); + + pdata->vdata->init_function_ptrs_phy_impl(&pdata->phy_if); +} + +struct xgbe_prv_data *xgbe_alloc_pdata(struct device *dev) +{ + struct xgbe_prv_data *pdata; + struct net_device *netdev; + + netdev = alloc_etherdev_mq(sizeof(struct xgbe_prv_data), + XGBE_MAX_DMA_CHANNELS); + if (!netdev) { + dev_err(dev, "alloc_etherdev_mq failed\n"); + return ERR_PTR(-ENOMEM); + } + SET_NETDEV_DEV(netdev, dev); + pdata = netdev_priv(netdev); + pdata->netdev = netdev; + pdata->dev = dev; + + spin_lock_init(&pdata->lock); + spin_lock_init(&pdata->xpcs_lock); + mutex_init(&pdata->rss_mutex); + spin_lock_init(&pdata->tstamp_lock); + mutex_init(&pdata->i2c_mutex); + init_completion(&pdata->i2c_complete); + init_completion(&pdata->mdio_complete); + + pdata->msg_enable = netif_msg_init(debug, default_msg_level); + + set_bit(XGBE_DOWN, &pdata->dev_state); + set_bit(XGBE_STOPPED, &pdata->dev_state); + + return pdata; +} + +void xgbe_free_pdata(struct xgbe_prv_data *pdata) +{ + struct net_device *netdev = pdata->netdev; + + free_netdev(netdev); +} + +void xgbe_set_counts(struct xgbe_prv_data *pdata) +{ + /* Set all the function pointers */ + xgbe_init_all_fptrs(pdata); + + /* Populate the hardware features */ + xgbe_get_all_hw_features(pdata); + + /* Set default max values if not provided */ + if (!pdata->tx_max_channel_count) + pdata->tx_max_channel_count = pdata->hw_feat.tx_ch_cnt; + if (!pdata->rx_max_channel_count) + pdata->rx_max_channel_count = pdata->hw_feat.rx_ch_cnt; + + if (!pdata->tx_max_q_count) + pdata->tx_max_q_count = pdata->hw_feat.tx_q_cnt; + if (!pdata->rx_max_q_count) + pdata->rx_max_q_count = pdata->hw_feat.rx_q_cnt; + + /* Calculate the number of Tx and Rx rings to be created + * -Tx (DMA) Channels map 1-to-1 to Tx Queues so set + * the number of Tx queues to the number of Tx channels + * enabled + * -Rx (DMA) Channels do not map 1-to-1 so use the actual + * number of Rx queues or maximum allowed + */ + pdata->tx_ring_count = min_t(unsigned int, num_online_cpus(), + pdata->hw_feat.tx_ch_cnt); + pdata->tx_ring_count = min_t(unsigned int, pdata->tx_ring_count, + pdata->tx_max_channel_count); + pdata->tx_ring_count = min_t(unsigned int, pdata->tx_ring_count, + pdata->tx_max_q_count); + + pdata->tx_q_count = pdata->tx_ring_count; + + pdata->rx_ring_count = min_t(unsigned int, num_online_cpus(), + pdata->hw_feat.rx_ch_cnt); + pdata->rx_ring_count = min_t(unsigned int, pdata->rx_ring_count, + pdata->rx_max_channel_count); + + pdata->rx_q_count = min_t(unsigned int, pdata->hw_feat.rx_q_cnt, + pdata->rx_max_q_count); + + if (netif_msg_probe(pdata)) { + dev_dbg(pdata->dev, "TX/RX DMA channel count = %u/%u\n", + pdata->tx_ring_count, pdata->rx_ring_count); + dev_dbg(pdata->dev, "TX/RX hardware queue count = %u/%u\n", + pdata->tx_q_count, pdata->rx_q_count); + } +} + +int xgbe_config_netdev(struct xgbe_prv_data *pdata) +{ + struct net_device *netdev = pdata->netdev; + struct device *dev = pdata->dev; + int ret; + + netdev->irq = pdata->dev_irq; + netdev->base_addr = (unsigned long)pdata->xgmac_regs; + eth_hw_addr_set(netdev, pdata->mac_addr); + + /* Initialize ECC timestamps */ + pdata->tx_sec_period = jiffies; + pdata->tx_ded_period = jiffies; + pdata->rx_sec_period = jiffies; + pdata->rx_ded_period = jiffies; + pdata->desc_sec_period = jiffies; + pdata->desc_ded_period = jiffies; + + /* Issue software reset to device */ + ret = pdata->hw_if.exit(pdata); + if (ret) { + dev_err(dev, "software reset failed\n"); + return ret; + } + + /* Set default configuration data */ + xgbe_default_config(pdata); + + /* Set the DMA mask */ + ret = dma_set_mask_and_coherent(dev, + DMA_BIT_MASK(pdata->hw_feat.dma_width)); + if (ret) { + dev_err(dev, "dma_set_mask_and_coherent failed\n"); + return ret; + } + + /* Set default max values if not provided */ + if (!pdata->tx_max_fifo_size) + pdata->tx_max_fifo_size = pdata->hw_feat.tx_fifo_size; + if (!pdata->rx_max_fifo_size) + pdata->rx_max_fifo_size = pdata->hw_feat.rx_fifo_size; + + /* Set and validate the number of descriptors for a ring */ + BUILD_BUG_ON_NOT_POWER_OF_2(XGBE_TX_DESC_CNT); + pdata->tx_desc_count = XGBE_TX_DESC_CNT; + + BUILD_BUG_ON_NOT_POWER_OF_2(XGBE_RX_DESC_CNT); + pdata->rx_desc_count = XGBE_RX_DESC_CNT; + + /* Adjust the number of queues based on interrupts assigned */ + if (pdata->channel_irq_count) { + pdata->tx_ring_count = min_t(unsigned int, pdata->tx_ring_count, + pdata->channel_irq_count); + pdata->rx_ring_count = min_t(unsigned int, pdata->rx_ring_count, + pdata->channel_irq_count); + + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, + "adjusted TX/RX DMA channel count = %u/%u\n", + pdata->tx_ring_count, pdata->rx_ring_count); + } + + /* Initialize RSS hash key */ + netdev_rss_key_fill(pdata->rss_key, sizeof(pdata->rss_key)); + + XGMAC_SET_BITS(pdata->rss_options, MAC_RSSCR, IP2TE, 1); + XGMAC_SET_BITS(pdata->rss_options, MAC_RSSCR, TCP4TE, 1); + XGMAC_SET_BITS(pdata->rss_options, MAC_RSSCR, UDP4TE, 1); + + /* Call MDIO/PHY initialization routine */ + pdata->debugfs_an_cdr_workaround = pdata->vdata->an_cdr_workaround; + ret = pdata->phy_if.phy_init(pdata); + if (ret) + return ret; + + /* Set device operations */ + netdev->netdev_ops = xgbe_get_netdev_ops(); + netdev->ethtool_ops = xgbe_get_ethtool_ops(); +#ifdef CONFIG_AMD_XGBE_DCB + netdev->dcbnl_ops = xgbe_get_dcbnl_ops(); +#endif + + /* Set device features */ + netdev->hw_features = NETIF_F_SG | + NETIF_F_IP_CSUM | + NETIF_F_IPV6_CSUM | + NETIF_F_RXCSUM | + NETIF_F_TSO | + NETIF_F_TSO6 | + NETIF_F_GRO | + NETIF_F_HW_VLAN_CTAG_RX | + NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_HW_VLAN_CTAG_FILTER; + + if (pdata->hw_feat.rss) + netdev->hw_features |= NETIF_F_RXHASH; + + if (pdata->hw_feat.vxn) { + netdev->hw_enc_features = NETIF_F_SG | + NETIF_F_IP_CSUM | + NETIF_F_IPV6_CSUM | + NETIF_F_RXCSUM | + NETIF_F_TSO | + NETIF_F_TSO6 | + NETIF_F_GRO | + NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM; + + netdev->hw_features |= NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM; + + netdev->udp_tunnel_nic_info = xgbe_get_udp_tunnel_info(); + } + + netdev->vlan_features |= NETIF_F_SG | + NETIF_F_IP_CSUM | + NETIF_F_IPV6_CSUM | + NETIF_F_TSO | + NETIF_F_TSO6; + + netdev->features |= netdev->hw_features; + pdata->netdev_features = netdev->features; + + netdev->priv_flags |= IFF_UNICAST_FLT; + netdev->min_mtu = 0; + netdev->max_mtu = XGMAC_JUMBO_PACKET_MTU; + + /* Use default watchdog timeout */ + netdev->watchdog_timeo = 0; + + xgbe_init_rx_coalesce(pdata); + xgbe_init_tx_coalesce(pdata); + + netif_carrier_off(netdev); + ret = register_netdev(netdev); + if (ret) { + dev_err(dev, "net device registration failed\n"); + return ret; + } + + if (IS_REACHABLE(CONFIG_PTP_1588_CLOCK)) + xgbe_ptp_register(pdata); + + xgbe_debugfs_init(pdata); + + netif_dbg(pdata, drv, pdata->netdev, "%u Tx software queues\n", + pdata->tx_ring_count); + netif_dbg(pdata, drv, pdata->netdev, "%u Rx software queues\n", + pdata->rx_ring_count); + + return 0; +} + +void xgbe_deconfig_netdev(struct xgbe_prv_data *pdata) +{ + struct net_device *netdev = pdata->netdev; + + xgbe_debugfs_exit(pdata); + + if (IS_REACHABLE(CONFIG_PTP_1588_CLOCK)) + xgbe_ptp_unregister(pdata); + + unregister_netdev(netdev); + + pdata->phy_if.phy_exit(pdata); +} + +static int xgbe_netdev_event(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct net_device *netdev = netdev_notifier_info_to_dev(data); + struct xgbe_prv_data *pdata = netdev_priv(netdev); + + if (netdev->netdev_ops != xgbe_get_netdev_ops()) + goto out; + + switch (event) { + case NETDEV_CHANGENAME: + xgbe_debugfs_rename(pdata); + break; + + default: + break; + } + +out: + return NOTIFY_DONE; +} + +static struct notifier_block xgbe_netdev_notifier = { + .notifier_call = xgbe_netdev_event, +}; + +static int __init xgbe_mod_init(void) +{ + int ret; + + ret = register_netdevice_notifier(&xgbe_netdev_notifier); + if (ret) + return ret; + + ret = xgbe_platform_init(); + if (ret) + goto err_platform_init; + + ret = xgbe_pci_init(); + if (ret) + goto err_pci_init; + + return 0; + +err_pci_init: + xgbe_platform_exit(); +err_platform_init: + unregister_netdevice_notifier(&xgbe_netdev_notifier); + return ret; +} + +static void __exit xgbe_mod_exit(void) +{ + xgbe_pci_exit(); + + xgbe_platform_exit(); + + unregister_netdevice_notifier(&xgbe_netdev_notifier); +} + +module_init(xgbe_mod_init); +module_exit(xgbe_mod_exit); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c new file mode 100644 index 000000000..60be836b2 --- /dev/null +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -0,0 +1,1677 @@ +/* + * AMD 10Gb Ethernet driver + * + * This file is available to you under your choice of the following two + * licenses: + * + * License 1: GPLv2 + * + * Copyright (c) 2014-2016 Advanced Micro Devices, Inc. + * + * This file is free software; you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or (at + * your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * + * License 2: Modified BSD + * + * Copyright (c) 2014-2016 Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/kmod.h> +#include <linux/mdio.h> +#include <linux/phy.h> +#include <linux/of.h> +#include <linux/bitops.h> +#include <linux/jiffies.h> + +#include "xgbe.h" +#include "xgbe-common.h" + +static int xgbe_phy_module_eeprom(struct xgbe_prv_data *pdata, + struct ethtool_eeprom *eeprom, u8 *data) +{ + if (!pdata->phy_if.phy_impl.module_eeprom) + return -ENXIO; + + return pdata->phy_if.phy_impl.module_eeprom(pdata, eeprom, data); +} + +static int xgbe_phy_module_info(struct xgbe_prv_data *pdata, + struct ethtool_modinfo *modinfo) +{ + if (!pdata->phy_if.phy_impl.module_info) + return -ENXIO; + + return pdata->phy_if.phy_impl.module_info(pdata, modinfo); +} + +static void xgbe_an37_clear_interrupts(struct xgbe_prv_data *pdata) +{ + int reg; + + reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_STAT); + reg &= ~XGBE_AN_CL37_INT_MASK; + XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_STAT, reg); +} + +static void xgbe_an37_disable_interrupts(struct xgbe_prv_data *pdata) +{ + int reg; + + reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL); + reg &= ~XGBE_AN_CL37_INT_MASK; + XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL, reg); + + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_PCS_DIG_CTRL); + reg &= ~XGBE_PCS_CL37_BP; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_PCS_DIG_CTRL, reg); +} + +static void xgbe_an37_enable_interrupts(struct xgbe_prv_data *pdata) +{ + int reg; + + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_PCS_DIG_CTRL); + reg |= XGBE_PCS_CL37_BP; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_PCS_DIG_CTRL, reg); + + reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL); + reg |= XGBE_AN_CL37_INT_MASK; + XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL, reg); +} + +static void xgbe_an73_clear_interrupts(struct xgbe_prv_data *pdata) +{ + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, 0); +} + +static void xgbe_an73_disable_interrupts(struct xgbe_prv_data *pdata) +{ + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, 0); +} + +static void xgbe_an73_enable_interrupts(struct xgbe_prv_data *pdata) +{ + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, XGBE_AN_CL73_INT_MASK); +} + +static void xgbe_an_enable_interrupts(struct xgbe_prv_data *pdata) +{ + switch (pdata->an_mode) { + case XGBE_AN_MODE_CL73: + case XGBE_AN_MODE_CL73_REDRV: + xgbe_an73_enable_interrupts(pdata); + break; + case XGBE_AN_MODE_CL37: + case XGBE_AN_MODE_CL37_SGMII: + xgbe_an37_enable_interrupts(pdata); + break; + default: + break; + } +} + +static void xgbe_an_clear_interrupts_all(struct xgbe_prv_data *pdata) +{ + xgbe_an73_clear_interrupts(pdata); + xgbe_an37_clear_interrupts(pdata); +} + +static void xgbe_kr_mode(struct xgbe_prv_data *pdata) +{ + /* Set MAC to 10G speed */ + pdata->hw_if.set_speed(pdata, SPEED_10000); + + /* Call PHY implementation support to complete rate change */ + pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_KR); +} + +static void xgbe_kx_2500_mode(struct xgbe_prv_data *pdata) +{ + /* Set MAC to 2.5G speed */ + pdata->hw_if.set_speed(pdata, SPEED_2500); + + /* Call PHY implementation support to complete rate change */ + pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_KX_2500); +} + +static void xgbe_kx_1000_mode(struct xgbe_prv_data *pdata) +{ + /* Set MAC to 1G speed */ + pdata->hw_if.set_speed(pdata, SPEED_1000); + + /* Call PHY implementation support to complete rate change */ + pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_KX_1000); +} + +static void xgbe_sfi_mode(struct xgbe_prv_data *pdata) +{ + /* If a KR re-driver is present, change to KR mode instead */ + if (pdata->kr_redrv) + return xgbe_kr_mode(pdata); + + /* Set MAC to 10G speed */ + pdata->hw_if.set_speed(pdata, SPEED_10000); + + /* Call PHY implementation support to complete rate change */ + pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_SFI); +} + +static void xgbe_x_mode(struct xgbe_prv_data *pdata) +{ + /* Set MAC to 1G speed */ + pdata->hw_if.set_speed(pdata, SPEED_1000); + + /* Call PHY implementation support to complete rate change */ + pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_X); +} + +static void xgbe_sgmii_1000_mode(struct xgbe_prv_data *pdata) +{ + /* Set MAC to 1G speed */ + pdata->hw_if.set_speed(pdata, SPEED_1000); + + /* Call PHY implementation support to complete rate change */ + pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_SGMII_1000); +} + +static void xgbe_sgmii_100_mode(struct xgbe_prv_data *pdata) +{ + /* Set MAC to 1G speed */ + pdata->hw_if.set_speed(pdata, SPEED_1000); + + /* Call PHY implementation support to complete rate change */ + pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_SGMII_100); +} + +static enum xgbe_mode xgbe_cur_mode(struct xgbe_prv_data *pdata) +{ + return pdata->phy_if.phy_impl.cur_mode(pdata); +} + +static bool xgbe_in_kr_mode(struct xgbe_prv_data *pdata) +{ + return (xgbe_cur_mode(pdata) == XGBE_MODE_KR); +} + +static void xgbe_change_mode(struct xgbe_prv_data *pdata, + enum xgbe_mode mode) +{ + switch (mode) { + case XGBE_MODE_KX_1000: + xgbe_kx_1000_mode(pdata); + break; + case XGBE_MODE_KX_2500: + xgbe_kx_2500_mode(pdata); + break; + case XGBE_MODE_KR: + xgbe_kr_mode(pdata); + break; + case XGBE_MODE_SGMII_100: + xgbe_sgmii_100_mode(pdata); + break; + case XGBE_MODE_SGMII_1000: + xgbe_sgmii_1000_mode(pdata); + break; + case XGBE_MODE_X: + xgbe_x_mode(pdata); + break; + case XGBE_MODE_SFI: + xgbe_sfi_mode(pdata); + break; + case XGBE_MODE_UNKNOWN: + break; + default: + netif_dbg(pdata, link, pdata->netdev, + "invalid operation mode requested (%u)\n", mode); + } +} + +static void xgbe_switch_mode(struct xgbe_prv_data *pdata) +{ + xgbe_change_mode(pdata, pdata->phy_if.phy_impl.switch_mode(pdata)); +} + +static bool xgbe_set_mode(struct xgbe_prv_data *pdata, + enum xgbe_mode mode) +{ + if (mode == xgbe_cur_mode(pdata)) + return false; + + xgbe_change_mode(pdata, mode); + + return true; +} + +static bool xgbe_use_mode(struct xgbe_prv_data *pdata, + enum xgbe_mode mode) +{ + return pdata->phy_if.phy_impl.use_mode(pdata, mode); +} + +static void xgbe_an37_set(struct xgbe_prv_data *pdata, bool enable, + bool restart) +{ + unsigned int reg; + + reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_CTRL1); + reg &= ~MDIO_VEND2_CTRL1_AN_ENABLE; + + if (enable) + reg |= MDIO_VEND2_CTRL1_AN_ENABLE; + + if (restart) + reg |= MDIO_VEND2_CTRL1_AN_RESTART; + + XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_CTRL1, reg); +} + +static void xgbe_an37_restart(struct xgbe_prv_data *pdata) +{ + xgbe_an37_enable_interrupts(pdata); + xgbe_an37_set(pdata, true, true); + + netif_dbg(pdata, link, pdata->netdev, "CL37 AN enabled/restarted\n"); +} + +static void xgbe_an37_disable(struct xgbe_prv_data *pdata) +{ + xgbe_an37_set(pdata, false, false); + xgbe_an37_disable_interrupts(pdata); + + netif_dbg(pdata, link, pdata->netdev, "CL37 AN disabled\n"); +} + +static void xgbe_an73_set(struct xgbe_prv_data *pdata, bool enable, + bool restart) +{ + unsigned int reg; + + /* Disable KR training for now */ + reg = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL); + reg &= ~XGBE_KR_TRAINING_ENABLE; + XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, reg); + + /* Update AN settings */ + reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_CTRL1); + reg &= ~MDIO_AN_CTRL1_ENABLE; + + if (enable) + reg |= MDIO_AN_CTRL1_ENABLE; + + if (restart) + reg |= MDIO_AN_CTRL1_RESTART; + + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_CTRL1, reg); +} + +static void xgbe_an73_restart(struct xgbe_prv_data *pdata) +{ + xgbe_an73_enable_interrupts(pdata); + xgbe_an73_set(pdata, true, true); + + netif_dbg(pdata, link, pdata->netdev, "CL73 AN enabled/restarted\n"); +} + +static void xgbe_an73_disable(struct xgbe_prv_data *pdata) +{ + xgbe_an73_set(pdata, false, false); + xgbe_an73_disable_interrupts(pdata); + + pdata->an_start = 0; + + netif_dbg(pdata, link, pdata->netdev, "CL73 AN disabled\n"); +} + +static void xgbe_an_restart(struct xgbe_prv_data *pdata) +{ + if (pdata->phy_if.phy_impl.an_pre) + pdata->phy_if.phy_impl.an_pre(pdata); + + switch (pdata->an_mode) { + case XGBE_AN_MODE_CL73: + case XGBE_AN_MODE_CL73_REDRV: + xgbe_an73_restart(pdata); + break; + case XGBE_AN_MODE_CL37: + case XGBE_AN_MODE_CL37_SGMII: + xgbe_an37_restart(pdata); + break; + default: + break; + } +} + +static void xgbe_an_disable(struct xgbe_prv_data *pdata) +{ + if (pdata->phy_if.phy_impl.an_post) + pdata->phy_if.phy_impl.an_post(pdata); + + switch (pdata->an_mode) { + case XGBE_AN_MODE_CL73: + case XGBE_AN_MODE_CL73_REDRV: + xgbe_an73_disable(pdata); + break; + case XGBE_AN_MODE_CL37: + case XGBE_AN_MODE_CL37_SGMII: + xgbe_an37_disable(pdata); + break; + default: + break; + } +} + +static void xgbe_an_disable_all(struct xgbe_prv_data *pdata) +{ + xgbe_an73_disable(pdata); + xgbe_an37_disable(pdata); +} + +static enum xgbe_an xgbe_an73_tx_training(struct xgbe_prv_data *pdata, + enum xgbe_rx *state) +{ + unsigned int ad_reg, lp_reg, reg; + + *state = XGBE_RX_COMPLETE; + + /* If we're not in KR mode then we're done */ + if (!xgbe_in_kr_mode(pdata)) + return XGBE_AN_PAGE_RECEIVED; + + /* Enable/Disable FEC */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2); + + reg = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FECCTRL); + reg &= ~(MDIO_PMA_10GBR_FECABLE_ABLE | MDIO_PMA_10GBR_FECABLE_ERRABLE); + if ((ad_reg & 0xc000) && (lp_reg & 0xc000)) + reg |= pdata->fec_ability; + + XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FECCTRL, reg); + + /* Start KR training */ + if (pdata->phy_if.phy_impl.kr_training_pre) + pdata->phy_if.phy_impl.kr_training_pre(pdata); + + reg = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL); + reg |= XGBE_KR_TRAINING_ENABLE; + reg |= XGBE_KR_TRAINING_START; + XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, reg); + pdata->kr_start_time = jiffies; + + netif_dbg(pdata, link, pdata->netdev, + "KR training initiated\n"); + + if (pdata->phy_if.phy_impl.kr_training_post) + pdata->phy_if.phy_impl.kr_training_post(pdata); + + return XGBE_AN_PAGE_RECEIVED; +} + +static enum xgbe_an xgbe_an73_tx_xnp(struct xgbe_prv_data *pdata, + enum xgbe_rx *state) +{ + u16 msg; + + *state = XGBE_RX_XNP; + + msg = XGBE_XNP_MCF_NULL_MESSAGE; + msg |= XGBE_XNP_MP_FORMATTED; + + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_XNP + 2, 0); + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_XNP + 1, 0); + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_XNP, msg); + + return XGBE_AN_PAGE_RECEIVED; +} + +static enum xgbe_an xgbe_an73_rx_bpa(struct xgbe_prv_data *pdata, + enum xgbe_rx *state) +{ + unsigned int link_support; + unsigned int reg, ad_reg, lp_reg; + + /* Read Base Ability register 2 first */ + reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1); + + /* Check for a supported mode, otherwise restart in a different one */ + link_support = xgbe_in_kr_mode(pdata) ? 0x80 : 0x20; + if (!(reg & link_support)) + return XGBE_AN_INCOMPAT_LINK; + + /* Check Extended Next Page support */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA); + + return ((ad_reg & XGBE_XNP_NP_EXCHANGE) || + (lp_reg & XGBE_XNP_NP_EXCHANGE)) + ? xgbe_an73_tx_xnp(pdata, state) + : xgbe_an73_tx_training(pdata, state); +} + +static enum xgbe_an xgbe_an73_rx_xnp(struct xgbe_prv_data *pdata, + enum xgbe_rx *state) +{ + unsigned int ad_reg, lp_reg; + + /* Check Extended Next Page support */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_XNP); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPX); + + return ((ad_reg & XGBE_XNP_NP_EXCHANGE) || + (lp_reg & XGBE_XNP_NP_EXCHANGE)) + ? xgbe_an73_tx_xnp(pdata, state) + : xgbe_an73_tx_training(pdata, state); +} + +static enum xgbe_an xgbe_an73_page_received(struct xgbe_prv_data *pdata) +{ + enum xgbe_rx *state; + unsigned long an_timeout; + enum xgbe_an ret; + + if (!pdata->an_start) { + pdata->an_start = jiffies; + } else { + an_timeout = pdata->an_start + + msecs_to_jiffies(XGBE_AN_MS_TIMEOUT); + if (time_after(jiffies, an_timeout)) { + /* Auto-negotiation timed out, reset state */ + pdata->kr_state = XGBE_RX_BPA; + pdata->kx_state = XGBE_RX_BPA; + + pdata->an_start = jiffies; + + netif_dbg(pdata, link, pdata->netdev, + "CL73 AN timed out, resetting state\n"); + } + } + + state = xgbe_in_kr_mode(pdata) ? &pdata->kr_state + : &pdata->kx_state; + + switch (*state) { + case XGBE_RX_BPA: + ret = xgbe_an73_rx_bpa(pdata, state); + break; + + case XGBE_RX_XNP: + ret = xgbe_an73_rx_xnp(pdata, state); + break; + + default: + ret = XGBE_AN_ERROR; + } + + return ret; +} + +static enum xgbe_an xgbe_an73_incompat_link(struct xgbe_prv_data *pdata) +{ + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + + /* Be sure we aren't looping trying to negotiate */ + if (xgbe_in_kr_mode(pdata)) { + pdata->kr_state = XGBE_RX_ERROR; + + if (!XGBE_ADV(lks, 1000baseKX_Full) && + !XGBE_ADV(lks, 2500baseX_Full)) + return XGBE_AN_NO_LINK; + + if (pdata->kx_state != XGBE_RX_BPA) + return XGBE_AN_NO_LINK; + } else { + pdata->kx_state = XGBE_RX_ERROR; + + if (!XGBE_ADV(lks, 10000baseKR_Full)) + return XGBE_AN_NO_LINK; + + if (pdata->kr_state != XGBE_RX_BPA) + return XGBE_AN_NO_LINK; + } + + xgbe_an_disable(pdata); + + xgbe_switch_mode(pdata); + + pdata->an_result = XGBE_AN_READY; + + xgbe_an_restart(pdata); + + return XGBE_AN_INCOMPAT_LINK; +} + +static void xgbe_an37_isr(struct xgbe_prv_data *pdata) +{ + unsigned int reg; + + /* Disable AN interrupts */ + xgbe_an37_disable_interrupts(pdata); + + /* Save the interrupt(s) that fired */ + reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_STAT); + pdata->an_int = reg & XGBE_AN_CL37_INT_MASK; + pdata->an_status = reg & ~XGBE_AN_CL37_INT_MASK; + + if (pdata->an_int) { + /* Clear the interrupt(s) that fired and process them */ + reg &= ~XGBE_AN_CL37_INT_MASK; + XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_STAT, reg); + + queue_work(pdata->an_workqueue, &pdata->an_irq_work); + } else { + /* Enable AN interrupts */ + xgbe_an37_enable_interrupts(pdata); + + /* Reissue interrupt if status is not clear */ + if (pdata->vdata->irq_reissue_support) + XP_IOWRITE(pdata, XP_INT_REISSUE_EN, 1 << 3); + } +} + +static void xgbe_an73_isr(struct xgbe_prv_data *pdata) +{ + /* Disable AN interrupts */ + xgbe_an73_disable_interrupts(pdata); + + /* Save the interrupt(s) that fired */ + pdata->an_int = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_INT); + + if (pdata->an_int) { + /* Clear the interrupt(s) that fired and process them */ + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, ~pdata->an_int); + + queue_work(pdata->an_workqueue, &pdata->an_irq_work); + } else { + /* Enable AN interrupts */ + xgbe_an73_enable_interrupts(pdata); + + /* Reissue interrupt if status is not clear */ + if (pdata->vdata->irq_reissue_support) + XP_IOWRITE(pdata, XP_INT_REISSUE_EN, 1 << 3); + } +} + +static void xgbe_an_isr_task(struct tasklet_struct *t) +{ + struct xgbe_prv_data *pdata = from_tasklet(pdata, t, tasklet_an); + + netif_dbg(pdata, intr, pdata->netdev, "AN interrupt received\n"); + + switch (pdata->an_mode) { + case XGBE_AN_MODE_CL73: + case XGBE_AN_MODE_CL73_REDRV: + xgbe_an73_isr(pdata); + break; + case XGBE_AN_MODE_CL37: + case XGBE_AN_MODE_CL37_SGMII: + xgbe_an37_isr(pdata); + break; + default: + break; + } +} + +static irqreturn_t xgbe_an_isr(int irq, void *data) +{ + struct xgbe_prv_data *pdata = (struct xgbe_prv_data *)data; + + if (pdata->isr_as_tasklet) + tasklet_schedule(&pdata->tasklet_an); + else + xgbe_an_isr_task(&pdata->tasklet_an); + + return IRQ_HANDLED; +} + +static irqreturn_t xgbe_an_combined_isr(struct xgbe_prv_data *pdata) +{ + xgbe_an_isr_task(&pdata->tasklet_an); + + return IRQ_HANDLED; +} + +static void xgbe_an_irq_work(struct work_struct *work) +{ + struct xgbe_prv_data *pdata = container_of(work, + struct xgbe_prv_data, + an_irq_work); + + /* Avoid a race between enabling the IRQ and exiting the work by + * waiting for the work to finish and then queueing it + */ + flush_work(&pdata->an_work); + queue_work(pdata->an_workqueue, &pdata->an_work); +} + +static const char *xgbe_state_as_string(enum xgbe_an state) +{ + switch (state) { + case XGBE_AN_READY: + return "Ready"; + case XGBE_AN_PAGE_RECEIVED: + return "Page-Received"; + case XGBE_AN_INCOMPAT_LINK: + return "Incompatible-Link"; + case XGBE_AN_COMPLETE: + return "Complete"; + case XGBE_AN_NO_LINK: + return "No-Link"; + case XGBE_AN_ERROR: + return "Error"; + default: + return "Undefined"; + } +} + +static void xgbe_an37_state_machine(struct xgbe_prv_data *pdata) +{ + enum xgbe_an cur_state = pdata->an_state; + + if (!pdata->an_int) + return; + + if (pdata->an_int & XGBE_AN_CL37_INT_CMPLT) { + pdata->an_state = XGBE_AN_COMPLETE; + pdata->an_int &= ~XGBE_AN_CL37_INT_CMPLT; + + /* If SGMII is enabled, check the link status */ + if ((pdata->an_mode == XGBE_AN_MODE_CL37_SGMII) && + !(pdata->an_status & XGBE_SGMII_AN_LINK_STATUS)) + pdata->an_state = XGBE_AN_NO_LINK; + } + + netif_dbg(pdata, link, pdata->netdev, "CL37 AN %s\n", + xgbe_state_as_string(pdata->an_state)); + + cur_state = pdata->an_state; + + switch (pdata->an_state) { + case XGBE_AN_READY: + break; + + case XGBE_AN_COMPLETE: + netif_dbg(pdata, link, pdata->netdev, + "Auto negotiation successful\n"); + break; + + case XGBE_AN_NO_LINK: + break; + + default: + pdata->an_state = XGBE_AN_ERROR; + } + + if (pdata->an_state == XGBE_AN_ERROR) { + netdev_err(pdata->netdev, + "error during auto-negotiation, state=%u\n", + cur_state); + + pdata->an_int = 0; + xgbe_an37_clear_interrupts(pdata); + } + + if (pdata->an_state >= XGBE_AN_COMPLETE) { + pdata->an_result = pdata->an_state; + pdata->an_state = XGBE_AN_READY; + + if (pdata->phy_if.phy_impl.an_post) + pdata->phy_if.phy_impl.an_post(pdata); + + netif_dbg(pdata, link, pdata->netdev, "CL37 AN result: %s\n", + xgbe_state_as_string(pdata->an_result)); + } + + xgbe_an37_enable_interrupts(pdata); +} + +static void xgbe_an73_state_machine(struct xgbe_prv_data *pdata) +{ + enum xgbe_an cur_state = pdata->an_state; + + if (!pdata->an_int) + return; + +next_int: + if (pdata->an_int & XGBE_AN_CL73_PG_RCV) { + pdata->an_state = XGBE_AN_PAGE_RECEIVED; + pdata->an_int &= ~XGBE_AN_CL73_PG_RCV; + } else if (pdata->an_int & XGBE_AN_CL73_INC_LINK) { + pdata->an_state = XGBE_AN_INCOMPAT_LINK; + pdata->an_int &= ~XGBE_AN_CL73_INC_LINK; + } else if (pdata->an_int & XGBE_AN_CL73_INT_CMPLT) { + pdata->an_state = XGBE_AN_COMPLETE; + pdata->an_int &= ~XGBE_AN_CL73_INT_CMPLT; + } else { + pdata->an_state = XGBE_AN_ERROR; + } + +again: + netif_dbg(pdata, link, pdata->netdev, "CL73 AN %s\n", + xgbe_state_as_string(pdata->an_state)); + + cur_state = pdata->an_state; + + switch (pdata->an_state) { + case XGBE_AN_READY: + pdata->an_supported = 0; + break; + + case XGBE_AN_PAGE_RECEIVED: + pdata->an_state = xgbe_an73_page_received(pdata); + pdata->an_supported++; + break; + + case XGBE_AN_INCOMPAT_LINK: + pdata->an_supported = 0; + pdata->parallel_detect = 0; + pdata->an_state = xgbe_an73_incompat_link(pdata); + break; + + case XGBE_AN_COMPLETE: + pdata->parallel_detect = pdata->an_supported ? 0 : 1; + netif_dbg(pdata, link, pdata->netdev, "%s successful\n", + pdata->an_supported ? "Auto negotiation" + : "Parallel detection"); + break; + + case XGBE_AN_NO_LINK: + break; + + default: + pdata->an_state = XGBE_AN_ERROR; + } + + if (pdata->an_state == XGBE_AN_NO_LINK) { + pdata->an_int = 0; + xgbe_an73_clear_interrupts(pdata); + } else if (pdata->an_state == XGBE_AN_ERROR) { + netdev_err(pdata->netdev, + "error during auto-negotiation, state=%u\n", + cur_state); + + pdata->an_int = 0; + xgbe_an73_clear_interrupts(pdata); + } + + if (pdata->an_state >= XGBE_AN_COMPLETE) { + pdata->an_result = pdata->an_state; + pdata->an_state = XGBE_AN_READY; + pdata->kr_state = XGBE_RX_BPA; + pdata->kx_state = XGBE_RX_BPA; + pdata->an_start = 0; + + if (pdata->phy_if.phy_impl.an_post) + pdata->phy_if.phy_impl.an_post(pdata); + + netif_dbg(pdata, link, pdata->netdev, "CL73 AN result: %s\n", + xgbe_state_as_string(pdata->an_result)); + } + + if (cur_state != pdata->an_state) + goto again; + + if (pdata->an_int) + goto next_int; + + xgbe_an73_enable_interrupts(pdata); +} + +static void xgbe_an_state_machine(struct work_struct *work) +{ + struct xgbe_prv_data *pdata = container_of(work, + struct xgbe_prv_data, + an_work); + + mutex_lock(&pdata->an_mutex); + + switch (pdata->an_mode) { + case XGBE_AN_MODE_CL73: + case XGBE_AN_MODE_CL73_REDRV: + xgbe_an73_state_machine(pdata); + break; + case XGBE_AN_MODE_CL37: + case XGBE_AN_MODE_CL37_SGMII: + xgbe_an37_state_machine(pdata); + break; + default: + break; + } + + /* Reissue interrupt if status is not clear */ + if (pdata->vdata->irq_reissue_support) + XP_IOWRITE(pdata, XP_INT_REISSUE_EN, 1 << 3); + + mutex_unlock(&pdata->an_mutex); +} + +static void xgbe_an37_init(struct xgbe_prv_data *pdata) +{ + struct ethtool_link_ksettings lks; + unsigned int reg; + + pdata->phy_if.phy_impl.an_advertising(pdata, &lks); + + /* Set up Advertisement register */ + reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_ADVERTISE); + if (XGBE_ADV(&lks, Pause)) + reg |= 0x100; + else + reg &= ~0x100; + + if (XGBE_ADV(&lks, Asym_Pause)) + reg |= 0x80; + else + reg &= ~0x80; + + /* Full duplex, but not half */ + reg |= XGBE_AN_CL37_FD_MASK; + reg &= ~XGBE_AN_CL37_HD_MASK; + + XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_ADVERTISE, reg); + + /* Set up the Control register */ + reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL); + reg &= ~XGBE_AN_CL37_TX_CONFIG_MASK; + reg &= ~XGBE_AN_CL37_PCS_MODE_MASK; + + switch (pdata->an_mode) { + case XGBE_AN_MODE_CL37: + reg |= XGBE_AN_CL37_PCS_MODE_BASEX; + break; + case XGBE_AN_MODE_CL37_SGMII: + reg |= XGBE_AN_CL37_PCS_MODE_SGMII; + break; + default: + break; + } + + reg |= XGBE_AN_CL37_MII_CTRL_8BIT; + + XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL, reg); + + netif_dbg(pdata, link, pdata->netdev, "CL37 AN (%s) initialized\n", + (pdata->an_mode == XGBE_AN_MODE_CL37) ? "BaseX" : "SGMII"); +} + +static void xgbe_an73_init(struct xgbe_prv_data *pdata) +{ + struct ethtool_link_ksettings lks; + unsigned int reg; + + pdata->phy_if.phy_impl.an_advertising(pdata, &lks); + + /* Set up Advertisement register 3 first */ + reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); + if (XGBE_ADV(&lks, 10000baseR_FEC)) + reg |= 0xc000; + else + reg &= ~0xc000; + + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2, reg); + + /* Set up Advertisement register 2 next */ + reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); + if (XGBE_ADV(&lks, 10000baseKR_Full)) + reg |= 0x80; + else + reg &= ~0x80; + + if (XGBE_ADV(&lks, 1000baseKX_Full) || + XGBE_ADV(&lks, 2500baseX_Full)) + reg |= 0x20; + else + reg &= ~0x20; + + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1, reg); + + /* Set up Advertisement register 1 last */ + reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); + if (XGBE_ADV(&lks, Pause)) + reg |= 0x400; + else + reg &= ~0x400; + + if (XGBE_ADV(&lks, Asym_Pause)) + reg |= 0x800; + else + reg &= ~0x800; + + /* We don't intend to perform XNP */ + reg &= ~XGBE_XNP_NP_EXCHANGE; + + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE, reg); + + netif_dbg(pdata, link, pdata->netdev, "CL73 AN initialized\n"); +} + +static void xgbe_an_init(struct xgbe_prv_data *pdata) +{ + /* Set up advertisement registers based on current settings */ + pdata->an_mode = pdata->phy_if.phy_impl.an_mode(pdata); + switch (pdata->an_mode) { + case XGBE_AN_MODE_CL73: + case XGBE_AN_MODE_CL73_REDRV: + xgbe_an73_init(pdata); + break; + case XGBE_AN_MODE_CL37: + case XGBE_AN_MODE_CL37_SGMII: + xgbe_an37_init(pdata); + break; + default: + break; + } +} + +static const char *xgbe_phy_fc_string(struct xgbe_prv_data *pdata) +{ + if (pdata->tx_pause && pdata->rx_pause) + return "rx/tx"; + else if (pdata->rx_pause) + return "rx"; + else if (pdata->tx_pause) + return "tx"; + else + return "off"; +} + +static const char *xgbe_phy_speed_string(int speed) +{ + switch (speed) { + case SPEED_100: + return "100Mbps"; + case SPEED_1000: + return "1Gbps"; + case SPEED_2500: + return "2.5Gbps"; + case SPEED_10000: + return "10Gbps"; + case SPEED_UNKNOWN: + return "Unknown"; + default: + return "Unsupported"; + } +} + +static void xgbe_phy_print_status(struct xgbe_prv_data *pdata) +{ + if (pdata->phy.link) + netdev_info(pdata->netdev, + "Link is Up - %s/%s - flow control %s\n", + xgbe_phy_speed_string(pdata->phy.speed), + pdata->phy.duplex == DUPLEX_FULL ? "Full" : "Half", + xgbe_phy_fc_string(pdata)); + else + netdev_info(pdata->netdev, "Link is Down\n"); +} + +static void xgbe_phy_adjust_link(struct xgbe_prv_data *pdata) +{ + int new_state = 0; + + if (pdata->phy.link) { + /* Flow control support */ + pdata->pause_autoneg = pdata->phy.pause_autoneg; + + if (pdata->tx_pause != pdata->phy.tx_pause) { + new_state = 1; + pdata->tx_pause = pdata->phy.tx_pause; + pdata->hw_if.config_tx_flow_control(pdata); + } + + if (pdata->rx_pause != pdata->phy.rx_pause) { + new_state = 1; + pdata->rx_pause = pdata->phy.rx_pause; + pdata->hw_if.config_rx_flow_control(pdata); + } + + /* Speed support */ + if (pdata->phy_speed != pdata->phy.speed) { + new_state = 1; + pdata->phy_speed = pdata->phy.speed; + } + + if (pdata->phy_link != pdata->phy.link) { + new_state = 1; + pdata->phy_link = pdata->phy.link; + } + } else if (pdata->phy_link) { + new_state = 1; + pdata->phy_link = 0; + pdata->phy_speed = SPEED_UNKNOWN; + } + + if (new_state && netif_msg_link(pdata)) + xgbe_phy_print_status(pdata); +} + +static bool xgbe_phy_valid_speed(struct xgbe_prv_data *pdata, int speed) +{ + return pdata->phy_if.phy_impl.valid_speed(pdata, speed); +} + +static int xgbe_phy_config_fixed(struct xgbe_prv_data *pdata) +{ + enum xgbe_mode mode; + + netif_dbg(pdata, link, pdata->netdev, "fixed PHY configuration\n"); + + /* Disable auto-negotiation */ + xgbe_an_disable(pdata); + + /* Set specified mode for specified speed */ + mode = pdata->phy_if.phy_impl.get_mode(pdata, pdata->phy.speed); + switch (mode) { + case XGBE_MODE_KX_1000: + case XGBE_MODE_KX_2500: + case XGBE_MODE_KR: + case XGBE_MODE_SGMII_100: + case XGBE_MODE_SGMII_1000: + case XGBE_MODE_X: + case XGBE_MODE_SFI: + break; + case XGBE_MODE_UNKNOWN: + default: + return -EINVAL; + } + + /* Validate duplex mode */ + if (pdata->phy.duplex != DUPLEX_FULL) + return -EINVAL; + + /* Force the mode change for SFI in Fixed PHY config. + * Fixed PHY configs needs PLL to be enabled while doing mode set. + * When the SFP module isn't connected during boot, driver assumes + * AN is ON and attempts autonegotiation. However, if the connected + * SFP comes up in Fixed PHY config, the link will not come up as + * PLL isn't enabled while the initial mode set command is issued. + * So, force the mode change for SFI in Fixed PHY configuration to + * fix link issues. + */ + if (mode == XGBE_MODE_SFI) + xgbe_change_mode(pdata, mode); + else + xgbe_set_mode(pdata, mode); + + return 0; +} + +static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata, bool set_mode) +{ + int ret; + + mutex_lock(&pdata->an_mutex); + + set_bit(XGBE_LINK_INIT, &pdata->dev_state); + pdata->link_check = jiffies; + + ret = pdata->phy_if.phy_impl.an_config(pdata); + if (ret) + goto out; + + if (pdata->phy.autoneg != AUTONEG_ENABLE) { + ret = xgbe_phy_config_fixed(pdata); + if (ret || !pdata->kr_redrv) + goto out; + + netif_dbg(pdata, link, pdata->netdev, "AN redriver support\n"); + } else { + netif_dbg(pdata, link, pdata->netdev, "AN PHY configuration\n"); + } + + /* Disable auto-negotiation interrupt */ + disable_irq(pdata->an_irq); + + if (set_mode) { + /* Start auto-negotiation in a supported mode */ + if (xgbe_use_mode(pdata, XGBE_MODE_KR)) { + xgbe_set_mode(pdata, XGBE_MODE_KR); + } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_2500)) { + xgbe_set_mode(pdata, XGBE_MODE_KX_2500); + } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_1000)) { + xgbe_set_mode(pdata, XGBE_MODE_KX_1000); + } else if (xgbe_use_mode(pdata, XGBE_MODE_SFI)) { + xgbe_set_mode(pdata, XGBE_MODE_SFI); + } else if (xgbe_use_mode(pdata, XGBE_MODE_X)) { + xgbe_set_mode(pdata, XGBE_MODE_X); + } else if (xgbe_use_mode(pdata, XGBE_MODE_SGMII_1000)) { + xgbe_set_mode(pdata, XGBE_MODE_SGMII_1000); + } else if (xgbe_use_mode(pdata, XGBE_MODE_SGMII_100)) { + xgbe_set_mode(pdata, XGBE_MODE_SGMII_100); + } else { + enable_irq(pdata->an_irq); + ret = -EINVAL; + goto out; + } + } + + /* Disable and stop any in progress auto-negotiation */ + xgbe_an_disable_all(pdata); + + /* Clear any auto-negotitation interrupts */ + xgbe_an_clear_interrupts_all(pdata); + + pdata->an_result = XGBE_AN_READY; + pdata->an_state = XGBE_AN_READY; + pdata->kr_state = XGBE_RX_BPA; + pdata->kx_state = XGBE_RX_BPA; + + /* Re-enable auto-negotiation interrupt */ + enable_irq(pdata->an_irq); + + xgbe_an_init(pdata); + xgbe_an_restart(pdata); + +out: + if (ret) + set_bit(XGBE_LINK_ERR, &pdata->dev_state); + else + clear_bit(XGBE_LINK_ERR, &pdata->dev_state); + + mutex_unlock(&pdata->an_mutex); + + return ret; +} + +static int xgbe_phy_config_aneg(struct xgbe_prv_data *pdata) +{ + return __xgbe_phy_config_aneg(pdata, true); +} + +static int xgbe_phy_reconfig_aneg(struct xgbe_prv_data *pdata) +{ + return __xgbe_phy_config_aneg(pdata, false); +} + +static bool xgbe_phy_aneg_done(struct xgbe_prv_data *pdata) +{ + return (pdata->an_result == XGBE_AN_COMPLETE); +} + +static void xgbe_check_link_timeout(struct xgbe_prv_data *pdata) +{ + unsigned long link_timeout; + unsigned long kr_time; + int wait; + + link_timeout = pdata->link_check + (XGBE_LINK_TIMEOUT * HZ); + if (time_after(jiffies, link_timeout)) { + if ((xgbe_cur_mode(pdata) == XGBE_MODE_KR) && + pdata->phy.autoneg == AUTONEG_ENABLE) { + /* AN restart should not happen while KR training is in progress. + * The while loop ensures no AN restart during KR training, + * waits up to 500ms and AN restart is triggered only if KR + * training is failed. + */ + wait = XGBE_KR_TRAINING_WAIT_ITER; + while (wait--) { + kr_time = pdata->kr_start_time + + msecs_to_jiffies(XGBE_AN_MS_TIMEOUT); + if (time_after(jiffies, kr_time)) + break; + /* AN restart is not required, if AN result is COMPLETE */ + if (pdata->an_result == XGBE_AN_COMPLETE) + return; + usleep_range(10000, 11000); + } + } + netif_dbg(pdata, link, pdata->netdev, "AN link timeout\n"); + xgbe_phy_config_aneg(pdata); + } +} + +static enum xgbe_mode xgbe_phy_status_aneg(struct xgbe_prv_data *pdata) +{ + return pdata->phy_if.phy_impl.an_outcome(pdata); +} + +static bool xgbe_phy_status_result(struct xgbe_prv_data *pdata) +{ + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + enum xgbe_mode mode; + + XGBE_ZERO_LP_ADV(lks); + + if ((pdata->phy.autoneg != AUTONEG_ENABLE) || pdata->parallel_detect) + mode = xgbe_cur_mode(pdata); + else + mode = xgbe_phy_status_aneg(pdata); + + switch (mode) { + case XGBE_MODE_SGMII_100: + pdata->phy.speed = SPEED_100; + break; + case XGBE_MODE_X: + case XGBE_MODE_KX_1000: + case XGBE_MODE_SGMII_1000: + pdata->phy.speed = SPEED_1000; + break; + case XGBE_MODE_KX_2500: + pdata->phy.speed = SPEED_2500; + break; + case XGBE_MODE_KR: + case XGBE_MODE_SFI: + pdata->phy.speed = SPEED_10000; + break; + case XGBE_MODE_UNKNOWN: + default: + pdata->phy.speed = SPEED_UNKNOWN; + } + + pdata->phy.duplex = DUPLEX_FULL; + + if (!xgbe_set_mode(pdata, mode)) + return false; + + if (pdata->an_again) + xgbe_phy_reconfig_aneg(pdata); + + return true; +} + +static void xgbe_phy_status(struct xgbe_prv_data *pdata) +{ + unsigned int link_aneg; + int an_restart; + + if (test_bit(XGBE_LINK_ERR, &pdata->dev_state)) { + netif_carrier_off(pdata->netdev); + + pdata->phy.link = 0; + goto adjust_link; + } + + link_aneg = (pdata->phy.autoneg == AUTONEG_ENABLE); + + pdata->phy.link = pdata->phy_if.phy_impl.link_status(pdata, + &an_restart); + if (an_restart) { + xgbe_phy_config_aneg(pdata); + goto adjust_link; + } + + if (pdata->phy.link) { + if (link_aneg && !xgbe_phy_aneg_done(pdata)) { + xgbe_check_link_timeout(pdata); + return; + } + + if (xgbe_phy_status_result(pdata)) + return; + + if (test_bit(XGBE_LINK_INIT, &pdata->dev_state)) + clear_bit(XGBE_LINK_INIT, &pdata->dev_state); + + netif_carrier_on(pdata->netdev); + } else { + if (test_bit(XGBE_LINK_INIT, &pdata->dev_state)) { + xgbe_check_link_timeout(pdata); + + if (link_aneg) + return; + } + + xgbe_phy_status_result(pdata); + + netif_carrier_off(pdata->netdev); + } + +adjust_link: + xgbe_phy_adjust_link(pdata); +} + +static void xgbe_phy_stop(struct xgbe_prv_data *pdata) +{ + netif_dbg(pdata, link, pdata->netdev, "stopping PHY\n"); + + if (!pdata->phy_started) + return; + + /* Indicate the PHY is down */ + pdata->phy_started = 0; + + /* Disable auto-negotiation */ + xgbe_an_disable_all(pdata); + + if (pdata->dev_irq != pdata->an_irq) { + devm_free_irq(pdata->dev, pdata->an_irq, pdata); + tasklet_kill(&pdata->tasklet_an); + } + + pdata->phy_if.phy_impl.stop(pdata); + + pdata->phy.link = 0; + + xgbe_phy_adjust_link(pdata); +} + +static int xgbe_phy_start(struct xgbe_prv_data *pdata) +{ + struct net_device *netdev = pdata->netdev; + int ret; + + netif_dbg(pdata, link, pdata->netdev, "starting PHY\n"); + + ret = pdata->phy_if.phy_impl.start(pdata); + if (ret) + return ret; + + /* If we have a separate AN irq, enable it */ + if (pdata->dev_irq != pdata->an_irq) { + tasklet_setup(&pdata->tasklet_an, xgbe_an_isr_task); + + ret = devm_request_irq(pdata->dev, pdata->an_irq, + xgbe_an_isr, 0, pdata->an_name, + pdata); + if (ret) { + netdev_err(netdev, "phy irq request failed\n"); + goto err_stop; + } + } + + /* Set initial mode - call the mode setting routines + * directly to insure we are properly configured + */ + if (xgbe_use_mode(pdata, XGBE_MODE_KR)) { + xgbe_kr_mode(pdata); + } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_2500)) { + xgbe_kx_2500_mode(pdata); + } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_1000)) { + xgbe_kx_1000_mode(pdata); + } else if (xgbe_use_mode(pdata, XGBE_MODE_SFI)) { + xgbe_sfi_mode(pdata); + } else if (xgbe_use_mode(pdata, XGBE_MODE_X)) { + xgbe_x_mode(pdata); + } else if (xgbe_use_mode(pdata, XGBE_MODE_SGMII_1000)) { + xgbe_sgmii_1000_mode(pdata); + } else if (xgbe_use_mode(pdata, XGBE_MODE_SGMII_100)) { + xgbe_sgmii_100_mode(pdata); + } else { + ret = -EINVAL; + goto err_irq; + } + + /* Indicate the PHY is up and running */ + pdata->phy_started = 1; + + xgbe_an_init(pdata); + xgbe_an_enable_interrupts(pdata); + + return xgbe_phy_config_aneg(pdata); + +err_irq: + if (pdata->dev_irq != pdata->an_irq) + devm_free_irq(pdata->dev, pdata->an_irq, pdata); + +err_stop: + pdata->phy_if.phy_impl.stop(pdata); + + return ret; +} + +static int xgbe_phy_reset(struct xgbe_prv_data *pdata) +{ + int ret; + + ret = pdata->phy_if.phy_impl.reset(pdata); + if (ret) + return ret; + + /* Disable auto-negotiation for now */ + xgbe_an_disable_all(pdata); + + /* Clear auto-negotiation interrupts */ + xgbe_an_clear_interrupts_all(pdata); + + return 0; +} + +static void xgbe_dump_phy_registers(struct xgbe_prv_data *pdata) +{ + struct device *dev = pdata->dev; + + dev_dbg(dev, "\n************* PHY Reg dump **********************\n"); + + dev_dbg(dev, "PCS Control Reg (%#06x) = %#06x\n", MDIO_CTRL1, + XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1)); + dev_dbg(dev, "PCS Status Reg (%#06x) = %#06x\n", MDIO_STAT1, + XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1)); + dev_dbg(dev, "Phy Id (PHYS ID 1 %#06x)= %#06x\n", MDIO_DEVID1, + XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_DEVID1)); + dev_dbg(dev, "Phy Id (PHYS ID 2 %#06x)= %#06x\n", MDIO_DEVID2, + XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_DEVID2)); + dev_dbg(dev, "Devices in Package (%#06x)= %#06x\n", MDIO_DEVS1, + XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_DEVS1)); + dev_dbg(dev, "Devices in Package (%#06x)= %#06x\n", MDIO_DEVS2, + XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_DEVS2)); + + dev_dbg(dev, "Auto-Neg Control Reg (%#06x) = %#06x\n", MDIO_CTRL1, + XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_CTRL1)); + dev_dbg(dev, "Auto-Neg Status Reg (%#06x) = %#06x\n", MDIO_STAT1, + XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_STAT1)); + dev_dbg(dev, "Auto-Neg Ad Reg 1 (%#06x) = %#06x\n", + MDIO_AN_ADVERTISE, + XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE)); + dev_dbg(dev, "Auto-Neg Ad Reg 2 (%#06x) = %#06x\n", + MDIO_AN_ADVERTISE + 1, + XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1)); + dev_dbg(dev, "Auto-Neg Ad Reg 3 (%#06x) = %#06x\n", + MDIO_AN_ADVERTISE + 2, + XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2)); + dev_dbg(dev, "Auto-Neg Completion Reg (%#06x) = %#06x\n", + MDIO_AN_COMP_STAT, + XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_COMP_STAT)); + + dev_dbg(dev, "\n*************************************************\n"); +} + +static int xgbe_phy_best_advertised_speed(struct xgbe_prv_data *pdata) +{ + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + + if (XGBE_ADV(lks, 10000baseKR_Full)) + return SPEED_10000; + else if (XGBE_ADV(lks, 10000baseT_Full)) + return SPEED_10000; + else if (XGBE_ADV(lks, 2500baseX_Full)) + return SPEED_2500; + else if (XGBE_ADV(lks, 2500baseT_Full)) + return SPEED_2500; + else if (XGBE_ADV(lks, 1000baseKX_Full)) + return SPEED_1000; + else if (XGBE_ADV(lks, 1000baseT_Full)) + return SPEED_1000; + else if (XGBE_ADV(lks, 100baseT_Full)) + return SPEED_100; + + return SPEED_UNKNOWN; +} + +static void xgbe_phy_exit(struct xgbe_prv_data *pdata) +{ + pdata->phy_if.phy_impl.exit(pdata); +} + +static int xgbe_phy_init(struct xgbe_prv_data *pdata) +{ + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + int ret; + + mutex_init(&pdata->an_mutex); + INIT_WORK(&pdata->an_irq_work, xgbe_an_irq_work); + INIT_WORK(&pdata->an_work, xgbe_an_state_machine); + pdata->mdio_mmd = MDIO_MMD_PCS; + + /* Check for FEC support */ + pdata->fec_ability = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, + MDIO_PMA_10GBR_FECABLE); + pdata->fec_ability &= (MDIO_PMA_10GBR_FECABLE_ABLE | + MDIO_PMA_10GBR_FECABLE_ERRABLE); + + /* Setup the phy (including supported features) */ + ret = pdata->phy_if.phy_impl.init(pdata); + if (ret) + return ret; + + /* Copy supported link modes to advertising link modes */ + XGBE_LM_COPY(lks, advertising, lks, supported); + + pdata->phy.address = 0; + + if (XGBE_ADV(lks, Autoneg)) { + pdata->phy.autoneg = AUTONEG_ENABLE; + pdata->phy.speed = SPEED_UNKNOWN; + pdata->phy.duplex = DUPLEX_UNKNOWN; + } else { + pdata->phy.autoneg = AUTONEG_DISABLE; + pdata->phy.speed = xgbe_phy_best_advertised_speed(pdata); + pdata->phy.duplex = DUPLEX_FULL; + } + + pdata->phy.link = 0; + + pdata->phy.pause_autoneg = pdata->pause_autoneg; + pdata->phy.tx_pause = pdata->tx_pause; + pdata->phy.rx_pause = pdata->rx_pause; + + /* Fix up Flow Control advertising */ + XGBE_CLR_ADV(lks, Pause); + XGBE_CLR_ADV(lks, Asym_Pause); + + if (pdata->rx_pause) { + XGBE_SET_ADV(lks, Pause); + XGBE_SET_ADV(lks, Asym_Pause); + } + + if (pdata->tx_pause) { + /* Equivalent to XOR of Asym_Pause */ + if (XGBE_ADV(lks, Asym_Pause)) + XGBE_CLR_ADV(lks, Asym_Pause); + else + XGBE_SET_ADV(lks, Asym_Pause); + } + + if (netif_msg_drv(pdata)) + xgbe_dump_phy_registers(pdata); + + return 0; +} + +void xgbe_init_function_ptrs_phy(struct xgbe_phy_if *phy_if) +{ + phy_if->phy_init = xgbe_phy_init; + phy_if->phy_exit = xgbe_phy_exit; + + phy_if->phy_reset = xgbe_phy_reset; + phy_if->phy_start = xgbe_phy_start; + phy_if->phy_stop = xgbe_phy_stop; + + phy_if->phy_status = xgbe_phy_status; + phy_if->phy_config_aneg = xgbe_phy_config_aneg; + + phy_if->phy_valid_speed = xgbe_phy_valid_speed; + + phy_if->an_isr = xgbe_an_combined_isr; + + phy_if->module_info = xgbe_phy_module_info; + phy_if->module_eeprom = xgbe_phy_module_eeprom; +} diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c new file mode 100644 index 000000000..f409d7bd1 --- /dev/null +++ b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c @@ -0,0 +1,538 @@ +/* + * AMD 10Gb Ethernet driver + * + * This file is available to you under your choice of the following two + * licenses: + * + * License 1: GPLv2 + * + * Copyright (c) 2016 Advanced Micro Devices, Inc. + * + * This file is free software; you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or (at + * your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * + * License 2: Modified BSD + * + * Copyright (c) 2016 Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/pci.h> +#include <linux/log2.h> + +#include "xgbe.h" +#include "xgbe-common.h" + +static int xgbe_config_multi_msi(struct xgbe_prv_data *pdata) +{ + unsigned int vector_count; + unsigned int i, j; + int ret; + + vector_count = XGBE_MSI_BASE_COUNT; + vector_count += max(pdata->rx_ring_count, + pdata->tx_ring_count); + + ret = pci_alloc_irq_vectors(pdata->pcidev, XGBE_MSI_MIN_COUNT, + vector_count, PCI_IRQ_MSI | PCI_IRQ_MSIX); + if (ret < 0) { + dev_info(pdata->dev, "multi MSI/MSI-X enablement failed\n"); + return ret; + } + + pdata->isr_as_tasklet = 1; + pdata->irq_count = ret; + + pdata->dev_irq = pci_irq_vector(pdata->pcidev, 0); + pdata->ecc_irq = pci_irq_vector(pdata->pcidev, 1); + pdata->i2c_irq = pci_irq_vector(pdata->pcidev, 2); + pdata->an_irq = pci_irq_vector(pdata->pcidev, 3); + + for (i = XGBE_MSI_BASE_COUNT, j = 0; i < ret; i++, j++) + pdata->channel_irq[j] = pci_irq_vector(pdata->pcidev, i); + pdata->channel_irq_count = j; + + pdata->per_channel_irq = 1; + pdata->channel_irq_mode = XGBE_IRQ_MODE_LEVEL; + + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "multi %s interrupts enabled\n", + pdata->pcidev->msix_enabled ? "MSI-X" : "MSI"); + + return 0; +} + +static int xgbe_config_irqs(struct xgbe_prv_data *pdata) +{ + int ret; + + ret = xgbe_config_multi_msi(pdata); + if (!ret) + goto out; + + ret = pci_alloc_irq_vectors(pdata->pcidev, 1, 1, + PCI_IRQ_LEGACY | PCI_IRQ_MSI); + if (ret < 0) { + dev_info(pdata->dev, "single IRQ enablement failed\n"); + return ret; + } + + pdata->isr_as_tasklet = pdata->pcidev->msi_enabled ? 1 : 0; + pdata->irq_count = 1; + pdata->channel_irq_count = 1; + + pdata->dev_irq = pci_irq_vector(pdata->pcidev, 0); + pdata->ecc_irq = pci_irq_vector(pdata->pcidev, 0); + pdata->i2c_irq = pci_irq_vector(pdata->pcidev, 0); + pdata->an_irq = pci_irq_vector(pdata->pcidev, 0); + + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "single %s interrupt enabled\n", + pdata->pcidev->msi_enabled ? "MSI" : "legacy"); + +out: + if (netif_msg_probe(pdata)) { + unsigned int i; + + dev_dbg(pdata->dev, " dev irq=%d\n", pdata->dev_irq); + dev_dbg(pdata->dev, " ecc irq=%d\n", pdata->ecc_irq); + dev_dbg(pdata->dev, " i2c irq=%d\n", pdata->i2c_irq); + dev_dbg(pdata->dev, " an irq=%d\n", pdata->an_irq); + for (i = 0; i < pdata->channel_irq_count; i++) + dev_dbg(pdata->dev, " dma%u irq=%d\n", + i, pdata->channel_irq[i]); + } + + return 0; +} + +static int xgbe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct xgbe_prv_data *pdata; + struct device *dev = &pdev->dev; + void __iomem * const *iomap_table; + struct pci_dev *rdev; + unsigned int ma_lo, ma_hi; + unsigned int reg; + int bar_mask; + int ret; + + pdata = xgbe_alloc_pdata(dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto err_alloc; + } + + pdata->pcidev = pdev; + pci_set_drvdata(pdev, pdata); + + /* Get the version data */ + pdata->vdata = (struct xgbe_version_data *)id->driver_data; + + ret = pcim_enable_device(pdev); + if (ret) { + dev_err(dev, "pcim_enable_device failed\n"); + goto err_pci_enable; + } + + /* Obtain the mmio areas for the device */ + bar_mask = pci_select_bars(pdev, IORESOURCE_MEM); + ret = pcim_iomap_regions(pdev, bar_mask, XGBE_DRV_NAME); + if (ret) { + dev_err(dev, "pcim_iomap_regions failed\n"); + goto err_pci_enable; + } + + iomap_table = pcim_iomap_table(pdev); + if (!iomap_table) { + dev_err(dev, "pcim_iomap_table failed\n"); + ret = -ENOMEM; + goto err_pci_enable; + } + + pdata->xgmac_regs = iomap_table[XGBE_XGMAC_BAR]; + if (!pdata->xgmac_regs) { + dev_err(dev, "xgmac ioremap failed\n"); + ret = -ENOMEM; + goto err_pci_enable; + } + pdata->xprop_regs = pdata->xgmac_regs + XGBE_MAC_PROP_OFFSET; + pdata->xi2c_regs = pdata->xgmac_regs + XGBE_I2C_CTRL_OFFSET; + if (netif_msg_probe(pdata)) { + dev_dbg(dev, "xgmac_regs = %p\n", pdata->xgmac_regs); + dev_dbg(dev, "xprop_regs = %p\n", pdata->xprop_regs); + dev_dbg(dev, "xi2c_regs = %p\n", pdata->xi2c_regs); + } + + pdata->xpcs_regs = iomap_table[XGBE_XPCS_BAR]; + if (!pdata->xpcs_regs) { + dev_err(dev, "xpcs ioremap failed\n"); + ret = -ENOMEM; + goto err_pci_enable; + } + if (netif_msg_probe(pdata)) + dev_dbg(dev, "xpcs_regs = %p\n", pdata->xpcs_regs); + + /* Set the PCS indirect addressing definition registers */ + rdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0, 0)); + if (rdev && + (rdev->vendor == PCI_VENDOR_ID_AMD) && (rdev->device == 0x15d0)) { + pdata->xpcs_window_def_reg = PCS_V2_RV_WINDOW_DEF; + pdata->xpcs_window_sel_reg = PCS_V2_RV_WINDOW_SELECT; + } else if (rdev && (rdev->vendor == PCI_VENDOR_ID_AMD) && + (rdev->device == 0x14b5)) { + pdata->xpcs_window_def_reg = PCS_V2_YC_WINDOW_DEF; + pdata->xpcs_window_sel_reg = PCS_V2_YC_WINDOW_SELECT; + + /* Yellow Carp devices do not need cdr workaround */ + pdata->vdata->an_cdr_workaround = 0; + + /* Yellow Carp devices do not need rrc */ + pdata->vdata->enable_rrc = 0; + } else { + pdata->xpcs_window_def_reg = PCS_V2_WINDOW_DEF; + pdata->xpcs_window_sel_reg = PCS_V2_WINDOW_SELECT; + } + pci_dev_put(rdev); + + /* Configure the PCS indirect addressing support */ + reg = XPCS32_IOREAD(pdata, pdata->xpcs_window_def_reg); + pdata->xpcs_window = XPCS_GET_BITS(reg, PCS_V2_WINDOW_DEF, OFFSET); + pdata->xpcs_window <<= 6; + pdata->xpcs_window_size = XPCS_GET_BITS(reg, PCS_V2_WINDOW_DEF, SIZE); + pdata->xpcs_window_size = 1 << (pdata->xpcs_window_size + 7); + pdata->xpcs_window_mask = pdata->xpcs_window_size - 1; + if (netif_msg_probe(pdata)) { + dev_dbg(dev, "xpcs window def = %#010x\n", + pdata->xpcs_window_def_reg); + dev_dbg(dev, "xpcs window sel = %#010x\n", + pdata->xpcs_window_sel_reg); + dev_dbg(dev, "xpcs window = %#010x\n", + pdata->xpcs_window); + dev_dbg(dev, "xpcs window size = %#010x\n", + pdata->xpcs_window_size); + dev_dbg(dev, "xpcs window mask = %#010x\n", + pdata->xpcs_window_mask); + } + + pci_set_master(pdev); + + /* Enable all interrupts in the hardware */ + XP_IOWRITE(pdata, XP_INT_EN, 0x1fffff); + + /* Retrieve the MAC address */ + ma_lo = XP_IOREAD(pdata, XP_MAC_ADDR_LO); + ma_hi = XP_IOREAD(pdata, XP_MAC_ADDR_HI); + pdata->mac_addr[0] = ma_lo & 0xff; + pdata->mac_addr[1] = (ma_lo >> 8) & 0xff; + pdata->mac_addr[2] = (ma_lo >> 16) & 0xff; + pdata->mac_addr[3] = (ma_lo >> 24) & 0xff; + pdata->mac_addr[4] = ma_hi & 0xff; + pdata->mac_addr[5] = (ma_hi >> 8) & 0xff; + if (!XP_GET_BITS(ma_hi, XP_MAC_ADDR_HI, VALID) || + !is_valid_ether_addr(pdata->mac_addr)) { + dev_err(dev, "invalid mac address\n"); + ret = -EINVAL; + goto err_pci_enable; + } + + /* Clock settings */ + pdata->sysclk_rate = XGBE_V2_DMA_CLOCK_FREQ; + pdata->ptpclk_rate = XGBE_V2_PTP_CLOCK_FREQ; + + /* Set the DMA coherency values */ + pdata->coherent = 1; + pdata->arcr = XGBE_DMA_PCI_ARCR; + pdata->awcr = XGBE_DMA_PCI_AWCR; + pdata->awarcr = XGBE_DMA_PCI_AWARCR; + + /* Read the port property registers */ + pdata->pp0 = XP_IOREAD(pdata, XP_PROP_0); + pdata->pp1 = XP_IOREAD(pdata, XP_PROP_1); + pdata->pp2 = XP_IOREAD(pdata, XP_PROP_2); + pdata->pp3 = XP_IOREAD(pdata, XP_PROP_3); + pdata->pp4 = XP_IOREAD(pdata, XP_PROP_4); + if (netif_msg_probe(pdata)) { + dev_dbg(dev, "port property 0 = %#010x\n", pdata->pp0); + dev_dbg(dev, "port property 1 = %#010x\n", pdata->pp1); + dev_dbg(dev, "port property 2 = %#010x\n", pdata->pp2); + dev_dbg(dev, "port property 3 = %#010x\n", pdata->pp3); + dev_dbg(dev, "port property 4 = %#010x\n", pdata->pp4); + } + + /* Set the maximum channels and queues */ + pdata->tx_max_channel_count = XP_GET_BITS(pdata->pp1, XP_PROP_1, + MAX_TX_DMA); + pdata->rx_max_channel_count = XP_GET_BITS(pdata->pp1, XP_PROP_1, + MAX_RX_DMA); + pdata->tx_max_q_count = XP_GET_BITS(pdata->pp1, XP_PROP_1, + MAX_TX_QUEUES); + pdata->rx_max_q_count = XP_GET_BITS(pdata->pp1, XP_PROP_1, + MAX_RX_QUEUES); + if (netif_msg_probe(pdata)) { + dev_dbg(dev, "max tx/rx channel count = %u/%u\n", + pdata->tx_max_channel_count, + pdata->rx_max_channel_count); + dev_dbg(dev, "max tx/rx hw queue count = %u/%u\n", + pdata->tx_max_q_count, pdata->rx_max_q_count); + } + + /* Set the hardware channel and queue counts */ + xgbe_set_counts(pdata); + + /* Set the maximum fifo amounts */ + pdata->tx_max_fifo_size = XP_GET_BITS(pdata->pp2, XP_PROP_2, + TX_FIFO_SIZE); + pdata->tx_max_fifo_size *= 16384; + pdata->tx_max_fifo_size = min(pdata->tx_max_fifo_size, + pdata->vdata->tx_max_fifo_size); + pdata->rx_max_fifo_size = XP_GET_BITS(pdata->pp2, XP_PROP_2, + RX_FIFO_SIZE); + pdata->rx_max_fifo_size *= 16384; + pdata->rx_max_fifo_size = min(pdata->rx_max_fifo_size, + pdata->vdata->rx_max_fifo_size); + if (netif_msg_probe(pdata)) + dev_dbg(dev, "max tx/rx max fifo size = %u/%u\n", + pdata->tx_max_fifo_size, pdata->rx_max_fifo_size); + + /* Configure interrupt support */ + ret = xgbe_config_irqs(pdata); + if (ret) + goto err_pci_enable; + + /* Configure the netdev resource */ + ret = xgbe_config_netdev(pdata); + if (ret) + goto err_irq_vectors; + + netdev_notice(pdata->netdev, "net device enabled\n"); + + return 0; + +err_irq_vectors: + pci_free_irq_vectors(pdata->pcidev); + +err_pci_enable: + xgbe_free_pdata(pdata); + +err_alloc: + dev_notice(dev, "net device not enabled\n"); + + return ret; +} + +static void xgbe_pci_remove(struct pci_dev *pdev) +{ + struct xgbe_prv_data *pdata = pci_get_drvdata(pdev); + + xgbe_deconfig_netdev(pdata); + + pci_free_irq_vectors(pdata->pcidev); + + /* Disable all interrupts in the hardware */ + XP_IOWRITE(pdata, XP_INT_EN, 0x0); + + xgbe_free_pdata(pdata); +} + +static int __maybe_unused xgbe_pci_suspend(struct device *dev) +{ + struct xgbe_prv_data *pdata = dev_get_drvdata(dev); + struct net_device *netdev = pdata->netdev; + int ret = 0; + + if (netif_running(netdev)) + ret = xgbe_powerdown(netdev, XGMAC_DRIVER_CONTEXT); + + pdata->lpm_ctrl = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); + pdata->lpm_ctrl |= MDIO_CTRL1_LPOWER; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, pdata->lpm_ctrl); + + return ret; +} + +static int __maybe_unused xgbe_pci_resume(struct device *dev) +{ + struct xgbe_prv_data *pdata = dev_get_drvdata(dev); + struct net_device *netdev = pdata->netdev; + int ret = 0; + + XP_IOWRITE(pdata, XP_INT_EN, 0x1fffff); + + pdata->lpm_ctrl &= ~MDIO_CTRL1_LPOWER; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, pdata->lpm_ctrl); + + if (netif_running(netdev)) { + ret = xgbe_powerup(netdev, XGMAC_DRIVER_CONTEXT); + + /* Schedule a restart in case the link or phy state changed + * while we were powered down. + */ + schedule_work(&pdata->restart_work); + } + + return ret; +} + +static struct xgbe_version_data xgbe_v2a = { + .init_function_ptrs_phy_impl = xgbe_init_function_ptrs_phy_v2, + .xpcs_access = XGBE_XPCS_ACCESS_V2, + .mmc_64bit = 1, + .tx_max_fifo_size = 229376, + .rx_max_fifo_size = 229376, + .tx_tstamp_workaround = 1, + .ecc_support = 1, + .i2c_support = 1, + .irq_reissue_support = 1, + .tx_desc_prefetch = 5, + .rx_desc_prefetch = 5, + .an_cdr_workaround = 1, + .enable_rrc = 1, +}; + +static struct xgbe_version_data xgbe_v2b = { + .init_function_ptrs_phy_impl = xgbe_init_function_ptrs_phy_v2, + .xpcs_access = XGBE_XPCS_ACCESS_V2, + .mmc_64bit = 1, + .tx_max_fifo_size = 65536, + .rx_max_fifo_size = 65536, + .tx_tstamp_workaround = 1, + .ecc_support = 1, + .i2c_support = 1, + .irq_reissue_support = 1, + .tx_desc_prefetch = 5, + .rx_desc_prefetch = 5, + .an_cdr_workaround = 1, + .enable_rrc = 1, +}; + +static const struct pci_device_id xgbe_pci_table[] = { + { PCI_VDEVICE(AMD, 0x1458), + .driver_data = (kernel_ulong_t)&xgbe_v2a }, + { PCI_VDEVICE(AMD, 0x1459), + .driver_data = (kernel_ulong_t)&xgbe_v2b }, + /* Last entry must be zero */ + { 0, } +}; +MODULE_DEVICE_TABLE(pci, xgbe_pci_table); + +static SIMPLE_DEV_PM_OPS(xgbe_pci_pm_ops, xgbe_pci_suspend, xgbe_pci_resume); + +static struct pci_driver xgbe_driver = { + .name = XGBE_DRV_NAME, + .id_table = xgbe_pci_table, + .probe = xgbe_pci_probe, + .remove = xgbe_pci_remove, + .driver = { + .pm = &xgbe_pci_pm_ops, + } +}; + +int xgbe_pci_init(void) +{ + return pci_register_driver(&xgbe_driver); +} + +void xgbe_pci_exit(void) +{ + pci_unregister_driver(&xgbe_driver); +} diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c new file mode 100644 index 000000000..d16eae415 --- /dev/null +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c @@ -0,0 +1,853 @@ +/* + * AMD 10Gb Ethernet driver + * + * This file is available to you under your choice of the following two + * licenses: + * + * License 1: GPLv2 + * + * Copyright (c) 2016 Advanced Micro Devices, Inc. + * + * This file is free software; you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or (at + * your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * + * License 2: Modified BSD + * + * Copyright (c) 2016 Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/module.h> +#include <linux/kmod.h> +#include <linux/device.h> +#include <linux/property.h> +#include <linux/mdio.h> +#include <linux/phy.h> + +#include "xgbe.h" +#include "xgbe-common.h" + +#define XGBE_BLWC_PROPERTY "amd,serdes-blwc" +#define XGBE_CDR_RATE_PROPERTY "amd,serdes-cdr-rate" +#define XGBE_PQ_SKEW_PROPERTY "amd,serdes-pq-skew" +#define XGBE_TX_AMP_PROPERTY "amd,serdes-tx-amp" +#define XGBE_DFE_CFG_PROPERTY "amd,serdes-dfe-tap-config" +#define XGBE_DFE_ENA_PROPERTY "amd,serdes-dfe-tap-enable" + +/* Default SerDes settings */ +#define XGBE_SPEED_1000_BLWC 1 +#define XGBE_SPEED_1000_CDR 0x2 +#define XGBE_SPEED_1000_PLL 0x0 +#define XGBE_SPEED_1000_PQ 0xa +#define XGBE_SPEED_1000_RATE 0x3 +#define XGBE_SPEED_1000_TXAMP 0xf +#define XGBE_SPEED_1000_WORD 0x1 +#define XGBE_SPEED_1000_DFE_TAP_CONFIG 0x3 +#define XGBE_SPEED_1000_DFE_TAP_ENABLE 0x0 + +#define XGBE_SPEED_2500_BLWC 1 +#define XGBE_SPEED_2500_CDR 0x2 +#define XGBE_SPEED_2500_PLL 0x0 +#define XGBE_SPEED_2500_PQ 0xa +#define XGBE_SPEED_2500_RATE 0x1 +#define XGBE_SPEED_2500_TXAMP 0xf +#define XGBE_SPEED_2500_WORD 0x1 +#define XGBE_SPEED_2500_DFE_TAP_CONFIG 0x3 +#define XGBE_SPEED_2500_DFE_TAP_ENABLE 0x0 + +#define XGBE_SPEED_10000_BLWC 0 +#define XGBE_SPEED_10000_CDR 0x7 +#define XGBE_SPEED_10000_PLL 0x1 +#define XGBE_SPEED_10000_PQ 0x12 +#define XGBE_SPEED_10000_RATE 0x0 +#define XGBE_SPEED_10000_TXAMP 0xa +#define XGBE_SPEED_10000_WORD 0x7 +#define XGBE_SPEED_10000_DFE_TAP_CONFIG 0x1 +#define XGBE_SPEED_10000_DFE_TAP_ENABLE 0x7f + +/* Rate-change complete wait/retry count */ +#define XGBE_RATECHANGE_COUNT 500 + +static const u32 xgbe_phy_blwc[] = { + XGBE_SPEED_1000_BLWC, + XGBE_SPEED_2500_BLWC, + XGBE_SPEED_10000_BLWC, +}; + +static const u32 xgbe_phy_cdr_rate[] = { + XGBE_SPEED_1000_CDR, + XGBE_SPEED_2500_CDR, + XGBE_SPEED_10000_CDR, +}; + +static const u32 xgbe_phy_pq_skew[] = { + XGBE_SPEED_1000_PQ, + XGBE_SPEED_2500_PQ, + XGBE_SPEED_10000_PQ, +}; + +static const u32 xgbe_phy_tx_amp[] = { + XGBE_SPEED_1000_TXAMP, + XGBE_SPEED_2500_TXAMP, + XGBE_SPEED_10000_TXAMP, +}; + +static const u32 xgbe_phy_dfe_tap_cfg[] = { + XGBE_SPEED_1000_DFE_TAP_CONFIG, + XGBE_SPEED_2500_DFE_TAP_CONFIG, + XGBE_SPEED_10000_DFE_TAP_CONFIG, +}; + +static const u32 xgbe_phy_dfe_tap_ena[] = { + XGBE_SPEED_1000_DFE_TAP_ENABLE, + XGBE_SPEED_2500_DFE_TAP_ENABLE, + XGBE_SPEED_10000_DFE_TAP_ENABLE, +}; + +struct xgbe_phy_data { + /* 1000/10000 vs 2500/10000 indicator */ + unsigned int speed_set; + + /* SerDes UEFI configurable settings. + * Switching between modes/speeds requires new values for some + * SerDes settings. The values can be supplied as device + * properties in array format. The first array entry is for + * 1GbE, second for 2.5GbE and third for 10GbE + */ + u32 blwc[XGBE_SPEEDS]; + u32 cdr_rate[XGBE_SPEEDS]; + u32 pq_skew[XGBE_SPEEDS]; + u32 tx_amp[XGBE_SPEEDS]; + u32 dfe_tap_cfg[XGBE_SPEEDS]; + u32 dfe_tap_ena[XGBE_SPEEDS]; +}; + +static void xgbe_phy_kr_training_pre(struct xgbe_prv_data *pdata) +{ + XSIR0_IOWRITE_BITS(pdata, SIR0_KR_RT_1, RESET, 1); +} + +static void xgbe_phy_kr_training_post(struct xgbe_prv_data *pdata) +{ + XSIR0_IOWRITE_BITS(pdata, SIR0_KR_RT_1, RESET, 0); +} + +static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata) +{ + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + struct xgbe_phy_data *phy_data = pdata->phy_data; + enum xgbe_mode mode; + unsigned int ad_reg, lp_reg; + + XGBE_SET_LP_ADV(lks, Autoneg); + XGBE_SET_LP_ADV(lks, Backplane); + + /* Compare Advertisement and Link Partner register 1 */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA); + if (lp_reg & 0x400) + XGBE_SET_LP_ADV(lks, Pause); + if (lp_reg & 0x800) + XGBE_SET_LP_ADV(lks, Asym_Pause); + + if (pdata->phy.pause_autoneg) { + /* Set flow control based on auto-negotiation result */ + pdata->phy.tx_pause = 0; + pdata->phy.rx_pause = 0; + + if (ad_reg & lp_reg & 0x400) { + pdata->phy.tx_pause = 1; + pdata->phy.rx_pause = 1; + } else if (ad_reg & lp_reg & 0x800) { + if (ad_reg & 0x400) + pdata->phy.rx_pause = 1; + else if (lp_reg & 0x400) + pdata->phy.tx_pause = 1; + } + } + + /* Compare Advertisement and Link Partner register 2 */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1); + if (lp_reg & 0x80) + XGBE_SET_LP_ADV(lks, 10000baseKR_Full); + if (lp_reg & 0x20) { + if (phy_data->speed_set == XGBE_SPEEDSET_2500_10000) + XGBE_SET_LP_ADV(lks, 2500baseX_Full); + else + XGBE_SET_LP_ADV(lks, 1000baseKX_Full); + } + + ad_reg &= lp_reg; + if (ad_reg & 0x80) { + mode = XGBE_MODE_KR; + } else if (ad_reg & 0x20) { + if (phy_data->speed_set == XGBE_SPEEDSET_2500_10000) + mode = XGBE_MODE_KX_2500; + else + mode = XGBE_MODE_KX_1000; + } else { + mode = XGBE_MODE_UNKNOWN; + } + + /* Compare Advertisement and Link Partner register 3 */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2); + if (lp_reg & 0xc000) + XGBE_SET_LP_ADV(lks, 10000baseR_FEC); + + return mode; +} + +static void xgbe_phy_an_advertising(struct xgbe_prv_data *pdata, + struct ethtool_link_ksettings *dlks) +{ + struct ethtool_link_ksettings *slks = &pdata->phy.lks; + + XGBE_LM_COPY(dlks, advertising, slks, advertising); +} + +static int xgbe_phy_an_config(struct xgbe_prv_data *pdata) +{ + /* Nothing uniquely required for an configuration */ + return 0; +} + +static enum xgbe_an_mode xgbe_phy_an_mode(struct xgbe_prv_data *pdata) +{ + return XGBE_AN_MODE_CL73; +} + +static void xgbe_phy_pcs_power_cycle(struct xgbe_prv_data *pdata) +{ + unsigned int reg; + + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); + + reg |= MDIO_CTRL1_LPOWER; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg); + + usleep_range(75, 100); + + reg &= ~MDIO_CTRL1_LPOWER; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg); +} + +static void xgbe_phy_start_ratechange(struct xgbe_prv_data *pdata) +{ + /* Assert Rx and Tx ratechange */ + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, RATECHANGE, 1); +} + +static void xgbe_phy_complete_ratechange(struct xgbe_prv_data *pdata) +{ + unsigned int wait; + u16 status; + + /* Release Rx and Tx ratechange */ + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, RATECHANGE, 0); + + /* Wait for Rx and Tx ready */ + wait = XGBE_RATECHANGE_COUNT; + while (wait--) { + usleep_range(50, 75); + + status = XSIR0_IOREAD(pdata, SIR0_STATUS); + if (XSIR_GET_BITS(status, SIR0_STATUS, RX_READY) && + XSIR_GET_BITS(status, SIR0_STATUS, TX_READY)) + goto rx_reset; + } + + netif_dbg(pdata, link, pdata->netdev, "SerDes rx/tx not ready (%#hx)\n", + status); + +rx_reset: + /* Perform Rx reset for the DFE changes */ + XRXTX_IOWRITE_BITS(pdata, RXTX_REG6, RESETB_RXD, 0); + XRXTX_IOWRITE_BITS(pdata, RXTX_REG6, RESETB_RXD, 1); +} + +static void xgbe_phy_kr_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int reg; + + /* Set PCS to KR/10G speed */ + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2); + reg &= ~MDIO_PCS_CTRL2_TYPE; + reg |= MDIO_PCS_CTRL2_10GBR; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL2, reg); + + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); + reg &= ~MDIO_CTRL1_SPEEDSEL; + reg |= MDIO_CTRL1_SPEED10G; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg); + + xgbe_phy_pcs_power_cycle(pdata); + + /* Set SerDes to 10G speed */ + xgbe_phy_start_ratechange(pdata); + + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, DATARATE, XGBE_SPEED_10000_RATE); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, WORDMODE, XGBE_SPEED_10000_WORD); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PLLSEL, XGBE_SPEED_10000_PLL); + + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, CDR_RATE, + phy_data->cdr_rate[XGBE_SPEED_10000]); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, TXAMP, + phy_data->tx_amp[XGBE_SPEED_10000]); + XRXTX_IOWRITE_BITS(pdata, RXTX_REG20, BLWC_ENA, + phy_data->blwc[XGBE_SPEED_10000]); + XRXTX_IOWRITE_BITS(pdata, RXTX_REG114, PQ_REG, + phy_data->pq_skew[XGBE_SPEED_10000]); + XRXTX_IOWRITE_BITS(pdata, RXTX_REG129, RXDFE_CONFIG, + phy_data->dfe_tap_cfg[XGBE_SPEED_10000]); + XRXTX_IOWRITE(pdata, RXTX_REG22, + phy_data->dfe_tap_ena[XGBE_SPEED_10000]); + + xgbe_phy_complete_ratechange(pdata); + + netif_dbg(pdata, link, pdata->netdev, "10GbE KR mode set\n"); +} + +static void xgbe_phy_kx_2500_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int reg; + + /* Set PCS to KX/1G speed */ + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2); + reg &= ~MDIO_PCS_CTRL2_TYPE; + reg |= MDIO_PCS_CTRL2_10GBX; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL2, reg); + + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); + reg &= ~MDIO_CTRL1_SPEEDSEL; + reg |= MDIO_CTRL1_SPEED1G; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg); + + xgbe_phy_pcs_power_cycle(pdata); + + /* Set SerDes to 2.5G speed */ + xgbe_phy_start_ratechange(pdata); + + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, DATARATE, XGBE_SPEED_2500_RATE); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, WORDMODE, XGBE_SPEED_2500_WORD); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PLLSEL, XGBE_SPEED_2500_PLL); + + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, CDR_RATE, + phy_data->cdr_rate[XGBE_SPEED_2500]); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, TXAMP, + phy_data->tx_amp[XGBE_SPEED_2500]); + XRXTX_IOWRITE_BITS(pdata, RXTX_REG20, BLWC_ENA, + phy_data->blwc[XGBE_SPEED_2500]); + XRXTX_IOWRITE_BITS(pdata, RXTX_REG114, PQ_REG, + phy_data->pq_skew[XGBE_SPEED_2500]); + XRXTX_IOWRITE_BITS(pdata, RXTX_REG129, RXDFE_CONFIG, + phy_data->dfe_tap_cfg[XGBE_SPEED_2500]); + XRXTX_IOWRITE(pdata, RXTX_REG22, + phy_data->dfe_tap_ena[XGBE_SPEED_2500]); + + xgbe_phy_complete_ratechange(pdata); + + netif_dbg(pdata, link, pdata->netdev, "2.5GbE KX mode set\n"); +} + +static void xgbe_phy_kx_1000_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int reg; + + /* Set PCS to KX/1G speed */ + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2); + reg &= ~MDIO_PCS_CTRL2_TYPE; + reg |= MDIO_PCS_CTRL2_10GBX; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL2, reg); + + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); + reg &= ~MDIO_CTRL1_SPEEDSEL; + reg |= MDIO_CTRL1_SPEED1G; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg); + + xgbe_phy_pcs_power_cycle(pdata); + + /* Set SerDes to 1G speed */ + xgbe_phy_start_ratechange(pdata); + + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, DATARATE, XGBE_SPEED_1000_RATE); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, WORDMODE, XGBE_SPEED_1000_WORD); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PLLSEL, XGBE_SPEED_1000_PLL); + + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, CDR_RATE, + phy_data->cdr_rate[XGBE_SPEED_1000]); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, TXAMP, + phy_data->tx_amp[XGBE_SPEED_1000]); + XRXTX_IOWRITE_BITS(pdata, RXTX_REG20, BLWC_ENA, + phy_data->blwc[XGBE_SPEED_1000]); + XRXTX_IOWRITE_BITS(pdata, RXTX_REG114, PQ_REG, + phy_data->pq_skew[XGBE_SPEED_1000]); + XRXTX_IOWRITE_BITS(pdata, RXTX_REG129, RXDFE_CONFIG, + phy_data->dfe_tap_cfg[XGBE_SPEED_1000]); + XRXTX_IOWRITE(pdata, RXTX_REG22, + phy_data->dfe_tap_ena[XGBE_SPEED_1000]); + + xgbe_phy_complete_ratechange(pdata); + + netif_dbg(pdata, link, pdata->netdev, "1GbE KX mode set\n"); +} + +static enum xgbe_mode xgbe_phy_cur_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + enum xgbe_mode mode; + unsigned int reg; + + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2); + reg &= MDIO_PCS_CTRL2_TYPE; + + if (reg == MDIO_PCS_CTRL2_10GBR) { + mode = XGBE_MODE_KR; + } else { + if (phy_data->speed_set == XGBE_SPEEDSET_2500_10000) + mode = XGBE_MODE_KX_2500; + else + mode = XGBE_MODE_KX_1000; + } + + return mode; +} + +static enum xgbe_mode xgbe_phy_switch_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + enum xgbe_mode mode; + + /* If we are in KR switch to KX, and vice-versa */ + if (xgbe_phy_cur_mode(pdata) == XGBE_MODE_KR) { + if (phy_data->speed_set == XGBE_SPEEDSET_2500_10000) + mode = XGBE_MODE_KX_2500; + else + mode = XGBE_MODE_KX_1000; + } else { + mode = XGBE_MODE_KR; + } + + return mode; +} + +static enum xgbe_mode xgbe_phy_get_mode(struct xgbe_prv_data *pdata, + int speed) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + switch (speed) { + case SPEED_1000: + return (phy_data->speed_set == XGBE_SPEEDSET_1000_10000) + ? XGBE_MODE_KX_1000 : XGBE_MODE_UNKNOWN; + case SPEED_2500: + return (phy_data->speed_set == XGBE_SPEEDSET_2500_10000) + ? XGBE_MODE_KX_2500 : XGBE_MODE_UNKNOWN; + case SPEED_10000: + return XGBE_MODE_KR; + default: + return XGBE_MODE_UNKNOWN; + } +} + +static void xgbe_phy_set_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) +{ + switch (mode) { + case XGBE_MODE_KX_1000: + xgbe_phy_kx_1000_mode(pdata); + break; + case XGBE_MODE_KX_2500: + xgbe_phy_kx_2500_mode(pdata); + break; + case XGBE_MODE_KR: + xgbe_phy_kr_mode(pdata); + break; + default: + break; + } +} + +static bool xgbe_phy_check_mode(struct xgbe_prv_data *pdata, + enum xgbe_mode mode, bool advert) +{ + if (pdata->phy.autoneg == AUTONEG_ENABLE) { + return advert; + } else { + enum xgbe_mode cur_mode; + + cur_mode = xgbe_phy_get_mode(pdata, pdata->phy.speed); + if (cur_mode == mode) + return true; + } + + return false; +} + +static bool xgbe_phy_use_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) +{ + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + + switch (mode) { + case XGBE_MODE_KX_1000: + return xgbe_phy_check_mode(pdata, mode, + XGBE_ADV(lks, 1000baseKX_Full)); + case XGBE_MODE_KX_2500: + return xgbe_phy_check_mode(pdata, mode, + XGBE_ADV(lks, 2500baseX_Full)); + case XGBE_MODE_KR: + return xgbe_phy_check_mode(pdata, mode, + XGBE_ADV(lks, 10000baseKR_Full)); + default: + return false; + } +} + +static bool xgbe_phy_valid_speed(struct xgbe_prv_data *pdata, int speed) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + switch (speed) { + case SPEED_1000: + if (phy_data->speed_set != XGBE_SPEEDSET_1000_10000) + return false; + return true; + case SPEED_2500: + if (phy_data->speed_set != XGBE_SPEEDSET_2500_10000) + return false; + return true; + case SPEED_10000: + return true; + default: + return false; + } +} + +static int xgbe_phy_link_status(struct xgbe_prv_data *pdata, int *an_restart) +{ + unsigned int reg; + + *an_restart = 0; + + /* Link status is latched low, so read once to clear + * and then read again to get current state + */ + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1); + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1); + + return (reg & MDIO_STAT1_LSTATUS) ? 1 : 0; +} + +static void xgbe_phy_stop(struct xgbe_prv_data *pdata) +{ + /* Nothing uniquely required for stop */ +} + +static int xgbe_phy_start(struct xgbe_prv_data *pdata) +{ + /* Nothing uniquely required for start */ + return 0; +} + +static int xgbe_phy_reset(struct xgbe_prv_data *pdata) +{ + unsigned int reg, count; + + /* Perform a software reset of the PCS */ + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); + reg |= MDIO_CTRL1_RESET; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg); + + count = 50; + do { + msleep(20); + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); + } while ((reg & MDIO_CTRL1_RESET) && --count); + + if (reg & MDIO_CTRL1_RESET) + return -ETIMEDOUT; + + return 0; +} + +static void xgbe_phy_exit(struct xgbe_prv_data *pdata) +{ + /* Nothing uniquely required for exit */ +} + +static int xgbe_phy_init(struct xgbe_prv_data *pdata) +{ + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + struct xgbe_phy_data *phy_data; + int ret; + + phy_data = devm_kzalloc(pdata->dev, sizeof(*phy_data), GFP_KERNEL); + if (!phy_data) + return -ENOMEM; + + /* Retrieve the PHY speedset */ + ret = device_property_read_u32(pdata->phy_dev, XGBE_SPEEDSET_PROPERTY, + &phy_data->speed_set); + if (ret) { + dev_err(pdata->dev, "invalid %s property\n", + XGBE_SPEEDSET_PROPERTY); + return ret; + } + + switch (phy_data->speed_set) { + case XGBE_SPEEDSET_1000_10000: + case XGBE_SPEEDSET_2500_10000: + break; + default: + dev_err(pdata->dev, "invalid %s property\n", + XGBE_SPEEDSET_PROPERTY); + return -EINVAL; + } + + /* Retrieve the PHY configuration properties */ + if (device_property_present(pdata->phy_dev, XGBE_BLWC_PROPERTY)) { + ret = device_property_read_u32_array(pdata->phy_dev, + XGBE_BLWC_PROPERTY, + phy_data->blwc, + XGBE_SPEEDS); + if (ret) { + dev_err(pdata->dev, "invalid %s property\n", + XGBE_BLWC_PROPERTY); + return ret; + } + } else { + memcpy(phy_data->blwc, xgbe_phy_blwc, + sizeof(phy_data->blwc)); + } + + if (device_property_present(pdata->phy_dev, XGBE_CDR_RATE_PROPERTY)) { + ret = device_property_read_u32_array(pdata->phy_dev, + XGBE_CDR_RATE_PROPERTY, + phy_data->cdr_rate, + XGBE_SPEEDS); + if (ret) { + dev_err(pdata->dev, "invalid %s property\n", + XGBE_CDR_RATE_PROPERTY); + return ret; + } + } else { + memcpy(phy_data->cdr_rate, xgbe_phy_cdr_rate, + sizeof(phy_data->cdr_rate)); + } + + if (device_property_present(pdata->phy_dev, XGBE_PQ_SKEW_PROPERTY)) { + ret = device_property_read_u32_array(pdata->phy_dev, + XGBE_PQ_SKEW_PROPERTY, + phy_data->pq_skew, + XGBE_SPEEDS); + if (ret) { + dev_err(pdata->dev, "invalid %s property\n", + XGBE_PQ_SKEW_PROPERTY); + return ret; + } + } else { + memcpy(phy_data->pq_skew, xgbe_phy_pq_skew, + sizeof(phy_data->pq_skew)); + } + + if (device_property_present(pdata->phy_dev, XGBE_TX_AMP_PROPERTY)) { + ret = device_property_read_u32_array(pdata->phy_dev, + XGBE_TX_AMP_PROPERTY, + phy_data->tx_amp, + XGBE_SPEEDS); + if (ret) { + dev_err(pdata->dev, "invalid %s property\n", + XGBE_TX_AMP_PROPERTY); + return ret; + } + } else { + memcpy(phy_data->tx_amp, xgbe_phy_tx_amp, + sizeof(phy_data->tx_amp)); + } + + if (device_property_present(pdata->phy_dev, XGBE_DFE_CFG_PROPERTY)) { + ret = device_property_read_u32_array(pdata->phy_dev, + XGBE_DFE_CFG_PROPERTY, + phy_data->dfe_tap_cfg, + XGBE_SPEEDS); + if (ret) { + dev_err(pdata->dev, "invalid %s property\n", + XGBE_DFE_CFG_PROPERTY); + return ret; + } + } else { + memcpy(phy_data->dfe_tap_cfg, xgbe_phy_dfe_tap_cfg, + sizeof(phy_data->dfe_tap_cfg)); + } + + if (device_property_present(pdata->phy_dev, XGBE_DFE_ENA_PROPERTY)) { + ret = device_property_read_u32_array(pdata->phy_dev, + XGBE_DFE_ENA_PROPERTY, + phy_data->dfe_tap_ena, + XGBE_SPEEDS); + if (ret) { + dev_err(pdata->dev, "invalid %s property\n", + XGBE_DFE_ENA_PROPERTY); + return ret; + } + } else { + memcpy(phy_data->dfe_tap_ena, xgbe_phy_dfe_tap_ena, + sizeof(phy_data->dfe_tap_ena)); + } + + /* Initialize supported features */ + XGBE_ZERO_SUP(lks); + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, Backplane); + XGBE_SET_SUP(lks, 10000baseKR_Full); + switch (phy_data->speed_set) { + case XGBE_SPEEDSET_1000_10000: + XGBE_SET_SUP(lks, 1000baseKX_Full); + break; + case XGBE_SPEEDSET_2500_10000: + XGBE_SET_SUP(lks, 2500baseX_Full); + break; + } + + if (pdata->fec_ability & MDIO_PMA_10GBR_FECABLE_ABLE) + XGBE_SET_SUP(lks, 10000baseR_FEC); + + pdata->phy_data = phy_data; + + return 0; +} + +void xgbe_init_function_ptrs_phy_v1(struct xgbe_phy_if *phy_if) +{ + struct xgbe_phy_impl_if *phy_impl = &phy_if->phy_impl; + + phy_impl->init = xgbe_phy_init; + phy_impl->exit = xgbe_phy_exit; + + phy_impl->reset = xgbe_phy_reset; + phy_impl->start = xgbe_phy_start; + phy_impl->stop = xgbe_phy_stop; + + phy_impl->link_status = xgbe_phy_link_status; + + phy_impl->valid_speed = xgbe_phy_valid_speed; + + phy_impl->use_mode = xgbe_phy_use_mode; + phy_impl->set_mode = xgbe_phy_set_mode; + phy_impl->get_mode = xgbe_phy_get_mode; + phy_impl->switch_mode = xgbe_phy_switch_mode; + phy_impl->cur_mode = xgbe_phy_cur_mode; + + phy_impl->an_mode = xgbe_phy_an_mode; + + phy_impl->an_config = xgbe_phy_an_config; + + phy_impl->an_advertising = xgbe_phy_an_advertising; + + phy_impl->an_outcome = xgbe_phy_an_outcome; + + phy_impl->kr_training_pre = xgbe_phy_kr_training_pre; + phy_impl->kr_training_post = xgbe_phy_kr_training_post; +} diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c new file mode 100644 index 000000000..c731a0473 --- /dev/null +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c @@ -0,0 +1,3470 @@ +/* + * AMD 10Gb Ethernet driver + * + * This file is available to you under your choice of the following two + * licenses: + * + * License 1: GPLv2 + * + * Copyright (c) 2016 Advanced Micro Devices, Inc. + * + * This file is free software; you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or (at + * your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * + * License 2: Modified BSD + * + * Copyright (c) 2016 Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/kmod.h> +#include <linux/mdio.h> +#include <linux/phy.h> +#include <linux/ethtool.h> + +#include "xgbe.h" +#include "xgbe-common.h" + +#define XGBE_PHY_PORT_SPEED_100 BIT(1) +#define XGBE_PHY_PORT_SPEED_1000 BIT(2) +#define XGBE_PHY_PORT_SPEED_2500 BIT(3) +#define XGBE_PHY_PORT_SPEED_10000 BIT(4) + +#define XGBE_MUTEX_RELEASE 0x80000000 + +#define XGBE_SFP_DIRECT 7 + +/* I2C target addresses */ +#define XGBE_SFP_SERIAL_ID_ADDRESS 0x50 +#define XGBE_SFP_DIAG_INFO_ADDRESS 0x51 +#define XGBE_SFP_PHY_ADDRESS 0x56 +#define XGBE_GPIO_ADDRESS_PCA9555 0x20 + +/* SFP sideband signal indicators */ +#define XGBE_GPIO_NO_TX_FAULT BIT(0) +#define XGBE_GPIO_NO_RATE_SELECT BIT(1) +#define XGBE_GPIO_NO_MOD_ABSENT BIT(2) +#define XGBE_GPIO_NO_RX_LOS BIT(3) + +/* Rate-change complete wait/retry count */ +#define XGBE_RATECHANGE_COUNT 500 + +/* CDR delay values for KR support (in usec) */ +#define XGBE_CDR_DELAY_INIT 10000 +#define XGBE_CDR_DELAY_INC 10000 +#define XGBE_CDR_DELAY_MAX 100000 + +/* RRC frequency during link status check */ +#define XGBE_RRC_FREQUENCY 10 + +enum xgbe_port_mode { + XGBE_PORT_MODE_RSVD = 0, + XGBE_PORT_MODE_BACKPLANE, + XGBE_PORT_MODE_BACKPLANE_2500, + XGBE_PORT_MODE_1000BASE_T, + XGBE_PORT_MODE_1000BASE_X, + XGBE_PORT_MODE_NBASE_T, + XGBE_PORT_MODE_10GBASE_T, + XGBE_PORT_MODE_10GBASE_R, + XGBE_PORT_MODE_SFP, + XGBE_PORT_MODE_BACKPLANE_NO_AUTONEG, + XGBE_PORT_MODE_MAX, +}; + +enum xgbe_conn_type { + XGBE_CONN_TYPE_NONE = 0, + XGBE_CONN_TYPE_SFP, + XGBE_CONN_TYPE_MDIO, + XGBE_CONN_TYPE_RSVD1, + XGBE_CONN_TYPE_BACKPLANE, + XGBE_CONN_TYPE_MAX, +}; + +/* SFP/SFP+ related definitions */ +enum xgbe_sfp_comm { + XGBE_SFP_COMM_DIRECT = 0, + XGBE_SFP_COMM_PCA9545, +}; + +enum xgbe_sfp_cable { + XGBE_SFP_CABLE_UNKNOWN = 0, + XGBE_SFP_CABLE_ACTIVE, + XGBE_SFP_CABLE_PASSIVE, + XGBE_SFP_CABLE_FIBER, +}; + +enum xgbe_sfp_base { + XGBE_SFP_BASE_UNKNOWN = 0, + XGBE_SFP_BASE_1000_T, + XGBE_SFP_BASE_1000_SX, + XGBE_SFP_BASE_1000_LX, + XGBE_SFP_BASE_1000_CX, + XGBE_SFP_BASE_10000_SR, + XGBE_SFP_BASE_10000_LR, + XGBE_SFP_BASE_10000_LRM, + XGBE_SFP_BASE_10000_ER, + XGBE_SFP_BASE_10000_CR, +}; + +enum xgbe_sfp_speed { + XGBE_SFP_SPEED_UNKNOWN = 0, + XGBE_SFP_SPEED_100_1000, + XGBE_SFP_SPEED_1000, + XGBE_SFP_SPEED_10000, +}; + +/* SFP Serial ID Base ID values relative to an offset of 0 */ +#define XGBE_SFP_BASE_ID 0 +#define XGBE_SFP_ID_SFP 0x03 + +#define XGBE_SFP_BASE_EXT_ID 1 +#define XGBE_SFP_EXT_ID_SFP 0x04 + +#define XGBE_SFP_BASE_10GBE_CC 3 +#define XGBE_SFP_BASE_10GBE_CC_SR BIT(4) +#define XGBE_SFP_BASE_10GBE_CC_LR BIT(5) +#define XGBE_SFP_BASE_10GBE_CC_LRM BIT(6) +#define XGBE_SFP_BASE_10GBE_CC_ER BIT(7) + +#define XGBE_SFP_BASE_1GBE_CC 6 +#define XGBE_SFP_BASE_1GBE_CC_SX BIT(0) +#define XGBE_SFP_BASE_1GBE_CC_LX BIT(1) +#define XGBE_SFP_BASE_1GBE_CC_CX BIT(2) +#define XGBE_SFP_BASE_1GBE_CC_T BIT(3) + +#define XGBE_SFP_BASE_CABLE 8 +#define XGBE_SFP_BASE_CABLE_PASSIVE BIT(2) +#define XGBE_SFP_BASE_CABLE_ACTIVE BIT(3) + +#define XGBE_SFP_BASE_BR 12 +#define XGBE_SFP_BASE_BR_1GBE_MIN 0x0a +#define XGBE_SFP_BASE_BR_10GBE_MIN 0x64 + +#define XGBE_SFP_BASE_CU_CABLE_LEN 18 + +#define XGBE_SFP_BASE_VENDOR_NAME 20 +#define XGBE_SFP_BASE_VENDOR_NAME_LEN 16 +#define XGBE_SFP_BASE_VENDOR_PN 40 +#define XGBE_SFP_BASE_VENDOR_PN_LEN 16 +#define XGBE_SFP_BASE_VENDOR_REV 56 +#define XGBE_SFP_BASE_VENDOR_REV_LEN 4 + +#define XGBE_SFP_BASE_CC 63 + +/* SFP Serial ID Extended ID values relative to an offset of 64 */ +#define XGBE_SFP_BASE_VENDOR_SN 4 +#define XGBE_SFP_BASE_VENDOR_SN_LEN 16 + +#define XGBE_SFP_EXTD_OPT1 1 +#define XGBE_SFP_EXTD_OPT1_RX_LOS BIT(1) +#define XGBE_SFP_EXTD_OPT1_TX_FAULT BIT(3) + +#define XGBE_SFP_EXTD_DIAG 28 +#define XGBE_SFP_EXTD_DIAG_ADDR_CHANGE BIT(2) + +#define XGBE_SFP_EXTD_SFF_8472 30 + +#define XGBE_SFP_EXTD_CC 31 + +struct xgbe_sfp_eeprom { + u8 base[64]; + u8 extd[32]; + u8 vendor[32]; +}; + +#define XGBE_SFP_DIAGS_SUPPORTED(_x) \ + ((_x)->extd[XGBE_SFP_EXTD_SFF_8472] && \ + !((_x)->extd[XGBE_SFP_EXTD_DIAG] & XGBE_SFP_EXTD_DIAG_ADDR_CHANGE)) + +#define XGBE_SFP_EEPROM_BASE_LEN 256 +#define XGBE_SFP_EEPROM_DIAG_LEN 256 +#define XGBE_SFP_EEPROM_MAX (XGBE_SFP_EEPROM_BASE_LEN + \ + XGBE_SFP_EEPROM_DIAG_LEN) + +#define XGBE_BEL_FUSE_VENDOR "BEL-FUSE " +#define XGBE_BEL_FUSE_PARTNO "1GBT-SFP06 " + +#define XGBE_MOLEX_VENDOR "Molex Inc. " + +struct xgbe_sfp_ascii { + union { + char vendor[XGBE_SFP_BASE_VENDOR_NAME_LEN + 1]; + char partno[XGBE_SFP_BASE_VENDOR_PN_LEN + 1]; + char rev[XGBE_SFP_BASE_VENDOR_REV_LEN + 1]; + char serno[XGBE_SFP_BASE_VENDOR_SN_LEN + 1]; + } u; +}; + +/* MDIO PHY reset types */ +enum xgbe_mdio_reset { + XGBE_MDIO_RESET_NONE = 0, + XGBE_MDIO_RESET_I2C_GPIO, + XGBE_MDIO_RESET_INT_GPIO, + XGBE_MDIO_RESET_MAX, +}; + +/* Re-driver related definitions */ +enum xgbe_phy_redrv_if { + XGBE_PHY_REDRV_IF_MDIO = 0, + XGBE_PHY_REDRV_IF_I2C, + XGBE_PHY_REDRV_IF_MAX, +}; + +enum xgbe_phy_redrv_model { + XGBE_PHY_REDRV_MODEL_4223 = 0, + XGBE_PHY_REDRV_MODEL_4227, + XGBE_PHY_REDRV_MODEL_MAX, +}; + +enum xgbe_phy_redrv_mode { + XGBE_PHY_REDRV_MODE_CX = 5, + XGBE_PHY_REDRV_MODE_SR = 9, +}; + +#define XGBE_PHY_REDRV_MODE_REG 0x12b0 + +/* PHY related configuration information */ +struct xgbe_phy_data { + enum xgbe_port_mode port_mode; + + unsigned int port_id; + + unsigned int port_speeds; + + enum xgbe_conn_type conn_type; + + enum xgbe_mode cur_mode; + enum xgbe_mode start_mode; + + unsigned int rrc_count; + + unsigned int mdio_addr; + + /* SFP Support */ + enum xgbe_sfp_comm sfp_comm; + unsigned int sfp_mux_address; + unsigned int sfp_mux_channel; + + unsigned int sfp_gpio_address; + unsigned int sfp_gpio_mask; + unsigned int sfp_gpio_inputs; + unsigned int sfp_gpio_rx_los; + unsigned int sfp_gpio_tx_fault; + unsigned int sfp_gpio_mod_absent; + unsigned int sfp_gpio_rate_select; + + unsigned int sfp_rx_los; + unsigned int sfp_tx_fault; + unsigned int sfp_mod_absent; + unsigned int sfp_changed; + unsigned int sfp_phy_avail; + unsigned int sfp_cable_len; + enum xgbe_sfp_base sfp_base; + enum xgbe_sfp_cable sfp_cable; + enum xgbe_sfp_speed sfp_speed; + struct xgbe_sfp_eeprom sfp_eeprom; + + /* External PHY support */ + enum xgbe_mdio_mode phydev_mode; + struct mii_bus *mii; + struct phy_device *phydev; + enum xgbe_mdio_reset mdio_reset; + unsigned int mdio_reset_addr; + unsigned int mdio_reset_gpio; + + /* Re-driver support */ + unsigned int redrv; + unsigned int redrv_if; + unsigned int redrv_addr; + unsigned int redrv_lane; + unsigned int redrv_model; + + /* KR AN support */ + unsigned int phy_cdr_notrack; + unsigned int phy_cdr_delay; +}; + +/* I2C, MDIO and GPIO lines are muxed, so only one device at a time */ +static DEFINE_MUTEX(xgbe_phy_comm_lock); + +static enum xgbe_an_mode xgbe_phy_an_mode(struct xgbe_prv_data *pdata); + +static int xgbe_phy_i2c_xfer(struct xgbe_prv_data *pdata, + struct xgbe_i2c_op *i2c_op) +{ + return pdata->i2c_if.i2c_xfer(pdata, i2c_op); +} + +static int xgbe_phy_redrv_write(struct xgbe_prv_data *pdata, unsigned int reg, + unsigned int val) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + struct xgbe_i2c_op i2c_op; + __be16 *redrv_val; + u8 redrv_data[5], csum; + unsigned int i, retry; + int ret; + + /* High byte of register contains read/write indicator */ + redrv_data[0] = ((reg >> 8) & 0xff) << 1; + redrv_data[1] = reg & 0xff; + redrv_val = (__be16 *)&redrv_data[2]; + *redrv_val = cpu_to_be16(val); + + /* Calculate 1 byte checksum */ + csum = 0; + for (i = 0; i < 4; i++) { + csum += redrv_data[i]; + if (redrv_data[i] > csum) + csum++; + } + redrv_data[4] = ~csum; + + retry = 1; +again1: + i2c_op.cmd = XGBE_I2C_CMD_WRITE; + i2c_op.target = phy_data->redrv_addr; + i2c_op.len = sizeof(redrv_data); + i2c_op.buf = redrv_data; + ret = xgbe_phy_i2c_xfer(pdata, &i2c_op); + if (ret) { + if ((ret == -EAGAIN) && retry--) + goto again1; + + return ret; + } + + retry = 1; +again2: + i2c_op.cmd = XGBE_I2C_CMD_READ; + i2c_op.target = phy_data->redrv_addr; + i2c_op.len = 1; + i2c_op.buf = redrv_data; + ret = xgbe_phy_i2c_xfer(pdata, &i2c_op); + if (ret) { + if ((ret == -EAGAIN) && retry--) + goto again2; + + return ret; + } + + if (redrv_data[0] != 0xff) { + netif_dbg(pdata, drv, pdata->netdev, + "Redriver write checksum error\n"); + ret = -EIO; + } + + return ret; +} + +static int xgbe_phy_i2c_write(struct xgbe_prv_data *pdata, unsigned int target, + void *val, unsigned int val_len) +{ + struct xgbe_i2c_op i2c_op; + int retry, ret; + + retry = 1; +again: + /* Write the specfied register */ + i2c_op.cmd = XGBE_I2C_CMD_WRITE; + i2c_op.target = target; + i2c_op.len = val_len; + i2c_op.buf = val; + ret = xgbe_phy_i2c_xfer(pdata, &i2c_op); + if ((ret == -EAGAIN) && retry--) + goto again; + + return ret; +} + +static int xgbe_phy_i2c_read(struct xgbe_prv_data *pdata, unsigned int target, + void *reg, unsigned int reg_len, + void *val, unsigned int val_len) +{ + struct xgbe_i2c_op i2c_op; + int retry, ret; + + retry = 1; +again1: + /* Set the specified register to read */ + i2c_op.cmd = XGBE_I2C_CMD_WRITE; + i2c_op.target = target; + i2c_op.len = reg_len; + i2c_op.buf = reg; + ret = xgbe_phy_i2c_xfer(pdata, &i2c_op); + if (ret) { + if ((ret == -EAGAIN) && retry--) + goto again1; + + return ret; + } + + retry = 1; +again2: + /* Read the specfied register */ + i2c_op.cmd = XGBE_I2C_CMD_READ; + i2c_op.target = target; + i2c_op.len = val_len; + i2c_op.buf = val; + ret = xgbe_phy_i2c_xfer(pdata, &i2c_op); + if ((ret == -EAGAIN) && retry--) + goto again2; + + return ret; +} + +static int xgbe_phy_sfp_put_mux(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + struct xgbe_i2c_op i2c_op; + u8 mux_channel; + + if (phy_data->sfp_comm == XGBE_SFP_COMM_DIRECT) + return 0; + + /* Select no mux channels */ + mux_channel = 0; + i2c_op.cmd = XGBE_I2C_CMD_WRITE; + i2c_op.target = phy_data->sfp_mux_address; + i2c_op.len = sizeof(mux_channel); + i2c_op.buf = &mux_channel; + + return xgbe_phy_i2c_xfer(pdata, &i2c_op); +} + +static int xgbe_phy_sfp_get_mux(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + struct xgbe_i2c_op i2c_op; + u8 mux_channel; + + if (phy_data->sfp_comm == XGBE_SFP_COMM_DIRECT) + return 0; + + /* Select desired mux channel */ + mux_channel = 1 << phy_data->sfp_mux_channel; + i2c_op.cmd = XGBE_I2C_CMD_WRITE; + i2c_op.target = phy_data->sfp_mux_address; + i2c_op.len = sizeof(mux_channel); + i2c_op.buf = &mux_channel; + + return xgbe_phy_i2c_xfer(pdata, &i2c_op); +} + +static void xgbe_phy_put_comm_ownership(struct xgbe_prv_data *pdata) +{ + mutex_unlock(&xgbe_phy_comm_lock); +} + +static int xgbe_phy_get_comm_ownership(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned long timeout; + unsigned int mutex_id; + + /* The I2C and MDIO/GPIO bus is multiplexed between multiple devices, + * the driver needs to take the software mutex and then the hardware + * mutexes before being able to use the busses. + */ + mutex_lock(&xgbe_phy_comm_lock); + + /* Clear the mutexes */ + XP_IOWRITE(pdata, XP_I2C_MUTEX, XGBE_MUTEX_RELEASE); + XP_IOWRITE(pdata, XP_MDIO_MUTEX, XGBE_MUTEX_RELEASE); + + /* Mutex formats are the same for I2C and MDIO/GPIO */ + mutex_id = 0; + XP_SET_BITS(mutex_id, XP_I2C_MUTEX, ID, phy_data->port_id); + XP_SET_BITS(mutex_id, XP_I2C_MUTEX, ACTIVE, 1); + + timeout = jiffies + (5 * HZ); + while (time_before(jiffies, timeout)) { + /* Must be all zeroes in order to obtain the mutex */ + if (XP_IOREAD(pdata, XP_I2C_MUTEX) || + XP_IOREAD(pdata, XP_MDIO_MUTEX)) { + usleep_range(100, 200); + continue; + } + + /* Obtain the mutex */ + XP_IOWRITE(pdata, XP_I2C_MUTEX, mutex_id); + XP_IOWRITE(pdata, XP_MDIO_MUTEX, mutex_id); + + return 0; + } + + mutex_unlock(&xgbe_phy_comm_lock); + + netdev_err(pdata->netdev, "unable to obtain hardware mutexes\n"); + + return -ETIMEDOUT; +} + +static int xgbe_phy_mdio_mii_write(struct xgbe_prv_data *pdata, int addr, + int reg, u16 val) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + if (reg & MII_ADDR_C45) { + if (phy_data->phydev_mode != XGBE_MDIO_MODE_CL45) + return -ENOTSUPP; + } else { + if (phy_data->phydev_mode != XGBE_MDIO_MODE_CL22) + return -ENOTSUPP; + } + + return pdata->hw_if.write_ext_mii_regs(pdata, addr, reg, val); +} + +static int xgbe_phy_i2c_mii_write(struct xgbe_prv_data *pdata, int reg, u16 val) +{ + __be16 *mii_val; + u8 mii_data[3]; + int ret; + + ret = xgbe_phy_sfp_get_mux(pdata); + if (ret) + return ret; + + mii_data[0] = reg & 0xff; + mii_val = (__be16 *)&mii_data[1]; + *mii_val = cpu_to_be16(val); + + ret = xgbe_phy_i2c_write(pdata, XGBE_SFP_PHY_ADDRESS, + mii_data, sizeof(mii_data)); + + xgbe_phy_sfp_put_mux(pdata); + + return ret; +} + +static int xgbe_phy_mii_write(struct mii_bus *mii, int addr, int reg, u16 val) +{ + struct xgbe_prv_data *pdata = mii->priv; + struct xgbe_phy_data *phy_data = pdata->phy_data; + int ret; + + ret = xgbe_phy_get_comm_ownership(pdata); + if (ret) + return ret; + + if (phy_data->conn_type == XGBE_CONN_TYPE_SFP) + ret = xgbe_phy_i2c_mii_write(pdata, reg, val); + else if (phy_data->conn_type & XGBE_CONN_TYPE_MDIO) + ret = xgbe_phy_mdio_mii_write(pdata, addr, reg, val); + else + ret = -ENOTSUPP; + + xgbe_phy_put_comm_ownership(pdata); + + return ret; +} + +static int xgbe_phy_mdio_mii_read(struct xgbe_prv_data *pdata, int addr, + int reg) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + if (reg & MII_ADDR_C45) { + if (phy_data->phydev_mode != XGBE_MDIO_MODE_CL45) + return -ENOTSUPP; + } else { + if (phy_data->phydev_mode != XGBE_MDIO_MODE_CL22) + return -ENOTSUPP; + } + + return pdata->hw_if.read_ext_mii_regs(pdata, addr, reg); +} + +static int xgbe_phy_i2c_mii_read(struct xgbe_prv_data *pdata, int reg) +{ + __be16 mii_val; + u8 mii_reg; + int ret; + + ret = xgbe_phy_sfp_get_mux(pdata); + if (ret) + return ret; + + mii_reg = reg; + ret = xgbe_phy_i2c_read(pdata, XGBE_SFP_PHY_ADDRESS, + &mii_reg, sizeof(mii_reg), + &mii_val, sizeof(mii_val)); + if (!ret) + ret = be16_to_cpu(mii_val); + + xgbe_phy_sfp_put_mux(pdata); + + return ret; +} + +static int xgbe_phy_mii_read(struct mii_bus *mii, int addr, int reg) +{ + struct xgbe_prv_data *pdata = mii->priv; + struct xgbe_phy_data *phy_data = pdata->phy_data; + int ret; + + ret = xgbe_phy_get_comm_ownership(pdata); + if (ret) + return ret; + + if (phy_data->conn_type == XGBE_CONN_TYPE_SFP) + ret = xgbe_phy_i2c_mii_read(pdata, reg); + else if (phy_data->conn_type & XGBE_CONN_TYPE_MDIO) + ret = xgbe_phy_mdio_mii_read(pdata, addr, reg); + else + ret = -ENOTSUPP; + + xgbe_phy_put_comm_ownership(pdata); + + return ret; +} + +static void xgbe_phy_sfp_phy_settings(struct xgbe_prv_data *pdata) +{ + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + struct xgbe_phy_data *phy_data = pdata->phy_data; + + if (!phy_data->sfp_mod_absent && !phy_data->sfp_changed) + return; + + XGBE_ZERO_SUP(lks); + + if (phy_data->sfp_mod_absent) { + pdata->phy.speed = SPEED_UNKNOWN; + pdata->phy.duplex = DUPLEX_UNKNOWN; + pdata->phy.autoneg = AUTONEG_ENABLE; + pdata->phy.pause_autoneg = AUTONEG_ENABLE; + + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, TP); + XGBE_SET_SUP(lks, FIBRE); + + XGBE_LM_COPY(lks, advertising, lks, supported); + + return; + } + + switch (phy_data->sfp_base) { + case XGBE_SFP_BASE_1000_T: + case XGBE_SFP_BASE_1000_SX: + case XGBE_SFP_BASE_1000_LX: + case XGBE_SFP_BASE_1000_CX: + pdata->phy.speed = SPEED_UNKNOWN; + pdata->phy.duplex = DUPLEX_UNKNOWN; + pdata->phy.autoneg = AUTONEG_ENABLE; + pdata->phy.pause_autoneg = AUTONEG_ENABLE; + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + if (phy_data->sfp_base == XGBE_SFP_BASE_1000_T) { + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) + XGBE_SET_SUP(lks, 100baseT_Full); + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) + XGBE_SET_SUP(lks, 1000baseT_Full); + } else { + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) + XGBE_SET_SUP(lks, 1000baseX_Full); + } + break; + case XGBE_SFP_BASE_10000_SR: + case XGBE_SFP_BASE_10000_LR: + case XGBE_SFP_BASE_10000_LRM: + case XGBE_SFP_BASE_10000_ER: + case XGBE_SFP_BASE_10000_CR: + pdata->phy.speed = SPEED_10000; + pdata->phy.duplex = DUPLEX_FULL; + pdata->phy.autoneg = AUTONEG_DISABLE; + pdata->phy.pause_autoneg = AUTONEG_DISABLE; + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) { + switch (phy_data->sfp_base) { + case XGBE_SFP_BASE_10000_SR: + XGBE_SET_SUP(lks, 10000baseSR_Full); + break; + case XGBE_SFP_BASE_10000_LR: + XGBE_SET_SUP(lks, 10000baseLR_Full); + break; + case XGBE_SFP_BASE_10000_LRM: + XGBE_SET_SUP(lks, 10000baseLRM_Full); + break; + case XGBE_SFP_BASE_10000_ER: + XGBE_SET_SUP(lks, 10000baseER_Full); + break; + case XGBE_SFP_BASE_10000_CR: + XGBE_SET_SUP(lks, 10000baseCR_Full); + break; + default: + break; + } + } + break; + default: + pdata->phy.speed = SPEED_UNKNOWN; + pdata->phy.duplex = DUPLEX_UNKNOWN; + pdata->phy.autoneg = AUTONEG_DISABLE; + pdata->phy.pause_autoneg = AUTONEG_DISABLE; + break; + } + + switch (phy_data->sfp_base) { + case XGBE_SFP_BASE_1000_T: + case XGBE_SFP_BASE_1000_CX: + case XGBE_SFP_BASE_10000_CR: + XGBE_SET_SUP(lks, TP); + break; + default: + XGBE_SET_SUP(lks, FIBRE); + break; + } + + XGBE_LM_COPY(lks, advertising, lks, supported); +} + +static bool xgbe_phy_sfp_bit_rate(struct xgbe_sfp_eeprom *sfp_eeprom, + enum xgbe_sfp_speed sfp_speed) +{ + u8 *sfp_base, min; + + sfp_base = sfp_eeprom->base; + + switch (sfp_speed) { + case XGBE_SFP_SPEED_1000: + min = XGBE_SFP_BASE_BR_1GBE_MIN; + break; + case XGBE_SFP_SPEED_10000: + min = XGBE_SFP_BASE_BR_10GBE_MIN; + break; + default: + return false; + } + + return sfp_base[XGBE_SFP_BASE_BR] >= min; +} + +static void xgbe_phy_free_phy_device(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + if (phy_data->phydev) { + phy_detach(phy_data->phydev); + phy_device_remove(phy_data->phydev); + phy_device_free(phy_data->phydev); + phy_data->phydev = NULL; + } +} + +static bool xgbe_phy_finisar_phy_quirks(struct xgbe_prv_data *pdata) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0, }; + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int phy_id = phy_data->phydev->phy_id; + + if (phy_data->port_mode != XGBE_PORT_MODE_SFP) + return false; + + if ((phy_id & 0xfffffff0) != 0x01ff0cc0) + return false; + + /* Enable Base-T AN */ + phy_write(phy_data->phydev, 0x16, 0x0001); + phy_write(phy_data->phydev, 0x00, 0x9140); + phy_write(phy_data->phydev, 0x16, 0x0000); + + /* Enable SGMII at 100Base-T/1000Base-T Full Duplex */ + phy_write(phy_data->phydev, 0x1b, 0x9084); + phy_write(phy_data->phydev, 0x09, 0x0e00); + phy_write(phy_data->phydev, 0x00, 0x8140); + phy_write(phy_data->phydev, 0x04, 0x0d01); + phy_write(phy_data->phydev, 0x00, 0x9140); + + linkmode_set_bit_array(phy_10_100_features_array, + ARRAY_SIZE(phy_10_100_features_array), + supported); + linkmode_set_bit_array(phy_gbit_features_array, + ARRAY_SIZE(phy_gbit_features_array), + supported); + + linkmode_copy(phy_data->phydev->supported, supported); + + phy_support_asym_pause(phy_data->phydev); + + netif_dbg(pdata, drv, pdata->netdev, + "Finisar PHY quirk in place\n"); + + return true; +} + +static bool xgbe_phy_belfuse_phy_quirks(struct xgbe_prv_data *pdata) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0, }; + struct xgbe_phy_data *phy_data = pdata->phy_data; + struct xgbe_sfp_eeprom *sfp_eeprom = &phy_data->sfp_eeprom; + unsigned int phy_id = phy_data->phydev->phy_id; + int reg; + + if (phy_data->port_mode != XGBE_PORT_MODE_SFP) + return false; + + if (memcmp(&sfp_eeprom->base[XGBE_SFP_BASE_VENDOR_NAME], + XGBE_BEL_FUSE_VENDOR, XGBE_SFP_BASE_VENDOR_NAME_LEN)) + return false; + + /* For Bel-Fuse, use the extra AN flag */ + pdata->an_again = 1; + + if (memcmp(&sfp_eeprom->base[XGBE_SFP_BASE_VENDOR_PN], + XGBE_BEL_FUSE_PARTNO, XGBE_SFP_BASE_VENDOR_PN_LEN)) + return false; + + if ((phy_id & 0xfffffff0) != 0x03625d10) + return false; + + /* Reset PHY - wait for self-clearing reset bit to clear */ + genphy_soft_reset(phy_data->phydev); + + /* Disable RGMII mode */ + phy_write(phy_data->phydev, 0x18, 0x7007); + reg = phy_read(phy_data->phydev, 0x18); + phy_write(phy_data->phydev, 0x18, reg & ~0x0080); + + /* Enable fiber register bank */ + phy_write(phy_data->phydev, 0x1c, 0x7c00); + reg = phy_read(phy_data->phydev, 0x1c); + reg &= 0x03ff; + reg &= ~0x0001; + phy_write(phy_data->phydev, 0x1c, 0x8000 | 0x7c00 | reg | 0x0001); + + /* Power down SerDes */ + reg = phy_read(phy_data->phydev, 0x00); + phy_write(phy_data->phydev, 0x00, reg | 0x00800); + + /* Configure SGMII-to-Copper mode */ + phy_write(phy_data->phydev, 0x1c, 0x7c00); + reg = phy_read(phy_data->phydev, 0x1c); + reg &= 0x03ff; + reg &= ~0x0006; + phy_write(phy_data->phydev, 0x1c, 0x8000 | 0x7c00 | reg | 0x0004); + + /* Power up SerDes */ + reg = phy_read(phy_data->phydev, 0x00); + phy_write(phy_data->phydev, 0x00, reg & ~0x00800); + + /* Enable copper register bank */ + phy_write(phy_data->phydev, 0x1c, 0x7c00); + reg = phy_read(phy_data->phydev, 0x1c); + reg &= 0x03ff; + reg &= ~0x0001; + phy_write(phy_data->phydev, 0x1c, 0x8000 | 0x7c00 | reg); + + /* Power up SerDes */ + reg = phy_read(phy_data->phydev, 0x00); + phy_write(phy_data->phydev, 0x00, reg & ~0x00800); + + linkmode_set_bit_array(phy_10_100_features_array, + ARRAY_SIZE(phy_10_100_features_array), + supported); + linkmode_set_bit_array(phy_gbit_features_array, + ARRAY_SIZE(phy_gbit_features_array), + supported); + linkmode_copy(phy_data->phydev->supported, supported); + phy_support_asym_pause(phy_data->phydev); + + netif_dbg(pdata, drv, pdata->netdev, + "BelFuse PHY quirk in place\n"); + + return true; +} + +static void xgbe_phy_external_phy_quirks(struct xgbe_prv_data *pdata) +{ + if (xgbe_phy_belfuse_phy_quirks(pdata)) + return; + + if (xgbe_phy_finisar_phy_quirks(pdata)) + return; +} + +static int xgbe_phy_find_phy_device(struct xgbe_prv_data *pdata) +{ + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + struct xgbe_phy_data *phy_data = pdata->phy_data; + struct phy_device *phydev; + int ret; + + /* If we already have a PHY, just return */ + if (phy_data->phydev) + return 0; + + /* Clear the extra AN flag */ + pdata->an_again = 0; + + /* Check for the use of an external PHY */ + if (phy_data->phydev_mode == XGBE_MDIO_MODE_NONE) + return 0; + + /* For SFP, only use an external PHY if available */ + if ((phy_data->port_mode == XGBE_PORT_MODE_SFP) && + !phy_data->sfp_phy_avail) + return 0; + + /* Set the proper MDIO mode for the PHY */ + ret = pdata->hw_if.set_ext_mii_mode(pdata, phy_data->mdio_addr, + phy_data->phydev_mode); + if (ret) { + netdev_err(pdata->netdev, + "mdio port/clause not compatible (%u/%u)\n", + phy_data->mdio_addr, phy_data->phydev_mode); + return ret; + } + + /* Create and connect to the PHY device */ + phydev = get_phy_device(phy_data->mii, phy_data->mdio_addr, + (phy_data->phydev_mode == XGBE_MDIO_MODE_CL45)); + if (IS_ERR(phydev)) { + netdev_err(pdata->netdev, "get_phy_device failed\n"); + return -ENODEV; + } + netif_dbg(pdata, drv, pdata->netdev, "external PHY id is %#010x\n", + phydev->phy_id); + + /*TODO: If c45, add request_module based on one of the MMD ids? */ + + ret = phy_device_register(phydev); + if (ret) { + netdev_err(pdata->netdev, "phy_device_register failed\n"); + phy_device_free(phydev); + return ret; + } + + ret = phy_attach_direct(pdata->netdev, phydev, phydev->dev_flags, + PHY_INTERFACE_MODE_SGMII); + if (ret) { + netdev_err(pdata->netdev, "phy_attach_direct failed\n"); + phy_device_remove(phydev); + phy_device_free(phydev); + return ret; + } + phy_data->phydev = phydev; + + xgbe_phy_external_phy_quirks(pdata); + + linkmode_and(phydev->advertising, phydev->advertising, + lks->link_modes.advertising); + + phy_start_aneg(phy_data->phydev); + + return 0; +} + +static void xgbe_phy_sfp_external_phy(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + int ret; + + if (!phy_data->sfp_changed) + return; + + phy_data->sfp_phy_avail = 0; + + if (phy_data->sfp_base != XGBE_SFP_BASE_1000_T) + return; + + /* Check access to the PHY by reading CTRL1 */ + ret = xgbe_phy_i2c_mii_read(pdata, MII_BMCR); + if (ret < 0) + return; + + /* Successfully accessed the PHY */ + phy_data->sfp_phy_avail = 1; +} + +static bool xgbe_phy_check_sfp_rx_los(struct xgbe_phy_data *phy_data) +{ + u8 *sfp_extd = phy_data->sfp_eeprom.extd; + + if (!(sfp_extd[XGBE_SFP_EXTD_OPT1] & XGBE_SFP_EXTD_OPT1_RX_LOS)) + return false; + + if (phy_data->sfp_gpio_mask & XGBE_GPIO_NO_RX_LOS) + return false; + + if (phy_data->sfp_gpio_inputs & (1 << phy_data->sfp_gpio_rx_los)) + return true; + + return false; +} + +static bool xgbe_phy_check_sfp_tx_fault(struct xgbe_phy_data *phy_data) +{ + u8 *sfp_extd = phy_data->sfp_eeprom.extd; + + if (!(sfp_extd[XGBE_SFP_EXTD_OPT1] & XGBE_SFP_EXTD_OPT1_TX_FAULT)) + return false; + + if (phy_data->sfp_gpio_mask & XGBE_GPIO_NO_TX_FAULT) + return false; + + if (phy_data->sfp_gpio_inputs & (1 << phy_data->sfp_gpio_tx_fault)) + return true; + + return false; +} + +static bool xgbe_phy_check_sfp_mod_absent(struct xgbe_phy_data *phy_data) +{ + if (phy_data->sfp_gpio_mask & XGBE_GPIO_NO_MOD_ABSENT) + return false; + + if (phy_data->sfp_gpio_inputs & (1 << phy_data->sfp_gpio_mod_absent)) + return true; + + return false; +} + +static void xgbe_phy_sfp_parse_eeprom(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + struct xgbe_sfp_eeprom *sfp_eeprom = &phy_data->sfp_eeprom; + u8 *sfp_base; + + sfp_base = sfp_eeprom->base; + + if (sfp_base[XGBE_SFP_BASE_ID] != XGBE_SFP_ID_SFP) + return; + + if (sfp_base[XGBE_SFP_BASE_EXT_ID] != XGBE_SFP_EXT_ID_SFP) + return; + + /* Update transceiver signals (eeprom extd/options) */ + phy_data->sfp_tx_fault = xgbe_phy_check_sfp_tx_fault(phy_data); + phy_data->sfp_rx_los = xgbe_phy_check_sfp_rx_los(phy_data); + + /* Assume FIBER cable unless told otherwise */ + if (sfp_base[XGBE_SFP_BASE_CABLE] & XGBE_SFP_BASE_CABLE_PASSIVE) { + phy_data->sfp_cable = XGBE_SFP_CABLE_PASSIVE; + phy_data->sfp_cable_len = sfp_base[XGBE_SFP_BASE_CU_CABLE_LEN]; + } else if (sfp_base[XGBE_SFP_BASE_CABLE] & XGBE_SFP_BASE_CABLE_ACTIVE) { + phy_data->sfp_cable = XGBE_SFP_CABLE_ACTIVE; + } else { + phy_data->sfp_cable = XGBE_SFP_CABLE_FIBER; + } + + /* Determine the type of SFP */ + if (phy_data->sfp_cable != XGBE_SFP_CABLE_FIBER && + xgbe_phy_sfp_bit_rate(sfp_eeprom, XGBE_SFP_SPEED_10000)) + phy_data->sfp_base = XGBE_SFP_BASE_10000_CR; + else if (sfp_base[XGBE_SFP_BASE_10GBE_CC] & XGBE_SFP_BASE_10GBE_CC_SR) + phy_data->sfp_base = XGBE_SFP_BASE_10000_SR; + else if (sfp_base[XGBE_SFP_BASE_10GBE_CC] & XGBE_SFP_BASE_10GBE_CC_LR) + phy_data->sfp_base = XGBE_SFP_BASE_10000_LR; + else if (sfp_base[XGBE_SFP_BASE_10GBE_CC] & XGBE_SFP_BASE_10GBE_CC_LRM) + phy_data->sfp_base = XGBE_SFP_BASE_10000_LRM; + else if (sfp_base[XGBE_SFP_BASE_10GBE_CC] & XGBE_SFP_BASE_10GBE_CC_ER) + phy_data->sfp_base = XGBE_SFP_BASE_10000_ER; + else if (sfp_base[XGBE_SFP_BASE_1GBE_CC] & XGBE_SFP_BASE_1GBE_CC_SX) + phy_data->sfp_base = XGBE_SFP_BASE_1000_SX; + else if (sfp_base[XGBE_SFP_BASE_1GBE_CC] & XGBE_SFP_BASE_1GBE_CC_LX) + phy_data->sfp_base = XGBE_SFP_BASE_1000_LX; + else if (sfp_base[XGBE_SFP_BASE_1GBE_CC] & XGBE_SFP_BASE_1GBE_CC_CX) + phy_data->sfp_base = XGBE_SFP_BASE_1000_CX; + else if (sfp_base[XGBE_SFP_BASE_1GBE_CC] & XGBE_SFP_BASE_1GBE_CC_T) + phy_data->sfp_base = XGBE_SFP_BASE_1000_T; + + switch (phy_data->sfp_base) { + case XGBE_SFP_BASE_1000_T: + phy_data->sfp_speed = XGBE_SFP_SPEED_100_1000; + break; + case XGBE_SFP_BASE_1000_SX: + case XGBE_SFP_BASE_1000_LX: + case XGBE_SFP_BASE_1000_CX: + phy_data->sfp_speed = XGBE_SFP_SPEED_1000; + break; + case XGBE_SFP_BASE_10000_SR: + case XGBE_SFP_BASE_10000_LR: + case XGBE_SFP_BASE_10000_LRM: + case XGBE_SFP_BASE_10000_ER: + case XGBE_SFP_BASE_10000_CR: + phy_data->sfp_speed = XGBE_SFP_SPEED_10000; + break; + default: + break; + } +} + +static void xgbe_phy_sfp_eeprom_info(struct xgbe_prv_data *pdata, + struct xgbe_sfp_eeprom *sfp_eeprom) +{ + struct xgbe_sfp_ascii sfp_ascii; + char *sfp_data = (char *)&sfp_ascii; + + netif_dbg(pdata, drv, pdata->netdev, "SFP detected:\n"); + memcpy(sfp_data, &sfp_eeprom->base[XGBE_SFP_BASE_VENDOR_NAME], + XGBE_SFP_BASE_VENDOR_NAME_LEN); + sfp_data[XGBE_SFP_BASE_VENDOR_NAME_LEN] = '\0'; + netif_dbg(pdata, drv, pdata->netdev, " vendor: %s\n", + sfp_data); + + memcpy(sfp_data, &sfp_eeprom->base[XGBE_SFP_BASE_VENDOR_PN], + XGBE_SFP_BASE_VENDOR_PN_LEN); + sfp_data[XGBE_SFP_BASE_VENDOR_PN_LEN] = '\0'; + netif_dbg(pdata, drv, pdata->netdev, " part number: %s\n", + sfp_data); + + memcpy(sfp_data, &sfp_eeprom->base[XGBE_SFP_BASE_VENDOR_REV], + XGBE_SFP_BASE_VENDOR_REV_LEN); + sfp_data[XGBE_SFP_BASE_VENDOR_REV_LEN] = '\0'; + netif_dbg(pdata, drv, pdata->netdev, " revision level: %s\n", + sfp_data); + + memcpy(sfp_data, &sfp_eeprom->extd[XGBE_SFP_BASE_VENDOR_SN], + XGBE_SFP_BASE_VENDOR_SN_LEN); + sfp_data[XGBE_SFP_BASE_VENDOR_SN_LEN] = '\0'; + netif_dbg(pdata, drv, pdata->netdev, " serial number: %s\n", + sfp_data); +} + +static bool xgbe_phy_sfp_verify_eeprom(u8 cc_in, u8 *buf, unsigned int len) +{ + u8 cc; + + for (cc = 0; len; buf++, len--) + cc += *buf; + + return cc == cc_in; +} + +static int xgbe_phy_sfp_read_eeprom(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + struct xgbe_sfp_eeprom sfp_eeprom; + u8 eeprom_addr; + int ret; + + ret = xgbe_phy_sfp_get_mux(pdata); + if (ret) { + dev_err_once(pdata->dev, "%s: I2C error setting SFP MUX\n", + netdev_name(pdata->netdev)); + return ret; + } + + /* Read the SFP serial ID eeprom */ + eeprom_addr = 0; + ret = xgbe_phy_i2c_read(pdata, XGBE_SFP_SERIAL_ID_ADDRESS, + &eeprom_addr, sizeof(eeprom_addr), + &sfp_eeprom, sizeof(sfp_eeprom)); + if (ret) { + dev_err_once(pdata->dev, "%s: I2C error reading SFP EEPROM\n", + netdev_name(pdata->netdev)); + goto put; + } + + /* Validate the contents read */ + if (!xgbe_phy_sfp_verify_eeprom(sfp_eeprom.base[XGBE_SFP_BASE_CC], + sfp_eeprom.base, + sizeof(sfp_eeprom.base) - 1)) { + ret = -EINVAL; + goto put; + } + + if (!xgbe_phy_sfp_verify_eeprom(sfp_eeprom.extd[XGBE_SFP_EXTD_CC], + sfp_eeprom.extd, + sizeof(sfp_eeprom.extd) - 1)) { + ret = -EINVAL; + goto put; + } + + /* Check for an added or changed SFP */ + if (memcmp(&phy_data->sfp_eeprom, &sfp_eeprom, sizeof(sfp_eeprom))) { + phy_data->sfp_changed = 1; + + if (netif_msg_drv(pdata)) + xgbe_phy_sfp_eeprom_info(pdata, &sfp_eeprom); + + memcpy(&phy_data->sfp_eeprom, &sfp_eeprom, sizeof(sfp_eeprom)); + + xgbe_phy_free_phy_device(pdata); + } else { + phy_data->sfp_changed = 0; + } + +put: + xgbe_phy_sfp_put_mux(pdata); + + return ret; +} + +static void xgbe_phy_sfp_signals(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + u8 gpio_reg, gpio_ports[2]; + int ret; + + /* Read the input port registers */ + gpio_reg = 0; + ret = xgbe_phy_i2c_read(pdata, phy_data->sfp_gpio_address, + &gpio_reg, sizeof(gpio_reg), + gpio_ports, sizeof(gpio_ports)); + if (ret) { + dev_err_once(pdata->dev, "%s: I2C error reading SFP GPIOs\n", + netdev_name(pdata->netdev)); + return; + } + + phy_data->sfp_gpio_inputs = (gpio_ports[1] << 8) | gpio_ports[0]; + + phy_data->sfp_mod_absent = xgbe_phy_check_sfp_mod_absent(phy_data); +} + +static void xgbe_phy_sfp_mod_absent(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + xgbe_phy_free_phy_device(pdata); + + phy_data->sfp_mod_absent = 1; + phy_data->sfp_phy_avail = 0; + memset(&phy_data->sfp_eeprom, 0, sizeof(phy_data->sfp_eeprom)); +} + +static void xgbe_phy_sfp_reset(struct xgbe_phy_data *phy_data) +{ + phy_data->sfp_rx_los = 0; + phy_data->sfp_tx_fault = 0; + phy_data->sfp_mod_absent = 1; + phy_data->sfp_base = XGBE_SFP_BASE_UNKNOWN; + phy_data->sfp_cable = XGBE_SFP_CABLE_UNKNOWN; + phy_data->sfp_speed = XGBE_SFP_SPEED_UNKNOWN; +} + +static void xgbe_phy_sfp_detect(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + int ret; + + /* Reset the SFP signals and info */ + xgbe_phy_sfp_reset(phy_data); + + ret = xgbe_phy_get_comm_ownership(pdata); + if (ret) + return; + + /* Read the SFP signals and check for module presence */ + xgbe_phy_sfp_signals(pdata); + if (phy_data->sfp_mod_absent) { + xgbe_phy_sfp_mod_absent(pdata); + goto put; + } + + ret = xgbe_phy_sfp_read_eeprom(pdata); + if (ret) { + /* Treat any error as if there isn't an SFP plugged in */ + xgbe_phy_sfp_reset(phy_data); + xgbe_phy_sfp_mod_absent(pdata); + goto put; + } + + xgbe_phy_sfp_parse_eeprom(pdata); + + xgbe_phy_sfp_external_phy(pdata); + +put: + xgbe_phy_sfp_phy_settings(pdata); + + xgbe_phy_put_comm_ownership(pdata); +} + +static int xgbe_phy_module_eeprom(struct xgbe_prv_data *pdata, + struct ethtool_eeprom *eeprom, u8 *data) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + u8 eeprom_addr, eeprom_data[XGBE_SFP_EEPROM_MAX]; + struct xgbe_sfp_eeprom *sfp_eeprom; + unsigned int i, j, rem; + int ret; + + rem = eeprom->len; + + if (!eeprom->len) { + ret = -EINVAL; + goto done; + } + + if ((eeprom->offset + eeprom->len) > XGBE_SFP_EEPROM_MAX) { + ret = -EINVAL; + goto done; + } + + if (phy_data->port_mode != XGBE_PORT_MODE_SFP) { + ret = -ENXIO; + goto done; + } + + if (!netif_running(pdata->netdev)) { + ret = -EIO; + goto done; + } + + if (phy_data->sfp_mod_absent) { + ret = -EIO; + goto done; + } + + ret = xgbe_phy_get_comm_ownership(pdata); + if (ret) { + ret = -EIO; + goto done; + } + + ret = xgbe_phy_sfp_get_mux(pdata); + if (ret) { + netdev_err(pdata->netdev, "I2C error setting SFP MUX\n"); + ret = -EIO; + goto put_own; + } + + /* Read the SFP serial ID eeprom */ + eeprom_addr = 0; + ret = xgbe_phy_i2c_read(pdata, XGBE_SFP_SERIAL_ID_ADDRESS, + &eeprom_addr, sizeof(eeprom_addr), + eeprom_data, XGBE_SFP_EEPROM_BASE_LEN); + if (ret) { + netdev_err(pdata->netdev, + "I2C error reading SFP EEPROM\n"); + ret = -EIO; + goto put_mux; + } + + sfp_eeprom = (struct xgbe_sfp_eeprom *)eeprom_data; + + if (XGBE_SFP_DIAGS_SUPPORTED(sfp_eeprom)) { + /* Read the SFP diagnostic eeprom */ + eeprom_addr = 0; + ret = xgbe_phy_i2c_read(pdata, XGBE_SFP_DIAG_INFO_ADDRESS, + &eeprom_addr, sizeof(eeprom_addr), + eeprom_data + XGBE_SFP_EEPROM_BASE_LEN, + XGBE_SFP_EEPROM_DIAG_LEN); + if (ret) { + netdev_err(pdata->netdev, + "I2C error reading SFP DIAGS\n"); + ret = -EIO; + goto put_mux; + } + } + + for (i = 0, j = eeprom->offset; i < eeprom->len; i++, j++) { + if ((j >= XGBE_SFP_EEPROM_BASE_LEN) && + !XGBE_SFP_DIAGS_SUPPORTED(sfp_eeprom)) + break; + + data[i] = eeprom_data[j]; + rem--; + } + +put_mux: + xgbe_phy_sfp_put_mux(pdata); + +put_own: + xgbe_phy_put_comm_ownership(pdata); + +done: + eeprom->len -= rem; + + return ret; +} + +static int xgbe_phy_module_info(struct xgbe_prv_data *pdata, + struct ethtool_modinfo *modinfo) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + if (phy_data->port_mode != XGBE_PORT_MODE_SFP) + return -ENXIO; + + if (!netif_running(pdata->netdev)) + return -EIO; + + if (phy_data->sfp_mod_absent) + return -EIO; + + if (XGBE_SFP_DIAGS_SUPPORTED(&phy_data->sfp_eeprom)) { + modinfo->type = ETH_MODULE_SFF_8472; + modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN; + } else { + modinfo->type = ETH_MODULE_SFF_8079; + modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN; + } + + return 0; +} + +static void xgbe_phy_phydev_flowctrl(struct xgbe_prv_data *pdata) +{ + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + struct xgbe_phy_data *phy_data = pdata->phy_data; + u16 lcl_adv = 0, rmt_adv = 0; + u8 fc; + + pdata->phy.tx_pause = 0; + pdata->phy.rx_pause = 0; + + if (!phy_data->phydev) + return; + + lcl_adv = linkmode_adv_to_lcl_adv_t(phy_data->phydev->advertising); + + if (phy_data->phydev->pause) { + XGBE_SET_LP_ADV(lks, Pause); + rmt_adv |= LPA_PAUSE_CAP; + } + if (phy_data->phydev->asym_pause) { + XGBE_SET_LP_ADV(lks, Asym_Pause); + rmt_adv |= LPA_PAUSE_ASYM; + } + + fc = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv); + if (fc & FLOW_CTRL_TX) + pdata->phy.tx_pause = 1; + if (fc & FLOW_CTRL_RX) + pdata->phy.rx_pause = 1; +} + +static enum xgbe_mode xgbe_phy_an37_sgmii_outcome(struct xgbe_prv_data *pdata) +{ + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + enum xgbe_mode mode; + + XGBE_SET_LP_ADV(lks, Autoneg); + XGBE_SET_LP_ADV(lks, TP); + + /* Use external PHY to determine flow control */ + if (pdata->phy.pause_autoneg) + xgbe_phy_phydev_flowctrl(pdata); + + switch (pdata->an_status & XGBE_SGMII_AN_LINK_SPEED) { + case XGBE_SGMII_AN_LINK_SPEED_100: + if (pdata->an_status & XGBE_SGMII_AN_LINK_DUPLEX) { + XGBE_SET_LP_ADV(lks, 100baseT_Full); + mode = XGBE_MODE_SGMII_100; + } else { + /* Half-duplex not supported */ + XGBE_SET_LP_ADV(lks, 100baseT_Half); + mode = XGBE_MODE_UNKNOWN; + } + break; + case XGBE_SGMII_AN_LINK_SPEED_1000: + if (pdata->an_status & XGBE_SGMII_AN_LINK_DUPLEX) { + XGBE_SET_LP_ADV(lks, 1000baseT_Full); + mode = XGBE_MODE_SGMII_1000; + } else { + /* Half-duplex not supported */ + XGBE_SET_LP_ADV(lks, 1000baseT_Half); + mode = XGBE_MODE_UNKNOWN; + } + break; + default: + mode = XGBE_MODE_UNKNOWN; + } + + return mode; +} + +static enum xgbe_mode xgbe_phy_an37_outcome(struct xgbe_prv_data *pdata) +{ + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + enum xgbe_mode mode; + unsigned int ad_reg, lp_reg; + + XGBE_SET_LP_ADV(lks, Autoneg); + XGBE_SET_LP_ADV(lks, FIBRE); + + /* Compare Advertisement and Link Partner register */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_ADVERTISE); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_LP_ABILITY); + if (lp_reg & 0x100) + XGBE_SET_LP_ADV(lks, Pause); + if (lp_reg & 0x80) + XGBE_SET_LP_ADV(lks, Asym_Pause); + + if (pdata->phy.pause_autoneg) { + /* Set flow control based on auto-negotiation result */ + pdata->phy.tx_pause = 0; + pdata->phy.rx_pause = 0; + + if (ad_reg & lp_reg & 0x100) { + pdata->phy.tx_pause = 1; + pdata->phy.rx_pause = 1; + } else if (ad_reg & lp_reg & 0x80) { + if (ad_reg & 0x100) + pdata->phy.rx_pause = 1; + else if (lp_reg & 0x100) + pdata->phy.tx_pause = 1; + } + } + + if (lp_reg & 0x20) + XGBE_SET_LP_ADV(lks, 1000baseX_Full); + + /* Half duplex is not supported */ + ad_reg &= lp_reg; + mode = (ad_reg & 0x20) ? XGBE_MODE_X : XGBE_MODE_UNKNOWN; + + return mode; +} + +static enum xgbe_mode xgbe_phy_an73_redrv_outcome(struct xgbe_prv_data *pdata) +{ + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + struct xgbe_phy_data *phy_data = pdata->phy_data; + enum xgbe_mode mode; + unsigned int ad_reg, lp_reg; + + XGBE_SET_LP_ADV(lks, Autoneg); + XGBE_SET_LP_ADV(lks, Backplane); + + /* Use external PHY to determine flow control */ + if (pdata->phy.pause_autoneg) + xgbe_phy_phydev_flowctrl(pdata); + + /* Compare Advertisement and Link Partner register 2 */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1); + if (lp_reg & 0x80) + XGBE_SET_LP_ADV(lks, 10000baseKR_Full); + if (lp_reg & 0x20) + XGBE_SET_LP_ADV(lks, 1000baseKX_Full); + + ad_reg &= lp_reg; + if (ad_reg & 0x80) { + switch (phy_data->port_mode) { + case XGBE_PORT_MODE_BACKPLANE: + case XGBE_PORT_MODE_BACKPLANE_NO_AUTONEG: + mode = XGBE_MODE_KR; + break; + default: + mode = XGBE_MODE_SFI; + break; + } + } else if (ad_reg & 0x20) { + switch (phy_data->port_mode) { + case XGBE_PORT_MODE_BACKPLANE: + case XGBE_PORT_MODE_BACKPLANE_NO_AUTONEG: + mode = XGBE_MODE_KX_1000; + break; + case XGBE_PORT_MODE_1000BASE_X: + mode = XGBE_MODE_X; + break; + case XGBE_PORT_MODE_SFP: + switch (phy_data->sfp_base) { + case XGBE_SFP_BASE_1000_T: + if (phy_data->phydev && + (phy_data->phydev->speed == SPEED_100)) + mode = XGBE_MODE_SGMII_100; + else + mode = XGBE_MODE_SGMII_1000; + break; + case XGBE_SFP_BASE_1000_SX: + case XGBE_SFP_BASE_1000_LX: + case XGBE_SFP_BASE_1000_CX: + default: + mode = XGBE_MODE_X; + break; + } + break; + default: + if (phy_data->phydev && + (phy_data->phydev->speed == SPEED_100)) + mode = XGBE_MODE_SGMII_100; + else + mode = XGBE_MODE_SGMII_1000; + break; + } + } else { + mode = XGBE_MODE_UNKNOWN; + } + + /* Compare Advertisement and Link Partner register 3 */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2); + if (lp_reg & 0xc000) + XGBE_SET_LP_ADV(lks, 10000baseR_FEC); + + return mode; +} + +static enum xgbe_mode xgbe_phy_an73_outcome(struct xgbe_prv_data *pdata) +{ + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + enum xgbe_mode mode; + unsigned int ad_reg, lp_reg; + + XGBE_SET_LP_ADV(lks, Autoneg); + XGBE_SET_LP_ADV(lks, Backplane); + + /* Compare Advertisement and Link Partner register 1 */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA); + if (lp_reg & 0x400) + XGBE_SET_LP_ADV(lks, Pause); + if (lp_reg & 0x800) + XGBE_SET_LP_ADV(lks, Asym_Pause); + + if (pdata->phy.pause_autoneg) { + /* Set flow control based on auto-negotiation result */ + pdata->phy.tx_pause = 0; + pdata->phy.rx_pause = 0; + + if (ad_reg & lp_reg & 0x400) { + pdata->phy.tx_pause = 1; + pdata->phy.rx_pause = 1; + } else if (ad_reg & lp_reg & 0x800) { + if (ad_reg & 0x400) + pdata->phy.rx_pause = 1; + else if (lp_reg & 0x400) + pdata->phy.tx_pause = 1; + } + } + + /* Compare Advertisement and Link Partner register 2 */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1); + if (lp_reg & 0x80) + XGBE_SET_LP_ADV(lks, 10000baseKR_Full); + if (lp_reg & 0x20) + XGBE_SET_LP_ADV(lks, 1000baseKX_Full); + + ad_reg &= lp_reg; + if (ad_reg & 0x80) + mode = XGBE_MODE_KR; + else if (ad_reg & 0x20) + mode = XGBE_MODE_KX_1000; + else + mode = XGBE_MODE_UNKNOWN; + + /* Compare Advertisement and Link Partner register 3 */ + ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); + lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2); + if (lp_reg & 0xc000) + XGBE_SET_LP_ADV(lks, 10000baseR_FEC); + + return mode; +} + +static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata) +{ + switch (pdata->an_mode) { + case XGBE_AN_MODE_CL73: + return xgbe_phy_an73_outcome(pdata); + case XGBE_AN_MODE_CL73_REDRV: + return xgbe_phy_an73_redrv_outcome(pdata); + case XGBE_AN_MODE_CL37: + return xgbe_phy_an37_outcome(pdata); + case XGBE_AN_MODE_CL37_SGMII: + return xgbe_phy_an37_sgmii_outcome(pdata); + default: + return XGBE_MODE_UNKNOWN; + } +} + +static void xgbe_phy_an_advertising(struct xgbe_prv_data *pdata, + struct ethtool_link_ksettings *dlks) +{ + struct ethtool_link_ksettings *slks = &pdata->phy.lks; + struct xgbe_phy_data *phy_data = pdata->phy_data; + + XGBE_LM_COPY(dlks, advertising, slks, advertising); + + /* Without a re-driver, just return current advertising */ + if (!phy_data->redrv) + return; + + /* With the KR re-driver we need to advertise a single speed */ + XGBE_CLR_ADV(dlks, 1000baseKX_Full); + XGBE_CLR_ADV(dlks, 10000baseKR_Full); + + /* Advertise FEC support is present */ + if (pdata->fec_ability & MDIO_PMA_10GBR_FECABLE_ABLE) + XGBE_SET_ADV(dlks, 10000baseR_FEC); + + switch (phy_data->port_mode) { + case XGBE_PORT_MODE_BACKPLANE: + case XGBE_PORT_MODE_BACKPLANE_NO_AUTONEG: + XGBE_SET_ADV(dlks, 10000baseKR_Full); + break; + case XGBE_PORT_MODE_BACKPLANE_2500: + XGBE_SET_ADV(dlks, 1000baseKX_Full); + break; + case XGBE_PORT_MODE_1000BASE_T: + case XGBE_PORT_MODE_1000BASE_X: + case XGBE_PORT_MODE_NBASE_T: + XGBE_SET_ADV(dlks, 1000baseKX_Full); + break; + case XGBE_PORT_MODE_10GBASE_T: + if (phy_data->phydev && + (phy_data->phydev->speed == SPEED_10000)) + XGBE_SET_ADV(dlks, 10000baseKR_Full); + else + XGBE_SET_ADV(dlks, 1000baseKX_Full); + break; + case XGBE_PORT_MODE_10GBASE_R: + XGBE_SET_ADV(dlks, 10000baseKR_Full); + break; + case XGBE_PORT_MODE_SFP: + switch (phy_data->sfp_base) { + case XGBE_SFP_BASE_1000_T: + case XGBE_SFP_BASE_1000_SX: + case XGBE_SFP_BASE_1000_LX: + case XGBE_SFP_BASE_1000_CX: + XGBE_SET_ADV(dlks, 1000baseKX_Full); + break; + default: + XGBE_SET_ADV(dlks, 10000baseKR_Full); + break; + } + break; + default: + XGBE_SET_ADV(dlks, 10000baseKR_Full); + break; + } +} + +static int xgbe_phy_an_config(struct xgbe_prv_data *pdata) +{ + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + struct xgbe_phy_data *phy_data = pdata->phy_data; + int ret; + + ret = xgbe_phy_find_phy_device(pdata); + if (ret) + return ret; + + if (!phy_data->phydev) + return 0; + + phy_data->phydev->autoneg = pdata->phy.autoneg; + linkmode_and(phy_data->phydev->advertising, + phy_data->phydev->supported, + lks->link_modes.advertising); + + if (pdata->phy.autoneg != AUTONEG_ENABLE) { + phy_data->phydev->speed = pdata->phy.speed; + phy_data->phydev->duplex = pdata->phy.duplex; + } + + ret = phy_start_aneg(phy_data->phydev); + + return ret; +} + +static enum xgbe_an_mode xgbe_phy_an_sfp_mode(struct xgbe_phy_data *phy_data) +{ + switch (phy_data->sfp_base) { + case XGBE_SFP_BASE_1000_T: + return XGBE_AN_MODE_CL37_SGMII; + case XGBE_SFP_BASE_1000_SX: + case XGBE_SFP_BASE_1000_LX: + case XGBE_SFP_BASE_1000_CX: + return XGBE_AN_MODE_CL37; + default: + return XGBE_AN_MODE_NONE; + } +} + +static enum xgbe_an_mode xgbe_phy_an_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + /* A KR re-driver will always require CL73 AN */ + if (phy_data->redrv) + return XGBE_AN_MODE_CL73_REDRV; + + switch (phy_data->port_mode) { + case XGBE_PORT_MODE_BACKPLANE: + return XGBE_AN_MODE_CL73; + case XGBE_PORT_MODE_BACKPLANE_NO_AUTONEG: + case XGBE_PORT_MODE_BACKPLANE_2500: + return XGBE_AN_MODE_NONE; + case XGBE_PORT_MODE_1000BASE_T: + return XGBE_AN_MODE_CL37_SGMII; + case XGBE_PORT_MODE_1000BASE_X: + return XGBE_AN_MODE_CL37; + case XGBE_PORT_MODE_NBASE_T: + return XGBE_AN_MODE_CL37_SGMII; + case XGBE_PORT_MODE_10GBASE_T: + return XGBE_AN_MODE_CL73; + case XGBE_PORT_MODE_10GBASE_R: + return XGBE_AN_MODE_NONE; + case XGBE_PORT_MODE_SFP: + return xgbe_phy_an_sfp_mode(phy_data); + default: + return XGBE_AN_MODE_NONE; + } +} + +static int xgbe_phy_set_redrv_mode_mdio(struct xgbe_prv_data *pdata, + enum xgbe_phy_redrv_mode mode) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + u16 redrv_reg, redrv_val; + + redrv_reg = XGBE_PHY_REDRV_MODE_REG + (phy_data->redrv_lane * 0x1000); + redrv_val = (u16)mode; + + return pdata->hw_if.write_ext_mii_regs(pdata, phy_data->redrv_addr, + redrv_reg, redrv_val); +} + +static int xgbe_phy_set_redrv_mode_i2c(struct xgbe_prv_data *pdata, + enum xgbe_phy_redrv_mode mode) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int redrv_reg; + int ret; + + /* Calculate the register to write */ + redrv_reg = XGBE_PHY_REDRV_MODE_REG + (phy_data->redrv_lane * 0x1000); + + ret = xgbe_phy_redrv_write(pdata, redrv_reg, mode); + + return ret; +} + +static void xgbe_phy_set_redrv_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + enum xgbe_phy_redrv_mode mode; + int ret; + + if (!phy_data->redrv) + return; + + mode = XGBE_PHY_REDRV_MODE_CX; + if ((phy_data->port_mode == XGBE_PORT_MODE_SFP) && + (phy_data->sfp_base != XGBE_SFP_BASE_1000_CX) && + (phy_data->sfp_base != XGBE_SFP_BASE_10000_CR)) + mode = XGBE_PHY_REDRV_MODE_SR; + + ret = xgbe_phy_get_comm_ownership(pdata); + if (ret) + return; + + if (phy_data->redrv_if) + xgbe_phy_set_redrv_mode_i2c(pdata, mode); + else + xgbe_phy_set_redrv_mode_mdio(pdata, mode); + + xgbe_phy_put_comm_ownership(pdata); +} + +static void xgbe_phy_rx_reset(struct xgbe_prv_data *pdata) +{ + int reg; + + reg = XMDIO_READ_BITS(pdata, MDIO_MMD_PCS, MDIO_PCS_DIGITAL_STAT, + XGBE_PCS_PSEQ_STATE_MASK); + if (reg == XGBE_PCS_PSEQ_STATE_POWER_GOOD) { + /* Mailbox command timed out, reset of RX block is required. + * This can be done by asseting the reset bit and wait for + * its compeletion. + */ + XMDIO_WRITE_BITS(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_RX_CTRL1, + XGBE_PMA_RX_RST_0_MASK, XGBE_PMA_RX_RST_0_RESET_ON); + ndelay(20); + XMDIO_WRITE_BITS(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_RX_CTRL1, + XGBE_PMA_RX_RST_0_MASK, XGBE_PMA_RX_RST_0_RESET_OFF); + usleep_range(40, 50); + netif_err(pdata, link, pdata->netdev, "firmware mailbox reset performed\n"); + } +} + +static void xgbe_phy_pll_ctrl(struct xgbe_prv_data *pdata, bool enable) +{ + /* PLL_CTRL feature needs to be enabled for fixed PHY modes (Non-Autoneg) only */ + if (pdata->phy.autoneg != AUTONEG_DISABLE) + return; + + XMDIO_WRITE_BITS(pdata, MDIO_MMD_PMAPMD, MDIO_VEND2_PMA_MISC_CTRL0, + XGBE_PMA_PLL_CTRL_MASK, + enable ? XGBE_PMA_PLL_CTRL_ENABLE + : XGBE_PMA_PLL_CTRL_DISABLE); + + /* Wait for command to complete */ + usleep_range(100, 200); +} + +static void xgbe_phy_perform_ratechange(struct xgbe_prv_data *pdata, + enum xgbe_mb_cmd cmd, enum xgbe_mb_subcmd sub_cmd) +{ + unsigned int s0 = 0; + unsigned int wait; + + /* Disable PLL re-initialization during FW command processing */ + xgbe_phy_pll_ctrl(pdata, false); + + /* Log if a previous command did not complete */ + if (XP_IOREAD_BITS(pdata, XP_DRIVER_INT_RO, STATUS)) { + netif_dbg(pdata, link, pdata->netdev, + "firmware mailbox not ready for command\n"); + xgbe_phy_rx_reset(pdata); + } + + /* Construct the command */ + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, COMMAND, cmd); + XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, sub_cmd); + + /* Issue the command */ + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_0, s0); + XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_1, 0); + XP_IOWRITE_BITS(pdata, XP_DRIVER_INT_REQ, REQUEST, 1); + + /* Wait for command to complete */ + wait = XGBE_RATECHANGE_COUNT; + while (wait--) { + if (!XP_IOREAD_BITS(pdata, XP_DRIVER_INT_RO, STATUS)) + goto reenable_pll; + + usleep_range(1000, 2000); + } + + netif_dbg(pdata, link, pdata->netdev, + "firmware mailbox command did not complete\n"); + + /* Reset on error */ + xgbe_phy_rx_reset(pdata); + +reenable_pll: + /* Enable PLL re-initialization, not needed for PHY Power Off and RRC cmds */ + if (cmd != XGBE_MB_CMD_POWER_OFF && + cmd != XGBE_MB_CMD_RRC) + xgbe_phy_pll_ctrl(pdata, true); +} + +static void xgbe_phy_rrc(struct xgbe_prv_data *pdata) +{ + /* Receiver Reset Cycle */ + xgbe_phy_perform_ratechange(pdata, XGBE_MB_CMD_RRC, XGBE_MB_SUBCMD_NONE); + + netif_dbg(pdata, link, pdata->netdev, "receiver reset complete\n"); +} + +static void xgbe_phy_power_off(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + /* Power off */ + xgbe_phy_perform_ratechange(pdata, XGBE_MB_CMD_POWER_OFF, XGBE_MB_SUBCMD_NONE); + + phy_data->cur_mode = XGBE_MODE_UNKNOWN; + + netif_dbg(pdata, link, pdata->netdev, "phy powered off\n"); +} + +static void xgbe_phy_sfi_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + xgbe_phy_set_redrv_mode(pdata); + + /* 10G/SFI */ + if (phy_data->sfp_cable != XGBE_SFP_CABLE_PASSIVE) { + xgbe_phy_perform_ratechange(pdata, XGBE_MB_CMD_SET_10G_SFI, XGBE_MB_SUBCMD_ACTIVE); + } else { + if (phy_data->sfp_cable_len <= 1) + xgbe_phy_perform_ratechange(pdata, XGBE_MB_CMD_SET_10G_SFI, + XGBE_MB_SUBCMD_PASSIVE_1M); + else if (phy_data->sfp_cable_len <= 3) + xgbe_phy_perform_ratechange(pdata, XGBE_MB_CMD_SET_10G_SFI, + XGBE_MB_SUBCMD_PASSIVE_3M); + else + xgbe_phy_perform_ratechange(pdata, XGBE_MB_CMD_SET_10G_SFI, + XGBE_MB_SUBCMD_PASSIVE_OTHER); + } + + phy_data->cur_mode = XGBE_MODE_SFI; + + netif_dbg(pdata, link, pdata->netdev, "10GbE SFI mode set\n"); +} + +static void xgbe_phy_x_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + xgbe_phy_set_redrv_mode(pdata); + + /* 1G/X */ + xgbe_phy_perform_ratechange(pdata, XGBE_MB_CMD_SET_1G, XGBE_MB_SUBCMD_1G_KX); + + phy_data->cur_mode = XGBE_MODE_X; + + netif_dbg(pdata, link, pdata->netdev, "1GbE X mode set\n"); +} + +static void xgbe_phy_sgmii_1000_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + xgbe_phy_set_redrv_mode(pdata); + + /* 1G/SGMII */ + xgbe_phy_perform_ratechange(pdata, XGBE_MB_CMD_SET_1G, XGBE_MB_SUBCMD_1G_SGMII); + + phy_data->cur_mode = XGBE_MODE_SGMII_1000; + + netif_dbg(pdata, link, pdata->netdev, "1GbE SGMII mode set\n"); +} + +static void xgbe_phy_sgmii_100_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + xgbe_phy_set_redrv_mode(pdata); + + /* 100M/SGMII */ + xgbe_phy_perform_ratechange(pdata, XGBE_MB_CMD_SET_1G, XGBE_MB_SUBCMD_100MBITS); + + phy_data->cur_mode = XGBE_MODE_SGMII_100; + + netif_dbg(pdata, link, pdata->netdev, "100MbE SGMII mode set\n"); +} + +static void xgbe_phy_kr_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + xgbe_phy_set_redrv_mode(pdata); + + /* 10G/KR */ + xgbe_phy_perform_ratechange(pdata, XGBE_MB_CMD_SET_10G_KR, XGBE_MB_SUBCMD_NONE); + + phy_data->cur_mode = XGBE_MODE_KR; + + netif_dbg(pdata, link, pdata->netdev, "10GbE KR mode set\n"); +} + +static void xgbe_phy_kx_2500_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + xgbe_phy_set_redrv_mode(pdata); + + /* 2.5G/KX */ + xgbe_phy_perform_ratechange(pdata, XGBE_MB_CMD_SET_2_5G, XGBE_MB_SUBCMD_NONE); + + phy_data->cur_mode = XGBE_MODE_KX_2500; + + netif_dbg(pdata, link, pdata->netdev, "2.5GbE KX mode set\n"); +} + +static void xgbe_phy_kx_1000_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + xgbe_phy_set_redrv_mode(pdata); + + /* 1G/KX */ + xgbe_phy_perform_ratechange(pdata, XGBE_MB_CMD_SET_1G, XGBE_MB_SUBCMD_1G_KX); + + phy_data->cur_mode = XGBE_MODE_KX_1000; + + netif_dbg(pdata, link, pdata->netdev, "1GbE KX mode set\n"); +} + +static enum xgbe_mode xgbe_phy_cur_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + return phy_data->cur_mode; +} + +static enum xgbe_mode xgbe_phy_switch_baset_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + /* No switching if not 10GBase-T */ + if (phy_data->port_mode != XGBE_PORT_MODE_10GBASE_T) + return xgbe_phy_cur_mode(pdata); + + switch (xgbe_phy_cur_mode(pdata)) { + case XGBE_MODE_SGMII_100: + case XGBE_MODE_SGMII_1000: + return XGBE_MODE_KR; + case XGBE_MODE_KR: + default: + return XGBE_MODE_SGMII_1000; + } +} + +static enum xgbe_mode xgbe_phy_switch_bp_2500_mode(struct xgbe_prv_data *pdata) +{ + return XGBE_MODE_KX_2500; +} + +static enum xgbe_mode xgbe_phy_switch_bp_mode(struct xgbe_prv_data *pdata) +{ + /* If we are in KR switch to KX, and vice-versa */ + switch (xgbe_phy_cur_mode(pdata)) { + case XGBE_MODE_KX_1000: + return XGBE_MODE_KR; + case XGBE_MODE_KR: + default: + return XGBE_MODE_KX_1000; + } +} + +static enum xgbe_mode xgbe_phy_switch_mode(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + switch (phy_data->port_mode) { + case XGBE_PORT_MODE_BACKPLANE: + case XGBE_PORT_MODE_BACKPLANE_NO_AUTONEG: + return xgbe_phy_switch_bp_mode(pdata); + case XGBE_PORT_MODE_BACKPLANE_2500: + return xgbe_phy_switch_bp_2500_mode(pdata); + case XGBE_PORT_MODE_1000BASE_T: + case XGBE_PORT_MODE_NBASE_T: + case XGBE_PORT_MODE_10GBASE_T: + return xgbe_phy_switch_baset_mode(pdata); + case XGBE_PORT_MODE_1000BASE_X: + case XGBE_PORT_MODE_10GBASE_R: + case XGBE_PORT_MODE_SFP: + /* No switching, so just return current mode */ + return xgbe_phy_cur_mode(pdata); + default: + return XGBE_MODE_UNKNOWN; + } +} + +static enum xgbe_mode xgbe_phy_get_basex_mode(struct xgbe_phy_data *phy_data, + int speed) +{ + switch (speed) { + case SPEED_1000: + return XGBE_MODE_X; + case SPEED_10000: + return XGBE_MODE_KR; + default: + return XGBE_MODE_UNKNOWN; + } +} + +static enum xgbe_mode xgbe_phy_get_baset_mode(struct xgbe_phy_data *phy_data, + int speed) +{ + switch (speed) { + case SPEED_100: + return XGBE_MODE_SGMII_100; + case SPEED_1000: + return XGBE_MODE_SGMII_1000; + case SPEED_2500: + return XGBE_MODE_KX_2500; + case SPEED_10000: + return XGBE_MODE_KR; + default: + return XGBE_MODE_UNKNOWN; + } +} + +static enum xgbe_mode xgbe_phy_get_sfp_mode(struct xgbe_phy_data *phy_data, + int speed) +{ + switch (speed) { + case SPEED_100: + return XGBE_MODE_SGMII_100; + case SPEED_1000: + if (phy_data->sfp_base == XGBE_SFP_BASE_1000_T) + return XGBE_MODE_SGMII_1000; + else + return XGBE_MODE_X; + case SPEED_10000: + case SPEED_UNKNOWN: + return XGBE_MODE_SFI; + default: + return XGBE_MODE_UNKNOWN; + } +} + +static enum xgbe_mode xgbe_phy_get_bp_2500_mode(int speed) +{ + switch (speed) { + case SPEED_2500: + return XGBE_MODE_KX_2500; + default: + return XGBE_MODE_UNKNOWN; + } +} + +static enum xgbe_mode xgbe_phy_get_bp_mode(int speed) +{ + switch (speed) { + case SPEED_1000: + return XGBE_MODE_KX_1000; + case SPEED_10000: + return XGBE_MODE_KR; + default: + return XGBE_MODE_UNKNOWN; + } +} + +static enum xgbe_mode xgbe_phy_get_mode(struct xgbe_prv_data *pdata, + int speed) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + switch (phy_data->port_mode) { + case XGBE_PORT_MODE_BACKPLANE: + case XGBE_PORT_MODE_BACKPLANE_NO_AUTONEG: + return xgbe_phy_get_bp_mode(speed); + case XGBE_PORT_MODE_BACKPLANE_2500: + return xgbe_phy_get_bp_2500_mode(speed); + case XGBE_PORT_MODE_1000BASE_T: + case XGBE_PORT_MODE_NBASE_T: + case XGBE_PORT_MODE_10GBASE_T: + return xgbe_phy_get_baset_mode(phy_data, speed); + case XGBE_PORT_MODE_1000BASE_X: + case XGBE_PORT_MODE_10GBASE_R: + return xgbe_phy_get_basex_mode(phy_data, speed); + case XGBE_PORT_MODE_SFP: + return xgbe_phy_get_sfp_mode(phy_data, speed); + default: + return XGBE_MODE_UNKNOWN; + } +} + +static void xgbe_phy_set_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) +{ + switch (mode) { + case XGBE_MODE_KX_1000: + xgbe_phy_kx_1000_mode(pdata); + break; + case XGBE_MODE_KX_2500: + xgbe_phy_kx_2500_mode(pdata); + break; + case XGBE_MODE_KR: + xgbe_phy_kr_mode(pdata); + break; + case XGBE_MODE_SGMII_100: + xgbe_phy_sgmii_100_mode(pdata); + break; + case XGBE_MODE_SGMII_1000: + xgbe_phy_sgmii_1000_mode(pdata); + break; + case XGBE_MODE_X: + xgbe_phy_x_mode(pdata); + break; + case XGBE_MODE_SFI: + xgbe_phy_sfi_mode(pdata); + break; + default: + break; + } +} + +static bool xgbe_phy_check_mode(struct xgbe_prv_data *pdata, + enum xgbe_mode mode, bool advert) +{ + if (pdata->phy.autoneg == AUTONEG_ENABLE) { + return advert; + } else { + enum xgbe_mode cur_mode; + + cur_mode = xgbe_phy_get_mode(pdata, pdata->phy.speed); + if (cur_mode == mode) + return true; + } + + return false; +} + +static bool xgbe_phy_use_basex_mode(struct xgbe_prv_data *pdata, + enum xgbe_mode mode) +{ + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + + switch (mode) { + case XGBE_MODE_X: + return xgbe_phy_check_mode(pdata, mode, + XGBE_ADV(lks, 1000baseX_Full)); + case XGBE_MODE_KR: + return xgbe_phy_check_mode(pdata, mode, + XGBE_ADV(lks, 10000baseKR_Full)); + default: + return false; + } +} + +static bool xgbe_phy_use_baset_mode(struct xgbe_prv_data *pdata, + enum xgbe_mode mode) +{ + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + + switch (mode) { + case XGBE_MODE_SGMII_100: + return xgbe_phy_check_mode(pdata, mode, + XGBE_ADV(lks, 100baseT_Full)); + case XGBE_MODE_SGMII_1000: + return xgbe_phy_check_mode(pdata, mode, + XGBE_ADV(lks, 1000baseT_Full)); + case XGBE_MODE_KX_2500: + return xgbe_phy_check_mode(pdata, mode, + XGBE_ADV(lks, 2500baseT_Full)); + case XGBE_MODE_KR: + return xgbe_phy_check_mode(pdata, mode, + XGBE_ADV(lks, 10000baseT_Full)); + default: + return false; + } +} + +static bool xgbe_phy_use_sfp_mode(struct xgbe_prv_data *pdata, + enum xgbe_mode mode) +{ + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + struct xgbe_phy_data *phy_data = pdata->phy_data; + + switch (mode) { + case XGBE_MODE_X: + if (phy_data->sfp_base == XGBE_SFP_BASE_1000_T) + return false; + return xgbe_phy_check_mode(pdata, mode, + XGBE_ADV(lks, 1000baseX_Full)); + case XGBE_MODE_SGMII_100: + if (phy_data->sfp_base != XGBE_SFP_BASE_1000_T) + return false; + return xgbe_phy_check_mode(pdata, mode, + XGBE_ADV(lks, 100baseT_Full)); + case XGBE_MODE_SGMII_1000: + if (phy_data->sfp_base != XGBE_SFP_BASE_1000_T) + return false; + return xgbe_phy_check_mode(pdata, mode, + XGBE_ADV(lks, 1000baseT_Full)); + case XGBE_MODE_SFI: + if (phy_data->sfp_mod_absent) + return true; + return xgbe_phy_check_mode(pdata, mode, + XGBE_ADV(lks, 10000baseSR_Full) || + XGBE_ADV(lks, 10000baseLR_Full) || + XGBE_ADV(lks, 10000baseLRM_Full) || + XGBE_ADV(lks, 10000baseER_Full) || + XGBE_ADV(lks, 10000baseCR_Full)); + default: + return false; + } +} + +static bool xgbe_phy_use_bp_2500_mode(struct xgbe_prv_data *pdata, + enum xgbe_mode mode) +{ + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + + switch (mode) { + case XGBE_MODE_KX_2500: + return xgbe_phy_check_mode(pdata, mode, + XGBE_ADV(lks, 2500baseX_Full)); + default: + return false; + } +} + +static bool xgbe_phy_use_bp_mode(struct xgbe_prv_data *pdata, + enum xgbe_mode mode) +{ + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + + switch (mode) { + case XGBE_MODE_KX_1000: + return xgbe_phy_check_mode(pdata, mode, + XGBE_ADV(lks, 1000baseKX_Full)); + case XGBE_MODE_KR: + return xgbe_phy_check_mode(pdata, mode, + XGBE_ADV(lks, 10000baseKR_Full)); + default: + return false; + } +} + +static bool xgbe_phy_use_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + switch (phy_data->port_mode) { + case XGBE_PORT_MODE_BACKPLANE: + case XGBE_PORT_MODE_BACKPLANE_NO_AUTONEG: + return xgbe_phy_use_bp_mode(pdata, mode); + case XGBE_PORT_MODE_BACKPLANE_2500: + return xgbe_phy_use_bp_2500_mode(pdata, mode); + case XGBE_PORT_MODE_1000BASE_T: + case XGBE_PORT_MODE_NBASE_T: + case XGBE_PORT_MODE_10GBASE_T: + return xgbe_phy_use_baset_mode(pdata, mode); + case XGBE_PORT_MODE_1000BASE_X: + case XGBE_PORT_MODE_10GBASE_R: + return xgbe_phy_use_basex_mode(pdata, mode); + case XGBE_PORT_MODE_SFP: + return xgbe_phy_use_sfp_mode(pdata, mode); + default: + return false; + } +} + +static bool xgbe_phy_valid_speed_basex_mode(struct xgbe_phy_data *phy_data, + int speed) +{ + switch (speed) { + case SPEED_1000: + return (phy_data->port_mode == XGBE_PORT_MODE_1000BASE_X); + case SPEED_10000: + return (phy_data->port_mode == XGBE_PORT_MODE_10GBASE_R); + default: + return false; + } +} + +static bool xgbe_phy_valid_speed_baset_mode(struct xgbe_phy_data *phy_data, + int speed) +{ + switch (speed) { + case SPEED_100: + case SPEED_1000: + return true; + case SPEED_2500: + return (phy_data->port_mode == XGBE_PORT_MODE_NBASE_T); + case SPEED_10000: + return (phy_data->port_mode == XGBE_PORT_MODE_10GBASE_T); + default: + return false; + } +} + +static bool xgbe_phy_valid_speed_sfp_mode(struct xgbe_phy_data *phy_data, + int speed) +{ + switch (speed) { + case SPEED_100: + return (phy_data->sfp_speed == XGBE_SFP_SPEED_100_1000); + case SPEED_1000: + return ((phy_data->sfp_speed == XGBE_SFP_SPEED_100_1000) || + (phy_data->sfp_speed == XGBE_SFP_SPEED_1000)); + case SPEED_10000: + return (phy_data->sfp_speed == XGBE_SFP_SPEED_10000); + default: + return false; + } +} + +static bool xgbe_phy_valid_speed_bp_2500_mode(int speed) +{ + switch (speed) { + case SPEED_2500: + return true; + default: + return false; + } +} + +static bool xgbe_phy_valid_speed_bp_mode(int speed) +{ + switch (speed) { + case SPEED_1000: + case SPEED_10000: + return true; + default: + return false; + } +} + +static bool xgbe_phy_valid_speed(struct xgbe_prv_data *pdata, int speed) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + switch (phy_data->port_mode) { + case XGBE_PORT_MODE_BACKPLANE: + case XGBE_PORT_MODE_BACKPLANE_NO_AUTONEG: + return xgbe_phy_valid_speed_bp_mode(speed); + case XGBE_PORT_MODE_BACKPLANE_2500: + return xgbe_phy_valid_speed_bp_2500_mode(speed); + case XGBE_PORT_MODE_1000BASE_T: + case XGBE_PORT_MODE_NBASE_T: + case XGBE_PORT_MODE_10GBASE_T: + return xgbe_phy_valid_speed_baset_mode(phy_data, speed); + case XGBE_PORT_MODE_1000BASE_X: + case XGBE_PORT_MODE_10GBASE_R: + return xgbe_phy_valid_speed_basex_mode(phy_data, speed); + case XGBE_PORT_MODE_SFP: + return xgbe_phy_valid_speed_sfp_mode(phy_data, speed); + default: + return false; + } +} + +static int xgbe_phy_link_status(struct xgbe_prv_data *pdata, int *an_restart) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int reg; + int ret; + + *an_restart = 0; + + if (phy_data->port_mode == XGBE_PORT_MODE_SFP) { + /* Check SFP signals */ + xgbe_phy_sfp_detect(pdata); + + if (phy_data->sfp_changed) { + *an_restart = 1; + return 0; + } + + if (phy_data->sfp_mod_absent || phy_data->sfp_rx_los) + return 0; + } + + if (phy_data->phydev) { + /* Check external PHY */ + ret = phy_read_status(phy_data->phydev); + if (ret < 0) + return 0; + + if ((pdata->phy.autoneg == AUTONEG_ENABLE) && + !phy_aneg_done(phy_data->phydev)) + return 0; + + if (!phy_data->phydev->link) + return 0; + } + + /* Link status is latched low, so read once to clear + * and then read again to get current state + */ + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1); + reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1); + if (reg & MDIO_STAT1_LSTATUS) + return 1; + + if (pdata->phy.autoneg == AUTONEG_ENABLE && + phy_data->port_mode == XGBE_PORT_MODE_BACKPLANE) { + if (!test_bit(XGBE_LINK_INIT, &pdata->dev_state)) { + netif_carrier_off(pdata->netdev); + *an_restart = 1; + } + } + + /* No link, attempt a receiver reset cycle */ + if (pdata->vdata->enable_rrc && phy_data->rrc_count++ > XGBE_RRC_FREQUENCY) { + phy_data->rrc_count = 0; + xgbe_phy_rrc(pdata); + } + + return 0; +} + +static void xgbe_phy_sfp_gpio_setup(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + phy_data->sfp_gpio_address = XGBE_GPIO_ADDRESS_PCA9555 + + XP_GET_BITS(pdata->pp3, XP_PROP_3, + GPIO_ADDR); + + phy_data->sfp_gpio_mask = XP_GET_BITS(pdata->pp3, XP_PROP_3, + GPIO_MASK); + + phy_data->sfp_gpio_rx_los = XP_GET_BITS(pdata->pp3, XP_PROP_3, + GPIO_RX_LOS); + phy_data->sfp_gpio_tx_fault = XP_GET_BITS(pdata->pp3, XP_PROP_3, + GPIO_TX_FAULT); + phy_data->sfp_gpio_mod_absent = XP_GET_BITS(pdata->pp3, XP_PROP_3, + GPIO_MOD_ABS); + phy_data->sfp_gpio_rate_select = XP_GET_BITS(pdata->pp3, XP_PROP_3, + GPIO_RATE_SELECT); + + if (netif_msg_probe(pdata)) { + dev_dbg(pdata->dev, "SFP: gpio_address=%#x\n", + phy_data->sfp_gpio_address); + dev_dbg(pdata->dev, "SFP: gpio_mask=%#x\n", + phy_data->sfp_gpio_mask); + dev_dbg(pdata->dev, "SFP: gpio_rx_los=%u\n", + phy_data->sfp_gpio_rx_los); + dev_dbg(pdata->dev, "SFP: gpio_tx_fault=%u\n", + phy_data->sfp_gpio_tx_fault); + dev_dbg(pdata->dev, "SFP: gpio_mod_absent=%u\n", + phy_data->sfp_gpio_mod_absent); + dev_dbg(pdata->dev, "SFP: gpio_rate_select=%u\n", + phy_data->sfp_gpio_rate_select); + } +} + +static void xgbe_phy_sfp_comm_setup(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int mux_addr_hi, mux_addr_lo; + + mux_addr_hi = XP_GET_BITS(pdata->pp4, XP_PROP_4, MUX_ADDR_HI); + mux_addr_lo = XP_GET_BITS(pdata->pp4, XP_PROP_4, MUX_ADDR_LO); + if (mux_addr_lo == XGBE_SFP_DIRECT) + return; + + phy_data->sfp_comm = XGBE_SFP_COMM_PCA9545; + phy_data->sfp_mux_address = (mux_addr_hi << 2) + mux_addr_lo; + phy_data->sfp_mux_channel = XP_GET_BITS(pdata->pp4, XP_PROP_4, + MUX_CHAN); + + if (netif_msg_probe(pdata)) { + dev_dbg(pdata->dev, "SFP: mux_address=%#x\n", + phy_data->sfp_mux_address); + dev_dbg(pdata->dev, "SFP: mux_channel=%u\n", + phy_data->sfp_mux_channel); + } +} + +static void xgbe_phy_sfp_setup(struct xgbe_prv_data *pdata) +{ + xgbe_phy_sfp_comm_setup(pdata); + xgbe_phy_sfp_gpio_setup(pdata); +} + +static int xgbe_phy_int_mdio_reset(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + unsigned int ret; + + ret = pdata->hw_if.set_gpio(pdata, phy_data->mdio_reset_gpio); + if (ret) + return ret; + + ret = pdata->hw_if.clr_gpio(pdata, phy_data->mdio_reset_gpio); + + return ret; +} + +static int xgbe_phy_i2c_mdio_reset(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + u8 gpio_reg, gpio_ports[2], gpio_data[3]; + int ret; + + /* Read the output port registers */ + gpio_reg = 2; + ret = xgbe_phy_i2c_read(pdata, phy_data->mdio_reset_addr, + &gpio_reg, sizeof(gpio_reg), + gpio_ports, sizeof(gpio_ports)); + if (ret) + return ret; + + /* Prepare to write the GPIO data */ + gpio_data[0] = 2; + gpio_data[1] = gpio_ports[0]; + gpio_data[2] = gpio_ports[1]; + + /* Set the GPIO pin */ + if (phy_data->mdio_reset_gpio < 8) + gpio_data[1] |= (1 << (phy_data->mdio_reset_gpio % 8)); + else + gpio_data[2] |= (1 << (phy_data->mdio_reset_gpio % 8)); + + /* Write the output port registers */ + ret = xgbe_phy_i2c_write(pdata, phy_data->mdio_reset_addr, + gpio_data, sizeof(gpio_data)); + if (ret) + return ret; + + /* Clear the GPIO pin */ + if (phy_data->mdio_reset_gpio < 8) + gpio_data[1] &= ~(1 << (phy_data->mdio_reset_gpio % 8)); + else + gpio_data[2] &= ~(1 << (phy_data->mdio_reset_gpio % 8)); + + /* Write the output port registers */ + ret = xgbe_phy_i2c_write(pdata, phy_data->mdio_reset_addr, + gpio_data, sizeof(gpio_data)); + + return ret; +} + +static int xgbe_phy_mdio_reset(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + int ret; + + if (phy_data->conn_type != XGBE_CONN_TYPE_MDIO) + return 0; + + ret = xgbe_phy_get_comm_ownership(pdata); + if (ret) + return ret; + + if (phy_data->mdio_reset == XGBE_MDIO_RESET_I2C_GPIO) + ret = xgbe_phy_i2c_mdio_reset(pdata); + else if (phy_data->mdio_reset == XGBE_MDIO_RESET_INT_GPIO) + ret = xgbe_phy_int_mdio_reset(pdata); + + xgbe_phy_put_comm_ownership(pdata); + + return ret; +} + +static bool xgbe_phy_redrv_error(struct xgbe_phy_data *phy_data) +{ + if (!phy_data->redrv) + return false; + + if (phy_data->redrv_if >= XGBE_PHY_REDRV_IF_MAX) + return true; + + switch (phy_data->redrv_model) { + case XGBE_PHY_REDRV_MODEL_4223: + if (phy_data->redrv_lane > 3) + return true; + break; + case XGBE_PHY_REDRV_MODEL_4227: + if (phy_data->redrv_lane > 1) + return true; + break; + default: + return true; + } + + return false; +} + +static int xgbe_phy_mdio_reset_setup(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + if (phy_data->conn_type != XGBE_CONN_TYPE_MDIO) + return 0; + + phy_data->mdio_reset = XP_GET_BITS(pdata->pp3, XP_PROP_3, MDIO_RESET); + switch (phy_data->mdio_reset) { + case XGBE_MDIO_RESET_NONE: + case XGBE_MDIO_RESET_I2C_GPIO: + case XGBE_MDIO_RESET_INT_GPIO: + break; + default: + dev_err(pdata->dev, "unsupported MDIO reset (%#x)\n", + phy_data->mdio_reset); + return -EINVAL; + } + + if (phy_data->mdio_reset == XGBE_MDIO_RESET_I2C_GPIO) { + phy_data->mdio_reset_addr = XGBE_GPIO_ADDRESS_PCA9555 + + XP_GET_BITS(pdata->pp3, XP_PROP_3, + MDIO_RESET_I2C_ADDR); + phy_data->mdio_reset_gpio = XP_GET_BITS(pdata->pp3, XP_PROP_3, + MDIO_RESET_I2C_GPIO); + } else if (phy_data->mdio_reset == XGBE_MDIO_RESET_INT_GPIO) { + phy_data->mdio_reset_gpio = XP_GET_BITS(pdata->pp3, XP_PROP_3, + MDIO_RESET_INT_GPIO); + } + + return 0; +} + +static bool xgbe_phy_port_mode_mismatch(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + switch (phy_data->port_mode) { + case XGBE_PORT_MODE_BACKPLANE: + case XGBE_PORT_MODE_BACKPLANE_NO_AUTONEG: + if ((phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) || + (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000)) + return false; + break; + case XGBE_PORT_MODE_BACKPLANE_2500: + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_2500) + return false; + break; + case XGBE_PORT_MODE_1000BASE_T: + if ((phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) || + (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000)) + return false; + break; + case XGBE_PORT_MODE_1000BASE_X: + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) + return false; + break; + case XGBE_PORT_MODE_NBASE_T: + if ((phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) || + (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) || + (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_2500)) + return false; + break; + case XGBE_PORT_MODE_10GBASE_T: + if ((phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) || + (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) || + (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000)) + return false; + break; + case XGBE_PORT_MODE_10GBASE_R: + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) + return false; + break; + case XGBE_PORT_MODE_SFP: + if ((phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) || + (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) || + (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000)) + return false; + break; + default: + break; + } + + return true; +} + +static bool xgbe_phy_conn_type_mismatch(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + switch (phy_data->port_mode) { + case XGBE_PORT_MODE_BACKPLANE: + case XGBE_PORT_MODE_BACKPLANE_NO_AUTONEG: + case XGBE_PORT_MODE_BACKPLANE_2500: + if (phy_data->conn_type == XGBE_CONN_TYPE_BACKPLANE) + return false; + break; + case XGBE_PORT_MODE_1000BASE_T: + case XGBE_PORT_MODE_1000BASE_X: + case XGBE_PORT_MODE_NBASE_T: + case XGBE_PORT_MODE_10GBASE_T: + case XGBE_PORT_MODE_10GBASE_R: + if (phy_data->conn_type == XGBE_CONN_TYPE_MDIO) + return false; + break; + case XGBE_PORT_MODE_SFP: + if (phy_data->conn_type == XGBE_CONN_TYPE_SFP) + return false; + break; + default: + break; + } + + return true; +} + +static bool xgbe_phy_port_enabled(struct xgbe_prv_data *pdata) +{ + if (!XP_GET_BITS(pdata->pp0, XP_PROP_0, PORT_SPEEDS)) + return false; + if (!XP_GET_BITS(pdata->pp0, XP_PROP_0, CONN_TYPE)) + return false; + + return true; +} + +static void xgbe_phy_cdr_track(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + if (!pdata->debugfs_an_cdr_workaround) + return; + + if (!phy_data->phy_cdr_notrack) + return; + + usleep_range(phy_data->phy_cdr_delay, + phy_data->phy_cdr_delay + 500); + + XMDIO_WRITE_BITS(pdata, MDIO_MMD_PMAPMD, MDIO_VEND2_PMA_CDR_CONTROL, + XGBE_PMA_CDR_TRACK_EN_MASK, + XGBE_PMA_CDR_TRACK_EN_ON); + + phy_data->phy_cdr_notrack = 0; +} + +static void xgbe_phy_cdr_notrack(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + if (!pdata->debugfs_an_cdr_workaround) + return; + + if (phy_data->phy_cdr_notrack) + return; + + XMDIO_WRITE_BITS(pdata, MDIO_MMD_PMAPMD, MDIO_VEND2_PMA_CDR_CONTROL, + XGBE_PMA_CDR_TRACK_EN_MASK, + XGBE_PMA_CDR_TRACK_EN_OFF); + + xgbe_phy_rrc(pdata); + + phy_data->phy_cdr_notrack = 1; +} + +static void xgbe_phy_kr_training_post(struct xgbe_prv_data *pdata) +{ + if (!pdata->debugfs_an_cdr_track_early) + xgbe_phy_cdr_track(pdata); +} + +static void xgbe_phy_kr_training_pre(struct xgbe_prv_data *pdata) +{ + if (pdata->debugfs_an_cdr_track_early) + xgbe_phy_cdr_track(pdata); +} + +static void xgbe_phy_an_post(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + switch (pdata->an_mode) { + case XGBE_AN_MODE_CL73: + case XGBE_AN_MODE_CL73_REDRV: + if (phy_data->cur_mode != XGBE_MODE_KR) + break; + + xgbe_phy_cdr_track(pdata); + + switch (pdata->an_result) { + case XGBE_AN_READY: + case XGBE_AN_COMPLETE: + break; + default: + if (phy_data->phy_cdr_delay < XGBE_CDR_DELAY_MAX) + phy_data->phy_cdr_delay += XGBE_CDR_DELAY_INC; + else + phy_data->phy_cdr_delay = XGBE_CDR_DELAY_INIT; + break; + } + break; + default: + break; + } +} + +static void xgbe_phy_an_pre(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + switch (pdata->an_mode) { + case XGBE_AN_MODE_CL73: + case XGBE_AN_MODE_CL73_REDRV: + if (phy_data->cur_mode != XGBE_MODE_KR) + break; + + xgbe_phy_cdr_notrack(pdata); + break; + default: + break; + } +} + +static void xgbe_phy_stop(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + /* If we have an external PHY, free it */ + xgbe_phy_free_phy_device(pdata); + + /* Reset SFP data */ + xgbe_phy_sfp_reset(phy_data); + xgbe_phy_sfp_mod_absent(pdata); + + /* Reset CDR support */ + xgbe_phy_cdr_track(pdata); + + /* Power off the PHY */ + xgbe_phy_power_off(pdata); + + /* Stop the I2C controller */ + pdata->i2c_if.i2c_stop(pdata); +} + +static int xgbe_phy_start(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + int ret; + + /* Start the I2C controller */ + ret = pdata->i2c_if.i2c_start(pdata); + if (ret) + return ret; + + /* Set the proper MDIO mode for the re-driver */ + if (phy_data->redrv && !phy_data->redrv_if) { + ret = pdata->hw_if.set_ext_mii_mode(pdata, phy_data->redrv_addr, + XGBE_MDIO_MODE_CL22); + if (ret) { + netdev_err(pdata->netdev, + "redriver mdio port not compatible (%u)\n", + phy_data->redrv_addr); + return ret; + } + } + + /* Start in highest supported mode */ + xgbe_phy_set_mode(pdata, phy_data->start_mode); + + /* Reset CDR support */ + xgbe_phy_cdr_track(pdata); + + /* After starting the I2C controller, we can check for an SFP */ + switch (phy_data->port_mode) { + case XGBE_PORT_MODE_SFP: + xgbe_phy_sfp_detect(pdata); + break; + default: + break; + } + + /* If we have an external PHY, start it */ + ret = xgbe_phy_find_phy_device(pdata); + if (ret) + goto err_i2c; + + return 0; + +err_i2c: + pdata->i2c_if.i2c_stop(pdata); + + return ret; +} + +static int xgbe_phy_reset(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + enum xgbe_mode cur_mode; + int ret; + + /* Reset by power cycling the PHY */ + cur_mode = phy_data->cur_mode; + xgbe_phy_power_off(pdata); + xgbe_phy_set_mode(pdata, cur_mode); + + if (!phy_data->phydev) + return 0; + + /* Reset the external PHY */ + ret = xgbe_phy_mdio_reset(pdata); + if (ret) + return ret; + + return phy_init_hw(phy_data->phydev); +} + +static void xgbe_phy_exit(struct xgbe_prv_data *pdata) +{ + struct xgbe_phy_data *phy_data = pdata->phy_data; + + /* Unregister for driving external PHYs */ + mdiobus_unregister(phy_data->mii); +} + +static int xgbe_phy_init(struct xgbe_prv_data *pdata) +{ + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + struct xgbe_phy_data *phy_data; + struct mii_bus *mii; + int ret; + + /* Check if enabled */ + if (!xgbe_phy_port_enabled(pdata)) { + dev_info(pdata->dev, "device is not enabled\n"); + return -ENODEV; + } + + /* Initialize the I2C controller */ + ret = pdata->i2c_if.i2c_init(pdata); + if (ret) + return ret; + + phy_data = devm_kzalloc(pdata->dev, sizeof(*phy_data), GFP_KERNEL); + if (!phy_data) + return -ENOMEM; + pdata->phy_data = phy_data; + + phy_data->port_mode = XP_GET_BITS(pdata->pp0, XP_PROP_0, PORT_MODE); + phy_data->port_id = XP_GET_BITS(pdata->pp0, XP_PROP_0, PORT_ID); + phy_data->port_speeds = XP_GET_BITS(pdata->pp0, XP_PROP_0, PORT_SPEEDS); + phy_data->conn_type = XP_GET_BITS(pdata->pp0, XP_PROP_0, CONN_TYPE); + phy_data->mdio_addr = XP_GET_BITS(pdata->pp0, XP_PROP_0, MDIO_ADDR); + if (netif_msg_probe(pdata)) { + dev_dbg(pdata->dev, "port mode=%u\n", phy_data->port_mode); + dev_dbg(pdata->dev, "port id=%u\n", phy_data->port_id); + dev_dbg(pdata->dev, "port speeds=%#x\n", phy_data->port_speeds); + dev_dbg(pdata->dev, "conn type=%u\n", phy_data->conn_type); + dev_dbg(pdata->dev, "mdio addr=%u\n", phy_data->mdio_addr); + } + + phy_data->redrv = XP_GET_BITS(pdata->pp4, XP_PROP_4, REDRV_PRESENT); + phy_data->redrv_if = XP_GET_BITS(pdata->pp4, XP_PROP_4, REDRV_IF); + phy_data->redrv_addr = XP_GET_BITS(pdata->pp4, XP_PROP_4, REDRV_ADDR); + phy_data->redrv_lane = XP_GET_BITS(pdata->pp4, XP_PROP_4, REDRV_LANE); + phy_data->redrv_model = XP_GET_BITS(pdata->pp4, XP_PROP_4, REDRV_MODEL); + if (phy_data->redrv && netif_msg_probe(pdata)) { + dev_dbg(pdata->dev, "redrv present\n"); + dev_dbg(pdata->dev, "redrv i/f=%u\n", phy_data->redrv_if); + dev_dbg(pdata->dev, "redrv addr=%#x\n", phy_data->redrv_addr); + dev_dbg(pdata->dev, "redrv lane=%u\n", phy_data->redrv_lane); + dev_dbg(pdata->dev, "redrv model=%u\n", phy_data->redrv_model); + } + + /* Validate the connection requested */ + if (xgbe_phy_conn_type_mismatch(pdata)) { + dev_err(pdata->dev, "phy mode/connection mismatch (%#x/%#x)\n", + phy_data->port_mode, phy_data->conn_type); + return -EINVAL; + } + + /* Validate the mode requested */ + if (xgbe_phy_port_mode_mismatch(pdata)) { + dev_err(pdata->dev, "phy mode/speed mismatch (%#x/%#x)\n", + phy_data->port_mode, phy_data->port_speeds); + return -EINVAL; + } + + /* Check for and validate MDIO reset support */ + ret = xgbe_phy_mdio_reset_setup(pdata); + if (ret) + return ret; + + /* Validate the re-driver information */ + if (xgbe_phy_redrv_error(phy_data)) { + dev_err(pdata->dev, "phy re-driver settings error\n"); + return -EINVAL; + } + pdata->kr_redrv = phy_data->redrv; + + /* Indicate current mode is unknown */ + phy_data->cur_mode = XGBE_MODE_UNKNOWN; + + /* Initialize supported features */ + XGBE_ZERO_SUP(lks); + + switch (phy_data->port_mode) { + /* Backplane support */ + case XGBE_PORT_MODE_BACKPLANE: + XGBE_SET_SUP(lks, Autoneg); + fallthrough; + case XGBE_PORT_MODE_BACKPLANE_NO_AUTONEG: + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, Backplane); + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) { + XGBE_SET_SUP(lks, 1000baseKX_Full); + phy_data->start_mode = XGBE_MODE_KX_1000; + } + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) { + XGBE_SET_SUP(lks, 10000baseKR_Full); + if (pdata->fec_ability & MDIO_PMA_10GBR_FECABLE_ABLE) + XGBE_SET_SUP(lks, 10000baseR_FEC); + phy_data->start_mode = XGBE_MODE_KR; + } + + phy_data->phydev_mode = XGBE_MDIO_MODE_NONE; + break; + case XGBE_PORT_MODE_BACKPLANE_2500: + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, Backplane); + XGBE_SET_SUP(lks, 2500baseX_Full); + phy_data->start_mode = XGBE_MODE_KX_2500; + + phy_data->phydev_mode = XGBE_MDIO_MODE_NONE; + break; + + /* MDIO 1GBase-T support */ + case XGBE_PORT_MODE_1000BASE_T: + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, TP); + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) { + XGBE_SET_SUP(lks, 100baseT_Full); + phy_data->start_mode = XGBE_MODE_SGMII_100; + } + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) { + XGBE_SET_SUP(lks, 1000baseT_Full); + phy_data->start_mode = XGBE_MODE_SGMII_1000; + } + + phy_data->phydev_mode = XGBE_MDIO_MODE_CL22; + break; + + /* MDIO Base-X support */ + case XGBE_PORT_MODE_1000BASE_X: + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, FIBRE); + XGBE_SET_SUP(lks, 1000baseX_Full); + phy_data->start_mode = XGBE_MODE_X; + + phy_data->phydev_mode = XGBE_MDIO_MODE_CL22; + break; + + /* MDIO NBase-T support */ + case XGBE_PORT_MODE_NBASE_T: + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, TP); + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) { + XGBE_SET_SUP(lks, 100baseT_Full); + phy_data->start_mode = XGBE_MODE_SGMII_100; + } + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) { + XGBE_SET_SUP(lks, 1000baseT_Full); + phy_data->start_mode = XGBE_MODE_SGMII_1000; + } + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_2500) { + XGBE_SET_SUP(lks, 2500baseT_Full); + phy_data->start_mode = XGBE_MODE_KX_2500; + } + + phy_data->phydev_mode = XGBE_MDIO_MODE_CL45; + break; + + /* 10GBase-T support */ + case XGBE_PORT_MODE_10GBASE_T: + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, TP); + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) { + XGBE_SET_SUP(lks, 100baseT_Full); + phy_data->start_mode = XGBE_MODE_SGMII_100; + } + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) { + XGBE_SET_SUP(lks, 1000baseT_Full); + phy_data->start_mode = XGBE_MODE_SGMII_1000; + } + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) { + XGBE_SET_SUP(lks, 10000baseT_Full); + phy_data->start_mode = XGBE_MODE_KR; + } + + phy_data->phydev_mode = XGBE_MDIO_MODE_CL45; + break; + + /* 10GBase-R support */ + case XGBE_PORT_MODE_10GBASE_R: + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, FIBRE); + XGBE_SET_SUP(lks, 10000baseSR_Full); + XGBE_SET_SUP(lks, 10000baseLR_Full); + XGBE_SET_SUP(lks, 10000baseLRM_Full); + XGBE_SET_SUP(lks, 10000baseER_Full); + if (pdata->fec_ability & MDIO_PMA_10GBR_FECABLE_ABLE) + XGBE_SET_SUP(lks, 10000baseR_FEC); + phy_data->start_mode = XGBE_MODE_SFI; + + phy_data->phydev_mode = XGBE_MDIO_MODE_NONE; + break; + + /* SFP support */ + case XGBE_PORT_MODE_SFP: + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, TP); + XGBE_SET_SUP(lks, FIBRE); + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) + phy_data->start_mode = XGBE_MODE_SGMII_100; + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) + phy_data->start_mode = XGBE_MODE_SGMII_1000; + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) + phy_data->start_mode = XGBE_MODE_SFI; + + phy_data->phydev_mode = XGBE_MDIO_MODE_CL22; + + xgbe_phy_sfp_setup(pdata); + break; + default: + return -EINVAL; + } + + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "phy supported=0x%*pb\n", + __ETHTOOL_LINK_MODE_MASK_NBITS, + lks->link_modes.supported); + + if ((phy_data->conn_type & XGBE_CONN_TYPE_MDIO) && + (phy_data->phydev_mode != XGBE_MDIO_MODE_NONE)) { + ret = pdata->hw_if.set_ext_mii_mode(pdata, phy_data->mdio_addr, + phy_data->phydev_mode); + if (ret) { + dev_err(pdata->dev, + "mdio port/clause not compatible (%d/%u)\n", + phy_data->mdio_addr, phy_data->phydev_mode); + return -EINVAL; + } + } + + if (phy_data->redrv && !phy_data->redrv_if) { + ret = pdata->hw_if.set_ext_mii_mode(pdata, phy_data->redrv_addr, + XGBE_MDIO_MODE_CL22); + if (ret) { + dev_err(pdata->dev, + "redriver mdio port not compatible (%u)\n", + phy_data->redrv_addr); + return -EINVAL; + } + } + + phy_data->phy_cdr_delay = XGBE_CDR_DELAY_INIT; + + /* Register for driving external PHYs */ + mii = devm_mdiobus_alloc(pdata->dev); + if (!mii) { + dev_err(pdata->dev, "mdiobus_alloc failed\n"); + return -ENOMEM; + } + + mii->priv = pdata; + mii->name = "amd-xgbe-mii"; + mii->read = xgbe_phy_mii_read; + mii->write = xgbe_phy_mii_write; + mii->parent = pdata->dev; + mii->phy_mask = ~0; + snprintf(mii->id, sizeof(mii->id), "%s", dev_name(pdata->dev)); + ret = mdiobus_register(mii); + if (ret) { + dev_err(pdata->dev, "mdiobus_register failed\n"); + return ret; + } + phy_data->mii = mii; + + return 0; +} + +void xgbe_init_function_ptrs_phy_v2(struct xgbe_phy_if *phy_if) +{ + struct xgbe_phy_impl_if *phy_impl = &phy_if->phy_impl; + + phy_impl->init = xgbe_phy_init; + phy_impl->exit = xgbe_phy_exit; + + phy_impl->reset = xgbe_phy_reset; + phy_impl->start = xgbe_phy_start; + phy_impl->stop = xgbe_phy_stop; + + phy_impl->link_status = xgbe_phy_link_status; + + phy_impl->valid_speed = xgbe_phy_valid_speed; + + phy_impl->use_mode = xgbe_phy_use_mode; + phy_impl->set_mode = xgbe_phy_set_mode; + phy_impl->get_mode = xgbe_phy_get_mode; + phy_impl->switch_mode = xgbe_phy_switch_mode; + phy_impl->cur_mode = xgbe_phy_cur_mode; + + phy_impl->an_mode = xgbe_phy_an_mode; + + phy_impl->an_config = xgbe_phy_an_config; + + phy_impl->an_advertising = xgbe_phy_an_advertising; + + phy_impl->an_outcome = xgbe_phy_an_outcome; + + phy_impl->an_pre = xgbe_phy_an_pre; + phy_impl->an_post = xgbe_phy_an_post; + + phy_impl->kr_training_pre = xgbe_phy_kr_training_pre; + phy_impl->kr_training_post = xgbe_phy_kr_training_post; + + phy_impl->module_info = xgbe_phy_module_info; + phy_impl->module_eeprom = xgbe_phy_module_eeprom; +} diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-platform.c b/drivers/net/ethernet/amd/xgbe/xgbe-platform.c new file mode 100644 index 000000000..4d790a89f --- /dev/null +++ b/drivers/net/ethernet/amd/xgbe/xgbe-platform.c @@ -0,0 +1,629 @@ +/* + * AMD 10Gb Ethernet driver + * + * This file is available to you under your choice of the following two + * licenses: + * + * License 1: GPLv2 + * + * Copyright (c) 2014-2016 Advanced Micro Devices, Inc. + * + * This file is free software; you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or (at + * your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * + * License 2: Modified BSD + * + * Copyright (c) 2014-2016 Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_net.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/of_device.h> +#include <linux/clk.h> +#include <linux/property.h> +#include <linux/acpi.h> +#include <linux/mdio.h> + +#include "xgbe.h" +#include "xgbe-common.h" + +#ifdef CONFIG_ACPI +static const struct acpi_device_id xgbe_acpi_match[]; + +static struct xgbe_version_data *xgbe_acpi_vdata(struct xgbe_prv_data *pdata) +{ + const struct acpi_device_id *id; + + id = acpi_match_device(xgbe_acpi_match, pdata->dev); + + return id ? (struct xgbe_version_data *)id->driver_data : NULL; +} + +static int xgbe_acpi_support(struct xgbe_prv_data *pdata) +{ + struct device *dev = pdata->dev; + u32 property; + int ret; + + /* Obtain the system clock setting */ + ret = device_property_read_u32(dev, XGBE_ACPI_DMA_FREQ, &property); + if (ret) { + dev_err(dev, "unable to obtain %s property\n", + XGBE_ACPI_DMA_FREQ); + return ret; + } + pdata->sysclk_rate = property; + + /* Obtain the PTP clock setting */ + ret = device_property_read_u32(dev, XGBE_ACPI_PTP_FREQ, &property); + if (ret) { + dev_err(dev, "unable to obtain %s property\n", + XGBE_ACPI_PTP_FREQ); + return ret; + } + pdata->ptpclk_rate = property; + + return 0; +} +#else /* CONFIG_ACPI */ +static struct xgbe_version_data *xgbe_acpi_vdata(struct xgbe_prv_data *pdata) +{ + return NULL; +} + +static int xgbe_acpi_support(struct xgbe_prv_data *pdata) +{ + return -EINVAL; +} +#endif /* CONFIG_ACPI */ + +#ifdef CONFIG_OF +static const struct of_device_id xgbe_of_match[]; + +static struct xgbe_version_data *xgbe_of_vdata(struct xgbe_prv_data *pdata) +{ + const struct of_device_id *id; + + id = of_match_device(xgbe_of_match, pdata->dev); + + return id ? (struct xgbe_version_data *)id->data : NULL; +} + +static int xgbe_of_support(struct xgbe_prv_data *pdata) +{ + struct device *dev = pdata->dev; + + /* Obtain the system clock setting */ + pdata->sysclk = devm_clk_get(dev, XGBE_DMA_CLOCK); + if (IS_ERR(pdata->sysclk)) { + dev_err(dev, "dma devm_clk_get failed\n"); + return PTR_ERR(pdata->sysclk); + } + pdata->sysclk_rate = clk_get_rate(pdata->sysclk); + + /* Obtain the PTP clock setting */ + pdata->ptpclk = devm_clk_get(dev, XGBE_PTP_CLOCK); + if (IS_ERR(pdata->ptpclk)) { + dev_err(dev, "ptp devm_clk_get failed\n"); + return PTR_ERR(pdata->ptpclk); + } + pdata->ptpclk_rate = clk_get_rate(pdata->ptpclk); + + return 0; +} + +static struct platform_device *xgbe_of_get_phy_pdev(struct xgbe_prv_data *pdata) +{ + struct device *dev = pdata->dev; + struct device_node *phy_node; + struct platform_device *phy_pdev; + + phy_node = of_parse_phandle(dev->of_node, "phy-handle", 0); + if (phy_node) { + /* Old style device tree: + * The XGBE and PHY resources are separate + */ + phy_pdev = of_find_device_by_node(phy_node); + of_node_put(phy_node); + } else { + /* New style device tree: + * The XGBE and PHY resources are grouped together with + * the PHY resources listed last + */ + get_device(dev); + phy_pdev = pdata->platdev; + } + + return phy_pdev; +} +#else /* CONFIG_OF */ +static struct xgbe_version_data *xgbe_of_vdata(struct xgbe_prv_data *pdata) +{ + return NULL; +} + +static int xgbe_of_support(struct xgbe_prv_data *pdata) +{ + return -EINVAL; +} + +static struct platform_device *xgbe_of_get_phy_pdev(struct xgbe_prv_data *pdata) +{ + return NULL; +} +#endif /* CONFIG_OF */ + +static unsigned int xgbe_resource_count(struct platform_device *pdev, + unsigned int type) +{ + unsigned int count; + int i; + + for (i = 0, count = 0; i < pdev->num_resources; i++) { + struct resource *res = &pdev->resource[i]; + + if (type == resource_type(res)) + count++; + } + + return count; +} + +static struct platform_device *xgbe_get_phy_pdev(struct xgbe_prv_data *pdata) +{ + struct platform_device *phy_pdev; + + if (pdata->use_acpi) { + get_device(pdata->dev); + phy_pdev = pdata->platdev; + } else { + phy_pdev = xgbe_of_get_phy_pdev(pdata); + } + + return phy_pdev; +} + +static struct xgbe_version_data *xgbe_get_vdata(struct xgbe_prv_data *pdata) +{ + return pdata->use_acpi ? xgbe_acpi_vdata(pdata) + : xgbe_of_vdata(pdata); +} + +static int xgbe_platform_probe(struct platform_device *pdev) +{ + struct xgbe_prv_data *pdata; + struct device *dev = &pdev->dev; + struct platform_device *phy_pdev; + const char *phy_mode; + unsigned int phy_memnum, phy_irqnum; + unsigned int dma_irqnum, dma_irqend; + enum dev_dma_attr attr; + int ret; + + pdata = xgbe_alloc_pdata(dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto err_alloc; + } + + pdata->platdev = pdev; + pdata->adev = ACPI_COMPANION(dev); + platform_set_drvdata(pdev, pdata); + + /* Check if we should use ACPI or DT */ + pdata->use_acpi = dev->of_node ? 0 : 1; + + /* Get the version data */ + pdata->vdata = xgbe_get_vdata(pdata); + + phy_pdev = xgbe_get_phy_pdev(pdata); + if (!phy_pdev) { + dev_err(dev, "unable to obtain phy device\n"); + ret = -EINVAL; + goto err_phydev; + } + pdata->phy_platdev = phy_pdev; + pdata->phy_dev = &phy_pdev->dev; + + if (pdev == phy_pdev) { + /* New style device tree or ACPI: + * The XGBE and PHY resources are grouped together with + * the PHY resources listed last + */ + phy_memnum = xgbe_resource_count(pdev, IORESOURCE_MEM) - 3; + phy_irqnum = platform_irq_count(pdev) - 1; + dma_irqnum = 1; + dma_irqend = phy_irqnum; + } else { + /* Old style device tree: + * The XGBE and PHY resources are separate + */ + phy_memnum = 0; + phy_irqnum = 0; + dma_irqnum = 1; + dma_irqend = platform_irq_count(pdev); + } + + /* Obtain the mmio areas for the device */ + pdata->xgmac_regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pdata->xgmac_regs)) { + dev_err(dev, "xgmac ioremap failed\n"); + ret = PTR_ERR(pdata->xgmac_regs); + goto err_io; + } + if (netif_msg_probe(pdata)) + dev_dbg(dev, "xgmac_regs = %p\n", pdata->xgmac_regs); + + pdata->xpcs_regs = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(pdata->xpcs_regs)) { + dev_err(dev, "xpcs ioremap failed\n"); + ret = PTR_ERR(pdata->xpcs_regs); + goto err_io; + } + if (netif_msg_probe(pdata)) + dev_dbg(dev, "xpcs_regs = %p\n", pdata->xpcs_regs); + + pdata->rxtx_regs = devm_platform_ioremap_resource(phy_pdev, + phy_memnum++); + if (IS_ERR(pdata->rxtx_regs)) { + dev_err(dev, "rxtx ioremap failed\n"); + ret = PTR_ERR(pdata->rxtx_regs); + goto err_io; + } + if (netif_msg_probe(pdata)) + dev_dbg(dev, "rxtx_regs = %p\n", pdata->rxtx_regs); + + pdata->sir0_regs = devm_platform_ioremap_resource(phy_pdev, + phy_memnum++); + if (IS_ERR(pdata->sir0_regs)) { + dev_err(dev, "sir0 ioremap failed\n"); + ret = PTR_ERR(pdata->sir0_regs); + goto err_io; + } + if (netif_msg_probe(pdata)) + dev_dbg(dev, "sir0_regs = %p\n", pdata->sir0_regs); + + pdata->sir1_regs = devm_platform_ioremap_resource(phy_pdev, + phy_memnum++); + if (IS_ERR(pdata->sir1_regs)) { + dev_err(dev, "sir1 ioremap failed\n"); + ret = PTR_ERR(pdata->sir1_regs); + goto err_io; + } + if (netif_msg_probe(pdata)) + dev_dbg(dev, "sir1_regs = %p\n", pdata->sir1_regs); + + /* Retrieve the MAC address */ + ret = device_property_read_u8_array(dev, XGBE_MAC_ADDR_PROPERTY, + pdata->mac_addr, + sizeof(pdata->mac_addr)); + if (ret || !is_valid_ether_addr(pdata->mac_addr)) { + dev_err(dev, "invalid %s property\n", XGBE_MAC_ADDR_PROPERTY); + if (!ret) + ret = -EINVAL; + goto err_io; + } + + /* Retrieve the PHY mode - it must be "xgmii" */ + ret = device_property_read_string(dev, XGBE_PHY_MODE_PROPERTY, + &phy_mode); + if (ret || strcmp(phy_mode, phy_modes(PHY_INTERFACE_MODE_XGMII))) { + dev_err(dev, "invalid %s property\n", XGBE_PHY_MODE_PROPERTY); + if (!ret) + ret = -EINVAL; + goto err_io; + } + pdata->phy_mode = PHY_INTERFACE_MODE_XGMII; + + /* Check for per channel interrupt support */ + if (device_property_present(dev, XGBE_DMA_IRQS_PROPERTY)) { + pdata->per_channel_irq = 1; + pdata->channel_irq_mode = XGBE_IRQ_MODE_EDGE; + } + + /* Obtain device settings unique to ACPI/OF */ + if (pdata->use_acpi) + ret = xgbe_acpi_support(pdata); + else + ret = xgbe_of_support(pdata); + if (ret) + goto err_io; + + /* Set the DMA coherency values */ + attr = device_get_dma_attr(dev); + if (attr == DEV_DMA_NOT_SUPPORTED) { + dev_err(dev, "DMA is not supported"); + ret = -ENODEV; + goto err_io; + } + pdata->coherent = (attr == DEV_DMA_COHERENT); + if (pdata->coherent) { + pdata->arcr = XGBE_DMA_OS_ARCR; + pdata->awcr = XGBE_DMA_OS_AWCR; + } else { + pdata->arcr = XGBE_DMA_SYS_ARCR; + pdata->awcr = XGBE_DMA_SYS_AWCR; + } + + /* Set the maximum fifo amounts */ + pdata->tx_max_fifo_size = pdata->vdata->tx_max_fifo_size; + pdata->rx_max_fifo_size = pdata->vdata->rx_max_fifo_size; + + /* Set the hardware channel and queue counts */ + xgbe_set_counts(pdata); + + /* Always have XGMAC and XPCS (auto-negotiation) interrupts */ + pdata->irq_count = 2; + + /* Get the device interrupt */ + ret = platform_get_irq(pdev, 0); + if (ret < 0) + goto err_io; + pdata->dev_irq = ret; + + /* Get the per channel DMA interrupts */ + if (pdata->per_channel_irq) { + unsigned int i, max = ARRAY_SIZE(pdata->channel_irq); + + for (i = 0; (i < max) && (dma_irqnum < dma_irqend); i++) { + ret = platform_get_irq(pdata->platdev, dma_irqnum++); + if (ret < 0) + goto err_io; + + pdata->channel_irq[i] = ret; + } + + pdata->channel_irq_count = max; + + pdata->irq_count += max; + } + + /* Get the auto-negotiation interrupt */ + ret = platform_get_irq(phy_pdev, phy_irqnum++); + if (ret < 0) + goto err_io; + pdata->an_irq = ret; + + /* Configure the netdev resource */ + ret = xgbe_config_netdev(pdata); + if (ret) + goto err_io; + + netdev_notice(pdata->netdev, "net device enabled\n"); + + return 0; + +err_io: + platform_device_put(phy_pdev); + +err_phydev: + xgbe_free_pdata(pdata); + +err_alloc: + dev_notice(dev, "net device not enabled\n"); + + return ret; +} + +static int xgbe_platform_remove(struct platform_device *pdev) +{ + struct xgbe_prv_data *pdata = platform_get_drvdata(pdev); + + xgbe_deconfig_netdev(pdata); + + platform_device_put(pdata->phy_platdev); + + xgbe_free_pdata(pdata); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int xgbe_platform_suspend(struct device *dev) +{ + struct xgbe_prv_data *pdata = dev_get_drvdata(dev); + struct net_device *netdev = pdata->netdev; + int ret = 0; + + DBGPR("-->xgbe_suspend\n"); + + if (netif_running(netdev)) + ret = xgbe_powerdown(netdev, XGMAC_DRIVER_CONTEXT); + + pdata->lpm_ctrl = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); + pdata->lpm_ctrl |= MDIO_CTRL1_LPOWER; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, pdata->lpm_ctrl); + + DBGPR("<--xgbe_suspend\n"); + + return ret; +} + +static int xgbe_platform_resume(struct device *dev) +{ + struct xgbe_prv_data *pdata = dev_get_drvdata(dev); + struct net_device *netdev = pdata->netdev; + int ret = 0; + + DBGPR("-->xgbe_resume\n"); + + pdata->lpm_ctrl &= ~MDIO_CTRL1_LPOWER; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, pdata->lpm_ctrl); + + if (netif_running(netdev)) { + ret = xgbe_powerup(netdev, XGMAC_DRIVER_CONTEXT); + + /* Schedule a restart in case the link or phy state changed + * while we were powered down. + */ + schedule_work(&pdata->restart_work); + } + + DBGPR("<--xgbe_resume\n"); + + return ret; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct xgbe_version_data xgbe_v1 = { + .init_function_ptrs_phy_impl = xgbe_init_function_ptrs_phy_v1, + .xpcs_access = XGBE_XPCS_ACCESS_V1, + .tx_max_fifo_size = 81920, + .rx_max_fifo_size = 81920, + .tx_tstamp_workaround = 1, +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id xgbe_acpi_match[] = { + { .id = "AMDI8001", + .driver_data = (kernel_ulong_t)&xgbe_v1 }, + {}, +}; + +MODULE_DEVICE_TABLE(acpi, xgbe_acpi_match); +#endif + +#ifdef CONFIG_OF +static const struct of_device_id xgbe_of_match[] = { + { .compatible = "amd,xgbe-seattle-v1a", + .data = &xgbe_v1 }, + {}, +}; + +MODULE_DEVICE_TABLE(of, xgbe_of_match); +#endif + +static SIMPLE_DEV_PM_OPS(xgbe_platform_pm_ops, + xgbe_platform_suspend, xgbe_platform_resume); + +static struct platform_driver xgbe_driver = { + .driver = { + .name = XGBE_DRV_NAME, +#ifdef CONFIG_ACPI + .acpi_match_table = xgbe_acpi_match, +#endif +#ifdef CONFIG_OF + .of_match_table = xgbe_of_match, +#endif + .pm = &xgbe_platform_pm_ops, + }, + .probe = xgbe_platform_probe, + .remove = xgbe_platform_remove, +}; + +int xgbe_platform_init(void) +{ + return platform_driver_register(&xgbe_driver); +} + +void xgbe_platform_exit(void) +{ + platform_driver_unregister(&xgbe_driver); +} diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c b/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c new file mode 100644 index 000000000..d06d260cf --- /dev/null +++ b/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c @@ -0,0 +1,279 @@ +/* + * AMD 10Gb Ethernet driver + * + * This file is available to you under your choice of the following two + * licenses: + * + * License 1: GPLv2 + * + * Copyright (c) 2014 Advanced Micro Devices, Inc. + * + * This file is free software; you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or (at + * your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * + * License 2: Modified BSD + * + * Copyright (c) 2014 Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/clk.h> +#include <linux/clocksource.h> +#include <linux/ptp_clock_kernel.h> +#include <linux/net_tstamp.h> + +#include "xgbe.h" +#include "xgbe-common.h" + +static u64 xgbe_cc_read(const struct cyclecounter *cc) +{ + struct xgbe_prv_data *pdata = container_of(cc, + struct xgbe_prv_data, + tstamp_cc); + u64 nsec; + + nsec = pdata->hw_if.get_tstamp_time(pdata); + + return nsec; +} + +static int xgbe_adjfreq(struct ptp_clock_info *info, s32 delta) +{ + struct xgbe_prv_data *pdata = container_of(info, + struct xgbe_prv_data, + ptp_clock_info); + unsigned long flags; + u64 adjust; + u32 addend, diff; + unsigned int neg_adjust = 0; + + if (delta < 0) { + neg_adjust = 1; + delta = -delta; + } + + adjust = pdata->tstamp_addend; + adjust *= delta; + diff = div_u64(adjust, 1000000000UL); + + addend = (neg_adjust) ? pdata->tstamp_addend - diff : + pdata->tstamp_addend + diff; + + spin_lock_irqsave(&pdata->tstamp_lock, flags); + + pdata->hw_if.update_tstamp_addend(pdata, addend); + + spin_unlock_irqrestore(&pdata->tstamp_lock, flags); + + return 0; +} + +static int xgbe_adjtime(struct ptp_clock_info *info, s64 delta) +{ + struct xgbe_prv_data *pdata = container_of(info, + struct xgbe_prv_data, + ptp_clock_info); + unsigned long flags; + + spin_lock_irqsave(&pdata->tstamp_lock, flags); + timecounter_adjtime(&pdata->tstamp_tc, delta); + spin_unlock_irqrestore(&pdata->tstamp_lock, flags); + + return 0; +} + +static int xgbe_gettime(struct ptp_clock_info *info, struct timespec64 *ts) +{ + struct xgbe_prv_data *pdata = container_of(info, + struct xgbe_prv_data, + ptp_clock_info); + unsigned long flags; + u64 nsec; + + spin_lock_irqsave(&pdata->tstamp_lock, flags); + + nsec = timecounter_read(&pdata->tstamp_tc); + + spin_unlock_irqrestore(&pdata->tstamp_lock, flags); + + *ts = ns_to_timespec64(nsec); + + return 0; +} + +static int xgbe_settime(struct ptp_clock_info *info, + const struct timespec64 *ts) +{ + struct xgbe_prv_data *pdata = container_of(info, + struct xgbe_prv_data, + ptp_clock_info); + unsigned long flags; + u64 nsec; + + nsec = timespec64_to_ns(ts); + + spin_lock_irqsave(&pdata->tstamp_lock, flags); + + timecounter_init(&pdata->tstamp_tc, &pdata->tstamp_cc, nsec); + + spin_unlock_irqrestore(&pdata->tstamp_lock, flags); + + return 0; +} + +static int xgbe_enable(struct ptp_clock_info *info, + struct ptp_clock_request *request, int on) +{ + return -EOPNOTSUPP; +} + +void xgbe_ptp_register(struct xgbe_prv_data *pdata) +{ + struct ptp_clock_info *info = &pdata->ptp_clock_info; + struct ptp_clock *clock; + struct cyclecounter *cc = &pdata->tstamp_cc; + u64 dividend; + + snprintf(info->name, sizeof(info->name), "%s", + netdev_name(pdata->netdev)); + info->owner = THIS_MODULE; + info->max_adj = pdata->ptpclk_rate; + info->adjfreq = xgbe_adjfreq; + info->adjtime = xgbe_adjtime; + info->gettime64 = xgbe_gettime; + info->settime64 = xgbe_settime; + info->enable = xgbe_enable; + + clock = ptp_clock_register(info, pdata->dev); + if (IS_ERR(clock)) { + dev_err(pdata->dev, "ptp_clock_register failed\n"); + return; + } + + pdata->ptp_clock = clock; + + /* Calculate the addend: + * addend = 2^32 / (PTP ref clock / 50Mhz) + * = (2^32 * 50Mhz) / PTP ref clock + */ + dividend = 50000000; + dividend <<= 32; + pdata->tstamp_addend = div_u64(dividend, pdata->ptpclk_rate); + + /* Setup the timecounter */ + cc->read = xgbe_cc_read; + cc->mask = CLOCKSOURCE_MASK(64); + cc->mult = 1; + cc->shift = 0; + + timecounter_init(&pdata->tstamp_tc, &pdata->tstamp_cc, + ktime_to_ns(ktime_get_real())); + + /* Disable all timestamping to start */ + XGMAC_IOWRITE(pdata, MAC_TSCR, 0); + pdata->tstamp_config.tx_type = HWTSTAMP_TX_OFF; + pdata->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE; +} + +void xgbe_ptp_unregister(struct xgbe_prv_data *pdata) +{ + if (pdata->ptp_clock) + ptp_clock_unregister(pdata->ptp_clock); +} diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h new file mode 100644 index 000000000..7a41367c4 --- /dev/null +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -0,0 +1,1391 @@ +/* + * AMD 10Gb Ethernet driver + * + * This file is available to you under your choice of the following two + * licenses: + * + * License 1: GPLv2 + * + * Copyright (c) 2014-2016 Advanced Micro Devices, Inc. + * + * This file is free software; you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or (at + * your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * + * License 2: Modified BSD + * + * Copyright (c) 2014-2016 Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * The Synopsys DWC ETHER XGMAC Software Driver and documentation + * (hereinafter "Software") is an unsupported proprietary work of Synopsys, + * Inc. unless otherwise expressly agreed to in writing between Synopsys + * and you. + * + * The Software IS NOT an item of Licensed Software or Licensed Product + * under any End User Software License Agreement or Agreement for Licensed + * Product with Synopsys or any supplement thereto. Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software + * annotated with this license and the Software, to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __XGBE_H__ +#define __XGBE_H__ + +#include <linux/dma-mapping.h> +#include <linux/netdevice.h> +#include <linux/workqueue.h> +#include <linux/phy.h> +#include <linux/if_vlan.h> +#include <linux/bitops.h> +#include <linux/ptp_clock_kernel.h> +#include <linux/timecounter.h> +#include <linux/net_tstamp.h> +#include <net/dcbnl.h> +#include <linux/completion.h> +#include <linux/cpumask.h> +#include <linux/interrupt.h> +#include <linux/dcache.h> +#include <linux/ethtool.h> +#include <linux/list.h> + +#define XGBE_DRV_NAME "amd-xgbe" +#define XGBE_DRV_DESC "AMD 10 Gigabit Ethernet Driver" + +/* Descriptor related defines */ +#define XGBE_TX_DESC_CNT 512 +#define XGBE_TX_DESC_MIN_FREE (XGBE_TX_DESC_CNT >> 3) +#define XGBE_TX_DESC_MAX_PROC (XGBE_TX_DESC_CNT >> 1) +#define XGBE_RX_DESC_CNT 512 + +#define XGBE_TX_DESC_CNT_MIN 64 +#define XGBE_TX_DESC_CNT_MAX 4096 +#define XGBE_RX_DESC_CNT_MIN 64 +#define XGBE_RX_DESC_CNT_MAX 4096 + +#define XGBE_TX_MAX_BUF_SIZE (0x3fff & ~(64 - 1)) + +/* Descriptors required for maximum contiguous TSO/GSO packet */ +#define XGBE_TX_MAX_SPLIT \ + ((GSO_LEGACY_MAX_SIZE / XGBE_TX_MAX_BUF_SIZE) + 1) + +/* Maximum possible descriptors needed for an SKB: + * - Maximum number of SKB frags + * - Maximum descriptors for contiguous TSO/GSO packet + * - Possible context descriptor + * - Possible TSO header descriptor + */ +#define XGBE_TX_MAX_DESCS (MAX_SKB_FRAGS + XGBE_TX_MAX_SPLIT + 2) + +#define XGBE_RX_MIN_BUF_SIZE (ETH_FRAME_LEN + ETH_FCS_LEN + VLAN_HLEN) +#define XGBE_RX_BUF_ALIGN 64 +#define XGBE_SKB_ALLOC_SIZE 256 +#define XGBE_SPH_HDSMS_SIZE 2 /* Keep in sync with SKB_ALLOC_SIZE */ + +#define XGBE_MAX_DMA_CHANNELS 16 +#define XGBE_MAX_QUEUES 16 +#define XGBE_PRIORITY_QUEUES 8 +#define XGBE_DMA_STOP_TIMEOUT 1 + +/* DMA cache settings - Outer sharable, write-back, write-allocate */ +#define XGBE_DMA_OS_ARCR 0x002b2b2b +#define XGBE_DMA_OS_AWCR 0x2f2f2f2f + +/* DMA cache settings - System, no caches used */ +#define XGBE_DMA_SYS_ARCR 0x00303030 +#define XGBE_DMA_SYS_AWCR 0x30303030 + +/* DMA cache settings - PCI device */ +#define XGBE_DMA_PCI_ARCR 0x000f0f0f +#define XGBE_DMA_PCI_AWCR 0x0f0f0f0f +#define XGBE_DMA_PCI_AWARCR 0x00000f0f + +/* DMA channel interrupt modes */ +#define XGBE_IRQ_MODE_EDGE 0 +#define XGBE_IRQ_MODE_LEVEL 1 + +#define XGMAC_MIN_PACKET 60 +#define XGMAC_STD_PACKET_MTU 1500 +#define XGMAC_MAX_STD_PACKET 1518 +#define XGMAC_JUMBO_PACKET_MTU 9000 +#define XGMAC_MAX_JUMBO_PACKET 9018 +#define XGMAC_ETH_PREAMBLE (12 + 8) /* Inter-frame gap + preamble */ + +#define XGMAC_PFC_DATA_LEN 46 +#define XGMAC_PFC_DELAYS 14000 + +#define XGMAC_PRIO_QUEUES(_cnt) \ + min_t(unsigned int, IEEE_8021QAZ_MAX_TCS, (_cnt)) + +/* Common property names */ +#define XGBE_MAC_ADDR_PROPERTY "mac-address" +#define XGBE_PHY_MODE_PROPERTY "phy-mode" +#define XGBE_DMA_IRQS_PROPERTY "amd,per-channel-interrupt" +#define XGBE_SPEEDSET_PROPERTY "amd,speed-set" + +/* Device-tree clock names */ +#define XGBE_DMA_CLOCK "dma_clk" +#define XGBE_PTP_CLOCK "ptp_clk" + +/* ACPI property names */ +#define XGBE_ACPI_DMA_FREQ "amd,dma-freq" +#define XGBE_ACPI_PTP_FREQ "amd,ptp-freq" + +/* PCI BAR mapping */ +#define XGBE_XGMAC_BAR 0 +#define XGBE_XPCS_BAR 1 +#define XGBE_MAC_PROP_OFFSET 0x1d000 +#define XGBE_I2C_CTRL_OFFSET 0x1e000 + +/* PCI MSI/MSIx support */ +#define XGBE_MSI_BASE_COUNT 4 +#define XGBE_MSI_MIN_COUNT (XGBE_MSI_BASE_COUNT + 1) + +/* PCI clock frequencies */ +#define XGBE_V2_DMA_CLOCK_FREQ 500000000 /* 500 MHz */ +#define XGBE_V2_PTP_CLOCK_FREQ 125000000 /* 125 MHz */ + +/* Timestamp support - values based on 50MHz PTP clock + * 50MHz => 20 nsec + */ +#define XGBE_TSTAMP_SSINC 20 +#define XGBE_TSTAMP_SNSINC 0 + +/* Driver PMT macros */ +#define XGMAC_DRIVER_CONTEXT 1 +#define XGMAC_IOCTL_CONTEXT 2 + +#define XGMAC_FIFO_MIN_ALLOC 2048 +#define XGMAC_FIFO_UNIT 256 +#define XGMAC_FIFO_ALIGN(_x) \ + (((_x) + XGMAC_FIFO_UNIT - 1) & ~(XGMAC_FIFO_UNIT - 1)) +#define XGMAC_FIFO_FC_OFF 2048 +#define XGMAC_FIFO_FC_MIN 4096 + +#define XGBE_TC_MIN_QUANTUM 10 + +/* Helper macro for descriptor handling + * Always use XGBE_GET_DESC_DATA to access the descriptor data + * since the index is free-running and needs to be and-ed + * with the descriptor count value of the ring to index to + * the proper descriptor data. + */ +#define XGBE_GET_DESC_DATA(_ring, _idx) \ + ((_ring)->rdata + \ + ((_idx) & ((_ring)->rdesc_count - 1))) + +/* Default coalescing parameters */ +#define XGMAC_INIT_DMA_TX_USECS 1000 +#define XGMAC_INIT_DMA_TX_FRAMES 25 + +#define XGMAC_MAX_DMA_RIWT 0xff +#define XGMAC_INIT_DMA_RX_USECS 30 +#define XGMAC_INIT_DMA_RX_FRAMES 25 + +/* Flow control queue count */ +#define XGMAC_MAX_FLOW_CONTROL_QUEUES 8 + +/* Flow control threshold units */ +#define XGMAC_FLOW_CONTROL_UNIT 512 +#define XGMAC_FLOW_CONTROL_ALIGN(_x) \ + (((_x) + XGMAC_FLOW_CONTROL_UNIT - 1) & ~(XGMAC_FLOW_CONTROL_UNIT - 1)) +#define XGMAC_FLOW_CONTROL_VALUE(_x) \ + (((_x) < 1024) ? 0 : ((_x) / XGMAC_FLOW_CONTROL_UNIT) - 2) +#define XGMAC_FLOW_CONTROL_MAX 33280 + +/* Maximum MAC address hash table size (256 bits = 8 bytes) */ +#define XGBE_MAC_HASH_TABLE_SIZE 8 + +/* Receive Side Scaling */ +#define XGBE_RSS_HASH_KEY_SIZE 40 +#define XGBE_RSS_MAX_TABLE_SIZE 256 +#define XGBE_RSS_LOOKUP_TABLE_TYPE 0 +#define XGBE_RSS_HASH_KEY_TYPE 1 + +/* Auto-negotiation */ +#define XGBE_AN_MS_TIMEOUT 500 +#define XGBE_LINK_TIMEOUT 5 +#define XGBE_KR_TRAINING_WAIT_ITER 50 + +#define XGBE_SGMII_AN_LINK_STATUS BIT(1) +#define XGBE_SGMII_AN_LINK_SPEED (BIT(2) | BIT(3)) +#define XGBE_SGMII_AN_LINK_SPEED_100 0x04 +#define XGBE_SGMII_AN_LINK_SPEED_1000 0x08 +#define XGBE_SGMII_AN_LINK_DUPLEX BIT(4) + +/* ECC correctable error notification window (seconds) */ +#define XGBE_ECC_LIMIT 60 + +/* MDIO port types */ +#define XGMAC_MAX_C22_PORT 3 + +/* Link mode bit operations */ +#define XGBE_ZERO_SUP(_ls) \ + ethtool_link_ksettings_zero_link_mode((_ls), supported) + +#define XGBE_SET_SUP(_ls, _mode) \ + ethtool_link_ksettings_add_link_mode((_ls), supported, _mode) + +#define XGBE_CLR_SUP(_ls, _mode) \ + ethtool_link_ksettings_del_link_mode((_ls), supported, _mode) + +#define XGBE_IS_SUP(_ls, _mode) \ + ethtool_link_ksettings_test_link_mode((_ls), supported, _mode) + +#define XGBE_ZERO_ADV(_ls) \ + ethtool_link_ksettings_zero_link_mode((_ls), advertising) + +#define XGBE_SET_ADV(_ls, _mode) \ + ethtool_link_ksettings_add_link_mode((_ls), advertising, _mode) + +#define XGBE_CLR_ADV(_ls, _mode) \ + ethtool_link_ksettings_del_link_mode((_ls), advertising, _mode) + +#define XGBE_ADV(_ls, _mode) \ + ethtool_link_ksettings_test_link_mode((_ls), advertising, _mode) + +#define XGBE_ZERO_LP_ADV(_ls) \ + ethtool_link_ksettings_zero_link_mode((_ls), lp_advertising) + +#define XGBE_SET_LP_ADV(_ls, _mode) \ + ethtool_link_ksettings_add_link_mode((_ls), lp_advertising, _mode) + +#define XGBE_CLR_LP_ADV(_ls, _mode) \ + ethtool_link_ksettings_del_link_mode((_ls), lp_advertising, _mode) + +#define XGBE_LP_ADV(_ls, _mode) \ + ethtool_link_ksettings_test_link_mode((_ls), lp_advertising, _mode) + +#define XGBE_LM_COPY(_dst, _dname, _src, _sname) \ + bitmap_copy((_dst)->link_modes._dname, \ + (_src)->link_modes._sname, \ + __ETHTOOL_LINK_MODE_MASK_NBITS) + +struct xgbe_prv_data; + +struct xgbe_packet_data { + struct sk_buff *skb; + + unsigned int attributes; + + unsigned int errors; + + unsigned int rdesc_count; + unsigned int length; + + unsigned int header_len; + unsigned int tcp_header_len; + unsigned int tcp_payload_len; + unsigned short mss; + + unsigned short vlan_ctag; + + u64 rx_tstamp; + + u32 rss_hash; + enum pkt_hash_types rss_hash_type; + + unsigned int tx_packets; + unsigned int tx_bytes; +}; + +/* Common Rx and Tx descriptor mapping */ +struct xgbe_ring_desc { + __le32 desc0; + __le32 desc1; + __le32 desc2; + __le32 desc3; +}; + +/* Page allocation related values */ +struct xgbe_page_alloc { + struct page *pages; + unsigned int pages_len; + unsigned int pages_offset; + + dma_addr_t pages_dma; +}; + +/* Ring entry buffer data */ +struct xgbe_buffer_data { + struct xgbe_page_alloc pa; + struct xgbe_page_alloc pa_unmap; + + dma_addr_t dma_base; + unsigned long dma_off; + unsigned int dma_len; +}; + +/* Tx-related ring data */ +struct xgbe_tx_ring_data { + unsigned int packets; /* BQL packet count */ + unsigned int bytes; /* BQL byte count */ +}; + +/* Rx-related ring data */ +struct xgbe_rx_ring_data { + struct xgbe_buffer_data hdr; /* Header locations */ + struct xgbe_buffer_data buf; /* Payload locations */ + + unsigned short hdr_len; /* Length of received header */ + unsigned short len; /* Length of received packet */ +}; + +/* Structure used to hold information related to the descriptor + * and the packet associated with the descriptor (always use + * the XGBE_GET_DESC_DATA macro to access this data from the ring) + */ +struct xgbe_ring_data { + struct xgbe_ring_desc *rdesc; /* Virtual address of descriptor */ + dma_addr_t rdesc_dma; /* DMA address of descriptor */ + + struct sk_buff *skb; /* Virtual address of SKB */ + dma_addr_t skb_dma; /* DMA address of SKB data */ + unsigned int skb_dma_len; /* Length of SKB DMA area */ + + struct xgbe_tx_ring_data tx; /* Tx-related data */ + struct xgbe_rx_ring_data rx; /* Rx-related data */ + + unsigned int mapped_as_page; + + /* Incomplete receive save location. If the budget is exhausted + * or the last descriptor (last normal descriptor or a following + * context descriptor) has not been DMA'd yet the current state + * of the receive processing needs to be saved. + */ + unsigned int state_saved; + struct { + struct sk_buff *skb; + unsigned int len; + unsigned int error; + } state; +}; + +struct xgbe_ring { + /* Ring lock - used just for TX rings at the moment */ + spinlock_t lock; + + /* Per packet related information */ + struct xgbe_packet_data packet_data; + + /* Virtual/DMA addresses and count of allocated descriptor memory */ + struct xgbe_ring_desc *rdesc; + dma_addr_t rdesc_dma; + unsigned int rdesc_count; + + /* Array of descriptor data corresponding the descriptor memory + * (always use the XGBE_GET_DESC_DATA macro to access this data) + */ + struct xgbe_ring_data *rdata; + + /* Page allocation for RX buffers */ + struct xgbe_page_alloc rx_hdr_pa; + struct xgbe_page_alloc rx_buf_pa; + int node; + + /* Ring index values + * cur - Tx: index of descriptor to be used for current transfer + * Rx: index of descriptor to check for packet availability + * dirty - Tx: index of descriptor to check for transfer complete + * Rx: index of descriptor to check for buffer reallocation + */ + unsigned int cur; + unsigned int dirty; + + /* Coalesce frame count used for interrupt bit setting */ + unsigned int coalesce_count; + + union { + struct { + unsigned int queue_stopped; + unsigned int xmit_more; + unsigned short cur_mss; + unsigned short cur_vlan_ctag; + } tx; + }; +} ____cacheline_aligned; + +/* Structure used to describe the descriptor rings associated with + * a DMA channel. + */ +struct xgbe_channel { + char name[16]; + + /* Address of private data area for device */ + struct xgbe_prv_data *pdata; + + /* Queue index and base address of queue's DMA registers */ + unsigned int queue_index; + void __iomem *dma_regs; + + /* Per channel interrupt irq number */ + int dma_irq; + char dma_irq_name[IFNAMSIZ + 32]; + + /* Netdev related settings */ + struct napi_struct napi; + + /* Per channel interrupt enablement tracker */ + unsigned int curr_ier; + unsigned int saved_ier; + + unsigned int tx_timer_active; + struct timer_list tx_timer; + + struct xgbe_ring *tx_ring; + struct xgbe_ring *rx_ring; + + int node; + cpumask_t affinity_mask; +} ____cacheline_aligned; + +enum xgbe_state { + XGBE_DOWN, + XGBE_LINK_INIT, + XGBE_LINK_ERR, + XGBE_STOPPED, +}; + +enum xgbe_int { + XGMAC_INT_DMA_CH_SR_TI, + XGMAC_INT_DMA_CH_SR_TPS, + XGMAC_INT_DMA_CH_SR_TBU, + XGMAC_INT_DMA_CH_SR_RI, + XGMAC_INT_DMA_CH_SR_RBU, + XGMAC_INT_DMA_CH_SR_RPS, + XGMAC_INT_DMA_CH_SR_TI_RI, + XGMAC_INT_DMA_CH_SR_FBE, + XGMAC_INT_DMA_ALL, +}; + +enum xgbe_int_state { + XGMAC_INT_STATE_SAVE, + XGMAC_INT_STATE_RESTORE, +}; + +enum xgbe_ecc_sec { + XGBE_ECC_SEC_TX, + XGBE_ECC_SEC_RX, + XGBE_ECC_SEC_DESC, +}; + +enum xgbe_speed { + XGBE_SPEED_1000 = 0, + XGBE_SPEED_2500, + XGBE_SPEED_10000, + XGBE_SPEEDS, +}; + +enum xgbe_xpcs_access { + XGBE_XPCS_ACCESS_V1 = 0, + XGBE_XPCS_ACCESS_V2, +}; + +enum xgbe_an_mode { + XGBE_AN_MODE_CL73 = 0, + XGBE_AN_MODE_CL73_REDRV, + XGBE_AN_MODE_CL37, + XGBE_AN_MODE_CL37_SGMII, + XGBE_AN_MODE_NONE, +}; + +enum xgbe_an { + XGBE_AN_READY = 0, + XGBE_AN_PAGE_RECEIVED, + XGBE_AN_INCOMPAT_LINK, + XGBE_AN_COMPLETE, + XGBE_AN_NO_LINK, + XGBE_AN_ERROR, +}; + +enum xgbe_rx { + XGBE_RX_BPA = 0, + XGBE_RX_XNP, + XGBE_RX_COMPLETE, + XGBE_RX_ERROR, +}; + +enum xgbe_mode { + XGBE_MODE_KX_1000 = 0, + XGBE_MODE_KX_2500, + XGBE_MODE_KR, + XGBE_MODE_X, + XGBE_MODE_SGMII_100, + XGBE_MODE_SGMII_1000, + XGBE_MODE_SFI, + XGBE_MODE_UNKNOWN, +}; + +enum xgbe_speedset { + XGBE_SPEEDSET_1000_10000 = 0, + XGBE_SPEEDSET_2500_10000, +}; + +enum xgbe_mdio_mode { + XGBE_MDIO_MODE_NONE = 0, + XGBE_MDIO_MODE_CL22, + XGBE_MDIO_MODE_CL45, +}; + +enum xgbe_mb_cmd { + XGBE_MB_CMD_POWER_OFF = 0, + XGBE_MB_CMD_SET_1G, + XGBE_MB_CMD_SET_2_5G, + XGBE_MB_CMD_SET_10G_SFI, + XGBE_MB_CMD_SET_10G_KR, + XGBE_MB_CMD_RRC +}; + +enum xgbe_mb_subcmd { + XGBE_MB_SUBCMD_NONE = 0, + + /* 10GbE SFP subcommands */ + XGBE_MB_SUBCMD_ACTIVE = 0, + XGBE_MB_SUBCMD_PASSIVE_1M, + XGBE_MB_SUBCMD_PASSIVE_3M, + XGBE_MB_SUBCMD_PASSIVE_OTHER, + + /* 1GbE Mode subcommands */ + XGBE_MB_SUBCMD_10MBITS = 0, + XGBE_MB_SUBCMD_100MBITS, + XGBE_MB_SUBCMD_1G_SGMII, + XGBE_MB_SUBCMD_1G_KX +}; + +struct xgbe_phy { + struct ethtool_link_ksettings lks; + + int address; + + int autoneg; + int speed; + int duplex; + + int link; + + int pause_autoneg; + int tx_pause; + int rx_pause; +}; + +enum xgbe_i2c_cmd { + XGBE_I2C_CMD_READ = 0, + XGBE_I2C_CMD_WRITE, +}; + +struct xgbe_i2c_op { + enum xgbe_i2c_cmd cmd; + + unsigned int target; + + void *buf; + unsigned int len; +}; + +struct xgbe_i2c_op_state { + struct xgbe_i2c_op *op; + + unsigned int tx_len; + unsigned char *tx_buf; + + unsigned int rx_len; + unsigned char *rx_buf; + + unsigned int tx_abort_source; + + int ret; +}; + +struct xgbe_i2c { + unsigned int started; + unsigned int max_speed_mode; + unsigned int rx_fifo_size; + unsigned int tx_fifo_size; + + struct xgbe_i2c_op_state op_state; +}; + +struct xgbe_mmc_stats { + /* Tx Stats */ + u64 txoctetcount_gb; + u64 txframecount_gb; + u64 txbroadcastframes_g; + u64 txmulticastframes_g; + u64 tx64octets_gb; + u64 tx65to127octets_gb; + u64 tx128to255octets_gb; + u64 tx256to511octets_gb; + u64 tx512to1023octets_gb; + u64 tx1024tomaxoctets_gb; + u64 txunicastframes_gb; + u64 txmulticastframes_gb; + u64 txbroadcastframes_gb; + u64 txunderflowerror; + u64 txoctetcount_g; + u64 txframecount_g; + u64 txpauseframes; + u64 txvlanframes_g; + + /* Rx Stats */ + u64 rxframecount_gb; + u64 rxoctetcount_gb; + u64 rxoctetcount_g; + u64 rxbroadcastframes_g; + u64 rxmulticastframes_g; + u64 rxcrcerror; + u64 rxrunterror; + u64 rxjabbererror; + u64 rxundersize_g; + u64 rxoversize_g; + u64 rx64octets_gb; + u64 rx65to127octets_gb; + u64 rx128to255octets_gb; + u64 rx256to511octets_gb; + u64 rx512to1023octets_gb; + u64 rx1024tomaxoctets_gb; + u64 rxunicastframes_g; + u64 rxlengtherror; + u64 rxoutofrangetype; + u64 rxpauseframes; + u64 rxfifooverflow; + u64 rxvlanframes_gb; + u64 rxwatchdogerror; +}; + +struct xgbe_ext_stats { + u64 tx_tso_packets; + u64 rx_split_header_packets; + u64 rx_buffer_unavailable; + + u64 txq_packets[XGBE_MAX_DMA_CHANNELS]; + u64 txq_bytes[XGBE_MAX_DMA_CHANNELS]; + u64 rxq_packets[XGBE_MAX_DMA_CHANNELS]; + u64 rxq_bytes[XGBE_MAX_DMA_CHANNELS]; + + u64 tx_vxlan_packets; + u64 rx_vxlan_packets; + u64 rx_csum_errors; + u64 rx_vxlan_csum_errors; +}; + +struct xgbe_hw_if { + int (*tx_complete)(struct xgbe_ring_desc *); + + int (*set_mac_address)(struct xgbe_prv_data *, const u8 *addr); + int (*config_rx_mode)(struct xgbe_prv_data *); + + int (*enable_rx_csum)(struct xgbe_prv_data *); + int (*disable_rx_csum)(struct xgbe_prv_data *); + + int (*enable_rx_vlan_stripping)(struct xgbe_prv_data *); + int (*disable_rx_vlan_stripping)(struct xgbe_prv_data *); + int (*enable_rx_vlan_filtering)(struct xgbe_prv_data *); + int (*disable_rx_vlan_filtering)(struct xgbe_prv_data *); + int (*update_vlan_hash_table)(struct xgbe_prv_data *); + + int (*read_mmd_regs)(struct xgbe_prv_data *, int, int); + void (*write_mmd_regs)(struct xgbe_prv_data *, int, int, int); + int (*set_speed)(struct xgbe_prv_data *, int); + + int (*set_ext_mii_mode)(struct xgbe_prv_data *, unsigned int, + enum xgbe_mdio_mode); + int (*read_ext_mii_regs)(struct xgbe_prv_data *, int, int); + int (*write_ext_mii_regs)(struct xgbe_prv_data *, int, int, u16); + + int (*set_gpio)(struct xgbe_prv_data *, unsigned int); + int (*clr_gpio)(struct xgbe_prv_data *, unsigned int); + + void (*enable_tx)(struct xgbe_prv_data *); + void (*disable_tx)(struct xgbe_prv_data *); + void (*enable_rx)(struct xgbe_prv_data *); + void (*disable_rx)(struct xgbe_prv_data *); + + void (*powerup_tx)(struct xgbe_prv_data *); + void (*powerdown_tx)(struct xgbe_prv_data *); + void (*powerup_rx)(struct xgbe_prv_data *); + void (*powerdown_rx)(struct xgbe_prv_data *); + + int (*init)(struct xgbe_prv_data *); + int (*exit)(struct xgbe_prv_data *); + + int (*enable_int)(struct xgbe_channel *, enum xgbe_int); + int (*disable_int)(struct xgbe_channel *, enum xgbe_int); + void (*dev_xmit)(struct xgbe_channel *); + int (*dev_read)(struct xgbe_channel *); + void (*tx_desc_init)(struct xgbe_channel *); + void (*rx_desc_init)(struct xgbe_channel *); + void (*tx_desc_reset)(struct xgbe_ring_data *); + void (*rx_desc_reset)(struct xgbe_prv_data *, struct xgbe_ring_data *, + unsigned int); + int (*is_last_desc)(struct xgbe_ring_desc *); + int (*is_context_desc)(struct xgbe_ring_desc *); + void (*tx_start_xmit)(struct xgbe_channel *, struct xgbe_ring *); + + /* For FLOW ctrl */ + int (*config_tx_flow_control)(struct xgbe_prv_data *); + int (*config_rx_flow_control)(struct xgbe_prv_data *); + + /* For RX coalescing */ + int (*config_rx_coalesce)(struct xgbe_prv_data *); + int (*config_tx_coalesce)(struct xgbe_prv_data *); + unsigned int (*usec_to_riwt)(struct xgbe_prv_data *, unsigned int); + unsigned int (*riwt_to_usec)(struct xgbe_prv_data *, unsigned int); + + /* For RX and TX threshold config */ + int (*config_rx_threshold)(struct xgbe_prv_data *, unsigned int); + int (*config_tx_threshold)(struct xgbe_prv_data *, unsigned int); + + /* For RX and TX Store and Forward Mode config */ + int (*config_rsf_mode)(struct xgbe_prv_data *, unsigned int); + int (*config_tsf_mode)(struct xgbe_prv_data *, unsigned int); + + /* For TX DMA Operate on Second Frame config */ + int (*config_osp_mode)(struct xgbe_prv_data *); + + /* For MMC statistics */ + void (*rx_mmc_int)(struct xgbe_prv_data *); + void (*tx_mmc_int)(struct xgbe_prv_data *); + void (*read_mmc_stats)(struct xgbe_prv_data *); + + /* For Timestamp config */ + int (*config_tstamp)(struct xgbe_prv_data *, unsigned int); + void (*update_tstamp_addend)(struct xgbe_prv_data *, unsigned int); + void (*set_tstamp_time)(struct xgbe_prv_data *, unsigned int sec, + unsigned int nsec); + u64 (*get_tstamp_time)(struct xgbe_prv_data *); + u64 (*get_tx_tstamp)(struct xgbe_prv_data *); + + /* For Data Center Bridging config */ + void (*config_tc)(struct xgbe_prv_data *); + void (*config_dcb_tc)(struct xgbe_prv_data *); + void (*config_dcb_pfc)(struct xgbe_prv_data *); + + /* For Receive Side Scaling */ + int (*enable_rss)(struct xgbe_prv_data *); + int (*disable_rss)(struct xgbe_prv_data *); + int (*set_rss_hash_key)(struct xgbe_prv_data *, const u8 *); + int (*set_rss_lookup_table)(struct xgbe_prv_data *, const u32 *); + + /* For ECC */ + void (*disable_ecc_ded)(struct xgbe_prv_data *); + void (*disable_ecc_sec)(struct xgbe_prv_data *, enum xgbe_ecc_sec); + + /* For VXLAN */ + void (*enable_vxlan)(struct xgbe_prv_data *); + void (*disable_vxlan)(struct xgbe_prv_data *); + void (*set_vxlan_id)(struct xgbe_prv_data *); +}; + +/* This structure represents implementation specific routines for an + * implementation of a PHY. All routines are required unless noted below. + * Optional routines: + * an_pre, an_post + * kr_training_pre, kr_training_post + * module_info, module_eeprom + */ +struct xgbe_phy_impl_if { + /* Perform Setup/teardown actions */ + int (*init)(struct xgbe_prv_data *); + void (*exit)(struct xgbe_prv_data *); + + /* Perform start/stop specific actions */ + int (*reset)(struct xgbe_prv_data *); + int (*start)(struct xgbe_prv_data *); + void (*stop)(struct xgbe_prv_data *); + + /* Return the link status */ + int (*link_status)(struct xgbe_prv_data *, int *); + + /* Indicate if a particular speed is valid */ + bool (*valid_speed)(struct xgbe_prv_data *, int); + + /* Check if the specified mode can/should be used */ + bool (*use_mode)(struct xgbe_prv_data *, enum xgbe_mode); + /* Switch the PHY into various modes */ + void (*set_mode)(struct xgbe_prv_data *, enum xgbe_mode); + /* Retrieve mode needed for a specific speed */ + enum xgbe_mode (*get_mode)(struct xgbe_prv_data *, int); + /* Retrieve new/next mode when trying to auto-negotiate */ + enum xgbe_mode (*switch_mode)(struct xgbe_prv_data *); + /* Retrieve current mode */ + enum xgbe_mode (*cur_mode)(struct xgbe_prv_data *); + + /* Retrieve current auto-negotiation mode */ + enum xgbe_an_mode (*an_mode)(struct xgbe_prv_data *); + + /* Configure auto-negotiation settings */ + int (*an_config)(struct xgbe_prv_data *); + + /* Set/override auto-negotiation advertisement settings */ + void (*an_advertising)(struct xgbe_prv_data *, + struct ethtool_link_ksettings *); + + /* Process results of auto-negotiation */ + enum xgbe_mode (*an_outcome)(struct xgbe_prv_data *); + + /* Pre/Post auto-negotiation support */ + void (*an_pre)(struct xgbe_prv_data *); + void (*an_post)(struct xgbe_prv_data *); + + /* Pre/Post KR training enablement support */ + void (*kr_training_pre)(struct xgbe_prv_data *); + void (*kr_training_post)(struct xgbe_prv_data *); + + /* SFP module related info */ + int (*module_info)(struct xgbe_prv_data *pdata, + struct ethtool_modinfo *modinfo); + int (*module_eeprom)(struct xgbe_prv_data *pdata, + struct ethtool_eeprom *eeprom, u8 *data); +}; + +struct xgbe_phy_if { + /* For PHY setup/teardown */ + int (*phy_init)(struct xgbe_prv_data *); + void (*phy_exit)(struct xgbe_prv_data *); + + /* For PHY support when setting device up/down */ + int (*phy_reset)(struct xgbe_prv_data *); + int (*phy_start)(struct xgbe_prv_data *); + void (*phy_stop)(struct xgbe_prv_data *); + + /* For PHY support while device is up */ + void (*phy_status)(struct xgbe_prv_data *); + int (*phy_config_aneg)(struct xgbe_prv_data *); + + /* For PHY settings validation */ + bool (*phy_valid_speed)(struct xgbe_prv_data *, int); + + /* For single interrupt support */ + irqreturn_t (*an_isr)(struct xgbe_prv_data *); + + /* For ethtool PHY support */ + int (*module_info)(struct xgbe_prv_data *pdata, + struct ethtool_modinfo *modinfo); + int (*module_eeprom)(struct xgbe_prv_data *pdata, + struct ethtool_eeprom *eeprom, u8 *data); + + /* PHY implementation specific services */ + struct xgbe_phy_impl_if phy_impl; +}; + +struct xgbe_i2c_if { + /* For initial I2C setup */ + int (*i2c_init)(struct xgbe_prv_data *); + + /* For I2C support when setting device up/down */ + int (*i2c_start)(struct xgbe_prv_data *); + void (*i2c_stop)(struct xgbe_prv_data *); + + /* For performing I2C operations */ + int (*i2c_xfer)(struct xgbe_prv_data *, struct xgbe_i2c_op *); + + /* For single interrupt support */ + irqreturn_t (*i2c_isr)(struct xgbe_prv_data *); +}; + +struct xgbe_desc_if { + int (*alloc_ring_resources)(struct xgbe_prv_data *); + void (*free_ring_resources)(struct xgbe_prv_data *); + int (*map_tx_skb)(struct xgbe_channel *, struct sk_buff *); + int (*map_rx_buffer)(struct xgbe_prv_data *, struct xgbe_ring *, + struct xgbe_ring_data *); + void (*unmap_rdata)(struct xgbe_prv_data *, struct xgbe_ring_data *); + void (*wrapper_tx_desc_init)(struct xgbe_prv_data *); + void (*wrapper_rx_desc_init)(struct xgbe_prv_data *); +}; + +/* This structure contains flags that indicate what hardware features + * or configurations are present in the device. + */ +struct xgbe_hw_features { + /* HW Version */ + unsigned int version; + + /* HW Feature Register0 */ + unsigned int gmii; /* 1000 Mbps support */ + unsigned int vlhash; /* VLAN Hash Filter */ + unsigned int sma; /* SMA(MDIO) Interface */ + unsigned int rwk; /* PMT remote wake-up packet */ + unsigned int mgk; /* PMT magic packet */ + unsigned int mmc; /* RMON module */ + unsigned int aoe; /* ARP Offload */ + unsigned int ts; /* IEEE 1588-2008 Advanced Timestamp */ + unsigned int eee; /* Energy Efficient Ethernet */ + unsigned int tx_coe; /* Tx Checksum Offload */ + unsigned int rx_coe; /* Rx Checksum Offload */ + unsigned int addn_mac; /* Additional MAC Addresses */ + unsigned int ts_src; /* Timestamp Source */ + unsigned int sa_vlan_ins; /* Source Address or VLAN Insertion */ + unsigned int vxn; /* VXLAN/NVGRE */ + + /* HW Feature Register1 */ + unsigned int rx_fifo_size; /* MTL Receive FIFO Size */ + unsigned int tx_fifo_size; /* MTL Transmit FIFO Size */ + unsigned int adv_ts_hi; /* Advance Timestamping High Word */ + unsigned int dma_width; /* DMA width */ + unsigned int dcb; /* DCB Feature */ + unsigned int sph; /* Split Header Feature */ + unsigned int tso; /* TCP Segmentation Offload */ + unsigned int dma_debug; /* DMA Debug Registers */ + unsigned int rss; /* Receive Side Scaling */ + unsigned int tc_cnt; /* Number of Traffic Classes */ + unsigned int hash_table_size; /* Hash Table Size */ + unsigned int l3l4_filter_num; /* Number of L3-L4 Filters */ + + /* HW Feature Register2 */ + unsigned int rx_q_cnt; /* Number of MTL Receive Queues */ + unsigned int tx_q_cnt; /* Number of MTL Transmit Queues */ + unsigned int rx_ch_cnt; /* Number of DMA Receive Channels */ + unsigned int tx_ch_cnt; /* Number of DMA Transmit Channels */ + unsigned int pps_out_num; /* Number of PPS outputs */ + unsigned int aux_snap_num; /* Number of Aux snapshot inputs */ +}; + +struct xgbe_version_data { + void (*init_function_ptrs_phy_impl)(struct xgbe_phy_if *); + enum xgbe_xpcs_access xpcs_access; + unsigned int mmc_64bit; + unsigned int tx_max_fifo_size; + unsigned int rx_max_fifo_size; + unsigned int tx_tstamp_workaround; + unsigned int ecc_support; + unsigned int i2c_support; + unsigned int irq_reissue_support; + unsigned int tx_desc_prefetch; + unsigned int rx_desc_prefetch; + unsigned int an_cdr_workaround; + unsigned int enable_rrc; +}; + +struct xgbe_prv_data { + struct net_device *netdev; + struct pci_dev *pcidev; + struct platform_device *platdev; + struct acpi_device *adev; + struct device *dev; + struct platform_device *phy_platdev; + struct device *phy_dev; + + /* Version related data */ + struct xgbe_version_data *vdata; + + /* ACPI or DT flag */ + unsigned int use_acpi; + + /* XGMAC/XPCS related mmio registers */ + void __iomem *xgmac_regs; /* XGMAC CSRs */ + void __iomem *xpcs_regs; /* XPCS MMD registers */ + void __iomem *rxtx_regs; /* SerDes Rx/Tx CSRs */ + void __iomem *sir0_regs; /* SerDes integration registers (1/2) */ + void __iomem *sir1_regs; /* SerDes integration registers (2/2) */ + void __iomem *xprop_regs; /* XGBE property registers */ + void __iomem *xi2c_regs; /* XGBE I2C CSRs */ + + /* Port property registers */ + unsigned int pp0; + unsigned int pp1; + unsigned int pp2; + unsigned int pp3; + unsigned int pp4; + + /* Overall device lock */ + spinlock_t lock; + + /* XPCS indirect addressing lock */ + spinlock_t xpcs_lock; + unsigned int xpcs_window_def_reg; + unsigned int xpcs_window_sel_reg; + unsigned int xpcs_window; + unsigned int xpcs_window_size; + unsigned int xpcs_window_mask; + + /* RSS addressing mutex */ + struct mutex rss_mutex; + + /* Flags representing xgbe_state */ + unsigned long dev_state; + + /* ECC support */ + unsigned long tx_sec_period; + unsigned long tx_ded_period; + unsigned long rx_sec_period; + unsigned long rx_ded_period; + unsigned long desc_sec_period; + unsigned long desc_ded_period; + + unsigned int tx_sec_count; + unsigned int tx_ded_count; + unsigned int rx_sec_count; + unsigned int rx_ded_count; + unsigned int desc_ded_count; + unsigned int desc_sec_count; + + int dev_irq; + int ecc_irq; + int i2c_irq; + int channel_irq[XGBE_MAX_DMA_CHANNELS]; + + unsigned int per_channel_irq; + unsigned int irq_count; + unsigned int channel_irq_count; + unsigned int channel_irq_mode; + + char ecc_name[IFNAMSIZ + 32]; + + struct xgbe_hw_if hw_if; + struct xgbe_phy_if phy_if; + struct xgbe_desc_if desc_if; + struct xgbe_i2c_if i2c_if; + + /* AXI DMA settings */ + unsigned int coherent; + unsigned int arcr; + unsigned int awcr; + unsigned int awarcr; + + /* Service routine support */ + struct workqueue_struct *dev_workqueue; + struct work_struct service_work; + struct timer_list service_timer; + + /* Rings for Tx/Rx on a DMA channel */ + struct xgbe_channel *channel[XGBE_MAX_DMA_CHANNELS]; + unsigned int tx_max_channel_count; + unsigned int rx_max_channel_count; + unsigned int channel_count; + unsigned int tx_ring_count; + unsigned int tx_desc_count; + unsigned int rx_ring_count; + unsigned int rx_desc_count; + + unsigned int new_tx_ring_count; + unsigned int new_rx_ring_count; + + unsigned int tx_max_q_count; + unsigned int rx_max_q_count; + unsigned int tx_q_count; + unsigned int rx_q_count; + + /* Tx/Rx common settings */ + unsigned int blen; + unsigned int pbl; + unsigned int aal; + unsigned int rd_osr_limit; + unsigned int wr_osr_limit; + + /* Tx settings */ + unsigned int tx_sf_mode; + unsigned int tx_threshold; + unsigned int tx_osp_mode; + unsigned int tx_max_fifo_size; + + /* Rx settings */ + unsigned int rx_sf_mode; + unsigned int rx_threshold; + unsigned int rx_max_fifo_size; + + /* Tx coalescing settings */ + unsigned int tx_usecs; + unsigned int tx_frames; + + /* Rx coalescing settings */ + unsigned int rx_riwt; + unsigned int rx_usecs; + unsigned int rx_frames; + + /* Current Rx buffer size */ + unsigned int rx_buf_size; + + /* Flow control settings */ + unsigned int pause_autoneg; + unsigned int tx_pause; + unsigned int rx_pause; + unsigned int rx_rfa[XGBE_MAX_QUEUES]; + unsigned int rx_rfd[XGBE_MAX_QUEUES]; + + /* Receive Side Scaling settings */ + u8 rss_key[XGBE_RSS_HASH_KEY_SIZE]; + u32 rss_table[XGBE_RSS_MAX_TABLE_SIZE]; + u32 rss_options; + + /* VXLAN settings */ + u16 vxlan_port; + + /* Netdev related settings */ + unsigned char mac_addr[ETH_ALEN]; + netdev_features_t netdev_features; + struct napi_struct napi; + struct xgbe_mmc_stats mmc_stats; + struct xgbe_ext_stats ext_stats; + + /* Filtering support */ + unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; + + /* Device clocks */ + struct clk *sysclk; + unsigned long sysclk_rate; + struct clk *ptpclk; + unsigned long ptpclk_rate; + + /* Timestamp support */ + spinlock_t tstamp_lock; + struct ptp_clock_info ptp_clock_info; + struct ptp_clock *ptp_clock; + struct hwtstamp_config tstamp_config; + struct cyclecounter tstamp_cc; + struct timecounter tstamp_tc; + unsigned int tstamp_addend; + struct work_struct tx_tstamp_work; + struct sk_buff *tx_tstamp_skb; + u64 tx_tstamp; + + /* DCB support */ + struct ieee_ets *ets; + struct ieee_pfc *pfc; + unsigned int q2tc_map[XGBE_MAX_QUEUES]; + unsigned int prio2q_map[IEEE_8021QAZ_MAX_TCS]; + unsigned int pfcq[XGBE_MAX_QUEUES]; + unsigned int pfc_rfa; + u8 num_tcs; + + /* Hardware features of the device */ + struct xgbe_hw_features hw_feat; + + /* Device work structures */ + struct work_struct restart_work; + struct work_struct stopdev_work; + + /* Keeps track of power mode */ + unsigned int power_down; + + /* Network interface message level setting */ + u32 msg_enable; + + /* Current PHY settings */ + phy_interface_t phy_mode; + int phy_link; + int phy_speed; + + /* MDIO/PHY related settings */ + unsigned int phy_started; + void *phy_data; + struct xgbe_phy phy; + int mdio_mmd; + unsigned long link_check; + struct completion mdio_complete; + + unsigned int kr_redrv; + + char an_name[IFNAMSIZ + 32]; + struct workqueue_struct *an_workqueue; + + int an_irq; + struct work_struct an_irq_work; + + /* Auto-negotiation state machine support */ + unsigned int an_int; + unsigned int an_status; + struct mutex an_mutex; + enum xgbe_an an_result; + enum xgbe_an an_state; + enum xgbe_rx kr_state; + enum xgbe_rx kx_state; + struct work_struct an_work; + unsigned int an_again; + unsigned int an_supported; + unsigned int parallel_detect; + unsigned int fec_ability; + unsigned long an_start; + unsigned long kr_start_time; + enum xgbe_an_mode an_mode; + + /* I2C support */ + struct xgbe_i2c i2c; + struct mutex i2c_mutex; + struct completion i2c_complete; + char i2c_name[IFNAMSIZ + 32]; + + unsigned int lpm_ctrl; /* CTRL1 for resume */ + + unsigned int isr_as_tasklet; + struct tasklet_struct tasklet_dev; + struct tasklet_struct tasklet_ecc; + struct tasklet_struct tasklet_i2c; + struct tasklet_struct tasklet_an; + + struct dentry *xgbe_debugfs; + + unsigned int debugfs_xgmac_reg; + + unsigned int debugfs_xpcs_mmd; + unsigned int debugfs_xpcs_reg; + + unsigned int debugfs_xprop_reg; + + unsigned int debugfs_xi2c_reg; + + bool debugfs_an_cdr_workaround; + bool debugfs_an_cdr_track_early; +}; + +/* Function prototypes*/ +struct xgbe_prv_data *xgbe_alloc_pdata(struct device *); +void xgbe_free_pdata(struct xgbe_prv_data *); +void xgbe_set_counts(struct xgbe_prv_data *); +int xgbe_config_netdev(struct xgbe_prv_data *); +void xgbe_deconfig_netdev(struct xgbe_prv_data *); + +int xgbe_platform_init(void); +void xgbe_platform_exit(void); +#ifdef CONFIG_PCI +int xgbe_pci_init(void); +void xgbe_pci_exit(void); +#else +static inline int xgbe_pci_init(void) { return 0; } +static inline void xgbe_pci_exit(void) { } +#endif + +void xgbe_init_function_ptrs_dev(struct xgbe_hw_if *); +void xgbe_init_function_ptrs_phy(struct xgbe_phy_if *); +void xgbe_init_function_ptrs_phy_v1(struct xgbe_phy_if *); +void xgbe_init_function_ptrs_phy_v2(struct xgbe_phy_if *); +void xgbe_init_function_ptrs_desc(struct xgbe_desc_if *); +void xgbe_init_function_ptrs_i2c(struct xgbe_i2c_if *); +const struct net_device_ops *xgbe_get_netdev_ops(void); +const struct ethtool_ops *xgbe_get_ethtool_ops(void); +const struct udp_tunnel_nic_info *xgbe_get_udp_tunnel_info(void); + +#ifdef CONFIG_AMD_XGBE_DCB +const struct dcbnl_rtnl_ops *xgbe_get_dcbnl_ops(void); +#endif + +void xgbe_ptp_register(struct xgbe_prv_data *); +void xgbe_ptp_unregister(struct xgbe_prv_data *); +void xgbe_dump_tx_desc(struct xgbe_prv_data *, struct xgbe_ring *, + unsigned int, unsigned int, unsigned int); +void xgbe_dump_rx_desc(struct xgbe_prv_data *, struct xgbe_ring *, + unsigned int); +void xgbe_print_pkt(struct net_device *, struct sk_buff *, bool); +void xgbe_get_all_hw_features(struct xgbe_prv_data *); +int xgbe_powerup(struct net_device *, unsigned int); +int xgbe_powerdown(struct net_device *, unsigned int); +void xgbe_init_rx_coalesce(struct xgbe_prv_data *); +void xgbe_init_tx_coalesce(struct xgbe_prv_data *); +void xgbe_restart_dev(struct xgbe_prv_data *pdata); +void xgbe_full_restart_dev(struct xgbe_prv_data *pdata); + +#ifdef CONFIG_DEBUG_FS +void xgbe_debugfs_init(struct xgbe_prv_data *); +void xgbe_debugfs_exit(struct xgbe_prv_data *); +void xgbe_debugfs_rename(struct xgbe_prv_data *pdata); +#else +static inline void xgbe_debugfs_init(struct xgbe_prv_data *pdata) {} +static inline void xgbe_debugfs_exit(struct xgbe_prv_data *pdata) {} +static inline void xgbe_debugfs_rename(struct xgbe_prv_data *pdata) {} +#endif /* CONFIG_DEBUG_FS */ + +/* NOTE: Uncomment for function trace log messages in KERNEL LOG */ +#if 0 +#define YDEBUG +#define YDEBUG_MDIO +#endif + +/* For debug prints */ +#ifdef YDEBUG +#define DBGPR(x...) pr_alert(x) +#else +#define DBGPR(x...) do { } while (0) +#endif + +#ifdef YDEBUG_MDIO +#define DBGPR_MDIO(x...) pr_alert(x) +#else +#define DBGPR_MDIO(x...) do { } while (0) +#endif + +#endif |